Вопрос: Как восстановить бросок в Git?


Я часто использую git stashа также git stash popдля сохранения и восстановления изменений в рабочем дереве. Вчера у меня были некоторые изменения в рабочем дереве, которые я спрятал и выскочил, а затем сделал больше изменений в рабочем дереве. Я хотел бы вернуться и рассмотреть вчерашние сложенные изменения, но git stash popпоявляется, чтобы удалить все ссылки на связанную фиксацию.

Я знаю, что если я использую git stashтогда .git / refs / stash содержит ссылка на фиксацию, используемую для создания кошелька. А также .git / logs / refs / stash содержит весь тайник. Но эти ссылки исчезли после git stash pop, Я знаю, что коммит все еще находится в моем хранилище, но я не знаю, что это было.

Есть ли простой способ восстановить вчерашнюю ссылку на фиксацию?

Обратите внимание, что это не важно для меня сегодня, потому что у меня есть ежедневные резервные копии и я могу вернуться к вчерашнему рабочему дереву, чтобы получить мои изменения. Я спрашиваю, потому что должен быть более простой способ!


1309


источник


Ответы:


Если вы только что открыли его, а терминал все еще открыт, вы все еще имеют значение хэш-функции, напечатанное git stash popна экране (спасибо, Дольда).

В противном случае вы можете найти его, используя это для Linux и Unix:

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

и для Windows:

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

Это покажет вам все коммиты на кончиках вашего графика фиксации, которые больше не ссылаются ни на одну ветку, ни на тег - каждая потерянная фиксация, включая все сделанные вами котировки, будет где-то на этом графике.

Возможно, самый простой способ найти скот, который вы хотите, - это, вероятно, передать этот список gitk:

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

Это запустит браузер хранилища, в котором вы увидите каждый фиксатор в репозитории , независимо от того, достигнута она или нет.

Вы можете заменить gitkтам что-то вроде git log --graph --oneline --decorateесли вы предпочитаете хороший график на консоли в отдельном графическом приложении.

Чтобы выявить фиксации закладок, найдите сообщения фиксации этой формы:

WIP по somebranch : commithash Некоторое старое сообщение фиксации

Заметка : Сообщение фиксации будет только в этой форме (начиная с «WIP on»), если вы не представили сообщение, когда вы это сделали git stash,

Как только вы узнаете хэш коммита, который вы хотите, вы можете применить его как кэш:

git stash apply $ stash_hash 

Или вы можете использовать контекстное меню в gitkдля создания филиалов для любых недостижимых коммитов, которые вас интересуют. После этого вы можете делать с ними все, что хотите, со всеми обычными инструментами. Когда вы закончите, просто удалите эти ветви снова.


2118



Если вы не закрыли терминал, просто посмотрите на git stash popи у вас будет ID объекта упавшего файла. Обычно это выглядит так:

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

(Обратите внимание, что git stash dropтакже производит ту же линию.)

Чтобы вернуть этот кошелек, просто запустите git branch tmp 2cae03e, и вы получите его как ветку. Чтобы преобразовать это в stash, запустите:

git stash apply tmp
git stash

Наличие его как ветви также позволяет вам свободно манипулировать им; например, вишнево-подбирать или сливать его.


588



Просто хотел упомянуть это дополнение к принятому решению. Это было сразу не очевидно для меня в первый раз, когда я попробовал этот метод (возможно, это должно было быть), но чтобы применить stash из значения хэша, просто используйте «git stash apply»:

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Когда я был не знаком с git, мне это было непонятно, и я пытался использовать разные комбинации «git show», «git apply», «patch» и т. Д.


224



I just constructed a command that helped me find my lost stash commit:

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

This lists all the objects in the .git/objects tree, locates the ones that are of type commit, then shows a summary of each one. From this point it was just a matter of looking through the commits to find an appropriate "WIP on work: 6a9bb2" ("work" is my branch, 619bb2 is a recent commit).

I note that if I use "git stash apply" instead of "git stash pop" I wouldn't have this problem, and if I use "git stash save message" then the commit might have been easier to find.

Update: With Nathan's idea, this becomes shorter:

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

68



To get the list of stashes that are still in your repository, but not reachable any more:

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

If you gave a title to your stash, replace "WIP" in -grep=WIP at the end of the command with a part of your message, e.g. -grep=Tesselation.

The command is grepping for "WIP" because the default commit message for a stash is in the form WIP on mybranch: [previous-commit-hash] Message of the previous commit.


53



git fsck --unreachable | grep commit should show the sha1, although the list it returns might be quite large. git show <sha1> will show if it is the commit you want.

git cherry-pick -m 1 <sha1> will merge the commit onto the current branch.


36



If you want to restash a lost stash, you need to find the hash of your lost stash first.

As Aristotle Pagaltzis suggested a git fsck should help you.

Personally I use my log-all alias which show me every commit (recoverable commits) to have a better view of the situation :

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

You can do an even faster search if you're looking only for "WIP on" messages.

Once you know your sha1, you simply change your stash reflog to add the old stash :

git update-ref refs/stash ed6721d

You'll probably prefer to have an associated message so a -m

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

And you'll even want to use this as an alias :

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

21



I liked Aristotle's approach, but didn't like using GITK... as I'm used to using GIT from the command line.

Instead, I took the dangling commits and output the code to a DIFF file for review in my code editor.

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

Now you can load up the resulting diff/txt file (its in your home folder) into your txt editor and see the actual code and resulting SHA.

Then just use

git stash apply ad38abbf76e26c803b27a6079348192d32f52219

15



In OSX with git v2.6.4, I just run git stash drop accidentally, then I found it by going trough below steps

If you know name of the stash then use:

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

otherwise you will find ID from the result by manually with:

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

Then when you find the commit-id just hit the git stash apply {commit-id}

Hope this helps someone quickly


12



Windows PowerShell equivalent using gitk:

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

There is probably a more efficient way to do this in one pipe, but this does the job.


11



I want to add to the accepted solution another good way to go through all the changes, when you either don't have gitk available or no X for output.

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

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

Then you get all the diffs for those hashes displayed one after another. Press 'q' to get to the next diff.


10



The accepted answer by Aristotle will show all reachable commits, including non-stash-like commits. To filter out the noise:

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

This will only include commits which have exactly 3 parent commits (which a stash will have), and whose message includes "WIP on".

Keep in mind, that if you saved your stash with a message (e.g. git stash save "My newly created stash"), this will override the default "WIP on..." message.

You can display more information about each commit, e.g. display the commit message, or pass it to 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

8