35

在我们的托管出现一些问题后,我们决定将我们的 Git 存储库移动到 GitHub。所以我克隆了存储库并尝试将其推送到 GitHub。但是,我偶然发现了一些我们以前从未遇到过的错误:

 C:\repositories\appName [master]> git push -u origin master
 Counting objects: 54483, done.
 Delta compression using up to 2 threads.
 Compressing objects: 100% (18430/18430), done.
 error: object 9eac1e639bbf890f4d1d52e04c32d72d5c29082e:contains duplicate file entries
 fatal: Error in object
 fatal: sha1 file '<stdout>' write error: Invalid arguments
 error: failed to push some refs to 'ssh://git@github.com/User/Project.git'

当我运行时fsck

C:\repositories\appName [master]> git fsck --full
Checking object directories: 100% (256/256), done.
error in tree 0db4b3eb0e0b9e3ee41842229cdc058f01cd9c32: contains duplicate file entries
error in tree 9eac1e639bbf890f4d1d52e04c32d72d5c29082e: contains duplicate file entries
error in tree 4ff6e424d9dd2e3a004d62c56f99e798ac27e7bf: contains duplicate file entries
Checking objects: 100% (54581/54581), done.

当我ls-tree使用糟糕的 SHA1 运行时:

C:\repositories\appName [master]> git ls-tree 9eac1e639bbf890f4d1d52e04c32d72d5c29082e
160000 commit 5de114491070a2ccc58ae8c8ac4bef61522e0667  MenuBundle
040000 tree 9965718812098a5680e74d3abbfa26f527d4e1fb    MenuBundle

我尝试了这个StackOverflow question上已经给出的所有答案,但没有任何成功。有什么办法可以防止这个存储库及其历史被毁灭吗?

4

3 回答 3

13

方法一。

做第git fsck一个。

$ git fsck --full
error in tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29: contains duplicate file entries

如果这不能解决问题,那你就有麻烦了。您可以忽略该问题,从备份中恢复存储库,或将文件移动到新存储库中。如果您在将 repo 推送到 github 时遇到问题,请尝试将存储库更改为其他存储库或检查:Can't push to GitHub error: pack-objects dead of signal 13 and Can't push new git repository to github


以下方法仅适用于高级 git 用户。请在开始前做好备份。以下步骤不能保证修复,它可能会使情况变得更糟,所以为了您自己的风险或教育目的而这样做。


方法2。

使用 git ls-tree 识别重复文件。

$ git read-tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29 # Just a hint.
$ git ls-tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29 # Try also with: --full-tree -rt -l
160000 commit def08273a99cc8d965a20a8946f02f8b247eaa66  commerce_coupon_per_user
100644 blob 89a5293b512e28ffbaac1d66dfa1428d5ae65ce0    commerce_coupon_per_user
100644 blob 2f527480ce0009dda7766647e36f5e71dc48213b    commerce_coupon_per_user
100644 blob dfdd2a0b740f8cd681a6e7aa0a65a0691d7e6059    commerce_coupon_per_user
100644 blob 45886c0eda2ef57f92f962670fad331e80658b16    commerce_coupon_per_user
100644 blob 9f81b5ca62ed86c1a2363a46e1e68da1c7b452ee    commerce_coupon_per_user

如您所见,它包含重复的文件条目(commerce_coupon_per_user)!

$ git show bb81a5af7e9203f36c3201f2736fca77ab7c8f29
tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29

commerce_coupon_per_user
commerce_coupon_per_user
commerce_coupon_per_user
commerce_coupon_per_user
commerce_coupon_per_user
commerce_coupon_per_user

同样,您可以看到重复的文件条目 (commerce_coupon_per_user)!

您可以尝试使用git show每个列出的 blob 并检查每个文件的内容。

然后在你的不同 git 克隆中继续为那个无效的 ls-tree 对象运行 ls-tree,看看你是否可以跟踪有效的对象,或者是否所有对象都损坏了。

git ls-tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29

If you found the valid object containing non-duplicated file entries, save it into the file and re-create by using `git mktree` and `git replace`, e.g.

remote$ git ls-tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29 > working_tree.txt
$ cat working_tree.txt | git mktree
NEWTREEbb81a5af7e9203f36c3201f2736fca77ab7c8f29
$ git replace bb81a5af7e9203f36c3201f2736fca77ab7c8f29 NEWTREE4b825dc642cb6eb9a060e54bf8d69288fbee4904

如果这没有帮助,您可以通过以下方式撤消更改:

$ git replace -d NEWTREE4b825dc642cb6eb9a060e54bf8d69288fbee4904

方法3。

当您知道哪个文件/目录条目重复时,您可以尝试删除该文件并稍后重新创建它。例如:

