Git子模块头“引用不是树”错误


305

我有一个项目,该项目的子模块指向无效的提交:该子模块的提交保持在本地状态,当我尝试从另一个存储库中获取它时,我得到了:

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

我知道子模块HEAD应该是什么,有什么办法可以在本地更改此子模块,而无需从确实提交的仓库中推送2d7cfbd09fc96c04c4c41148d44ed7778add6b43

我不确定是否很清楚... 这是我发现的类似情况


11
引用子模块时,“致命:引用不是树”似乎通常表示父级回购所期望的子模块提交尚未被推送或以其他某种方式被破坏。对我们来说,这个令人困惑的错误消息是通过仅推送一个有人忘记推送的子模块而解决的。
克里斯·莫斯基尼

1
@ChrisMoschini-我刚刚遇到了那个问题,那是我的“解决方案”,我推了拉主仓库。但是我忘了将最后一次提交推到子模块的仓库中。谢谢!
Rotem

也许您忘了推送最新的子模块提交了
-Hafenkranich

Answers:


378

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

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

反了

如果你已经知道其呈交你想要的子模块来使用,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)中获取子模块的提交,并改用上述“由内而外”的过程。

超级项目中的结帐

$ git checkout ce5d37c~ -- sub

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

更新子模块

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

子模块更新正常(指示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

这将提交固定的子模块条目。


在“外部,内部”方法中,您能否阐明“为什么ce5d37c看起来像坏了”?那个手指犯了什么罪?
加勒特·奥尔布赖特

5
@Garrett:假设是e47c0a一个本地存储库中不存在的提交sub,但是超级项目sub指向该提交。之所以可能发生这种情况,是因为有人e47c0a在其副本中创建了其他人sub,更新了他们的超级项目以指向该提交,并在不推e47c0a送到的中央/共享存储库的情况下推送了超级项目sub。当我们从中央/共享超级项目拉动,我们得到新的commit点sube47c0a,但我们不能“看到”承诺。ce5d37c之所以怀疑是因为,它基于diff引入了e47c0a
克里斯·约翰森

仍然很模糊,将sub其作为子模块的父存储库中保存的特定哈希值在哪里,以及是否可以sub不依赖于父文件的较早状态而直接将其直接操作为当前的HEAD?回购,可能并不总是有帮助。
matanster,2015年


187

尝试这个:

git submodule sync
git submodule update

2
不幸的是,对于我来说,并非如此,我们的一个子模块被主git存储库以add命令作为目标,现在无法撤消该子模块
Daniel

9
也为我工作。很想知道为什么。
BenBtg 2012年

12
原来,在git submodule sync给定子模块的远程URL已更改的情况下,必须执行a 。在我们的案例中,我们从公共仓库添加了子模块,然后将URL更改为私有fork-并加入了这个特定的pickle。
Samscam

例如:我建立了一个repo(A),并带有一个指向我的github repo(B)的子模块。我在存储库A中创建了一个分支,因为我想将B指向其他人的github存储库。经过一些努力并提交了分支,我将我的仓库A换回了主仓库,仓库B遇到了这个问题。@ Lonre Wang的解决方案将其修复。
fbicknel 2014年

2
假设没有人真正搞砸(在这种情况下,您将需要Chris Johnsen出色的答案)Lonre Wang的答案将解决此问题,...除非您的子模块具有自己的子模块(问题出在子模块内部)。在那种情况下,您需要进入包含有问题子模块的子模块,然后执行上述命令。请注意,update具有--recursive选项(git子模块update --recursive),但是sync没有。您确实必须在具有问题的sub(sub)module的子模块中手动运行“ git submodule sync”。这是我的问题;)。
卡洛·伍德

16

此错误可能意味着子模块中缺少提交。即,存储库(A)具有子模块(B)。A想要加载B,以便它指向某个提交(在B中)。如果该提交丢失了,您将得到该错误。可能的原因:对提交的引用已在A中推送,但实际的提交并未从B中推送。因此,我将从此处开始。

不太可能出现权限问题,并且无法撤消提交(如果使用git + ssh,则可能)。

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

最后一件事-在子模块目录中:git reset HEAD --hard


3
我已经在问题中解释了……问题本身就是如何解决它。大约两年前,它已经被成功地解决了……许可与此无关。
Mauricio Scheffer

1
你说了,你当然没有解释。
Daniel Tsadok

我的意思是,此答案不会添加任何有价值的信息,因此我将其删除。
Mauricio Scheffer

4
“ git reset HEAD --hard”也对我有帮助...没有其他工作。我也尝试过以前的解决方案,没有骰子。谢谢!
维吉尔

