协慌网

登录 贡献 社区

添加 Git 子模块时如何指定分支 / 标签?

git submodule add -b如何工作?

在添加具有特定分支的子模块后,新的克隆存储库(在git submodule update --init )将处于特定提交,而不是分支本身( git status显示 “当前不在任何分支上”)。

我在.gitmodules.git/config上找不到有关子模块的分支或任何特定提交的任何信息,那么 Git 如何解决呢?

另外,是否可以指定标签而不是分支?

我正在使用 1.6.5.2 版。

答案

注意:Git 1.8.2 增加了跟踪分支的可能性。请参阅下面的一些答案。


习惯这个有点困惑,但是子模块不在分支上。就像您说的那样,它们只是指向子模块存储库的特定提交的指针。

这意味着,当其他人签出您的存储库或提取您的代码并进行 git 子模块更新时,该子模块将签出到该特定提交。

这对于不经常更改的子模块非常有用,因为这样项目中的每个人都可以在同一提交中拥有该子模块。

如果要将子模块移动到特定标签:

cd submodule_directory
git checkout v1.0
cd ..
git add submodule_directory
git commit -m "moved submodule to v1.0"
git push

然后,另一个想要将 submodule_directory 更改为该标签的开发人员执行此操作

git pull
git submodule update --init

git pull提交其子模块目录指向的更改。 git submodule update实际上合并在新代码中。

我想在这里添加一个答案,它实际上只是其他答案的集合,但我认为它可能会更完整。

当您拥有这两件事时,您就知道您拥有一个 Git 子模块。

  1. 您的.gitmodules条目如下所示:

    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
  2. 您的 Git 存储库中有一个子模块对象(在本示例中为 SubmoduleTestRepo)。 GitHub 将它们显示为 “子模块” 对象。或者从命令行git submodule status Git 子模块对象是特殊类型的 Git 对象,它们保存特定提交的 SHA 信息。

    每当您执行git submodule update ,它将使用提交中的内容填充您的子模块。 .gitmodules中的信息,它知道在哪里可以找到提交。

    现在,所有-b所做的就是在.gitmodules文件中添加一行。因此,按照相同的示例,它看起来像这样:

    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
        branch = master

    注意: .gitmodules文件中仅支持分支名称,但不支持 SHA 和 TAG! git add . ” 来跟踪和更新每个模块的分支提交,例如,像git add ./SubmoduleTestRepo ,并且您不必每次都.gitmodules

    子模块对象仍指向特定的提交。 -b选项可以为您提供的唯一功能就是根据 Vogella 的回答在您的更新中--remote

    git submodule update --remote

    与其将子模块的内容填充到该子模块所指向的提交,而不是用主分支上的最新提交替换该提交,然后使用该提交填充子模块。这可以通过 djacobs7 答案分两步完成。由于现在已经更新了子模块对象指向的提交,因此必须将更改后的子模块对象提交到 Git 存储库中。

    git submodule add -b并不是使分支保持最新状态的一种神奇方法。 .gitmodules文件中添加有关分支的信息,并为您提供了在填充子模块对象之前将其更新为指定分支的最新提交的选项。

