Git子模块:指定分支/标记

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

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

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

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

PS:我正在使用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 

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

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

当你有这两件事情时,你知道你有一个git子模块。

1)你的.gitmodule有这样一个条目:

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

2)你的git仓库中有一个子模块对象(在这个例子中叫做SubmoduleTestRepo)。 Github将这些显示为“子模块”对象。 或者从命令行执行git submodule status 。 Git子模块对象是特殊types的git对象,它们为特定的提交保存SHA信息。

每当你做一个git submodule update ,它将填充提交的内容你的子模块。 它知道在哪里find提交,因为.gitmodules中的信息。

现在,所有的-b都是在.gitmodule文件中添加一行。 所以下面的例子看起来像这样:

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

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

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

 git submodule update --remote 

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

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

请注意, 如果您有一个现有的子模块尚未跟踪分支 ,那么( 如果您有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”是子模块已经克隆的上游远程repo的名称。
该子模块内的git remote -v将显示它。 通常是“起源”)

  • 不要忘记将你的子模块的新状态logging在你的父回购中:

     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+ (Q3 2016),您可以使用' . '作为分支名称:

分支的名称被logging为submodule.<name>.branch.gitmodules用于update --remote
一个特殊的价值. 用于指示子模块中分支的名称应与当前存储库中当前分支的名称相同


如果你想更新分支之后的所有子模块:

  git submodule update --recursive --remote 

请注意,对于每个更新的子模块,结果将总是一个分离的HEAD ,正如Dan Cameron在他的回答中所指出的那样。
为了确保分支实际被检出(并且不会修改代表父回购子模块的特殊条目的SHA1),他build议:

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

每个子模块仍然会引用相同的SHA1,但是如果您做了新的提交,您将能够推送它们,因为它们将被子模块要跟踪的分支引用。
在子模块内部进行推送之后,不要忘记返回到父回购,为这些修改的子模块添加,提交并推送新的SHA1。

请注意, 在 Alexander Pogrebnyak 的评论中推荐使用$toplevel
$toplevel是在2010年5月的git1.7.2中引入的: 提交f030c96 。

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

dtmland 在评论中补充道:

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 在评论中精简了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)' 

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

我如何使用git子模块的一个例子。

  1. 创build一个新的回购
  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 

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

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

而不是手动检查正确的分支手动或通过脚本git submodule foreach可以使用。

这将检查分支属性的子模块configuration文件并检查设置的分支。

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

git子模块有点奇怪 – 它们总是处于“分离头”模式 – 它们并不像你期望的那样更新到分支上的最新提交。

但是,当你考虑这个时,这确实是有意义的。 比方说,我用子模块栏创build存储库富。 我推我的变化,并告诉你检查从仓库foo提交a7402be。

然后想象一下,有人在做你的克隆之前提交了一个repo吧的更改。

当你从repo foo签出提交a7402be时,你希望得到我推送的相同的代码。 这就是为什么子模块不会更新,直到你明确地告诉它们,然后做出新的提交。

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

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

  • cd到包含子模块的回购站的根目录
  • 打开.gitmodules进行编辑
  • path = ...下面添加一行path = ...url = ...表示branch = your-branch ,对于每个子模块; 保存文件.gitmodules
  • 然后不改变目录做$ git submodule update --remote

…这应该在指定的分支上提交最新的提交,对于每个被修改的子模块。

我有这个在我的.gitconfig文件。 这仍然是一个草案,但现在certificate有用。 它帮助我总是将子模块重新连接到它们的分支。

 [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, ie 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-" 

我们使用Quack从其他的git仓库中提取特定的模块。 我们需要抽取代码,而不需要提供整个代码库 – 我们需要从这个庞大的仓库中获取特定的模块/文件,每次运行update时都应该更新。

所以我们这样做,

创buildconfiguration

 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'] 

通过上面的configuration,它可以从第一个模块configuration中指定的github仓库创build一个目录,另一个是从给定的仓库中提取和创build文件。

其他开发人员只需要运行

 $ quack 

它从上面的configuration拉代码。