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

How can I specify a branch/tag when adding a Git submodule?

git submodule add -b是如何工作的?

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

我找不到关于该子模块分支或任何特定提交的.gitmodules.git/config的任何信息,那么Git是如何计算出来的?

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

我使用的是1.6.5.2版。


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

习惯这个有点让人困惑,但是子模块不在分支上。正如您所说,它们只是指向子模块存储库特定提交的一个指针。

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

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

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

1
2
3
4
5
6
cd submodule_directory
git checkout v1.0
cd ..
git add submodule_directory
git commit -m"moved submodule to v1.0"
git push

然后,另一个想要将子模块目录更改为该标记的开发人员,是这样做的吗?

1
2
git pull
git submodule update

git pull更改,将其子模块目录提交到。git submodule update实际上合并了新代码。


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

当你有这两样东西的时候,你知道你有一个子模块。

  • 你的.gitmodules有这样一个条目:

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

    每当您执行EDOCX1[1]时,它将用提交中的内容填充子模块。由于.gitmodules中的信息,它知道在哪里找到提交。

    现在,-b所做的就是在.gitmodules文件中添加一行。所以按照同样的例子,它看起来是这样的:

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

    编辑:上面只支持分支名称,不支持sha或tag。

    子模块对象仍然指向特定的提交。-b选项为您购买的唯一功能是根据Vogella的回答在更新中添加--remote标志:

    1
    git submodule update --remote

    它没有将子模块的内容填充到子模块指向的提交中,而是将该提交替换为主分支上的最新提交,然后用该提交填充子模块。Djacobs7答案可以分两步完成。由于您现在已经更新了子模块对象指向的提交,所以您必须将更改后的子模块对象提交到Git存储库中。

    git submodule add -b并不是一种神奇的方法,可以让所有的东西都与一个分支保持同步。它只是在.gitmodules文件中添加有关分支的信息,并提供在填充子模块对象之前将其更新为指定分支的最新提交的选项。


  • (2019年第2季度2.22吉特引进了git submodule set-branch --branch aBranch -- )

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

    • 确保父回购协议知道其子模块现在跟踪分支:

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

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

    &(a)(其中"origin"是克隆子模块的上游远程repo的名称。&(a)该子模块内的git remote -v将显示它。通常,它是"起源")。

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

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

      1
      2
      3
      4
      5
      6
      7
      # 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年第3季度),您可以使用'.作为分支名称:

    The name of the branch is recorded as submodule..branch in .gitmodules for update --remote.
    A special value of . is used to indicate that the name of the branch in the submodule should be the same name as the current branch in the current repository.

    如果要更新分支后的所有子模块:

    1
        git submodule update --recursive --remote

    请注意,对于每个更新的子模块,结果几乎总是一个独立的头,正如丹·卡梅伦在他的回答中指出的那样。

    (clintm在评论中指出,如果运行git submodule update --remote并生成的sha1与子模块当前所在的分支相同,它将不会做任何事情,并且子模块仍然"在该分支上",而不是处于分离的头状态。)

    为了确保分支机构被实际签出(这不会修改代表父回购子模块的特殊条目的sha1),他建议:

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

    每个子模块仍然引用同一个sha1,但是如果您进行了新的提交,您将能够推送它们,因为它们将被您希望子模块跟踪的分支引用。在子模块内进行推送之后,不要忘记返回到父repo,为那些修改过的子模块添加、提交和推送新的sha1。

    注意Alexander Pogrebnyak在评论中建议使用$toplevel。2010年5月,在Git1.7.2中引入了$toplevel:commit f030c96。

    it contains the absolute path of the top level directory (where .gitmodules is).

    dtmland在评论中增加了:

    The foreach script will fail to checkout submodules that are not following a branch.
    However, this command gives you both:

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

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

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

    统一建模语言?UTE在注释中使用简化版本对dtmland的命令进行了优化:

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

    多线:

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


    Git 1.8.2增加了跟踪分支的可能性。

    1
    2
    3
    4
    5
    # add submodule to track master branch
    git submodule add -b branch_name URL_to_Git_repo optional_directory_rename

    # update your submodule
    git submodule update --remote

    另见Git子模块


    一个如何使用Git子模块的示例。

  • 创建新存储库
  • 然后将另一个存储库克隆为子模块
  • 然后让该子模块使用一个名为v3.1.2的标记
  • 然后我们承诺。
  • 看起来有点像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    git init
    vi README
    git add README
    git commit
    git submodule add git://github.com/XXXXX/xxx.yyyy.git stm32_std_lib
    git status

    git submodule init
    git submodule update

    cd stm32_std_lib/
    git reset --hard V3.1.2
    cd ..
    git commit -a

    git submodule status

    也许它有帮助(即使我使用标签而不是分支)?


    根据我的经验,无论子模块是否正确添加和跟踪(即@djacobs7和@johnny z answers),在超级项目中切换分支或未来签出仍将导致子模块的头分离。

    而不是手动签出正确的分支,也可以使用脚本git foreach子模块。

    这将检查branch属性的子模块配置文件并签出set branch。

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


    Git子模块有点奇怪——它们总是处于"分离头"模式——它们不会像您预期的那样更新到分支上的最新提交。

    不过,当你想到这一点时,这的确有些道理。假设我使用子模块栏创建存储库foo。我推送我的更改,并告诉您从存储库foo签出commit a7402be。

    然后假设有人在您进行克隆之前提交了对存储库栏的更改。

    当您从存储库foo签出commit a7402be时,您期望得到与我推送的代码相同的代码。这就是为什么子模块在您告诉它们显式地进行一次新的提交之前不会更新的原因。

    我个人认为子模块是git中最容易混淆的部分。有很多地方可以比我更好地解释子模块。我推荐Scott Chacon的Pro Git。


    要切换子模块的分支(假设您已经将子模块作为存储库的一部分):

    • cd到包含子模块的存储库根目录
    • 打开.gitmodules进行编辑
    • path = ...url = ...下为每个子模块添加一行,即branch = your-branch;保存文件.gitmodules
    • 然后在不更改目录的情况下执行$ git submodule update --remote

    …这应该为这样修改的每个子模块拉入指定分支上的最新提交。


    我的.gitconfig文件中有这个。这仍然是草案,但到目前为止证明是有用的。它帮助我总是把子模块重新连接到它们的分支上。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    [alias]

    ######################
    #
    #Submodules aliases
    #
    ######################


    #git sm-trackbranch : places all submodules on their respective branch specified in .gitmodules
    #This works if submodules are configured to track a branch, i.e if .gitmodules looks like :
    #[submodule"my-submodule"]
    #   path = my-submodule
    #   url = [email protected]/my-submodule.git
    #   branch = my-branch
    sm-trackbranch ="! git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; git checkout $branch'"

    #sm-pullrebase :
    # - pull --rebase on the master repo
    # - sm-trackbranch on every submodule
    # - pull --rebase on each submodule
    #
    # Important note :
    #- have a clean master repo and subrepos before doing this !
    #- this is *not* equivalent to getting the last committed
    #  master repo + its submodules: if some submodules are tracking branches
    #  that have evolved since the last commit in the master repo,
    #  they will be using those more recent commits !
    #
    #  (Note : On the contrary, git submodule update will stick
    #to the last committed SHA1 in the master repo)
    #
    sm-pullrebase ="! git pull --rebase; git submodule update; git sm-trackbranch ; git submodule foreach 'git pull --rebase'"

    # git sm-diff will diff the master repo *and* its submodules
    sm-diff ="! git diff && git submodule foreach 'git diff'"

    #git sm-push will ask to push also submodules
    sm-push = push --recurse-submodules=on-demand

    #git alias : list all aliases
    #useful in order to learn git syntax
    alias ="!git config -l | grep alias | cut -c 7-"

    我们使用Quack从另一个Git存储库中提取特定模块。我们需要在不使用所提供存储库的整个代码库的情况下提取代码——我们需要从这个庞大的存储库中提取一个非常具体的模块/文件,并且每次运行更新时都应该对其进行更新。

    所以我们是这样实现的:

    创建配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    name: Project Name

    modules:
      local/path:
        repository: https://github.com/<username>/<repo>.git
        path: repo/path
        branch: dev
      other/local/path/filename.txt:
        repository: https://github.com/<username>/<repo>.git
        hexsha: 9e3e9642cfea36f4ae216d27df100134920143b9
        path: repo/path/filename.txt

    profiles:
      init:
        tasks: ['modules']

    使用上述配置,它将从所提供的Github存储库中创建第一个模块配置中指定的一个目录,另一个目录是从给定的存储库中提取和创建文件。

    其他开发人员只需要运行

    1
    $ quack

    它从上面的配置中提取代码。


    为子模块选择分支的唯一效果是,每当您在git submodule update命令行中通过--remote选项时,git将以分离头模式(如果选择默认--checkout行为)签出所选远程分支的最新提交。

    如果您使用子模块的浅克隆,那么在为Git子模块使用此远程分支跟踪功能时必须特别小心。在子模块设置中为此目的选择的分支不是将在git submodule update --remote期间克隆的分支。如果您还传递了--depth参数,并且您不指示git要克隆哪个分支——实际上您不能在git submodule update命令行中克隆!!--,当显式的--branch参数丢失时,它将隐式地表现为在git clone --single-branchgit-clone(1)文档中解释的那样,因此它将只克隆主分支。

    毫不奇怪,在git submodule update命令执行克隆阶段之后,它最终将尝试检查您以前为子模块设置的远程分支的最新提交,如果这不是主分支,则它不是本地浅克隆的一部分,因此它将失败。

    fatal: Needed a single revision

    Unable to find current origin/NotThePrimaryBranch revision in submodule path 'mySubmodule'


    git子模块add-b develop--name分支名称--https://branch.git