首页 文章

如何在Git中恢复被删除的存储?

提问于
浏览
1401

我经常使用 git stashgit stash pop 来保存和恢复工作树中的更改 . 昨天我的工作树上有一些变化,我已经藏起来了,然后我对工作树做了更多修改 . 我保留了更改,但 git stash pop 似乎删除了对关联提交的所有引用 .

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

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

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

19 回答

  • 243

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

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

    如果您为藏匿物提供了 Headers ,请将命令末尾的 -grep=WIP 中的"WIP"替换为部分信息,例如: -grep=Tesselation .

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

  • 2253

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

    $ git stash apply ad38abbf76e26c803b27a6079348192d32f52219
    

    当我刚开始使用git时,这对我来说并不清楚,我正在尝试“git show”,“git apply”,“patch”等的不同组合 .

  • 9

    使用gitk等效的Windows PowerShell:

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

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

  • 10

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

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

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

  • 15

    另一个常见用例: You tried popping onto the wrong branch and got conflicts?

    你想要的只是撤消pop,但仍然将它保存在存储列表中,这样你就可以在正确的分支上弹出它 .

    要解决此问题,请执行以下操作:

    git reset HEAD --hard
    git checkout my_correct_branch
    git stash pop
    

    完成!

  • 11

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

    git fsck --unreachable
    

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

    git show hash
    

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

    git stash apply hash
    
  • 70

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

    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
    
  • 14

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

    正如亚里士多德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
    
  • 24

    亚里士多德接受的答案将显示所有可到达的提交,包括非存储类提交 . 滤除噪音:

    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

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

    也许它有助于某人;)

  • 8

    我喜欢亚里士多德的方法,但不喜欢使用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
    
  • 0

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

  • 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时,应用

    希望这可以帮助别人

  • 7

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

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

  • 0

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

    • 标识已删除的存储哈希码:

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

    • Cherry Pick the Stash:

    git cherry-pick -m 1 $ stash_hash_code

    • 解决冲突(如果有)使用:

    git mergetool

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

    • 使用硬重置为先前的提交,然后重新发送此更改 .

    • 您也可以隐藏变更,重新定义和重新发送 .

  • 37

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

    • 首次运行: git fsck --unreachable | findstr "commit"

    • 将输出复制到记事本

    • 使用 start cmd /k git show 查找替换"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文件并运行它

    • 该脚本将打开一堆命令窗口,显示每次提交

    • 如果找到了您要找的那个,请运行: git stash apply (your hash)

    可能不是最好的解决方案,但对我有用

  • 4

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

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

  • 71

    如果你刚刚弹出它并且终端仍然打开,你将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应用程序上在控制台上使用一个漂亮的图形,你可以用 git log --graph --oneline --decorate 替换 gitk .

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

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

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

    一旦知道了所需提交的哈希值,就可以将其应用为存储:

    git stash apply $stash_hash
    

    或者您可以使用 gitk 中的上下文菜单为您感兴趣的任何无法访问的提交创建分支 . 之后,您可以使用所有常规工具随意执行任何操作 . 当你完成后,再次吹走那些树枝 .

  • 637

    我来这里寻找的是如何实际获得藏匿,无论我检查了什么 . 特别是,我藏了些东西,然后检查了一个旧的版本,然后加油,但是那个藏匿处在那个早期的时间点是无操作的,所以藏匿物消失了;我不能只做 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.
    

相关问题