我经常使用gitstash和gitstashpop来保存和恢复工作树中的更改。昨天,我在我的工作树中做了一些修改,这些修改是我藏起来并弹出的,然后我对工作树做了更多的修改。我想回去回顾一下昨天隐藏的更改,但gitstashpop似乎删除了对相关提交的所有引用。

我知道如果我使用git stash,那么.git/refs/stash包含用于创建stash的提交的引用。git/logs/refs/stash包含整个存储。但这些参考资料在git stash pop之后就不见了。我知道提交仍在我的存储库中,但我不知道它是什么。

有没有一种简单的方法可以恢复昨天的存储提交引用?


gitfsck——不可访问|grep提交应该显示sha1,尽管它返回的列表可能很大。gitshow<sha1>将显示它是否是您想要的提交。

gitcherry-pick-m1<sha1>将提交合并到当前分支。


我刚刚构造了一个命令,帮助我找到丢失的藏匿物品:

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

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

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

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

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

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

…或使用PowerShell for Windows:

git fsck --no-reflog | select-string 'dangling commit' | foreach { $_.ToString().Split(" ")[2] }

这将在提交图的顶端显示所有提交,这些提交不再从任何分支或标记引用——每个丢失的提交,包括您创建的每个隐藏提交,都将位于该图的某个位置。

找到您想要的隐藏提交的最简单方法可能是将该列表传递给gitk:

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

…如果使用PowerShell for Windows,请查看emragins的答案。

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

如果您更喜欢控制台上的图形而不是单独的GUI应用程序,那么可以用git-log-grap-online之类的东西来替换gitk。

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

某个分支上的WIP:commithash一些旧的提交消息

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


我只是想在公认的解决方案中提及这一点。第一次尝试这个方法时,我并没有立即意识到这一点(也许应该是这样),但要从哈希值应用存储,只需使用“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中的“WIP”替换为消息的一部分,例如-grep=Tesselation。

该命令是“WIP”的grepping,因为存储的默认提交消息的格式为mybranch上的WIP:[先前提交哈希]先前提交的消息。

找到提交后,使用git stash apply<commit_hash>应用它


如果你想重新存放丢失的东西,你需要先找到丢失的东西的散列。

正如亚里斯多德·帕加尔茨所建议的那样,一个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

你可能更希望有一个相关的消息,所以

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

如果您没有关闭终端,只需查看git stash pop的输出,您将获得丢弃的stash的对象ID。通常情况如下:

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

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

要取回存储,只需运行gitbranchtmp2cae03e,就可以将其作为一个分支获取。要将其转换为存储,请运行:

git stash apply tmp
git stash

把它作为一个分支也可以让你自由地操纵它;例如,樱桃采摘或合并它。


我想在已接受的解决方案中添加另一个好方法,以在您没有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”进入下一个差异。


我喜欢亚里士多德的方法,但不喜欢使用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 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 stashpop。我在做二等分,在每一个二等分步骤上都有一个小补丁。现在我正在做这件事:

$ 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‘/悬空提交/{print$3}‘)樱桃摘葡萄:git cherry pick-m 1$stash_hash_code使用以下方法解决冲突(如果有):git合并工具

此外,如果使用gerrit,您可能会遇到提交消息的问题。在执行下一个备选方案之前,请先保存您的更改:

使用硬重置到上一次提交,然后重新提交此更改。你也可以把零钱藏起来,重新放在底座上,然后重新提交。


使用gitk的Windows PowerShell等效程序:

gitk--all$(git fsck--no reflog |选择字符串“(悬空提交)(.*)”|%{$_.Line.Split('')[2]})

也许有一种更有效的方法可以在一个管道中完成这项工作,但这确实有效。


亚里士多德接受的答案将显示所有可到达的提交,包括非隐藏类提交。要过滤掉噪声:

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存储保存“我新创建的存储”),这将覆盖默认的“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

为什么人们会问这个问题?因为他们还不知道或不了解回流。

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

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


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

若你们知道储藏物的名称,那个么使用:

$git fsck--无法访问|grep commit |cut-c 20-|xargs git show |grep-B 6-A 2<存储的名称>

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

