Comment annuler la réinitialisation de git --hard HEAD ~ 1?

1160

Est-il possible d'annuler les modifications provoquées par la commande suivante? Si c'est le cas, comment?

git reset --hard HEAD~1
Paul Wicks
la source
8
J'ai écrit un guide complet pour récupérer tout commit perdu avec git. Il a même des illustrations :-) [Découvrez-le] [fixLink] [fixLink]: programblings.com/2008/06/07/…
webmat
12
--hardrejette les modifications non validées. Étant donné que ceux-ci ne sont pas suivis par git, il n'y a aucun moyen de les restaurer via git.
Zaz
1
Ceci est un excellent article pour vous aider à récupérer vos fichiers.
Jan Swart
2
Ceci est une excellente ressource directement de Github: Comment annuler (presque) n'importe quoi avec Git
jasonleonhard

Réponses:

1775

Pat Notz a raison. Vous pouvez récupérer le commit tant qu'il a été effectué en quelques jours. git ne récupère que les ordures après environ un mois, sauf si vous lui demandez explicitement de supprimer les nouveaux blobs.

$ git init
Initialized empty Git repository in .git/

$ echo "testing reset" > file1
$ git add file1
$ git commit -m 'added file1'
Created initial commit 1a75c1d: added file1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file1

$ echo "added new file" > file2
$ git add file2
$ git commit -m 'added file2'
Created commit f6e5064: added file2
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file2

$ git reset --hard HEAD^
HEAD is now at 1a75c1d... added file1

$ cat file2
cat: file2: No such file or directory

$ git reflog
1a75c1d... HEAD@{0}: reset --hard HEAD^: updating HEAD
f6e5064... HEAD@{1}: commit: added file2

$ git reset --hard f6e5064
HEAD is now at f6e5064... added file2

$ cat file2
added new file

Vous pouvez voir dans l'exemple que le fichier2 a été supprimé à la suite de la réinitialisation matérielle, mais a été remis en place lorsque je réinitialise via le reflog.

Brian Riehman
la source
222
Vous pouvez utiliser "git reset --hard HEAD @ {1}", pas besoin d'utiliser SHA1. Dans la plupart des cas, il devrait suffire d'utiliser "git reset --hard ORIG_HEAD".
Jakub Narębski
39
git log -gpeut être un moyen un peu plus agréable de visualiser le reflog que git reflog.
Dan Moulding du
60
Il y a une mise en garde très importante avec cela .. et c'est la partie "--hard". --hard supprime vos modifications locales non validées. Et vous ne pouvez pas les récupérer comme ça (car ils n'ont été engagés nulle part). Je crois que vous ne pouvez rien y faire :(
Michael Anderson
3
^ Juste pour que vous sachiez que vous pouvez cacher vos modifications locales avant de faire une réinitialisation - dur, puis simplement les faire éclater et vous ne perdez rien! Je dois aimer ce connard.
Lethjakman
5
Je préfère git reflogplus git log -gsimplement parce que vous obtenez toutes les informations sur une ligne avec SHA1, informations HEAD et commits tous alignés. Beaucoup plus facile à lire.
Snowcrash
371

Ce que vous voulez faire, c'est spécifier le sha1 du commit sur lequel vous souhaitez restaurer. Vous pouvez obtenir le sha1 en examinant le reflog ( git reflog) puis en faisant

git reset --hard <sha1 of desired commit>

Mais n'attendez pas trop longtemps ... après quelques semaines, git verra finalement ce commit comme non référencé et supprimera tous les blobs.

Pat Notz
la source
2
avertissement pour moi-même il y a quelques minutes: cela réinitialisera toutes les modifications. il ne touchera cependant pas les fichiers non suivis.
aexl
175

La réponse est cachée dans la réponse détaillée ci-dessus, vous pouvez simplement faire:

$> git reset --hard HEAD@{1}

(Voir la sortie de git reflog show )

markmc
la source
17
Notez que ce n'est pas la solution si vous avez apporté d'autres modifications au référentiel depuis la réinitialisation. Jetez un coup d'œil au reflog avant d'exécuter quoi que ce soit.
forresthopkinsa
2
Réinitialiser accidentellement mon référentiel, pensant que mon travail était perdu pour toujours. Cette réponse m'a sauvé la journée.
Zaiyang Li
woh! tu me
sauves
Génial. Ça m'a vraiment aidé.
Prashant Biradar
117

Il est possible de le récupérer si Git n'a pas encore récupéré les ordures.

Obtenez un aperçu des commissions pendantes avec fsck:

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

Récupérez le commit pendant avec rebase:

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf
sverrejoh
la source
1
C'est génial, des vies sauvées.
Mohhamad Hasham
Une explication détaillée peut être trouvée ici: medium.com/@CarrieGuss/… . Des trucs qui sauvent des vies.
tuan.dinh
49

Si vous êtes vraiment chanceux, comme je l'ai été, vous pouvez retourner dans votre éditeur de texte et appuyer sur «Annuler».

Je sais que ce n'est pas vraiment une bonne réponse, mais cela m'a épargné une demi-journée de travail, alors j'espère que cela fera la même chose pour quelqu'un d'autre!

Chris
la source
3
C'est en fait une très bonne astuce, ça m'a fait gagner beaucoup de temps;) Et c'est plus simple que de faire quoi que ce soit dans git ...
Severin
3
et merci de toute bonté gracieuse dans ce monde entier. Merci. Merci. Merci.
Voyage
9
C'est le seul moyen de récupérer les modifications non mises en scène des fichiers après la réinitialisation matérielle.
M'a
4
Comme indice supplémentaire, certains IDE comme éclipse ont également enregistré l'historique des fichiers récents. De cette façon, vous pourrez même récupérer des modifications plus anciennes après la fermeture de l'éditeur. Cela a fait des merveilles pour moi.
martin
1
Que Dieu vous bénisse Chris
Adam Waite
45

autant que je sache, --hardrejettera les modifications non validées. Puisque ceux-ci ne sont pas suivis par git. mais vous pouvez annuler le discarded commit.

$ git reflog

répertorie:

b0d059c HEAD@{0}: reset: moving to HEAD~1
4bac331 HEAD@{1}: commit: added level introduction....
....

4bac331est le discarded commit.

Maintenant, déplacez simplement la tête vers ce commit ::

$ git reset --hard 4bac331
suhailvs
la source
34

Exemple de cas IRL:

$ git fsck --lost-found

Checking object directories: 100% (256/256), done.
Checking objects: 100% (3/3), done.
dangling blob 025cab9725ccc00fbd7202da543f556c146cb119
dangling blob 84e9af799c2f5f08fb50874e5be7fb5cb7aa7c1b
dangling blob 85f4d1a289e094012819d9732f017c7805ee85b4
dangling blob 8f654d1cd425da7389d12c17dd2d88d318496d98
dangling blob 9183b84bbd292dcc238ca546dab896e073432933
dangling blob 1448ee51d0ea16f259371b32a557b60f908d15ee
dangling blob 95372cef6148d980ab1d7539ee6fbb44f5e87e22
dangling blob 9b3bf9fb1ee82c6d6d5ec9149e38fe53d4151fbd
dangling blob 2b21002ca449a9e30dbb87e535fbd4e65bac18f7
dangling blob 2fff2f8e4ea6408ac84a8560477aa00583002e66
dangling blob 333e76340b59a944456b4befd0e007c2e23ab37b
dangling blob b87163c8def315d40721e592f15c2192a33816bb
dangling blob c22aafb90358f6bf22577d1ae077ad89d9eea0a7
dangling blob c6ef78dd64c886e9c9895e2fc4556e69e4fbb133
dangling blob 4a71f9ff8262701171d42559a283c751fea6a201
dangling blob 6b762d368f44ddd441e5b8eae6a7b611335b49a2
dangling blob 724d23914b48443b19eada79c3eb1813c3c67fed
dangling blob 749ffc9a412e7584245af5106e78167b9480a27b
dangling commit f6ce1a403399772d4146d306d5763f3f5715cb5a    <- it's this one

$ git show f6ce1a403399772d4146d306d5763f3f5715cb5a

commit f6ce1a403399772d4146d306d5763f3f5715cb5a
Author: Stian Gudmundsen Høiland <[email protected]>
Date:   Wed Aug 15 08:41:30 2012 +0200

    *MY COMMIT MESSAGE IS DISPLAYED HERE*

diff --git a/Some.file b/Some.file
new file mode 100644
index 0000000..15baeba
--- /dev/null
+++ b/Some.file
*THE WHOLE COMMIT IS DISPLAYED HERE*

$ git rebase f6ce1a403399772d4146d306d5763f3f5715cb5a

First, rewinding head to replay your work on top of it...
Fast-forwarded master to f6ce1a403399772d4146d306d5763f3f5715cb5a.
Stian Høiland
la source
2
La goutte pendante ressemble à un monstre AD&D!
sbichenko
Merci @Stian bien expliqué! Je voudrais ajouter pour ceux qui trouvent cette réponse que si vous avez plus d'un commit "pendant", vous n'êtes pas sûr de vouloir rebaser sur la dernière ligne :)
JimiSweden
git show a sauvé certains de mes fichiers, merci beaucoup mec!
William
32

