如何在Git中恢复掉下的藏匿处?


1733

我经常使用git stashgit stash pop保存和恢复工作树中的更改。昨天,我在工作树中进行了一些隐藏和弹出的更改,然后对工作树进行了更多更改。我想返回查看昨天的隐藏更改,但git stash pop似乎删除了对关联提交的所有引用。

我知道,如果我用git stash那么git的/裁判/藏匿包含的参考提交用于创建藏匿。和git的/日志/裁判/藏匿包含了整个藏匿。但是那些参考文献已经消失了git stash pop。我知道提交仍在我的存储库中的某个位置,但是我不知道它是什么。

有没有简单的方法来恢复昨天的隐藏提交参考?

请注意,这对我今天并不重要,因为我每天都有备份,可以回到昨天的工作树中进行更改。我问,因为必须有一个更简单的方法!


74
未来注意事项:如果您不想每次都丢失自己的藏匿处git stash pop,则可以这样做git stash apply。它执行相同的操作,不同之处在于它不会删除对已应用存储的引用。
凯文

3
在这里尝试了所有内容,找不到已经弹出的存储区。很高兴IntelliJ的jetbrains.com/help/idea/local-history.html
Juan Mendes


我有这个问题。要更新我的回购协议,我跑了git stashgit pull -r upstreamgit push -f origingit stash pop,和流行说“致命:日志裁判/藏匿处是空的”。😲我尝试了很多这样的答案,但没有任何效果。当我查看.git / refs / stash时,SHA在那里。将Windows网络驱动器标记为脱机同步也许有问题?♂‍♂️
brianary

Answers:


2781

一旦知道丢弃的隐藏提交的哈希,就可以将其作为隐藏应用:

git stash apply $stash_hash

或者,您可以使用

git branch recovered $stash_hash

之后,您可以使用所有常规工具执行任何操作。完成后,只需将分支吹走即可。

查找哈希

如果仅弹出它,而终端仍处于打开状态,则您仍将在屏幕上打印出哈希值git stash pop(感谢Dolda)。

否则,您可以在Linux,Unix或Windows的Git Bash上使用它找到它:

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

...或使用Windows的Powershell:

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}' )

...或者如果使用Windows的Powershell,请从emragins中查看答案

这将启动一个存储库浏览器,向您显示存储库中的每个提交,无论它是否可以访问。

您可以将其替换gitk为类似的内容,例如git log --graph --oneline --decorate,相比于单独的GUI应用程序,您更喜欢控制台上的漂亮图形。

要发现隐藏提交,请查找以下形式的提交消息:

        在somebranch执行 WIP :commithash一些旧的提交消息

注意:仅当提交时未提供消息时,提交消息才会采用这种形式(以“ WIP on”开头)git stash


49
贾德尔从我口中说出了这些话。这篇文章挽救了我的工作:)我想补充一下-记住丢失日期的处理日期,可以更轻松地浏览gitk寻找所需内容。
Sridhar Sarnobat

4
@Codey:因为PowerShell。我不知道MsysGit是否提供AWK二进制文件。Googling告诉我,类似的操作%{ $_.Split(' ')[2]; }应执行PowerShell {print $3}中该awk命令中的等效操作,但是我没有Windows系统对此进行测试,因此您仍然需要等效的/dangling commit/部件。无论如何,只要运行git fsck --no-reflog并查看输出即可。您需要“悬挂提交<commitID>”行中的哈希。
亚里斯多德·帕加尔兹

7
值得一提的是,如果您在存储时未提供自己的消息(例如,这样做git stash save "<message>"),则提交消息将仅具有字符串“ WIP” 。
萨米尔·阿吉亚尔

12
如果您知道何时发生删除,则可以使用此单行代码通过增加时间来获取悬空提交的列表:git fsck --no-reflog | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ci %H" | sort最后一个条目可能是您想要的条目stash apply
ris8_allo_zen0

3
git stash apply {ref}恢复了一个隐藏的藏匿处!git太棒了,应该是非法的!
汤姆·罗素

707

如果您没有关闭终端,只需查看其输出git stash pop,您将获得放置的存储的对象ID。通常看起来像这样:

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

(请注意,这git stash drop也会产生相同的行。)

要恢复该存储,只需运行git branch tmp 2cae03e,然后将其作为一个分支即可。要将其转换为隐藏,请运行:

git stash apply tmp
git stash

将其作为分支还可以让您自由地操作它。例如,选择或合并它。


54
你也可以做git stash apply commitid,然后git stash得到一个新的藏匿处。
马修·弗拉申

32
请注意,如果git自动合并存储库并发生冲突,则不会向您显示哈希。
詹姆斯

31
@James:再说一次,如果那些冲突是运行所导致的git stash pop,它也不会掉线,所以通常这不是问题。
Dolda2000

