题
我经常使用 git stash
和 git 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
在屏幕上 (谢谢,多尔达)。
否则,您可以使用适用于 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}' )
...或者看看 来自emragins的回答 如果使用 Windows 版 Powershell。
这将启动一个存储库浏览器,向您显示 存储库中的每一次提交, ,无论是否可达。
您可以更换 gitk
那里有类似的东西 git log --graph --oneline --decorate
如果您更喜欢控制台上的漂亮图表而不是单独的 GUI 应用程序。
要发现存储提交,请查找以下形式的提交消息:
开发中 某个分支: commithash 一些旧的提交消息
笔记: :如果您在提交时没有提供消息,则提交消息只会采用这种形式(以“WIP on”开头) git stash
.
其他提示
如果您没有关闭终端,只需查看输出 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
将它作为一个分支也可以让你自由地操纵它;例如,挑选它或合并它。
只是想提及已接受的解决方案中的这一补充。当我第一次尝试这种方法时,这对我来说并不是很明显(也许应该是),但是要应用哈希值中的存储,只需使用“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
如果您为藏品指定了标题,请将“WIP”替换为 -grep=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 树中的所有对象,找到提交类型的对象,然后显示每个对象的摘要。从这一点来看,只需查看提交以找到合适的“工作中的 WIP:6a9bb2”(“work”是我的分支,619bb2 是最近的提交)。
我注意到,如果我使用“git stash apply”而不是“git stash pop”,我就不会出现这个问题,如果我使用“git stash save” 信息“那么提交可能会更容易找到。
更新:根据 Nathan 的想法,这变得更短:
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,你只需更改你的 stash reflog 来添加旧的 stash :
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 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 apply {commit-id}
希望这可以快速帮助别人
为什么人们会问这个问题?因为他们还不知道或理解转发日志。
这个问题的大多数答案都给出了很长的命令和几乎没有人会记住的选项。因此,人们遇到这个问题时,会复制粘贴他们认为需要的任何内容,然后几乎立即忘记它。
我建议有这个问题的每个人只检查引用日志(git reflog),仅此而已。一旦您看到所有提交的列表,就有一百种方法可以找到您正在寻找的提交并挑选它或从中创建分支。在此过程中,您将了解 reflog 以及各种基本 git 命令的有用选项。
您可以通过在终端中编写此命令来列出所有无法访问的提交 -
git fsck --unreachable
检查无法访问的提交哈希 -
git show hash
如果您找到隐藏的物品,最后申请 -
git stash apply hash
当您没有可用的 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”进入下一个差异。
亚里士多德接受的答案将显示所有可到达的提交,包括非存储类提交。滤除噪音:
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
我无法在简单的命令窗口(在我的例子中是 Windows 7)中获得在 Windows 上工作的任何答案。 awk
, grep
和 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)
可能不是最好的解决方案,但对我有用
我来这里寻找的是如何真正取回藏匿的东西,无论我检查了什么。特别是,我隐藏了一些东西,然后签出旧版本,然后弹出它,但在那个较早的时间点,隐藏的内容是无操作的,所以隐藏的消失了;我不能只是做 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 apply
不是 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.
使用以下步骤恢复它:
识别已删除的存储哈希代码:
gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )
樱桃挑选藏品:
gitcherry-pick-m 1 $stash_hash_code
使用以下方法解决冲突(如果有):
git 合并工具
此外,如果您使用 gerrit,您可能会遇到提交消息问题。请在执行以下替代方案之前先隐藏您的更改:
- 使用硬重置到之前的提交,然后重新提交此更改。
- 您还可以隐藏更改、变基并重新提交。
我确实不小心删除了 GitUP 应用程序中的隐藏内容。只需按 Ctrl+Z 即可撤消它。
也许它对某人有帮助;)