(Git 2.22,Q2 2019,引入了git submodule set-branch --branch aBranch -- <submodule_path>

请注意,如果您有一个尚未跟踪分支的现有子模块,那么(如果您有 git 1.8.2+ ):

  • 确保父仓库知道其子模块现在跟踪分支:

    cd /path/to/your/parent/repo
      git config -f .gitmodules submodule.<path>.branch <branch>
  • 确保您的子模块实际上是该分支的最新版本:

    cd path/to/your/submodule
      git checkout -b branch --track origin/branch
        # if the master branch already exist:
        git branch -u origin/master master

(“origin” 是从其克隆了子模块的上游远程回购的名称。
该子模块中的git remote -v通常是 “起源”)

  • 不要忘记在父仓库中记录子模块的新状态:

    cd /path/to/your/parent/repo
      git add path/to/your/submodule
      git commit -m "Make submodule tracking a branch"
  • 该子模块的后续更新将必须使用--remote选项:

    # update your submodule
      # --remote will also fetch and ensure that
      # the latest commit from the branch is used
      git submodule update --remote
    
      # to avoid fetching use
      git submodule update --remote --no-fetch

请注意,在Git 2.10+ (2016 年第三季度)中,您可以使用 ' . ' 作为分支名称:

该分支的名称被记录为submodule.<name>.branch.gitmodules用于update --remote
的特殊值.用来表示子模块中分支的名称应该与当前存储库中的当前分支相同

但是, 正如LubosD 所说

使用git checkout ,如果要遵循的分支名称是 “ . ”,它将杀死您的未提交工作!
请改用git switch

这意味着 Git 2.23(2019 年 8 月)或更高版本。

请参阅 “ git checkout感到困惑


如果要在分支后更新所有子模块,请执行以下操作:

git submodule update --recursive --remote

请注意,对于每个更新的子模块,结果几乎总是一个独立的 HEAD ,如Dan Cameron其答案中所述

Clintm 在注释中指出,如果您运行git submodule update --remote ,并且生成的 sha1 与该子模块当前所在的分支相同,它将不会执行任何操作,并且将子模块仍保留在该分支上,并且不在头顶状态。)

为了确保实际检出分支(不会修改代表父仓库的子模块的特殊条目的 SHA1),他建议:

git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; git switch $branch'

每个子模块仍将引用相同的 SHA1,但是如果您进行新的提交,则可以推送它们,因为它们将由您希望子模块跟踪的分支引用。
在子模块中执行该推送之后,不要忘记返回到父存储库,为那些修改后的子模块添加,提交并推送新的 SHA1。

请注意Alexander Pogrebnyak 的注释中推荐$toplevel的使用。
$toplevel于 2010 年 5 月在 git1.7.2 中引入: commit f030c96

它包含顶级目录的绝对路径( .gitmodules在其中)。

dtmland 在评论中添加:

foreach 脚本将无法检出不在分支之后的子模块。
但是,此命令可同时为您提供:

git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git switch $branch' –

相同的命令,但更易于阅读:

git submodule foreach -q --recursive \
    'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; \
     [ "$branch" = "" ] && \
     git checkout master || git switch $branch' –

umläute 在注释中使用简化版本改进了dtmland的命令:

git submodule foreach -q --recursive 'git switch $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'

多行:

git submodule foreach -q --recursive \
  'git switch \
  $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'

在 Git 2.26(Q1 2020)之前,被告知要递归地获取子模块中的更新的获取不可避免地会产生大量的输出,并且变得很难发现错误消息。

该命令已被教导枚举在操作结束时有错误的子模块

参见Emily Shaffer( nasamuffin 提交的 0222540 (2020 年 1 月 16 日)。
(由Junio C gitster -gitster - 在commit b5c71cc 中合并,2020 年 2 月 5 日)

fetch :强调子模块提取期间的失败

签名人:艾米莉 · 谢弗(Emily Shaffer)

fetch-by-oid上,则唯一失败的子模块提取错误将隐藏在其他子模块的活动下。
尽早排除故障,以便用户知道出了什么问题以及在哪里

因为fetch_finish()仅由run_processes_parallel,所以不需要在submodules_with_errors周围进行静音。


请注意,在 Git 2.28(2020 年第三季度)中,继续重写脚本化的 “git 子模块” Porcelain 命令的各个部分;这次git submodule set-branch ” 子命令了。

参见Shourya Shukla( periperidip )的提交 2964d6e (2020 年 6 月 2 日)。
(由Junio C gitster -gitster - 在commit 1046282 中合并,2020 年 6 月 25 日)

submodule :从 shell 到 C 的端口子命令 “set-branch”

指导:克里斯蒂安 · 库德(Christian Couder)
指导:卡尔蒂克 · 西瓦拉姆
帮助:刘登敦
帮助:埃里克 · 阳光
帮助:ĐoànTrầnCôngDanh
签名人:Shourya Shukla

将子模块子命令'set-branch' 转换为内置命令,并通过git submodule.sh调用它。