Git basique : commit, branch et merge

1. Vue d’ensemble

Git est l’outil de gestion de version que presque tous les projets utilisent, mais beaucoup de débutants ne retiennent que quelques commandes sans comprendre le fond. Cet article vous guide à travers trois concepts essentiels : commit (sauvegarder un snapshot), branch (branche de travail indépendante) et merge (fusionner les branches). Après sa lecture, vous serez confiant pour créer de nouvelles branches, faire des commits correctement, gérer les conflits lors d’une fusion et savoir quand utiliser merge au lieu de rebase.

2. Commit – sauvegarder un snapshot

2.1. Qu’est-ce qu’un commit

Chaque commit est un « snapshot » de l’état complet des fichiers à un moment donné, accompagné d’un pointeur vers le commit parent. Git ne stocke pas les diffs, mais les snapshots — cela rend les branches et les fusions rapides et légers.

# Initialiser le repo et faire le premier commit
git init order-service
cd order-service
echo "# Order Service" > README.md
git add README.md
git commit -m "docs: ajouter README pour Order Service"

Explication :

  • git init crée un nouveau repo, génère un répertoire caché .git.
  • git add README.md place le fichier dans la staging area (zone de travail en attente de commit).
  • git commit -m "..." crée un snapshot, attache un message décrivant les modifications.

Vérifiez l’historique :

git log --oneline
a1b2c3d docs: ajouter README pour Order Service

2.2. Écrire de bons messages de commit

Un commit doit répondre à la question « que fait ce changement et pourquoi ». Convention courante (conventional commits) :

feat: ajouter l'API de création de commande
fix: corriger le calcul incorrect du total avec coupon appliqué
refactor: extraire OrderValidator de OrderService

Note : Évitez les messages flous comme "update" ou "fix bug". Quelques mois après, en regardant git log, vous ne saurez pas ce que ce commit a corrigé.

2.3. Commiter par parties avec git add -p

Quand un fichier contient plusieurs modifications sans lien entre elles, ne commitez pas tout d’un coup.

git add -p src/main/java/com/nonladev/OrderService.java

Git vous demandera quel « hunk » (morceau de changement) vous voulez stager, ce qui vous permet de séparer en commits plus petits, plus faciles à review et à revert si nécessaire.

3. Branch – travailler indépendamment sur plusieurs branches

3.1. Créer et basculer vers une branche

Une branche n’est qu’un pointeur léger dirigé vers un commit spécifique. Créer une nouvelle branche ne copie pas tout le code, cela crée simplement un nouveau pointeur.

# Créer une nouvelle branche et basculer dessus
git branch feature/add-payment
git checkout feature/add-payment

# Ou fusionner les deux commandes ci-dessus en une (Git ≥ 2.23)
git switch -c feature/add-payment

Explication :

  • git branch <nom> crée une branche mais reste sur la branche actuelle.
  • git checkout <nom> ou git switch <nom> déplace HEAD vers cette branche.
  • L’option -c dans git switch est un raccourci pour créer et basculer en même temps.

3.2. Voir la liste des branches

git branch -v
* feature/add-payment  d4e5f6a ajouter PaymentService skeleton
  main                 a1b2c3d docs: ajouter README pour Order Service

L’astérisque * indique la branche actuelle (celle vers laquelle HEAD pointe).

3.3. Utiliser une convention de noms pour les branches

Donner des noms de branche clairs aide toute l’équipe à comprendre ce que chaque branche fait :

feature/add-payment       # ajouter une nouvelle fonctionnalité
fix/order-total-wrong     # corriger un bug
refactor/extract-validator
hotfix/prod-null-pointer

3.4. Supprimer une branche après la terminer

# Supprimer une branche locale déjà fusionnée
git branch -d feature/add-payment

# Supprimer une branche non fusionnée (suppression forcée, attention à ne pas perdre de commits)
git branch -D feature/add-payment

-d (minuscule) est plus sûr car Git vous avertira si la branche n’a pas encore été fusionnée nulle part.

4. Merge – fusionner les branches

4.1. Fast-forward merge

Si main n’a aucun nouveau commit depuis que vous avez créé la branche, Git déplace simplement le pointeur main vers le dernier commit de la branche feature — c’est ce qu’on appelle un fast-forward.

git switch main
git merge feature/add-payment
Updating a1b2c3d..d4e5f6a
Fast-forward
 PaymentService.java | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

Aucun commit de fusion n’est créé, l’historique reste une ligne droite.

4.2. Merge commit (3-way merge)

Si les deux branches ont des commits distincts depuis le point de divergence, Git crée un commit de fusion avec deux parents, fusionnant le contenu des deux branches.

git switch main
git commit -am "fix: corriger la validation du nombre d'éléments"   # main a un nouveau commit
git switch feature/add-payment
git switch main
git merge feature/add-payment
Merge made by the 'recursive' strategy.
 PaymentService.java | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

Git crée automatiquement un commit de fusion avec le message par défaut Merge branch 'feature/add-payment'.

4.3. Gérer les conflits lors d’une fusion

Un conflit survient quand deux branches modifient la même région de code. Exemple avec la classe OrderService :

