Git子模块head '引用不是树'错误

我有一个指向无效提交的子模块的项目:子模块提交仍然是本地的,当我试图从另一个repo获取它时,我得到:

$ git submodule update
fatal: reference is not a tree: 2d7cfbd09fc96c04c4c41148d44ed7778add6b43
Unable to checkout '2d7cfbd09fc96c04c4c41148d44ed7778add6b43' in submodule path 'mysubmodule'

我知道子模块HEAD应该是什么,是否有任何方法可以在本地更改它,而不从已经提交2d7cfbd09fc96c04c4c41148d44ed7778add6b43的回购中推?

我不确定我是否说清楚了…我找到了。

277926 次浏览

无论如何,子模块的历史记录都安全地保存在子模块git中。

那么,为什么不直接删除子模块并重新添加它呢?

否则,你是否尝试手动编辑子模块.git中的HEADrefs/master/head

假设子模块的存储库确实包含您想要使用的提交(与从超级项目的当前状态引用的提交不同),有两种方法可以做到这一点。

第一种方法要求您已经知道要使用的子模块的提交。它通过直接调整子模块,然后更新超级项目,从“内向外”工作。第二种方法从“由外而内”的方式工作,找到修改子模块的超级项目提交,然后重置超级项目的索引以引用不同的子模块提交。

内,

如果你已经知道你想让子模块使用哪个提交,cd到子模块,检查出你想要的提交,然后git addgit commit返回到超级项目中。

例子:

$ git submodule update
fatal: reference is not a tree: e47c0a16d5909d8cb3db47c81896b8b885ae1556
Unable to checkout 'e47c0a16d5909d8cb3db47c81896b8b885ae1556' in submodule path 'sub'

哎呀,有人做了一个超级项目提交,它引用了子模块sub中未发布的提交。以某种方式,我们已经知道希望子模块位于commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c。直接去那里看看。

子模块中的签出

$ cd sub
$ git checkout 5d5a3ee314476701a20f2c6ec4a53f88d651df6c
Note: moving to '5d5a3ee314476701a20f2c6ec4a53f88d651df6c' which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
git checkout -b <new_branch_name>
HEAD is now at 5d5a3ee... quux
$ cd ..

由于我们正在签出一个提交,这将在子模块中产生一个分离的HEAD。如果你想确保子模块正在使用一个分支,那么使用git checkout -b newbranch <commit>来创建并在提交时签出一个分支,或者签出你想要的分支(例如,在提示处有所需的提交)。

更新超级项目

子模块中的签出反映在超级项目中,作为对工作树的更改。因此,我们需要对超级项目的索引进行更改,并验证结果。

$ git add sub

检查结果

$ git submodule update
$ git diff
$ git diff --cached
diff --git c/sub i/sub
index e47c0a1..5d5a3ee 160000
--- c/sub
+++ i/sub
@@ -1 +1 @@
-Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

子模块更新是静默的,因为子模块已经处于指定的提交状态。第一个差异表明索引和工作树是相同的。第三个差异表明,唯一的阶段性变化是将sub子模块移动到不同的提交。

提交

git commit

这将提交已修复的子模块条目。


外,在

如果您不确定应该从子模块中使用哪个提交,您可以查看超级项目中的历史记录来指导您。您还可以直接从超级项目管理重置。

$ git submodule update
fatal: reference is not a tree: e47c0a16d5909d8cb3db47c81896b8b885ae1556
Unable to checkout 'e47c0a16d5909d8cb3db47c81896b8b885ae1556' in submodule path 'sub'

这与上面的情况相同。但这一次,我们将专注于从超级项目中修复它,而不是将其浸入子模块中。

找到超级项目的错误提交

$ git log --oneline -p -- sub
ce5d37c local change in sub
diff --git a/sub b/sub
index 5d5a3ee..e47c0a1 160000
--- a/sub
+++ b/sub
@@ -1 +1 @@
-Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c
+Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
bca4663 added sub
diff --git a/sub b/sub
new file mode 160000
index 0000000..5d5a3ee
--- /dev/null
+++ b/sub
@@ -0,0 +1 @@
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

好吧,看起来它在ce5d37c中坏了,所以我们将从它的父模块(ce5d37c~)中恢复子模块。

或者,你也可以从补丁文本(5d5a3ee314476701a20f2c6ec4a53f88d651df6c)中获取子模块的提交,并使用上面的“inside, out”过程。

签入超级项目

$ git checkout ce5d37c~ -- sub

这将把sub的子模块条目重置为在超级项目中提交ce5d37c~时的状态。

更新子模块

$ git submodule update
Submodule path 'sub': checked out '5d5a3ee314476701a20f2c6ec4a53f88d651df6c'

子模块更新OK(它表示一个分离的HEAD)。

检查结果

$ git diff ce5d37c~ -- sub
$ git diff
$ git diff --cached
diff --git c/sub i/sub
index e47c0a1..5d5a3ee 160000
--- c/sub
+++ i/sub
@@ -1 +1 @@
-Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

