Skip to main content

Git, en toute simplicité : modifier l’historique avec rebase — partie II

Écrit par
Headshot of Or Kamara

Or Kamara

17 juin 2020

0 minutes de lecture

Ravi de vous retrouver pour la deuxième partie de notre guide pour travailler avec Git en toute simplicité !

Dans l’article précédent, nous avons vu les bases de Rebase ainsi que deux cas courants que nous pouvons rencontrer lorsque nous travaillons avec Git. Vous trouverez la première partie ici. Nous allons maintenant aborder quatre autres cas courants et tirer quelques conclusions.

C’est parti !

Cas courants (et comment les gérer !) — partie II

Reprenons là où nous en étions la dernière fois avec quelques cas courants où vous pourriez vouloir modifier l’historique de votre code, et voyons comment procéder avec Git :

1. Scinder un ancien commit en deux (edit)

Imaginons que vous veniez de vous rendre compte que vous avez fait une erreur en fusionnant deux commits (voir la partie I pour plus de détails) et que vous souhaitiez annuler le squash de 17a73c1 dans e2952e7, afin que le nouveau fichier soit placé dans un commit distinct. Pour cela, exécutez tous les commits jusqu’au point à modifier, arrêtez-vous, effectuez les changements, puis continuez. Autrement dit, utilisez l’option edit.

Commençons par relancer git rebase -i master, mais cette fois avec l’option edit sur le commit à scinder (le deuxième commit, `6871683`) :

pick 682c27f feat: 1st commit
edit 6871683 feat: 2nd commit
pick 1b2f5b1 feat: 3rd commit

Après avoir quitté l’écran de rebase, voici ce qui s’affiche :

Stopped at 6871683...  feat: 2nd commit
You can amend the commit now, with

  git commit --amend '-S'

Once you are satisfied with your changes, run

  git rebase --continue

Git a donc exécuté les premier et deuxième commits, puis s’est arrêté : à nous de jouer. Nous pouvons apporter tous les changements souhaités !

Voici à quoi ressemble actuellement notre graphe Git :

* 6871683 (HEAD) feat: 2nd commit* 682c27f feat: 1st commit
* 24aa86a (master) Init: Git History

Comme indiqué précédemment, nous voulons scinder le deuxième commit en deux commits distincts, un par fichier. Pour commencer, réinitialisons les modifications du deuxième commit en exécutant git reset --soft HEAD^. La commande reset modifie le commit vers lequel pointe actuellement la branche HEAD. Avec l’option --soft, l’index et le répertoire de travail sont conservés, tandis que tous les fichiers modifiés entre le HEAD d’origine et le commit sont placés dans la zone de préparation.

Voici le nouveau statut Git. Comme vous pouvez le voir, les deux fichiers sont en attente :

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
 modified:   b.txt
 new file:   b2.txt

Retirons maintenant b2.txt de la liste des fichiers indexés en exécutant git restore --staged b2.txt :

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
 modified:   b.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
 b2.txt

Avec seulement b.txt comme fichier indexé, nous pouvons maintenant créer le premier commit correspondant à la première partie du deuxième commit. Ensuite, indexons b2.txt et créons le deuxième commit :

❯ git commit -m "feat: 2nd commit - part 1"
❯ git add b2.txt
❯ git commit -m "feat: 2nd commit - part 2"

Notre graphe Git ressemble maintenant à ceci :

* 96127c6 feat: 2nd commit - part 2
* 93260e5 feat: 2nd commit - part 1
* 9882b2f feat: 1st commit
* 24aa86a (master) Init: Git History

Il ne reste plus qu’à poursuivre le rebase et à exécuter les commits suivants en lançant

git rebase --continue. Le graphe Git final devrait ressembler à ceci :

* 09fe03e (HEAD -> feat/git-history) feat: 3rd commit
* 96127c6 feat: 2nd commit - part 2
* 93260e5 feat: 2nd commit - part 1
* 9882b2f feat: 1st commit
* 24aa86a (master) Init: Git History

