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

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

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

有没有一种简单的方法来恢复昨天的stash提交引用?

467267 次浏览

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

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

我刚刚构建了一个命令,帮助我找到丢失的stash提交:

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/对象树中的所有对象,定位提交类型的对象,然后显示每个对象的摘要。从这一点来看,只需查看提交以找到适当的“工作进展中:6a9bb2”(“工作”是我的分支,619bb2是最近的提交)。

我注意到,如果我使用“git stash应用”而不是“git stash pop”,我就不会有这个问题,如果我使用“git stash保存消息”,那么提交可能更容易找到。

更新:使用Nathan的想法,这变得更短:

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

一旦你知道你丢弃的stash提交的哈希值,你就可以将其应用为stash:

git stash apply $stash_hash

或者,您可以为它创建一个单独的分支

git branch recovered $stash_hash

在那之后,你可以用所有的普通工具做任何你想做的事情。当你完成时,把树枝吹走。

查找哈希

如果您刚刚弹出它并且终端仍然打开,您将仍然有#0在屏幕上打印的哈希值(谢谢,Dolda)。

否则,您可以在Windows的Linux、Unix或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] }

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

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

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

…或如果使用PowerShell for Windows,请参阅来自emragins的答案

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

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

要发现stash提交,请查找此表单的提交消息:

        进展中某个分支一些旧的提交消息

说明:提交消息只会以这种形式出现(以“进展中”开头),如果您在执行git stash时没有提供消息。

只是想在接受的解决方案中提到这个补充。当我第一次尝试这种方法时,我并不是很明显(也许它应该是),但是要应用哈希值的存储,只需使用“git stash应用”:

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

当我刚接触git时,这对我来说并不清楚,我正在尝试“git show”、“git应用”、“patch”等不同的组合。

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

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

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

该命令表示“进展中”,因为存储的默认提交消息的形式为WIP on mybranch: [previous-commit-hash] Message of the previous commit.

当您找到提交时,使用git stash apply <commit_hash>应用它

如果你想重置一个丢失的藏匿,你需要先找到你丢失的藏匿的哈希。

正如亚里士多德·帕加尔齐斯所建议的那样,0应该对你有所帮助。

就我个人而言,我使用我的log-all别名,它向我展示每一次提交(可恢复的提交),以便更好地了解情况:

git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)

如果您只查找“进展中”消息,则可以进行更快的搜索。

一旦你知道了你的sha1,你只需更改你的stash reflg来添加旧的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

如果你没有关闭终端,只需查看git stash pop的输出,你就会得到删除的存储的对象ID。它通常看起来像这样:

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

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

要取回该藏匿,只需运行git branch tmp 2cae03e,你将把它作为一个分支获得。要将其转换为藏匿,请运行:

git stash apply tmpgit 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 stash pop。我正在做bisect,并且有一个小补丁,我想在每个bisect步骤应用。现在我正在这样做:

$ git reset --hard; git bisect good; git stash apply$ # Run tests$ git reset --hard; git bisect bad; git stash applyetc.

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

  1. 确定已删除的存储哈希代码:

  2. 樱桃采摘:

    git chury-pice-m 1$stash_hash_code

  3. 使用以下方法解决冲突:

    git mergetool

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

  1. 使用硬重置到以前的提交,然后重新启动此更改。
  2. 您也可以隐藏更改、重新定位和重新启动。

使用gitk的Windows PowerShell等效:

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

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

亚里士多德接受的答案将显示所有可达的提交,包括非类似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将有),其消息包括“进展中”。

请记住,如果您使用消息(例如git stash save "My newly created stash")保存了您的存储,这将覆盖默认的“进展中…”消息。

您可以显示有关每个提交的更多信息,例如显示提交消息,或将其传递给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

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

这个问题的大多数答案都给出了带有选项的长命令,几乎没有人会记住。所以人们进入这个问题并复制粘贴他们认为需要的任何东西,然后几乎立即忘记它。

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

在带有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应用{提交ID}

希望这能帮助别人快

我无法在一个简单的命令窗口(我的情况是Windows 7)中获得任何在Windows上工作的答案。awkgrepSelect-string无法识别为命令。所以我尝试了一种不同的方法:

  • 第一次运行:git fsck --unreachable | findstr "commit"
  • 将输出复制到记事本
  • 将“不可达提交”替换为start cmd /k git show

它看起来像这样:

启动cmd /kgit show 8506d235f935b92df65d58e7d75e9441220537a4启动cmd /kgit show 44078733e1b36962571019126243782421fcd8ae启动cmd /kgit show ec09069ec893db4ec1901f94eefc8dc606b1dbf1启动cmd /kgit show d00aab9198e8b81d052d90720165e48b287c302e

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

可能不是最好的解决办法但对我有效

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

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确实揭示了丢失的藏匿条目,但--unreachable(如许多其他答案所示)没有。

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

信用:上述命令的详细信息取自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}' )

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

您可以按照以下步骤一步一步:

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

2-显示不可达的提交哈希git show hash

3-复制所有日志,可以看到日志状、不可达的blob、提交、树。

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

这对我来说很有效(在2022年),从Windows环境中恢复我在git中意外删除的藏匿。

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

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

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

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

  4. 如果对应的哈希与您要恢复的内容匹配,请使用以下命令将其恢复"git stash apply [hash]

知道近似的文件名和它的位置,并且能够找到丢弃的隐藏文件greping悬空提交路径

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

只查看stash提交,它们附加在哪里,以及它们的内容是什么

结果样本

Checking object directories: 100% (256/256), done.2022-08-31 10:20:46 +0900 8d02f61 WIP on master: 243b594 add cssA       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 ...

恢复样本输入图片描述

不完全是一个答案得到一个藏匿,但如果目标是得到未提交的更改,首先藏匿,然后弹出在另一个分支,但意味着两者,并以以下方式完成:

  1. branch_a中进行更改
  2. git stash
  3. branch_bbranch_a
  4. git stash apply

然后将未提交的更改恢复到branch_a

  1. git checkout branch_a
  2. git merge branch_b
  3. git reset HEAD~1