130 votes

Annulation de git reset --hard avec des fichiers non validés dans la zone de transit

J'essaie de récupérer mon travail. J'ai bêtement fait git reset --hard mais avant cela, je n'ai fait que get add . et n'a pas fait git commit . Aidez-moi ! Voici mon journal :

MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

#   modified:   .gitignore
...

MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api

Est-il possible d'annuler git reset --hard dans cette situation ?

0 votes

@MarkLongair génial mec ! Tu viens de récupérer mon travail ! J'ai écrit un script Python pour créer des fichiers de toutes les sorties ! Je vais ajouter le script comme réponse

6 votes

Pas "stupidement"... mais "naïvement"... parce que je viens de faire la même chose !

0 votes

Cela pourrait encore être stupidement ;-)

201voto

Mark Longair Points 93104

Vous devriez pouvoir récupérer tous les fichiers que vous avez ajoutés à l'index (par exemple, comme dans votre situation, avec git add . ) bien que cela puisse représenter un peu de travail. Afin d'ajouter un fichier à l'index, git l'ajoute à la base de données des objets, ce qui signifie qu'il peut être récupéré tant que le garbage collection n'a pas encore eu lieu. Il y a un exemple de comment faire cela donné dans La réponse de Jakub Narebski ici :

Cependant, j'ai essayé cela sur un référentiel de test, et il y a eu quelques problèmes --cached devrait être --cache et j'ai constaté qu'il ne créait pas réellement le fichier .git/lost-found répertoire. Cependant, les étapes suivantes ont fonctionné pour moi :

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

Cela devrait donner tous les objets de la base de données des objets qui ne sont pas accessibles par une référence, dans l'index, ou via le reflog. La sortie ressemblera à quelque chose comme ceci :

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... et pour chacun de ces blobs, vous pouvez faire :

git show 907b308

Pour sortir le contenu du fichier.


Trop de production ?

Mise à jour en réponse à sehe Le commentaire de l'auteur ci-dessous :

Si vous trouvez que vous avez beaucoup de commits et d'arbres listés dans la sortie de cette commande, vous pouvez vouloir supprimer de la sortie tous les objets qui sont référencés à partir de commits non référencés. (Typiquement, vous pouvez revenir à ces commits via le reflog de toute façon - nous sommes juste intéressés par les objets qui ont été ajoutés à l'index mais ne peuvent jamais être trouvés via un commit).

Tout d'abord, enregistrez la sortie de la commande, avec :

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all

Maintenant les noms d'objets de ces commits inaccessibles peuvent être trouvés avec :

egrep commit all | cut -d ' ' -f 3

Ainsi, vous pouvez trouver seulement les arbres et les objets qui ont été ajoutés à l'index, mais pas engagés à un moment donné, avec :

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
  $(egrep commit all | cut -d ' ' -f 3)

Cela réduit énormément le nombre d'objets à prendre en compte.


Mise à jour : Philip Oakley ci-dessous suggère une autre façon de réduire le nombre d'objets à prendre en compte, qui consiste à ne considérer que les fichiers les plus récemment modifiés dans la rubrique .git/objects . Vous pouvez les trouver avec :

find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort

