Comment utiliser le workflow “rebase and merge” avec git

Michel Blancard
5 min readDec 10, 2018

--

Rather read it in English?

Une bonne connaissance du 3ème chapitre du Pro Git Book sur les branches est nécessaire pour bien comprendre cet article.

Il y a de nombreuses manières d’utiliser git pour ses projets, mais celle que je préfère se nomme “rebase and merge”. Mon but n’est pas de montrer pourquoi c’est le meilleur workflow (il y a déjà assez d’information dans la nature pour vous faire votre propre jugement), mais comment l’utiliser.

L’interface en ligne de commande de git

Nous allons utiliser git directement, en ligne de commande. Il y a d’autres outils plus agréables à utiliser (j’aime bien gitk et GitKraken qui sont des surcouches graphiques sur git), mais je déconseille d’utiliser les fonctionnalités des outils graphiques qu’on ne saurait pas reproduire avec la ligne de commande.

Qu’est-ce qu’on souhaite obtenir ?

Pour afficher l’historique git, on utilise la commande suivante :

git log --graph --decorate --oneline

Avec le workflow “rebase and merge”, on s’attend à un historique qui ressemble à cela :

*   fa7d931 (HEAD -> master, origin/master, origin/HEAD) Merge pull request #292 from branch-184
|\
| * 41b1949 Add some nice feature C
|/
* 3a5b9bc Merge pull request #290 from branch-182
|\
| * 8bc44bf Make another change related to B
| * a93b1fe Make some useful changes for B
|/
* 513cd52 Merge pull request #282 from branch-180
|\
| * 748df5e Add the last touch to our crazy feature A
| * b0f4b66 Add a third change of A
| * 5378b8d Add the second part of A
| * fa728d8 Add the first part of a crazy feature A
|/
* 5e9b404 Merge pull request #289 from branch-175
|\
| * ...
| * ...

Toutes les fonctionnalités logicielles apparaissent dans l’historique comme si elles avaient été rajoutées l’une après l’autre dans le répertoire git. Pourtant, si ce répertoire git est partagé par une équipe de développeurs, il est probable que des fonctionnalités aient été développées simultanément. Pour retirer toute trace de la simultanéité du processus de développement logiciel, il faut trafiquer un peu l’historique git. C’est le “rebase” de “rebase and merge”.

Les commits sont groupés par fonctionnalité logicielle. Les merge commits (commits de fusion) permettent de bien séparer les fonctionnalités. C’est le “merge” de “rebase and merge”.

Comment réaliser une telle sorcellerie ? Commençons par partir d’un répertoire git, dans lequel nous souhaiterions ajouter une fonctionnalité simple et bien définie.

Créer une branche

La première étape est de créer une nouvelle branche depuis la branche master :

git checkout master
git fetch
git checkout -b awesome-feature
git push --upstream origin awesome-feature

Implémenter la fonctionnalité

Il est temps d’implémenter la fonctionnalité. Ici, nous ne nous occuperons pas de savoir si nous travaillons en méthode test-driven en non, seul ou en paire, en remote ou au travail…

En revanche, l’important est que nous travaillions en isolation complète des autres chantiers simultanés du répertoire git. Ainsi toute notre attention est réservée à la fonctionnalité attendue. Cette isolation n’est réalisable en pratique que si les fonctionnalités sont rapides à développer : une ou deux heures au maximum. Un ou deux jours de travail peuvent peut-être encore passer, mais c’est le signe que les fonctionnalités sont trop complexes ou mal définies.

nano sourcecode.js
git add sourcecode.js
git commit -m "Add a new class ABC"
...
git push

Les fonctionnalités sont considérées comme implémentées quand les tests réussissent et le code est revu (ou n’importe quel ensemble de règles appliquées par l’équipe est satisfait).

L’historique git ressemble à quelque chose comme cela :

* 60b4757 (HEAD -> awesome-branch) Fix typo on that_variable
* ed0e5f0 Apply requested change on class ABC
* bd8f78a Really fix class ABC
* 0b46bd2 Fix class ABC
* f790959 Rename this_variable to that_varible
* 0195077 Add a new class ABC
* fa7d931 (origin/master, origin/HEAD, master) Merge pull request #292 from branch-184

Nettoyer les commits (optionnel)

Un rebase interactif permet de déplacer et d’écraser (squash) les commits qui ne méritent pas leur ligne à eux dans l’historique git :

git rebase --interactive

C’est maintenant beaucoup mieux :

* 0dfb35c (HEAD -> awesome-branch) Rename this_variable to that_variable
* 8995a57 Add a new class ABC
* fa7d931 (origin/master, origin/HEAD, master) Merge pull request #292 from branch-184

Rebase sur master

Il reste à partager notre travail en mettant à jour la branche master. Mais attention, notre branche master n’est plus à jour ! Un autre membre de l’équipe à très bien pu terminer et fusionner (merge) une fonctionnalité dans l’intervalle de temps.

D’abord, mettons à jour notre copie locale de la branche master :

git checkout master
git fetch
git log

Si la branche master n’a pas été modifiée, cette étape peut-être sautée. Sinon, il faut appliquer un rebase de notre branche sur la branche master.

git checkout awesome-feature
git rebase master

Si notre nouvelle implémentation est en conflit avec les changements extérieurs, c’est maintenant qu’il faut les résoudre. C’est souvent le moment le plus redouté lorsqu’on utilise git. Mais aucun workflow git ne peut faire ce travail à notre place…

Enfin il faut pousser la branche vers l’origine pour faire exécuter l’intégration continue. Nous devons forcer le push, ce qu’il n’est pas interdit car nous sommes les seuls à travailler sur cette branche.

git checkout awesome-feature
git push -f

Pour être satisfaits, vérifions bien que les tests d’intégration passent.

Fusionner dans la branche master

Nous pourrions verser nos changements avec un fast-forward, mais comme je l’expliquais, je préfère bien séparer les fonctionnalités par le commit de fusion de la pull request. De plus, cela permet de retrouver plus facilement dans le futur la trace de la pull request qui a introduit un changement donné.

git checkout master
git merge --no-ff awesome-feature
git push

Sur GitHub, nous pourrions juste fusionner la pull request avec l’option “rebase and merge” ou l’option “create a merge commit”.

Supprimer la branche

Il est inutile d’embrouiller les autres avec des branches déjà fusionnées.

git checkout master
git branch --delete awesome-feature
git push origin --delete awesome-feature

C’est fini !

Cela paraît compliqué ? Le contrôle de version est un problème complexe auquel nous sommes confrontés tous les jours. Nous rechignons à investir du temps sur ce problème qui paraît tellement secondaire, et nous sommes tentés de continuer à utiliser des workflows plus simples même s’ils ne sont pas toujours satisfaisants. Pauvres fous que nous sommes !

--

--