En clair, que fait «git reset»?

674

J'ai vu des articles intéressants expliquant les subtilités git reset.

Malheureusement, plus j'en lis, plus il apparaît que je ne le comprends pas complètement. Je viens d'un arrière-plan SVN et Git est un tout nouveau paradigme. Je suis devenu mercurial facilement, mais Git est beaucoup plus technique.

Je pense que git resetc'est proche hg revert, mais il semble qu'il y ait des différences.

Alors qu'est-ce que git resetça fait exactement ? Veuillez inclure des explications détaillées sur:

  • les options --hard, --softet --merge;
  • l'étrange notation que vous utilisez avec HEADcomme HEAD^et HEAD~1;
  • cas d'utilisation concrets et flux de travail;
  • conséquences sur la copie de travail, le HEADet votre niveau de stress global.
e-satis
la source
17
Je pense qu'une référence Visual Git donne un bon aperçu de ce qui se passe lors de l'utilisation des commandes git courantes.

Réponses:

992

En général, git resetla fonction de est de prendre la branche courante et de la réinitialiser pour pointer ailleurs, et éventuellement de ramener l'index et l'arbre de travail. Plus concrètement, si votre branche master (actuellement extraite) est comme ceci:

- A - B - C (HEAD, master)

et vous vous rendez compte que vous voulez que maître pointe vers B, pas C, vous allez le git reset Bdéplacer là:

- A - B (HEAD, master)      # - C is still here, but there's no branch pointing to it anymore

Digression: c'est différent d'une caisse. Si vous couriez git checkout B, vous obtiendriez ceci:

- A - B (HEAD) - C (master)

Vous vous êtes retrouvé dans un état HEAD détaché. HEAD, arbre de travail, indexer toutes les correspondances B, mais la branche principale a été laissée à C. Si vous effectuez un nouveau commit Dà ce stade, vous obtiendrez ceci, ce qui n'est probablement pas ce que vous voulez:

- A - B - C (master)
       \
        D (HEAD)

Rappelez-vous que reset ne fait pas de commit, il met simplement à jour une branche (qui est un pointeur vers un commit) pour pointer vers un autre commit. Le reste n'est que des détails de ce qui arrive à votre index et à votre arbre de travail.

Cas d'utilisation

Je couvre plusieurs des principaux cas d'utilisation git resetdans ma description des différentes options dans la section suivante. Il peut vraiment être utilisé pour une grande variété de choses; le fil conducteur est que tous impliquent la réinitialisation de la branche, de l'index et / ou de l'arbre de travail pour pointer / correspondre à un commit donné.

Choses à faire attention

  • --hardpeut vous faire vraiment perdre du travail. Il modifie votre arbre de travail.

  • git reset [options] commitpeut vous faire (en quelque sorte) perdre des commits. Dans l'exemple de jouet ci-dessus, nous avons perdu la validation C. Il est toujours dans le référentiel, et vous pouvez le trouver en regardant git reflog show HEADou git reflog show master, mais il n'est plus accessible depuis aucune branche.

  • Git supprime définitivement ces validations au bout de 30 jours, mais d'ici là, vous pouvez récupérer C en pointant à nouveau une branche vers lui ( git checkout C; git branch <new branch name>).

Arguments

En paraphrasant la page de manuel, l'utilisation la plus courante est de la forme git reset [<commit>] [paths...], qui réinitialisera les chemins d'accès donnés à leur état à partir de la validation donnée. Si les chemins ne sont pas fournis, l'arborescence entière est réinitialisée et si la validation n'est pas fournie, elle est considérée comme étant HEAD (la validation actuelle). C'est un modèle commun à toutes les commandes git (par exemple checkout, diff, log, bien que la sémantique exacte varie), donc cela ne devrait pas être trop surprenant.

Par exemple, git reset other-branch path/to/fooréinitialise tout dans chemin / vers / foo à son état dans l'autre branche, git reset -- .réinitialise le répertoire actuel à son état dans HEAD, et un simple git resetréinitialise tout à son état dans HEAD.

L'arborescence de travail principale et les options d'index

Il existe quatre options principales pour contrôler ce qui arrive à votre arborescence de travail et à votre index pendant la réinitialisation.

