Développement Web

Rebase vs merge : un historique Git propre

13 دقائق للقراءة
📍 À lire d’abord : Maîtriser Git et GitHub — le guide complet. Ce tutoriel compare deux façons d’intégrer du travail : la fusion et le rebasage.

Ouvrez l’historique d’un vieux projet d’équipe et vous verrez parfois un enchevêtrement de commits de fusion impossible à suivre. Ouvrez celui d’une équipe disciplinée : une ligne droite, lisible commit après commit. La différence tient souvent à un seul choix d’outil — merge ou rebase. Tous deux intègrent le travail d’une branche dans une autre, mais ils racontent l’histoire différemment. À la fin de ce tutoriel, vous saurez rebaser une branche, nettoyer vos commits avec le rebasage interactif, et — surtout — vous connaîtrez la règle d’or qui sépare un usage sûr d’une catastrophe pour votre équipe.

🎯 Ce que vous allez apprendre

  • Comprendre ce que git rebase fait réellement aux commits.
  • Rebaser une branche de fonctionnalité au-dessus de main.
  • Appliquer la règle d’or : ne jamais rebaser un historique déjà partagé.
  • Nettoyer une série de commits avec le rebasage interactif (squash, reword, drop).
  • Choisir entre merge et rebase selon le contexte.
  • Annuler un rebasage qui tourne mal.

🛠️ Ce que vous allez construire

Sur le dépôt boutique-inventaire, vous développerez une fonctionnalité de filtres sur plusieurs commits « brouillons », puis vous les transformerez en un historique impeccable : d’abord en rebasant la branche sur une main qui a avancé, ensuite en condensant vos commits désordonnés en une suite claire, prête à être relue par un collègue.

Prérequis

  • Être à l’aise avec les branches et la fusion. Au besoin, revoir Branches Git et fusion.
  • Un dépôt avec une branche main et de quoi créer une branche de fonctionnalité.
  • Niveau intermédiaire. ⏱️ Temps estimé : environ 50 minutes.

Étape 1 — Le problème : deux manières de réunir des branches

Quand votre branche de fonctionnalité et main ont divergé, il faut les réunir. La fusion (merge) crée un commit de fusion qui relie les deux histoires : elle préserve fidèlement ce qui s’est passé, mais multiplie les bifurcations dans le graphe. Le rebasage (rebase) adopte la stratégie inverse : il rejoue vos commits, un par un, au-dessus du dernier commit de main, comme si vous aviez commencé votre travail à partir de là. Résultat : une ligne droite, sans commit de fusion.

Aucune des deux approches n’est « meilleure » dans l’absolu. La fusion dit la vérité historique ; le rebasage raconte une histoire plus lisible. Le bon réflexe consiste à savoir ce que chacune fait vraiment sous le capot — c’est l’objet des étapes suivantes — pour choisir en connaissance de cause.

Étape 2 — Ce que rebase fait réellement

Point crucial, source de toutes les confusions : le rebasage ne déplace pas vos commits, il en crée de nouveaux. Chaque commit rejoué reçoit un nouvel identifiant SHA, car son parent a changé. Les commits d’origine, eux, ne sont plus référencés par la branche et seront nettoyés à terme. Autrement dit, rebaser, c’est réécrire l’historique de la branche.

Mettons-le en pratique. Créons une fonctionnalité pendant que main avance de son côté, puis rebasons.

git switch -c feature/filtres
echo "function filtrer(stock, critere) { return stock.filter(critere); }" > filtres.js
git add filtres.js
git commit -m "Ajoute le filtrage du stock"
git switch main
echo "// maj entête" >> README.md
git add README.md
git commit -m "Met à jour l'entête du projet"
git switch feature/filtres
git rebase main

Git annonce Successfully rebased and updated refs/heads/feature/filtres. Votre commit de filtrage est maintenant posé au-dessus du commit d’entête de main, alors qu’il avait été écrit avant lui. Vérifiez avec git log --oneline --graph --all : l’historique de la branche est linéaire, sans commit de fusion. La fonctionnalité semble avoir été développée à partir de la version la plus récente de main.

Point d’étape — Après le rebasage, git log --oneline sur feature/filtres montre le commit d’entête de main avant votre commit de filtrage. Si un conflit survient pendant le rebasage, c’est normal — sa résolution est détaillée dans le tutoriel sur les conflits (résumé : résoudre, git add, puis git rebase --continue).

Étape 3 — La règle d’or du rebasage

Voici la règle la plus importante de tout ce tutoriel, à graver dans le marbre : ne rebasez jamais des commits que vous avez déjà partagés (poussés sur un serveur que d’autres utilisent). La raison est mécanique. Comme le rebasage crée de nouveaux commits avec de nouveaux SHA, votre branche locale et la branche distante divergent. Pour pousser, vous seriez contraint à un push --force, qui réécrit l’historique distant. Tout collègue ayant déjà récupéré les anciens commits se retrouve alors avec une histoire incompatible, des doublons, et des heures perdues à réparer.

