如何恢复Git中掉落的存储?

如何恢复Git中掉落的存储?,git,recovery,git-stash,Git,Recovery,Git Stash,我经常使用git stash和git stash pop来保存和恢复工作树中的更改。昨天我在我的工作树上做了一些修改,我把这些修改藏了起来,然后我对我的工作树做了更多的修改。我想回顾一下昨天隐藏的更改,但是git stash pop似乎删除了对相关提交的所有引用 我知道如果我使用git stash,那么.git/refs/stash包含用于创建stash的提交的引用。和.git/logs/refs/stash包含整个隐藏。但是那些引用在git stash pop之后就消失了。我知道提交仍然在我的

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

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

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

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

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


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

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

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:6a9bb2”(“工作”是我的分支,619bb2是最近的提交)

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

更新:有了Nathan的想法,这变得更短了:

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

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

git stash apply $stash_hash
或者,您可以使用为其创建单独的分支

git branch recovered $stash_hash
在那之后,你可以用所有的普通工具做任何你想做的事情。完成后,就把树枝吹走

查找散列 如果你刚刚打开它,终端仍然打开,你会的(谢谢,多尔达)

否则,您可以在Linux、Unix或Git Bash for Windows中使用以下命令找到它:

git fsck --no-reflog | awk '/dangling commit/ {print $3}'
git fsck --no-reflog | select-string 'dangling commit' | foreach { $_.ToString().Split(" ")[2] }
…或使用Powershell for Windows:

git fsck --no-reflog | awk '/dangling commit/ {print $3}'
git fsck --no-reflog | select-string 'dangling commit' | foreach { $_.ToString().Split(" ")[2] }
这将显示提交图顶端不再从任何分支或标记引用的所有提交–每个丢失的提交,包括您创建的每个隐藏提交,都将位于该图的某个位置

找到所需隐藏提交的最简单方法可能是将该列表传递给
gitk

gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )
…或查看是否使用Powershell for Windows

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

如果您更喜欢控制台上的图形而不是单独的GUI应用程序,那么您可以使用类似于
git log--graph--oneline--decoration
的东西来替换那里的
gitk

要发现隐藏提交,请查找以下表单的提交消息:

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


注意:如果您在执行git stash时未提供消息,则提交消息将仅以这种形式出现(以“WIP on”开头)。git stash只是想在已接受的解决方案中提及此添加内容。在我第一次尝试这种方法时,我并没有立即意识到这一点(也许应该是这样),但要从散列值应用隐藏,只需使用“git stash apply”:


当我刚接触git时,我并不清楚这一点,我尝试了“git show”、“git apply”、“patch”等不同组合,以获取仍在存储库中但无法访问的存储列表:

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

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


该命令正在为“WIP”进行灰色映射,因为存储的默认提交消息的格式为上一次提交的
WIP on mybranch:[previous commit hash]消息。

如果要重新刷新丢失的存储,需要先找到丢失的存储的哈希

正如亚里士多德·帕格尔茨(Aristotle 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

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

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

我想在没有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
现在您可以加载结果了
$ 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 reset --hard; git bisect good; git stash apply
$ # Run tests
$ git reset --hard; git bisect bad; git stash apply
etc.
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
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
git fsck --unreachable
git show hash
git stash apply hash
git log --oneline  $( git fsck --no-reflogs | awk '/dangling commit/ {print $3}' )
git stash apply THE_COMMIT_HASH_FOUND
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}' )
git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP