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


755

git submodule add -b工作如何?

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

我无法找到任何信息.gitmodules.git/config关于子模块的分支或任何具体的承诺,它是如何与Git的数字出来?

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

我正在使用1.6.5.2版。


3
如果你有一个现有的子模块,其不跟踪分支还,但你希望它现在将跟踪一个分支... 看到我的回答如下
VonC

Answers:


744

注意: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实际上合并到新代码中。


8
这是一个很好的解释,谢谢!当然,在阅读完您的答案后,我意识到提交已保存在子模块本身(submodule / .git / HEAD)中。
伊万,

4
这似乎在git 1.7.4.4上不起作用。cd my_submodule; git checkout [ref in submodule's repository产量fatal: reference is not a tree: ...。好像git只能在父存储库上运行。
詹姆斯·罗森

3
即使对于经常更新的项目,也最好使用git子模块。linux内核使用它,而且还不错

10
git checkout v1.0分支还是标签?
2015年

8
将标签视为提交的可读别名。提交是每个文件的一组特定状态。分支本质上是同一件事,只是您可以对其进行更改。
deadbabykitten

656

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

当您拥有这两件事时,您就知道您拥有一个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 .” 来跟踪和更新每个模块的分支提交,例如like git add ./SubmoduleTestRepo,并且您不必.gitmodules每次都更改文件)

    子模块对象仍指向特定的提交。该-b选件可以为--remote您提供的唯一好处是可以根据Vogella的答案在更新中添加标志:

    git submodule update --remote
    

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

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


14
这个答案应该有更多的赞成票。在过去的一天里,我一直在阅读许多帖子,这消除了所有的困惑。来自SVN世界并使用外部环境-人们想相信git子模块分支跟踪确实神奇地使分支中的所有内容保持最新-但这不是事实!您必须显式更新它们!如您所述,您必须提交更改的子模块对象。
dtmland 2015年

12
此分支跟踪是否也适用于标签?我在分支中指定了一个标签,而不是分支.gitmodules,执行完后出现$ git submodule update --init --remote TestModule了一个错误,说fatal: Needed a single revisionUnable to find current origin/TestTag revision in submodule path 'TestModule'。在使用真实分支时,它可以工作。无论如何,.gitmodules无需指定确切的提交就可以指定标签?
2015年

5
这似乎不起作用。我更新了哈希.gitmodules并运行git submodule update,什么都没有发生?
CMCDragonkai '16

2
不知何故,这对我不起作用。随着SHA提交ID,我总是得到一个错误“无法找到当前版本(我双重检查HEAD的版本号和它的正确的)但是,如果我使用主它的工作原理。
infoclogged

2
在branch属性中输入SHA对我也不起作用。docs 还不支持这种用法:git-scm.com/docs/gitmodules
Jakub Bochenski

338

