Undoing changes

Annuler les commits et les changements

Dans cette section, nous allons aborder les stratégies et les commandes d'annulation Git disponibles. En premier lieu, il est important de noter que Git ne dispose pas de système d'annulation traditionnel comme ceux présents dans une application de traitement de texte. Il sera utile de vous abstenir de mapper des opérations Git à un modèle mental d'annulation traditionnel, quel qu'il soit. En outre, Git possède sa propre nomenclature pour les opérations d'annulation, et il convient de l'évoquer durant une discussion. Cette nomenclature inclut des termes comme reset, revert, checkout, clean, et bien d'autres.

Une métaphore amusante consiste à considérer Git comme un utilitaire de gestion des chronologies. Les commits sont des instantanés d'un point dans le temps ou de points d'intérêt tout au long de l'historique d'un projet. En outre, il est possible de gérer plusieurs chronologies en utilisant des branches. Lorsque vous faites des annulations dans Git, vous reculez généralement dans le temps ou vous revenez à une autre chronologie dans laquelle les erreurs n'ont pas été commises.

Ce tutoriel présente toutes les compétences nécessaires pour utiliser les versions précédentes d'un projet logiciel. Tout d'abord, il montre comment explorer d'anciens commits, puis il explique la différence entre le revert de commits publics dans l'historique du projet et le reset des changements non publiés sur votre machine locale.

Retrouvez des éléments perdus : comment passer en revue d'anciens commits

L'idée sous-jacente de tout système de contrôle de version est de stocker des copies « sécurisées » d'un projet afin que vous n'ayez plus à vous préoccuper de corrompre irrémédiablement votre base de code. Une fois que vous avez créé un historique de projet pour les commits, vous pouvez passer en revue et revoir tous les commits de l'historique. La commande git log constitue l'un des meilleurs outils pour passer en revue l'historique d'un dépôt Git. Dans l'exemple ci-dessous, nous utilisons git log pour obtenir la liste des derniers commits effectués dans une bibliothèque graphique open source populaire.

git log --oneline
e2f9a78fe Replaced FlyControls with OrbitControls
d35ce0178 Editor: Shortcuts panel Safari support.
9dbe8d0cf Editor: Sidebar.Controls to Sidebar.Settings.Shortcuts. Clean up.
05c5288fc Merge pull request #12612 from TyLindberg/editor-controls-panel
0d8b6e74b Merge pull request #12805 from harto/patch-1
23b20c22e Merge pull request #12801 from gam0022/improve-raymarching-example-v2
fe78029f1 Fix typo in documentation
7ce43c448 Merge pull request #12794 from WestLangley/dev-x
17452bb93 Merge pull request #12778 from OndrejSpanel/unitTestFixes
b5c1b5c70 Merge pull request #12799 from dhritzkiv/patch-21
1b48ff4d2 Updated builds.
88adbcdf6 WebVRManager: Clean up.
2720fbb08 Merge pull request #12803 from dmarcos/parentPoseObject
9ed629301 Check parent of poseObject instead of camera
219f3eb13 Update GLTFLoader.js
15f13bb3c Update GLTFLoader.js
6d9c22a3b Update uniforms only when onWindowResize
881b25b58 Update ProjectionMatrix on change aspect

Chaque commit possède une empreinte d'identification SHA-1 unique. Ces identifiants sont utilisés pour explorer la chronologie commitée et réexaminer les commits. Par défaut, git log affiche uniquement les commits pour la branche actuellement sélectionnée. Il est tout à fait possible que le commit que vous recherchez se trouve sur une autre branche. Vous pouvez afficher tous les commits sur toutes les branches en exécutant la commande git log --branches=*. La commande git branch permet d'afficher et d'explorer d'autres branches. Lorsque vous appelez la commande git branch -a, celle-ci renvoie une liste de tous les noms de branche connus. L'un de ces noms de branche peut ensuite être consigné à l'aide de la commande git log <nom_branche>.

Une fois que vous avez trouvé une référence de commit au point dans l'historique que vous souhaitez explorer, vous pouvez utiliser la commande git checkout pour explorer ce commit. git checkout est une méthode simple pour « charger » l'un de ces instantanés sauvegardés sur votre machine de développement. Durant le cours normal du développement, l'élément HEAD pointe généralement vers la branche master ou une autre branche locale, mais lorsque vous faites un check-out d'un ancien commit, HEAD ne pointe plus vers une branche. Il pointe directement vers un commit. On parle alors d'état « HEAD détaché », qui peut être illustré comme suit :

Git Tutorial: Checking out a previous commit

Lorsque vous faites le check-out d'un ancien fichier, vous ne déplacez pas le pointeur HEAD. Ce dernier reste sur la même branche et le même commit, à l'état « HEAD détaché ». Vous pouvez ensuite faire un commit de l'ancienne version du fichier dans un nouvel instantané comme vous le feriez pour tout autre changement. Utiliser git checkout dans un fichier permet donc de revenir à une ancienne version d'un fichier donné. Pour plus d'informations sur ces deux modes, consultez la page git checkout.