$ find . -name commerce_coupon_per_user # Find the duplicate entry.
$ git rm --cached `find . -name commerce_coupon_per_user` # Add -r for the dir.
$ git commit -m'Removing invalid git entry for now.' -a
$ git gc --aggressive --prune # Deletes loose objects! Please do the backup before just in case.

阅读更多:


方法4。

检查您的提交是否有无效条目。

让我们再次检查我们的树。

$ git ls-tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29 --full-tree -rt -l
160000 commit def08273a99cc8d965a20a8946f02f8b247eaa66  commerce_coupon_per_user
100644 blob 89a5293b512e28ffbaac1d66dfa1428d5ae65ce0     270    commerce_coupon_per_user
....
$ git show def08273a99cc8d965a20a8946f02f8b247eaa66
fatal: bad object def08273a99cc8d965a20a8946f02f8b247eaa66
$ git cat-file commit def08273a99cc8d965a20a8946f02f8b247eaa66
fatal: git cat-file def08273a99cc8d965a20a8946f02f8b247eaa66: bad file

似乎上面的提交是无效的,让我们使用以下命令之一扫描我们的 git 日志以检查发生了什么:

$ git log -C3 --patch | less +/def08273a99cc8d965a20a8946f02f8b247eaa66
$ git log -C3 --patch | grep -C10 def08273a99cc8d965a20a8946f02f8b247eaa66

commit 505446e02c68fe306aec5b0dc2ccb75b274c75a9
Date:   Thu Jul 3 16:06:25 2014 +0100

    Added dir.

new file mode 160000
index 0000000..def0827
--- /dev/null
+++ b/sandbox/commerce_coupon_per_user
@@ -0,0 +1 @@
+Subproject commit def08273a99cc8d965a20a8946f02f8b247eaa66

在这种特殊情况下,我们的提交指向了错误的对象,因为它是作为 git 子项目的一部分提交的,它不再存在(检查git submodule status)。

您可以通过以下方式从 ls-tree 中排除该无效对象并重新创建没有此坏对象的树:

$ git ls-tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29 | grep -v def08273a99cc8d965a20a8946f02f8b247eaa66 | git mktree
b964946faf34468cb2ee8e2f24794ae1da1ebe20

$ git replace bb81a5af7e9203f36c3201f2736fca77ab7c8f29 b964946faf34468cb2ee8e2f24794ae1da1ebe20

$ git ls-tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29 # Re-test.
$ git fsck -full

注意:旧对象仍应抛出重复的文件条目,但如果您现在在新树中重复,则需要从该树中删除更多内容。所以:

$ git replace # List replace objects.
bb81a5af7e9203f36c3201f2736fca77ab7c8f29
$ git replace -d bb81a5af7e9203f36c3201f2736fca77ab7c8f29 # Remove previously replaced object.

现在让我们尝试从该树中删除所有提交和 blob,并再次替换:

$ git ls-tree bb81a5af7e9203f36c3201f2736fca77ab7c8f29 | grep -ve commit -e blob | git mktree
4b825dc642cb6eb9a060e54bf8d69288fbee4904
$ git replace bb81a5af7e9203f36c3201f2736fca77ab7c8f29 4b825dc642cb6eb9a060e54bf8d69288fbee4904

现在你有那个无效条目的空树。

$ git status # Check if everything is fine.
$ git show 4b825dc642cb6eb9a060e54bf8d69288fbee4904 # Re-check
$ git ls-tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 --full-tree # Re-check

如果您对阶段有一些奇怪的更改,请通过以下方式重置您的存储库:

$ git reset HEAD --hard

如果您将遇到以下错误:

HEAD is now at 5a4ed8e Some message at bb81a5af7e9203f36c3201f2736fca77ab7c8f29

执行变基并删除该提交(通过更改pickedit):

$ git rebase -i
$ git commit -m'Fixed invalid commit.' -a
rebase in progress; onto 691f725
You are currently editing a commit while rebasing branch 'dev' on '691f725'.
$ git rebase --continue
$ git reset --hard
$ git reset HEAD --hard
$ git reset origin/master --hard

方法5。

尝试删除和压缩包含无效对象的无效提交。

$ git rebase -i HEAD~100 # 100 commits behind HEAD, increase if required.

阅读更多:Git 工具 - 重写历史以及如何在跳过特定提交时重新设置基准?


方法6。

通过以下方法识别无效的 git 对象进行手动删除:

  • 对于未压缩的对象(*请删除前两个字符,因为 git 使用它作为目录名称):

    $ find . -name 81a5af7e9203f36c3201f2736fca77ab7c8f29
    
  • 对于压缩对象

    $ find . -name \*.idx -exec cat {} \; | git show-index | grep bb81a5af7e9203f36c3201f2736fca77ab7c8f29
    # Then you need to find the file manually.
    $ git unpack-objects $FILE # Expand the particular file.
    $ git unpack-objects < .git/objects/pack/pack-*.pack # Expand all.
    

