我在我的git代码库中解决了这个问题,我认为它是由不稳定的文件系统(在Windows主机上运行的Linux虚拟机)在硬重置时出现故障引起的。
我和你一样,也是从同样的起点开始:
jack@machine:~/git/cs$ git status
error: object file .git/objects/2e/f529faaaed03b2384b9f4d49a2ea95d7833894 is empty
error: object file .git/objects/2e/f529faaaed03b2384b9f4d49a2ea95d7833894 is empty
fatal: loose object 2ef529faaaed03b2384b9f4d49a2ea95d7833894 (stored in .git/objects/2e/f529faaaed03b2384b9f4d49a2ea95d7833894) is corrupt
在进行任何操作之前,我通过将整个树形目录递归复制到另一个文件夹中进行了备份。 务必这样做。
然后我强制删除了损坏的对象:
jack@machine:~/git/cs$ rm .git/objects/2e/f529faaaed03b2384b9f4d49a2ea95d7833894
rm: remove write-protected regular empty file ‘.git/objects/2e/f529faaaed03b2384b9f4d49a2ea95d7833894’? y
接着我遇到了另一个问题,这个问题阻止了我运行任何 git 命令:
jack@machine:~/git/cs$ git status
fatal: bad object HEAD
有些人建议使用 checkout
命令将 HEAD
重置为一个好的状态,但我使用了 reset
命令。 checkout
命令会改变你的文件树状态和所在的分支,而 reset
命令(不带 --hard
参数)只会改变所在的分支,但不会改变任何文件。我想保留所有文件的当前状态,特别是因为它们是我最近提交的唯一记录(由于我所在的分支存在问题,无法被记录下来)。
我上次使用 Git 时,我在一个功能分支上,称其为 feature_foo
。由于这个分支指向已删除的提交 2ef529fa..
,所以我无法将其重置为 feature_foo
。因此,我将其重置为了 master
分支。我认为重置到哪里并不重要,任何其他分支都可以。
jack@machine:~/git/cs$ git reset master
Unstaged changes after reset:
M a/bunch/of_changes
M more/changes
error: Trying to write ref ORIG_HEAD with nonexistent object 2ef529faaaed03b2384b9f4d49a2ea95d7833894
error: Cannot update the ref 'ORIG_HEAD'.
现在我在 master
分支上,但是文件树和之前一样。由于 feature_foo
和 master
有很大不同,所以我看到了许多未暂存、未提交的更改。这是正常的。我认为有关 ORIG_HEAD
的消息只是意味着 Git 记录了我的先前位置,并且现在它不开心是因为我的先前位置是被删除的提交。
现在我有了一个正确的 HEAD
,我可以运行 git reflog
命令:
jack@machine:~/git/cs$ git reflog
61ac654 HEAD@{0}: commit: Commit message from a commit I did earlier
f9a1ce9 HEAD@{1}: commit: Another commit message
b26a6e9 HEAD@{2}: commit: Yet another commit message
我发现在我丢失工作之前在特性分支上做的所有提交都还在。当然,我认为只有最近的那个提交 2ef529fa..
被删除了。 我想重置到 HEAD@{1}
,以回到带有这些提交的分支。
jack@machine:~/git/cs$ git reset HEAD@{1}
fatal: Log .git/logs/HEAD is corrupt.
所以我打开这个文件来编辑它:
jack@machine:~/git/cs$ vim .git/logs/HEAD
这个文件看起来像一大堆SHA1和提交信息,直到最后几行:
b26a6e99762e703914dc3749fe136d99e100ac74 f9a1ce9c9eaa54b51aa29efdeafec023202de470 Jack <jack@example.com> 1434447540 +0100 commit: Another commit message
f9a1ce9c9eaa54b51aa29efdeafec023202de470 420ded21ffed88a2865cc0adaf3f54b0b44864e2 Jack <jack@example.com> 1434449503 +0100 commit: Commit message from a commit I did earlier
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@2ef529faaaed03b2384b9f4d49a2ea95d7833894 53b404d482a426046f2de6484b8855d1154b7fca Jack <jack@example.com> 1434465895 +0100 reset: moving to master
换句话说,在引用日志中,我看到了相同的有效提交记录,然后是一堆垃圾,最后是一个提交记录,显示我从无效的提交记录重置为master
(向右滚动以查看)。
我删除所有垃圾和它后面的内容,然后保存文件。现在我可以再次查看引用日志:
jack@machine:~/git/cs$ git reflog
61ac654 HEAD@{0}: commit: Commit message from a commit I did earlier
f9a1ce9 HEAD@{1}: commit: Another commit message
b26a6e9 HEAD@{2}: commit: Yet another commit message
现在,第一行缩写的SHA1,61ac654
,似乎指向master
。我查看下面的reflog并看到相同的SHA1出现在其他地方时,我正在master
上。此外,执行git reset HEAD@{0}
根本不起作用。但如果我执行:
jack@machine:~/git/cs$ git reset HEAD@{1}
Unstaged changes after reset:
M just/a/few_changes
未暂存的变更很少,这表明我的文件树现在非常接近我在Git中提交的内容。运行git log
确认我现在的历史记录中有我在reflog中看到的所有提交,除了第一个提交。因此,我只需要重新提交这个提交和那个调皮的提交2ef529f
,就可以恢复到原点了!
虽然这需要一些计算,但意味着我没有丢失自上次推送以来的十几个提交。
git reflog
实际上是查找.git/logs/HEAD
文件的。该文件中包含了我的提交哈希值,所以我可以回滚到出现问题之前的提交。如果git reflog
出现问题,请检查它尝试读取的文件:.git/logs/HEAD
。 - Steve Armstrong