我负责做项目的一个分支,包括所有子模块中的相同分支 然后提交并推送所有子仓库和主仓库 我希望能够克隆所有分支
在我提交并一切正常之后-我尝试克隆。
对于某些重新处理,子模型不指向新分支 强迫我在子模块中切换/结帐
这是正常的behaiore吗? 我做错了什么/可以做得更好吗? 有没有办法通过分支切换来“克隆”所有子模块?
谢谢。 大卫。
答案 0 :(得分:-1)
这是正常现象。您将需要编写或找到一个小的包装程序来处理它(或挂钩:请参见see philb's comment)。请继续阅读以获取详细说明。
这有点夸大其词,但这里的主要要点是它只是轻微。如果您从这个想法开始,那么其余的一切都是有道理的。
记住什么是子模块:
首先,它是一个Git存储库,因此它包含提交。它具有分支和标记名称,可以帮助Git 找到这些提交,但实际上,所有这些都与提交有关。
第二个-这是使它成为子模块而不是普通存储库的部分-它由其他一些Git存储库控制,它们被Git称为 superproject < / em>此子模块。
了解所有有关子模块的关键是超级项目列出了 raw commit hash 。超级项目Git将运行:
git -C path/to/submodule checkout $hash
或等效地:
git -C path/to/submodule switch --detach $hash
每当您负责超级项目时,请在超级项目中签出一些 。此命令的$hash
值由超级项目Git存储库中的 index 确定,并且索引哈希值最初是来自当前提交的哈希值。因此,是当前提交控制超级项目强制子模块使用的哈希ID。
现在,这也太有说服力了:超级项目不会总是 进入子模块并运行分离的HEAD switch / checkout命令。它在某些特定时间这样做。准确地何时进入子模块并执行此操作,将我们带入所有污点细节。但这是子模块的工作方式。例如,使用git submodule add -b
选项声明子模块的分支,不会更改此行为。
您显然希望看到的是:
git -C path/to/submodule checkout $branch
或:
git -C path/to/submodule switch $branch
其中$branch
设置为来自某个地方的分支名称。确实没有发生确切的情况,但是有几种方法可以实现。
在每个子模块中实际运行git checkout
的是git submodule update
命令。 (请参见下面的边栏。)如果您咨询the documentation,将会发现git submodule update
有很多选择。没有使git submodule update
运行您想要的命令的选项组合。
如果您已为该签出设置了递归选项,则Git将在超级项目中的git checkout
上进行自动子模块更新(再次参见边栏)。也就是说,git checkout --recurse-submodules
会执行此操作,如果在配置中将git checkout
设置为submodule.recurse
,则普通true
也会执行此操作。明确的git checkout --no-recurse-submodules
将禁止Git的自动更新。
(请注意,git clone
本身通过运行内部git checkout
命令结束,除非您使用git clone --no-checkout
。结帐将基于--recurse-submodules
递归或不递归标志传递给git clone
,或再次传递给您已设置的配置。)
如果您没有git checkout
执行自动更新,通常现在就可以手动运行您自己的 git submodule update
命令。您可以选择 not 来运行git submodule update
,也可以运行它,但后面跟着一个:
git -C path/to/submodule switch $branch
命令。当然,这都不是自动的。
git checkout
不使用git submodule update
用于更新子模块的面向用户命令为git submodule update
。但是git checkout
本身并不实际运行git submodule update
:相反,它已将git submodule update --checkout
的行为硬编码到其中。至少目前在entry.c
中,它注意到gitlink哈希ID发生了更改并调用了submodule_move_head
in submodule.c
,后者当前使用git read-tree
的调用,并向其传递了原始哈希ID。因此它无法使用所需的命令。
git submodule update
您可以选择子模块更新模式:
根据命令可以通过几种方式完成“更新” 行选项和
submodule.<name>.update
配置的值 变量。命令行选项优先于配置 变量。如果都未给出,则执行checkout
。 ...
checkout
记录在超级项目中的提交将是 已签出...
这是默认设置,也就是您所看到的。
rebase
子模块的当前分支将 重新基于超级项目中记录的提交。
这更接近于您想要的东西,但根本不是您想要的东西,另外还有一个引导问题:子模块的当前分支到底是什么?
merge
超级项目中记录的提交将 被合并到子模块中的当前分支中。
那也不是您想要的,并且有相同的问题需要解决。
以下
update
过程仅通过submodule.<name>.update
配置变量可用:自定义命令
只需一个命令的任意shell命令 参数(在超级项目中记录的提交的sha1)为 被执行。当submodule.<name>.update
设置为!command 时, 感叹号后的其余部分是自定义命令。
这是可以进行所需操作的唯一更新命令之一。您可以设置一个自定义命令,以执行所需的git checkout
或git switch
。请注意,没有为您提供分支名称。您将不得不从某个地方钓出它。我认为这太困难了;继续阅读或跳至结论部分,以了解我认为正确的方法。
none
子模块未更新
(出于完整性考虑,我将其包括在内,但显然并未按照您的意愿进行。)
但是,等等:还有另一种选择!由于某种原因,这里没有描述。它留在选项部分中。这是--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更改合并到子模块中。为了确保当前跟踪分支状态,
update --remote
在计算SHA-1之前先获取子模块的远程存储库。 如果您不想获取,则应使用submodule update --remote --no-fetch
。
这几乎实现了您想要的一切。有两个问题:
它先运行git -C path/to/submodule fetch
,然后运行git -C path/to/submodule rev-parse origin/$branch
,然后运行git -C path/to/submodule switch --detach $hash
,其中$hash
是rev-parse
步骤的结果。 (当两个fetch
和switch --detach
足够时,它使用三个命令来实现这一点有点傻:开关可以在内部进行rev-parse。但这是这种方式的副作用它被编码。)
这里的缺点是额外的fetch
和使用origin/$branch
。您可以使用--no-fetch
停止抓取,尽管这可能是无害的,而且有时是可取的。使用origin/$branch
也可能是无害的:如果您刚刚克隆了子模块存储库,则$branch
甚至可能不存在,并且结果总是很明显。 HEAD,因为子模块始终是分离的HEAD,所以origin/$branch
应该没问题。
更大的缺点是,您实际上 不能首先通过手动运行--remote
来获得git submodule update
的指定。而且,无论如何,您仍然仍然留在子模块中的独立HEAD上!
是否可以通过分支切换“克隆”所有子模块?
不。但是,如果您愿意使用Git以外的其他东西(例如,使用运行 Git的包装器),这很容易。对于您的情况而言,运行git clone
,然后依次运行git submodule update --init
,然后运行git submodule foreach --recursive
和一个带有git switch
命令的shell函数(由您提供)的简单包装程序可以完成此工作。 (我认为)使用git submodule foreach
优于尝试使用自定义更新命令,因为您可以轻松访问超级项目和子模块部分,以便可以从超级项目的设置中提取分支名称。>
如果足够的话,还可以使用git clone
后跟git submodule update --checkout --remote
来获得正确的哈希ID 。