git子模块add -b如何工作?

在添加带有特定分支的子模块后,一个新的克隆存储库(在git子模块update——init之后)将在特定的提交处,而不是分支本身(子模块上的git状态显示为“not current on any branch”)。

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

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

我使用的是1.6.5.2版本。


当前回答

(Git 2.22, Q2 2019,引入了Git子模块set-branch——branch branch——<submodule_path>)

注意,如果你有一个现有的子模块,它还没有跟踪分支,那么(如果你有git 1.8.2+):

确保父repo知道它的子模块现在跟踪一个分支: cd /道路/ /你/父母/回购 Git config -f . Git modules submodule.<path>。分支<分支> 确保你的子模块实际上位于该分支的最新位置: cd路径/ /你/子模块 Git checkout -b branch——跟踪原点/分支 #如果主分支已经存在: Git branch -u origin/master master

         (其中'origin'是子模块克隆的上游远程repo的名称。          该子模块中的git remote -v将显示它。通常是“origin”)

不要忘记在父repo中记录子模块的新状态: cd /道路/ /你/父母/回购 Git添加路径/到/your/子模块 让子模块跟踪一个分支 该子模块的后续更新必须使用——remote选项: #更新子模块 #——remote也将获取并确保 #使用来自分支的最新提交 Git子模块update——remote #来避免抓取使用 Git子模块更新——remote——no-fetch


注意,在Git 2.10+(2016年Q3)中,您可以使用'。'作为分支名称:

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

但是,正如LubosD所评论的那样

使用git签出,如果分支名称是“。”,它将终止您未提交的工作! 使用git switch代替。

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

参见“git checkout让人困惑”


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

    git submodule update --recursive --remote

注意,对于每个更新的子模块,结果几乎总是一个分离的HEAD,正如Dan Cameron在他的回答中指出的那样。

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

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

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

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

注意使用$ topllevel,这是Alexander Pogrebnyak在评论中推荐的。 $ topllevel于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 (nasamuffin)提交的0222540(2020年1月16日)。 (由Junio C Hamano—gitster—在commit b5c71cc中合并,2020年2月5日)

获取:强调子模块获取失败 署名:Emily Shaffer

在有很多子模块的情况下,当一个子模块读取失败时,如果多个读取返回到按oid读取,那么来自唯一失败的子模块读取的错误将被隐藏在其他子模块的活动之下。 延迟调用故障,这样用户就能意识到哪里出了问题。

因为fetch_finish()只由run_processes_parallel同步调用,所以在submodules_with_errors周围不需要互斥。


注意,在Git 2.28 (Q3 2020)中,继续重写脚本化的“Git子模块”Porcelain命令的部分内容;这次轮到“git submodule set-branch”子命令了。

参见Shourya Shukla (periperidip)提交2964d6e (02 Jun 2020)。 (由Junio C Hamano—gitster—在commit 1046282中合并,2020年6月25日)

子模块:端口子命令'set-branch'从shell到C 导师:Christian Couder 导师:karartic Sivaraam 帮助:Denton Liu 资助人:Eric Sunshine 帮助:Đoàn trn Công Danh 署名:Shourya Shukla

将子模块子命令“set-branch”转换为内置命令,并通过git submodule.sh调用它。

其他回答

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

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

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

Git子模块foreach -q——recursive 'branch="$(Git config -f $ topllevel . "gitmodules子模块。name.branch美元)”;Git checkout $branch'

Git子模块add -b develop——name branch-name——https://branch.git

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

有了这两个东西,就有了Git子模块。

Your .gitmodules has an entry like so: [submodule "SubmoduleTestRepo"] path = SubmoduleTestRepo url = https://github.com/jzaccone/SubmoduleTestRepo.git You have a submodule object (named SubmoduleTestRepo in this example) in your Git repository. GitHub shows these as "submodule" objects. Or do git submodule status from a command line. Git submodule objects are special kinds of Git objects, and they hold the SHA information for a specific commit. Whenever you do a git submodule update, it will populate your submodule with content from the commit. It knows where to find the commit because of the information in the .gitmodules. Now, all the -b does is add one line in your .gitmodules file. So following the same example, it would look like this: [submodule "SubmoduleTestRepo"] path = SubmoduleTestRepo url = https://github.com/jzaccone/SubmoduleTestRepo.git branch = master Note: only branch name is supported in a .gitmodules file, but SHA and TAG are not supported! (instead of that, the branch's commit of each module can be tracked and updated using "git add .", for example like git add ./SubmoduleTestRepo, and you do not need to change the .gitmodules file each time) The submodule object is still pointing at a specific commit. The only thing that the -b option buys you is the ability to add a --remote flag to your update as per Vogella's answer: git submodule update --remote Instead of populating the content of the submodule to the commit pointed to by the submodule, it replaces that commit with the latest commit on the master branch, THEN it populates the submodule with that commit. This can be done in two steps by djacobs7 answer. Since you have now updated the commit the submodule object is pointing to, you have to commit the changed submodule object into your Git repository. git submodule add -b is not some magically way to keep everything up to date with a branch. It is simply adds information about a branch in the .gitmodules file and gives you the option to update the submodule object to the latest commit of a specified branch before populating it.

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

不过,当你仔细思考时,这确实是有道理的。假设我用子模块bar创建了存储库foo。我推送我的更改并告诉您从存储库foo中提交a7402be。

然后想象一下,在您可以克隆之前,有人将更改提交到存储库栏。

当您从存储库foo签出commit a7402be时,您希望得到与我推送的相同的代码。这就是为什么子模块不会更新,直到你显式地告诉它们,然后进行新的提交。

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

注意: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拉修改提交他们的子模块目录指向。Git子模块update实际上合并到新代码中。