En clair, que fait "git reset"?
J'ai vu messages intéressants expliquant les subtilités de git reset
.
Malheureusement, plus je lis à ce sujet, plus il semble que je ne le comprenne pas bien. Je viens d’un milieu SVN et Git est un tout nouveau paradigme. J'ai facilement Mercurial, mais Git est beaucoup plus technique.
Je pense que git reset
est proche de hg revert
, mais il semble y avoir des différences.
Alors, que fait exactement git reset
? Veuillez inclure des explications détaillées sur:
- les options
--hard
,--soft
et--merge
; - la notation étrange que vous utilisez avec
HEAD
telle queHEAD^
etHEAD~1
; - cas d'utilisation concrets et flux de travail;
- conséquences sur la copie de travail, la
HEAD
et votre niveau de stress global.
En général, la fonction de git reset
consiste à prendre la branche actuelle et à la réinitialiser pour qu'elle pointe ailleurs, et éventuellement à apporter l'index et l'arbre de travail. Plus concrètement, si votre branche principale (actuellement extraite) ressemble à ceci:
- A - B - C (HEAD, master)
et vous vous rendez compte que vous voulez que le maître pointe sur B, pas sur C, vous utiliserez git reset B
pour le déplacer là:
- A - B (HEAD, master) # - C is still here, but there's no branch pointing to it anymore
Digression: Ceci est différent d'une caisse. Si vous exécutiez git checkout B
, vous obtiendriez ceci:
- A - B (HEAD) - C (master)
Vous vous êtes retrouvé dans un état détaché HEAD. HEAD
, arbre de travail, indexer tous les éléments correspondants B
, mais la branche principale a été laissée derrière à C
. Si vous faites 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 commet pas de commits, il met simplement à jour une branche (qui est un pointeur sur un commit) pour pointer vers un commit différent. Le reste n'est que des détails sur ce qui arrive à votre index et à votre arbre de travail.
Cas d'utilisation
Je couvre beaucoup des principaux cas d'utilisation de git reset
dans la description des diverses options de la section suivante. Il peut vraiment être utilisé pour une grande variété de choses; le dénominateur commun est que tous impliquent la réinitialisation de la branche, de l'index et/ou de l'arbre de travail afin qu'ils pointent/correspondent à une validation donnée.
Les choses à faire attention
--hard
peut vous faire perdre beaucoup de travail. Cela modifie votre arbre de travail.git reset [options] commit
peut vous amener à (en quelque sorte) perdre des commits. Dans l'exemple ci-dessus, nous avons perdu le commitC
. Il est toujours dans le référentiel et vous pouvez le trouver en consultantgit reflog show HEAD
ougit reflog show master
, mais il n'est plus accessible depuis aucune branche.Git supprime définitivement ces commits après 30 jours, mais jusque-là, vous pouvez récupérer C en pointant une branche dessus (
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...]
, ce qui réinitialisera les chemins donnés à leur état à partir de la livraison donnée. Si les chemins ne sont pas fournis, toute l'arborescence est réinitialisée et si la validation n'est pas fournie, elle est considérée comme étant HEAD (la validation actuelle). Il s'agit d'un modèle courant dans les commandes git (par exemple, checkout, diff, log, même si la sémantique exacte varie), il ne devrait donc pas être trop surprenant.
Par exemple, git reset other-branch path/to/foo
réinitialise tout le contenu du chemin/à/foo à son état dans une autre branche, git reset -- .
réinitialise le répertoire en cours à son état dans HEAD, et un simple git reset
réinitialise tout à son état en tête.
L'arbre de travail principal et les options d'index
Il existe quatre options principales pour contrôler l’évolution de votre arbre de travail et de votre index lors de la réinitialisation.
Rappelez-vous que l'index est la "zone de transit" de git - c'est là que les choses vont quand vous dites git add
en préparation de la validation.
--hard
fait en sorte que tout corresponde au commit que vous avez réinitialisé. C'est le plus facile à comprendre, probablement. Tous vos changements locaux sont obstrués. Une des utilisations principales est d’évacuer votre travail mais pas de commettre des commits:git reset --hard
signifiegit reset --hard HEAD
, c’est-à-dire ne changez pas de branche, mais supprimez tous les changements locaux. L'autre consiste simplement à déplacer une branche d'un endroit à un autre et à maintenir synchrone index/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 de lancerreset --hard
.--mixed
est la valeur par défaut, c'est-à-diregit reset
signifiegit reset --mixed
. Il réinitialise l'index, mais pas l'arbre de travail. Cela signifie que tous vos fichiers sont intacts, mais toute différence entre le commit original et celui que vous avez réinitialisé apparaîtra sous forme de modifications locales (ou de fichiers non suivis) avec le statut git. Utilisez cette option lorsque vous réalisez que vous avez commis des erreurs, mais que vous souhaitez conserver tout le travail que vous avez effectué afin de pouvoir y remédier et de recommencer. Pour pouvoir valider, vous devrez à nouveau ajouter des fichiers à l'index (git add ...
).--soft
ne touche pas l'index ou l'arbre de travail. Tous vos fichiers sont intacts, comme avec--mixed
, mais toutes les modifications apparaissent sous la formechanges to be committed
avec le statut git (c’est-à-dire archivées en vue de la validation). Utilisez cette option lorsque vous réalisez que vous avez commis de mauvaises actions, mais que tout le travail est bon - tout ce que vous avez à faire est de les réengager différemment. L'index n'est pas touché, vous pouvez donc valider immédiatement si vous le souhaitez. Le commit résultant aura le même contenu que celui que vous aviez avant la réinitialisation.--merge
a été ajouté récemment et est destiné à vous aider à abandonner une fusion ayant échoué. Cela est nécessaire cargit merge
vous permettra en fait de fusionner avec un arbre de travail altéré (avec des modifications locales), à condition que ces modifications soient dans des fichiers non affectés par la fusion.git reset --merge
réinitialise l'index (comme--mixed
- toutes les modifications s'affichent sous forme de modifications locales), 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 commegit reset --merge
(ce qui signifiegit reset --merge HEAD
) parce que vous souhaitez uniquement réinitialiser la fusion, mais pas réellement déplacer la branche. (HEAD
n'a pas encore été mis à jour, car la fusion a échoué)Pour être plus concret, supposons que vous ayez modifié les fichiers A et B et que vous tentiez de fusionner une branche ayant modifié les fichiers C et D. La fusion échoue pour une raison quelconque et vous décidez de l'abandonner. Vous utilisez
git reset --merge
. Cela ramène C et D à leur position dansHEAD
, mais laisse vos modifications à A et B seules, car elles ne faisaient pas partie de la tentative de fusion.
Veut en savoir plus?
Je pense que man git reset
est vraiment très bon pour cela. Peut-être avez-vous besoin de comprendre un peu la façon dont git fonctionne pour eux, mais pour qu'ils puissent réellement s'imprégner. En particulier, si vous prenez le temps de les lire attentivement, ces tableaux détaillant l’état des fichiers dans l’index et l’arborescence de travail de toutes les options et de tous les cas sont très utiles. (Mais oui, ils sont très denses - ils transmettent une grande quantité des informations ci-dessus sous une forme très concise.)
Notation étrange
La "notation étrange" (HEAD^
et HEAD~1
) que vous mentionnez est simplement un raccourci pour spécifier des commits, sans avoir à utiliser un nom de hachage comme 3ebe3f6
. Il est entièrement documenté dans la section section "" spécification des révisions " de la page de manuel de git-rev-parse, avec de nombreux exemples et la syntaxe correspondante. Le caret et le tilde signifient en fait choses différentes :
HEAD~
est l'abréviation deHEAD~1
et signifie le premier parent du commit.HEAD~2
signifie le premier parent du commit. Pensez àHEAD~n
comme "n commets avant HEAD" ou "l'ancêtre de la nième génération de HEAD".HEAD^
(ouHEAD^1
) signifie également le premier parent du commit.HEAD^2
signifie le parent second du = commit. N'oubliez pas qu'un commit de fusion normal a deux parents: le premier parent est le commit fusionné dans, et le second parent est le commit qui a été fusionné. En général, les fusions peuvent avoir arbitrairement beaucoup de parents (fusions de poulpes).- Les opérateurs
^
et~
peuvent être liés ensemble, comme dansHEAD~3^2
, le deuxième parent de l'ancêtre de troisième génération deHEAD
,HEAD^^2
, le second. parent du premier parent deHEAD
, ou mêmeHEAD^^^
, ce qui équivaut àHEAD~3
.
Rappelez-vous que dans git
vous avez:
- le
HEAD
pointeur, qui vous indique le commit sur lequel vous travaillez - le arbre de travail, qui représente l'état des fichiers sur votre système
- le zone intermédiaire (également appelé le index), qui "modifie" les modifications de manière à ce qu'elles puissent ensuite être validées ensemble
Veuillez inclure des explications détaillées sur:
--hard
,--soft
et--merge
;
Par ordre croissant de dangerosité:
--soft
déplaceHEAD
mais ne touche pas la zone de travail intermédiaire ni l'arbre de travail.--mixed
déplaceHEAD
et met à jour la zone de transfert, mais pas l'arbre de travail.--merge
déplaceHEAD
, réinitialise la zone de transfert et tente de transférer toutes les modifications de votre arbre de travail dans le nouvel arbre de travail.--hard
se déplaceHEAD
et ajuste votre zone de travail intermédiaire et votre arbre de travail sur la nouvelleHEAD
, en jetant tout.
cas d'utilisation concrets et flux de travail;
- Utilisez
--soft
lorsque vous souhaitez passer à un autre commit et corriger les modifications sans "perdre votre place". C'est assez rare que vous ayez besoin de ça.
-
# 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
(la valeur par défaut) lorsque vous souhaitez voir à quoi ressemble un autre commit, mais que vous ne voulez pas perdre les modifications que vous avez déjà.Utilisez
--merge
lorsque vous souhaitez passer à un nouvel emplacement tout en intégrant les modifications déjà apportées dans l’arbre de travail.Utilisez
--hard
pour tout effacer et commencer une nouvelle ardoise au nouveau commit.
Le message Réinitialiser démystifié dans le blog Pro Git donne une explication très simple sur git reset
et git checkout
.
Après toute la discussion utile au début de ce post, l'auteur réduit les règles aux trois étapes simples suivantes:
C'est fondamentalement ça. La commande
reset
écrase ces trois arbres dans un ordre spécifique, en s'arrêtant lorsque vous le leur dites.
- Déplacer n'importe quelle branche HEAD pointe sur (arrête si
--soft
)- ALORS, faites ressembler l’index à ça (arrêtez ici sauf si
--hard
)- ALORS, faire ressembler le répertoire de travail à ça
Il y a aussi les options
--merge
et--keep
, mais je préférerais que les choses restent plus simples pour l'instant - ce sera pour un autre article.
Lorsque vous validez quelque chose, vous devez d’abord mettre en forme (ajouter à l’index) vos modifications. Cela signifie que vous devez ajouter tous les fichiers que vous voulez 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:
alors, c'est simple maintenant. Nous devons travailler dans un répertoire de travail, créer des fichiers, des répertoires et tout. Ces modifications sont des modifications non suivies. Pour les 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 transférer dans le référentiel git.
Mais tout à coup nous avons appris en commettant que nous avons un fichier supplémentaire que nous avons ajouté dans index n’est pas requis pour le dépôt Push in git. Cela signifie que nous ne voulons pas que ce fichier soit 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. (il suffit de tout dégrader).
Il peut être utilisé avec plusieurs options. Il existe trois options principales à utiliser avec git reset: --hard, --soft et --mixed. Ceux-ci affectent ce qui doit être réinitialisé en plus du pointeur HEAD lorsque vous réinitialisez.
Tout d'abord, - difficile réinitialise tout. Votre répertoire actuel serait exactement comme si vous suiviez cette branche depuis le début. Le répertoire de travail et l'index sont modifiés pour cette validation. 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 ni l’arbre de travail ni l’index. Il déplace uniquement le pointeur HEAD. Cela laisse votre état actuel avec des modifications différentes de celles du commit que vous passez en place dans votre annuaire et de la "mise en scène" pour la validation. Si vous faites une validation localement mais n’avez pas poussé celle-ci sur le serveur git, vous pouvez rétablir la validation précédente et la renvoyer avec un message de validation correct.
Enfin, - mixed réinitialise l'index, mais pas l'arbre de travail. Donc, tous les changements sont toujours là, mais sont "non mis en scène" et auraient besoin d'être additionnés de git ou git commit -a. nous l'utilisons parfois si nous avons commis plus que ce que nous voulions faire avec git commit -a, nous pouvons annuler le commit avec git reset --mixed, ajouter les éléments que nous souhaitons commettre et ne les commettre que.
Différence entre git revert et git reset : -
En termes simples, git reset est une commande pour "corriger les erreurs non commises" et git revert est une commande pour "fix erreur commise ".
Cela signifie que si nous avons commis une erreur dans un changement et que nous avons commis la même chose et que nous avons poussé la même chose vers git repo, alors git revert est la solution. Et si dans le cas où nous avions identifié la même erreur avant de pousser/commettre, nous pouvons utiliser git reset pour résoudre le problème.
J'espère que cela vous aidera à vous débarrasser de votre confusion.
TL; DR
git reset
réinitialise Staging à la dernière validation. Utilisez--hard
pour réinitialiser également les fichiers de votre répertoire de travail sur le dernier commit.
VERSION PLUS LONGUE
Mais c'est évidemment simpliste d'où les nombreuses réponses plutôt verbeuses. Il était plus logique pour moi de lire git reset
dans le contexte de l'annulation des modifications. Par exemple. regarde ça:
Si git revert est un moyen "sûr" d'annuler les modifications, vous pouvez envisager de réinitialiser git comme une méthode dangereuse. Lorsque vous annulez avec git reset (et que les commits ne sont plus référencés ni par refog), il n’ya aucun moyen de récupérer la copie originale; c’est une annulation permanente. Vous devez faire attention lorsque vous utilisez cet outil, car il s’agit de l’une des seules commandes Git pouvant potentiellement perdre votre travail.
De 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 le sommet d’une branche vers un commit différent. Ceci peut être utilisé pour supprimer les commits de la branche actuelle.
De https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting/commit-level-operations
Veuillez noter qu’il s’agit d’une explication simplifiée conçue comme une première étape pour tenter de comprendre cette fonctionnalité complexe.
Peut être utile pour les apprenants visuels qui souhaitent visualiser 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 le contenu de B et C en vert (mis en scène et prêt à être utilisé)
git reset --mixed A
(ou git reset A
) et vous verrez les informations de B et C en rouge (non mises en scène et prêtes à être mises en scène (vert) puis validées)
git reset --hard A
et vous ne verrez plus nulle part les modifications de B et C (comme si elles n'avaient jamais existé)
Ou pour ceux qui utilisent un programme graphique comme "Tower" ou "SourceTree"
git reset --soft A
et vous verrez les éléments de B et C dans la zone 'fichiers de stockage' prêts à être validés
git reset --mixed A
(ou git reset A
) et vous verrez les données de B et C dans la zone "fichiers non gérés" prêtes à être déplacées vers un processus, puis validées.
git reset --hard A
et vous ne verrez plus nulle part les modifications de B et C (comme si elles n'avaient jamais existé)
Checkout pointe la tête vers un commit spécifique.
Réinitialiser pointe une branche sur un commit spécifique. (Une branche est un pointeur sur un commit.)
Incidemment, si votre tête ne pointe pas vers un commit qui est également indiqué par une branche, vous avez alors une tête détachée.(s'est avéré être faux. Voir les commentaires ...)