Git "松散对象 ... 损坏" - 我丢失了哪些数据?

9

我在本地分支上进行了几次提交,然后尝试执行git fetch。但是遇到了以下错误:

fatal: loose object 7b36029a951eacd979d24e993e020c4d018ca265 (stored in .git/objects/7b/36029a951eacd979d24e993e020c4d018ca265) is corrupt
fatal: unpack-objects failed

运行git fsck命令只会显示与第一行相同的内容。任何实际提交或推送更改的命令似乎都会因为同样的原因失败。
该文件似乎只包含了许多零。我看到了如何解决GIT错误:对象文件为空?,其中有人通过删除损坏的文件来成功解决这个问题。
我的问题是:如果我逐个删除每个被认为是损坏的文件,我会失去什么?我真的会丢失任何提交吗?
请注意,我不确定我是如何处于这种状态的,尽管它发生在我的电脑蓝屏之后,所以可能是那造成了这个问题。
5个回答

17

我在我的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_foomaster 有很大不同,所以我看到了许多未暂存、未提交的更改。这是正常的。我认为有关 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 reflog 实际上是查找 .git/logs/HEAD 文件的。该文件中包含了我的提交哈希值,所以我可以回滚到出现问题之前的提交。如果 git reflog 出现问题,请检查它尝试读取的文件:.git/logs/HEAD - Steve Armstrong

5
这种情况通常是由于代码库中的 .git 文件夹中的某些文件损坏。最有可能的是,您可能没有丢失任何数据。文件损坏可能是由于意外断电造成的。如果是这种情况,则有可能会丢失数据。
在我遇到问题时,原因是停电。我只需在另一个位置克隆远程仓库复制整个 .git 文件夹到我的现有仓库即可解决问题,并且我的未提交/未推送的更改都没有丢失。
注: .git 目录是本地仓库根目录中的隐藏目录。您可能需要启用显示隐藏文件和文件夹选项,具体取决于您的操作系统。

这也解决了我的问题。谢谢。 - Ashish Shah

0

我遇到了类似的问题,按照@jwg的解决方案进行,但在git reflog步骤中无法继续。

git reflog 只产生如下输出:

1fd3a1d HEAD@{0}: reset: moving to develop

然而,结果是我的git不再损坏,而且在所有修改后我的代码仍然没有被触及。只是这些更改是未提交的,我所做的所有提交都消失了。

此时,我可以执行git-commit -a,失去所有提交,但仍保留最新的代码。

然后,我注意到一个分支temp被创建了。里面有一个稍后版本的代码,虽然不是最新的。如下所示:

当前分支 - feature/feature1(现在包含我的最新代码,但缺少大部分提交)

另一个分支 - temp(不包含最新的代码,但比当前分支有更多的提交)

因此,我通过git commit -a我的当前分支,然后git rebase temp,解决了一些冲突,完成了!最终,我仍然失去了一些提交,但至少代码是最新的。

编辑

结果发现我没有最新的可用代码,所以我只能保存一些提交记录。我使用备份恢复了文件。


0
在我们的情况下,远程 git 存储库中有一个损坏的文件 blob 大约 28mb。这个文件应该是大约 32mb 的,但不知何故变得损坏了。
解决方案是从进行提交的计算机上复制 blob 文件,并覆盖远程 git 存储库上的损坏文件。

-1

一个简单的解决方法是将旧仓库移动到其他地方并重新克隆:

$ cd ..
$ mv <repo-name> <repo-name>-original
$ git clone <url> <repo-name>

如果有文件丢失,可以从旧的存储库中检索它们,但首先尝试重新克隆。


这只是给了我 fatal: loose object 7b36029a951eacd979d24e993e020c4d018ca265 (stored in .git/objects/7b/36029a951eacd979d24e993e020c4d018ca265) is corrupt - stripybadger
我可以重新克隆它(实际上我已经这样做了),但是我仍然有旧工作副本中无法推送的提交。 “如果有丢失的内容,可以从旧存储库中检索它们” - 如何操作? - stripybadger
@stripybadger 这可能有点棘手,但这是官方答案:https://www.kernel.org/pub/software/scm/git/docs/user-manual.html#recovering-from-repository-corruption - Agis
谢谢,我已经以某种形式看到过了。它似乎依赖于 git ls-tree 命令告诉你哪个文件是损坏的。在我的情况下,git ls-tree 1df3846c... 给出了相同的 fatal: loose ... corrupt 错误信息。因此,我不确定如何将那里给出的说明应用到我的情况中。 - stripybadger
这不是一个“简单的修复”。这意味着除了您当前树中具有的特定版本之外,所有本地提交和分支,一切都会丢失。 - jwg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接