Aller au contenu

Formation : Cours complet Git, GitLab & GitFlow

Table des matières

  1. Introduction — Pourquoi versionner son code ?
  2. Les concepts fondamentaux de Git
  3. Installer et configurer Git
  4. Les commandes Git essentielles
  5. Les branches — Travailler en parallèle
  6. GitLab — La plateforme collaborative
  7. Travailler avec un dépôt distant
  8. GitFlow — Le workflow professionnel
  9. Les Merge Requests sur GitLab
  10. Résolution de conflits
  11. GitLab CI/CD — Automatiser les vérifications
  12. Exercices pratiques avec solutions
  13. TP Final — Projet Spring Boot en équipe

1. Introduction

1.1 Le problème sans versioning

Imaginez une situation sans Git : vous travaillez sur un projet en équipe.

   Projet/
   ├── MonApplication.java
   ├── MonApplication_v2.java
   ├── MonApplication_v2_final.java
   ├── MonApplication_v2_final_ALICE.java
   ├── MonApplication_v2_final_ALICE_corrigé.java
   └── MonApplication_UTILISER_CELUI_LA.java   ← Lequel ???

Les problèmes sans versioning :

  • Qui a fait quoi ? Quand ? Pourquoi ?
  • Comment revenir à une version qui fonctionnait ?
  • Comment plusieurs personnes travaillent sur le même fichier en même temps ?
  • Comment savoir quelle version est en production ?

1.2 Qu’est-ce que Git ?

Git est un système de contrôle de version distribué (DVCS — Distributed Version Control System) créé par Linus Torvalds en 2005 (le créateur de Linux).

Analogie : Git est comme un historique de sauvegarde ultra-puissant pour votre code. Imaginez un document Word où chaque modification est enregistrée avec la date, l’auteur, et la raison du changement — et où vous pouvez revenir à n’importe quel état passé en un clic.

Ce que Git permet :

1.3 Git vs GitHub vs GitLab

C’est une source de confusion fréquente :

Outil Nature Rôle
Git Logiciel local Le système de versioning en lui-même
GitHub Plateforme web (Microsoft) Héberger des dépôts Git en ligne
GitLab Plateforme web (open source) Héberger des dépôts Git + CI/CD intégré
Bitbucket Plateforme web (Atlassian) Alternative à GitHub/GitLab

Analogie : Git est comme le protocole email (SMTP). GitHub/GitLab sont comme Gmail ou Outlook — des interfaces qui utilisent ce protocole.

1.4 Pourquoi GitLab plutôt que GitHub ?

Dans ce cours, nous utilisons GitLab car :


2. Les concepts fondamentaux de Git

Avant de taper la moindre commande, il faut comprendre le vocabulaire et les mécanismes de Git.

2.1 Le dépôt (Repository)

Un dépôt (ou repo) est le dossier de votre projet qui contient à la fois vos fichiers et tout l’historique des modifications. Git stocke cet historique dans un sous-dossier caché .git/.

mon-projet/
├── .git/          ← Le "cerveau" de Git — ne jamais modifier manuellement !
│   ├── HEAD
│   ├── objects/
│   └── refs/
├── src/
│   └── Main.java
└── README.md

Il existe deux types de dépôts :

2.2 Les trois zones de Git

C’est le concept le plus important à comprendre. Chaque fichier peut se trouver dans une de ces trois zones :

┌─────────────────┐    git add    ┌─────────────────┐   git commit  ┌─────────────────┐
│                 │  ──────────→  │                 │ ────────────→ │                 │
│  WORKING DIR    │               │  STAGING AREA   │               │   REPOSITORY    │
│  (Répertoire    │               │  (Zone de       │               │   (Historique   │
│   de travail)   │  ←──────────  │   transit /     │               │    des commits) │
│                 │  git restore  │   Index)        │               │                 │
└─────────────────┘               └─────────────────┘               └─────────────────┘
    Vos fichiers                   Prêts à être                      Sauvegardés
    modifiés                       validés                           définitivement

Working Directory (Répertoire de travail) : C’est simplement votre dossier de travail — les fichiers tels que vous les voyez dans votre explorateur. Quand vous modifiez un fichier, la modification reste ici jusqu’à ce que vous la “stagiez”.

Staging Area (Zone de transit / Index) : C’est une zone intermédiaire où vous préparez votre prochain commit. Vous choisissez précisément quelles modifications vous souhaitez inclure dans le prochain enregistrement. C’est comme préparer un colis avant de l’envoyer.

Repository (Dépôt local) : L’historique permanent de tous vos commits. Une fois qu’un commit est fait, il est conservé indéfiniment (sauf action explicite).

Exemple concret : Vous modifiez 5 fichiers en une matinée. Vous voulez créer deux commits distincts : un pour la nouvelle fonctionnalité (3 fichiers), un pour la correction de bug (2 fichiers). La staging area vous permet de sélectionner précisément les fichiers de chaque commit.

2.3 Le commit

Un commit est un enregistrement d’un état du projet à un instant donné. C’est l’unité de base de Git.

Chaque commit contient :

commit a3f2b1c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
Author: Alice Dupont <alice@example.com>
Date:   Mon Jan 15 09:30:00 2024 +0100

    feat: ajouter la fonctionnalité de recherche par auteur

    - Ajout de la méthode findByAuthor() dans BookRepository
    - Ajout du endpoint GET /api/books/search?author=...
    - Tests unitaires correspondants

La chaîne de commits : Les commits forment une chaîne chronologique. Chaque commit pointe vers son parent, créant ainsi l’historique du projet.

[commit 1] → [commit 2] → [commit 3] → [commit 4] ← HEAD (position actuelle)
  "init"      "feat:..."   "fix:..."    "feat:..."

2.4 HEAD

HEAD est un pointeur qui indique votre position actuelle dans l’historique. En temps normal, HEAD pointe vers le dernier commit de la branche active.

main   → [C1] → [C2] → [C3] → [C4]
                               ↑
                              HEAD

2.5 Les branches

Une branche est une ligne de développement indépendante. C’est techniquement juste un pointeur vers un commit.

main    : [C1] → [C2] → [C3] → [C4]
                         ↓
feature :                [C3] → [C5] → [C6]

Analogie : Imaginez un arbre. Le tronc est la branche principale (main). Les branches latérales sont les fonctionnalités en cours de développement. Elles peuvent se développer indépendamment, puis être “greffées” (fusionnées) sur le tronc.


3. Installer et configurer Git

3.1 Installation

Windows :

Téléchargez Git for Windows sur https://git-scm.com/download/win L’installateur inclut Git Bash (un terminal Unix pour Windows).

macOS :

# Avec Homebrew
brew install git

# Ou via Xcode Command Line Tools
xcode-select --install

Linux (Ubuntu/Debian) :

sudo apt update
sudo apt install git

Vérifier l’installation :

git --version
# git version 2.43.0

3.2 Configuration initiale (OBLIGATOIRE)

Avant toute chose, configurez votre identité. Ces informations apparaîtront dans chaque commit que vous créez.

# Votre nom (tel qu'il apparaîtra dans les commits)
git config --global user.name "Alice Dupont"

# Votre email (doit correspondre à votre compte GitLab)
git config --global user.email "alice.dupont@example.com"

# Éditeur de texte par défaut (pour les messages de commit)
git config --global core.editor "nano"   # ou "vim", "code" pour VS Code

# Définir 'main' comme nom de branche principale par défaut
git config --global init.defaultBranch main

# Activer les couleurs dans le terminal
git config --global color.ui auto

# Stratégie de pull (recommandée)
git config --global pull.rebase false

Vérifier la configuration :

git config --list
# Affiche toute la configuration

git config user.name
# Alice Dupont

Portées de la configuration :

--global   # S'applique à tous vos projets (stocké dans ~/.gitconfig)
--local    # S'applique uniquement au dépôt courant (stocké dans .git/config)
--system   # S'applique à tous les utilisateurs de la machine

3.3 Configurer l’authentification avec GitLab

Pour communiquer avec GitLab sans saisir son mot de passe à chaque fois, utilisez SSH.

# 1. Générer une clé SSH
ssh-keygen -t ed25519 -C "alice.dupont@example.com"
# Appuyez sur Entrée pour les questions (emplacement et passphrase optionnelle)