2. Modifier tous les fichiers d’un commit spécifique (edit)

Le scénario suivant vous est probablement familier : vous avez envoyé tout votre code, parfaitement réparti en plusieurs commits, puis vous vous rendez compte que vous avez oublié de lancer le linter. Comme nous l’avons expliqué, la solution n’est pas de créer un autre commit nommé fix linting, mais de corriger le linting dans chaque commit.

S’il s’agit d’une modification mineure, nous pouvons utiliser l’option squash. Mais si plusieurs changements concernent plusieurs commits, mieux vaut exécuter notre script de linting un par un sur chaque commit (ou sur les commits à corriger). Pour cela, nous pouvons à nouveau utiliser l’option edit (comme dans l’exemple précédent).

Relançons git commit rebase -i en utilisant l’option edit sur le premier commit (qui comprend plusieurs fichiers) :

edit 9882b2f feat: 1st commit
pick 93260e5 feat: 2nd commit - part 1
pick 96127c6 feat: 2nd commit - part 2
pick 09fe03e feat: 3rd commit

Comme précédemment, Git s’arrête après avoir exécuté le premier commit :

Stopped at 9882b2f...  feat: 1st commit
You can amend the commit now, with

  git commit --amend '-S'

Once you are satisfied with your changes, run

  git rebase --continue

Nous pouvons maintenant lancer notre linter (par exemple, npm run lint) et vérifier l’état de notre dépôt :

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
 modified:   a.txt
 modified:   a1.txt
 modified:   a2.txt
 modified:   a3.txt
 modified:   a4.txt
 modified:   a5.txt

Comme vous pouvez le voir, le script du linter a modifié le contenu de 6 fichiers (a*.txt). Il ne reste plus qu’à intégrer les modifications actuelles en les indexant et en créant un commit, puis à poursuivre le rebase :

❯ git add a*
❯ git commit --amend --no-edit
❯ git rebase --continue
Successfully rebased and updated refs/heads/feat/git-history.

Et voici à quoi devrait ressembler le graphe Git :

* 8f404fe (HEAD -> feat/git-history) feat: 3rd commit
* 7e37b6c feat: 2nd commit - part 2
* f71ad8e feat: 2nd commit - part 1
* b50f18f feat: 1st commit
* 24aa86a (master) Init: Git History

3. Vérifier que les tests passent pour chaque commit (exec)

Lorsque vous répartissez votre code en plusieurs commits, gardez à l’esprit que chacun doit pouvoir fonctionner de manière autonome. Autrement dit, après avoir fusionné plusieurs commits dans l’une des branches principales, vous devez pouvoir revenir à chacun d’eux et vous attendre à ce que le code compile et que tous les tests passent.

Comment vérifier tout cela avant de fusionner les commits ?

Lançons manuellement tous les tests sur chaque commit ! Je plaisante, bien sûr : nous devrions automatiser cette tâche :) Pour cela, nous pouvons utiliser l’option exec.

Cette option nous permet d’exécuter des scripts shell dans le cadre du processus de rebase. Il suffit d’ajouter de nouvelles lignes avec la commande exec aux endroits souhaités. Par exemple, relançons git rebase -i master :

pick b50f18f feat: 1st commit
pick f71ad8e feat: 2nd commit - part 1
pick 7e37b6c feat: 2nd commit - part 2
pick 8f404fe feat: 3rd commit

Passons maintenant à la partie amusante : lancer les tests (en exécutant le script ./run_tests.sh) après chaque commit :

pick b50f18f feat: 1st commit
exec ./run_tests.sh
pick f71ad8e feat: 2nd commit
exec ./run_tests.sh
pick 7e37b6c feat: 2nd commit - part 2
exec ./run_tests.sh
pick 8f404fe feat: 3rd commit
exec ./run_tests.sh

Voici le résultat affiché lorsque vous quittez l’écran de rebase :

❯ git rebase -i master
Executing: ./run_tests.sh
All tests passed!
Executing: ./run_tests.sh
5 tests failed :( 108 tests passed!
Executing: ./run_tests.sh
All tests passed!
Executing: ./run_tests.sh
All tests passed!
Successfully rebased and updated refs/heads/feat/git-history.

Comme vous pouvez le voir, Git a exécuté le script de test après chaque commit. Nous savons donc que le commit feat: 2nd commit contient un élément qui fait échouer les tests. Il est également possible d’ajouter une seule commande exec après chaque commit en exécutant :

$ git rebase -i --exec "./run_tests.sh"

Dernière chose à savoir (ou plutôt première) : comment annuler

Ce n’est pas vraiment un scénario, mais plutôt un avertissement : la commande rebase peut être dangereuse !Si vous l’utilisez sans comprendre les effets des commandes, vous risquez de perdre votre travail. C’est pourquoi je vais commencer par deux recommandations :

  • Avant de modifier les commits, assurez-vous toujours d’avoir une sauvegarde fiable de votre dépôt Git. Pour ma part, le plus simple est d’envoyer toutes mes modifications vers une branche distante. En cas de problème, je peux toujours revenir à mon code d’origine (avec git reset).

  • Assurez-vous de bien comprendre les commandes. Avant de les utiliser pour la première fois sur votre code, testez-les dans un dépôt Git temporaire.

N’oubliez pas que vous pouvez toujours interrompre le processus de rebase et revenir à l’état initial :

  • Sur l’écran initial du rebase, supprimez simplement toutes les lignes de commits : rien ne changera.

  • Au milieu du processus de rebase, si vous êtes perdu pour une raison quelconque et ne savez pas quoi faire, vous pouvez toujours exécuter git rebase --abort. Cette commande interrompt le rebase et rétablit l’état d’origine de votre dépôt (avant l’exécution de git rebase -i).

Pour résumer

Comme nous l’avons vu dans les derniers exemples, git rebase est une commande puissante, en particulier en mode interactif. Elle peut servir dans une infinité de situations, mais j’espère vous avoir donné les bases pour comprendre quand chaque option est utile.

Ce que j’aimerais surtout que vous reteniez de cet article, c’est qu’il faut utiliser git rebase avec discernement et sans crainte. Je sais que les débuts peuvent être un peu intimidants et que vous aimeriez peut-être avoir quelqu’un à vos côtés au moment d’appuyer sur Entrée, mais c’est tout à fait normal. Avec un peu de pratique, cela deviendra une habitude. Et je suis sûr qu’il existe bien d’autres scénarios, propres à vos cas d’usage, où cette commande peut vous être utile.

Amusez-vous bien !

Lancez-vous dans les compétitions Capture The Flag

Apprenez à résoudre des défis Capture The Flag en regardant à la demande notre atelier virtuel d’initiation.

Publié dans:

Lire la suite

Blog

Stadium Summer : la tournée Fan Zone de Snyk Connect

La tournée Fan Zone de Snyk a proposé des ateliers sur la sécurité de l’IA, des occasions de réseauter et une compétition amicale dans 8 villes et lors de 3 sessions virtuelles. Les participants ont développé leurs compétences, échangé des idées et progressé ensemble.

Blog

Snyk VulnBench JS 1.0 : les LLM peuvent-ils détecter deux fois les mêmes bugs ?

Snyk VulnBench JS 1.0 : 300 analyses répétées montrent que les résultats de sécurité des LLM varient d’une exécution à l’autre, tandis que SAST et modèles détectent différentes vulnérabilités.

feature insights context
Blog

Construire la sécurité de l’IA avec nos clients : 5 leçons du programme de partenaires de conception d’Evo

Découvrez 5 leçons clés tirées du programme de partenaires de conception d’Evo de Snyk. Découvrez comment la découverte de l’IA, le renseignement sur les risques et l’automatisation des politiques aident les équipes à sécuriser l’IA générative et à maîtriser la prolifération de l’IA à grande échelle.