2142

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

我知道如果我使用git stash然后.git/refs/stash 包含用于创建存储的提交的引用。并且.git/logs/refs/stash 包含整个存储。但是那些参考文献都没有了git stash pop。我知道提交仍在我的存储库中的某个地方,但我不知道它是什么。

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

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

4

24 回答 24

3433

一旦您知道您删除的存储提交的哈希值,您就可以将其应用为存储:

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 { $_.ToString().Split(" ")[2] }

这将在提交图的提示处向您显示不再从任何分支或标签引用的所有提交——每个丢失的提交,包括您曾经创建的每个存储提交,都将在该图中的某个位置。

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

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

...或者如果使用 Windows 的 Powershell,请参阅emragins 的答案。

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

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

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

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

注意:如果您在提供消息时没有提供消息,则提交消息只会采用这种形式(以“WIP on”开头)git stash

于 2008-09-18T11:38:25.550 回答
770

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

将它作为一个分支还可以让您自由地操作它;例如,挑选它或合并它。

于 2011-10-21T03:13:24.043 回答
291

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

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

当我刚接触 git 时,我并不清楚,我正在尝试“git show”、“git apply”、“patch”等的不同组合。

于 2009-03-03T20:28:51.603 回答
166

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

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

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

该命令正在搜索“WIP”,因为存储的默认提交消息采用以下形式WIP on mybranch: [previous-commit-hash] Message of the previous commit.

于 2011-05-04T06:42:36.980 回答
89

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

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 message ”那么提交可能更容易找到。

更新:有了 Nathan 的想法,这变得更短:

for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
于 2008-09-18T02:10:13.467 回答
66

使用 gitk 的 Windows PowerShell 等效项:

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

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

于 2016-01-07T23:22:05.920 回答
51

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

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

于 2008-09-18T02:08:15.390 回答
44

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

正如亚里士多德 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
于 2011-06-23T14:20:51.370 回答
23

我喜欢亚里士多德的方法,但不喜欢使用 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
于 2013-01-07T20:17:34.283 回答
23

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

git fsck --unreachable

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

git show hash

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

git stash apply hash
于 2018-03-07T11:31:52.277 回答
21

我最喜欢的是这个单行:

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

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

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

git stash apply THE_COMMIT_HASH_FOUND

对我来说, using--no-reflogs确实揭示了丢失的存储条目,但--unreachable(在许多其他答案中发现)没有。

当你在 Windows 下时,在 git bash 上运行它。

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

于 2019-08-26T08:14:31.040 回答
18

在带有 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

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

希望这可以快速帮助某人

于 2016-06-09T13:19:05.383 回答
16

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

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

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

于 2016-05-26T10:38:18.757 回答
16

我无法在一个简单的命令窗口(在我的情况下为 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)

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

于 2017-02-22T09:05:08.060 回答
14

当您没有可用的 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”进入下一个差异。

于 2013-01-04T12:32:19.047 回答
13

您可以通过 2 个简单的步骤实现此目的

  1. 列出丢失的存储 -> 为所有存储都被丢弃的项目运行此命令:

    git fsck --unreachable | grep 提交 | 剪切-d''-f3 | xargs git log --merges --no-walk

  2. 将丢失的存储发送回它的来源 --> 让我们使用第二个存储的提交哈希:

    git update-ref refs/stash 4b3fc45c94caadcc87d783064624585c194f4be8 -m "我恢复的存储"

于 2020-07-30T11:25:29.307 回答
12

亚里士多德接受的答案将显示所有可访问的提交,包括非隐藏式提交。滤除噪音:

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
于 2016-05-17T04:33:50.830 回答
5

通过使用以下步骤恢复它:

  1. 识别已删除的存储哈希码:

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

  2. 樱桃挑选藏匿处:

    git 樱桃挑选 -m 1 $stash_hash_code

  3. 使用以下方法解决冲突(如果有):

    git 合并工具

此外,如果您使用 gerrit,您可能会遇到提交消息的问题。请在遵循下一个替代方案之前存储您的更改:

  1. 使用硬重置到先前的提交,然后重新提交此更改。
  2. 您还可以存储更改、变基和重新提交。
于 2015-08-26T02:18:20.407 回答
5

要在终端中查看提交,只过滤我们关心的提交,我们可以使用:

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

这是基于亚里士多德 Pagaltzis 的回答。

于 2021-02-17T20:07:08.043 回答
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.
于 2013-11-07T15:00:19.517 回答
4
git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP

试试这个

于 2021-05-25T12:59:03.133 回答
2

这对我(2022 年)有用,可以从 Windows 环境中恢复我在 git 中意外删除的存储。

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

  1. 导航到您的项目所在的目录。

  2. 输入命令:git fsck --no-reflogs | find "dangling commit" 在此处输入图像描述

  3. 将出现悬空提交的哈希列表。这些将包括已删除的分支和存储。从复制并粘贴列表末尾附近的哈希开始,以找到您的存储或分支。例如,使用以下命令:git log -1 [hash]

  4. 如果相应的哈希与您要恢复的哈希匹配,请使用以下命令恢复它“ git stash apply [hash]

于 2022-01-27T00:14:50.980 回答
0

您可以逐步按照以下流程进行操作:

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

2-通过 git show hash 显示无法访问的提交哈希

3-复制所有日志,你可以看到日志,无法访问的blob,提交,树。

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

于 2021-09-09T11:58:34.113 回答
0

知道大概的文件名和它的位置,并且能够找到丢弃的存储文件 grepping 悬空提交的路径

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
于 2022-03-02T19:10:52.867 回答