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

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

我知道,如果我使用git stash.git / refs / stash 包含用于创建隐藏的提交的引用。 .git / logs / refs / stash 包含整个存储。但是这些引用在git stash pop之后消失了。我知道提交仍在我的存储库中的某个位置,但是我不知道它是什么。

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

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

答案

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

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 中查看答案

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

如果您更喜欢控制台上的图形而不是单独的 GUI 应用程序,则可以使用git log --graph --oneline --decorate类的东西替换gitk

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

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

注意 :如果您在执行git stash时未提供消息,则提交消息将仅采用这种形式(以 “WIP on” 开头)。

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

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

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

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

git stash apply tmp
git stash

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

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

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

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

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

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

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

该命令用于 “WIP”,因为用于存储的默认提交消息的格式WIP on mybranch: [previous-commit-hash] Message of the previous 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

这将列出. 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

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

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

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

正如亚里斯多德 · 帕加兹(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

等效于 gitk 的 Windows PowerShell:

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

在一个管道中执行此操作可能是一种更有效的方法,但这可以完成工作。

我喜欢亚里斯多德的方法,但是不喜欢使用 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

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

git fsck --unreachable

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

git show hash

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

git stash apply hash