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

我经常使用git stashgit stash pop来保存和恢复工作树中的更改。 昨天,我在工作树上发生了一些变化,我把它藏起来,然后对工作树进行了更多的修改。 我想回顾一下昨天的隐藏的更改,但git stash pop隐藏git stash pop似乎删除所有对相关提交的引用。

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

有没有简单的方法来恢复昨天的存储提交参考?

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

如果你刚刚popup它,并且terminal仍然是打开的,你仍然可以在屏幕上用git stash pop打印散列值 (谢谢Dolda)。

否则,你可以在Linux和Unix上使用它:

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

和Windows:

 git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];} 

这将向您显示提交图的提示中的所有提交,这些提交不再从任何分支或标记中引用 – 每次丢失的提交(包括您创build的每个提交提交)都将位于该图的某处。

find你想要的隐藏提交最简单的方法可能是将该列表传递给gitk

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

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

你可以用git log --graph --oneline --decorate来代替那里的gitk ,如果你喜欢在一个单独的GUI应用程序的控制台上的一个很好的graphics。

要发现存储提交,查找这种forms的提交消息:

WIP on somebranchcommithash一些旧的提交消息

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

一旦你知道你想要的提交的散列,你可以将它作为一个藏匿:

 git stash apply $stash_hash 

或者你可以使用gitk的上下文菜单来为你感兴趣的任何无法访问的提交创build分支。在那之后,你可以用所有普通的工具对它们做任何你想做的事情。 当你完成后,再把这些树枝吹走。

如果你没有closuresterminal,只要看看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 

拥有它作为一个分支也可以让你自由操纵它; 例如,挑选或合并它。

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

 $ git stash apply ad38abbf76e26c803b27a6079348192d32f52219 

当我刚接触git的时候,我并不清楚,而且我尝试了不同的“git show”,“git apply”,“patch”等组合。

我刚刚构build了一个帮助我find丢失的存储提交的命令:

 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树中的所有对象,findtypes为commit的对象,然后显示每个对象的摘要。 从这一点来看,只是通过提交find一个适当的“工作WIP:6a9bb2”(“工作”是我的分支,619bb2是最近的提交)的问题。

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

更新:与纳森的想法,这变得更短:

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

要获取仍在您的存储库中,但无法访问的存储列表:

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

如果你给藏了一个标题,用-grep=Tesselation命令末尾的“WIP”。

该命令是grep for“WIP”,因为WIP on mybranch: [previous-commit-hash] Message of the previous commit.的默认提交消息的格式WIP on mybranch: [previous-commit-hash] Message of the previous commit.

git fsck --unreachable | grep commit git fsck --unreachable | grep commit应该显示sha1,虽然它返回的列表可能会相当大。 git show <sha1>会显示它是否是你想要的提交。

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

如果你想恢复一个丢失的藏匿处,你需要先find你遗失的藏匿处的散列。

亚里斯多德Pagaltzisbuild议git fsck应该帮助你。

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

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

如果您仅查看“在制品”消息,则可以进行更快的search。

一旦你知道了你的sha1,你只需要改变你的存储引用来添加旧的存储:

 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 

我喜欢亚里士多德的方法,但不喜欢使用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 v2.6.4的OSX中,我只是无意中运行git stash drop,然后通过下面的步骤find它

如果你知道藏匿的名字,然后使用:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>

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

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

然后,当你findcommit-id时,只要按下git stash apply {commit-id}

希望这可以帮助快速的人

我想添加到接受的解决scheme,通过所有的变化,当你要么没有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'进入下一个区别。

Windows PowerShell相当于使用gitk:

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

在一个pipe道中可能有更高效的方法来完成这个工作,但是这个工作是完成的。

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

这个问题的大部分答案都给出了几乎没人会记得的选项的长命令。 因此,人们会进入这个问题,并复制粘贴任何他们认为需要的东西,几乎马上就忘记了。

我会build议大家用这个问题来检查reflog(git reflog),而不是更多。 一旦你看到所有提交的列表,有一百种方法来找出你正在寻找什么提交,并挑选它或从中创build一个分支。 在这个过程中,您将了解到各种基本的git命令的reflog和有用的选项。

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

 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 

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

  • 第一次运行: git fsck --unreachable | findstr "commit" git fsck --unreachable | findstr "commit"
  • 将输出复制到记事本
  • start cmd /k git showfindreplace“unreachable commit”

会看起来像这样:

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文件并运行
  • 该脚本将打开一堆命令窗口,显示每个提交
  • 如果你find了你要找的那个,运行: git stash apply (your hash)

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

我到这里来寻找的是如何真正把藏东西,无论我已经检查了。 特别是,我藏了一些东西,然后检查出一个老版本,然后把它扔了,但是在那个较早的时间点,这个存储器是没有操作的,所以这个存储器就消失了。 我不能只是把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 applygit 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. 

通过以下步骤恢复它:

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

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

  2. 樱桃挑选藏匿:

    git cherry-pick -m 1 $ stash_hash_code

  3. 解决冲突(如果有的话):

    混帐mergetool

另外,如果你正在使用gerrit,你可能会遇到提交信息的问题。 请在下一个备选scheme之前存储您的更改:

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