Dans la plupart des cas, oui.

Selon l'état dans lequel se trouvait votre référentiel lorsque vous avez exécuté la commande, les effets de git reset --hardpeuvent varier de triviaux à annuler, voire fondamentalement impossibles.

Ci-dessous, j'ai énuméré une gamme de différents scénarios possibles et comment vous pouvez en récupérer.

Tous mes changements ont été validés, mais maintenant les validations ont disparu!

Cette situation se produit généralement lorsque vous exécutez git resetavec un argument, comme dansgit reset --hard HEAD~ . Ne vous inquiétez pas, c'est facile à récupérer!

Si vous venez de courir git resetet n'avez rien fait d'autre depuis, vous pouvez revenir où vous étiez avec ce one-liner:

git reset --hard @{1}

Cela réinitialise votre branche actuelle quel que soit l'état dans lequel elle se trouvait avant la dernière modification (dans votre cas, la modification la plus récente de la branche serait la réinitialisation matérielle que vous essayez d'annuler).

Si, cependant, vous avez apporté d'autres modifications à votre branche depuis la réinitialisation, le one-liner ci-dessus ne fonctionnera pas. Au lieu de cela, vous devez exécuter pour voir une liste de toutes les modifications récentes apportées à votre branche (y compris les réinitialisations). Cette liste ressemblera à ceci:git reflog <branchname>

7c169bd master@{0}: reset: moving to HEAD~
3ae5027 master@{1}: commit: Changed file2
7c169bd master@{2}: commit: Some change
5eb37ca master@{3}: commit (initial): Initial commit

Recherchez l'opération dans cette liste que vous souhaitez "annuler". Dans l'exemple ci-dessus, ce serait la première ligne, celle qui dit "reset: passer à HEAD ~". Copiez ensuite la représentation du commit avant (ci-dessous) cette opération. Dans notre cas, ce serait master@{1}(ou 3ae5027, ils représentent tous deux le même commit), et exécutezgit reset --hard <commit> pour réinitialiser votre branche actuelle sur ce commit.

J'ai organisé mes changements avec git add , mais jamais engagé. Maintenant, mes changements ont disparu!

C'est un peu plus difficile à récupérer. git possède des copies des fichiers que vous avez ajoutés, mais comme ces copies n'ont jamais été liées à un commit particulier, vous ne pouvez pas restaurer les modifications d'un coup. Au lieu de cela, vous devez localiser les fichiers individuels dans la base de données de git et les restaurer manuellement. Vous pouvez le faire en utilisantgit fsck .

Pour plus de détails à ce sujet, voir Annuler la réinitialisation de git --hard avec des fichiers non validés dans la zone de transfert .

J'ai eu des modifications de fichiers dans mon répertoire de travail avec lesquelles je n'ai jamais mis en scène git add et jamais commis. Maintenant, mes changements ont disparu!

Euh oh. Je déteste te dire ça, mais tu n'as probablement pas de chance. git ne stocke pas les modifications que vous n'y ajoutez pas ou ne vous engagez pas, et selon la documentation degit reset :

--difficile

Réinitialise l'index et l'arborescence de travail. Toutes les modifications apportées aux fichiers suivis dans l'arborescence de travail depuis <commit>sont ignorées.

Il est possible que vous puissiez récupérer vos modifications avec une sorte d'utilitaire de récupération de disque ou un service de récupération de données professionnel, mais à ce stade, c'est probablement plus de problèmes que cela ne vaut.

Ajedi32
la source
1
One-liner a fonctionné pour moi, merci, mais je me demande juste ce que fait exactement "@ {1}" ..
Stan Bashtavenko
1
@StanB La documentation est ici: git-scm.com/docs/git-rev-parse fondamentalement, elle fait référence à la première entrée reflog sur la branche actuelle.
Ajedi32
Merci d'avoir couvert tous les cas. Je n'avais pas engagé ou ajouté le mien.
xdhmoore
21

Si vous n'avez pas encore récupéré votre mémoire (par exemple en utilisant git repack -dougit gc , mais notez que le nettoyage peut également se produire automatiquement), alors votre commit est toujours là - il n'est tout simplement plus accessible via HEAD.

Vous pouvez essayer de trouver votre commit en parcourant la sortie de git fsck --lost-found .

