【问题标题】:Tree contains duplicate file entries树包含重复的文件条目
【发布时间】:2012-10-22 03:18:00
【问题描述】:

在我们的托管出现一些问题后,我们决定将 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.

当我使用错误的 SHA1 运行 ls-tree 时:

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

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

【问题讨论】:

  • 您可能已经尝试过这些,但stackoverflow.com/q/10931954/6309 中的建议看起来很有希望。
  • 这看起来像你搞砸了你的子模块设置。什么不适用于链接的主题?创建新的树对象来替换损坏的对象应该是解决方案。
  • 我在 Windows 中处理存储库时看到了类似的情况。在 windows 中 File.txt 和 file.txt 是同一个文件。你的历史上有过这样的事情吗?
  • 如果没有更多信息,就无法提供进一步的帮助。具体回答上述cmets中的问题。
  • 这个问题可以重复吗stackoverflow.com/questions/10931954/…

标签: git object duplicates


【解决方案1】:

方法一。

先执行git fsck

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

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


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


方法二。

使用 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)!

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

然后在不同的 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

方法三。

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

$ 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.

阅读更多:


方法四。

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

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

$ 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

执行变基并删除该提交(通过将 pick 更改为 edit):

$ 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 Tools - Rewriting HistoryHow do I rebase while skipping a particular commit?


方法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.
    

见:How to unpack all objects of a git repository?


相关:

【讨论】:

    【解决方案2】:

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

    以交互方式编辑对象的内容。 &lt;object&gt; 的现有内容被漂亮地打印到一个临时文件中,在该文件上启动一个编辑器,并解析结果以创建一个与 &lt;object&gt; 相同类型的新对象。
    然后创建一个替换 ref 以将 &lt;object&gt; 替换为新创建的对象。
    See git-var 了解有关如何选择编辑器的详细信息。

    commit 2deda62Jeff King (peff):

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

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

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

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

    知道如何使用 Jeff 调试 Git(如 in this case),看到这个选项我并不感到惊讶。


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

    这已得到纠正。

    参见René Scharfe (rscharfe)commit 9068cfb(2020 年 5 月 10 日)。
    (由 Junio C Hamano -- gitster -- 合并到 commit 0498840,2020 年 5 月 14 日)

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

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

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

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

    这样的树不能完全检出。

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


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

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

    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 不能被写入,直到新的包被移动到位。所以,我们有一个循环依赖。

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

    这种行为可以追溯到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) 之后,这不再可能,因为pack files不是根据其逻辑内容(即对象集)命名,而是根据其内容的实际校验和命名。
    所以,这个old- 行为可以安全地进行,这样我们就可以避免上面的循环依赖。

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

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

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

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

    参见Taylor Blau (ttaylorr)commit f89ecf7commit ec1e28ecommit 15316a4commit f9221e2(2021 年 6 月 23 日)。
    (由 Junio C Hamano -- gitster -- 合并于 commit 3b57e72,2021 年 7 月 16 日)

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

    推荐人:Derrick Stolee
    签名人:Taylor Blau

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

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

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

    通过调用midx_checksum_valid(),教“verify”子命令识别校验和中的损坏。


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

    commit e861b09(2021 年 10 月 6 日)Jeff King (peff)
    请参阅commit 324efc9(2021 年 10 月 1 日)和 commit 6d08b9dcommit 1d89d88commit 5f18e31commit a169166commit 90f838bcommit 08944d1commit 08944d1commit 6fb22cacommit 56d863e(2020 年 9 月 28 日 37 月 28 日) @.
    (由 Junio C Hamano -- gitster -- 合并于 commit 0b69bb0,2021 年 10 月 18 日)

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

    签字人:Taylor Blau

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

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

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

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

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

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

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

    git repack 现在包含在其man page 中:

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

    git repack 现在仍包含在其man page 中:

    -m

    --write-midx

    编写多包索引(请参阅git multi-pack-index) 包含非冗余包。

    【讨论】:

      【解决方案3】:

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

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

      git tree contains duplicate file entries

      【讨论】:

        猜你喜欢
        • 2013-04-20
        • 2015-08-09
        • 1970-01-01
        • 2019-04-22
        • 1970-01-01
        • 1970-01-01
        • 2020-03-19
        • 1970-01-01
        • 2013-02-21
        相关资源
        最近更新 更多