git子模块add -b如何工作?
在添加带有特定分支的子模块后,一个新的克隆存储库(在git子模块update——init之后)将在特定的提交处,而不是分支本身(子模块上的git状态显示为“not current on any branch”)。
我在.gitmodules或.git/config中找不到任何关于子模块分支或任何特定提交的信息,那么Git是如何解决的呢?
另外,是否可以指定一个标记而不是分支?
我使用的是1.6.5.2版本。
git子模块add -b如何工作?
在添加带有特定分支的子模块后,一个新的克隆存储库(在git子模块update——init之后)将在特定的提交处,而不是分支本身(子模块上的git状态显示为“not current on any branch”)。
我在.gitmodules或.git/config中找不到任何关于子模块分支或任何特定提交的信息,那么Git是如何解决的呢?
另外,是否可以指定一个标记而不是分支?
我使用的是1.6.5.2版本。
当前回答
现有的答案忽略了第二步,并且有过多的细节。
切换一个现有的子模块来跟踪一个新的远程url和/或一个新的分支:
在.gitmodules中编辑真相的来源。
例如,从
[submodule "api"]
path = api
url = https://github.com/<original_repo>/api.git
to
[submodule "api"]
path = api
url = https://github.com/<another_repo>/api.git
branch = work-in-progress
你也可以使用hexsha来进行提交哈希。或标签,但见3。
git子模块同步:从.gitmodules中指定的刚刚编辑过的真值源中更新git在.git/modules中缓存的子模块的描述。 只有当你指定一个标签:git submodule foreach——recursive 'git fetch——tags'来获取标签。 git子模块update——init——recursive——remote:更新工作副本中签出的子模块。 提交更改。
其他回答
Git子模块有点奇怪——它们总是处于“分离头”模式——它们不会像你期望的那样更新到分支上的最新提交。
不过,当你仔细思考时,这确实是有道理的。假设我用子模块bar创建了存储库foo。我推送我的更改并告诉您从存储库foo中提交a7402be。
然后想象一下,在您可以克隆之前,有人将更改提交到存储库栏。
当您从存储库foo签出commit a7402be时,您希望得到与我推送的相同的代码。这就是为什么子模块不会更新,直到你显式地告诉它们,然后进行新的提交。
我个人认为子模块是Git中最令人困惑的部分。有很多地方可以比我更好地解释子模块。我推荐Scott Chacon的Pro Git。
Git 1.8.2增加了跟踪分支的可能性。
# add submodule to track branch_name branch
git submodule add -b branch_name URL_to_Git_repo optional_directory_rename
# update your submodule
git submodule update --remote
参见Git子模块
我们使用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
它从上面的配置中提取代码。
(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调用它。
注意: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实际上合并到新代码中。