题 如何在Git中恢复被删除的存储?


我经常使用 git stash 和 git stash pop 保存和恢复工作树中的更改。昨天我的工作树上有一些变化,我已经藏起来了,然后我对工作树做了更多修改。我想回去看看昨天发生的变化,但是 git stash pop 似乎删除了对关联提交的所有引用。

我知道,如果我使用 git stash 然后 .git / refs / stash包含 用于创建存储的提交的引用。和 .git / logs / refs / stash包含 整个藏匿处。但那些参考文献已经消失了 git stash pop。我知道提交仍然在我的存储库中,但我不知道它是什么。

有没有一种简单的方法来恢复昨天的隐藏提交引用?

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


1334
2017-09-18 01:59


起源


未来的注意事项:如果您不希望每次都丢失藏匿处 git stash pop, 你可以做 git stash apply 代替。它执行相同的操作,但它不会删除对应用的存储的引用。 - Kevin
在修改时使用藏匿时要非常小心 - 请参阅 这个 - Mr_and_Mrs_D
在这里尝试了一切,找不到已经弹出的藏品。很高兴IntelliJ的 jetbrains.com/help/idea/local-history.html - Juan Mendes


答案:


如果您刚刚弹出它并且终端仍然打开,您将会 仍然有打印的哈希值 git stash pop 在屏幕上 (谢谢,Dolda)。

否则,你可以在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 如果您喜欢通过单独的GUI应用程序在控制台上使用漂亮的图形。

要查找存储提交,请查找此表单的提交消息:

WIP上 somebranchcommithash一些旧的提交消息

注意:如果您没有提供消息,则提交消息将仅采用此形式(以“WIP on”开头) git stash

一旦知道了所需提交的哈希值,就可以将其应用为存储:

git stash apply $ stash_hash

或者您可以使用上下文菜单 gitk 为您感兴趣的任何无法访问的提交创建分支。之后,您可以使用所有常规工具随意执行任何操作。当你完成后,再次吹走那些树枝。


2159
2017-09-18 11:38



杰德尔从我嘴里说出了这些话。这篇文章保存了我的工作:)我只想添加 - 记住你工作的日期,无论你丢失了什么,都可以更轻松地浏览gitk以寻找你想要的东西。 - Sridhar-Sarnobat
@Codey:因为PowerShell。我不知道MsysGit是否附带了AWK二进制文件。谷歌搜索告诉我类似的东西 %{ $_.Split(' ')[2]; } 应该做相当于 {print $3} 在那里面 awk 在PowerShell中的命令,但我没有Windows系统来测试它,你仍然需要一个等效的 /dangling commit/部分。无论如何,只是跑 git fsck --no-reflog 并查看输出。你想要来自“悬挂提交<commitID>”行的哈希。 - Aristotle Pagaltzis
很棒的命令。请参阅我对PowerShell版本的回答: stackoverflow.com/a/34666995/219072 - emragins
值得一提的是,如果您在存储时没有提供自己的消息,那么提交消息将只有字符串“WIP”(即通过执行 git stash save "<message>")。 - Samir Aguiar
如果您知道丢弃发生的时间,您可以使用此单行程序通过增加时间来获取悬挂提交列表: 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


如果您没有关闭终端,只需查看输出 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

将它作为分支也允许您自由地操作它;例如,樱桃挑选或合并它。


598
2017-10-21 03:13



这通常是最简单的答案,值得在列表中更高 - Casebash
你也可以这样做 git stash apply commitid 然后 git stash 得到一个新的藏匿处。 - Matthew Flaschen
请注意,如果git自动合并存储并且存在冲突,它将不会显示哈希值。 - James
@James:再说一遍,如果这些冲突是跑步的结果 git stash pop,它也不会丢下藏匿,所以这通常不是问题。 - Dolda2000
我做了一个git stash drop,并认为我的变化永远失去了。这个答案救了我:)这需要投票更多! - raghav710


只是想提到对已接受的解决方案的这一补充。我第一次尝试这种方法时并不是很明显(也许应该是这样),但是要从哈希值中应用存储,只需使用“git stash apply”:

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

当我刚开始使用git时,这对我来说并不清楚,我正在尝试“git show”,“git apply”,“patch”等的不同组合。


232
2018-03-03 20:28



这在意外打字后节省了我的一天 git stash drop 代替 git stash pop。 - Jörn Horstmann
尼斯。我刚刚意识到它在丢弃存储时打印SHA1(即弹出时)。 - asmeurer
请注意,这会将(存储!)存储应用于当前工作树。如果树是脏的,您可能要先使用临时分支或存储,从SHA-1应用存储,再次存储,然后弹出第二个到最后一个存储(称为存储@ {1})。 - musiKk


我刚刚构建了一个命令,帮助我找到丢失的存储提交:

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 on work:6a9bb2”(“work”是我的分支,619bb2是最近的提交)。

我注意到如果我使用“git stash apply”而不是“git stash pop”我就不会有这个问题,如果我使用“git stash save” 信息“那么提交可能更容易找到。

更新:根据内森的想法,这变得更短:

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

68
2017-09-18 02:10



感谢您再次磨练我的shell技能!我能用你的for循环和git show找到我丢失的藏匿处。 - wprater
这很完美:显示提交的摘要 less 做了这个 不重要的。 - cbowns


要获取仍在存储库中但不再可访问的存储列表:

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

如果您为藏匿处提供了标题,请将“WIP”替换为 -grep=WIP 在命令的末尾,包含部分信息,例如 -grep=Tesselation

该命令正在grepping“WIP”,因为存储的默认提交消息在表单中 WIP on mybranch: [previous-commit-hash] Message of the previous commit.


57
2018-05-04 06:42



这个对我来说很有用 - 但我必须编辑答案以便后面的双倍空间 cut -d 被展示。只有一个空间, cut 不承认分隔符。 - s4y
我已经重新编辑了 cut args甚至更清楚 - dty
echo'git fsck --unreachable | grep commit | cut -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
尝试了接受和流行的答案,并没有奏效。这个做到了。 - juliangonzalez


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

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


36
2017-09-18 02:08



啊,这肯定会改进我的方法!我现在有130个无法访问的提交,所以我仍然需要我的解决方案的后半部分。 - Greg Hewgill


如果你想重建一个丢失的藏匿处,你需要先找到丢失的藏匿的哈希值。

正如亚里士多德帕加齐斯所说的那样 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

你可能更愿意有一个相关的消息,所以a -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

23
2018-06-23 14:20



我喜欢这个,因为它以历史顺序显示事物。 - joeytwiddle
但是,那 -d\\  应该 -d\  (甚至更清楚 -d' ') - joeytwiddle
得到一个错误:“致命:模棱两可的争论'悬空':未知的修订或路径不在工作树中。” - Daniel Ryan
你还需要用引号包装子命令 git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721 - Andrei Shostik