Quantcast
Channel: Publicis Sapient Engineering – Engineering Done Right
Viewing all articles
Browse latest Browse all 1865

git essentials – 4 – rebase

$
0
0

Ceci est le quatrième article d’une série consacrée aux commandes de Git, le système de gestion de révisions décentralisé. Legit essentials sujet de cet article est la commande git rebase, qui permet de déplacer, réordonner et modifier des commits, et donc des branches, dans l’arborescence de révisions.

Retrouvez les précédents articles de la série :

Dans cet article, nous allons :

  • comprendre l’utilité du déplacement de commits et de branches ;
  • montrer comment déplacer une branche dans deux cas différents ;
  • apprendre à fusionner des commits ;
  • évoquer quelques usages avancés.

Quelle est l’utilité du rebase ?

Les raisons de vouloir déplacer ou réordonner un groupe de commits, ou branche, sont variées :

  • volonté de conserver un historique de révision linéaire, donc facile à comprendre et analyser ;
  • éviter que le graphe d’historique ne ressemble à un plat de spaghettis, c’est aussi faciliter le debug, notamment avec git bisect ;
  • intégrer de nouveaux commits dans la branche courante ;
  • déplacer une branche créée au mauvais endroit ;
  • compresser de nombreux commits locaux correspondant à différentes étapes d’un développement en un seul, propre, avant de pousser sur le dépôt central.

Déplacer une Branche

Nous allons voir ici une utilisation de base de la commande rebase. Prenons un projet d’exemple, où nous avons deux branches : la principale, master, et une autre contenant des développements en rapport avec une nouvelle fonctionnalité, appelée featureF. Le graphe de révision est le suivant :

A---B---C---D master
  \
      E---F---G featureF

Mais nous nous apercevons que la branche featureF aurait dû être tirée depuis le commit D. Nous allons donc « déplacer » la branche, pour lui faire intégrer ces commits et faire « comme si » elle avait été créée à partir de D, le dernier commit de master.

Point important : la branche courante est featureF.

git rebase master

Cette commande est équivalente à utiliser un identifiant de commit :

git rebase D

Le résultat est le suivant :

A---B---C---D master
            \
             E'---F'---G' featureF

Il est possible d’obtenir le même résultat sans avoir à se placer au préalable sur featureF en précisant un argument supplémentaire :

git rebase master featureF

Déplacer une branche sur une autre branche

Considérons l’état suivant :

A---B---C---D master
     \
      E---F---G featureF
               \
                H---I featureG

Nous avons une branche featureG, créée à partir de featureF, elle-même créée à partir de master. Il s’agit d’une erreur, car featureG aurait dû être créée à partir de master et ne dépend pas de featureF. Nous allons donc déplacer featureG sans ses commits communs avec featureF, c’est-à-dire les commits EF et G pour la « raccrocher » à master :

git rebase --onto master featureF featureG

Le résultat est le suivant :

A---B---C---D master
     \       \
      \       H'---I' featureG
       \
        E---F---G featureF

Là encore il est possible d’utiliser des identifants de commits en lieu et place du nom des branches.

Fusionner des commits

git rebase permet également de réordonner, modifier, fusionner ou encore séparer des commits. Nous allons ici nous pencher sur un usage courant, qui consiste à vouloir fusionner une série de petits commits. Par exemple, ce besoin peut se présenter après un développement compliqué, qui a nécessité plusieurs commits (étapes) intermédiaires avant d’arriver au résultat final. Une fois le résultat final atteint, seul celui-ci va intéresser le reste de l’équipe. En effet, personne ne sera intéressé par un état incohérent (fonctionnalités incomplètes, présence d’anomalies, compilation en échec…). Prenons l’exemple suivant :

* 875fdcc (HEAD -> myAwesomeFeature) Add button on UI
* d1fa7e0 Fix error on database request
* 4b2ddd3 Part 2, error on database request
* 4a3e967 Part 1
* 32dee61 More work, does not compile
* 20bd297 Create sample database structure
| * afc62ce (origin/master, master) Add powerful script
|/  
* 3dfe024 Modify a.txt
* e48e446 Initial commit

Nous venons de terminer le développement de la fonctionnalité myAwesomeFeature et nous nous retrouvons avec tous les commits (6) intermédiaires qui nous ont été utiles pendant le développement, mais plus maintenant. Nous souhaitons donc compresser tous nos commits en un seul, afin de ne pas polluer l’historique de révision et fournir à l’équipe uniquement des informations utiles. Pour ce faire, nous allons indiquer à git sur quel ensemble de commits nous souhaitons travailler. Il s’agit des 6 derniers :

git rebase -i HEAD~6

