262

我不清楚以下是什么意思(来自Git 子模块更新文档):

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

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

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

  • 在这些子模块中,我可以像在常规存储库中一样创建分支/修改并使用推/拉,还是有什么需要注意的地方?
  • 我将如何将子模块引用的提交从说(标记)1.0 推进到 1.1(即使原始存储库的负责人已经在 2.0),或者选择使用哪个分支的提交?
4

4 回答 4

322

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

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


注意 2013 年 3 月:

正如“ git submodule tracking latest ”中提到的,现在一个子模块(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 --remotegit pull”。

MindTooth回答说明了手动更新(没有本地配置):

git submodule -q foreach git pull -q origin master

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

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


为避免此问题,请在子模块目录中使用 git checkout -b work 或类似的东西创建一个分支。当你第二次更新子模块时,它仍然会恢复你的工作,但至少你有一个指针可以返回。

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


所以,回答你的问题:

我可以像在常规回购中一样创建分支/修改并使用推/拉,还是有什么需要注意的地方?

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

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

我如何将子模块引用的提交从说(标记)1.0推进到1.1(即使原始回购的负责人已经在2.0)

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

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

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

这些一起对特定存储库的特定修订进行三角测量,该修订被检出到项目中的特定位置。


git 子模块页面

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

100% 正确:您不能修改子模块,只能参考其中一个提交。

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

  • 需要在子模块提交和推送(到上游模块),并且
  • 然后进入你的主项目,并重新提交(为了让那个主项目引用你刚刚创建和推送的新子模块提交)

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

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

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

但主要思想仍然存在:引用特定组件:

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

您在主项目中引用的特定提交列表定义了您的配置(这就是配置管理的全部内容,仅包含版本控制系统

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

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

于 2009-12-30T09:10:02.377 回答
140

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

git submodule -q foreach git pull -q origin master

您可以删除-q选项以跟踪整个过程。

于 2012-02-01T20:46:46.163 回答
19

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

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

git submodule update

您处于无 HEAD 状态,因此您此时所做的任何提交都很难恢复。因此,您已经开始在子模块 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。)

于 2014-01-25T00:50:07.067 回答
13

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

git submodule update --rebase --remote

将从每个子模块中的上游获取最新更改,重新设置它们,并检查子模块的最新版本。正如文档所说:

- 偏僻的

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

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

(这是从这个答案复制的。)

于 2017-12-15T16:46:03.303 回答