Aller au contenu principal
Bonnes pratiques Git · 05

Éviter
les conflits

Un conflit n’est pas une erreur : c’est Git qui demande de choisir. Bien organisé, on en rencontre peu, et on les résout vite.

Objectif

Mettre sa branche à jour sans abîmer l’historique, et résoudre un conflit sans perdre le travail de personne.

D’où vient un conflit​

Pour fusionner deux branches, Git compare chacune à leur ancêtre commun. Une ligne modifiée d’un seul côté est reprise automatiquement. Une ligne modifiée des deux côtés produit un conflit : Git ne sait pas quelle version garder.

Plus une branche vit longtemps et touche de fichiers partagés, plus le risque augmente.

Prévenir​

  1. Des branches courtes, qui partent du dernier origin/develop.
  2. Se mettre à jour chaque jour, plutôt qu’une seule fois avant la merge request.
  3. Des commits et des MR ciblés : moins de fichiers touchés, moins de conflits possibles.
  4. Un formateur commun (Prettier côté Angular, Spotless côté Java), lancé par toute l’équipe. Un reformatage massif mérite sa propre MR, annoncée à l’équipe.
  5. Prévenir l’équipe avant de modifier un fichier très partagé : pom.xml, package.json, routes Angular, configuration Spring.
  6. Des migrations de base horodatées (Flyway, Liquibase) plutôt que numérotées à la suite, pour éviter que deux branches créent la même version.
  7. Un .gitattributes qui fixe les fins de ligne (voir Configuration du poste) : sinon, des fichiers entiers apparaissent modifiés.

Se mettre à jour correctement​

La procédure de référence, sur sa branche de travail :

git switch feature/CMA-142-export-ordonnances
git fetch origin
git rebase --autostash origin/develop
# en cas de conflit : résoudre, git add, git rebase --continue
./mvnw verify # ou npm test : retester AVANT de pousser
git push --force-with-lease

:::warning Rebaser sur origin/develop, pas sur develop git fetch met à jour origin/develop, pas votre branche locale develop. Un git rebase develop après un simple fetch rebase sur une base périmée, et les conflits réapparaissent dans la merge request. :::

  • --autostash met de côté le travail non commité puis le restaure, sans git stash manuel.
  • --force-with-lease est nécessaire parce que le rebase a créé de nouveaux commits. Il refuse d’écraser un travail poussé entre-temps par quelqu’un d’autre.

Si la branche est partagée par plusieurs développeurs, ne la rebasez pas : fusionnez la cible dedans, ce qui ne réécrit rien.

git fetch origin
git merge origin/develop

:::danger Ne jamais pousser --force sur une branche commune Sur develop, recette, main, preprod ou prod, un force push efface le travail des autres et désynchronise ArgoCD. Ces branches sont protégées pour l’empêcher. :::

Résoudre un conflit pas à pas​

1. Identifier les fichiers en conflit

git status
# both modified: src/app/vente/vente.service.ts

2. Lire les marqueurs. Avec merge.conflictStyle zdiff3, Git affiche aussi la version de l’ancêtre commun, ce qui aide à comprendre ce que chaque côté a voulu faire :

<<<<<<< HEAD
const total = arrondir(sousTotal * (1 + tva), 0);
||||||| ancêtre commun
const total = sousTotal * (1 + tva);
=======
const total = sousTotal * (1 + tva) - remise;
>>>>>>> fix(vente): appliquer la remise fidélité

3. Écrire la bonne version, qui combine souvent les deux intentions, puis supprimer les marqueurs :

const total = arrondir(sousTotal * (1 + tva) - remise, 0);

4. Marquer le fichier comme résolu et continuer

git add src/app/vente/vente.service.ts
git rebase --continue # ou git merge --continue

5. Retester. Un code qui a fusionné sans conflit textuel peut quand même ne plus compiler ou ne plus fonctionner.

Les éditeurs d’IntelliJ IDEA et de VS Code proposent une vue à trois colonnes très pratique, accessible aussi par git mergetool.

:::warning --ours et --theirs s’inversent pendant un rebase Pendant un merge, --ours désigne votre branche. Pendant un rebase, c’est l’inverse : --ours désigne la branche sur laquelle vous rejouez vos commits (origin/develop) et --theirs vos propres commits. :::

Cas particuliers​

FichierRésolution
package-lock.jsonReprendre la version de la cible, puis npm install pour réintégrer vos dépendances
Migration de base en doubleRenommer votre migration avec un horodatage postérieur, ne jamais modifier une migration déjà appliquée
Fichier généréRégénérer à partir de la source, ne pas l’éditer à la main
Conflit répété à chaque rebaseActiver rerere : Git rejoue automatiquement la résolution déjà faite

Abandonner ou annuler​

Rien n’est perdu tant qu’on ne panique pas :

git rebase --abort # annule le rebase en cours, retour à l'état de départ
git merge --abort # annule le merge en cours

git reset --merge ORIG_HEAD # annule un git pull (merge) terminé par erreur

Pour retrouver un travail après une fausse manœuvre, voir la section Récupérer un travail « perdu » de Git avancé.