Git子模块更新


242

我不清楚以下含义(来自Git子模块更新文档):

...将使子模块HEAD分离,除非指定--rebase--merge

如何--rebase/ --merge改变事物?

我的主要用例是拥有一堆中央存储库,我将通过子模块将其嵌入到其他存储库中。我希望能够直接在其原始位置或从其嵌入式存储库(通过子模块使用它们的存储库)内部对这些中央存储库进行改进。

  • 在这些子模块中,是否可以像在常规存储库中一样创建分支/修改并使用推/拉,还是需要谨慎?
  • 我如何将子模块引用的提交从(标记)1.0提升到1.1(即使原始存储库的头部已经是2.0),还是选择完全使用哪个分支的提交?

关于“分离头”的主题,另请参见stackoverflow.com/questions/964876/head-and-orighead-in-gitstackoverflow.com/questions/237408/…作为实际示例(与子模块无关,但仍)
VonC

“您不能在主项目中修改子模块的内容”:是,正确。我已经编辑了答案,以阐明这一明显的矛盾(不可修改的子模块,您仍然可以从主项目存储库中进行修改!)
VonC

Answers:


304

这个GitPro页面确实很好地总结了git子模块更新的结果

运行时git submodule update,它将签出项目的特定版本,但不在分支中。这被称为具有分离的头部-这意味着HEAD文件直接指向提交,而不是符号引用。
问题是您通常不希望在独立的头部环境中工作,因为它很容易丢失更改
如果执行初始子模块更新,则在不创建要使用的分支的情况下提交该子模块目录,然后在不提交的情况下再次从超级项目运行git子模块更新,而Git会在不通知您的情况下覆盖您的更改。从技术上讲,您不会丢失工作,但不会有指向它的分支,因此检索起来有些困难。


注意:2013年3月:

如“ git子模块跟踪最新 ”所述,现在的子模块(git1.8.2)可以跟踪分支。

# add submodule to track master branch
git submodule add -b master [URL to Git repo];

# update your submodule
git submodule update --remote 
# or (with rebase)
git submodule update --rebase --remote

参见“ git submodule update --remotevsgit pull ”。

MindTooth答案说明了手动更新(无本地配置):

git submodule -q foreach git pull -q origin master

在这两种情况下,这都会更改子模块引用(gitlink父回购索引中特殊条目),并且您将需要从主回购中添加,提交和推送所述引用。
下次克隆该父存储库时,它将填充子模块以反映那些新的SHA1引用。

该答案的其余部分详细介绍了经典的子模块功能(对固定提交的引用,这是子模块概念背后的全部内容)。


为避免此问题,请在使用git checkout -b work或类似功能的子模块目录中工作时创建一个分支。当您第二次更新子模块时,它仍将还原您的工作,但是至少您有一个指针可以返回。

切换其中具有子模块的分支也很棘手。如果您创建一个新分支,在其中添加一个子模块,然后切换回没有该子模块的分支,则您仍将子模块目录作为未跟踪目录:


因此,回答您的问题:

是否可以像在常规存储库中一样创建分支/修改并使用推/拉,还是需要谨慎?

您可以创建分支并推送修改。

警告(来自Git Submodule Tutorial):始终发布(推送)子模块更改,然后再发布(推送)更改到引用它的超级项目。如果您忘记发布子模块更改,则其他人将无法克隆存储库。

我如何将子模块引用的提交从(标记)1.0提升到1.1(即使原始存储库的头部已经是2.0)

了解子模块 ” 页面可以提供帮助

Git子模块使用两个移动部分来实现:

  • .gitmodules文件和
  • 一种特殊的树对象。

这些一起对特定存储库的特定修订版进行了三角划分,并检出到项目中的特定位置。


git子模块页面

您不能从主项目中修改子模块的内容

100%正确:您不能修改子模块,只能引用其提交之一。

这就是为什么当您从主项目中修改子模块时,您:

  • 需要子模块提交并推送(到上游模块),并且
  • 然后进入您的主项目,然后重新提交(以使该主项目引用您刚刚创建并推送的新子模块提交)

子模块使您能够进行基于 组件的方法开发,其中主项目仅引用其他组件的特定提交(此处称为“其他Git存储库声明为子模块”)。

子模块是另一个Git存储库的标记(提交),它不受主项目开发周期的约束:它(“其他” Git存储库)可以独立发展。
由主项目决定是否需要从其他仓库中选择任何提交。

但是,如果您出于方便目的直接从主项目中修改其中一个子模块,则Git允许您这样做,前提是您首先将这些子模块的修改发布到其原始Git存储库中,然后再提交与所述子模块的版本。

但是主要思想仍然是:引用特定的组件,这些组件:

  • 有自己的生命周期
  • 有自己的标签集
  • 有自己的发展