Rappelez-vous, l'index est la «zone de transit» de git - c'est là que les choses se passent lorsque vous dites que vous vous préparez git addà vous engager.

  • --hardfait tout correspondre au commit que vous avez réinitialisé. C'est probablement le plus facile à comprendre. Tous vos changements locaux sont bouleversés. Une utilisation primaire souffle dans votre travail , mais pas de commutation commits: des git reset --hardmoyens git reset --hard HEAD, par exemple ne changent pas la branche , mais se débarrasser de tous les changements locaux. L'autre consiste simplement à déplacer une branche d'un endroit à un autre et à synchroniser l'index / l'arbre de travail. C'est celui qui peut vraiment vous faire perdre du travail, car il modifie votre arbre de travail. Soyez très sûr de vouloir jeter le travail local avant d'en exécuter reset --hard.

  • --mixedest la valeur par défaut, c'est à git resetdire git reset --mixed. Il réinitialise l'index, mais pas l'arborescence de travail. Cela signifie que tous vos fichiers sont intacts, mais toutes les différences entre le commit d'origine et celui que vous réinitialisez apparaîtront comme des modifications locales (ou fichiers non suivis) avec le statut git. Utilisez-le lorsque vous réalisez que vous avez commis de mauvaises validations, mais que vous souhaitez conserver tout le travail que vous avez effectué afin de pouvoir le corriger et le recommencer. Pour valider, vous devrez à nouveau ajouter des fichiers à l'index ( git add ...).

  • --softne touche pas l'index ou l' arbre de travail. Tous vos fichiers sont intacts comme avec --mixed, mais toutes les modifications apparaissent comme changes to be committedavec l'état git (c'est-à-dire archivées en préparation pour la validation). Utilisez-le lorsque vous réalisez que vous avez commis de mauvaises validations, mais le travail est tout bon - tout ce que vous avez à faire est de le réengager différemment. L'index n'est pas modifié, vous pouvez donc valider immédiatement si vous le souhaitez - le commit résultant aura le même contenu que celui où vous étiez avant de réinitialiser.

  • --mergea été ajouté récemment et vise à vous aider à abandonner une fusion qui a échoué. Cela est nécessaire car vous git mergepermettra en réalité de tenter une fusion avec un arbre de travail sale (avec des modifications locales) tant que ces modifications se trouvent dans des fichiers non affectés par la fusion. git reset --mergeréinitialise l'index (comme --mixed- toutes les modifications apparaissent comme des modifications locales), et réinitialise les fichiers affectés par la fusion, mais laisse les autres seuls. J'espère que cela rétablira tout ce qu'il était avant la mauvaise fusion. Vous l'utiliserez généralement comme git reset --merge(sens git reset --merge HEAD) parce que vous voulez seulement réinitialiser la fusion, pas déplacer la branche. ( HEADn'a pas encore été mis à jour, car la fusion a échoué)

    Pour être plus concret, supposons que vous avez modifié les fichiers A et B et que vous essayez de fusionner dans une branche qui a modifié les fichiers C et D. La fusion échoue pour une raison quelconque et vous décidez de l'annuler. Vous utilisez git reset --merge. Cela ramène C et D à leur état d'origine HEAD, mais laisse vos modifications à A et B seules, car elles ne faisaient pas partie de la tentative de fusion.

Vouloir en savoir davantage?

Je pense que man git resetc'est vraiment très bon pour cela - peut-être avez-vous besoin d'un peu de sens de la façon dont git fonctionne pour qu'ils s'enfoncent vraiment. En particulier, si vous prenez le temps de les lire attentivement, ces tableaux détaillant les états des fichiers dans l'index et l'arborescence de travail pour toutes les différentes options et cas sont très utiles. (Mais oui, ils sont très denses - ils transmettent énormément d'informations ci-dessus sous une forme très concise.)

Étrange notation

La "notation étrange" ( HEAD^et HEAD~1) que vous mentionnez est simplement un raccourci pour spécifier les commits, sans avoir à utiliser un nom de hachage comme 3ebe3f6. Il est entièrement documenté dans la section "spécification des révisions" de la page de manuel de git-rev-parse, avec de nombreux exemples et la syntaxe associée. Le caret et le tilde signifient en réalité des choses différentes :

  • HEAD~est l'abréviation de HEAD~1et signifie le premier parent du commit. HEAD~2signifie le premier parent du premier parent de la validation. Pensez HEAD~nà "n s'engage avant HEAD" ou "l'ancêtre de la nième génération de HEAD".
  • HEAD^(ou HEAD^1) signifie également le premier parent du commit. HEAD^2signifie le deuxième parent du commit . N'oubliez pas qu'une validation de fusion normale a deux parents - le premier parent est la validation fusionnée et le deuxième parent est la validation qui a été fusionnée. En général, les fusions peuvent avoir arbitrairement de nombreux parents (fusion de poulpe).
  • Les ^et les ~opérateurs peuvent être enfilées ensemble, comme dans HEAD~3^2le second parent de l'ancêtre de la troisième génération de HEAD, HEAD^^2, le second parent du premier parent HEAD, ou même HEAD^^^, ce qui équivaut à HEAD~3.

caret et tilde

Cascabel
la source
"vous utiliserez git reset pour le déplacer là-bas." pourquoi n'utilisez-vous pas git checkout pour le faire?
e-satis
5
@ e-satis: git checkout déplacera HEAD, mais laissera la branche où elle était. C'est pour quand vous voulez déplacer la branche.
Cascabel
Donc, si je comprends bien, réinitialiser B ferait: - A - B - C - B (maître) alors que la caisse B ferait - A - B (maître)?
e-satis
32
les documents sont bons même s'il faut une éternité pour les lire et ils sont très denses et il faut une éternité pour vérifier qu'ils disent que cela fonctionne comme si vous savez déjà comment cela fonctionne. Ça ne me semble pas que les documents me soient bons ...
Kirby
4
Une explication beaucoup plus simple et compréhensible est donnée par cette réponse SO: stackoverflow.com/questions/3528245/whats-the-difference-between-git-reset-mixed-soft-and-hard
Nitin Bansal
80

N'oubliez pas qu'en gitvous avez:

  • le HEADpointeur , qui vous indique sur quel commit vous travaillez
  • l' arborescence de travail , qui représente l'état des fichiers sur votre système
  • la zone de transit (également appelée index ), qui "met en scène" les changements de façon à ce qu'ils puissent ensuite être validés ensemble

Veuillez inclure des explications détaillées sur:

--hard, --softet --merge;

Par ordre croissant de dangerosité:

  • --softse déplace HEADmais ne touche pas la zone de préparation ou l'arbre de travail.
  • --mixeddéplace HEADet met à jour la zone de transfert, mais pas l'arborescence de travail.
  • --mergedéplace HEAD, réinitialise la zone de transit et essaie de déplacer toutes les modifications de votre arborescence de travail dans la nouvelle arborescence de travail.
  • --harddéplace HEAD et ajuste votre zone de préparation et votre arbre de travail au nouveau HEAD, jetant tout.

cas d'utilisation concrets et workflows;

  • À utiliser --softlorsque vous souhaitez passer à un autre commit et corriger les choses sans "perdre votre place". Il est assez rare que vous en ayez besoin.

-

# git reset --soft example
touch foo                            // Add a file, make some changes.
git add foo                          // 
git commit -m "bad commit message"   // Commit... D'oh, that was a mistake!
git reset --soft HEAD^               // Go back one commit and fix things.
git commit -m "good commit"          // There, now it's right.

-

  • Utilisez --mixed(qui est la valeur par défaut) lorsque vous souhaitez voir à quoi ressemblent les autres validations, mais vous ne voulez pas perdre les modifications que vous avez déjà.

  • À utiliser --mergelorsque vous souhaitez vous déplacer vers un nouvel emplacement mais incorporer les modifications que vous avez déjà dans l'arborescence de travail.

  • Utilisez --hardpour tout effacer et commencer une nouvelle ardoise au nouveau commit.

John Feminella
la source
2
Ce n'est pas le cas d'utilisation prévu pour reset --merge. Il n'effectue pas de fusion à trois. C'est vraiment uniquement pour réinitialiser les fusions conflictuelles, comme décrit dans la documentation. Vous voudrez utiliser checkout --mergepour faire ce dont vous parlez. Si vous souhaitez également déplacer la branche, je pense que la seule façon est de faire un suivi / réinitialiser pour la faire glisser.
Cascabel
@Jefromi »Oui, je n'ai pas très bien formulé cela. Par "un nouvel endroit", je voulais dire "un nouvel endroit où vous n'avez pas la fusion conflictuelle".
John Feminella
1
Ah, je vois. Je pense que l'important ici est qu'à moins que vous ne sachiez vraiment ce que vous faites, vous ne voudrez probablement jamais l'utiliser reset --mergeavec une cible en plus (par défaut) HEAD, car dans les cas en plus d'interrompre une fusion conflictuelle, cela va jeter informations que vous pourriez autrement enregistrer.
Cascabel
2
J'ai trouvé cette réponse la plus simple et la plus utile
Jazzepi
Veuillez ajouter des informations sur ces commandes: git resetet git reset -- ..
Flimm
35

Le post Reset Demystified dans le blog Pro Git donne une explication très simple sur git resetet git checkout.

Après toutes les discussions utiles en haut de cet article, l'auteur réduit les règles aux trois étapes simples suivantes:

C'est essentiellement ça. La resetcommande écrase ces trois arborescences dans un ordre spécifique, s'arrêtant lorsque vous le lui dites.

  1. Déplacez la branche sur laquelle pointe HEAD (arrêtez si --soft)
  2. ALORS, faites ressembler l'Index à cela (arrêtez-vous ici à moins que --hard)
  3. ALORS, faites ressembler le répertoire de travail

Il y a aussi --mergeet --keepoptions, mais je préfère garder les choses plus simples pour l' instant - qui sera pour un autre article.

Daniel Hershcovich
la source
25

Lorsque vous validez quelque chose à git, vous devez d'abord mettre en scène (ajouter à l'index) vos modifications. Cela signifie que vous devez git ajouter tous les fichiers que vous souhaitez inclure dans ce commit avant que git les considère comme faisant partie du commit. Jetons d'abord un coup d'œil sur l'image d'un dépôt git: entrez la description de l'image ici

donc, c'est simple maintenant. Nous devons travailler dans le répertoire de travail, créer des fichiers, des répertoires et tout. Ces modifications sont des modifications non suivies. Pour les faire suivre, nous devons les ajouter à git index en utilisant la commande git add . Une fois qu'ils sont ajoutés à git index. Nous pouvons maintenant valider ces modifications, si nous voulons le pousser vers le dépôt git.

Mais tout à coup, nous avons appris en validant que nous avons un fichier supplémentaire que nous avons ajouté dans l'index n'est pas nécessaire pour pousser dans le référentiel git. Cela signifie que nous ne voulons pas de ce fichier dans l'index. Maintenant, la question est de savoir comment supprimer ce fichier de l'index git, puisque nous avons utilisé git add pour les mettre dans l'index, il serait logique d'utiliser git rm ? Faux! git rm supprimera simplement le fichier et ajoutera la suppression à l'index. Alors que faire maintenant:

Utilisation:-

git reset

Il efface votre index, laisse votre répertoire de travail intact. (simplement décortiquer tout).

Il peut être utilisé avec un certain nombre d'options. Il existe trois options principales à utiliser avec git reset: --hard, --soft et --mixed . Ceux-ci affectent la réinitialisation de get en plus du pointeur HEAD lors de la réinitialisation.

Tout d'abord, --hard réinitialise tout. Votre répertoire actuel serait exactement le même que si vous aviez suivi cette branche tout au long. Le répertoire de travail et l'index sont modifiés pour ce commit. C'est la version que j'utilise le plus souvent. git reset --hard est quelque chose comme svn revert .

Ensuite, l'opposé complet, —soft , ne réinitialise pas l'arborescence de travail ni l'index. Il déplace uniquement le pointeur HEAD. Cela laisse votre état actuel avec des modifications différentes de la validation vers laquelle vous basculez en place dans votre répertoire, et «par étapes» pour la validation. Si vous effectuez un commit localement mais n'avez pas poussé le commit vers le serveur git, vous pouvez réinitialiser le commit précédent et recommencer avec un bon message de commit.

Enfin, --mixed réinitialise l'index, mais pas l'arborescence de travail. Ainsi, les modifications sont toujours là, mais ne sont pas "mises en scène" et devraient être git ajoutées ou git commit -a . nous l'utilisons parfois si nous avons commis plus que ce que nous voulions avec git commit -a, nous pouvons annuler le commit avec git reset --mixed, ajouter les choses que nous voulons valider et simplement les valider.

Différence entre git revert et git reset : -


En termes simples, git reset est une commande pour "corriger les erreurs non validées" et git revert est une commande pour "corriger les erreurs commises " .

Cela signifie que si nous avons fait une erreur dans un changement et que nous nous sommes engagés et poussés de la même manière pour git repo, alors git revert est la solution. Et si dans le cas où nous avons identifié la même erreur avant de pousser / valider, nous pouvons utiliser git reset pour résoudre le problème.

J'espère que cela vous aidera à vous débarrasser de votre confusion.

amour
la source
2
Ceci est une belle réponse en anglais simple comme demandé par OP.
Episodex
1
Même si je l'ai peut-être manqué dans votre réponse. Qu'est-ce que git reset HEADpar défaut? --hard, --softou --mixed? Grande réponse btw.
giannis christofakis
1
Excellente réponse, mais je dirais plus clairement que git reset --hardvous perdrez certaines données. Et il y a un point qui pourrait être faux (bien que je ne sois pas sûr à 100% ... J'apprends toujours!): Parler de --mixedvous dit que "nous utilisons parfois cela si nous nous engageons plus que ce que nous voulions avec git commit -a". Vouliez-vous dire: "si nous avons mis en scène plus que ce que nous voulions git stage ."? Si vous l'avez vraiment commis, je pense qu'il est trop tard (comme vous le dites à la fin, git reset est une commande pour "corriger les erreurs non validées")
Fabio dit Reinstate Monica
6

TL; DR

git resetréinitialise le staging au dernier commit. Permet --hardégalement de réinitialiser les fichiers de votre répertoire de travail au dernier commit.

VERSION PLUS LONGUE

Mais c'est évidemment simpliste d'où les nombreuses réponses assez verbeuses. Il était plus logique pour moi de poursuivre la lecture git resetdans le contexte de l'annulation des changements. Par exemple, voyez ceci:

Si git revert est un moyen «sûr» d'annuler les modifications, vous pouvez considérer git reset comme la méthode dangereuse. Lorsque vous annulez avec git reset (et que les validations ne sont plus référencées par aucune référence ou reflog), il n'y a aucun moyen de récupérer la copie d'origine - il s'agit d'une annulation permanente. Il faut faire attention lors de l'utilisation de cet outil, car c'est l'une des seules commandes Git qui a le potentiel de perdre votre travail.

Depuis https://www.atlassian.com/git/tutorials/undoing-changes/git-reset

et ça

Au niveau de la validation, la réinitialisation est un moyen de déplacer la pointe d'une branche vers une autre validation. Cela peut être utilisé pour supprimer les validations de la branche actuelle.

Depuis https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations

Snowcrash
la source
2

Veuillez noter qu'il s'agit d'une explication simplifiée conçue comme une première étape pour chercher à comprendre cette fonctionnalité complexe.

Peut être utile pour les apprenants visuels qui souhaitent visualiser à quoi ressemble l'état de leur projet après chacune de ces commandes:


Pour ceux qui utilisent Terminal avec la couleur activée (git config --global color.ui auto):

git reset --soft A et vous verrez les trucs de B et C en vert (mis en scène et prêts à s'engager)

git reset --mixed A(ou git reset A) et vous verrez les trucs de B et C en rouge (non mis en scène et prêts à être mis en scène (vert) puis validés)

git reset --hard A et vous ne verrez plus les changements de B et C n'importe où (ce sera comme s'ils n'avaient jamais existé)


Ou pour ceux qui utilisent un programme GUI comme «Tower» ou «SourceTree»

git reset --soft A et vous verrez les trucs de B et C dans la zone des "fichiers intermédiaires" prêts à être validés

git reset --mixed A(ou git reset A) et vous verrez les trucs de B et C dans la zone des "fichiers non mis en scène" prêts à être déplacés vers mis en scène puis validés

git reset --hard A et vous ne verrez plus les changements de B et C n'importe où (ce sera comme s'ils n'avaient jamais existé)

timhc22
la source
1

Checkout pointe la tête vers un commit spécifique.

Reset pointe une branche sur un commit spécifique. (Une branche est un pointeur vers une validation.)

Soit dit en passant, si votre tête ne pointe pas vers une validation qui est également pointée par une branche, vous avez une tête détachée. (s'est avéré être faux. Voir les commentaires ...)

Ian Warburton
la source
1
Sans pinailler, mais (oui, en fait , il est tatillonne , mais nous allons ajouter pour la fin) votre 3ème phrase est techniquement faux. Supposons que votre HEAD pointe vers la branche B qui à son tour pointe vers la validation abc123. Si vous extrayez maintenant la validation abc123, votre HEAD et votre branche B pointent tous les deux vers la validation abc123 ET votre HEAD est détaché. S'engager à ce stade ne mettra pas à jour la position de la branche B. Vous auriez pu dire "si votre tête ne pointe pas vers une branche alors vous avez une tête détachée"
RomainValeri
@RomainValeri Que fera l'engagement dans cette circonstance?
Ian Warburton
1
La validation créerait des validations qui ne sont référencées par aucune branche, et la branche B continuerait de pointer vers le même commit abc123 même après que vous l'ayez fait plusieurs fois par la suite. Cela implique que ces validations deviendraient des candidats pour la collecte des ordures lorsque HEAD cesserait de pointer sur la dernière validation de cette série «sauvage» de validations.
RomainValeri