$git fsck--无法访问|grep commit |cut-c 20-|xargs git show

然后,当您找到提交id时,只需点击git stash apply{commit id}

希望这能帮助到某人


我无法在一个简单的命令窗口(在我的例子中是Windows7)中获得在Windows上工作的任何答案。awk、grep和Select字符串未被识别为命令。所以我尝试了一种不同的方法:

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

将看起来像这样:

启动cmd/k git show 8506d235f935b92df65d58e7d75e9441220537a4启动cmd/k git show 44078733e1b36962571019126243782421fcd8ae启动cmd/k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1启动cmd/k git show d00aab9198e8b81d052d90720165e48b287c302e

保存为.bat文件并运行它脚本将打开一组命令窗口,显示每个提交如果你找到了你要找的,运行:git stash apply(你的哈希)

也许不是最好的解决方案,但对我来说有效


通过在终端中写入此命令,可以列出所有无法访问的提交-

git fsck --unreachable

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

git show hash

如果你找到了隐藏的物品,最后申请-

git stash apply hash

我最喜欢的是这一行:

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

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

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

git stash apply THE_COMMIT_HASH_FOUND

对我来说,使用--no reflogs确实揭示了丢失的存储条目,但--unareachable(正如许多其他答案中所发现的)并没有。

在Windows下,在gitbash上运行它。

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


要查看终端中的提交,只有过滤我们关心的提交,我们才能使用:

git log --oneline --all --grep="^WIP on .*: [a-f0-9]\+" --grep="^On [^ ]*:" --grep="^index on [^ ]*:" $( env LANG=C git fsck --no-reflog | awk '/dangling commit/ {print $3}' )

这是基于亚里斯多德·帕加尔茨的回答。


您可以一步一步地执行以下过程:

1-使用下面列出所有无法访问的提交git fsck—无法访问

2-显示无法访问的提交哈希git显示哈希

3-复制所有日志,您可以看到类似日志的、无法访问的blob、提交和树。

4-使用具有提交哈希的日志应用git stashgit stash apply[替换哈希]


这对我来说(在2022年)很有用,我从windows环境中恢复了我意外删除的git存储。

这些步骤概述了如何恢复任何已删除的git存储或分支(假设它未被垃圾收集永久删除)。

导航到项目所在的目录。输入命令:git fsck--no reflogs | find“悬空提交”将出现悬空提交的哈希列表。这些将包含已删除的分支和存储。从复制并粘贴列表末尾附近的哈希开始,以找到您的隐藏或分支。例如,使用命令:git-log-1[hash]如果相应的哈希值与您尝试恢复的哈希值匹配,请使用以下命令将其还原“git存储应用


知道了大概的文件名及其位置,并且能够找到丢弃的隐藏文件,并将悬空提交添加到路径中

for i in $(git fsck --no-reflogs | awk '/dangling commit/ {print $3}'); do
  if git log -5 --name-only -u $i | grep -q "<path-to-files>/.*<partial-file-name>.*"; then
    echo "found something in commit $i";
  fi;
done

只查看隐藏提交、它们附加的位置以及它们的内容

结果样本

Checking object directories: 100% (256/256), done.
2022-08-31 10:20:46 +0900 8d02f61 WIP on master: 243b594 add css
A       favicon.ico

命令

git fsck --dangling | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ct %h" | sort | awk '{print $2}' | { while read hash; do status=$(git stash show $hash --name-status 2>/dev/null); if (( $? == 0 )); then git show $hash -s --format="%C(green)%ci %C(yellow)%h %C(blue)%B"; echo "$status"; fi; done; }

若要查看完整哈希,请将%h更改为%h为了减少时间,尾部fsck类似于gitfsck--悬挂|tail-100|awk。。。

回收样品


不完全是一个获取隐藏的答案,但如果目标是获取未提交的更改,这些更改首先被隐藏,然后被弹出到另一个分支中,但这两种更改都适用,并按以下方式完成:

在分支中进行更改(_a)暂存从branch_a生成branch_b应用git存储

然后,要将未提交的更改还原到branch_a:

git结帐分支agit合并分支bgit重置HEAD~1