(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.gitmodulesupdate --remote
的特殊价值.用来指示子模块中分支的名称应与当前存储库中的当前分支的名称相同

但是,作为评论 LubosD

使用git checkout,如果要跟随的分支名称是“ .”,它将杀死您的未提交工作!
采用git switch代替。

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

参见“ 混淆者git checkout


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

    git submodule update --recursive --remote

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

克林特姆在评论中指出是,如果你运行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。

使用注意事项$toplevel,建议在评论亚历山大Pogrebnyak
$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(提交的0222540(2020年1月16日(由Junio C Hamano合并--commit b5c71cc中,2020年2月5日)nasamuffin
gitster

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

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

如果在子模块存在多个子模块的情况下子模块获取失败,则如果失败的原因多于一个,则来自唯一失败子模块获取的错误将隐藏在其他子模块的活动中fetch-by-oid
尽早排除故障,以便用户知道出了点问题以及在哪里

因为fetch_finish()run_processes_parallel,需要通过静音同步调用即可,因此不需要submodules_with_errors


1
问题:如果我有文件夹subModule1并希望跟踪master分支,则生成的命令将如下所示: git config -f .gitmodules submodule.subModule1.branch master
BraveNewMath 2013年

1
foreach脚本将不依赖于硬编码<path>,如果你替换<path>$toplevel/
亚历山大·波格瑞布纳克

1
foreach脚本将无法检出不在分支之后的子模块。但是,此命令可以为您提供以下两者:git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git checkout $branch'
dtmland 2015年

2
:这里的@ dtmland的脚本的简化版本git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
umläute

1
哦!实际上,foreach脚本是不必要的。我们必须使用--merge或--rebase开关:git submodule update --remote --merge或来执行子模块更新git submodule update --remote --rebase。这些命令跟踪远程分支。
GregTom

206

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

# 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子模块


4
这也适用于标签吗?
ThorSummoner 2014年

1
这样添加子模块如何反映在.gitmodules文件上?
尤金

1
谢谢,我只是使用了有关信息来帮助我创建一个与GitHub gh-pages网站同步的子模块文件夹:完整示例请参见github.com/o2platform/fluentnode/issues/22
Dinis Cruz

4
您可以锁定一个标签git submodule add -b tags/<sometag> <url>您可以为线看到branch = tags/<sometag>.gitmodules
KCD

9
@KCD哪个版本的git可以使用标签做到这一点。我的不行吗?
CMCDragonkai '16

58

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

  1. 创建一个新的仓库
  2. 然后将另一个存储库克隆为子模块
  3. 然后,我们让该子模块使用名为V3.1.2的标签
  4. 然后我们承诺。

看起来有点像这样:

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 

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


4
它的答案与djacobs7基本上相同,但无论如何还是要感谢:)
Ivan

1
您之后应该能够进行更改git reset --hard V3.1.2吗?我只得到git status父目录的“无可奉告” 。
尼克·拉德福德

1
@Ivan:您能解释一下这与djacobs7的回复一样吗?据我所知,他的响应甚至不包括“ submodule add”命令,而是直接添加存储库,而没有任何指向模块原始git repo的链接。至少当我尝试这种方法时,.gitmodules中没有链接。
米歇尔·穆勒(

djacobs7的响应未包括从添加子模块开始的全部说明。他认为您已经拥有它。
CodeMonkey

难道不只是将整个子模块的内容作为跟踪对象添加到您的主仓库中?
user1312695

38

以我的经验,无论是否正确添加和跟踪了子模块(即@ djacobs7和@Johnny Z的答案),在超级项目中切换分支或将来的结帐仍会导致子模块的HEAD分离。

代替手动或通过脚本git子模块foreach手动签出正确的分支。

这将检查子模块配置文件中的branch属性,并检出set分支。

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


真好 +1。我已经在回答中包括了您的命令。
VonC

33

Git子模块有点奇怪-它们始终处于“分离头”模式-它们不会像您期望的那样更新到分支上的最新提交。

但是,当您考虑时,这确实有意义。假设我使用子模块bar创建存储库foo。我推送更改,并告诉您从存储库foo中检出commit a7402be 。

然后,假设有人对存储库进行了更改,然后再进行克隆。

当您从存储库foo中提交a7402be时,您期望得到与我推送的相同的代码。这就是为什么直到您明确告诉子模块然后进行新提交才更新子模块的原因。

我个人认为子模块是Git最令人困惑的部分。有很多地方可以比我更好地解释子模块。我推荐Scott Chacon的Pro Git


我想是时候开始阅读一些git了,谢谢你的推荐。
伊万,

抱歉,但是您无法确定是否可以通过推送到a7402be获得相同的结果,或者获得最新的bar(尽管您使用的是foo版本)。谢谢:)
mmm

6
问题是应该有一个选项说“将这个子模块保留在分支X上”,这样,如果您希望它自动更新,则可以实现。这将使子模块对于管理WordPress安装更加有用,例如,其中WordPress插件都是Git仓库,而不必为每个更新的插件重新保存超级项目。
jerclarke 2012年

@jeremyclark git clone git://github.com/git/git.git并推动该功能...?= D
Alastair

1
关于Git的最令人困惑的部分是,即使经过十多年的开发,一个旨在帮助我完成工作的工具仍然具有如此糟糕的用户体验,并且由于我自己无法完全理解的原因,人们都喜欢被Git所指责。时间。
0xC0000022L

20

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

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

...这应在修改后的每个子模块中提取指定分支上的最新提交。


10

我的.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存储库中提取特定模块。我们需要在没有提供的存储库的整个代码库的情况下提取代码-我们需要一个来自大型存储库的非常特定的模块/文件,并且每次运行update时都应对其进行更新。

因此,我们通过以下方式实现了这一目标:

创建配置

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

并且它从上述配置中提取代码。


2

为子模块选择分支的唯一效果是,每当您--remotegit submodule update命令行中传递该选项时,Git都会以分离的HEAD模式(如果--checkout选择了默认行为)签出该选定远程分支的最新提交。

如果使用子模块的浅表克隆,则在对Git子模块使用此远程分支跟踪功能时必须特别小心。您在子模块设置中为此选择的分支不是将在期间克隆的分支git submodule update --remote。如果您还传递了--depth参数,但是您没有指示Git您要克隆哪个分支 - 实际上您不能git submodule update命令行中使用!-时,它会隐含地像git-clone(1)文档中说明的那样在缺少git clone --single-branch显式--branch参数时起作用,因此它将仅克隆主分支

毫不奇怪,在git submodule update命令执行克隆阶段之后,它最终将尝试检出您先前为子模块设置的远程分支的最新提交,并且,如果不是主要分支,则不属于该分支您的本地浅表克隆,因此将失败

致命:需要进行一次修订

在子模块路径“ mySubmodule”中找不到当前源/ NotThePrimaryBranch修订版


如何解决该错误-需要单个修订版?
NidhinSPradeep

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.