La règle pratique est simple : rebasez librement vos commits locaux, tant qu’ils ne sont qu’à vous ; dès qu’un travail est partagé, intégrez les nouveautés avec merge, ou n’utilisez le rebasage que sur votre propre branche de fonctionnalité dont vous êtes le seul auteur. En cas de réécriture assumée d’une branche personnelle déjà poussée, préférez git push --force-with-lease à --force : cette variante refuse d’écraser le travail distant si quelqu’un a poussé entre-temps, un garde-fou précieux.

Étape 4 — Mettre à jour avec pull –rebase

Au quotidien, récupérer le travail des autres avec un simple git pull crée des commits de fusion à répétition, qui polluent l’historique. L’alternative git pull --rebase récupère les nouveautés puis rejoue vos commits locaux par-dessus, gardant une ligne propre.

git pull --rebase

Pour ne plus y penser, on peut en faire le comportement par défaut sur tous ses dépôts. Ce réglage est l’un des plus rentables de Git.

git config --global pull.rebase true

Attention toutefois : cette commande s’applique à vos commits locaux non encore partagés, ce qui respecte la règle d’or. C’est précisément le cas d’usage sûr et recommandé du rebasage.

Étape 5 — Le rebasage interactif : nettoyer ses commits

Le rebasage interactif est l’outil qui transforme un brouillon en chef-d’œuvre. Imaginez avoir commité trois fois pendant le développement : « début filtres », « oups typo », « ça marche enfin ». Personne n’a besoin de voir ce désordre. Condensons ces commits en un seul, propre, avant de proposer la fonctionnalité. Fabriquons d’abord ce désordre.

echo "// brouillon 1" >> filtres.js
git commit -am "debut filtres"
echo "// correction" >> filtres.js
git commit -am "oups typo"
echo "// fin" >> filtres.js
git commit -am "ca marche enfin"

Lancez le rebasage interactif sur les trois derniers commits. La syntaxe HEAD~3 désigne « les trois commits avant la pointe ».

git rebase -i HEAD~3

Votre éditeur s’ouvre avec la liste des commits et, en commentaire, le menu des actions possibles. Les plus utiles sont : pick (garder tel quel), reword (garder mais réécrire le message), edit (s’arrêter pour modifier le contenu), squash (fondre dans le commit précédent en combinant les messages), fixup (comme squash mais en jetant le message), drop (supprimer le commit), et la possibilité de réordonner les lignes pour réordonner les commits.

Pour condenser, laissez pick sur le premier et remplacez pick par fixup (ou squash) sur les deux suivants :

pick   a1b2c3d debut filtres
fixup  e4f5g6h oups typo
fixup  i7j8k9l ca marche enfin

Enregistrez et fermez l’éditeur. Git rejoue les commits selon vos instructions et il ne reste qu’un seul commit propre. Avec squash à la place de fixup, Git vous aurait proposé de rédiger un message combiné. Relisez le résultat avec git log --oneline.

Point d’étapegit log --oneline doit désormais montrer un unique commit pour la fonctionnalité de filtres, au lieu de trois. Si vous vous êtes trompé dans le menu, pas de panique : git rebase --abort annule tout et vous ramène à l’état d’avant.

Étape 6 — Aller plus vite avec –autosquash

Quand vous savez, au moment de commiter une correction, qu’elle devra fusionner dans un commit précis, marquez-la d’emblée. La commande git commit --fixup <sha> crée un commit spécial étiqueté pour ce commit cible.

git commit -a --fixup a1b2c3d
git rebase -i --autosquash HEAD~2

Avec --autosquash, Git préremplit le menu en plaçant automatiquement votre commit de correction en position fixup sous le bon commit : il ne vous reste qu’à confirmer. Ajoutez l’option --autostash si vous avez des modifications non commitées : Git les met de côté avant le rebasage et les restaure après, vous évitant un message d’erreur. Ces deux options transforment le nettoyage d’historique en routine fluide.

Étape 7 — Merge ou rebase : choisir à bon escient

Vous disposez maintenant des deux outils ; reste à savoir lequel dégainer. Le tableau ci-dessous résume les arbitrages courants. Notez aussi l’option intermédiaire git merge --squash <branche>, qui aplatit tous les commits d’une branche en un seul changement à committer sur la destination, sans commit de fusion ni réécriture de la branche source.

Situation Recommandation Pourquoi
Mettre à jour ma branche locale avec main rebase (ou pull --rebase) Historique linéaire, commits encore privés
Intégrer une branche déjà partagée merge Ne réécrit pas l’historique des autres
Nettoyer mes commits avant de les proposer rebase -i Squash, reword, réordonnancement
Garder la trace explicite d’une fonctionnalité merge --no-ff Le commit de fusion documente le bloc
Aplatir une branche en un seul commit merge --squash Un changement net, sans historique intermédiaire

Rebaser une portion précise avec –onto

Il arrive qu’une branche de fonctionnalité ait été créée à partir d’une autre branche de fonctionnalité, et non de main. Quand la branche parente est abandonnée, vous voulez « déplacer » uniquement vos commits sur main, sans embarquer ceux du parent. L’option --onto de git rebase sert exactement à cela : elle précise la nouvelle base, le point de départ à exclure, et la branche à rejouer.