Afficher une ancienne version

Pour cet exemple, partons du principe que vous avez commencé à développer quelque chose d'un peu fou, mais que vous hésitez encore à le conserver ou pas. Pour vous aider à prendre une décision, vous souhaitez examiner l'état du projet avant le début de votre test. Tout d'abord, vous devez rechercher l'ID de la version que vous souhaitez afficher.

git log --oneline

Supposons que votre historique de projet ressemble à ceci :

b7119f2 Continue doing crazy things
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import

Vous pouvez utiliser git checkout pour afficher le commit « Make some import changes to hello.txt » comme suit :

git checkout a1e8fb5

Ainsi, l'état de votre répertoire de travail correspond à celui du commit a1e8fb5. Vous pouvez visualiser les fichiers, compiler le projet, effectuer des tests et même modifier des fichiers sans craindre de compromettre l'état actuel du projet. Rien de ce que vous ferez ici ne sera enregistré dans votre dépôt. Pour poursuivre le développement, vous devez revenir à l'état « actuel » de votre projet :

git checkout master

Cela suppose que vous développiez sur la branche master par défaut. Une fois que vous êtes revenu sur la branche master, vous pouvez utiliser git revert ou git reset pour annuler les changements indésirables.

Annuler un instantané commité

Techniquement, il existe plusieurs stratégies différentes pour annuler un commit. Les exemples suivants partent du principe que nous possédons un historique des commits qui ressemble à ce qui suit :

git log --oneline
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import

Nous allons nous concentrer sur l'annulation du commit 872fa7e Try something crazy, qui était peut-être un peu trop insensé.

Comment annuler un commit avec git checkout

À l'aide de la commande git checkout, nous pouvons faire un check-out du commit précédent, a1e8fb5, pour rétablir le dépôt à un état antérieur au commit insensé. Lorsque vous faites le check-out d'un commit donné, le dépôt est placé à l'état « HEAD détaché ». Cela signifie que vous ne travaillez plus sur une branche. À l'état détaché, tous les nouveaux commits que vous ferez seront orphelins lorsque vous repassez à une branche établie. Les commits orphelins seront supprimés par la commande garbage collection de Git. Cette dernière s'exécute à intervalles fixes et supprime les commits orphelins de façon permanente. Pour éviter leur suppression par la commande garbage collection, nous devons nous assurer de travailler sur une branche.

À l'état HEAD détaché, nous pouvons exécuter la commande git checkout -b nouvelle_branche_sans_commit_insensé. Une nouvelle branche appelée nouvelle_branche_sans_commit_insensé va ainsi être créée, et l'état sera modifié en conséquence. Le dépôt se trouve maintenant sur une nouvelle chronologie dans laquelle le commit 872fa7e n'existe plus. À ce stade, nous pouvons continuer de travailler sur cette nouvelle branche dans laquelle le commit 872fa7e n'existe plus et la considérer comme « annulée ». Malheureusement, si vous avez besoin de la branche précédente, qui était peut-être votre master, cette stratégie d'annulation n'est pas appropriée. Voyons un peu quelques autres stratégies d'annulation. Pour plus d'informations et d'exemples, consultez notre discussion approfondie sur git checkout.

Comment annuler un commit public avec git revert

Supposons que nous sommes de retour dans notre historique des commits d'origine. Celui-ci inclut le commit 872fa7e. Essayons cette fois de faire un revert d'une annulation. Si nous exécutons la commande git revert HEAD, Git crée un nouveau commit avec l'inverse du dernier commit. Un nouveau commit est alors ajouté à l'historique de branche actuel, qui ressemble maintenant à cela :

git log --oneline
e2f9a78 Revert "Try something crazy"
872fa7e Try something crazy
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import

À ce stade, nous avons techniquement annulé le commit 872fa7e. Même si le commit 872fa7e existe encore dans l'historique, le nouveau commit e2f9a78 est l'inverse des changements apportés dans 872fa7e. Contrairement à notre précédente stratégie de check-out, nous pouvons continuer d'utiliser la même branche. Cette solution est satisfaisante. C'est la méthode d'annulation idéale pour travailler avec des dépôts publics partagés. Si vous devez conserver un historique Git organisé et minimal, cette stratégie peut ne pas être satisfaisante.

Comment annuler un commit avec git reset

Pour cette stratégie d'annulation, nous allons poursuivre avec notre exemple de travail. git reset est une commande poussée à usages et fonctions multiples. Si nous appelons git reset --hard a1e8fb5, l'historique des commits est réinitialisé sur ce commit spécifique. Lorsque vous exécutez la commande git log pour examiner l'historique des commits, celui-ci ressemble à ce qui suit :

git log --oneline
a1e8fb5 Make some important changes to hello.txt
435b61d Create hello.txt
9773e52 Initial import

