git子模块分支克隆

时间:2020-11-08 16:37:02

标签: git git-submodules

我负责做项目的一个分支,包括所有子模块中的相同分支 然后提交并推送所有子仓库和主仓库 我希望能够克隆所有分支

在我提交并一切正常之后-我尝试克隆。

对于某些重新处理,子模型不指向新分支 强迫我在子模块中切换/结帐

这是正常的behaiore吗? 我做错了什么/可以做得更好吗? 有没有办法通过分支切换来“克隆”所有子模块?

谢谢。 大卫。

1 个答案:

答案 0 :(得分:-1)

TL; DR

这是正常现象。您将需要编写或找到一个小的包装程序来处理它(或挂钩:请参见see philb's comment)。请继续阅读以获取详细说明。

子模块使用 commits ,而不是 branchs

这有点夸大其词,但这里的主要要点是它只是轻微。如果您从这个想法开始,那么其余的一切都是有道理的。

记住什么是子模块:

  • 首先,它一个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 checkoutgit 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,其中$hashrev-parse步骤的结果。 (当两个fetchswitch --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