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

问题描述 投票:1495回答:18

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

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

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

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

git recovery git-stash
18个回答
2518
投票

一旦你知道你删除的存储提交的哈希值,你可以将它作为存储应用:

git stash apply $stash_hash

或者,您可以为其创建单独的分支

git branch recovered $stash_hash

之后,您可以使用所有常规工具执行任何操作。当你完成后,只需将树枝吹走。

Finding the hash

如果你刚刚弹出它并且终端仍然打开,你将still have the hash value printed by git stash pop on screen(谢谢,Dolda)。

否则,你可以在Linux,Unix或Git Bash for Windows中找到它:

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

...或者使用Powershell for Windows:

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

...或者如果使用Powershell for Windows,请参阅the answer from emragins

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

如果你喜欢在一个单独的GUI应用程序上在控制台上使用一个漂亮的图形,你可以用gitk替换git log --graph --oneline --decorate

要查找存储提交,请查找此表单的提交消息:

在somebranch上的WIP:commithash一些旧的提交消息

注意:如果您在执行git stash时未提供消息,则提交消息将仅采用此形式(以“WIP on”开头)。


14
投票

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

git fsck --unreachable

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

git show hash

如果你发现藏匿物品,最后申请 -

git stash apply hash

12
投票

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

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

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


12
投票

在使用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时,应用{commit-id}

希望这可以帮助别人


10
投票

我想添加到已接受的解决方案另一个好方法来完成所有更改,当你没有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'进入下一个差异。


10
投票

我无法在一个简单的命令窗口(在我的例子中是Windows 7)中获得在Windows上工作的任何答案。 awkgrepSelect-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)

可能不是最好的解决方案,但为我工作


9
投票

亚里士多德接受的答案将显示所有可达的提交,包括非类似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个父提交(存储将具有)的提交,并且其消息包括“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

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 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.

4
投票

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

  1. 识别已删除的存储哈希码: gitk --all $(git fsck --no-reflog | awk'/ dangling commit / {print $ 3}')
  2. Cherry Pick the Stash: git cherry-pick -m 1 $ stash_hash_code
  3. 解决冲突,如果有的话: git mergetool

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

  1. 使用硬重置到先前的提交,然后重新发送此更改。
  2. 你也可以隐藏变化,改变和重新发明。

2
投票

我最喜欢的是这个单行:

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

这与this answer基本相同,但更短。当然,您仍然可以添加--graph以获得树状显示。

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

git stash apply THE_COMMIT_HASH_FOUND

对我来说,使用--no-reflogs确实揭示了丢失的藏匿条目,但--unreachable(在许多其他答案中找到)没有。

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

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


-2
投票

我确实意外地删除了GitUP应用程序中的藏匿处。只需按Ctrl + Z即可撤消它。

也许它有助于某人;)


671
投票

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

将它作为分支也允许您自由地操纵它;例如,樱桃挑选或合并它。


255
投票

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

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

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


88
投票

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

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

如果您为藏匿处提供了标题,请将命令末尾的-grep=WIP中的“WIP”替换为部分消息,例如: -grep=Tesselation

该命令正在为“WIP”进行grepping,因为存储的默认提交消息的格式为WIP on mybranch: [previous-commit-hash] Message of the previous commit.


71
投票

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

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

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

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

38
投票

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

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


30
投票

如果你想要重建一个丢失的存储,你需要先找到丢失的存储的哈希值。

正如亚里士多德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,你只需更改你的存储reflog以添加旧的存储:

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

25
投票

使用gitk等效的Windows PowerShell:

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

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


16
投票

我喜欢亚里士多德的方法,但不喜欢使用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
© www.soinside.com 2019 - 2024. All rights reserved.