Branches et rebase

Git squash : regrouper plusieurs commits locaux en un seul

Git squash avec un rebase interactif : regroupez vos commits locaux en un seul, choisissez le message final et vérifiez que les fichiers n’ont pas changé.

Sommaire

Quand regrouper des commits

Avec Git, squasher des commits, c’est en fondre plusieurs en un seul. C’est adapté quand quelques commits sont des étapes du même changement : un premier essai, un wip, une correction de faute de frappe. Regroupés, ils offrent au relecteur un changement cohérent plutôt que le récit de votre cheminement. Le guide d’Ekino y voit un moyen de garder un historique plus propre, notamment pour les pull requests.

Ne squashez que vos propres commits. Les regrouper les réécrit : un commit déjà poussé exige ensuite un push forcé, un risque que détaille le guide du rebase interactif. Si vous voulez seulement ajouter de nouveaux changements au tout dernier commit, l’amender est plus simple.

L’exemple est un dépôt jetable, orbit-api. La branche fix/pagination porte trois commits locaux, dont les deux derniers sont du bruit :

git log --oneline --decorate
e49e1b4 (HEAD -> fix/pagination) fix typo
cdead1f wip: handle the last page
12993ff fix: clamp page size
9fd61d6 (main) chore: init orbit-api

L’objectif est un seul commit avec les mêmes fichiers. Posez d’abord un repère pour comparer ensuite, et notez l’empreinte d’arbre, qui identifie le contenu exact des fichiers à un commit :

git branch before-squash
git rev-parse HEAD^{tree}
33c52f9998adf1e9846722a660d08529d2d67780

Vos identifiants seront différents, mais la méthode est la même.

Sélectionner les commits et lancer le rebase interactif

La base du rebase est le commit situé avant le premier que vous voulez regrouper. Pour les trois derniers commits, HEAD~3 convient :

git rebase -i HEAD~3

Git liste les commits du plus ancien au plus récent. Gardez pick sur la première ligne et changez les deux autres en squash. Voici la liste que j’ai enregistrée :

pick 12993ff fix: clamp page size
squash cdead1f wip: handle the last page
squash e49e1b4 fix typo

Cette liste vient de Git 2.30 ; Git 2.54 affiche chaque sujet après un #, comme dans pick 12993ff # fix: clamp page size. squash fond une ligne dans celle du dessus : la première doit donc rester pick. Si vous la squashez, Git s’arrête avec error: cannot 'squash' without a previous commit, et vous corrigez la liste avec git rebase --edit-todo ou vous abandonnez. Pro Git montre la même opération sur une liste de commits.

Dans GitMini, faites un clic droit sur le premier des trois commits et choisissez Interactive rebase from here…. Réglez les deuxième et troisième commits sur squash — merge (combined message). GitMini refuse une liste qui commence par un squash : « The first commit cannot be merged with a previous commit. »

Choisir le message et vérifier le contenu

Une fois la liste enregistrée, Git ouvre un éditeur avec les trois messages combinés. Tout ce qui ne commence pas par # devient le message : remplacez-le par celui que vous voulez :

# This is a combination of 3 commits.
# This is the 1st commit message:

fix: clamp page size

# This is the commit message #2:

wip: handle the last page

# This is the commit message #3:

fix typo

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# …

Je l’ai remplacé par fix: clamp page size and handle the last page. Git affiche aussi un compteur Rebasing (n/m), omis ici :

[detached HEAD ac08536] fix: clamp page size and handle the last page
 Date: Fri Sep 25 10:02:00 2026 +0000
 1 file changed, 9 insertions(+)
 create mode 100644 src/pagination.ts
Successfully rebased and updated refs/heads/fix/pagination.

Comparez maintenant avec le repère :

git log --oneline --decorate
git rev-parse before-squash^{tree} HEAD^{tree}
git diff before-squash HEAD
ac08536 (HEAD -> fix/pagination) fix: clamp page size and handle the last page
9fd61d6 (main) chore: init orbit-api
33c52f9998adf1e9846722a660d08529d2d67780
33c52f9998adf1e9846722a660d08529d2d67780

Il reste un seul commit, les deux empreintes d’arbre sont identiques et git diff n’affiche rien : les fichiers n’ont pas changé, seul l’historique a changé.

fixup est la variante pour les commits dont vous ne voulez pas le message. Dans une deuxième copie du dépôt, j’ai utilisé fixup sur les lignes deux et trois. Aucun éditeur ne s’est ouvert : Git garde le premier message et abandonne les autres, comme le décrit Pro Git :

ea8c011 (HEAD -> fix/pagination) fix: clamp page size
9fd61d6 (main) chore: init orbit-api

Son empreinte d’arbre était de nouveau 33c52f9998adf1e9846722a660d08529d2d67780. Utilisez squash quand les messages portent une information à garder, et fixup pour les commits jetables comme wip ou fix typo.

GitMini intègre ce choix dans la liste :

Panneau Interactive rebase de GitMini avec le deuxième commit réglé sur squash, son message combiné dans un champ de texte et l’aperçu 3 commits → 2 commits
Une ligne squash est décalée sous le commit qu’elle rejoint. Le champ de texte contient le message combiné, et le pied du panneau prévisualise le résultat avant Launch rebase.

La dernière ligne squash ou fixup du groupe porte un champ de texte, prérempli avec le message du premier commit et ceux de ses lignes squash. Une ligne fixup n’y ajoute rien. Modifiez le texte, vérifiez l’aperçu du nombre de commits, puis cliquez sur Launch rebase.

Distinguer squash local et squash merge

Un squash local réécrit votre branche. Un squash merge est autre chose : quand une pull request est fusionnée, la plateforme peut combiner tous ses commits en un seul sur la branche de base. GitHub le décrit dans sa documentation : les commits sont écrasés en un seul, et les commits intermédiaires ne sont pas conservés sur la branche de base. Cela se passe sur la branche de base au moment de la fusion ; l’équivalent local ci-dessous montre que la branche de fonctionnalité elle-même n’est pas touchée.

Git a un équivalent local, git merge --squash. Il prépare les changements combinés sans commiter, comme l’explique la documentation de git-merge. Dans une troisième copie, sur main :

git merge --squash fix/pagination
git commit -m "fix: paginate with a clamped page size"
git log --oneline --graph --decorate --all
* e49e1b4 (fix/pagination) fix typo
* cdead1f wip: handle the last page
* 12993ff fix: clamp page size
| * 8213c9e (HEAD -> main) fix: paginate with a clamped page size
|/
* 9fd61d6 chore: init orbit-api

main reçoit un nouveau commit, et fix/pagination garde ses trois commits. L’empreinte d’arbre de main est de nouveau 33c52f9998adf1e9846722a660d08529d2d67780.

La boîte de merge de GitMini propose trois modes : Fast forward if possible, Always create a merge commit et Fast-forward only. Elle n’a pas d’option de squash merge. Pour intégrer un seul commit sur main depuis GitMini, squashez d’abord la branche avec le rebase interactif, comme ci-dessus, puis fusionnez-la : si main n’a pas bougé, Fast forward if possible n’ajoute que ce commit. Pour la fusion elle-même, lisez merge ou rebase.

Sources