Deux opérations sur le même graphe de départ
Git merge ou rebase, c’est choisir entre deux façons de faire la même chose : intégrer le travail d’une branche dans une autre. La comparaison d’Atlassian le dit clairement ; la différence tient à la façon dont chacun l’enregistre.
Le dépôt est un dépôt jetable nommé orbit-api. La branche feature/search porte deux commits, et main a avancé avec un commit de son côté :
git log --oneline --graph --decorate --all
* 1ca9117 (HEAD -> main) docs: list the health route
| * aa029c4 (feature/search) feat: add search filters
| * 47fee04 feat: add search endpoint
|/
* b65211d chore: init orbit-api
J’ai copié le dossier pour que chaque commande parte de cet état. Vos identifiants seront différents.
Ce que merge conserve et ce que rebase réécrit
Dans la première copie, fusionnez feature/search dans main. Un fast-forward (avance rapide) est impossible, car main a un commit que la branche de fonctionnalité n’a pas, et --ff-only le confirme :
git merge --ff-only feature/search
fatal: Not possible to fast-forward, aborting.
Un merge simple crée un commit de fusion (j’ai ajouté --no-edit pour accepter le message proposé) :
git merge --no-edit feature/search
git log --oneline --graph --decorate
* 6f9924d (HEAD -> main) Merge branch 'feature/search'
|\
| * aa029c4 (feature/search) feat: add search filters
| * 47fee04 feat: add search endpoint
* | 1ca9117 docs: list the health route
|/
* b65211d chore: init orbit-api
Rien n’a été réécrit : aa029c4 et 47fee04 gardent leurs identifiants, et 6f9924d a deux parents. L’historique garde la trace de la jonction.
Dans la seconde copie, rebasez la branche de fonctionnalité sur main :
git checkout feature/search
git rebase main
git log --oneline --graph --decorate
* 4818ca2 (HEAD -> feature/search) feat: add search filters
* eac743c feat: add search endpoint
* 1ca9117 (main) docs: list the health route
* b65211d chore: init orbit-api
L’historique est une ligne droite, mais les deux commits de la fonctionnalité sont nouveaux : 47fee04 est devenu eac743c et aa029c4 est devenu 4818ca2. Git les a rejoués au-dessus de main. Comme main n’a pas bougé depuis, intégrer la branche est maintenant un fast-forward : main avance simplement jusqu’à 4818ca2, sans nouveau commit.
git checkout main
git merge --ff-only feature/search
Updating 1ca9117..4818ca2
Fast-forward
…
Pour conserver un commit de fusion même quand un fast-forward est possible, --no-ff en force un. Dans une troisième copie, prise juste après le rebase, git merge --no-edit --no-ff feature/search lancé depuis main donne ceci :
* 2d2cbf4 (HEAD -> main) Merge branch 'feature/search'
|\
| * 4818ca2 (feature/search) feat: add search filters
| * eac743c feat: add search endpoint
|/
* 1ca9117 docs: list the health route
* b65211d chore: init orbit-api
Dans les trois copies, git rev-parse main^{tree} a affiché c13fa637a85f3cb7cf1f47de226e6808b388e8b5. Les fichiers sont identiques ; seul l’historique diffère.
Choisir selon la branche et les collaborateurs
La règle qui compte est la « règle d’or du rebase » de la comparaison d’Atlassian : ne rebasez jamais une branche publique. Un rebase donne de nouveaux identifiants à vos commits ; ceux qui ont déjà les anciens se retrouvent avec une copie divergente. Pro Git déconseille d’inclure des commits déjà poussés sur un serveur central, et la documentation de git-rebase ajoute que réécrire une branche sur laquelle d’autres ont travaillé les oblige à corriger leur historique à la main.
| Situation | À préférer | Pourquoi |
|---|---|---|
| Une branche de fonctionnalité que vous seul utilisez | Rebase, puis fast-forward | Historique linéaire, sans commit de fusion |
| Une branche que des collègues ont tirée | Merge | Les commits existants gardent leurs identifiants |
main ou une autre branche partagée |
Merge, jamais rebase | D’autres en dépendent déjà |
| Vous voulez dater l’intégration d’une fonctionnalité | Merge avec --no-ff |
Le commit de fusion marque le moment |
Atlassian nomme le compromis : un rebase donne un historique plus propre mais perd le contexte qu’apporte un commit de fusion. Suivez la convention de votre équipe, et rangez vos propres commits avant l’une ou l’autre opération avec le rebase interactif.
Dans GitMini, vous faites un clic droit sur une branche autre que la branche courante :
- Avec
maincomme branche courante, Merge “feature/search” in “main” ouvre une boîte Merge. Elle propose Fast forward if possible, Always create a merge commit et Fast-forward only, désactivé avec « impossible: branches have diverged » quand le fast-forward n’est pas possible. - Avec
feature/searchcomme branche courante, Rebase feature/search on main ouvre une boîte Rebase qui indique « 2 commits of “feature/search” will be replayed on “main” (1ca9117) ».
Glisser une branche sur une autre dans le graphe ouvre un petit menu : le rebase, libellé « feature/search on main », et Merge feature/search in main quand la cible est la branche courante.
La boîte de rebase affiche des avertissements. Un commit déjà présent sur un dépôt distant porte Already pushed, avec « A force push (with lease) will be required and may affect your collaborators. » Une plage qui contient des commits de fusion affiche « 1 merge commit will be linearized. » Rebaser main ou master exige de cocher « I want to rebase the default branch “main” ».
Vérifier les conflits et l’historique final
Les conflits se comportent différemment. Dans une variante où main et la branche de fonctionnalité modifient la même ligne de README.md, un merge s’arrête une seule fois :
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
Un rebase de la même branche s’arrête sur le premier commit qui entre en conflit, ici le premier des deux :
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: could not apply 7e264ce... docs: announce the search route
…
Sorties capturées avec Git 2.30 ; Git 2.54 ajoute un bloc hint: au refus de --ff-only et préfixe les conseils de conflit par hint:. Un rebase rejoue les commits un par un et peut donc s’arrêter de nouveau plus loin : quand les deux commits de la fonctionnalité modifiaient cette même ligne, il s’est arrêté une seconde fois, sur le deuxième commit, après la résolution du premier arrêt. Dans les deux cas, git merge --abort ou git rebase --abort ramène à l’état d’avant la commande.
GitMini montre le même arrêt dans un bandeau. Pour un merge, il affiche « Merge feature/search into main » avec le nombre de fichiers en conflit, et Finish the merge devient actif une fois les fichiers indexés. Pour un rebase, il propose Continue, Skip et Abort. Le guide du rebase interactif détaille ce cas.
Terminez en lisant le résultat : git log --oneline --graph --decorate --all montre une boucle après le merge, une ligne après le rebase. Le guide de lecture du graphe explique comment le suivre.
Sources
- Atlassian : comparaison entre un merge et un rebase et version anglaise, consultées le 4 octobre 2026.
- Pro Git : Réécrire l’historique (édition française) et Rewriting History (édition anglaise), consultés le 4 octobre 2026.
- Documentation Git : git-merge (en anglais), consulté le 4 octobre 2026.
- Documentation Git : git-rebase (en anglais), consulté le 4 octobre 2026.
- README du produit GitMini, consulté le 4 octobre 2026.