请参阅:如何解压缩 git 存储库的所有对象?


有关的:

于 2014-07-21T15:19:28.137 回答
4

注意:Git 2.1 将添加两个选项,git replace在修改 git repo 中的损坏条目时会很有用:

以交互方式编辑对象的内容。的现有内容<object>被漂亮地打印到一个临时文件中,在该文件上启动一个编辑器,并解析结果以创建一个与<object>.
然后创建一个替换 ref 以替换<object>新创建的对象。有关如何选择编辑器的详细信息,
请参阅。git-var

并由Jeff King ( )提交2deda62 :peff

replace: 添加--raw模式--edit

“”的目的之一git replace --edit是帮助用户修复畸形或损坏的对象。
通常我们用“”漂亮地打印树ls-tree,这比原始二进制数据更容易使用。

但是,某些形式的损坏会破坏 tree-walker,在这种情况下,我们的漂亮打印会失败,从而使“ --edit”对用户无用。

此补丁引入了“ --raw”选项,可让您在这些实例中编辑二进制数据。

知道 Jeff 如何用于调试 Git(就像在这种情况下),看到这个选项我并不感到惊讶。


请注意,在 Git 2.27(2020 年第二季度)之前,“ git fsck”确保记录在树对象中的路径已排序且没有重复,但它没有注意到 blob 后跟在具有相同名称的树之前排序的条目的情况。

这已得到纠正。

