Le choix entre git rebase et git merge fait l'objet de bien plus de débats que de réelle compréhension dans la plupart des équipes. Les deux combinent le travail de deux branches ; la différence tient à l'apparence de l'historique ensuite et à la façon dont vous gérez les conflits. Cet article explique ce que fait réellement chaque commande, quand choisir l'une ou l'autre, et les accidents classiques que rencontrent les équipes, le tout avec des exemples concrets.
Les deux font le même travail, mais écrivent l'historique différemment
Imaginons que vous ayez créé la branche feature à partir de main, et que pendant votre travail, main ait avancé. Vous voulez maintenant récupérer les nouveaux commits de main dans votre branche. Il y a deux façons de faire.
Merge prend les pointes des deux branches et crée un nouveau commit de fusion (merge commit). Il ne touche pas à vos commits et relie simplement les deux historiques en un seul point :
git checkout feature
git merge main
Rebase, lui, met temporairement vos commits de côté, déplace le point de départ de votre branche vers le dernier état de main, puis réapplique vos commits un par un au-dessus de cette nouvelle base :
git checkout feature
git rebase main
Résultat : après un merge l'historique se ramifie puis se rejoint, tandis qu'après un rebase il ressemble à une ligne droite, comme si vous aviez travaillé sur le main actuel depuis le début.
Rebase copie les commits, il ne les déplace pas
Voici le point essentiel à comprendre : rebase ne déplace pas vos commits, il les copie. Chaque commit réappliqué reçoit un nouveau hash, car même si son contenu est identique, il est désormais rattaché à un parent différent. Les anciens commits subsistent un moment dans votre reflog, mais votre branche pointe maintenant vers les nouvelles copies.
La conséquence pratique est importante : rebase réécrit l'historique. Sur une branche locale que vous n'avez partagée avec personne, c'est parfaitement sûr. Mais si vous le faites sur une branche partagée que d'autres ont récupérée, l'historique de chacun cesse de correspondre au vôtre. D'où la règle la plus importante.
La règle d'or : ne jamais rebaser un historique partagé
Si vous ne retenez qu'une seule règle, vous éviterez l'essentiel des ennuis :
- Rebasez librement les branches qui n'appartiennent qu'à vous, pas encore poussées, ou sur lesquelles personne n'a basé de travail.
- Ne rebasez jamais les branches partagées par tous, comme
mainoudevelop.
Si vous avez rebasé une branche de fonctionnalité déjà poussée, vous devrez forcer le push pour mettre à jour le dépôt distant. Ici, au lieu d'un --force brut, préférez toujours la variante plus sûre :
git push --force-with-lease
--force-with-lease refuse le push si la branche distante diffère de ce que vous aviez vu en dernier (donc si quelqu'un a poussé entre-temps), pour ne pas écraser silencieusement le travail d'un collègue. Un simple --force n'offre pas cette protection.
Conflits : tout d'un coup, ou étape par étape
La gestion des conflits diffère nettement entre les deux. Avec merge, tous les changements se rencontrent en un seul point ; vous résolvez les conflits une fois, lancez git add, et terminez la fusion.
Avec rebase, comme vos commits sont réappliqués en séquence, les conflits peuvent surgir à chaque commit séparément. Si plusieurs de vos commits touchent la même ligne, vous devrez peut-être résoudre un conflit similaire plusieurs fois. Le flux fonctionne ainsi :
# corrigez les fichiers en conflit, puis :
git add <fichier>
git rebase --continue
# pour ignorer ce commit :
git rebase --skip
# pour tout annuler et revenir en arrière :
git rebase --abort
Si la situation se complique, git rebase --abort vous ramène exactement à l'état d'avant le rebase ; ainsi, quand vous êtes bloqué, vous pouvez annuler calmement et réessayer plutôt que paniquer.
Nettoyer l'historique avec le rebase interactif
L'usage le plus puissant du rebase n'est pas de combiner des branches mais d'éditer votre propre historique de commits. Avec git rebase -i (interactif), vous pouvez fusionner, réordonner ou corriger les messages des commits avant de pousser :
git rebase -i HEAD~4
Dans l'éditeur qui s'ouvre, vous choisissez une commande pour chaque commit : pick (garder tel quel), squash (fusionner avec le précédent), reword (changer le message), drop (supprimer). Vous condensez ainsi un fouillis de commits « correction », « encore une correction », « typo » en un seul commit cohérent, et rendez la pull request digne d'être relue. Là encore, faites-le uniquement sur des commits que vous n'avez pas encore partagés.
Une stratégie d'équipe concrète
Pour la plupart des équipes, une approche hybride fonctionne bien :
- Pour garder votre branche de fonctionnalité à jour, rebasez
maindans votre branche (git rebase main) afin que l'historique reste plat et que des merge commits inutiles ne s'accumulent pas. - Pour intégrer la branche dans
main, utilisez merge, souvent avec--no-ff, pour que « cette fonctionnalité a été intégrée ici » reste clairement consigné dans un seul merge commit. - Avant d'ouvrir la pull request, rangez vos commits avec
git rebase -i.
Beaucoup d'équipes intègrent aussi les PR via « squash and merge » : toute la fonctionnalité arrive sur main en un seul commit. L'historique de main reste alors extrêmement lisible, chaque ligne correspondant à une fonctionnalité. Quelle que soit la stratégie choisie, l'essentiel est d'être cohérent en équipe ; une équipe où la moitié rebase et l'autre fusionne finit avec le pire historique possible.
Questions fréquentes
Le rebase est-il plus dangereux que le merge ?
Il n'est pas dangereux, il a simplement des règles parce qu'il réécrit l'historique. Sur des branches locales et non partagées, le rebase est parfaitement sûr et garde l'historique propre. Le seul vrai danger est de rebaser et forcer le push d'une branche partagée utilisée par d'autres ; tant que vous ne brisez pas cette règle, vous pouvez l'utiliser librement.
Comment annuler un rebase si un conflit apparaît ?
La commande git rebase --abort vous ramène exactement à l'état d'avant le début du rebase. Vous ne perdez aucun commit, et abandonner pour continuer avec un merge est un choix tout à fait valable quand vous êtes perdu.
Que faire si je casse mon historique par accident ?
git reflog est votre filet de sécurité. Git conserve un moment chaque ancienne position pointée par votre branche ; trouvez le bon hash dans le reflog et revenez à ce moment avec git reset --hard <hash>. C'est pourquoi « réécrire l'historique » est bien moins définitif qu'il n'y paraît.
Vous voulez simplifier le flux Git de votre équipe ? Mettons en place ensemble des stratégies de branches sensées, une intégration CI/CD et un historique de commits propre. Contactez-moi et nous concevrons un workflow adapté à votre projet.