web-dev-qa-db-fra.com

Annuler git reset --hard avec des fichiers non validés dans la zone de transfert

J'essaie de récupérer mon travail. J'ai bêtement fait git reset --hard, mais auparavant je n'ai fait que get add . et n'a pas fait git commit. S'il vous plaît aider! 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?

102
eistrati

Vous devriez pouvoir récupérer tous les fichiers que vous avez ajoutés à l’index (par exemple, comme dans votre cas, avec git add .) bien que cela puisse être un peu de travail. Pour ajouter un fichier à l'index, git l'ajoute à la base de données de l'objet, ce qui signifie qu'il peut être récupéré tant que la récupération de place n'a pas encore eu lieu. Il y a un exemple de comment faire ceci donné dans réponse de Jakub Narębski ici:

Cependant, j'ai essayé cela sur un référentiel de test, et il y avait quelques problèmes - --cached devrait être --cache, et j’ai trouvé qu’il ne créait pas le .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 générer tous les objets de la base de données d’objets qui ne sont accessibles par aucune référence, dans l’index ou par le biais du refog. La sortie ressemblera à 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 sortie?

Mise à jour en réponse au commentaire de sehe ci-dessous:

Si vous constatez que de nombreux commits et arborescences sont répertoriés dans la sortie de cette commande, vous souhaiterez peut-être supprimer de la sortie tous les objets référencés à partir de validations non référencées. (En règle générale, vous pouvez quand même revenir à ces commits via le refoc. Nous ne sommes intéressés que par les objets ajoutés à l'index mais ne pouvant jamais être trouvés via un commit.)

Commencez par enregistrer le résultat de la commande avec:

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

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

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

Ainsi, vous pouvez trouver uniquement les arbres et les objets qui ont été ajoutés à l’index, mais n’ont été validés à aucun moment, 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 un autre moyen de réduire le nombre d'objets à prendre en compte, à savoir le plus fichiers récemment modifiés sous .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 enlever le / à la fin du chemin pour obtenir le nom de l'objet.)

162
Mark Longair

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

Je l'ai fait dans les minutes qui ont suivi la perte du commit, cela peut donc marcher pour certains d'entre vous.

58
Richard Saunders

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

J'ai tout d'abord sauvegardé tous les hachages dans un fichier:

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

ensuite, je les ai tous mis (en supprimant le "blob inaccessible") dans une liste et mis toutes 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-uns fichiers..hope cela aide 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
13
Boy

La solution de @ Ajedi32 dans les commentaires a fonctionné pour moi exactement dans 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 causer un. J’ai donc zippé le contenu de votre répertoire .git avant d’essayer quoi que ce soit, afin que vous ayez un instantané auquel revenir. travaillons pas pour vous.

5
Duncan McGregor

Couru dans le même problème, mais n'avait pas ajouté les modifications à l'index. Toutes les commandes ci-dessus ne m'ont donc pas rapporté les modifications souhaitées.

Après toutes les réponses détaillées ci-dessus, c’est une allusion naïve, mais peut-être sauvera-t-il quelqu'un qui n'y avait pas pensé en premier, comme je l’ai fait.

Dans le désespoir, j'ai essayé d'appuyer sur CTRL-Z dans mon éditeur (LightTable), une fois dans chaque onglet ouvert - cela a heureusement récupéré le fichier dans cet onglet, à son dernier état avant le git reset --hard. HTH.

3
olange

Ceci est probablement évident pour les professionnels des git, mais je voulais le dire car dans mes recherches frénétiques, je n’ai pas vu cela évoqué.

J'ai mis en scène des fichiers, et j'ai fait une réinitialisation de git - dure, un peu paniquée, puis j'ai remarqué que mon statut affichait tous mes fichiers toujours mis en scène ainsi que toutes leurs suppressions non mises en scène.

