首页 文章

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

提问于
浏览
579

git submodule add -b 如何运作?

添加具有特定分支的子模块后,新的克隆存储库(在 git submodule update --init 之后)将处于特定提交,而不是分支本身(子模块上的 git status 显示"Not currently on any branch") .

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

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

我正在使用1.6.5.2版 .

11 回答

  • 34

    注意: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
    

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

  • 7

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

    当你有这两件事时,你知道你有一个Git子模块 .

    • 您的 .gitmodules 有这样的条目:
    [submodule "SubmoduleTestRepo"]
        path = SubmoduleTestRepo
        url = https://github.com/jzaccone/SubmoduleTestRepo.git
    
    • 您的Git存储库中有一个子模块对象(在此示例中名为SubmoduleTestRepo) . GitHub将这些显示为"submodule"对象 . 或者从命令行执行 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
    

    编辑:上面只支持分支名称,而不是SHA或TAG .

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

    git submodule update --remote
    

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

    git submodule add -b 并不是一种神奇的方式来保持一切与分支一致 . 它只是在 .gitmodules 文件中添加有关分支的信息,并为您提供在填充之前将子模块对象更新为指定分支的最新提交的选项 .

  • 612

    注意 if you have an existing submodule which isn't tracking a branch yet ,然后(if you have git 1.8.2+):

    • 确保父repo知道它的子模块现在跟踪一个分支:
    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'是已从中克隆的子模块的名称'origin' .
    该子模块内的 git remote -v 将显示它 . 通常,它是'origin')

    • 不要忘记在父仓库中记录子模块的新状态:
    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年第3季度),您可以使用' . '作为分支名称:

    分支的名称记录为子模块 . <name> .branch in .gitmodules for update --remote . 一个特殊的 Value . 用于指示子模块中分支的名称应与当前存储库中的当前分支的名称相同 .


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

    git submodule update --recursive --remote
    

    请注意,对于每个更新的子模块,结果将 almost always be a detached HEADDan Cameron中的Dan Cameron注释 .

    Clintm注释in the comments,如果运行 git submodule update --remote ,结果sha1与子模块当前所在的分支相同,它不会做任何事情,并使子模块仍然"on that branch"而不是处于分离头状态 . )

    为了确保实际检出分支(并且不会修改表示父代表的子模块的 special entry 的SHA1),他建议:

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

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

    注意使用 $toplevelin the comments推荐in the comments .
    $toplevel 于2010年5月在git1.7.2中引入:commit f030c96 .

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

    dtmland添加in the comments

    foreach脚本将无法签出不在分支后面的子模块 . 但是,此命令为您提供:

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

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

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

    umläute使用简化版in the comments优化dtmland的命令:

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

    多行:

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

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

    # add submodule to track master branch
    git submodule add -b master [URL to Git repo];
    
    # update your submodule
    git submodule update --remote
    

    另见Git submodules

  • 257

    我如何使用Git子模块的一个例子 .

    • 创建新存储库

    • 然后将另一个存储库克隆为子模块

    • 然后我们让子模块使用一个名为V3.1.2的标签

    • 然后我们承诺 .

    这看起来有点像这样:

    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
    

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

  • 433

    根据我的经验,在超级项目中切换分支或将来检出仍将导致子模块的分离HEAD,无论子模块是否被正确添加和跟踪(即@ djacobs7和@Johnny Z答案) .

    而不是手动或通过脚本手动检出正确的分支git submodule foreach .

    这将检查分支属性的子模块配置文件并检查set分支 .

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

  • 49

    Git子模块有点奇怪 - 它们总是处于“独立头”模式 - 它们不会像您期望的那样更新到分支上的最新提交 .

    不过,当你考虑它时,这确实有些意义 . 假设我使用子模块栏创建存储库foo . 我推动我的更改,并告诉你检查从存储库foo提交a7402be .

    然后想象有人在您进行克隆之前将更改提交到存储库栏 .

    当您从存储库foo中检出提交a7402be时,您希望得到我推送的相同代码 . 那个's why submodules don't更新,直到你明确地告诉他们然后再做一个新的提交 .

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

  • 28

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

    • cd 到包含子模块的存储库的根目录

    • 打开 .gitmodules 进行编辑

    • 为每个子模块添加 path = ...url = ... 下面的行 branch = your-branch ;保存文件 .gitmodules .

    • 然后不改变目录做 $ git submodule update --remote

    ...这应该为每个被修改的子模块提取指定分支上的最新提交 .

  • 11

    我在.gitconfig文件中有这个 . 它仍然是一个草案,但到目前为止证明是有用的 . 它帮助我总是将子模块重新附加到它们的分支 .

    [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 = git@wherever.you.like/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-"
    
  • 3

    我们使用Quack从另一个Git存储库中提取特定模块 . 我们需要在没有提供的存储库的整个代码库的情况下提取代码 - 我们需要来自该庞大存储库的非常特定的模块/文件,并且每次运行更新时都应该更新 .

    所以我们用这种方式实现了它:

    Create configuration

    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存储库创建一个目录,另一个是从给定的存储库中提取和创建文件 .

    其他开发人员只需要运行

    $ quack
    

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

  • -1

    为子模块选择分支的唯一效果是,每当您在 git submodule update 命令行中传递 --remote 选项时,Git将以分离的HEAD模式(如果选择了默认的 --checkout 行为)检出所选 remote 分支的最新提交 .

    你必须特别如果您使用子模块的浅克隆,那么在使用Git子模块的远程分支跟踪功能时要小心 . 您在子模块设置中为此目的选择的分支 IS NOT 将在 git submodule update --remote 期间克隆的分支 . 如果您还传递了 --depth 参数,并且您没有指示Git关于要克隆的分支 - and actually you cannot 命令行中的 and actually you cannot !! - ,如果缺少显式的 --branch 参数,它将隐含行为,如 git-clone(1)git-clone(1) 文档中所述,因此 it will clone the primary branch only .

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

    致命:需要单个修订无法在子模块路径'mySubmodule'中找到当前原点/ NotThePrimaryBranch修订版

相关问题