(J'ai trouvé que find invocation ici .) La fin de cette liste pourrait ressembler à ceci :

2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a

Dans ce cas, vous pouvez voir ces objets avec :

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

(Notez que vous devez retirer le / à la fin du chemin pour obtenir le nom de l'objet).

0 votes

Avant de poster ma réponse, j'ai envisagé d'inclure exactement ces étapes. Cependant, en essayant ceci avec l'un de mes dépôts locaux, j'ai obtenu des centaines d'inaccessibles, et je n'ai pas pu trouver une approche adéquate pour, disons, les trier par date de soumission. Ce serait génial

0 votes

@sehe : J'ai mis à jour ma réponse suggérant comment réduire le nombre d'objets qui sont émis. Sur un référentiel existant sur lequel j'ai essayé cela, la sortie était alors tout à fait gérable.

0 votes

+1 pour quelques raffinements potentiellement utiles à mes propres procédures habituelles là !

64voto

Richard Saunders Points 311

Je viens de faire un git reset --hard et j'ai perdu un commit. Mais je connaissais le hash du commit, donc j'ai pu faire git cherry-pick COMMIT_HASH pour le restaurer.

J'ai fait cela quelques minutes après avoir perdu l'engagement, donc cela peut fonctionner pour certains d'entre vous.

5 votes

Merci Merci Merci Merci J'ai réussi à récupérer une journée de travail grâce à cette réponse.

24 votes

Il est probablement utile de mentionner que vous pouvez voir ces hachages en utilisant git reflog par exemple git reset --hard -> git reflog (en regardant le hash HEAD@{1}) et enfin git cherry-pick COMMIT_HASH

0 votes

C'est absolument le moyen le plus simple et le plus rapide de résoudre ce problème. C'est génial !

18voto

Boy Points 385

Grâce à Mark Longair, j'ai récupéré mes affaires !

D'abord, j'ai enregistré tous les hashs dans un fichier :

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes

Ensuite, je les ai tous mis (en enlevant le truc du 'blob inaccessible') dans une liste et j'ai mis les données dans de nouveaux fichiers... vous devez choisir vos fichiers et les renommer à nouveau ce dont vous avez besoin... mais je n'avais besoin que de quelques fichiers... j'espère que cela aidera quelqu'un...

commits = ["c2520e04839c05505ef17f985a49ffd42809f",
    "41901be74651829d97f29934f190055ae4e93",
    "50f078c937f07b508a1a73d3566a822927a57",
    "51077d43a3ed6333c8a3616412c9b3b0fb6d4",
    "56e290dc0aaa20e64702357b340d397213cb",
    "5b731d988cfb24500842ec5df84d3e1950c87",
    "9c438e09cf759bf84e109a2f0c18520",
    ...
    ]

from subprocess import call
filename = "file"
i = 1
for c in commits:
    f = open(filename + str(i),"wb")
    call(["git", "show", c],stdout=f)
    i+=1

1 votes

Oui ! C'est exactement ce dont j'avais besoin. J'aime aussi le script de Python pour recréer tous les fichiers. Les autres réponses me rendaient nerveux à l'idée de perdre mes données avec le garbage collection, donc vider les fichiers est une victoire pour moi :)

1 votes

J'ai écrit ce script basé sur cette réponse. Il fonctionne sans problème : github.com/pendashteh/git-recover-index

2 votes

Je trouve plus facile de l'automatiser comme ça : mkdir lost; git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") | grep -Po '\s\S{40}$' | xargs -i echo "git show {} > lost/{}.blob" | sh . Les fichiers se retrouveront dans lost/*.blob

9voto

sehe Points 123151

Erm... non, vous ne pouvez pas récupérer l'index d'avant un changement d'adresse. reset --hard . Vous ne pouvez récupérer que ce qui a été engagé/stocké et qui n'a pas encore été périmé dans les reflogs.

7voto

Duncan McGregor Points 5478

La solution de @Ajedi32 dans les commentaires a fonctionné pour moi dans exactement cette situation.

git reset --hard @{1}

Notez que toutes ces solutions reposent sur l'absence de git gc, et que certaines d'entre elles peuvent en provoquer un. Je vous conseille donc de zipper le contenu de votre répertoire .git avant d'essayer quoi que ce soit, afin d'avoir un instantané auquel vous pourrez revenir si l'une d'entre elles ne fonctionne pas.

0 votes

J'avais un certain nombre de fichiers non validés. Puis j'ai commité 1 fichier, et fait un git reset --hard ce qui a perturbé mon dépôt de manière inconnue. @Duncan, que fait le @{1} ? et à quel commentaire faites-vous référence ? Est-ce qu'il réinitialise un git reset ?

0 votes

Je pense que l'@{1} est une référence relative à l'avant-dernier commit, mais je ne suis pas un expert, je rapporte juste ce qui a fonctionné pour moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X