如何从 Git 错误中恢复

不要让一个 git 命令中的错误抹去几天的工作。
397 位读者喜欢这篇文章。
Bubble hands

Opensource.com

今天,我的同事差点丢失了他四天的工作成果。由于一个不正确的 git 命令,他丢弃了保存在 暂存区(stash) 中的更改。在这个令人难过的事件之后,我们寻找了一种方法来尝试恢复他的工作……我们做到了!

首先要警告的是:当您实施一个大型功能时,将其拆分成小块并定期提交。长时间工作而不提交更改不是一个好主意。

现在我们已经解决了这个问题,让我们演示如何恢复意外从暂存区(stash)丢弃的更改。

我的示例仓库只有一个源文件,main.c,看起来像这样

Repository with one source file

opensource.com

它只有一个提交,即初始提交

One commit

opensource.com

我们文件的第一个版本是

First version of the file

opensource.com

我将开始编写一些代码。对于这个例子,我不需要做很大的更改,只需要一些放入暂存区(stash)的东西,所以我将只添加一行新代码。git-diff 输出应该是

git-diff output

opensource.com

现在,假设我想从远程仓库拉取一些新的更改,但我还没有准备好提交我的更改。相反,我决定将它暂存(stash),拉取远程仓库的更改,然后将我的更改重新应用到主分支(master)。我执行以下命令将我的更改移动到暂存区(stash)

git stash

使用 git stash list 查看暂存区(stash),我可以看到我的更改在那里

Output of changes in our stash

opensource.com

我的代码在一个安全的地方,并且主分支(master)是干净的(我可以使用 git status 检查这一点)。现在我只需要拉取远程仓库的更改,然后将我的更改应用到主分支(master)上,我就应该完成了。

但我意外地执行了

git stash drop

这会删除暂存(stash),而不是

git stash pop

这会在从堆栈中删除暂存(stash)之前应用它。如果我再次执行 git stash list,我可以看到我从暂存区(stash)中丢弃了我的更改,而没有将其应用到主分支(master)上。天啊!谁能帮帮我?

好消息:git 没有删除包含我的更改的对象;它只是删除了对它的引用。为了证明这一点,我使用 git-fsck 命令,该命令验证数据库中对象的连接性和有效性。这是我在仓库上执行 git-fsck 命令后的输出

Output after executing the git-fsck command on the repository

opensource.com

使用 --unreachable 参数,我要求 git-fsck 显示不可达的对象。正如你所看到的,它没有显示不可达的对象。在我丢弃暂存区(stash)中的更改后,我执行了相同的命令,并收到了不同的输出

Output after dropping changes on stash

opensource.com

现在有三个不可达的对象。但哪个是我的更改呢?实际上,我不知道。我必须通过执行 git-show 命令来查看每个对象来搜索它。

Output after executing the git-show command

opensource.com

找到了!ID 95ccbd927ad4cd413ee2a28014c81454f4ede82c 对应于我的更改。现在我找到了丢失的更改,我可以恢复它了!一种解决方案是将 ID 检出(checkout)到一个新分支或直接应用提交。如果您有包含更改的对象的 ID,您可以决定将更改再次放到主分支(master)上的最佳方法。对于这个例子,我将使用 git-stash 将提交再次应用到我的主分支(master)上。

git stash apply 95ccbd927ad4cd413ee2a28014c81454f4ede82c

另一个需要记住的重要事项是 git 会定期运行其垃圾回收器。在执行 gc 后,您将无法再使用 git-fsck 查看不可达的对象。

本文最初发布在作者的博客上,并经许可转载。

标签
User profile image.
我是 José Guilherme Vanz,来自巴西南部。作为软件工程师工作。我热爱开源和编程

6 条评论

我认为如果用户可以配置 Git 在执行可能导致数据丢失的操作之前总是要求确认(即使有些操作像这里展示的那样可以恢复,大多数用户永远不会发现这一点),这将非常有趣。我记得有一次我丢失了一个完整的提交,不得不全部重写。我很幸运,我仍然记得部分代码。

好建议。您应该在 Git 社区中提出这个建议。也许他们可以实现您的想法。

回复 作者 teresaejunior (未验证)

唯一没有撤消的地方是 git reset --hard,它会影响工作目录。请改用 git stash -u。

很高兴知道。谢谢你的提示。当我写这篇文章时,我不知道这一点。所以,我找到了一个困难的解决方案。 ;)

回复 作者 Adam Dymitruk (未验证)

很有趣,但你为什么不使用 "git reflog" 呢?它可以让你摆脱几乎任何情况。

实际上,'git stash' 是使用 reflog _实现_ 的,而删除一个暂存条目意味着从实现暂存的 reflog 中删除条目。

如果您指的是 'master' 或 'HEAD' 的 reflog - 是的,这是一个好问题。

回复 作者 Jacopo (未验证)

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© . All rights reserved.