La sortie de journal indique que les commits e2f9a78 et 872fa7e n'existent plus dans l'historique. À ce stade, nous pouvons continuer de travailler et de créer des commits comme si les commits « insensés » n'avaient jamais eu lieu. Cette méthode d'annulation de changements est la plus « propre » pour l'historique. La commande git reset est idéale pour les changements locaux ; elle ajoute cependant des complications lorsque vous travaillez avec un dépôt distant partagé. Si nous avons un dépôt distant partagé vers lequel nous avons fait un push du commit 872fa7e et si nous essayons d'exécuter la commande git push sur une branche dans laquelle l'historique a été réinitialisé, Git s'en aperçoit et renvoie une erreur. Git suppose que la branche dont vous faites un push n'est pas à jour en raison de ses commits manquants. Dans ces scénarios, vous devriez préférer git revert comme méthode d'annulation.

Annuler le dernier commit

Dans la section précédente, nous avons vu différentes stratégies pour annuler des commits. Toutes sont également applicables au commit le plus récent. Mais, dans certains cas, vous ne pourrez peut-être pas supprimer ou restaurer le dernier commit. Il est possible qu'il ait été réalisé prématurément. Dans ce cas, vous pouvez modifier le commit le plus récent. Quand vous avez fait d'autres changements dans le répertoire de travail et que vous les avez stagés à des fins de commit à l'aide de git add, vous pouvez exécuter git commit --amend. Cette commande va demander à Git d'ouvrir l'éditeur système configuré pour vous permettre de modifier le dernier message de commit. Les nouveaux changements seront ajoutés au commit modifié.

Annuler des changements non commités

Avant que les changements ne soient commités dans l'historique du dépôt, ils résident dans l'index de staging et dans le répertoire de travail. Vous devrez peut-être annuler des changements dans ces deux zones. L'index de staging et le répertoire de travail sont des mécanismes de gestion des états internes à Git. Pour des informations plus détaillées sur le fonctionnement de ces deux mécanismes, consultez la page git reset qui les explore en détail.

Le répertoire de travail

Le répertoire de travail est généralement synchronisé avec le système de fichiers local. Pour annuler des changements dans le répertoire de travail, vous pouvez modifier des fichiers comme vous le feriez normalement à l'aide de votre éditeur préféré. Git propose plusieurs utilitaires pour vous aider à gérer le répertoire de travail. La commande git clean est un utilitaire pratique pour annuler des changements dans le répertoire de travail. En outre, git reset peut être appelée avec l'option --mixed ou --hard. Elle applique alors un reset sur le répertoire de travail.

L'index de staging

La commande git add permet d'ajouter des changements à l'index de staging. git reset est principalement utilisée pour annuler les changements apportés à l'index de staging. Un reset avec l'option --mixed déplace tout changement en attente de l'index de staging vers le répertoire de travail.

Annuler des changements publics

Lorsque vous travaillez en équipe sur des dépôts distants, des considérations supplémentaires entrent en ligne de compte lors de l'annulation de changements. git reset devrait généralement être considérée comme une méthode d'annulation locale. Un reset devrait être utilisé lorsque vous annulez des changements dans une branche privée. Vous isolez ainsi nettement la suppression de commits des autres branches susceptibles d'être utilisées par d'autres développeurs. Des problèmes se posent lorsqu'un reset est exécuté sur une branche partagée et qu'un push est réalisé à distance sur cette branche à l'aide de git push. Git bloque alors le push dans ce scénario, en arguant que la branche pushée n'est pas à jour par rapport à branche distante, car elle ne contient pas tous les commits.

La méthode privilégiée pour annuler un historique partagé est git revert. Un revert est plus sûr qu'un reset, car il ne supprime pas les commits d'un historique partagé. Un revert conserve les commits à annuler et crée un commit qui inverse le commit indésirable. Cette méthode est plus sûre pour la collaboration à distance, car un développeur travaillant à distance peut ensuite faire un pull de la branche et recevoir le nouveau commit restauré, lequel annule le commit indésirable.

Résumé

Nous avons abordé de nombreuses stratégies générales pour annuler des opérations dans Git. Il est important de vous rappeler qu'il existe plusieurs façons d'annuler des opérations dans un projet Git. La majeure partie de la discussion sur cette page abordait des points plus approfondis, qui sont expliqués plus en détail sur les pages spécifiques des commandes Git concernées. Les outils d'annulation les plus couramment utilisés sont git checkout, git revert et git reset. Voici quelques points clés à mémoriser :

  • Une fois que les changements ont été commités, ils sont généralement permanents.
  • Utilisez git checkout pour explorer l'historique des commits et le passer en revue.
  • git revert est le meilleur outil pour annuler des changements publics partagés.
  • git reset est idéale pour annuler des changements privés locaux.

Outre les principales commandes d'annulation, nous avons vu d'autres utilitaires Git : git log pour rechercher des commits perdus, git clean pour annuler des changements non commités et git add pour modifier l'index de staging.

Chacune de ces commandes possède sa propre documentation détaillée. Pour en savoir plus sur une commande spécifique mentionnée dans cet article, consultez les liens correspondants.