# 2. Afficher la clé publique
cat ~/.ssh/id_ed25519.pub
# Copiez tout le contenu affiché

# 3. Ajouter dans GitLab :
# → Profil GitLab → Préférences → Clés SSH → Coller la clé → Ajouter

# 4. Tester la connexion
ssh -T git@gitlab.com
# Welcome to GitLab, @alice_dupont!

4. Les commandes Git essentielles

4.1 Initialiser un dépôt

# Créer un nouveau dépôt Git dans le dossier courant
git init

# Ce que ça fait :
# Crée un dossier .git/ dans le répertoire courant
# Le dépôt est vide — aucun commit encore

# Vérifier que Git est initialisé
ls -la
# On voit le dossier .git/

4.2 Cloner un dépôt existant

# Cloner depuis GitLab (SSH — recommandé)
git clone git@gitlab.com:mon-groupe/mon-projet.git

# Cloner depuis GitLab (HTTPS)
git clone https://gitlab.com/mon-groupe/mon-projet.git

# Cloner dans un dossier avec un nom spécifique
git clone git@gitlab.com:mon-groupe/mon-projet.git mon-dossier

# Ce que ça fait :
# Crée un dossier avec tous les fichiers du projet
# Configure automatiquement le remote "origin"
# Récupère tout l'historique

4.3 Vérifier l’état du dépôt

git status

Exemple de sortie :

On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:          ← Dans la staging area
  (use "git restore --staged <file>..." to unstage)
        new file:   src/Main.java

Changes not staged for commit:    ← Modifiés mais pas stagés
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

Untracked files:                  ← Nouveaux fichiers, pas encore trackés
  (use "git add <file>..." to include in what will be committed)
        config.properties

Interprétation des couleurs :

  • Rouge : modifications non stagées (dans le working dir)
  • Vert : modifications stagées (dans la staging area, prêtes pour le commit)

4.4 Ajouter des fichiers à la staging area

# Ajouter un fichier spécifique
git add src/Main.java

# Ajouter plusieurs fichiers
git add src/Main.java src/Utils.java

# Ajouter tous les fichiers modifiés et nouveaux
git add .

# Ajouter tous les fichiers d'un dossier
git add src/

# Ajouter de manière interactive (choisir les parties à stager)
git add -p
# Très utile pour séparer les modifications en plusieurs commits

git add . ajoute TOUT — y compris des fichiers que vous ne voulez pas versionner (comme des fichiers de configuration locale, des secrets, des dossiers target/). C’est pour ça qu’on utilise .gitignore.

4.5 Le fichier .gitignore

.gitignore liste les fichiers et dossiers que Git doit ignorer complètement.

# .gitignore pour un projet Java/Spring Boot
# ← Les commentaires commencent par #

# Dossier de compilation Maven/Gradle
target/
build/
*.class

# Fichiers d'IDE
.idea/
*.iml
.eclipse/
.settings/
.classpath
.project

# Fichiers de log
*.log
logs/

# Fichiers de configuration sensibles (JAMAIS dans Git !)
application-prod.properties
*.env
.env

# Fichiers système
.DS_Store         # macOS
Thumbs.db         # Windows
desktop.ini

# Dossier node_modules (si frontend)
node_modules/

# Patterns avancés
*.tmp             # Tous les fichiers .tmp
!important.tmp    # Sauf important.tmp (! = exception)
doc/**/*.pdf      # Tous les PDF dans n'importe quel sous-dossier de doc/

Conseil : Générez votre .gitignore sur https://gitignore.io en saisissant votre stack technique (Java, Maven, IntelliJ, etc.).

4.6 Créer un commit

# Créer un commit avec un message
git commit -m "feat: ajouter la page de connexion utilisateur"

# Ouvrir l'éditeur pour un message multiligne
git commit
# Une fois dans l'éditeur, écrivez votre message et sauvegardez

# Raccourci : stager ET committer tous les fichiers déjà trackés
git commit -am "fix: corriger le bug d'affichage du menu"
# ATTENTION : ne stager PAS les nouveaux fichiers (untracked)

# Modifier le dernier commit (avant push !)
git commit --amend -m "feat: ajouter la page de connexion avec validation"

4.7 Les conventions de messages de commit

Un bon message de commit répond à la question : “Que fait ce commit si on l’applique ?”

Format recommandé (Conventional Commits) :

type(scope): description courte en minuscules

Corps optionnel : explication détaillée si nécessaire.
On peut mettre plusieurs lignes.

Footer optionnel : référence aux tickets/issues
Closes #123

Types de commits :

Type Usage
feat Nouvelle fonctionnalité
fix Correction de bug
docs Documentation uniquement
style Formatage, espaces (pas de logique)
refactor Refactoring (ni feat ni fix)
test Ajout ou modification de tests
chore Tâches de maintenance (deps, config…)
perf Amélioration de performance
ci Fichiers CI/CD

Exemples de bons messages :

feat(auth): ajouter la connexion via Google OAuth

fix(panier): corriger le calcul des remises cumulées

docs(readme): mettre à jour les instructions d'installation

test(user-service): ajouter les tests d'intégration de UserService

chore(deps): mettre à jour Spring Boot vers 3.2.0

Exemples de mauvais messages :

✗ "fix"                       ← Trop vague
✗ "modifications"             ← Inutile
✗ "ça marche maintenant"      ← Aucune info
✗ "WIP"                       ← Work In Progress = pas fini
✗ "asdfgh"                    ← Test ou fatigue
✗ "Correction du bug #2 de la feature ajoutée hier après-midi par Bob quand il a..." ← Trop long

Règle d’or : La description courte fait 50 caractères maximum, commence par une minuscule, et n’a pas de point final. Le corps du commit est séparé par une ligne vide et fait 72 caractères par ligne max.

4.8 Consulter l’historique

# Historique complet
git log

# Historique compact (une ligne par commit)
git log --oneline
# a3f2b1c feat: ajouter la recherche par auteur
# 7d8e9f0 fix: corriger la validation du formulaire
# 1a2b3c4 feat: créer la page d'accueil

# Historique avec graphe des branches
git log --oneline --graph --all
# * a3f2b1c (HEAD -> main) feat: ajouter la recherche
# * 7d8e9f0 fix: corriger la validation
# | * 5e6f7a8 (feature/panier) feat: ajouter le panier
# |/
# * 1a2b3c4 feat: créer la page d'accueil

# Voir les modifications d'un commit spécifique
git show a3f2b1c

# Historique d'un fichier spécifique
git log --follow src/Main.java

# Rechercher dans les messages de commit
git log --grep="panier"

# Commits par auteur
git log --author="Alice"

# Commits entre deux dates
git log --after="2024-01-01" --before="2024-02-01"

4.9 Voir les différences

# Différences dans le working directory (non stagées)
git diff

# Différences dans la staging area (stagées, prêtes pour commit)
git diff --staged
# ou : git diff --cached

# Différences entre deux commits
git diff a3f2b1c 7d8e9f0

# Différences entre deux branches
git diff main feature/panier

# Différences d'un fichier spécifique
git diff src/Main.java

Lecture d’un diff :

