首页 > 解决方案 > git子模块分支克隆

问题描述

我想做一个项目的分支,包括所有子模块中的相同分支然后在提交并推送所有子存储库和主存储库之后,我希望能够克隆新的分支以供永远使用

在我提交并且一切看起来都很好之后 - 我尝试克隆。

对于某些原因,子模型不指向新分支迫使我在子模块中进行检查/结帐

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

谢谢你。大卫。

标签: gitgit-submodules

解决方案


TL;博士

这个是正常的。您需要编写或找到一个小包装器来处理它(或钩子:请参阅 philb 的评论)。请继续阅读详细说明。

子模块使用提交,而不是分支

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

记住什么是子模块:

  • 首先,它一个 Git 存储库,因此它由提交组成。它具有帮助 Git找到这些提交的分支和标签名称,但它实际上都是关于提交的。

  • 其次——这是使它成为子模块的部分,而不仅仅是一个普通的存储库——它其他一些 Git 存储库控制,Git 称之为这个子模块的超级项目。

理解关于子模块的一切的关键是超级项目列出了一个原始的提交哈希。超级项目 Git 将运行:

git -C path/to/submodule checkout $hash

或等效地:

git -C path/to/submodule switch --detach $hash

每当您负责超级项目时,请检查超级项目中的一些提交。此命令的$hash值由超级项目 Git 存储库中的索引确定,索引哈希值最初是来自当前提交的值。因此,控制超级项目对子模块施加的哈希 ID 的是当前提交。

现在,这也太强了:超级项目并不总是进入子模块并运行 detached-HEAD 开关/结帐命令。它在某些特定的时间这样做。正是它进入子模块并这样做时,我们才能了解所有血腥的细节。但这是子模块的工作方式。git submodule add -b例如,使用选项声明子模块的分支不会改变这种行为。

显然,您希望看到的是:

git -C path/to/submodule checkout $branch

或者:

git -C path/to/submodule switch $branch

where$branch设置为来自某个地方的分支名称。没有确切发生这种情况的内置案例,但有几种方法可以实现。

那么你什么时候子模块中获得分支名称行为?

实际上git checkout在每个子模块内部运行的是一个git submodule update命令。(请参阅下面的侧边栏。)如果您查阅文档,您会看到git submodule update有大量的选项。没有选项组合可以git submodule update运行您想要的命令。

如果您为该结帐设置了递归选项, Git 将git checkout在超级项目中执行自动子模块更新(再次参见侧边栏) 。也就是说,会这样做,如果您已在配置中设置为,则将这样做。显式将禁止 Git 的自动更新。git checkout --recurse-submodulesgit checkoutsubmodule.recursetruegit checkout --no-recurse-submodules

(请注意,除非您使用 ,否则它git clone本身会以运行内部git checkout命令结束。结帐将根据传递给 的标志或您设置的配置git clone --no-checkout递归或不递归。)--recurse-submodulesgit clone

如果您没有git checkout进行自动更新,您通常会现在手动运行自己的 git submodule update命令。您可以选择运行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。这是,至少目前, in entry.c,它注意到 gitlink 哈希 ID 已更改并调用submodule_move_headinsubmodule.c,它当前使用 的调用git read-tree,并传递给它一个原始哈希 ID。所以它不能使用所需的命令。

使用git submodule update

您可以选择子模块更新模式:

“更新”可以通过多种方式完成,具体取决于命令行选项和submodule.<name>.update配置变量的值。命令行选项优先于配置变量。如果两者都没有给出,checkout则执行 a。...

checkout
    超级项目中记录的提交将被签出...

这是默认设置,也是您所看到的。

rebase
    子模块的当前分支将重新基于超级项目中记录的提交。

这更接近您想要的,但根本不是您想要的,而且还有一个引导问题:子模块的当前分支到底是什么?

merge
    超级项目中记录的提交将被合并到子模块中的当前分支中。

这也不是您想要的,并且有同样的问题要解决。

以下update过程仅可通过submodule.<name>.update配置变量获得:

自定义命令
    执行带有单个参数的任意 shell 命令(超级项目中记录的提交的 sha1)。当submodule.<name>.update设置为!command时,感叹号后的余数为自定义命令。

这是唯一可以执行您想要的更新命令之一。您可以设置一个自定义命令来执行您想要的git checkoutgit switch. 请注意,不会向您提供分支名称;你将不得不从某个地方把它捞出来。这太难了(我认为);继续阅读,或跳到结论部分,看看我认为正确的方法是什么。

none     子模块未更新

(为了完整起见,我将其包括在内,但显然不能满足您的要求。)

但是等等:还有一种选择!出于某种原因,这里没有描述;它留在选项部分。这是--remote选项:

--remote
    此选项仅对更新命令有效。不要使用超级项目记录的 SHA-1 来更新子模块,而是使用子模块的远程跟踪分支的状态。使用的遥控器是分支的遥控器 ( branch.<name>.remote),默认为origin. 使用的远程分支默认为 remote ,但可以通过在or中设置选项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的结果。(当两个和就足够了时,它使用三个命令来实现这一点有点愚蠢:开关可以在内部进行 rev-parse。但这是它编码方式的副作用。)$hashrev-parsefetchswitch --detach

    这里的缺点是额外的fetch和使用origin/$branch. 您可以使用 停止获取--no-fetch,尽管它可能是无害的,并且有时可能是可取的。的使用origin/$branch也可能是无害的:如果你刚刚克隆了子模块存储库,$branch可能甚至不存在,结果总是会是一个分离的 HEAD,因为子模块总是分离的 HEAD,所以origin/$branch应该没问题。

  • 更大的缺点是,如果不首先手动运行 a就无法指定。而且,无论如何,您仍然留在子模块中的一个分离的 HEAD 上!--remotegit submodule update

结论

有没有办法“克隆”所有切换分支的子模块?

不,但是,如果您愿意使用 Git 以外的其他东西——例如,使用运行Git 的包装器——这很容易。对于您的情况,一个简单的包装器运行git clone,然后运行一个调用命令的 shell 函数(您提供)git submodule update --init将完成这项工作。使用(我认为)优于尝试使用自定义更新命令,因为您可以轻松访问超级项目和子模块部分,以便您可以从超级项目的设置中找出分支名称。git submodule foreach --recursivegit switchgit submodule foreach

如果足够好,您还可以使用git clone其次git submodule update --checkout --remote来获得正确的哈希 ID 。


推荐阅读