您在主项目中引用的特定提交列表定义了您的配置(这是Configuration Management的全部内容,仅包含Version Control System

如果某个组件确实可以与您的主项目同时开发(因为对主项目的任何修改都涉及修改子目录,反之亦然),那么它就不再是“子模块”,而是子树合并(也在将遗留代码库从CVS转移到分布式存储库中出现)中,将两个Git 存储库的历史链接在一起。

这是否有助于理解Git子模块的真实本质?


77
哇。这么长的解释,对于那些基本上很简单的事情,应该足以吓到任何新手坚持使用svn:externals。;-)
conny

2
@conny:但是,正如我在“ git子模块为什么与svn externals不兼容? ”中详细介绍的那样,子模块本质上是不同的,并且与不兼容svn:externals
VonC

1
抱歉,要回答我自己的问题,我将cd'ing收集到子模块中并git checkout一个sha,否则git pull / fetch会很好。然后在本地存储库中提交更新。
亨里克

2
@hced:您还可以使用git submodule foreach
Dav Clark

1
..仍然没有得到它。网上某处有更简单的解释吗?
尤金

135

要更新每个子模块,您可以调用以下命令(在存储库的根目录):

git submodule -q foreach git pull -q origin master

您可以删除-q选项以遵循整个过程。


15
如果您仅从git submodule update --init --recursive根目录运行,它将以递归方式获取所有文件,如果尚未进行初始化,则将其初始化。
Sam Soffes

10
@SamSoffes完全具有不同的目的。更新子模块将在它们当前指向的提交处签出子模块(不一定是最新的提交)。上面答案中的解决方案将每个子模块的提交更新为来自原点/主节点的最新HEAD。
indragie 2013年

7
我的新方法:git submodule update --rebase --remote
MindTooth

19

要解决--rebasevs. --merge选项:

假设您拥有超级存储库A和子模块B,并且想要在子模块B中做一些工作。您已经完成作业,并且知道在调用之后

git submodule update

您处于无头状态,因此此时您所做的任何提交都很难恢复。因此,您已经开始在子模块B中的新分支上进行工作。

cd B
git checkout -b bestIdeaForBEver
<do work>

同时,项目A中的其他人已经决定B的最新和最佳版本才是A真正应得的。您出于习惯将最新的更改合并下来并更新您的子模块。

<in A>
git merge develop
git submodule update

哦,不!您又回到了无头状态,可能是因为B现在指向与B的新提示或其他提交关联的SHA。如果您只有:

git merge develop
git submodule update --rebase

Fast-forwarded bestIdeaForBEver to b798edfdsf1191f8b140ea325685c4da19a9d437.
Submodule path 'B': rebased into 'b798ecsdf71191f8b140ea325685c4da19a9d437'

现在,针对B的最佳想法已经基于新的提交,更重要的是,您仍在B的开发分支上,而不是一头雾水!

(这--merge会将从beforeUpdateSHA到afterUpdateSHA的更改合并到您的工作分支中,而不是将您的更改重新部署到afterUpdateSHA上。)


7

Git 1.8.2具有一个新选项,--remote它将完全启用此行为。跑步

git submodule update --rebase --remote

将从上游每个子模块中获取最新更改,为它们重新设置基础,并检出该子模块的最新版本。正如文档所述

- 远程

此选项仅对update命令有效。不要使用超级项目记录的SHA-1更新子模块,而要使用子模块的远程跟踪分支的状态。

这等效于git pull在每个子模块中运行,这通常正是您想要的。

(这是从此答案复制而来的。)


如果您决定回答一个已经建立并正确答案的较早的问题,那么在当天晚些时候添加一个新答案可能不会给您任何功劳。如果您有一些与众不同的新信息,或者您确信其他答案都是错误的,则一定要添加一个新答案,但“又一个答案”通常会在问了很长时间后给出相同的基本信息,但通常不会。不能赚很多钱。没有任何解释,甚至没有指向外部文档的链接(这还不够)。
乔纳森·勒夫勒

2
这不是“还另一个答案”,因为没有其他答案具有此命令(请证明我是错误的)。其他答案对我不起作用,此评论有用,因此我决定将其发布为答案,同时感谢原始所有者。因此,请考虑删除您的弃权票。
Iulian Onofrei

有一个评论通过MindTooth从2015年开始说这是他们现在做什么。您没有对此做任何解释(尽管您确实提到了MindTooth,但是对您的含义没有真正的解释-嵌入URL(如此注释中的内容会有所帮助))。您没有说为什么这是个好主意。你没有任何警告。我认为这不是一个有用的答案,因为它提出的问题多于解决的问题。
乔纳森·莱夫勒

1
那样的话,我的意思是它起作用而不是不起作用。相信我,如果更多的人看到这个答案,他们会很高兴的,因为它有效。对于这样的事情,大多数人只想知道更新git子模块的命令,而不是如何实现的。
Iulian Onofrei

我编辑了答案以证明您错了,也是stackoverflow.com/questions/1979167/git-submodule-update / ...!!!
Iulian Onofrei
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.