2
我的git stash pop输出中没有SHA。:(
扔掉帐户

2
@Honey:这就是重点git stash pop。如果要应用隐藏而不删除它,请git stash apply改用。另外,如果您要将更改应用于多个分支,则也可以改为选择提交。
Dolda2000 '19

271

只是想在接受的解决方案中提及这一新增功能。第一次尝试此方法(也许应该已经)对我来说并不是立即显而易见的,但是要应用哈希值中的隐藏,只需使用“ git stash apply”:

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

当我刚接触git时,这对我来说还不清楚,我尝试使用“ git show”,“ git apply”,“ patch”等不同的组合。


3
请注意,这会将隐藏项应用于当前工作树。如果树很脏,则可能要先使用一个临时分支,或者先存储,再从SHA-1应用该存储,再次存储,然后弹出倒数第二个存储(称为stash @ {1})。
musiKk 2014年

111

要获取仍在您的存储库中但无法再访问的存储的列表:

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

如果您为隐藏添加了标题,请-grep=WIP在命令末尾用部分消息替换“ WIP” ,例如-grep=Tesselation

该命令正在对“ WIP”进行grepping处理,因为隐藏的默认提交消息的格式为 WIP on mybranch: [previous-commit-hash] Message of the previous commit.


1
echo'git fsck-无法访问| grep提交| 切-d“” -f3 | xargs git log --merges --no-walk --grep = WIP'> / usr / local / bin / git-stashlog; chmod a + rx / usr / local / bin / git-stashlog#git stashlog
Erik Martino

或者,您可以将其作为别名添加到.gitconfig中(在命令前面加上!)。
asmeurer 2015年

保存了我的培根-并不是真的,但是保存了我重新编码工作的代码-非常感谢-鉴于我最近才放手,我只是从命令输出中选择了顶部的SHA-然后.... git stash apply SHA ...正如在其他答案中提到的
多谢

75

我只是构造了一个命令,该命令可以帮助我找到丢失的隐藏提交:

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

这将列出.git / objects树中的所有对象,找到那些类型为commit的对象,然后显示每个对象的摘要。从这一点开始,只需要查看提交即可找到合适的“工作中的WIP:6a9bb2”(“ work”是我的分支,619bb2是最近的提交)。

我注意到,如果我使用“ git stash apply”而不是“ git stash pop”,则不会有此问题,如果我使用“ git stash save message ”,则提交可能更容易找到。

更新:按照内森的想法,这变得更短了:

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

41

git fsck --unreachable | grep commit应该显示sha1,尽管它返回的列表可能很大。git show <sha1>将显示它是否是您想要的提交。

git cherry-pick -m 1 <sha1> 将提交合并到当前分支。


37

等效于gitk的Windows PowerShell:

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

在一个管道中执行此操作可能是一种更有效的方法,但这确实起作用。


1
我对你的回答很感激
ВиталийШебаниц

32

如果要重新存储丢失的存储卡,则需要先查找丢失的存储卡的哈希。

正如亚里斯多德·帕加尔茨(Aristotle Pagaltzis)所建议的那样,它git fsck应该可以为您提供帮助。

我个人使用log-all别名显示每个提交(可恢复的提交)以更好地了解情况:

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

如果仅查找“ WIP on”消息,则可以进行更快的搜索。

知道sha1之后,您只需更改存储的reflog即可添加旧的存储:

git update-ref refs/stash ed6721d

您可能更希望有一条关联的消息,因此 -m

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

而且,您甚至希望将其用作别名:

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

2
但是-d\\ 应该是-d\ (或更清晰-d' '
joeytwiddle

得到了一个错误:“致命:模糊的参数'悬挂':未知的修订或路径不在工作树中。”
丹尼尔·瑞安

您还需要用引号将子命令包裹起来 git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721
Andrei Shostik '17

18

我喜欢Aristotle的方法,但不喜欢使用GITK ...,因为我习惯从命令行使用GIT。

相反,我悬挂了提交,并将代码输出到DIFF文件中,以便在代码编辑器中进行检查。

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

现在,您可以将生成的diff / txt文件(位于主文件夹中)加载到txt编辑器中,并查看实际的代码和生成的SHA。

然后使用

git stash apply ad38abbf76e26c803b27a6079348192d32f52219

17

您可以通过在终端中编写以下命令来列出所有无法访问的提交-

git fsck --unreachable

检查无法访问的提交哈希-

git show hash

如果找到藏起来的物品,请最后申请-

git stash apply hash

15

人们为什么问这个问题?因为他们还不了解或不了解reflog。

该问题的大多数答案都给出了带有几乎没有人记得的选项的长命令。因此,人们遇到了这个问题,然后复制并粘贴他们认为需要的任何内容,然后几乎立即忘记了。

我建议有这个问题的每个人都只检查reflog(git reflog),仅此而已。一旦您看到了所有提交的列表,便有一百种方法可以找出您要查找的提交,并挑选或从中创建分支。在此过程中,您将了解reflog和各种基本git命令的有用选项。


1
嗨,罗比。如果您正在工作,被跟踪并且需要在几周前停下来的地方重新学习,那么这很重要,以便了解您找不到隐藏的工作-它可能会丢失在您所拥有的其他东西中在做。如果是最近的历史,则reflog很棒,但是时间间隔不长。
emragins

1
嘿,我同意,但这确实是OP的用例。我不确定在此发布的其他命令的行为如何,但是我的主意是,一旦清除了对他隐匿的提交的引用,它们也将停止工作。
RobbyD

1
嗯...上面的情况使我想到了这个问题,而且我知道,从我(不知不觉)失去藏匿处到能够恢复藏匿处之间,至少还需要两个星期的时间。
emragins

15

在带有git v2.6.4的OSX中,我只是偶然运行了git stash drop,然后通过以下步骤找到了它

如果您知道存储的名称,请使用:

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

否则,您可以通过以下方式手动从结果中找到ID:

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

然后,当您找到commit-id时,只需按一下git stash即可,应用{commit-id}

希望这对某人有帮助


12

当您没有可用的gitk或没有X输出时,我想向接受的解决方案中添加另一种进行所有更改的好方法。

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

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

然后,您将获得这些散列的所有差异。按“ q”进入下一个差异。


12

在简单的命令窗口(以Windows 7为例)下,我无法在Windows上使用任何答案。awkgrep并且Select-string未被识别为命令。所以我尝试了另一种方法:

  • 第一次运行: git fsck --unreachable | findstr "commit"
  • 将输出复制到记事本
  • 查找替换为“无法到达的提交” start cmd /k git show

看起来像这样:

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

  • 另存为.bat文件并运行
  • 该脚本将打开一堆命令窗口,显示每次提交
  • 如果找到了所需的,请运行: git stash apply (your hash)

可能不是最佳解决方案,但对我有用


您甚至可以在Windows上使用git bash。在git bash中,您拥有所需的所有(unixoid)命令行工具。
阿德里安W

10

亚里士多德(Aristotle)接受的答案将显示所有可到达的提交,包括非隐藏式提交。要滤除噪音:

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

这将仅包括恰好具有3个父提交(隐藏将具有该提交)且其消息包括“ WIP on”的提交。

请记住,如果您用信息保存了存储(例如 git stash save "My newly created stash"),这将覆盖默认的“ WIP on ...”消息。

您可以显示有关每个提交的更多信息,例如显示提交消息,或将其传递给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

6

我最喜欢的是这种单线:

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

这与该答案基本相同,但简短得多。当然,您仍然可以添加--graph以获得树状显示。

在列表中找到提交后,请使用

git stash apply THE_COMMIT_HASH_FOUND

对我来说,使用--no-reflogs确实显示了丢失的存储项,但是--unreachable(如在其他许多答案中所发现的)没有。

在Windows下,请在git bash上运行它。

鸣谢:以上命令的详细信息取自https://gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf


5

使用以下步骤将其恢复:

  1. 确定已删除的隐藏哈希码:

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

  2. 樱桃挑藏:

    git cherry-pick -m 1 $ stash_hash_code

  3. 解决冲突(如果有)使用:

    git mergetool

另外,如果您使用的是gerrit,则提交消息可能会出现问题。请保存您的更改,然后再执行下一个替代方案:

  1. 使用硬重置为上一次提交,然后重新提交此更改。
  2. 您也可以存储更改,重新设置基础并重新提交。

@ miva2您的编辑删除了此问题中最正确答案的链接。将链接添加回评论stackoverflow.com/questions/89332/…–
Abhijeet

4

我来这里寻找的是如何真正收回藏匿处,无论我已经签出了什么。特别是,我藏了一些东西,然后签出旧版本,然后弹出它,但是该存储在那个较早的时间点是无人操作的,因此该存储消失了。我不能只是git stash将其推回堆栈中。这为我工作:

$ 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.

回想起来,我应该一直使用git stash applynot git stash pop。我正在做一个bisect补丁,有一个小补丁要在每个bisect步骤中应用。现在我正在这样做:

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

这是答案还是问题的延续?
亚历克斯·布朗

两者都有。我找到此页面是因为我丢失了一个藏匿处并试图将其取回。对我来说,用例正在做一个二等分,我想在每个步骤进行测试之前应用更改。我了解了不能仅仅弹出,测试,隐藏,二等分的困难方法,因为这样会在隐藏上留下不同的提交stash apply
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.