Les versions plus récentes de Git ont quelque chose appelé le «reflog», qui est un journal de toutes les modifications apportées aux références (par opposition aux modifications apportées au contenu du référentiel). Ainsi, par exemple, chaque fois que vous changez de HEAD (c'est-à-dire chaque fois que vous faites un git checkoutpour changer de branche) qui sera enregistré. Et, bien sûr, vous avez git resetégalement manipulé le HEAD, il était donc également enregistré. Vous pouvez accéder aux anciens états de vos références de la même manière que vous pouvez accéder aux anciens états de votre référentiel, en utilisant un @signe au lieu d'un ~, commegit reset HEAD@{1} .

Il m'a fallu un certain temps pour comprendre la différence entre HEAD @ {1} et HEAD ~ 1, voici donc une petite explication:

git init
git commit --allow-empty -mOne
git commit --allow-empty -mTwo
git checkout -b anotherbranch
git commit --allow-empty -mThree
git checkout master # This changes the HEAD, but not the repository contents
git show HEAD~1 # => One
git show HEAD@{1} # => Three
git reflog

Donc, HEAD~1signifie "aller au commit avant le commit auquel HEAD pointe actuellement", tandis queHEAD@{1} signifie "aller au commit sur lequel HEAD a pointé avant qu'il pointe vers l'endroit où il pointe actuellement".

Cela vous permettra facilement de retrouver votre commit perdu et de le récupérer.

Jörg W Mittag
la source
2
Une autre explication qui, je pense, serait plus claire: HEAD ~ 1 signifie aller au "parent de HEAD", tandis que HEAD @ {1} aller à "remonter une étape dans l'histoire de HEAD"
kizzx2
1
Le problème est que le terme «historique» est vraiment surchargé dans les VCS. Une autre façon d'exprimer serait que ~ recule dans l' histoire de la validation , tandis que @ recule dans l'histoire chronologique ou temporelle . Mais aucune des trois versions n'est particulièrement bonne.
Jörg W Mittag
@ kizzx2 (et Jorg) en fait ces 3 explications, prises ensemble, aident beaucoup - thx
Richard Le Mesurier
14

Avant de répondre, ajoutons quelques informations, expliquant ce que c'est HEAD.

First of all what is HEAD?

HEADest simplement une référence au commit actuel (le plus récent) sur la branche courante.
Il ne peut y en avoir qu'un seul HEADà un moment donné. (à l'exclusiongit worktree )

Le contenu de HEADest stocké à l'intérieur .git/HEADet contient les 40 octets SHA-1 de la validation en cours.


detached HEAD

Si vous n'êtes pas sur le dernier commit - ce qui signifie qu'il HEADpointe vers un commit antérieur dans l'historique, c'est appelé detached HEAD.

entrez la description de l'image ici

Sur la ligne de commande, cela ressemblera à ceci - SHA-1 au lieu du nom de la branche car le HEADne pointe pas vers la pointe de la branche actuelle

entrez la description de l'image ici


Quelques options sur la façon de récupérer à partir d'une tête détachée:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Cela va extraire une nouvelle branche pointant vers le commit souhaité.
Cette commande extrait un commit donné.
À ce stade, vous pouvez créer une branche et commencer à travailler à partir de ce point.

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Vous pouvez également toujours utiliser le reflog.
git reflogaffichera tout changement qui a mis à jour le HEADet vérifier l'entrée de reflog souhaitée mettra le HEADdos à ce commit.

Chaque fois que le HEAD est modifié, il y aura une nouvelle entrée dans le reflog

git reflog
git checkout HEAD@{...}

Cela vous ramènera à votre engagement souhaité

entrez la description de l'image ici


git reset HEAD --hard <commit_id>

"Déplacer" votre tête vers le commit souhaité.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.
  • Remarque: ( depuis Git 2.7 ),
    vous pouvez également utiliser le git rebase --no-autostash.


git revert <sha-1>

"Annuler" la validation ou la plage de validation donnée.
La commande de réinitialisation "annulera" toutes les modifications apportées au commit donné.
Un nouveau commit avec le patch d'annulation sera validé tandis que le commit d'origine restera également dans l'historique.

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Ce schéma illustre quelle commande fait quoi.
Comme vous pouvez le voir, reset && checkoutmodifiez le HEAD.

entrez la description de l'image ici

CodeWizard
la source
Il semble que votre git reset HEAD --hard <commit_id>exemple soit tiré de stackoverflow.com/questions/4114095/… - Si tel est le cas, pourriez-vous modifier dans l'attribution?
Rob
12

git reflog

  • Trouvez votre commit sha dans la liste puis copiez et collez-le dans cette commande:

git cherry-pick <the sha>