1
每个线程都是自己的在线小世界。您所说的代表您-您不能期望人们会学习您的个人历史,并试图在允许您尊重的背景下撰写您的评论。善待他人,尊重他人,您无需让别人了解您的个人怪癖。如果您能像中外人一样在中立的背景下阅读您的评论,您将理解我的批评。
2013年

10

可能的原因

在以下情况下可能会发生这种情况:

  1. 子模块已被编辑
  2. 提交的子模块将更新所指向的子模块的哈希
  3. 子模块未推送

例如发生了这样的事情:

$ 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

6

我这样做时遇到了这个错误:

$ git submodule update --init --depth 1

但是父项目中的提交指向了较早的提交。

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

$ git submodule update --init

没有解决问题。我删除了回购协议,然后在没有深度标记的情况下再次尝试,它正常工作。

此错误在Ubuntu 16.04 git 2.7.4中发生,但在Ubuntu 18.04 git 2.17中不发生,TODO查找确切的修复提交或版本。


从那以后,我的团队就以我们的代码方式放弃了子模块,麻烦太多了
柏拉图

1
您有什么选择?
nuzzolilo '16

@nuzzolilo我们添加username/repo#sha到package.json中,一个更加灵活的选项是使用一组Docker容器来组织您的系统
Plato

3
真烦人 --depth=1当我不需要回购历史记录时,可以节省很多带宽。如果有人找到或知道为什么会这样,我很想知道。
i336_

@ i336_尽管我无法解释为什么,但是我写了一个cmake帮助器来帮助缓解此问题:github.com/LMMS/lmms/blob/…。它使用一种deinit可在大多数时间解决问题的方法。与构建系统捆绑在一起时,最终用户可以让构建系统获取子模块并recursive完全抛弃损坏的命令。在某些情况下,这种中断可能会中断,例如子模块已经强制执行并彻底清除了提交。
tresf

5

当您有一个子模块指向已重新建立基础的存储库,并且给定的提交为“ gone”时,也会发生这种情况。尽管提交可能仍在远程存储库中,但它不在分支中。如果您不能创建一个新分支(例如,不是您的存储库),那么您就不得不更新超级项目以指向一个新的提交。或者,您可以将子模块的副本之一推送到其他位置,然后更新超级项目以指向该存储库。


5

您的分支机构可能不是最新的,这是一个简单的解决方案,但是请尝试 git fetch


2

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

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

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

重新整理您的Git项目。

这将解决十分之九的“引用不是树”问题。如最佳答案所描述的,那一次不会是终端修复。


1

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

那么,为什么不删除子模块然后再次添加呢?

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


1
这将行不通,因为在某处有对2d7cfbd09fc96c04c4c41148d44ed7778add6b43的引用,该引用仅在其他地方的本地仓库中,但未发布
Mauricio Scheffer 2010年

1

只是要确保,请尝试更新您的git二进制文件。

适用于Windows的GitHub具有该版本git version 1.8.4.msysgit.0,在我看来,这就是问题所在。更新解决了它。


1

就我而言,以上答案都不能解决问题,即使它们是好的答案也是如此。因此,我发布了解决方案(在我的情况下,有两个git客户端,客户端A和B):

  1. 转到子模块的目录:

    cd sub
    
  2. 结帐给主人:

    git checkout master
    
  3. 重新设置为两个客户端都可以看到的提交代码

  4. 回到父母的目录:

  5. 致力于掌握

  6. 转到另一个客户,再做rebase一次。

  7. 终于可以了!也许会丢失几次提交,但它确实有效。

  8. 仅供参考,请勿尝试删除您的子模块,它会保留.git/modules在那里并且无法再次读取此子模块,除非是反应性的本地子模块。


1

要将git repo与子模块的头部同步,以防万一,这确实是您想要的,但我发现删除子模块然后读取它可以避免修改历史记录。不幸的是,删除子模块需要黑客攻击,而不是一个git命令,但是可行。

我遵循受https://gist.github.com/kyleturner/1563153启发而删除子模块的步骤:

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

同样,如果您只想再次指向子模块的头部,并且不需要通过保持子模块的本地副本完整而没有使事情复杂化,这将很有用。假设您将子模块“ right”作为其自己的存储库,无论它的来源是什么,并且您只想恢复到将其作为子模块正确包含在内。

注意:在进行这些类型的操作或除简单的commit或push之外的任何git命令之前,请始终对项目进行完整复制。我建议将其与所有其他答案以及通用git指南一起使用。


1

只是偶然发现了这个问题,这些解决方案都不适合我。事实证明,解决我的问题的方法实际上要简单得多:升级Git。我的版本是1.7.1,当我将其升级到2.16.1(最新版本)后,问题消失了!猜猜我要把它留在这里了,希望对您有所帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.