Git avancé : fusion et conflits
Les types de fusion
Section intitulée « Les types de fusion »git switch maingit merge fonctionnalite| Situation | Résultat |
|---|---|
main n’a pas avancé depuis la création de la branche |
Fast-forward : main avance simplement, aucun commit de fusion |
| Les deux branches ont avancé | Commit de fusion (deux parents) |
Fast-forward : Fusion à trois voies :A---B main A---B---C main A---B---C---M main \ \ \ / D---E fonctionnalite D---E fonctionnalite D-----EContrôler le comportement
Section intitulée « Contrôler le comportement »git merge --ff-only fonctionnalite # refuse si un commit de fusion serait nécessairegit merge --no-ff fonctionnalite # force un commit de fusion (garde la trace de la branche)git merge --squash fonctionnalite # applique tout en UN seul commit (à valider vous-même)git merge --no-commit fonctionnalite # fusionne mais n'enregistre pas (pour vérifier)git merge --abort # annule une fusion en cours| Option | Quand |
|---|---|
--ff-only |
Garder un historique linéaire ; échoue si un rebase est nécessaire |
--no-ff |
Conserver la notion de branche (une fonctionnalité = un commit de fusion) |
--squash |
Réduire une branche de travail à un commit propre sur main |
Réglage par défaut : git config --global merge.ff only (ou pull.ff only) pour éviter les fusions accidentelles lors d’un git pull.
Stratégies de fusion et d’équipe
Section intitulée « Stratégies de fusion et d’équipe »| Stratégie | Historique de main |
Pour qui |
|---|---|---|
Fusion --no-ff |
Commits de fusion, branches visibles | Branches de fonctionnalité nombreuses, historique fidèle |
| Squash | Un commit par fonctionnalité | Commits intermédiaires peu soignés |
| Rebase + fast-forward | Linéaire, tous les commits | Commits atomiques et propres (Rebase et historique) |
Les plateformes (Gitea, GitHub, GitLab) proposent ces trois modes à la fusion d’une demande : choisir un mode pour le projet et le documenter (voir Gitea : dépôts, accès et API).
Comprendre un conflit
Section intitulée « Comprendre un conflit »Un conflit survient quand deux branches modifient les mêmes lignes (ou qu’une branche supprime ce que l’autre modifie). Git ne sait pas arbitrer et s’arrête :
Auto-merging config.jsCONFLICT (content): Merge conflict in config.jsAutomatic merge failed; fix conflicts and then commit the result.Dans le fichier :
const port = 3000;<<<<<<< HEADconst hote = '127.0.0.1';=======const hote = '0.0.0.0';>>>>>>> fonctionnalite| Marqueur | Contenu |
|---|---|
<<<<<<< HEAD à ======= |
Version de la branche courante (main) |
======= à >>>>>>> |
Version de la branche fusionnée |
Résoudre
Section intitulée « Résoudre »- Ouvrir le fichier, choisir (ou combiner) le contenu voulu et supprimer les marqueurs.
git add config.jsgit commit(le message de fusion est pré-rempli) — ougit rebase --continuependant un rebase.
git status # liste « both modified »git diff --name-only --diff-filter=U # fichiers non résolusgit checkout --ours config.js # garder la version de la branche courante (pendant un merge)git checkout --theirs config.js # garder la version de l'autre branchegit add config.jsAfficher le contexte : diff3 et zdiff3
Section intitulée « Afficher le contexte : diff3 et zdiff3 »git config --global merge.conflictStyle zdiff3 # (ou diff3 sur une version plus ancienne)<<<<<<< HEADconst hote = '127.0.0.1';||||||| ancêtre communconst hote = 'localhost';=======const hote = '0.0.0.0';>>>>>>> fonctionnaliteLe bloc ||||||| montre la version de l’ancêtre commun : on voit ce que chaque côté a changé, ce qui rend l’arbitrage bien plus facile. zdiff3 réduit en plus les lignes communes en début et fin de bloc. Recommandé.
Outils de fusion
Section intitulée « Outils de fusion »git mergetool # lance l'outil configuré pour chaque fichier en conflitgit config --global merge.tool vimdiff # ou meld, kdiff3, code (VS Code)git config --global mergetool.keepBackup false| Outil | Remarque |
|---|---|
vimdiff |
Dans le terminal, déjà présent avec Vim |
meld |
Graphique (sudo apt install meld) |
| VS Code | Éditeur de fusion à trois colonnes intégré |
git diff --diff-filter=U |
Lire les conflits en ligne de commande |
Réduire les conflits
Section intitulée « Réduire les conflits »| Pratique | Effet |
|---|---|
| Branches courtes | Moins de divergence |
Rebaser régulièrement sur main |
Les conflits apparaissent tôt, par petits morceaux |
| Commits petits et ciblés | Conflits plus lisibles |
| Ne pas reformater un fichier entier dans un commit mêlé à d’autres changements | Évite des conflits sur tout le fichier |
| Formateur automatique commun (Prettier) | Plus de conflits de style |
| Séparer les fichiers à fort trafic | Moins de modifications concurrentes |
| Se coordonner sur les fichiers partagés (verrouillage de dépendances) | Voir plus bas |
Fichiers générés : fichiers de verrouillage
Section intitulée « Fichiers générés : fichiers de verrouillage »Un conflit dans package-lock.json ou pnpm-lock.yaml ne se résout pas à la main : accepter la version de main (ou de l’une des branches), puis régénérer :
git checkout --theirs package-lock.json # ou --ours, selon le sensnpm install # recalcule le fichier de verrouillagegit add package-lock.jsonPour qu’un fichier généré ne produise jamais de marqueurs de conflit illisibles, .gitattributes peut indiquer une stratégie de fusion :
package-lock.json merge=oursCHANGELOG.md merge=union*.png binarymerge=union conserve les lignes des deux côtés (utile pour un journal de modifications) ; merge=ours exige de définir le pilote : git config merge.ours.driver true. À utiliser avec discernement.
rerere : mémoriser les résolutions
Section intitulée « rerere : mémoriser les résolutions »git config --global rerere.enabled trueReuse recorded resolution : Git enregistre la façon dont on a résolu un conflit et la réapplique automatiquement si le même conflit se reproduit (rebase répété, fusion annulée puis refaite). Très utile quand on rebase souvent une longue branche.
git rerere statusgit rerere diffgit rerere forget fichier # oublier une résolution mauvaiseAnnuler une fusion
Section intitulée « Annuler une fusion »| Situation | Commande |
|---|---|
| Fusion en cours (conflits) | git merge --abort |
| Fusion faite mais non poussée | git reset --hard ORIG_HEAD (ou HEAD~1) |
| Fusion déjà poussée | git revert -m 1 <commit-de-fusion> |
git revert -m 1 a1b2c3d # -m 1 : garder le parent 1 (la branche principale) comme référenceFusionner depuis un autre dépôt
Section intitulée « Fusionner depuis un autre dépôt »git remote add collegue https://git.exemple.com/collegue/projet.gitgit fetch colleguegit merge collegue/fonctionnalitegit merge --allow-unrelated-histories collegue/main # historiques sans ancêtre commun (fusion de deux dépôts)Comprendre ce qui va être fusionné
Section intitulée « Comprendre ce qui va être fusionné »git log --oneline --graph --all --decorate -20git log main..fonctionnalite --oneline # commits de la branche absents de maingit diff main...fonctionnalite # changements de la branche depuis l'ancêtre commungit merge-base main fonctionnalite # ancêtre commungit range-diff main@{1} main fonctionnalite # comparer deux versions d'une branche réécritegit merge --no-commit --no-ff fonctionnalite # simuler : inspecter avant d'enregistrergit merge --abortgit diff A...B (trois points) compare B à l’ancêtre commun : exactement ce qu’apporterait la fusion.
Dépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
error: Your local changes … would be overwritten by merge |
Valider ou mettre de côté (git stash) avant de fusionner |
fatal: refusing to merge unrelated histories |
Dépôts sans ancêtre commun : --allow-unrelated-histories (si c’est voulu) |
Not possible to fast-forward, aborting |
--ff-only : rebaser d’abord ou accepter une fusion |
Marqueurs <<<<<<< restés dans le code |
Résolution oubliée : grep -rn '<<<<<<<' . ; hook de commit pour les refuser |
| Conflit sur un fichier binaire | Choisir une version : git checkout --ours fichier ou --theirs |
| Le même conflit revient sans cesse | Activer rerere, rebaser plus souvent |
| Fusion qui « perd » des modifications | Résolution incorrecte ; git log -p --follow fichier, git diff ORIG_HEAD |
git merge reporte « Already up to date » |
Les commits sont déjà inclus dans la branche courante |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Aucune propre à Ubuntu. zdiff3 demande Git 2.35 ou plus : disponible sur les deux versions d’Ubuntu (git --version).