À ce stade, vous pouvez valider ces modifications mises en scène, à condition de ne pas programmer leurs suppressions. Après cela, il ne vous reste plus qu'à avoir le courage de faire "git reset - hard" une fois de plus, ce qui vous ramènera aux changements que vous avez mis en scène et que vous venez de mettre en œuvre.

Encore une fois, ce n’est probablement pas nouveau pour la plupart des gens, mais j’espère que, puisque cela m’a aidé et que je n’ai rien trouvé qui puisse le suggérer, cela pourrait aider quelqu'un d’autre.

1
ddoty

Bonté divine, j'ai tiré mes cheveux jusqu'à rencontrer la question et ses réponses. Je pense que la réponse correcte et succincte à la question posée n’est disponible que si vous rassemblez deux des commentaires ci-dessus, alors ici, tout est réuni:

  1. Comme mentionné par chilicuil, lancez 'git reflog' pour identifier le hachage de commit sur lequel vous voulez revenir.

  2. Comme mentionné par akimsko, vous ne voudrez probablement PAS sélectionner sélectivement à moins que vous n'ayez perdu qu'un seul commit; vous devez donc exécuter 'git reset --hard

Remarque pour les utilisateurs d’egit Eclipse: Je n’ai pas trouvé le moyen de suivre ces étapes dans Eclipse avec egit. Fermer Eclipse, exécuter les commandes ci-dessus à partir d'une fenêtre de terminal, puis rouvrir Eclipse a parfaitement fonctionné pour moi.

1
Lolo

J'utilise IntelliJ et j'ai pu simplement parcourir chaque fichier et faire:

Edit -> reload from disk

Heureusement, je venais de faire un git status juste avant que je supprime mes modifications de travail, je savais donc exactement ce que je devais recharger.

0
Forrest

Les solutions ci-dessus peuvent fonctionner, cependant, il existe des moyens plus simples de récupérer ces informations au lieu de passer par la procédure d'annulation complexe de git-. Je suppose que la plupart des réinitialisations de git se produisent sur un petit nombre de fichiers, et si vous utilisez déjà le VIM, cela pourrait être la solution la plus rapide. La mise en garde est que vous devez déjà utiliser ViM's Persistent-undo, que vous devriez utiliser de toute façon, car cela vous permet d'annuler un nombre illimité de modifications.

Voici les étapes:

  1. Dans vim, appuyez sur : Et tapez la commande set undodir. Si persistent undo Est activé dans votre .vimrc, Un résultat similaire à undodir=~/.vim_runtime/temp_dirs/undodir S'affichera.

  2. Dans votre repo, utilisez git log Pour connaître la dernière date/heure où vous avez effectué le dernier commit.

  3. Dans votre shell, accédez à votre undodir en utilisant cd ~/.vim_runtime/temp_dirs/undodir.

  4. Dans ce répertoire, utilisez cette commande pour rechercher tous les fichiers que vous avez modifiés depuis le dernier commit.

    find . -newermt "2018-03-20 11:24:44" \! -newermt "2018-03-23" \( -type f -regextype posix-extended -regex '.*' \) \-not -path "*/env/*" -not -path "*/.git/*"

    Ici "2018-03-20 11:24:44" est la date et l'heure du dernier commit. Si la date à laquelle vous avez effectué le git reset --hard Est "2018-03-22", utilisez "2018-03-22", puis "2018-03-23". Cela est dû à une bizarrerie de découverte, où la limite inférieure est inclusive et la limite supérieure est exclusive. https://unix.stackexchange.com/a/70404/24298

  5. Ensuite, ouvrez chacun des fichiers et ouvrez-les dans vim, puis effectuez un "précédent 20m". Vous pouvez trouver plus de détails sur "plus tôt" en utilisant "h plus tôt". Ici earlier 20m Signifie revenir à l'état du fichier 20 minutes en arrière, en supposant que vous ayez fait le git hard --reset, 20 minutes en arrière. Répétez cette opération pour tous les fichiers générés à partir de la commande find. Je suis sûr que quelqu'un peut écrire un script combinant ces éléments.

0
alpha_989