Explication :

  • -i est l’option interactive, qui va laisser l’utilisateur dire quel type de modification (fusion, découpage, suppression, modification…) il souhaite appliquer aux commits considérés ;
  • HEAD~6 est un indicateur signifiant « 6 commits avant HEAD ».

À la validation de la commande, un éditeur de texte va s’ouvrir avec le contenu suivant :

pick 20bd297 Create sample database structure
pick 32dee61 More work, does not compile
pick 4a3e967 Part 1
pick 4b2ddd3 Part 2, error on database request
pick d1fa7e0 Fix error on database request
pick 875fdcc Add button on UI

# Rebase 3dfe024..875fdcc onto 3dfe024 (6 command(s))
#
# 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
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Tout ce qui est commenté après les premières lignes est une aide.

Les 6 premières lignes correspondent aux 6 commits sélectionnés au lancement de la commande. Ces derniers sont ordonnés du plus ancien au plus récent. Le verbe pick en préfixe indique l’action qui sera effectuée sur ces commits. Si nous enregistrons et quittons ce fichier en l’état, rien ne sera modifié. Si l’on veut compresser plusieurs commits en un seul, il faut remplacer l’action pick par une autre. Celle qui nous intéresse est l’action squash, qui indique qu’un commit doit être fusionné dans le précédent. Nous modifions donc le fichier en conséquence :

pick 20bd297 Create sample database structure
squash 32dee61 More work, does not compile
squash 4a3e967 Part 1
squash 4b2ddd3 Part 2, error on database request
squash d1fa7e0 Fix error on database request
squash 875fdcc Add button on UI

Le squash de la ligne 2 indique à git « fusionne ce commit avec le précédent (ligne 1) ». Ligne 3, un nouveau squash indique de fusionner le commit avec celui de la ligne 2, lui-même fusionné avec la ligne 1 ; de même avec les autres. In fine, les 6 commits vont donc être rassemblés en un seul.

Puis nous enregistrons et quittons. Un nouvel éditeur apparait, affichant les informations suivantes :

# This is a combination of 6 commits.

# The first commit's message is:
Create sample database structure

# This is the 2nd commit message:
More work, does not compile

# This is the 3rd commit message:
Part 1

# This is the 4th commit message:
Part 2, error on database request

# This is the 5th commit message:
Fix error on database request

# This is the 6th commit message:
Add button on UI

Comme nous sommes en train de fusionner 6 commits, disposant chacun de leur commentaire, Git nous demande quel est le commentaire à conserver pour le commit de fusion. Nous décidons de garder le dernier en supprimant les autres lignes.

Un fois enregistré, l’opération de rebase est effectuée, et le résultat est le suivant :

* e1a15c4 (HEAD -> myAwesomeFeature) Add button on UI
| * afc62ce (origin/master, master) Add powerful script
|/  
* 3dfe024 Modify a.txt
* e48e446 Initial commit

Nous pouvons constater que nos 6 commits se sont transformés en un seul. Il peut maintenant être intégré à master par la méthode de notre choix.

Autres opérations possibles

Nous venons d’utiliser le mode interactif de la commande rebase pour fusionner des commits. Comme mentionné plus haut, il est possible d’effectuer des modifications de nature différente grâce à ce mode interactif. Nous avons vu les verbes pick et squash, mais il existe également les opérations suivantes :

  • fixup : fonctionne de la même manière que squash, en excluant directement le message de commit ;
  • reword : permet de renommer le message de commit sur lequel il est appliqué ;
  • edit : conserve le commit, mais lorsque la commande rebase va s’exécuter, elle va s’arrêter à ce commit pour permettre une édition, comme par exemple ajouter un fichier oublié, puis continuer dès que nous l’indiquerons (avec un git rebase --continue) ;
  • exec : permet d’exécuter une commande shell entre deux lignes de commit ;
  • drop : supprime le commit.

Pour un exemple avancé, un screencast dédié à la commande rebase est disponible sur Xebia TV.

Conclusion

Nous venons de voir que grâce à la commande rebase, il est possible de réagencer un ensemble de commits de toutes les manières imaginables : déplacer des groupes de commits, les réordonner, les fusionner, les modifier… Cette commande est très complète, mais il faut garder à l’esprit qu’elle modifie l’historique, et donc ne doit être appliquée que sur des commits locaux, donc avant le push. S’il est tout de même possible de modifier une partie de l’historique déjà poussé, via un git push --force, cela est à éviter dans la majorité des situations, car cela causera des problèmes aux personnes ayant déjà récupéré les commits modifiés.

À bientôt pour le prochain article de cette série !


Viewing all articles
Browse latest Browse all 1865

Trending Articles