Ordonnez vos git rebase grâce à l’autosquash

Chez PMSIpilot, nous utilisons git depuis déjà plusieurs années.
Ce merveilleux outil recèle de nombreuses commandes et options parmi lesquelles, une, dont je vais vous parler aujourd’hui :

git rebase -i --autosquash "branchname"

Très rapidement, car les ressources ne manquent pas à ce sujet, la commande git rebase va « déplacer » la base de votre branche courante en l’accolant au dernier commit de la branche passée en argument.
Pour reprendre la documentation officielle voici ce que ça donne

      A---B---C topic
     /
D---E---F---G master
git rebase master
              A'---B'---C' topic
             /
D---E---F---G master

L’options interactive git rebase -i ajoute la possibilité de retravailler l’arbre de votre branche, en modifiant le message, le contenu ou même l’ordre de vos commits (ici A, B et C).
Il vous suffit pour cela d’associer une action à chacun de vos commits via l’éditeur qui s’ouvre lorsque vous tapez la commande.

....
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like 'squash', but discard this commit's log message
# x, exec = run command (the rest of the line) using shell

On notera notamment 2 actions qui vont nous intéresser par la suite, fixup et squash.
Ces commandes permettent de fusionner un commit avec le précédent (au contraire de fixup, squash gardera le message du commit fusionné).
L’autre attrait de l’option interactive est aussi de pouvoir modifier l’ordonnancement de vos commits.

Imaginons que vous souhaitiez merger A et C, il vous faudra déplacer le commit C puis lui assigner la commande squash.

C’est bien beau mais l’autosquash dans tous ça ?

Justement, que diriez vous si git pouvait vous pré-macher le travail en accolant directement les commits que vous souhaitez merger et en leur assignant directement la bonne action ?
C’est justement le but de l’autosquash.

Cas concret

Pour être plus clair rien ne vaut un bel exemple bien illustré.

Je viens de créer une branche partant de master pour ajouter un readme dans mon projet, appelons la add_readme.

Après avoir saisi une introduction je décide de faire un 1er commit :

$ vim README.md
...
...
$ git add README.md && git commit -m'Project introduction'

Je m’attaque ensuite aux pré-requis, que j’ajoute dans un second commit :

$ vim README.md
...
...
$ git add README.md && git commit -m'Project prerequisites'

Et là, catastrophe, j’ai oublié de saisir une partie de mon introduction.

J’ajoute donc les infos à mon fichier mais cette fois-ci lors du commit je vais ajouter un message particulier :

$ vim README.md
...
...
$ git add README.md && git commit -m'fixup! Project introduction'

Vous constatez que j’ai repris le message de mon 1er commit (notamment la 1ère ligne) préfixé de l’expression fixup!.

Pour l’instant cela ne change rien a mon arbre, un simple git log vous permettra de le constater.

$ git log --abbrev-commit --pretty=oneline
34a3c01 fixup! Project introduction
94168ce Project prerequisites
9b9d032 Project introduction
62b7328 initial commit

Mais lorsque je lance ma commande git rebase -i --autosquash master git me positionne automatiquement mon dernier commit en seconde position associé à la commande fixup

pick 9b9d032 Project introduction
fixup 34a3c01 fixup! Project introduction
pick 94168ce Project prerequisites

# Rebase 62b7328..34a3c01 onto 62b7328
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like 'squash', but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
....

Il me suffit de valider cet arbre en sortant de mon éditeur préféré et git se charge du reste.

Bonus

Durant la rédaction de cet article j’ai découvert que l’on pouvait même laisser git générer lui-même le message de « squash » en lui indiquant le hash du commit de fusion.

Pour reprendre notre exemple, cela aurait donné :

$ git commit --fixup 9b9d032
[add_readme 5d291ee] fixup! Project introduction
1 file changed, 2 insertions(+), 1 deletion(-)

Au dela de l’outil

La commande que je viens de vous présenter peut vous paraître superflue mais pour moi elle est un argument principal pour défendre une des « best practice » git : « commit early, commit often »

En effet, pour travailler quotidiennement sur une application assez importante et avec un historique de plusieurs années je suis parfois confronté à des blames du type : « Ajout de la feature machin » avec un commit de 36 fichiers modifiés alors que j’aimerais simplement comprendre le raisonnement du développeur lorsqu’il a ajouté ce petit bout de code qui me pose question.

La difficulté des « Early Commit » c’est que lors du développement d’une feature globale on revient régulièrement sur des parties déjà commitées pour les modifier sans pour autant changer la fonctionnalité associée au commit.

Grâce à l’autosquash je peux effectuer mes modifications en empilant mes commits les uns a la suite des autres.
Lorsque ma feature est prête et que je souhaite la publier, je lance mon rebase qui va réécrire automatiquement mon historique.

 

Bertrand Jamin

 

Laisser un commentaire