删除具有空SHA1的条目,并查找相应的提交

时间:2017-07-09 23:51:53

标签: git

所以,我继承了一个删除了一个子模块的存储库,我得到了可怕的:

warning in tree 6eb01385fa82fdef80719ec4990bec2e0b591d47: nullSha1: contains entries pointing to null sha1

我尝试使用this answer解决此问题。但是,假设我知道哪个提交包含了该树,而我找不到它:

git log --pretty='%H %T' | grep 6eb01385fa82fdef80719ec4990bec2e0b591d47

...不会返回任何内容。此外,使用filter-branch命令作为上述问题的OP引发了关于提交的抱怨,但这是一个不同的树,并且使用ls-tree列出此树也显示一对空的SHA1条目。

所以,总结一下:

  • 我似乎至少有两个带有空SHA1条目的树
  • fsck发现了一个,但似乎没有附加到任何提交
  • 另一个是附加到提交但fsck
  • 没有看到

也许我可以使用前面提到的答案修复属于提交的那个,但孤儿树怎么样?

编辑:

感谢此处的所有建议。在快速机器上的tmpfs上拥有repo的副本可以轻松地测试所有这些。最终找出问题的一部分:

  • 提交filter-branch抱怨(e884a3b0)包含树e057f815a
  • 树e057f815a只包含一棵树:6eb01385f
  • 树6eb01385f是具有两个空{1}}抱怨的空SHA-1的树

现在我想知道如何应用官方答案,因为麻烦的树不是提交的直接子。据我所知,我应该修理/更换 6eb01385f并重新插入e057f815a,然后重新生成e057f815a以将其插入提交e884a3b0。那就是:

  • fsck修复底层树
  • git ls-tree {badtree} | sed -e '/0\{40\}/d' | git mktree使父树指向它
  • 在提交中替换那个,如其他答案所示

好的,所以努力尝试:

git ls-tree {parenttree} | sed -e 's/badtree/fixedtree/' | git mktree

到目前为止一切顺利。但旧的承诺和树木仍在那里。如果我想用以下方法删除它们:

# Create new tree by removing empty SHA1s
git ls-tree 6eb01385fa82fdef80719ec4990bec2e0b591d47 | sed -e '/0\{40\}/d' | git mktree
0eabc1625026f92b2737e763a087f7c4000f0084

# Create new parent tree by replacing bad tree by fixed tree in parent tree
git ls-tree e057f815aec33a48981921289fc7ab25e9ea1a16 | sed -e 's/6eb01385fa82fdef80719ec4990bec2e0b591d47/0eabc1625026f92b2737e763a087f7c4000f0084/' | git mktree
df56fe08e90f1a30e6467ac2bba50a3d771c9de4

# Create new commit by replacing old parent tree by new parent tree
git cat-file commit e884a3b0040b3940d259cd72d82be20d5eb8d7c3 | sed 's/e057f815aec33a48981921289fc7ab25e9ea1a16/df56fe08e90f1a30e6467ac2bba50a3d771c9de4/' | git hash-object -t commit -w --stdin
b41674793c985ba63bc68b095024ebcb2fbf0370

# Replace old commit by new commit
git replace e884a3b0040b3940d259cd72d82be20d5eb8d7c3 b41674793c985ba63bc68b095024ebcb2fbf0370

它抱怨我应该使用“-r”,所以我使用:

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch Somedir1 Somedir2' --prune-empty --tag-name-filter cat -- --all

运行...但是子模块已经被同一位置具有相同名称的目录所取代,因此上面的内容也会丢失很多有用的文件。并且git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch Somedir1 Somedir2' --prune-empty --tag-name-filter cat -- --all 仍然找到了坏树,此外它还发现了许多“悬挂标签”。有没有办法只删除两个坏树和提交?

1 个答案:

答案 0 :(得分:1)

蛮力可能会成功:

which-commits-use-tree () 
{ 
    local REPLY;
    git rev-list --all --reflog | while read; do
            git ls-tree -dr $REPLY | grep -q $1 && echo $REPLY uses $1;
    done
}

作为中等回收的一次性可以容忍,它在我的小系统上用大约五分钟的时间扫描了整个git的历史。如果你有更大的东西,你需要耐心或更重的职责。

git cat-file --batch-check='%(objectname) %(objecttype)' --batch-all-objects --buffer \
| awk '/commit|tree/{print $1}' | git cat-file --batch | your-scanner-here

是我能想到的最快的方式来转储批量扫描的整个历史结构,这在git历史上耗时6秒;在linux repo上花了大约2m30,这是相当令人鼓舞的。不过,我可能不打算为此编写扫描仪。