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

问题描述 投票:632回答:12

git submodule add -b如何运作?

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

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

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

我正在使用1.6.5.2版。

git git-submodules
12个回答
664
投票

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


3
投票

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

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

创建配置

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
投票

为子模块选择分支的唯一效果是,每当您在--remote命令行中传递git 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修订版


0
投票

git submodule add -b develop --name branch-name - https://branch.git


513
投票

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

当你有这两件事时,你知道你有一个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 编辑:上面只支持分支名称,而不是SHA或TAG。 子模块对象仍指向特定的提交。根据Vogella的回答,-b选项购买的唯一内容是能够为您的更新添加--remote标志: git submodule update --remote 它不是将子模块的内容填充到子模块指向的提交,而是用主分支上的最新提交替换该提交,然后用该提交填充子模块。这可以通过djacobs7回答分两步完成。由于您现在已经更新了子模块对象所指向的提交,因此您必须将更改的子模块对象提交到Git存储库中。 git submodule add -b不是一个神奇的方式来保持一切与分支最新。它只是在.gitmodules文件中添加有关分支的信息,并为您提供在填充之前将子模块对象更新为指定分支的最新提交的选项。

283
投票

(Git 2.22,2019年第二季度,推出了git submodule set-branch --branch aBranch -- <submodule_path>

请注意,如果您现有的子模块尚未跟踪分支,那么(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'是upstream remote repo的名称,子模块已被克隆。 该子模块内的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年第3季度),您可以使用'.'作为分支名称:

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


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

    git submodule update --recursive --remote

请注意,对于每个更新的子模块,结果几乎总是detached HEAD,如Dan Cameron中的his answer音符。

Clintm注意到in the comments,如果你运行git submodule update --remote并且生成的sha1与子模块当前所在的分支相同,它将不会执行任何操作并使子模块仍然“在该分支上”而不是处于分离的头状态。)

为了确保分支实际被检出(并且不会修改代表父回购的子模块的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推荐的Alexander Pogrebnyak$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用简化版dtmland改进了in the comments的命令:

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

178
投票

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 submodules


50
投票

我如何使用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 

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


36
投票

根据我的经验,在超级项目中切换分支或将来检出仍将导致子模块的分离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'


30
投票

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

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

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

当您从存储库foo中检出提交a7402be时,您希望得到我推送的相同代码。这就是为什么子模块在您明确告诉它们然后进行新的提交之前不会更新的原因。

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


15
投票

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

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

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


9
投票

我在.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 = [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-"
© www.soinside.com 2019 - 2024. All rights reserved.