第一个差异表明sub现在在ce5d37c~中是相同的。第二个差异表明索引和工作树是相同的。第三个差异显示唯一的阶段性变化是将sub子模块移动到不同的提交。

提交

git commit

这将提交已修复的子模块条目。

试试这个:

git submodule sync
git submodule update

此错误可能意味着子模块中缺少提交。也就是说,存储库(A)有一个子模块(B)。A想要加载B,以便它指向某个提交(在B中)。如果该提交以某种方式丢失了,你会得到这个错误。一个可能的原因:提交的引用被推入了A,但实际的提交并没有从b推入,所以我从那里开始。

不太可能的情况是,存在权限问题,无法提交(如果使用git+ssh,可能会出现这种情况)。

确保子模块在.git/config和.gitmodules中的路径看起来正常。

最后要尝试的一件事-在子模块目录:git reset HEAD -hard

当你有一个子模块指向一个重新基于的存储库,并且给定的提交“消失”时,也可能发生这种情况。虽然提交可能仍然在远程存储库中,但它不在分支中。如果你不能创建一个新的分支(例如,不是你的存储库),你就不得不更新超级项目来指向一个新的提交。或者,您可以将子模块的一个副本推到其他地方,然后更新超级项目以指向该存储库。

为了确保安全,请尝试更新您的git二进制文件。

GitHub for Windows有版本git version 1.8.4.msysgit.0,在我的情况下是问题。更新解决了这个问题。

可能的原因

这可能发生在以下情况:

  1. 子模块已编辑到位
  2. 子模块被提交,哪个更新了被指向的子模块的散列
  3. 子模块(s) 不推

例如:发生了这样的事情:

$ cd submodule
$ emacs my_source_file  # edit some file(s)
$ git commit -am "Making some changes but will forget to push!"

此时应该已经推送了子模块。

$ cd .. # back to parent repository
$ git commit -am "updates to parent repository"
$ git push origin master

因此,远程用户不可能找到丢失的提交,因为它们仍然在本地磁盘上。

解决方案

通知修改子模块以推送的人,即。

$ cd submodule
$ git push

当我这样做时,我得到了这个错误:

$ git submodule update --init --depth 1

但是父项目中的提交指向了之前的提交。

删除子模块文件夹并运行:

$ git submodule update --init

并没有解决问题。我删除了回购,并再次尝试没有深度标志,它工作了。

此错误发生在Ubuntu 16.04 git 2.7.4中,但不会发生在Ubuntu 18.04 git 2.17中。

@pavan kumar在评论中写道:

我只是增加了深度计数以包括旧的提交,它起作用了。

在我看来,上面的答案都不能解决问题,尽管它们是很好的答案。所以我发布了我的解决方案(在我的例子中有两个git客户端,客户端A和B):

  1. 到子模块的目录:

    cd sub
    
  2. checkout to master:

    git checkout master
    
  3. rebase to a commit code which both client can see

  4. go back to the parent's dir:

  5. commit to master

  6. change to the other client, do rebase again.

  7. finally it works fine now! Maybe lose a couple of commits but it works.

  8. FYI, don't try to remove your submodule, it will remain .git/modules there and cannot readd this submodule again, unless reactive local one.

为了同步git repo与子模块的头部,如果这是你真正想要的,我发现删除子模块,然后读取它可以避免修补历史。不幸的是,删除子模块需要黑客攻击,而不是一个git命令,但这是可行的。

https://gist.github.com/kyleturner/1563153的启发,我删除子模块的步骤如下:

  1. 运行git rm——cached
  2. 从.gitmodules文件中删除相关行。
  3. 从.git/config中删除相关的部分。
  4. 删除现在未跟踪的子模块文件。
  5. 删除目录。git/modules/

同样,如果您只想再次指向子模块的头部,并且不需要保持子模块的本地副本而使事情变得复杂,那么这可能很有用。它假设您拥有子模块“正确”作为它自己的repo,无论它的起源在哪里,您只是想返回到适当地将它作为子模块包含。

注意:在进行这些操作或任何git命令之前,除了简单的提交或推送之外,一定要对项目进行完整的复制。我建议所有其他的答案,以及作为一个通用的git指南。

这个答案适用于终端git经验有限的SourceTree用户。

从Git项目(超级项目)中打开有问题的子模块。

获取并确保“获取所有标签”被选中。

重新建立你的Git项目。

这将解决“参考不是树”的问题十有八九。第一次不会,这是一个最终的解决方案,正如上面的答案所描述的那样。

只是偶然发现了这个问题,这些解决方案都不适合我。我的问题的解决方案实际上要简单得多:升级Git。我的版本是1.7.1,在我升级到2.16.1(最新版本)后,这个问题就消失得无影无踪了!我就把它留在这里了,希望能帮到别人。

你的分支可能不是最新的,一个简单的解决方案,但尝试git fetch