如何在Git中恢复丢失的存储库?

2467

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

我知道,如果我使用git stash,那么.git/refs/stash包含用于创建存储的提交的引用。而.git/logs/refs/stash包含整个存储。但是,在git stash pop之后,这些引用都没有了。我知道提交仍然在我的仓库中,但是我不知道它是什么。

是否有一种简单的方法来恢复昨天的存储提交引用?


105
未来注意事项:如果你不想在每次使用git stash pop时丢失你的储藏,你可以使用git stash apply代替。它做的与pop相同,但不会删除已应用储藏的引用。 - Kevin
16
在这里尝试了一切,都找不到已经弹出的藏匿点。非常感激 IntelliJ 的 https://www.jetbrains.com/help/idea/local-history.html - Ruan Mendes
另请参阅如何恢复隐藏的未提交更改 - mfaani
5
建议:对于任何你不愿意丢失的内容,避免使用 git stash。如果值得保存,那么就值得进行完整的提交(可能在单独的临时分支上)。使用 git commit,你的“stash”将更容易跟踪。首先,你可以包括提交信息。但更重要的是,即使你重置/删除了分支,你的更改也会在本地 reflog 中访问。详情请见 even if you reset/delete the branch - Brent Bradburn
我同意Brent的观点,不过你可以使用git stash push -m“我的存储消息…”为你的git存储条目添加消息,以更好地组织它们。 - James Hooper
非常感谢您提出这个问题,以及回答它的人。虽然这种情况并不经常发生在我身上,但在过去两周内,我的肌肉记忆竟然让我输入了 git stash clear 命令来清除杂乱的存储区。这个帖子帮助我恢复了存储区的提交。 - Aman
25个回答

24

我喜欢亚里士多德的方法,但不喜欢使用GITK……因为我习惯于使用命令行中的GIT。

相反,我提取了悬空提交,并将代码输出到DIFF文件,以便在我的代码编辑器中进行审查。

git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff

现在,您可以将生成的diff/txt文件(它在您的主文件夹中)加载到文本编辑器中,以查看实际代码和生成的SHA。

然后只需使用

git stash apply ad38abbf76e26c803b27a6079348192d32f52219

20
在macOS中,使用git v2.6.4时,我不小心运行了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} 即可。
希望这能够快速帮助到有需要的人。

19

我在简单的命令行窗口中(我的操作系统是Windows 7)无法使用任何答案。 命令 awkgrepSelect-string 都无法识别为命令。所以我尝试了不同的方法:

  • 首先运行:git fsck --unreachable | findstr "commit"
  • 将输出复制到记事本中
  • 找到 "unreachable 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)

可能不是最佳解决方案,但对我起作用了。


即使在Windows上,您也可以使用Git Bash。在Git Bash中,您拥有所有需要的(类Unix)命令行工具。 - Adrian W

19

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

大多数回答这个问题的人都会给出一些长长的命令和选项,几乎没有人会记住。所以人们进入这个问题并复制粘贴他们认为需要的内容,但几乎立刻就忘记了。

我建议所有有这个问题的人只需检查reflog(git reflog),不需要更多的操作。一旦你看到所有提交的列表,就有许多方法来找到你要查找的提交,并且可以挑选它或从它创建一个分支。在此过程中,你会学习到关于reflog和各种基本git命令的有用选项。


2
嗨,罗比。如果你正在工作,被分心了,需要回到几周前离开的地方继续工作,但发现找不到你存放的工作,那么这就很相关了——它可能在你做其他事情时丢失了。如果是最近的历史记录,reflog非常好用,但对于长时间间隔则不太适用。 - emragins
1
嘿emragins, 我同意,但这正是OP的用例。我不确定这里发布的其他命令会如何行事,但我的猜测是,一旦对他的存储提交的引用被清除,它们也将停止工作。 - RobbyD
1
嗯...上述情况引发了我提出这个问题,我知道在我(不知情地)丢失我的存储库和我能够恢复它之间至少有几周甚至更接近一个月的时间。 - emragins
1
这看起来很不错,但是......它没有列出我(最近)删除的stash的提交 - 相反我用了其中一个“诅咒的其他”解决方案找到了它;当然我不理解/记得他们命令中的所有内容,但它解决了我的问题,而且我随时可以回到这个页面(就像任何SO一样)!如果找到了另一个解决方案,则了解另一个“可能有效”的命令并不是必要的。 我不确定百分之百,但是这个RobbyD的解决方案似乎[没有涵盖stashes](https://dev59.com/NXVD5IYBdhLWcg3wGXlI#qLqkEYcBWogLw_1blRCi)。 - cellepo
@cellepo,你可能需要在__git reflog --all__命令中添加--all选项。据我所知,默认情况下reflog会过滤掉stash事件。 - James Moore
点赞是因为让自己陷入这种境地应该成为一次学习经历。不要让自己陷入需要奇怪咒语才能撤销的情况。 - Guildenstern

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”键可跳转到下一个差异。


12

Aristotle的被接受的回答将展示所有可达的提交,包括非类stash的提交。为了过滤掉噪音:

请参考Aristotle的被接受的答案来显示所有可以到达的提交,其中包括非类stash的提交。如果要过滤掉噪音:

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 个父提交(stash 就有)且消息中包含“WIP on”的提交。

请记住,如果您使用消息保存了您的 stash(例如 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

9

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

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

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

  2. 输入命令:git fsck --no-reflogs | find "dangling commit"enter image description here

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

  4. 如果相应的哈希值与您要恢复的内容匹配,请使用以下命令进行恢复:git stash apply [hash]


3
“dangling commit” 悬空提交,没有这样的文件或目录。 - lufinkey
@minTwin,非常感谢!你救了我(误删的文件)。 - InaFK

6

要在终端中查看提交记录,只筛选我们关注的提交记录,我们可以使用:

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

以下翻译基于 Aristotle Pagaltzis 的回答。


6

只查看stash提交记录,包括它们所附加的内容

示例结果

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:git fsck --dangling | tail -100 | awk ...

恢复示例 enter image description here


5

我来这里的目的是想知道如何恢复存储区,不论我之前检出了哪个版本。特别是,我曾经对某个文件进行了存储,然后检出了旧版本,并在该旧版本上执行了pop操作,但此时存储区在早期时间点上没有任何操作,因此存储区消失了;我不能只使用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.

这是一个回答,还是问题的延续? - Alex Brown
有点两者兼备。我之所以找到这个页面,是因为我丢失了一些代码并试图找回它。对于我来说,使用情况是在执行二分查找时,在每个步骤测试之前应用更改。我通过艰难的方式学会了,你不能只是弹出、测试、存储、二分,因为这可能会在存储中留下不同的提交,因此需要使用“stash apply”。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接