// Dans main
public class OrderService {
    public BigDecimal calculateTotal(Order order) {
        return order.getSubtotal().multiply(BigDecimal.valueOf(1.1)); // ajouter 10% de taxe
    }
}
// Dans feature/add-payment
public class OrderService {
    public BigDecimal calculateTotal(Order order) {
        return order.getSubtotal().subtract(order.getDiscount()); // soustraire la réduction
    }
}

Lors de la fusion, Git ne peut pas décider quelle ligne garder, et le fichier affichera un marqueur :

public class OrderService {
    public BigDecimal calculateTotal(Order order) {
<<<<<<< HEAD
        return order.getSubtotal().multiply(BigDecimal.valueOf(1.1));
=======
        return order.getSubtotal().subtract(order.getDiscount());
>>>>>>> feature/add-payment
    }
}

Vous devez éditer manuellement pour combiner correctement la logique (par exemple : soustraire la réduction d’abord, puis ajouter la taxe), supprimer les marqueurs <<<<<<<, =======, >>>>>>>, puis :

git add OrderService.java
git commit

Note : Quand vous commitez après avoir résolu un conflit, pas besoin de -m — Git ouvrira l’éditeur avec un message par défaut indiquant clairement qu’il s’agit d’un commit de fusion. Gardez ce message tel quel, à moins que vous ne vouliez ajouter une note.

4.4. Merge vs Rebase

Critère merge rebase
Historique Préservé, crée un commit de fusion Réécrit, branche aplatie
Sécurité N’altère pas les anciens commits Altère les anciens commits (change les hashs)
Utilisation Branches publiques partagées (main, develop) Branches personnelles, pour un historique propre avant créer une PR
# Rebase la branche feature sur le dernier main
git switch feature/add-payment
git rebase main

Note : Ne faites pas de rebase sur une branche que vous avez déjà poussée et que d’autres tirent — vous allez distordre leur historique et causer des conflits massifs à la prochaine fois qu’ils feront pull.

5. Tester un scénario de fusion avec JUnit

Bien que Git ne soit pas du code Java, vous pouvez tout à fait écrire des tests pour vérifier que la logique métier ne casse pas après la fusion de deux branches — c’est pourquoi les CI exécutent toujours les tests sur les commits de fusion. Voici un exemple de OrderService après avoir résolu le conflit à la section 4.3 :

public class OrderService {

    public BigDecimal calculateTotal(Order order) {
        BigDecimal afterDiscount = order.getSubtotal().subtract(order.getDiscount());
        return afterDiscount.multiply(BigDecimal.valueOf(1.1));
    }
}
class OrderServiceTest {

    private final OrderService orderService = new OrderService();

    @Test
    void shouldApplyDiscountThenTax() {
        Order order = new Order(
                BigDecimal.valueOf(100),  // sous-total
                BigDecimal.valueOf(10)    // réduction
        );

        BigDecimal total = orderService.calculateTotal(order);

        assertThat(total).isEqualByComparingTo(BigDecimal.valueOf(99.0));
    }
}

Explication : sous-total 100 moins réduction 10 = 90, multiplié par la taxe de 10% = 99. Ce test est le « filet de sécurité » assurant que la fusion des deux logiques (taxe et réduction) ne s’annulent pas mutuellement.

✓ shouldApplyDiscountThenTax()

6. Flux de travail recommandé pour une petite équipe

  1. Créez toujours une nouvelle branche à partir de main pour chaque tâche : git switch -c feature/xxx.
  2. Des commits petits, fréquents, avec des messages clairs.
  3. Avant de créer une Pull Request, mettez à jour votre branche avec le dernier main :
    git switch feature/add-payment
    git fetch origin
    git merge origin/main
  4. Résolvez les conflits (s’il y en a) sur votre machine locale, relancez les tests.
  5. Poussez et créez une PR, laissez la CI exécuter les tests sur le commit de fusion avant la fusion dans main.

Note : Ne commitez pas directement sur main quand vous travaillez en équipe — passez toujours par une branche + PR pour avoir une review et une vérification CI avant.

7. Erreurs courantes

  • Oublier git add avant de commiter : Git affiche nothing to commit, working tree clean bien que vous ayez modifié des fichiers — c’est parce que les fichiers n’ont pas été stagés.
  • Se tromper de branche à fusionner : vérifiez toujours avec git status et git branch où vous êtes avant de faire merge.
  • Marqueurs de conflit oubliés dans le code : oublier de supprimer <<<<<<< empêchera le code de compiler — buildez/testez toujours immédiatement après avoir résolu le conflit.
  • Push forcé sur une branche partagée : git push --force après un rebase sur une branche déjà partagée supprimera l’historique de vos collègues. N’utilisez --force-with-lease que sur les branches personnelles.

8. Conclusion

Commit, branch et merge sont les trois opérations que vous utilisez quotidiennement avec Git. Maîtriser les commits garde votre historique de code clair, les branches vous permettent de travailler en parallèle sans vous affecter mutuellement, et comprendre le merge (et comment résoudre les conflits) vous donne confiance en fusionnant le code de plusieurs personnes. Pratiquez les commits petits, utilisez des noms de branche conventionnels, et exécutez toujours les tests avant de fusionner vers main.

gitversion-controlbranchmergedevops