如何在Git中恢复被丢弃的存储?
我经常使用git stash
和git 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 somebranch : commithash一些旧的提交消息
注意 :如果您在执行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上的答案。 awk
, grep
和Select-string
不被识别为命令。 所以我尝试了一种不同的方法:
- 第一次运行:
git fsck --unreachable | findstr "commit"
git fsck --unreachable | findstr "commit"
- 将输出复制到记事本
- 用
start cmd /k git show
findreplace“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 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.
通过以下步骤恢复它:
-
确定已删除的存储哈希码:
gitk –all $(git fsck –no-reflog | awk'/ dangling commit / {print $ 3}')
-
樱桃挑选藏匿:
git cherry-pick -m 1 $ stash_hash_code
-
解决冲突(如果有的话):
混帐mergetool
另外,如果你正在使用gerrit,你可能会遇到提交信息的问题。 请在下一个备选scheme之前存储您的更改:
- 使用硬重置到以前的提交,然后重新提交此更改。
- 你也可以隐藏这个变化,重build和重新提交。