ScottyBlades
la source
1
git-cherry-pick - Applique les changements introduits par certaines validations existantes. Je pense que c'est simple et très utile dans ce contexte
Amitesh
1
tout le monde le recherche réellement lorsqu'il cherche comment annuler les modifications de réinitialisation matérielle. cette réponse devrait obtenir plus de votes positifs
ErenL
@ErenL Je suppose que je viens de comprendre que les gens aiment faire du travail supplémentaire. haha
ScottyBlades
1
Ça y est, vous venez de sauver ma journée Sylv
Sylvernus Akubo
11

Je sais que c'est un vieux fil de discussion ... mais comme beaucoup de gens cherchent des moyens d'annuler des choses dans Git, je pense toujours que ce serait une bonne idée de continuer à donner des conseils ici.

Lorsque vous faites un "git add" ou déplacez quoi que ce soit du haut à gauche vers le bas à gauche dans git gui, le contenu du fichier est stocké dans un blob et le contenu du fichier est possible de récupérer à partir de ce blob.

Il est donc possible de récupérer un fichier même s'il n'a pas été validé mais il doit avoir été ajouté.

git init  
echo hello >> test.txt  
git add test.txt  

Maintenant, le blob est créé, mais il est référencé par l'index, il ne sera donc pas répertorié avec git fsck jusqu'à ce que nous le réinitialisions. Nous avons donc réinitialisé ...

git reset --hard  
git fsck  

vous obtiendrez une goutte pendante ce013625030ba8dba906f756967f9e9ca394464a

git show ce01362  

vous donnera le contenu du fichier "bonjour" retour

Pour trouver des commits non référencés, j'ai trouvé un conseil quelque part suggérant cela.

gitk --all $(git log -g --pretty=format:%h)  

Je l'ai comme outil dans git gui et c'est très pratique.

Martin
la source
+1. Comme mentionné dans stackoverflow.com/a/21350689/6309 , git fsck --lost-foundpeut vous aider.
VonC
7

Si vous utilisez un IDE JetBrains (tout ce qui est basé sur IntelliJ), vous pouvez récupérer même vos modifications non validées via leur fonctionnalité "Historique local".

Faites un clic droit sur votre répertoire de niveau supérieur dans votre arborescence de fichiers, recherchez "Historique local" dans le menu contextuel et choisissez "Afficher l'historique". Cela ouvrira une vue où vos modifications récentes peuvent être trouvées, et une fois que vous avez trouvé la révision à laquelle vous souhaitez revenir, faites un clic droit dessus et cliquez sur "Revenir".

Newbyte
la source
4

Je viens de faire une réinitialisation matérielle sur un mauvais projet. Ce qui m'a sauvé la vie, c'est l'histoire locale d'Eclipse. IntelliJ Idea en aurait également un, et votre éditeur aussi, il vaut la peine de le vérifier:

  1. Rubrique d'aide d'Eclipse sur l'historique local
  2. http://wiki.eclipse.org/FAQ_Where_is_the_workspace_local_history_stored%3F
Maciek Łoziński
la source
1
L'histoire locale de Jetbrains CLion est superbe et m'a sauvé 2 heures de travail :)
Fabian Knapp
3

Création d'un petit script pour faciliter la recherche du commit recherché:

git fsck --lost-found | grep commit | cut -d ' ' -f 3 | xargs -i git show \{\} | egrep '^commit |Date:'

Oui, cela peut être rendu beaucoup plus joli avec awk ou quelque chose comme ça, mais c'est simple et j'en avais juste besoin. Pourrait sauver quelqu'un d'autre 30 secondes.

neurone
la source
0

Mon problème est presque similaire. J'ai des fichiers non validés avant d'entrer git reset --hard.

Heureusement. J'ai réussi à ignorer toutes ces ressources. Après avoir remarqué que je pouvais juste annuler ( ctrl-z). 😊 Je veux juste ajouter ceci à toutes les réponses ci-dessus.

Remarque. Il n'est pas possible d' ctrl-zouvrir des fichiers.

Vadamadafaka
la source
0

Cela m'a sauvé la vie: https://medium.com/@CarrieGuss/how-to-recover-from-a-git-hard-reset-b830b5e3f60c

Fondamentalement, vous devez exécuter:

for blob in $(git fsck --lost-found | awk ‘$2 == “blob” { print $3 }’); do git cat-file -p $blob > $blob.txt; done

Ensuite, passez manuellement à travers la douleur pour réorganiser vos fichiers dans la bonne structure.

À emporter: ne jamais utiliser git reset --hardsi vous ne comprenez pas à 100% comment cela fonctionne, mieux vaut ne pas l'utiliser.

tuan.dinh
la source