git rebase --onto main ancienne-base feature/ma-branche

Lisez la commande ainsi : « rejoue les commits de feature/ma-branche postérieurs à ancienne-base, et pose-les sur main ». Git ne reprend que vos commits propres, en écartant tout l’historique de la branche parente devenue inutile. C’est l’outil de précision du rebasage : là où un rebasage simple rejoue tout depuis l’ancêtre commun, --onto vous laisse découper exactement la tranche d’historique à transplanter. On y recourt rarement au début, mais le jour où une branche s’est greffée au mauvais endroit, c’est lui qui vous évite de recréer les commits à la main.

🐞 Pièges fréquents

Symptôme / erreur Cause probable Correctif
Collègues avec des commits en double après un push Rebasage d’un historique déjà partagé Respecter la règle d’or ; communiquer avant toute réécriture
Updates were rejected au push après rebasage La branche distante diverge (SHA réécrits) git push --force-with-lease sur une branche personnelle uniquement
Rebasage interrompu par un conflit Modifications concurrentes des mêmes lignes Résoudre, git add, git rebase --continue ; voir le tutoriel sur les conflits
« J’ai tout cassé pendant le rebasage » Manipulation interactive ratée git rebase --abort, ou récupérer via le reflog
--autosquash ne range rien Le commit n’a pas été créé avec --fixup Recréer la correction avec git commit --fixup <sha>

Travailler avec une connexion limitée

Le rebasage est une opération entièrement locale : réorganiser, condenser et linéariser votre historique ne demande aucune connexion. C’est même une excellente habitude lorsque le réseau est rare : on accumule des petits commits hors ligne au fil du travail, sans se soucier de leur propreté, puis on les peigne avec git rebase -i juste avant de se connecter pour partager une série de commits nette. La synchronisation finale envoie alors un historique déjà soigné, en une seule fois.

✅ Récapitulatif

Vous savez désormais que le rebasage rejoue vos commits en les réécrivant (nouveaux SHA), qu’il produit un historique linéaire, et qu’il ne doit jamais toucher un historique partagé — la règle d’or. Vous maîtrisez git pull --rebase pour des mises à jour propres, le rebasage interactif pour condenser et réordonner vos commits, et --autosquash pour automatiser le rangement. Vous savez enfin arbitrer entre merge et rebase. Une question demeure, inévitable dès que deux personnes touchent les mêmes lignes : que faire quand Git ne peut pas réconcilier seul ? C’est tout l’art de la résolution des conflits.

🧾 Aide-mémoire

Commande Rôle
git rebase main Rejouer la branche courante au-dessus de main
git pull --rebase Récupérer puis rejouer ses commits locaux
git rebase -i HEAD~n Rebasage interactif des n derniers commits
pick / reword / edit / squash / fixup / drop Actions du menu interactif
git commit --fixup <sha> Marquer une correction pour un commit cible
git rebase --autosquash Ranger automatiquement les commits --fixup
git rebase --continue / --abort Poursuivre / annuler un rebasage
git push --force-with-lease Pousser une branche personnelle réécrite, en sécurité

💪 À vous de jouer

Créez une branche avec trois commits dont les messages laissent à désirer (« wip », « test », « fix »). Utilisez le rebasage interactif pour : réécrire le message du premier, fondre les deux suivants dedans, et obtenir un unique commit clair. Vérifiez le résultat.

Voir une solution
git switch -c feature/exercice
echo "a" > ex.js && git add ex.js && git commit -m "wip"
echo "b" >> ex.js && git commit -am "test"
echo "c" >> ex.js && git commit -am "fix"
git rebase -i HEAD~3
# Dans l'éditeur :
#   reword    -> "Ajoute le module exercice"
#   fixup   
#   fixup   
git log --oneline

Le reword ouvre un éditeur pour le nouveau message ; les deux fixup absorbent les commits suivants sans garder leurs messages. Il ne reste qu’un commit propre.

FAQ

Q : Le rebasage supprime-t-il des commits ?
R : Il crée de nouveaux commits et délaisse les anciens, qui restent récupérables un temps via le reflog. Tant que vous ne rebasez pas d’historique partagé, c’est sûr.

Q : Pourquoi mes collègues se plaignent-ils après mon rebasage ?
R : Vous avez probablement réécrit un historique déjà partagé. Respectez la règle d’or : pas de rebasage sur des commits que d’autres ont récupérés.

Q : squash ou fixup ?
R : squash combine les messages et vous laisse les éditer ; fixup jette le message du commit absorbé. Utilisez fixup pour des corrections sans intérêt narratif.

Q : --force ou --force-with-lease ?
R : Toujours préférer --force-with-lease : il refuse d’écraser des commits distants que vous n’avez pas vus, contrairement à --force qui écrase aveuglément.

Q : Puis-je annuler un rebasage déjà terminé ?
R : Oui, via le reflog : git reflog pour retrouver l’état d’avant, puis git reset --hard HEAD@{n}. Voir le tutoriel sur l’annulation et la réparation.

Ressources

Dans la même série

🔝 Retour au guide complet : Maîtriser Git et GitHub.

مشاركة