如何解决主存储库的不同分支如何使用子模块的不同分支? TL; DR 长详细信息,或者何时{em> branch设置很重要?
假设我有一个带有某些子模块的主仓库y
,例如sub/x
。
还假设对于主模块和子模块存储库,master
是活动分支,并且主存储库的.gitmodules
文件指定branch = master
。
现在,假设除了主存储库master
的分支y
之外,还有主仓库yA
的分支master
x
分支,子模块仓库(xA
)有一个分支yA
。
我希望y
仓库的xA
分支“查看” /使用x
仓库的master
分支。
这意味着在主存储库中的yA
和master
分支之间进行切换将导致子模块中的xA
和yA
分支之间进行相应的切换。 / p>
git是否对此提供任何支持?
我尝试了以下操作:
- 切换到主存储库上的
xA
分支; - 切换到子模块存储库上的
master
分支; - 将
xA
替换为.gitmodules
作为主存储库master
文件中branch参数的值; - 在主存储库中,提交了(2)和(3)导致的所有更改。
这没有我希望的那样工作:如果我切换到主存储库上的master
分支,那么这对子模块存储库的活动分支设置(因此,{{1} }分支没有清除状态。
解决方法
git对[我想要的东西]有什么支持吗?
我认为排序。您需要确保.git/modules
(在超级项目中)没有获取设置。
TL; DR
在.gitmodules
中使用不同的设置(在超级项目提交中),并根据需要使用git submodule update --remote
。我尚未对此进行测试,但请参见详细说明。
长
我的总体一般建议:子模块的branch
设置通常是无用且无关紧要的。只是忽略它。不过,我们将稍作介绍 most 部分,然后看看是否可以使用它。
一个子模块被定义为一个Git存储库,其中一些 other Git有时会进入该子模块并运行一些Git命令。另一个Git被称为超级项目。
超级项目Git的主要操作是:
(cd $path && git checkout $hash)
在此序列中,任何分支名称都不会出现。这就是为什么 branch
设置不相关的原因。
$path
和$hash
部分来自超级项目Git的 index ,它们是从超级项目中的提交中提取出来的。该提交记录了子模块的路径以及原始哈希ID。这里也没有分支名称。
当您在超级项目中运行git checkout
或git switch
时,为了选择一些分支名称并因此选择某个特定的提交,超级项目Git将提取提交给其(超级项目)索引和工作的提交,该超级项目的树。这样会将正确的($ path,$ hash)对放入超级项目的索引中。
不幸的是,默认情况下它不调用$(cd $path && git checkout $hash)
部分来更新子模块。为此,您有几种选择:
- 运行
git submodule update
。该命令正是这样做的(嗯,默认情况下:请参阅下面的详细信息)。 - 运行
git checkout --recurse-submodules
(或与git switch
相同的标志)。此命令使git checkout
运行更新,并且并且传播到子模块Git中,以便在子模块运行git checkout
(或git switch
)时,如果子模块为另一个子模块的超级项目,该子模块将以其超级项目角色调用更新。对于所有嵌套的子模块,这将(递归)重复。 (我通常不使用它,但是我不必过多地处理递归子模块。由于递归,它非常强大。) - 将
submodule.recurse
设置为true。这将在多个命令(包括签出/切换)上启用--recurse-submodules
选项,在git fetch
和git pull
上也会启用。 (我不喜欢这个:我认为它太强大了。但是,您可以设置它,然后使用push.recurseSubmodules
设置显式禁用递归推送。)
详细信息,或者何时{em> branch
设置很重要?
The git submodule
documentation有几个很长且相当难以理解的段落来描述git submodule update
子命令。 (我认为这表明子模块的整体设置存在缺陷,但是我们必须使用已有的东西,至少要等到提出更好的东西为止。)让我在这里引用一下:
update [--init] [--remote] [-N|--no-fetch] [--[no-]recommend-shallow] [-f|--force] [--checkout|--rebase|--merge] [--reference <repository>] [--depth <depth>] [--recursive] [--jobs <n>] [--[no-]single-branch] [--] [<path>...]
通过克隆丢失的子模块,在子模块中获取丢失的提交并更新子模块的工作树,来更新已注册的子模块以符合超级项目的期望。根据命令行选项和submodule.<name>.update
配置变量的值,可以用几种方式完成“更新”。 ...
如您所见,有很多选择。为了使答案不再更长,我们只关注其中三个:--checkout
,--rebase
和--merge
。 没有选项还有两个,但您可以使用submodule.name.update
变量进行设置,在这里我们将忽略它们。这些选项---checkout
,--rebase
和--merge
-设置更新将使用哪种操作,与没有前导双连字符的选项名称相同。
checkout
模式是默认默认设置。也就是说,如果您没有设置显式的submodule.name.update
设置,而未指定--rebase
或--merge
,则会得到checkout
。这就是每个人都使用的-主要是!因此,这就是答案顶部的总体建议中的通常字。
现在,进入三种模式。我将再次引用该文档,随后进行一些小的格式更改和评论:
checkout
记录在超级项目中的提交将在独立的HEAD的子模块中检出。
rebase
子模块的当前分支将重新基于超级项目中记录的提交。
merge
超级项目中记录的提交将合并到子模块中的当前分支中。
因此,在默认模式下,任何分支名称都不会在任何地方输入图片。实际上,只有rebase
和merge
模式使用分支名称。所以现在我们要问一个问题:哪个分支名称?
文档说明得很清楚:子模块中的当前分支。那不是子模块的branch =
设置 ;这是子模块中的当前分支 。
但是子模块中当前有哪个分支?如果愿意,您可以查找:
(cd $path && git rev-parse --abbrev-ref HEAD)
会告诉您,对于每条经过的路径,当前是哪个分支。如果子模块正在使用分离头模式,则它将打印HEAD
,如果您运行过git submodule update --checkout
或使用git submodule update
模式的任何checkout
,则它将打印。 >
如果要预测当前分支,或者子模块是否位于分离的HEAD上,并因此位于任何分支上,那么您将如何预测?好吧,您运行过git submodule update
吗?除非您进行了递归模式签出,否则您必须首先进行git submodule update --init
,在这种情况下,Git会为您执行git submodule update --init --checkout
。因此,您的子模块很可能处于HEAD分离模式,因此没有当前分支。
换句话说,我们仍在海上。我们如何首先将子模块Git放在分支上?
有一个简单而显而易见的方法:我们可以自己做(cd $path; git checkout $branch)
,我们自己提供$path
和$branch
。这样,无论提交什么内容,子模块都在我们想要的分支上。但是由于我们提供了$branch
,所以我们不需要设置。我们只是这样做:
(cd path/to/submodule; git checkout feature/foo)
直接。所以也不是。
如果我们向下滚动到文档中的 OPTIONS 部分,然后再向下滚动到--remote
选项,我们最终找到一个实际使用设置的地方:
--remote
此选项仅对update命令有效。不用使用超级项目的记录的SHA-1来更新子模块,而是使用子模块的远程跟踪分支的状态。使用的遥控器是分支机构的遥控器(branch.<name>.remote
),默认为origin
。使用的远程分支默认为远程HEAD
,但是可以通过在submodule.<name>.branch
或.gitmodules
中设置.git/config
选项(使用.git/config
来覆盖分支名称。优先)。这适用于任何受支持的更新过程(
--checkout
,--rebase
等)。唯一的变化是目标SHA-1的来源。例如,submodule update --remote --merge
将上游子模块更改合并到子模块中,而submodule update --merge
将超级项目gitlink更改合并到子模块中。
严重的是,这段文本确实很难阅读-但是它表示git submodule update --remote
不仅会使用超级项目中的原始SHA-1哈希ID。相反,它将使用从其他位置获取的原始SHA-1哈希ID。恰好在其他地方?
为了确保当前跟踪分支状态,
update --remote
在计算SHA-1之前先获取子模块的远程存储库。如果您不想提取,则应使用submodule update --remote --no-fetch
。
因此:当您在--remote
命令中使用git submodule update
时,超级项目将:
- 步骤1:
(cd $path; git fetch)
,除非您添加--no-fetch
- 第2步:
(cd $path; git rev-parse $(complicated))
获取哈希ID。
$(complicated)
部分很复杂,但是它从branch =
或branch = master
的{{1}}设置中获取分支名称,例如.gitmodules
。它将其转换为远程跟踪名称,例如.git/config
,即第一步已更新。另请参见VonC's answer至How can I specify a branch/tag when adding a Git submodule?。
特殊名称origin/master
表示在超级项目中使用分支名称,但是:
我希望
.
仓库的yA
分支“查看” /使用y
仓库的xA
分支。
除非拼写完全匹配,否则无法使用x
技巧来实现。并且,如果子模块的分支名称已复制到超级项目的.
中,则它将保持设置为设置的任何值,如果没有,则超级项目Git将从{{ 1}}文件。
如果分支名称.git/config
中记录的在主存储库中提交的branch =
文件提交.gitmodules
表示.gitmodules
,那么在您运行{{1} }(有或没有$SHA_YA
),假设子模块yA
的{{1}}作为其远程对象,超级项目Git应该在branch = xA
上进行git submodule update --remote
。这将成为超级项目--no-fetch
运行git rev-parse
时超级项目origin/xA
传递给子模块x
的原始哈希ID的来源。
当您切换到其他提交时,请注意分支名称与此处无关。重要的是 commit哈希ID ,以及属于该提交的origin
文件-在超级项目中,超级项目中的y
文件可以具有其他一些{{ 1}}设置。您的x
命令将找到 that 设置,并让子模块Git执行不同的y
,以便在超级项目告诉子模块什么时,将哈希ID传递给子模块Git。签出。
这非常复杂,有很多活动部件。这些零件都必须在正确的时间排好队。超级项目最终实际上实际上只是使用原始哈希ID。仅使用正确的原始哈希ID会减少头痛的事情。一旦提交,便无法更改,通常这是正确的选择,因此您只需在提交之前确保它们是正确的即可。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。