web-dev-qa-db-fra.com

Comment récupérer une réserve abandonnée dans Git?

J'utilise fréquemment git stash et git stash pop pour enregistrer et restaurer les modifications dans mon arbre de travail. Hier, j'ai eu quelques changements dans mon arbre de travail que j'avais cachés et sautés, puis j'ai apporté d'autres modifications à mon arbre de travail. J'aimerais revenir en arrière et passer en revue les modifications cachées d'hier, mais git stash pop semble supprimer toutes les références au commit associé.

Je sais que si j'utilise git stash alors . Git/refs/stash contient la référence du commit utilisé pour créer le stash. Et . Git/logs/refs/stash contient toute la stash. Mais ces références ont disparu après git stash pop. Je sais que le commit est toujours dans mon référentiel quelque part, mais je ne sais pas ce que c'était.

Existe-t-il un moyen simple de récupérer la référence de validation de la collecte d'hier?

Notez que cela n’est pas critique pour moi aujourd’hui, car j’ai des sauvegardes quotidiennes et que je peux revenir à l’arborescence de travail d’hier pour obtenir mes modifications. Je demande parce qu'il doit y avoir un moyen plus facile!

1554
Greg Hewgill

Une fois que vous connaissez le hachage de la validation que vous avez supprimée, vous pouvez l'appliquer en tant que cachette:

git stash apply $stash_hash

Ou, vous pouvez créer une branche distincte avec

git branch recovered $stash_hash

Après cela, vous pouvez faire ce que vous voulez avec tous les outils habituels. Lorsque vous avez terminé, il suffit de balayer la branche.

Trouver le hash

Si vous venez juste de le faire apparaître et que le terminal est toujours ouvert, vous aurez toujours la valeur de hachage imprimée par git stash pop à l'écran (merci, Dolda).

Sinon, vous pouvez le trouver en utilisant ceci pour Linux, Unix ou Git Bash pour Windows:

git fsck --no-reflog | awk '/dangling commit/ {print $3}'

... ou en utilisant Powershell pour Windows:

git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];}

Les astuces de votre graphique de validation vous indiqueront toutes les validations qui ne sont plus référencées dans aucune branche ou balise. Chaque validation perdue, y compris toutes les validations de dissimulation que vous avez créées, sera quelque part dans ce graphique.

Le moyen le plus simple de trouver la validation que vous voulez utiliser est probablement de transmettre cette liste à gitk:

gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )

... ou voyez réponse d'Emragins si vous utilisez Powershell pour Windows.

Cela lancera un navigateur de référentiel vous montrant chaque commit dans le référentiel , que celui-ci soit accessible ou non.

Vous pouvez remplacer gitk par quelque chose comme git log --graph --oneline --decorate si vous préférez un graphique de Nice sur la console plutôt qu'une application graphique distincte.

Pour repérer les validations cachées, recherchez les messages de validation de ce formulaire:

WIP sur une branchecommithash Un vieux message de commit

Note : Le message de validation ne sera présent que sous cette forme (commençant par "WIP on") si vous n'avez pas fourni de message lorsque vous avez fait git stash.

2508
Aristotle Pagaltzis

Si vous n'avez pas fermé le terminal, il suffit de regarder le résultat de git stash pop et vous obtiendrez l'ID d'objet de la cachette supprimée. Cela ressemble normalement à ceci:

$ git stash pop
[...]
Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)

(Notez que git stash drop produit également la même ligne.)

Pour récupérer cette réserve, lancez simplement git branch tmp 2cae03e et vous l'obtiendrez sous forme de branche. Pour convertir ceci en une réserve, exécutez:

git stash apply tmp
git stash

Avoir cela comme une branche vous permet également de le manipuler librement; par exemple, pour le sélectionner ou le fusionner.

670
Dolda2000