diff --git a/src/Main.java b/src/Main.java
index 1234567..abcdefg 100644
--- a/src/Main.java     ← Version avant
+++ b/src/Main.java     ← Version après
@@ -10,6 +10,8 @@      ← Lignes concernées
 public class Main {
-    private String name;          ← Ligne supprimée (rouge)
+    private String firstName;     ← Ligne ajoutée (verte)
+    private String lastName;      ← Ligne ajoutée (verte)
     public void run() {

4.10 Annuler des modifications

#  Annuler les modifications dans le working dir (avant git add)
git restore src/Main.java
#   IRRÉVERSIBLE : les modifications sont perdues définitivement

#  Déstaguer un fichier (après git add, avant git commit)
git restore --staged src/Main.java
# Les modifications restent dans le working dir

#  Créer un commit qui annule un commit passé (SÛRE)
git revert a3f2b1c
# Crée un nouveau commit qui inverse les changements de a3f2b1c
# L'historique est préservé — recommandé pour les branches partagées

#   Revenir en arrière dans l'historique (DANGEREUSE)
git reset --soft HEAD~1   # Annule le dernier commit, garde les modifs stagées
git reset --mixed HEAD~1  # Annule le dernier commit, garde les modifs (non stagées)
git reset --hard HEAD~1   # Annule le dernier commit ET les modifications — IRRÉVERSIBLE !

#   Règle d'or : N'utilisez JAMAIS git reset sur des commits déjà poussés (push)

** Résumé des actions d’annulation :**

Situation Commande
Modifier un fichier → annuler avant git add git restore <fichier>
Après git add → déstaguer git restore --staged <fichier>
Après git commit → annuler proprement git revert <hash>
Dernier commit local pas encore pushé git reset --soft HEAD~1

4.11 La commande git stash — Mettre de côté

Vous êtes en train de travailler sur une fonctionnalité quand on vous demande de corriger un bug urgent. Vous ne voulez pas committer un travail inachevé. git stash met vos modifications de côté temporairement.

# Mettre les modifications de côté
git stash
# ou avec un nom
git stash push -m "travail en cours sur le panier"

# Voir les stash enregistrés
git stash list
# stash@{0}: On feature/panier: travail en cours sur le panier
# stash@{1}: On main: modifs temporaires

# Récupérer le dernier stash (et le supprimer de la liste)
git stash pop

# Récupérer un stash spécifique
git stash apply stash@{1}

# Supprimer un stash
git stash drop stash@{0}

# Supprimer tous les stash
git stash clear

5. Les branches

5.1 Créer et naviguer entre les branches

# Voir toutes les branches locales
git branch
# * main           ← L'étoile indique la branche courante
#   feature/panier
#   fix/bug-login

# Voir toutes les branches (locales + distantes)
git branch -a
# * main
#   feature/panier
#   remotes/origin/main
#   remotes/origin/develop

# Créer une nouvelle branche
git branch feature/nouvelle-fonctionnalite

# Se déplacer sur une branche existante
git checkout feature/nouvelle-fonctionnalite
# ou (moderne, depuis Git 2.23)
git switch feature/nouvelle-fonctionnalite

# Créer ET se déplacer sur la nouvelle branche (raccourci)
git checkout -b feature/nouvelle-fonctionnalite
# ou
git switch -c feature/nouvelle-fonctionnalite

# Supprimer une branche (locale, déjà fusionnée)
git branch -d feature/ancienne-fonctionnalite

# Forcer la suppression (même non fusionnée)
git branch -D feature/travail-abandonne

# Renommer une branche
git branch -m ancien-nom nouveau-nom

5.2 Nommer ses branches

Une convention de nommage claire est essentielle dans une équipe :

# Format recommandé
type/description-courte-en-kebab-case

# Exemples
feature/authentification-google
feature/page-de-paiement
fix/correction-calcul-tva
fix/bug-connexion-ie11
hotfix/faille-securite-xss
release/1.2.0
chore/mise-a-jour-dependances
refactor/extraction-service-email
docs/guide-installation

5.3 Fusionner des branches (Merge)

Une fois une fonctionnalité terminée, on la fusionne dans la branche principale.

# Se positionner sur la branche DESTINATION (celle qui reçoit)
git switch main

# Fusionner la branche source dans la branche courante
git merge feature/ma-fonctionnalite

Les types de merge :

Fast-Forward Merge (fusion rapide) : quand la branche principale n’a pas avancé depuis la création de la branche feature. Git “avance” simplement le pointeur.

Avant :
main    : [C1] → [C2]
                  ↓
feature :         [C2] → [C3] → [C4]

Après (fast-forward) :
main    : [C1] → [C2] → [C3] → [C4]

3-Way Merge : quand la branche principale a avancé pendant le développement de la feature. Git crée un commit de fusion.

Avant :
main    : [C1] → [C2] → [C5]
                  ↓
feature :         [C2] → [C3] → [C4]

Après (3-way merge) :
main    : [C1] → [C2] → [C5] → [M] (merge commit)
                  ↓               ↑
feature :         [C2] → [C3] → [C4]
# Forcer la création d'un merge commit (même en fast-forward)
git merge --no-ff feature/ma-fonctionnalite
# Recommandé : permet de voir clairement l'historique des features

# Merger sans créer de commit de merge (fast-forward uniquement)
git merge --ff-only feature/ma-fonctionnalite

# Abandonner un merge en cours
git merge --abort

5.4 Le Rebase — Réécrire l’histoire

Le rebase “déplace” une série de commits pour qu’ils s’appliquent sur une autre base.

# Se positionner sur la branche feature
git switch feature/ma-fonctionnalite

# Rebaser sur main (repositionner les commits au-dessus de main)
git rebase main

Différence visuelle :

Avant :
main    : [C1] → [C2] → [C5] → [C6]
                  ↓
feature :         [C2] → [C3] → [C4]

Après git rebase main (depuis feature) :
main    : [C1] → [C2] → [C5] → [C6]
                                 ↓
feature :                        [C6] → [C3'] → [C4']
(C3 et C4 sont "rejoués" au-dessus de C6, créant C3' et C4')

Règle absolue : Ne jamais rebaser des branches partagées (comme main ou develop). Le rebase réécrit l’historique — si d’autres personnes ont basé leur travail dessus, c’est la catastrophe. Le rebase est réservé aux branches locales, non partagées.


6. GitLab — La plateforme collaborative

6.1 Créer un compte et un projet

  1. Rendez-vous sur https://gitlab.com et créez un compte
  2. Cliquez sur “New project”
  3. Choisissez “Create blank project”
  4. Remplissez :
    • Project name : gitflow-formation
    • Visibility : Private (pour commencer)
    • Initialize repository with a README : Coché

6.2 L’interface GitLab

Projet GitLab
├── Overview          ← Vue d'ensemble, activité récente
├── Repository        ← Fichiers, branches, commits, tags
│   ├── Files            ← Explorateur de fichiers
│   ├── Commits          ← Historique des commits
│   ├── Branches         ← Liste des branches
│   └── Tags             ← Liste des tags
├── Merge Requests    ← Demandes de fusion (= Pull Requests sur GitHub)
├── Issues            ← Tickets de bugs et fonctionnalités
├── CI/CD             ← Pipelines d'intégration continue
├── Packages & Registries ← Registry Docker, packages
└── Settings         ← Configuration du projet

6.3 Les fonctionnalités clés de GitLab

Issues (Tickets) :

Les Issues permettent de suivre les tâches, bugs et fonctionnalités à développer.

Titre : [BUG] La page de connexion plante sur Firefox

Description :

## Description

La page de connexion génère une erreur JavaScript sur Firefox 121.

## Étapes pour reproduire

1. Ouvrir Firefox
2. Aller sur /login
3. Saisir des identifiants valides
4. Cliquer sur "Se connecter"

## Comportement attendu

Redirection vers le tableau de bord

## Comportement observé

Erreur : "Cannot read property 'token' of undefined"

## Environnement

- Firefox 121.0
- Windows 11
- Production

Labels (Étiquettes) :

Catégorisez vos issues avec des labels : bug, feature, urgent, en cours, refactoring

Milestones :

Regroupez les issues par version ou sprint : v1.0.0, Sprint 5, Q1 2024

6.4 Configurer les protections de branches

Dans un vrai projet, on protège les branches importantes pour éviter les push directs.

Settings → Repository → Protected Branches

main :
  - Allowed to merge: Maintainers
  - Allowed to push: No one (personne ne push directement sur main)
  - Require approval: Yes (1 approbateur minimum)

develop :
  - Allowed to merge: Developers + Maintainers
  - Allowed to push: No one
  - Require approval: Yes

7. Travailler avec un dépôt distant

7.1 Les remotes

Un remote est un raccourci vers un dépôt distant. Par convention, le remote principal s’appelle origin.

# Voir les remotes configurés
git remote -v
# origin  git@gitlab.com:alice/mon-projet.git (fetch)
# origin  git@gitlab.com:alice/mon-projet.git (push)

# Ajouter un remote
git remote add origin git@gitlab.com:alice/mon-projet.git

# Changer l'URL d'un remote
git remote set-url origin git@gitlab.com:alice/nouveau-nom.git

# Supprimer un remote
git remote remove origin

# Voir les informations détaillées d'un remote
git remote show origin

7.2 Push — Envoyer ses commits

# Envoyer la branche courante vers origin
git push

# Premier push d'une nouvelle branche (définit le remote tracking)
git push -u origin feature/ma-fonctionnalite
# -u = --set-upstream : lie la branche locale à la distante
# Après ça, git push suffit pour cette branche

# Envoyer une branche spécifique
git push origin main

# Envoyer toutes les branches
git push --all origin

# Envoyer les tags
git push --tags

#  Force push (DANGEREUX — à éviter sur les branches partagées)
git push --force-with-lease  # Plus sûr que --force

7.3 Pull — Récupérer les modifications distantes

# Récupérer ET fusionner les modifications distantes
git pull
# Équivaut à : git fetch + git merge

# Récupérer les modifications distantes SANS fusionner
git fetch
git fetch origin

# Voir ce qui a changé après un fetch
git log HEAD..origin/main --oneline

# Pull avec rebase (recommandé pour garder un historique propre)
git pull --rebase

Bonne pratique : Faites toujours un git pull avant de commencer à travailler et avant de faire un git push. Cela évite la majorité des conflits.

7.4 Le workflow quotidien

# 1. Chaque matin : mettre à jour votre dépôt local
git switch main
git pull

# 2. Créer une branche pour votre tâche du jour
git switch -c feature/ajout-recherche

# 3. Travailler... coder... tester...

# 4. Sauvegarder régulièrement
git add src/SearchService.java src/SearchController.java
git commit -m "feat(search): ajouter le service de recherche par titre"

# 5. Continuer...
git add src/SearchRepository.java
git commit -m "feat(search): ajouter le repository avec requête JPQL"

# 6. Pousser sa branche sur GitLab
git push -u origin feature/ajout-recherche

# 7. Créer une Merge Request sur GitLab (voir chapitre 9)

# 8. Après approbation et merge, nettoyer
git switch main
git pull
git branch -d feature/ajout-recherche

7.5 Les tags

Les tags marquent des points importants dans l’historique, généralement les versions.

# Créer un tag annoté (recommandé)
git tag -a v1.0.0 -m "Version 1.0.0 — Première release publique"

# Créer un tag léger (simple pointeur)
git tag v1.0.0-rc1

# Voir tous les tags
git tag

# Voir les détails d'un tag
git show v1.0.0

# Pousser les tags sur le remote
git push origin v1.0.0      # Un tag spécifique
git push origin --tags      # Tous les tags

# Créer un tag sur un commit passé
git tag -a v0.9.0 -m "Version bêta" a3f2b1c

# Supprimer un tag local
git tag -d v1.0.0-beta

# Supprimer un tag distant
git push origin --delete v1.0.0-beta

8. GitFlow — Le workflow professionnel

8.1 Qu’est-ce que GitFlow ?

GitFlow est une convention de travail (workflow) définie par Vincent Driessen en 2010. Ce n’est pas un outil Git — c’est un ensemble de règles sur comment utiliser les branches Git en équipe.

Analogie 🏭 : Imaginez une usine de production. GitFlow définit les “chaînes de montage” : quelle chaîne fait quoi, dans quel ordre, et comment les pièces passent d’une chaîne à l’autre. Sans organisation, c’est le chaos. Avec GitFlow, chaque développeur sait exactement sur quelle branche travailler.

Pourquoi GitFlow ?

8.2 Les 5 types de branches de GitFlow

Production    : main ──────────────────────────────────────────────────────→
                  │  (tag v1.0)        (tag v1.1)         (tag v2.0)
                  │       ↑                  ↑                  ↑
Intégration   : develop ──────────────────────────────────────────────────→
                      ↗ ↘            ↗ ↘               ↗ ↘
Features   : feat/A  feat/B     feat/C  feat/D      feat/E  feat/F
                      (abandonnée)

La structure complète :

┌─────────────────────────────────────────────────────────────┐
│  BRANCHES PERMANENTES (existent tout le temps)             │
│                                                             │
│  main        ← Code en PRODUCTION, toujours stable         │
│  develop     ← Intégration des features pour la prochaine  │
│                version (potentiellement instable)           │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  BRANCHES TEMPORAIRES (créées pour une tâche, puis supprimées) │
│                                                             │
│  feature/*   ← Nouvelles fonctionnalités                   │
│  release/*   ← Préparation d'une nouvelle version          │
│  hotfix/*    ← Corrections urgentes en production          │
└─────────────────────────────────────────────────────────────┘

8.3 La branche main

# main ne se modifie QUE par merge depuis release/* ou hotfix/*
# Jamais de commit direct !
# git commit -m "..." sur main = INTERDIT

8.4 La branche develop

# Initialisation de develop depuis main
git switch main
git switch -c develop
git push -u origin develop

8.5 Les branches feature/*

Règles :

# ── DÉBUT D'UNE FEATURE ──────────────────────────────────────

# 1. Se mettre à jour
git switch develop
git pull origin develop

# 2. Créer la branche feature
git switch -c feature/authentification-oauth

# 3. Développer (autant de commits que nécessaire)
git add .
git commit -m "feat(auth): configurer le provider OAuth2 Google"

git add .
git commit -m "feat(auth): ajouter le controller de callback OAuth"

git add .
git commit -m "test(auth): ajouter les tests d'intégration OAuth"

# ── FIN D'UNE FEATURE ────────────────────────────────────────

# 4. Mettre à jour depuis develop (au cas où d'autres features ont été mergées)
git switch develop
git pull origin develop
git switch feature/authentification-oauth
git merge develop   # Ou git rebase develop

# 5. Merger dans develop
git switch develop
git merge --no-ff feature/authentification-oauth
# --no-ff = No Fast Forward : force la création d'un merge commit
# Cela rend visible dans l'historique que cette feature a existé

# 6. Pousser
git push origin develop

# 7. Supprimer la branche feature
git branch -d feature/authentification-oauth
git push origin --delete feature/authentification-oauth

Message du merge commit (généré automatiquement ou personnalisé) :

Merge branch 'feature/authentification-oauth' into develop

Ajout de l'authentification via OAuth2 (Google).
- Provider configuré
- Callback handler
- Tests d'intégration

Closes #42

8.6 Les branches release/*

Quand develop contient toutes les features de la prochaine version, on crée une branche release.

Rôle : Préparation finale avant mise en production (corrections de bugs mineurs, mise à jour des numéros de version, documentation).

Règles :

# ── DÉBUT D'UNE RELEASE ──────────────────────────────────────

# 1. Partir de develop à jour
git switch develop
git pull origin develop

# 2. Créer la branche release
git switch -c release/1.2.0

# 3. Faire les ajustements de release
# Mettre à jour le numéro de version dans pom.xml, package.json...
nano pom.xml  # <version>1.2.0</version>
git commit -m "chore(release): passage en version 1.2.0"

# Corrections de bugs mineurs découverts lors des tests finaux
git commit -m "fix(ui): corriger l'alignement du footer sur mobile"
git commit -m "fix(api): corriger le format de date dans les réponses"

# ── FIN D'UNE RELEASE ────────────────────────────────────────

# 4. Merger dans main
git switch main
git merge --no-ff release/1.2.0 -m "chore(release): merge release 1.2.0 dans main"
git tag -a v1.2.0 -m "Version 1.2.0"

# 5. Merger dans develop (pour récupérer les corrections faites sur release)
git switch develop
git merge --no-ff release/1.2.0 -m "chore(release): merge release 1.2.0 dans develop"

# 6. Supprimer la branche release
git branch -d release/1.2.0

# 7. Pousser tout
git push origin main develop --tags
git push origin --delete release/1.2.0

8.7 Les branches hotfix/*

Un bug critique est découvert en production. Il faut corriger immédiatement sans attendre la prochaine release.

Règles :

# ── DÉBUT D'UN HOTFIX ────────────────────────────────────────

# 1. Partir de main (= code en production)
git switch main
git pull origin main

# 2. Créer la branche hotfix
git switch -c hotfix/faille-injection-sql

# 3. Corriger le bug
git commit -m "fix(security): corriger la faille d'injection SQL dans la recherche"
git commit -m "test(security): ajouter le test de régression"

# Mettre à jour la version (patch)
nano pom.xml  # <version>1.2.1</version>
git commit -m "chore: passage en version 1.2.1"

# ── FIN DU HOTFIX ────────────────────────────────────────────

# 4. Merger dans main
git switch main
git merge --no-ff hotfix/faille-injection-sql
git tag -a v1.2.1 -m "Hotfix v1.2.1 — Correction faille SQL"

# 5. Merger dans develop
git switch develop
git merge --no-ff hotfix/faille-injection-sql

# 6. Supprimer la branche hotfix
git branch -d hotfix/faille-injection-sql

# 7. Pousser
git push origin main develop --tags
git push origin --delete hotfix/faille-injection-sql

8.8 Schéma complet de GitFlow

time →
main   ──●─────────────────────────────●──────────────────●──→
         │ (v1.0)                       │ (v1.1)           │ (v1.1.1)
         │                              │                   │
         └──────────────────────────────┤                   │
develop ─●──────────────────────────────●──────────────────●──→
         ↑   ↑           ↑   ↑          │↑                  │
         │   │           │   │          ││                  │
feat/A ──●───●           │   │     release/1.1  hotfix/fix  │
                    feat/B   feat/C      │  │        │      │
                    ──●──●   ──●──●──●  │  └────────┘      │
                                        │
                               (préparation v1.1)

8.9 L’outil git-flow (optionnel)

Il existe un outil qui automatise les commandes GitFlow :

# Installation
# macOS
brew install git-flow-avh

# Ubuntu
apt-get install git-flow

# Windows : inclus dans Git for Windows

# Initialiser gitflow dans un dépôt
git flow init
# Répond aux questions :
# Branch name for production releases: main
# Branch name for "next release": develop
# Feature branches: feature/
# Release branches: release/
# Hotfix branches: hotfix/
# Tag prefix: v

# Commandes raccourcies
git flow feature start authentification-oauth
git flow feature finish authentification-oauth

git flow release start 1.2.0
git flow release finish 1.2.0

git flow hotfix start faille-sql
git flow hotfix finish faille-sql

Notre recommandation : Apprenez les commandes Git manuelles d’abord (c’est ce cours). Une fois à l’aise, l’outil git-flow est un raccourci pratique.


9. Les Merge Requests sur GitLab

9.1 Qu’est-ce qu’une Merge Request ?

Une Merge Request (MR) sur GitLab (appelée Pull Request sur GitHub) est une demande formelle de fusion d’une branche dans une autre. C’est le cœur du code review en équipe.

Le processus :

Développeur                    Revieweur(s)               Mainteneur
     │                              │                          │
     │ 1. Push la branche           │                          │
     │ 2. Crée une MR ──────────→  │                          │
     │                    3. Examine le code                   │
     │                    4. Laisse des commentaires           │
     │ ←────────────────  5. Demande des modifications         │
     │ 6. Corrige et re-push        │                          │
     │ ──────────────────→  7. Approuve ──────────────→        │
     │                                            8. Merge la MR
     │                                            9. Supprime la branche

9.2 Créer une Merge Request sur GitLab

Via l’interface web :

  1. Allez dans votre projet GitLab
  2. Cliquez sur “Merge Requests”“New Merge Request”
  3. Sélectionnez :
    • Source branch : feature/authentification-oauth
    • Target branch : develop
  4. Cliquez “Compare branches and continue”
  5. Remplissez le formulaire :
# Titre : feat(auth): ajouter l'authentification OAuth2 Google

## Description

Implémentation de l'authentification via Google OAuth2.

## Changements

- Configuration du provider OAuth2
- Controller de callback
- Service de gestion des tokens
- Tests d'intégration

## Tests effectués

- [x] Tests unitaires : tous passent
- [x] Tests d'intégration : tous passent
- [x] Test manuel sur Chrome, Firefox

## Screenshots

[Si applicable, ajouter des captures d'écran]

## Issue liée

Closes #42

## Notes pour le reviewer

Attention : le fichier application.properties doit avoir les clés
CLIENT_ID et CLIENT_SECRET configurées dans les variables d'env GitLab.
  1. Assignez des revieweurs
  2. Ajoutez des labels et une milestone
  3. Cliquez “Create Merge Request”

9.3 Faire une bonne code review

En tant que revieweur :

# Ce qu'on vérifie dans une code review :

## Fonctionnel

- Le code fait-il ce que la MR dit qu'il fait ?
- Les cas limites sont-ils gérés ?
- Les erreurs sont-elles traitées correctement ?

## Qualité du code

- Le code est-il lisible et bien nommé ?
- Y a-t-il de la duplication à factoriser ?
- La complexité est-elle justifiée ?

## Tests

- Y a-t-il des tests pour les nouvelles fonctionnalités ?
- Les tests existants passent-ils ?

## Sécurité

- Pas de secrets en dur dans le code ?
- Les entrées utilisateur sont-elles validées ?
- Les autorisations sont-elles vérifiées ?

## Performance

- Y a-t-il des problèmes de performance évidents ?

Les types de commentaires :

# Bloquant (doit être corrigé avant merge)

Cette méthode expose une faille de sécurité.
   La variable `userId` vient directement de la requête sans validation.

# Suggestion (non bloquant)

On pourrait extraire cette logique dans une méthode privée
   `buildEmailContent()` pour améliorer la lisibilité.

# Question

Pourquoi utilise-t-on une `LinkedList` ici plutôt qu'une `ArrayList` ?
   Est-ce intentionnel pour la performance d'insertion ?

# Nit (petit détail cosmétique)

init: petit espace en trop ligne 42.

9.4 Les commandes GitLab via les messages de commit

GitLab reconnaît des mots-clés dans les messages de commit et de MR :

# Fermer automatiquement une issue lors du merge
git commit -m "fix: corriger le bug de connexion

Closes #23
Fixes #24
Resolves #25"

# Mentionner une issue (sans la fermer)
git commit -m "feat: début de l'implémentation

Refs #42
Related to #43"

10. Résolution de conflits

10.1 Quand survient un conflit ?

Un conflit survient quand deux modifications différentes touchent la même ligne d’un même fichier sur deux branches différentes.

Branch A modifie ligne 15 de Main.java → "String name = "Alice";"
Branch B modifie ligne 15 de Main.java → "String name = "Bob";"

→ Git ne sait pas lequel choisir → CONFLIT

10.2 Lire les marqueurs de conflit

Quand Git détecte un conflit, il modifie le fichier en y insérant des marqueurs :

public class UserService {

    public String getGreeting(User user) {
<<<<<<< HEAD                            Votre branche (branche courante)
        return "Bonjour " + user.getFirstName() + " !";
=======                                 Séparateur
        return "Bienvenue, " + user.getFullName() + " !";
>>>>>>> feature/accueil-personnalise    La branche qu'on merge
    }
}

Signification des marqueurs :

10.3 Résoudre un conflit

# 1. Git vous indique les fichiers en conflit
git status
# both modified: src/main/java/UserService.java

# 2. Ouvrez le fichier et choisissez manuellement la résolution
# Trois options :
# A) Garder votre version (HEAD)
# B) Garder la version entrante
# C) Créer une version combinée (souvent la meilleure option)

# Option C — version combinée (exemple)
public class UserService {
    public String getGreeting(User user) {
        return "Bonjour " + user.getFullName() + " !";
        // On garde "Bonjour" de HEAD et getFullName() de l'autre branche
    }
}

# 3. Supprimer TOUS les marqueurs de conflit (<<<, ===, >>>)

# 4. Stager les fichiers résolus
git add src/main/java/UserService.java

# 5. Vérifier qu'il n'y a plus de conflits
git status

# 6. Terminer le merge
git commit -m "merge: résolution du conflit sur UserService.getGreeting"

# OU si vous aviez lancé git rebase :
git rebase --continue

# Abandonner la résolution si c'est trop complexe
git merge --abort
git rebase --abort

10.4 Outils de résolution de conflits

# Utiliser l'outil de merge intégré
git mergetool

# Configurer VS Code comme outil de merge
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

# Dans VS Code : l'interface graphique propose des boutons :
# "Accept Current Change"    → Garder HEAD
# "Accept Incoming Change"   → Garder la branche entrante
# "Accept Both Changes"      → Garder les deux
# "Compare Changes"          → Voir côte à côte

10.5 Prévenir les conflits

# Bonnes pratiques pour minimiser les conflits :

# 1. Mettre à jour régulièrement depuis develop
git switch feature/ma-feature
git fetch origin
git merge origin/develop   # ou git rebase origin/develop

# 2. Communiquer en équipe sur qui travaille sur quels fichiers

# 3. Faire des branches courtes (quelques jours max)

# 4. Merger régulièrement les features terminées

# 5. Découper les fichiers (éviter les "God classes")

11. GitLab CI/CD

11.1 Qu’est-ce que CI/CD ?

CI (Continuous Integration) : À chaque push, Git lance automatiquement les tests, la compilation, et les analyses de qualité. Si quelque chose échoue, l’équipe est notifiée immédiatement.

CD (Continuous Deployment/Delivery) : Le déploiement en production (ou en environnement de test) est automatisé.

Analogie 🏭 : Imaginez une chaîne de contrôle qualité automatique. Chaque fois qu’un développeur livre du code, la chaîne s’active : elle compile le code, lance les tests, vérifie la qualité, et si tout est OK, déploie automatiquement. Plus besoin de le faire manuellement.

11.2 Le fichier .gitlab-ci.yml

Toute la configuration CI/CD de GitLab se fait dans un fichier .gitlab-ci.yml à la racine du projet.

# .gitlab-ci.yml — Configuration CI/CD pour un projet Spring Boot

# Image Docker de base pour tous les jobs
image: maven:3.9-eclipse-temurin-17

# Les variables globales (disponibles dans tous les jobs)
variables:
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"

# Cache Maven pour accélérer les builds
cache:
  paths:
    - .m2/repository/

# Les étapes du pipeline (s'exécutent dans l'ordre)
stages:
  - build        # Compilation
  - test         # Tests
  - quality      # Qualité du code
  - deploy       # Déploiement

# ────────────────────────────────────────
# STAGE : BUILD
# ────────────────────────────────────────
compile:
  stage: build
  script:
    - mvn compile -q
  only:
    - main
    - develop
    - /^feature\/.*/    # Toutes les branches feature/*

# ────────────────────────────────────────
# STAGE : TEST
# ────────────────────────────────────────
unit-tests:
  stage: test
  script:
    - mvn test
  artifacts:
    # Conserver les rapports de test
    reports:
      junit: target/surefire-reports/*.xml
    paths:
      - target/surefire-reports/
    expire_in: 1 week
  coverage: '/Tests run: \d+, Failures: \d+, Errors: \d+, Skipped: \d+/'

# ────────────────────────────────────────
# STAGE : QUALITY
# ────────────────────────────────────────
code-quality:
  stage: quality
  script:
    - mvn checkstyle:check
  allow_failure: true   # Ne bloque pas le pipeline si ça échoue

# ────────────────────────────────────────
# STAGE : DEPLOY
# ────────────────────────────────────────
deploy-staging:
  stage: deploy
  script:
    - echo "Déploiement en staging..."
    - mvn package -DskipTests
    # Ici on pourrait avoir des commandes de déploiement
  environment:
    name: staging
    url: https://staging.mon-app.com
  only:
    - develop   # Ne déploie en staging que depuis develop

deploy-production:
  stage: deploy
  script:
    - echo "Déploiement en production..."
    - mvn package -DskipTests
  environment:
    name: production
    url: https://mon-app.com
  only:
    - main      # Ne déploie en prod que depuis main
  when: manual  # Déploiement manuel (nécessite un clic dans GitLab)

11.3 Voir les pipelines

Dans GitLab : Project → CI/CD → Pipelines

Pipeline #123    passed   main   Alice   il y a 5 min
Pipeline #122    failed   develop Bob    il y a 2h
Pipeline #121    passed   feature/auth   Charlie  hier

En cliquant sur un pipeline, on voit chaque job et ses logs :

compile     (23s)
unit-tests  (1m 42s) — 47 tests, 0 failures
code-quality (12s) — 3 avertissements
deploy-staging (45s)

12. Exercices pratiques

EXERCICE 1 — Initialisation et premiers commits

Objectif : Comprendre les 3 zones de Git et créer ses premiers commits.

Énoncé :

  1. Créez un dossier formation-git sur votre bureau
  2. Initialisez un dépôt Git dans le dossier
  3. Créez un fichier README.md avec votre prénom ou autre un autre format fichier
  4. Stagez et committez ce fichier
  5. Créez un fichier notes.txt avec votre éditeur préféré
  6. Modifiez README.md ou votre fichier (ajoutez une ligne ou plusieurs)
  7. Stagez UNIQUEMENT README.md ou votre fichier et committez-le
  8. Vérifiez l’état (notes.txt doit être “untracked”)
  9. Affichez l’historique avec une commande git

SOLUTION EXERCICE 1 :

# 1. Créer le dossier et y entrer
mkdir ~/Bureau/formation-git
cd ~/Bureau/formation-git

# 2. Initialiser le dépôt Git
git init
# Initialized empty Git repository in /Users/alice/Bureau/formation-git/.git/

# 3. Créer README.md ou d'une autre façon
echo "# Formation Git\nAuteur : Alicia Duponti" > README.md

# 4. Stager et committer
git add README.md
git commit -m "docs: ajouter le fichier README avec l'auteur"

# 5. Créer notes.txt
echo "Mes notes sur Git :" > notes.txt
echo "- Git est un système de versioning distribué" >> notes.txt

# 6. Modifier README.md
echo "Formation dispensée en 2026" >> README.md

# 7. Stager (add) UNIQUEMENT README.md
git add README.md
git status
# Changes to be committed:
#   modified: README.md
# Untracked files:
#   notes.txt

git commit -m "docs: ajouter la date de la formation dans README"

# 8. Vérifier l'état
git status
# Untracked files:
#   notes.txt  ← notes.txt n'est PAS dans le commit !

# 9. Afficher l'historique
git log --oneline
# b2c3d4e docs: ajouter la date de la formation dans README
# a1b2c3d docs: ajouter le fichier README avec l'auteur

EXERCICE 2 — Le .gitignore

Objectif : Comprendre et utiliser .gitignore.

Énoncé :

  1. Dans votre dépôt formation-git, créez ces fichiers :
    • config/database.properties (contient password=secret123)
    • logs/app.log
    • target/MonApp.class
    • src/Main.java
  2. Créez un fichier .gitignore pour ignorer :
    • Tous les fichiers .log
    • Le dossier target/
    • Le fichier config/database.properties (sensible !)
  3. Vérifiez que ces fichiers sont bien ignorés
  4. Committez uniquement src/Main.java et .gitignore

SOLUTION EXERCICE 2 :

# 1. Créer les fichiers et dossiers
# si vous ne savez pas utiliser echo, utilisez votre éditeur préféré à la place de la commande
mkdir -p config logs target src
echo "password=secret123" > config/database.properties
echo "2024-01-15 ERROR NullPointerException" > logs/app.log
echo "binary content" > target/MonApp.class
echo 'public class Main { public static void main(String[] args) {} }' > src/Main.java

# 2. Créer le .gitignore avec vs code ou notepad ou autre
cat > .gitignore << 'EOF'
# Logs — jamais dans le versioning
*.log
logs/

# Compilation Java
target/
*.class

# Fichiers de configuration sensibles
config/database.properties
EOF

# 3. Vérifier les fichiers ignorés
git status
# Untracked files:
#   .gitignore
#   src/Main.java
# (config/, logs/, target/ ne sont PAS listés = bien ignorés !)

# Confirmation explicite
git check-ignore -v config/database.properties
# .gitignore:8:config/database.properties   config/database.properties
git check-ignore -v logs/app.log
# .gitignore:3:*.log   logs/app.log

# 4. Committer .gitignore et src/Main.java
git add .gitignore src/Main.java
git commit -m "feat: ajouter le code source et configurer .gitignore"

git log --oneline
# c3d4e5f feat: ajouter le code source et configurer .gitignore
# b2c3d4e docs: ajouter la date de la formation dans README
# a1b2c3d docs: ajouter le fichier README avec l'auteur

EXERCICE 3 — Branches et merge

Objectif : Créer des branches, développer en parallèle, et fusionner.

Énoncé :

  1. Créez une branche feature/page-accueil depuis main
  2. Créez un fichier index.html avec un titre <h1>Bienvenue</h1>
  3. Committez
  4. Revenez sur main
  5. Créez une branche feature/page-contact depuis main
  6. Créez un fichier contact.html avec <h1>Contactez-nous</h1>
  7. Committez
  8. Revenez sur main et fusionnez d’abord feature/page-accueil
  9. Puis fusionnez feature/page-contact
  10. Affichez le graphe des branches

Voici le contenu du fichier index.html :

<!DOCTYPE html>
<html>
<head><title>Accueil</title></head>
<body><h1>Bienvenue</h1></body>
</html>

Voici le fichier contact.html :

!DOCTYPE html>
<html>
<head><title>Contact</title></head>
<body><h1>Contactez-nous</h1></body>
</html>

SOLUTION EXERCICE 3 :

# 1. Branche feature/page-accueil
git switch -c feature/page-accueil

# 2. Créer index.html
cat > index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>Accueil</title></head>
<body><h1>Bienvenue</h1></body>
</html>
EOF

# 3. Committer
git add index.html
git commit -m "feat: ajouter la page d'accueil"

# 4. Revenir sur main
git switch main

# Vérifier : index.html n'existe pas sur main !
ls
# README.md  notes.txt  .gitignore  src/

# 5. Branche feature/page-contact
git switch -c feature/page-contact

# 6. Créer contact.html
cat > contact.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>Contact</title></head>
<body><h1>Contactez-nous</h1></body>
</html>
EOF

# 7. Committer
git add contact.html
git commit -m "feat: ajouter la page de contact"

# 8. Merger feature/page-accueil dans main
git switch main
git merge --no-ff feature/page-accueil -m "merge: intégrer la feature page-accueil"

# 9. Merger feature/page-contact dans main
git merge --no-ff feature/page-contact -m "merge: intégrer la feature page-contact"

# 10. Afficher le graphe
git log --oneline --graph --all
# *   f5e6d7c merge: intégrer la feature page-contact
# |\
# | * e4d5c6b feat: ajouter la page de contact
# * |   d3c4b5a merge: intégrer la feature page-accueil
# |\ \
# | * | c2b3a49 feat: ajouter la page d'accueil
# |/ /
# * / b2c3d4e docs: ajouter la date de la formation
# |/
# * a1b2c3d docs: ajouter le fichier README

# Nettoyage des branches
git branch -d feature/page-accueil feature/page-contact

EXERCICE 4 — Résolution de conflits

Objectif : Provoquer et résoudre un conflit de merge.

Énoncé :

  1. Sur main, créez presentation.txt avec Bonjour, je suis Alicia.
  2. Committez
  3. Créez la branche feature/salutation-formelle
  4. Modifiez presentation.txtBonjour, je suis Alicia Duponti.
  5. Committez
  6. Revenez sur main
  7. Modifiez presentation.txtCoucou, je suis Alicia !
  8. Committez
  9. Mergez feature/salutation-formelle dans main → CONFLIT !
  10. Résolvez le conflit en choisissant la version formelle avec le nom complet

SOLUTION EXERCICE 4 :

# 1. Créer presentation.txt sur main
git switch main
echo "Bonjour, je suis Alicia." > presentation.txt

# 2. Committer
git add presentation.txt
git commit -m "docs: ajouter le fichier de présentation"

# 3. Créer la branche feature
git switch -c feature/salutation-formelle

# 4. Modifier la présentation (version formelle)
echo "Bonjour, je suis Alicia Duponti." > presentation.txt

# 5. Committer
git add presentation.txt
git commit -m "style: ajouter le nom complet dans la présentation"

# 6. Revenir sur main
git switch main

# 7. Modifier aussi presentation.txt sur main (version différente)
echo "Coucou, je suis Alicia !" > presentation.txt

# 8. Committer
git add presentation.txt
git commit -m "style: rendre la présentation plus décontractée"

# 9. Tenter le merge → CONFLIT !
git merge feature/salutation-formelle
# Auto-merging presentation.txt
# CONFLICT (content): Merge conflict in presentation.txt
# Automatic merge failed; fix conflicts and then commit the result.

# Voir le fichier en conflit
cat presentation.txt
# <<<<<<< HEAD
# Coucou, je suis Alice !
# =======
# Bonjour, je suis Alice Dupont.
# >>>>>>> feature/salutation-formelle

# 10. Résoudre : garder la version formelle avec nom complet
echo "Bonjour, je suis Alicia Duponti." > presentation.txt
# (On supprime tous les marqueurs de conflit)

# Stager la résolution
git add presentation.txt

# Vérifier qu'il n'y a plus de conflits
git status
# All conflicts fixed but you are still merging.
#   (use "git commit" to conclude merge)

# Créer le commit de merge
git commit -m "merge: résolution conflit sur presentation.txt — version formelle retenue"

# Vérifier le résultat
cat presentation.txt
# Bonjour, je suis Alice Dupont.

git log --oneline --graph
# *   h7i8j9k merge: résolution conflit sur presentation.txt
# |\
# | * g6h7i8j style: ajouter le nom complet dans la présentation
# * | f5g6h7i style: rendre la présentation plus décontractée
# |/
# * e4f5g6h docs: ajouter le fichier de présentation

EXERCICE 5 — GitFlow complet

Objectif : Simuler un cycle GitFlow complet avec feature, release et hotfix.

Énoncé :

Vous travaillez sur une application “Todo List”. Appliquez GitFlow :

  1. Initialisez GitFlow (branches main et develop)
  2. Créez la feature ajout-taches : créez todo.txt, ajoutez 3 tâches
  3. Mergez la feature dans develop
  4. Créez la feature suppression-taches : ajoutez une ligne dans todo.txt
  5. Mergez dans develop
  6. Créez la release 1.0.0 : mettez à jour version.txt
  7. Mergez la release dans main ET develop, taguez v1.0.0
  8. Simulez un hotfix correction-typo sur main
  9. Mergez le hotfix dans main ET develop, taguez v1.0.1
  10. Affichez le graphe final

SOLUTION EXERCICE 5 :

# ═══════════════════════════════════════════
# INITIALISATION GITFLOW
# ═══════════════════════════════════════════

mkdir todo-app && cd todo-app
git init
echo "# Todo App" > README.md
git add README.md
git commit -m "chore: initialisation du projet"

# Créer develop depuis main
git switch -c develop
git push -u origin develop  # si vous avez un remote
# (Dans cet exercice local, pas de push)

echo "Projet initialisé avec branches main et develop"
git branch
# * develop
#   main

# ═══════════════════════════════════════════
# FEATURE : ajout-taches
# ═══════════════════════════════════════════

git switch develop
git switch -c feature/ajout-taches

# Développement de la feature
cat > todo.txt << 'EOF'
=== Ma liste de tâches ===
[ ] Apprendre Git
[ ] Maîtriser GitFlow
[ ] Créer un projet sur GitLab
EOF

git add todo.txt
git commit -m "feat(todo): créer la liste de tâches initiale"

# Finaliser et merger dans develop
git switch develop
git merge --no-ff feature/ajout-taches -m "merge: intégrer la feature ajout-taches"
git branch -d feature/ajout-taches

echo "Feature ajout-taches mergée dans develop"

# ═══════════════════════════════════════════
# FEATURE : suppression-taches
# ═══════════════════════════════════════════

git switch -c feature/suppression-taches

# Ajout de la fonctionnalité
cat >> todo.txt << 'EOF'
[x] Configurer Git (terminé !)
EOF

git add todo.txt
git commit -m "feat(todo): ajouter exemple de tâche complétée"

# Merger dans develop
git switch develop
git merge --no-ff feature/suppression-taches -m "merge: intégrer la feature suppression-taches"
git branch -d feature/suppression-taches

# ═══════════════════════════════════════════
# RELEASE : 1.0.0
# ═══════════════════════════════════════════

git switch develop
git switch -c release/1.0.0

# Ajuster la version
echo "1.0.0" > version.txt
git add version.txt
git commit -m "chore(release): passage en version 1.0.0"

# Correction mineure découverte pendant les tests
sed -i 's/=== Ma liste de tâches ===/=== Todo App v1.0.0 ===/g' todo.txt
git add todo.txt
git commit -m "style(todo): mettre à jour le titre avec le numéro de version"

# Merger dans main
git switch main
git merge --no-ff release/1.0.0 -m "chore(release): merger la release 1.0.0 dans main"
git tag -a v1.0.0 -m "Version 1.0.0 — Première version publique de Todo App"

# Merger dans develop
git switch develop
git merge --no-ff release/1.0.0 -m "chore(release): merger les corrections de release dans develop"

# Supprimer la branche release
git branch -d release/1.0.0

echo "Release 1.0.0 terminée et taguée !"

# ═══════════════════════════════════════════
# HOTFIX : correction-typo
# ═══════════════════════════════════════════

# Bug trouvé en production !
git switch main
git switch -c hotfix/correction-typo

# Corriger le bug (typo dans le titre)
sed -i 's/Todo App v1.0.0/Todo App — v1.0.0/g' todo.txt
git add todo.txt
git commit -m "fix(todo): corriger le tiret manquant dans le titre"

# Mettre à jour la version patch
echo "1.0.1" > version.txt
git add version.txt
git commit -m "chore: passage en version 1.0.1"

# Merger dans main
git switch main
git merge --no-ff hotfix/correction-typo -m "hotfix: corriger la typo dans le titre (v1.0.1)"
git tag -a v1.0.1 -m "Version 1.0.1 — Correction de typo"

# Merger dans develop
git switch develop
git merge --no-ff hotfix/correction-typo -m "hotfix: porter la correction de typo dans develop"

# Supprimer la branche hotfix
git branch -d hotfix/correction-typo

# ═══════════════════════════════════════════
# RÉSULTAT FINAL
# ═══════════════════════════════════════════

echo ""
echo "=== Graphe final de l'historique ==="
git log --oneline --graph --all

# Affichage attendu :
# * (HEAD -> develop) hotfix: porter la correction de typo dans develop
# *   merge: merger les corrections de release dans develop
# |\
# | *   (tag: v1.0.1, main) hotfix: corriger la typo dans le titre
# | |\
# | | * fix(todo): corriger le tiret manquant dans le titre
# | | * chore: passage en version 1.0.1
# | * |   (tag: v1.0.0) chore(release): merger la release 1.0.0 dans main
# | |\ \
# | | * | style(todo): mettre à jour le titre
# | | * | chore(release): passage en version 1.0.0
# | |/ /
# * | / merge: intégrer la feature suppression-taches
# |\ \ \
# | * | | feat(todo): ajouter exemple de tâche complétée
# * | | | merge: intégrer la feature ajout-taches
# |/ / /
# * / / feat(todo): créer la liste de tâches initiale
# |/ /
# * / chore: initialisation du projet
# |/
# (= branche principale)

echo ""
echo "=== Tags créés ==="
git tag
# v1.0.0
# v1.0.1

echo ""
echo "=== État de main ==="
git switch main
cat todo.txt
# === Todo App — v1.0.0 ===
# [ ] Apprendre Git
# [ ] Maîtriser GitFlow
# [ ] Créer un projet sur GitLab
# [x] Configurer Git (terminé !)

13. TP Final — Projet Spring Boot en équipe

13.1 Objectif

Vous allez déposer le projet Spring Boot fourni sur GitLab en appliquant GitFlow correctement.

13.2 Prérequis

13.3 Étapes à réaliser

Étape 1 — Préparer GitLab

  1. Créez un nouveau projet GitLab vide nommé gitflow-springboot
  2. Protégez la branche main (Settings → Repository → Protected Branches)
  3. Récupérez l’URL SSH du projet

Étape 2 — Initialiser le dépôt local

# Dézippez le projet fourni
unzip gitflow-starter.zip
cd gitflow-starter

# Initialisez Git
git init
git remote add origin git@gitlab.com:VOTRE_USERNAME/gitflow-springboot.git

# Créez le .gitignore adapté à Spring Boot
# (déjà présent dans le projet fourni)

# Premier commit sur main
git add .
git commit -m "chore: initialisation du projet Spring Boot"
git push -u origin main

Étape 3 — Initialiser develop

git switch -c develop
git push -u origin develop

Étape 4 — Feature 1 : Page d’accueil

git switch -c feature/page-accueil

# Modifier src/main/java/com/gitflow/controller/AccueilController.java
# Changer le message de bienvenue
# Ajouter votre prénom dans le message

git add src/main/java/com/gitflow/controller/AccueilController.java
git commit -m "feat(accueil): personnaliser le message de bienvenue"
git push -u origin feature/page-accueil

# Créer une Merge Request sur GitLab (feature/page-accueil → develop)

Étape 5 — Feature 2 : Ajout d’un endpoint

git switch develop && git pull
git switch -c feature/endpoint-version

# Ajouter un endpoint /api/version dans AccueilController
# qui retourne { "version": "1.0.0", "auteur": "VotrePrénom" }

git add .
git commit -m "feat(api): ajouter l'endpoint /api/version"
git push -u origin feature/endpoint-version

# Créer une Merge Request sur GitLab

Étape 6 — Release 1.0.0

git switch develop && git pull
git switch -c release/1.0.0

# Mettre à jour le numéro de version dans pom.xml
# <version>1.0.0</version>

git add pom.xml
git commit -m "chore(release): passage en version 1.0.0"
git push -u origin release/1.0.0

# Merge Request : release/1.0.0 → main (sur GitLab)
# Merge Request : release/1.0.0 → develop (sur GitLab)
# Créer le tag v1.0.0 sur GitLab (Repository → Tags)

Étape 7 — Hotfix

git switch main && git pull
git switch -c hotfix/correction-description

# Corriger la description dans application.properties
# spring.application.name=gitflow-formation (corriger une faute)

git add src/main/resources/application.properties
git commit -m "fix: corriger la description de l'application"

# Mettre à jour version : 1.0.1
git add pom.xml
git commit -m "chore: passage en version 1.0.1"
git push -u origin hotfix/correction-description

# Merge Request : hotfix → main
# Merge Request : hotfix → develop
# Créer le tag v1.0.1

13.4 Critères de réussite


Récapitulatif des commandes essentielles

# ── CONFIGURATION ──────────────────────────────────
git config --global user.name "Prénom Nom"
git config --global user.email "email@example.com"

# ── INITIALISATION ─────────────────────────────────
git init                          # Nouveau dépôt local
git clone <url>                   # Cloner un dépôt distant

# ── QUOTIDIEN ──────────────────────────────────────
git status                        # État du dépôt
git add <fichier>                 # Stager un fichier
git add .                         # Stager tout
git commit -m "message"           # Committer
git log --oneline --graph         # Historique visuel
git diff                          # Voir les modifications

# ── BRANCHES ────────────────────────────────────────
git branch                        # Lister les branches
git switch -c <branche>           # Créer + aller sur la branche
git switch <branche>              # Changer de branche
git merge --no-ff <branche>       # Merger (avec commit de merge)
git branch -d <branche>           # Supprimer une branche

# ── REMOTE ─────────────────────────────────────────
git remote add origin <url>       # Ajouter un remote
git push -u origin <branche>      # Premier push d'une branche
git push                          # Pousser (branch déjà trackée)
git pull                          # Récupérer et merger
git fetch                         # Récupérer sans merger

# ── ANNULATION ─────────────────────────────────────
git restore <fichier>             # Annuler modifs (working dir)
git restore --staged <fichier>    # Déstaguer
git revert <hash>                 # Annuler un commit (safe)
git stash                         # Mettre de côté
git stash pop                     # Récupérer le stash

# ── GITFLOW ────────────────────────────────────────
# Feature
git switch develop && git pull
git switch -c feature/<nom>
# ... commits ...
git switch develop
git merge --no-ff feature/<nom>
git branch -d feature/<nom>

# Release
git switch develop && git pull
git switch -c release/<version>
# ... ajustements ...
git switch main && git merge --no-ff release/<version>
git tag -a v<version> -m "..."
git switch develop && git merge --no-ff release/<version>
git branch -d release/<version>

# Hotfix
git switch main && git pull
git switch -c hotfix/<nom>
# ... correction ...
git switch main && git merge --no-ff hotfix/<nom>
git tag -a v<version-patch> -m "..."
git switch develop && git merge --no-ff hotfix/<nom>
git branch -d hotfix/<nom>