git submodule add -b
是如何工作的?
添加具有特定分支的子模块后,新的克隆存储库(在 git submodule update --init
之后)将位于特定提交处,而不是分支本身(子模块上的 git status
显示“当前不在任何分支上”)。
我在 .gitmodules
或 .git/config
上找不到关于子模块分支或任何特定提交的任何信息,那么 Git 是如何计算出来的呢?
另外,是否可以指定标签而不是分支?
我正在使用版本 1.6.5.2。
我想在这里添加一个答案,它实际上只是其他答案的集合,但我认为它可能更完整。
当你拥有这两个东西时,你就知道你有一个 Git 子模块。
您的 .gitmodules 有一个类似这样的条目: [submodule "SubmoduleTestRepo"] path = SubmoduleTestRepo url = https://github.com/jzaccone/SubmoduleTestRepo.git 您的 Git 存储库中有一个子模块对象(在此示例中名为 SubmoduleTestRepo)。 GitHub 将这些显示为“子模块”对象。或者从命令行执行 git submodule status 。 Git 子模块对象是特殊类型的 Git 对象,它们保存特定提交的 SHA 信息。每当您执行 git 子模块更新时,它都会使用来自提交的内容填充您的子模块。由于 .gitmodules 中的信息,它知道在哪里可以找到提交。现在, -b 所做的只是在 .gitmodules 文件中添加一行。所以按照同样的例子,它看起来像这样: [submodule "SubmoduleTestRepo"] path = SubmoduleTestRepo url = https://github.com/jzaccone/SubmoduleTestRepo.git branch = master 注意:.gitmodules 中只支持分支名称文件,但不支持 SHA 和 TAG! (取而代之的是,可以使用“git add .”来跟踪和更新每个模块的分支提交,例如像 git add ./SubmoduleTestRepo,并且您不需要每次都更改 .gitmodules 文件)子模块对象是仍然指向特定的提交。 -b 选项给您带来的唯一好处是能够根据 Vogella 的回答向您的更新添加 --remote 标志: git submodule update --remote 而不是将子模块的内容填充到子模块指向的提交中,它将该提交替换为主分支上的最新提交,然后使用该提交填充子模块。这可以通过 djacobs7 回答分两步完成。由于您现在已经更新了子模块对象指向的提交,因此您必须将更改的子模块对象提交到您的 Git 存储库中。 git submodule add -b 并不是让分支保持最新状态的神奇方式。它只是在 .gitmodules 文件中添加有关分支的信息,并为您提供在填充之前将子模块对象更新为指定分支的最新提交的选项。
注意:Git 1.8.2 添加了跟踪分支的可能性。请参阅下面的一些答案。
习惯这一点有点令人困惑,但子模块不在分支上。正如您所说,它们只是指向子模块存储库的特定提交的指针。
这意味着,当其他人签出您的存储库或提取您的代码并执行 git submodule update 时,子模块将签出到该特定提交。
这对于不经常更改的子模块非常有用,因为项目中的每个人都可以在同一个提交中拥有子模块。
如果要将子模块移动到特定标签:
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
实际上合并到新代码中。
cd my_submodule; git checkout [ref in submodule's repository
产生 fatal: reference is not a tree: ...
。好像 git
只会在父存储库上运行。
git checkout v1.0
是分支还是标签?
(Git 2.22,Q2 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.
确保您的子模块实际上是该分支的最新版本: cd path/to/your/submodule git checkout -b branch --track origin/branch # 如果主分支已经存在:git branch -u origin/master master
(其中 'origin' 是从中克隆子模块的 upstream remote repo 的名称。
A git remote -v
在那个子模块里面会显示它。通常,它是'origin')
不要忘记在父仓库中记录子模块的新状态: cd /path/to/your/parent/repo git add path/to/your/submodule git commit -m "Make submodule tracking a branch"
该子模块的后续更新必须使用 --remote 选项: # 更新您的子模块 # --remote 还将获取并确保使用分支的最新提交 git submodule update --remote # 避免获取使用 git子模块更新 --remote --no-fetch
请注意,对于 Git 2.10+(2016 年第三季度),您可以使用“.
”作为分支名称:
分支的名称记录为 .gitmodules 中的 submodule.
但是,as commented by LubosD
使用 git checkout,如果要遵循的分支名称是“.”,它将杀死你未提交的工作!改用 git 开关。
这意味着 Git 2.23(2019 年 8 月)或更高版本。
如果要更新分支后的所有子模块:
git submodule update --recursive --remote
请注意,对于每个更新的子模块,结果将几乎始终为 detached HEAD,如 his answer 中的 Dan Cameron 注释。
(Clintm 注意到 in the comments,如果您运行 git submodule update --remote
并且生成的 sha1 与子模块当前所在的分支相同,它不会做任何事情,并且让子模块仍然“在那个分支上”而不是处于分离头部状态。)
为了确保实际签出分支(并且不会修改代表父 repo 子模块的 special entry 的 SHA1),他建议:
git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; git switch $branch'
每个子模块仍将引用相同的 SHA1,但如果您确实进行了新提交,您将能够推送它们,因为它们将被您希望子模块跟踪的分支引用。在子模块中推送之后,不要忘记返回父仓库,为那些修改过的子模块添加、提交和推送新的 SHA1。
请注意 Alexander Pogrebnyak 推荐使用 in the comments 的 $toplevel
。
$toplevel
于 2010 年 5 月在 git1.7.2 中引入:commit f030c96。
它包含顶级目录的绝对路径( .gitmodules 所在的位置)。
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 使用简化版本 in the comments 改进 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(2020 年第一季度)之前,被告知以递归方式获取子模块中的更新的 fetch 不可避免地会产生大量输出,并且很难发现错误消息。
该命令已被教导枚举在操作结束时出现错误的子模块。
请参阅 Emily Shaffer (nasamuffin
) 的commit 0222540(2020 年 1 月 16 日)。
(由 Junio C Hamano -- gitster
-- 在 commit b5c71cc 中合并,2020 年 2 月 5 日)
fetch:在子模块获取期间强调失败签名:Emily Shaffer
在有许多子模块时子模块获取失败的情况下,如果不止一个获取返回到 fetch-by-oid,则来自单独失败的子模块获取的错误会隐藏在其他子模块上的活动中。延迟调用失败,以便用户知道出了什么问题,以及哪里出了问题。
因为 fetch_finish() 只被 run_processes_parallel 同步调用,所以 submodules_with_errors 周围不需要互斥。
请注意,在 Git 2.28(2020 年第三季度)中,脚本化的“git submodule”Porcelain 命令的部分重写将继续;这次轮到“git submodule set-branch
”子命令了。
请参阅 Shourya Shukla (periperidip
) 的commit 2964d6e(2020 年 6 月 2 日)。
(由 Junio C Hamano -- gitster
-- 在 commit 1046282 中合并,2020 年 6 月 25 日)
子模块:port subcommand 'set-branch' 从 shell 到 C 指导者:Christian Couder 指导者:Kaartic Sivaraam 帮助者:Denton Liu 帮助者:Eric Sunshine 帮助者:Đoàn Trần Công Danh 签字者: Shourya Shukla
将子模块子命令 'set-branch' 转换为内置命令并通过 git submodule.sh 调用它。
<path>
替换为 $toplevel/
,foreach
脚本将不依赖于硬编码的 <path>
。
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 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
git submodule update --remote --merge
或 git submodule update --remote --rebase
。这些命令会跟踪远程分支。
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 submodules
.gitmodules
文件上?
git submodule add -b tags/<sometag> <url>
锁定到 标签,您可以在 .gitmodules
中看到行 branch = tags/<sometag>
我如何使用 Git 子模块的示例。
创建一个新的存储库然后克隆另一个存储库作为子模块然后我们让该子模块使用一个名为 V3.1.2 的标签然后我们提交。
这看起来有点像这样:
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
也许它有帮助(即使我使用标签而不是分支)?
git reset --hard V3.1.2
之后提交更改?我只是得到一个带有父目录 git status
的“没有提交”。
根据我的经验,在超级项目中切换分支或未来的结帐仍然会导致子模块的 HEAD 分离,无论是否正确添加和跟踪子模块(即@djacobs7 和@Johnny Z 的答案)。
并且可以使用手动或通过脚本 git submodule foreach 来代替手动检查正确的分支。
这将检查分支属性的子模块配置文件并签出设置的分支。
git submodule foreach -q --recursive 'branch="$(git config -f $toplevel.gitmodules submodule.$name.branch)"; git checkout $branch'
Git 子模块有点奇怪——它们总是处于“分离头”模式——它们不会像你想象的那样更新到分支上的最新提交。
但是,当您考虑它时,这确实是有道理的。假设我使用子模块 bar 创建存储库 foo。我推送我的更改并告诉您从存储库 foo 中签出提交 a7402be。
然后想象有人在您进行克隆之前提交了对存储库栏的更改。
当您从存储库 foo 签出提交 a7402be 时,您希望得到与我推送的相同的代码。这就是为什么子模块在您明确告诉它们然后进行新提交之前不会更新的原因。
我个人认为子模块是 Git 中最令人困惑的部分。有很多地方可以比我更好地解释子模块。我推荐 Scott Chacon 的Pro Git。
git clone git://github.com/git/git.git
并推动该功能......? =D
git
获取手指 10000000 次仍然比其他版本控制工具认为合理的野兽派更可取。
要为子模块切换分支(假设您已经将子模块作为存储库的一部分):
cd 到包含子模块的存储库的根目录
打开 .gitmodules 进行编辑
在 path = ... 和 url = ... 下面添加行,表示每个子模块的 branch = your-branch;保存文件 .gitmodules。
然后不改变目录做 $ git submodule update --remote
...这应该为每个这样修改的子模块拉入指定分支上的最新提交。
现有答案缺少第二步,并且细节过多。
要切换现有子模块以跟踪新的远程 url 和/或新分支:
在 .gitmodules 中编辑事实来源。
例如,从
[submodule "api"]
path = api
url = https://github.com/<original_repo>/api.git
至
[submodule "api"]
path = api
url = https://github.com/<another_repo>/api.git
branch = work-in-progress
git submodule sync:从 .gitmodules 中指定的刚刚编辑的真实来源更新 .git/modules 中 git 缓存的子模块的描述。 git submodule update --init --recursive --remote:更新工作副本中签出的子模块。提交更改。
我的 .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-"
我们使用 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
它从上述配置中提取代码。
为子模块选择分支的唯一效果是,每当您在 git submodule update
命令行中传递 --remote
选项时,Git 将在 detached HEAD 模式下签出(如果默认的 {3 } 行为被选中)该选定 remote 分支的最新提交。
如果您使用子模块的浅层克隆,则在为 Git 子模块使用此远程分支跟踪功能时必须特别小心。您在子模块设置中为此目的选择的分支不是将在 git submodule update --remote
期间克隆的分支。如果您还传递了 --depth
参数并且您没有指示 Git 要克隆哪个分支 -- 实际上您不能在 git submodule update
命令行中! ! -- ,当缺少显式 --branch
参数时,它会隐含地像 git clone --single-branch
的 git-clone(1)
文档中解释的那样运行,因此它将仅克隆主分支。
毫不奇怪,在 git submodule update
命令执行的克隆阶段之后,它最终会尝试检查您之前为子模块设置的 remote 分支的最新提交,如果这是不是主要的,它不是您本地浅克隆的一部分,因此它会失败
致命:需要单个修订版无法在子模块路径“mySubmodule”中找到当前来源/NotThePrimaryBranch 修订版
git 子模块添加 -b 开发 --name 分支名称 -- https://branch.git
git submodule add -b master --name master -- https://github.com/nlohmann/json.git libs/json
为小费干杯!
.gitmodules
中指定了一个标签而不是分支,但在执行$ git submodule update --init --remote TestModule
之后,我收到了一个错误,提示fatal: Needed a single revision
和Unable to find current origin/TestTag revision in submodule path 'TestModule'
。当使用真正的分支进行操作时,它可以工作。无论如何在.gitmodules
中指定一个标签而不必指定确切的提交?.gitmodules
中的哈希并运行了git submodule update
,但什么也没发生?