Je voulais juste mentionner cet ajout à la solution acceptée. Ce n’était pas évident pour moi la première fois que j’ai essayé cette méthode (peut-être aurait-elle dû l'être), mais pour appliquer la valeur stash à partir de la valeur de hachage, utilisez simplement "git stash apply":

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Quand j'étais nouveau sur git, ce n'était pas clair pour moi et j'essayais différentes combinaisons de "git show", "git apply", "patch", etc.

255
Wade

Pour obtenir la liste des caches encore présentes dans votre référentiel, mais inaccessibles:

git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP

Si vous avez donné un titre à votre réserve, remplacez "WIP" dans -grep=WIP à la fin de la commande par une partie de votre message, par exemple. -grep=Tesselation.

La commande est grepping pour "WIP" car le message de validation par défaut pour une stash est de la forme WIP on mybranch: [previous-commit-hash] Message of the previous commit.

86
Senthil A Kumar

Je viens de construire une commande qui m'a aidé à retrouver mon commit caché perdu:

for ref in `find .git/objects | sed -e 's#.git/objects/##' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less

Ceci répertorie tous les objets de l’arborescence .git/objects, localise ceux qui sont de type commit, puis affiche un résumé de chacun d’eux. À partir de ce moment, il suffisait de regarder à travers les commits pour trouver un "WIP sur le travail: 6a9bb2" approprié ("work" est ma branche, 619bb2 est un commit récent).

Je remarque que si j'utilise "git stash apply" au lieu de "git stash pop", le problème ne se pose pas et si j'utilise "git stash save message", la validation aurait peut-être été plus simple. trouver.

Mise à jour: avec l'idée de Nathan, cela devient plus court:

for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
71
Greg Hewgill

git fsck --unreachable | grep commit devrait afficher le sha1, bien que la liste renvoyée puisse être assez longue. git show <sha1> montrera s'il s'agit du commit que vous voulez.

git cherry-pick -m 1 <sha1> fusionnera la validation sur la branche actuelle.

38
Nathan Jones

Si vous souhaitez restaurer une cachette perdue, vous devez d'abord trouver la valeur de hachage de votre cachette perdue.

Comme Aristote suggéra Pagaltzis, un git fsck devrait vous aider.

Personnellement, j'utilise mon alias log-all qui me montre chaque commit (commets récupérables) pour avoir une meilleure vue d'ensemble de la situation:

git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)

Vous pouvez effectuer une recherche encore plus rapidement si vous ne recherchez que les messages "WIP on".

Une fois que vous connaissez votre sha1, il vous suffit de changer votre stog pour ajouter l’ancien stash:

git update-ref refs/stash ed6721d

Vous préférerez probablement avoir un message associé de sorte qu'un -m

git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721d

Et vous voudrez même utiliser ceci comme un alias:

restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1
30
Colin Hebert

Équivalent Windows PowerShell avec gitk:

gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })

Il existe probablement un moyen plus efficace de faire cela dans un seul tuyau, mais cela fait le travail.

25
emragins

J'aimais l'approche d'Aristote, mais n'aimais pas utiliser GITK ... car j'ai l'habitude d'utiliser GIT depuis la ligne de commande.

Au lieu de cela, j'ai pris les commits en suspens et ai sorti le code dans un fichier DIFF pour examen dans mon éditeur de code.

git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff

Maintenant, vous pouvez charger le fichier diff/txt obtenu (dans votre dossier personnel) dans votre éditeur de texte et voir le code actuel et le SHA résultant.

Alors juste utiliser

git stash apply ad38abbf76e26c803b27a6079348192d32f52219
16
Shaheen Ghiassy

Vous pouvez lister tous les commits inaccessibles en écrivant cette commande dans le terminal -

git fsck --unreachable

Vérifier le hachage de validation inaccessible -

git show hash

Enfin, appliquez si vous trouvez le produit caché -

git stash apply hash
14
Vivek Kumar

Sous OSX avec git v2.6.4, je viens d'exécuter accidentellement git stash drop, puis je l'ai trouvé en allant au-dessous des étapes

Si vous connaissez le nom de la réserve, utilisez:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>

sinon, vous trouverez l'ID du résultat manuellement avec:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

Ensuite, lorsque vous trouvez l'ID de validation, appuyez simplement sur la réserve git apply {commit-id}

J'espère que cela aide quelqu'un rapidement

12
Can Tecim

Pourquoi les gens posent cette question? Parce qu'ils ne connaissent pas encore ou ne comprennent pas le reflog.

La plupart des réponses à cette question donnent de longues commandes avec des options dont presque personne ne se souviendra. Alors les gens entrent dans cette question et copient-collent tout ce dont ils pensent avoir besoin et l’oublient presque immédiatement après.

Je conseillerais à tous ceux qui ont cette question de vérifier le reflog (git reflog), pas beaucoup plus que cela. Une fois que vous voyez cette liste de tous les commits, il existe une centaine de façons de déterminer le commet que vous recherchez et de le sélectionner ou de créer une branche à partir de celle-ci. Au cours de ce processus, vous en apprendrez davantage sur le processus de refonte et sur les options utiles des différentes commandes git de base.

11
RobbyD

Je souhaite ajouter à la solution acceptée un autre bon moyen de passer en revue tous les changements, lorsque vous ne disposez pas de gitk ou d'aucun X pour la sortie.

git fsck --no-reflog | awk '/dangling commit/ {print $3}' > tmp_commits

for h in `cat tmp_commits`; do git show $h | less; done

Ensuite, vous obtenez toutes les différences pour ces hachages affichées les unes après les autres. Appuyez sur 'q' pour vous rendre au prochain diff.

10
Phil

Je ne pouvais obtenir aucune des réponses pour travailler sur Windows dans une simple fenêtre de commande (Windows 7 dans mon cas). awk, grep et Select-string n'ont pas été reconnus comme des commandes. J'ai donc essayé une approche différente:

  • première exécution: git fsck --unreachable | findstr "commit"
  • copier la sortie sur le bloc-notes
  • find remplace "commit inaccessible" par start cmd /k git show

ressemblera à ceci:

start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4 start cmd /k git show 44078733e1b36962571019126243782421fcd8ae start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1 start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e

  • enregistrer en tant que fichier .bat et l'exécuter
  • le script ouvrira un tas de fenêtres de commande, montrant chaque commit
  • si vous avez trouvé celui que vous cherchez, lancez: git stash apply (your hash)

peut ne pas être la meilleure solution, mais a travaillé pour moi

9
kromakollision

La réponse acceptée par Aristote montrera tous les commits accessibles, y compris les commits non stash. Pour filtrer le bruit:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3

Cela n'inclut que les commits qui ont exactement 3 commits parents (qui auront une cachette) et dont le message inclut "WIP on".

N'oubliez pas que si vous avez enregistré votre réserve avec un message (par exemple git stash save "My newly created stash"), le message par défaut "WIP on ..." sera remplacé.

Vous pouvez afficher plus d'informations sur chaque commit, par exemple. affichez le message de validation ou transmettez-le à git stash show:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3 | \
xargs -n1 -I '{}' bash -c "\
  git log -1 --format=medium --color=always '{}'; echo; \
  git stash show --color=always '{}'; echo; echo" | \
less -R
9
Brad Feehan

Récupéré en utilisant les étapes suivantes:

  1. Identifiez le code de hachage stash supprimé:

    gitk --all $ (git fsck --no-reflog | awk '/ dangling commit/{print $ 3}' ')

  2. Cerise Choisissez la cachette:

    git cherry-pick -m 1 $ stash_hash_code

  3. Résoudre les conflits le cas échéant en utilisant:

    git mergetool

De plus, vous pourriez avoir des problèmes avec le message de validation si vous utilisez gerrit. Veuillez stocker vos modifications avant de suivre les alternatives suivantes:

  1. Utilisez une réinitialisation matérielle à la validation précédente, puis renouvelez cette modification.
  2. Vous pouvez également cacher le changement, rebaser et réengager.
4
Abhijeet

Ce que je suis venu chercher ici, c'est comment récupérer le cachette, peu importe ce que j'ai vérifié. En particulier, j'avais caché quelque chose, puis extrait une version plus ancienne, puis je l'avais fait apparaître, mais la cachette était inopérante à ce moment-là, de sorte que la cachette a disparu; Je ne pouvais pas simplement faire git stash pour le repousser dans la pile. Cela a fonctionné pour moi:

$ git checkout somethingOld
$ git stash pop
...
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (27f6bd8ba3c4a34f134e12fe69bf69c192f71179)
$ git checkout 27f6bd8ba3c
$ git reset HEAD^    # Make the working tree differ from the parent.
$ git stash # Put the stash back in the stack.
Saved working directory and index state WIP on (no branch): c2be516 Some message.
HEAD is now at c2be516 Some message.
$ git checkout somethingOld # Now we are back where we were.

En rétrospective, j'aurais dû utiliser git stash apply pas git stash pop. Je faisais un bisect et j'avais un petit patch que je voulais appliquer à chaque bisect. Maintenant je fais ça:

$ git reset --hard; git bisect good; git stash apply
$ # Run tests
$ git reset --hard; git bisect bad; git stash apply
etc.
4
Ben

Mon préféré est ce one-liner:

git log --oneline  $( git fsck --no-reflogs | awk '/dangling commit/ {print $3}' )

C'est fondamentalement la même idée que cette réponse mais beaucoup plus courte. Bien sûr, vous pouvez toujours ajouter --graph pour obtenir un affichage sous forme d'arborescence.

Lorsque vous avez trouvé le commit dans la liste, postulez avec

git stash apply THE_COMMIT_HASH_FOUND

Pour moi, utiliser --no-reflogs a révélé l'entrée de cachette perdue, mais pas --unreachable (comme dans de nombreuses autres réponses).

Lancez-le sur git bash lorsque vous êtes sous Windows.

Crédits: Les détails des commandes ci-dessus sont tirés de https://Gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf

1
Adrian W