请参阅René Scharfe ( ) 的提交 9068cfb(2020 年 5 月 10 日(由Junio C Hamano 合并 -- --提交 0498840中,2020 年 5 月 14 日)rscharfe
gitster

fsck:报告树中不连续的重复名称

建议人:Brandon Williams
原测试人:Brandon Williams
签字人:René Scharfe
审核人:Luke Diamand

树条目按路径顺序排序,这意味着目录名称会隐式附加斜杠 ('/')。

Git fsck 检查树是否包含连续重复,但由于这种排序,如果其中一个是目录而另一个不是,则也可能存在非连续重复。

这样的树不能被完全检出。

通过在堆栈上记录候选文件名来查找这些重复项,并根据该堆栈检查候选目录名称以查找匹配项。


使用 Git 2.30(2021 年第一季度),处理最终创建相同包文件的重新打包操作的逻辑已得到简化。

请参阅Taylor Blau ( ) 的提交2fcb03b ( 2020 年 11 月 17 日)和提交 704c4a5(2020 年 11 月 16 日。 请参阅Jeff King ( ) 的提交 63f4d5c(2020 年 11 月 16 日(由Junio C Hamano 合并 -- --提交 39d38a5中,2020 年 12 月 3 日)ttaylorr
peff
gitster

builtin/repack.c:不要将现有的包移开

帮助者:Jeff King
签字者:Taylor Blau

当 ' git repack' ( man )创建一个与任何现有包同名的包时,它会将现有包移动到 ' old-pack-xxx.{pack,idx,...}',然后将新包重命名到位。

git repack最终,如果让 ' ' ( man )允许在关键时间(在新包被写入/移动到位之后,但在旧包被删除之前)写入多包索引,那就太好了。猜测这个选项可能被称为 ' --write-midx',这使得以下情况(重新打包在没有任何新对象的情况下背靠背发出)不可能:

$ git repack -adb
$ git repack -adb --write-midx  

在第二次重新打包中,现有包被逐字覆盖,使用相同的重命名为旧序列。此时,当前 MIDX 无效,因为它指的是现在丢失的包。因此,该代码希望在重新编写 MIDX 后运行。但是(在这个补丁之前)新的 MIDX 不能被写入,直到新的包被移动到位。所以,我们有一个循环依赖。

这都是假设的,因为目前没有代码可以在 ' ( man ) ' 期间安全地编写 MIDX (' ' 这样做是不安全的)。抛开假设不谈:为什么我们需要重命名现有的包以添加前缀“old-”?git repackGIT_TEST_MULTI_PACK_INDEX

这种行为可以追溯到2ad47d6(“ git-repack:更新与现有包相同的包时要小心。”,2006-06-25,Git v1.4.1 -- merge)。2ad47d6主要关注新编写的包的结构与其索引不同的情况。当包名称是一组对象的哈希时,这曾经是可能的。在此命名方案下,存储同一组对象的两个包可能在增量选择、对象定位或两者方面有所不同。如果发生这种情况,那么在复制新包和新索引之间的瞬间,任何此类包都将不可读(即,索引或包将是陈旧的,具体取决于它们被复制的顺序)。

但是自从1190a1a (" pack-objects: name pack files after trail hash", 2013-12-05, Git v1.9-rc0 -- merge ) 以来,这不再可能,因为包文件不是以其逻辑内容命名的(即,对象集),但通过它们内容的实际校验和。
因此,这种old-行为可以安全地进行,这使我们能够避免上面的循环依赖。

除了避免循环依赖之外,这个补丁还让 ' git repack' ( man )变得更加简单,因为我们不必处理在将现有包重命名为前缀为 ' old-' 时遇到的故障。

此补丁主要限于删除处理“旧”前缀的代码路径,但在其自己的文件名中包含包名称的文件除外,如.idx.bitmap和相关文件。例外是我们希望继续相信 pack-objects 写的内容。也就是说,我们并不是假装 pack-objects 没有写入与已经存在的文件相同的文件,而是我们尊重 pack-objects 所写的内容作为事实的来源。这削减了两种方式:

  • 如果 pack-objects 生成的包与已经存在的带有位图的包相同,但没有生成位图,我们将删除已经存在的位图。(此行为已编入 t7700.14)。
  • 如果 pack-objects 生成了与已经存在的包相同的包,我们信任对应.idx.promisor、 和其他文件的刚刚编写的版本,而不是已经存在的文件。这确保了我们使用这些文件的最新版本,即使在文件格式发生变化时也是安全的.idx(这不会反映在.idx文件名中)。

在重新使用现有文件重建多包索引文件时,我们过去常常盲目地信任现有文件,最终将损坏的数据携带到更新的文件中,这已在 Git 2.33(2021 年第三季度)中得到纠正。

请参阅Taylor Blau ( ) 的提交 f89ecf7提交 ec1e28e提交 15316a4提交 f9221e2(2021 年 6 月 23 日(由Junio C Hamano 合并 -- --提交 3b57e72中,2021 年 7 月 16 日)ttaylorr
gitster

midx:在“验证”期间报告校验和不匹配

建议人:Derrick Stolee
签名人:Taylor Blau

' git multi-pack-index verify' ( man )通过检查记录的对象偏移是否正确等来检查现有 MIDX 中的数据是否正确。

但它不会检查文件的尾随校验和是否与它记录的数据匹配。
因此,如果在最后几个字节中发生了磁盘损坏(并且所有其他数据都被正确记录),我们将:

  • 从 ' ' 得到一个干净的结果git multi-pack-index verify,但是
  • 在编写新的 MIDX 时无法重用现有的 MIDX(因为我们现在在重用 MIDX 之前检查校验和不匹配)

通过调用' verify' 子命令来识别校验和中的损坏midx_checksum_valid()


在 Git 2.34(2021 年第四季度)中,“ git repackman已被教导生成多包可达性位图。

请参阅Jeff King ( ) 的提交 e861b09(2021 年 10 月 6 日。 请参阅提交 324efc9(2021 年 10 月 1 日)和提交 6d08b9d提交 1d89d88提交 5f18e31提交 a169166提交 90f838b提交 08944d1提交 6fb22ca提交 56d863e(2021 年 9 月 28 日)由Taylor Blau((由Junio C Hamano 合并 -- --0b69bb0 提交中,2021 年 10 月 18 日)peff
ttaylorr
gitster

builtin/repack.c: 支持在重新打包时编写 MIDX

签字人:Taylor Blau

为希望在重新打包时将多包索引保留在其存储库中的调用者教( man )一个新选项。git repack--write-midx

这个新标志有两个现有的替代方案,但它们不涵盖我们的特定用例。
这些替代方案是:

  • 运行 ' ' 后调用 ' git multi-pack-index write' ( man )git repack,或
  • GIT_TEST_MULTI_PACK_INDEX=1运行“ ”时在您的环境中设置“ git repack”。

前者有效,但在重新打包和编写新 MIDX 之间引入了位图覆盖空白(因为重新打包可能已删除现有 MIDX 中包含的包,使其完全无效)。

引入一个新选项,通过教导在关键点生成 MIDX 来消除这种竞争git repack:在新包被写入并移动到位之后,但在删除冗余包之前。

此选项与git repack'--bitmap' 选项兼容(它将解释更改为:“在生成后写入与 MIDX 对应的位图”)。

MIDX 代码不处理此问题,因此请避免首先尝试生成覆盖零包的 MIDX。

git repack现在在其手册页中包含:

如果创建了多个包文件,则此选项无效,除非写入 MIDX(在这种情况下会创建多包位图)。

现在仍然git repack包含在其手册页中:

-m

--write-midx

git multi-pack-index编写一个包含非冗余包的多包索引(请参阅参考资料)。

于 2014-08-03T19:59:57.377 回答
2

我遇到的唯一解决方案是使用 git-replace 和 git-mktree。它不是世界上最简单的解决方案,但它确实有效。

查看此链接以获取参考指南。

git 树包含重复的文件条目

于 2014-04-07T18:31:02.520 回答