Dynamic-Mess.com


"The world is a dynamic mess of jiggling things..."

Merge, Fast-Forward et rebase: un peu de culture git

Article posté le 27-02-2017 dans la catégorie Développement

Git jouit d'une popularité croissante et devient souvent une condition sine-qua-none pour pouvoir candidater à certaines offres d'emploi. Il est donc intéressant de voir parfois certaines notions avancées, qu'elles soient critiquables ou pas. Dans ce billet, nous allons aborder le fast-forward et le rebase.

Au sommaire de cette article:

1- Fast-Forward

La plupart des utilisateurs de Git se contentent de réaliser les opération suivantes:

  1. fetch ;
  2. checkout ;
  3. pull ;
  4. commit ;
  5. push.

La troisième est celle qui nous intéresse ici: elle consiste à faire, en une seule commande, un git fetch (recupère les modifs distantes mais sans les merger avec mon travail) + un git merge (fusionner mon travail avec celui qui est sur le dépôt distant).

La commande merge est souvent donc réalisée automatiquement. Pourant il y a une donnée important à connaître: il s'agit de la notion de fast-forward. Il s'agit d'une manière particulière de gérer l'historique des branches. Sachez qu'elle est utilisée par défaut, que vous fassiez un merge ou un pull directement.

A- Explications

Imaginons que vous créiez une branche feature/toto à partir de la branche develop. Vous réalisez vos travaux sur votre branche, plusieurs commits, puis enfin, en local ou directement sur le dépôt distant via un serveur comme GitLab, un merge. Si entre le moment où vous avez créé votre branche et celui ou vous voulez répercuter vos modifications sur la branche develop cette dernière n'a pas au de nouvelles modifications - que vous n'auriez donc pas - Git va opter pour un merge avec fast-forward.

Concrètement, il s'agit d'intégrer l'historique de votre branche dans celui de la branche develop. Si vous aviez opté pour un merge sans fast-forward, vous auriez un historique distinct. Explications avec les petits schémas ci-dessous.

Git merge

Vous remarquerez qu'en l'absence de fast-forward, il y a un commit de merge (vert). J'aime bien cette approche, qui permet de conserver un historique complet et d'annuler le merge en seul coup.

B- Paramétrage

Comme expliqué plus haut, le fast-forward est réalisé par défaut. Si vous souhaitez que cela ne soit pas le cas, vous avez deux possibilités:

1- au cas par cas

Qu'il s'agisse d'un merge ou d'un pull, vous avez juste à rajouter le paramètre -no-ff pour le désactiver juste pour cette commande, ou à l'inverse, si vous l'avez désactivé par défaut, utiliser le paramètre -ff pour l'activer juste pour cette commande.

git merge --no-ff

ou

git pull --no-ff

2- globalement

Dans votre fichier de configuration git ( ~/.gitconfig sous Linux) vous pouvez rajouter le bloc suivant:

[merge]
    ff = no
    commit = no

ou le faire avec la commande git config:

git config --global merge.commit no
git config --global merge.ff no

Note: Désactiver le fast forward a un effet gênant: il créé un commit de merge à chaque fois que vous faites un pull origin, ce qui va polluer votre historique. Plutôt que de faire un rebase (violent), vous pouvez autoriser le fast-forward juste pour les pull:

[pull]
        ff = yes

2- Rebase

Le rebase est lié, par le principe, au fast-forward dans la mesure où lui ausis sert à réécrire l'Histoire, mais de manière beaucoup plus radicale.

A- Explications

Imaginons le cas suivant: je crée ma propre branche depuis disons, la branche develop. Pendant que je fais mes développements, mes commits dans ma branche, d'autre personnes vont inclure leur développement (via des merges ou des fixes directement sur develop) dans la branche develop. Quand j'ai fini mon travail, je souhaite donc merger celui-ci dans develop. Que je sois pour ou contre le fast-forward ne compte pas puisqu'ici il n'est pas possible dans la mesure ou des modifications ont eu lieu dans develop. Si je me contente de merger, voici ce que nous aurons dans l'historique:

Rebase

 

Certains lead-developpeurs ou encore release-managers n'apprécient pas tout cela, de voir des boucles dans l'historique du projet. Attention: c'est toujours une bonne pratique de créer une branche pour chaque nouveau développement, cependant il arrive donc parfois que certains responsables de projets n'apprécient pas de voir dans l'historique final toutes sortes de boucles. La commande rebase peut alors avoir son intérêt, elle permet de merger les historiques. Ainsi, au lieu d'avoir ce que nous voyons dans l'image ci-dessus, voici ce que nous aurions :

Rebase 2

Pour y arriver, nous avons fait, en local:

git rebase develop
git checkout develop
git merge ma_branche

Notez que comme expliqué plus haut, vous pouvez faire un pull (fetch + merge). Dans le cas où vous voulez faire un rebase, il faudra alors taper:

git pull -rebase

 

B- Reprendre ses historiques avec l'option interactive

Reprenons notre cas d'avant la fusion:

Rebase

Je me rends compte que mes deux premiers commits servent à corriger le même bug, et qu'ils auraient pu être faits en un seul. Alors évidemment, par sécurité, on commite plusieurs fois quand on développe, mais à la fin, on peut aussi avoir envie de tout nettoyer et factoriser. Avant de merger (avec ou sans rebase) mes modifications dans develop, je vais donc réécrire l'histoire de mes commits. Nous allons encore utiliser la commande rebase:

git rebase -i HEAD~5

Elle va lancer git en mode interactif pour éditer les 5 derniers commits (j'ai pris 5 au hasard, ici nous ne voulons modifier que les 2 premiers).

Leur détail va apparaître dans les premières ligne de l'éditeur de texte, un commit par ligne, avec en dessous plein de commentaires (précédés du caractères #) vous indiquant la marche à suivre:

# 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
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

Si supprimer la ligne d'un commit supprime tout simplement ce dernier, changer le premier mot par un de ceux proposés vous permettra de faire des modifications. Dans notre cas, nous allons donc:

Conformément à ce qui est écrit dans les commentaires, je vais donc :

SI vous fermez l'éditeur, Git vous invite à saisir votre message pour le premier commit. Ensuite, il fusionnera vous deux commits, vous pourrez consulter le résultat avec la commande git log.

 

3- Avis perso

Les éléments mentionnées ci-dessus sont à prendre avec du recul. En effet, il y a des pour et des contre, il y a des gens qui disent amen et d'autres qui crient en voyant cela. En fait tout dépend donc du projet et surtout par qui il est géré, chacun ayant son opinion. En tant que développeur, on s'adapte. Je vais quand même vous donner mon avis.

Donc à mon humble opinion, le fast-forward et surtout le rebase me font penser à des petits héritages de SVN où tout était linéaire (SVN ne gérait pas les branches). L'intérêt de Git, c'est notamment les branches, et de conserver leur historique qui permet ainsi de tout pouvoir tracer, même si cela devient parfois illisible. Cependant, pour éviter les problèmes, il est évident qu'il faut adopter certaines bonnes pratiques... Pour le rebase, je dois quand même avouer que cela peut être intéressant pour nettoyer ses commits avant de pousser son travail.

 


Cet article vous a plu? Découvrez d'autres articles


Tweet
comments powered by Disqus