如何浅拉由分支名称跟踪的子模块
How to shallow pull submodule that is tracked by branch name
您好,我有一个包含子模块的超级项目。子模块由分支名称而不是 sha 提交号跟踪。在我们的构建服务器上,我想尽可能少地拉动。所以我尝试了
git submodule update --remote --init
但这并不肤浅。好像把所有东西都拉出来然后切换到 branch
git submodule update --remote --init --depth 1
这行不通,它失败了:
git submodule update --remote --init --depth 1 ThirdParty/protobuf
Submodule 'ThirdParty/protobuf' (ssh://myrepo/thirdparty/protobuf.git)
registered for path 'ThirdParty/protobuf'
Cloning into '/home/martin/jenkins/workspace/test_log_service/repo/ThirdParty/protobuf'...
fatal: Needed a single revision
Unable to find current origin/version/3.2.0-era revision in submodule path 'ThirdParty/protobuf'
关于浅层子模块有一个不同的问题,但是我没有看到它适用于分支,只适用于 sha 提交
TL;DR
我认为您遇到了 Git 中的错误。要解决此问题,请使用 --no-single-branch
或手动配置分支。
其他需要知道的事情:
如果您有递归子模块,请确保您的 Git 是最新的,并使用 --recommend-shallow
递归启用浅层子模块,或使用 --no-recommend-shallow
禁用它们。
您可能需要分两步完成。我将在下面将其显示为 two-step 序列。我知道这段代码在 Git 1.7 和当前(2.26 左右)Git 之间发生了很大变化,我希望 two-step 序列也适用于大多数旧版本。
两步是:
N=... # set your depth here, or expand it in the two commands
git submodule update --init --depth $N --no-single-branch
git submodule update --remote --depth $N
Git 人员最近一直在修复各种 shallow-clone 子模块错误,作为添加 --recommend-shallow
递归子模块的一部分,因此这可能作为一个命令全部起作用。根据下面的分析,它应该在当前Git中都作为一个命令工作。但是,--no-single-branch
比 --single-branch
获取更多的对象。
另一个选项可能是允许 single-branch 模式但修复子模块中的 fetch
refspec。这需要三个步骤——嗯,三个单独的 Git 命令,无论如何:
branch=... # set this to the branch you want
git submodule update --init --depth $N
(cd path/to/submodule &&
git config remote.origin.fetch +refs/heads/$branch:refs/remotes/origin/$branch)
git submodule update --remote --depth $N
(您可以在所有子模块中使用 git submodule foreach
执行此操作,但请记住选择正确的分支名称 per-submodule。)
总的来说——这不是你的错误所特有的——我建议避免浅层子模块:它们往往不能很好地工作。如果您真的想使用它们,请使用 pretty-big 深度:例如,50、100 或更多。根据您自己的存储库和需求调整它。 (您当前的设置确实允许 --depth 1
,前提是您解决了其他问题。)
长:这可能是 Git
中的错误
请注意,以下分析均基于源代码。我还没有真正测试过这个,所以我可能错过了一些东西。不过,这些原则都是合理的。
所有 子模块总是“sha 提交”,或者可能是“sha1”提交——Git 用于称呼它们那,但现在称它们为 OID,其中 OID 代表对象 ID。未来 Git 可能会使用 SHA-2。1 因此,如果希望避免 TLA 综合症,请使用“OID”或“哈希 ID”,2当然是一个更好的术语。所以让我这样说吧:所有子模块都使用 OID / hash-ID commits.
“所有子模块始终使用 OID/哈希 ID”是什么意思?嗯,这是浅层子模块的关键之一。浅子模块本质上是脆弱的,要 Git 在所有情况下都正确使用它们是很棘手的。此声明:
The submodule is tracked by a branch name and not by a sha commit number.
在一个重要方面是错误的。无论您多么努力,子模块——或者更准确地说,子模块提交——都会被哈希 ID 跟踪。
现在,子模块中确实有个分支名称参与克隆和获取。当您将 --shallow
与子模块一起使用时,这会变得 非常 重要,因为大多数服务器不允许 hash-ID 获取(附注,2021 年 1 月:this正在发生变化,因为 Git 中的一些新功能需要它——GitHub 已经允许通过 ID 获取——所以随着时间的推移,这种情况应该会有所改善)。您选择的深度——以及单个分支名称,因为 --depth
意味着 --single-branch
——因此必须足够深以达到 commit 超级项目 Git选择。
如果您覆盖 Git 的hash-ID 跟踪子模块提交跟踪,您可以绕过一个脆弱性问题。这就是你正在做的,但你遇到了一个错误。
1那不是很有趣吗? Git 在很大程度上取决于每个提交都有一个唯一的 OID;引入新的 OID 命名空间,因此每个 Git 都有 两个 OID,每个 OID 在其命名空间内都是唯一的,这意味着提交 不会 必须具有 适当的 OID。所有协议都变得更加复杂:任何仅支持旧方案的 Git 都需要(单个)OID 的 SHA-1 哈希,而任何使用新方案的 Git 都需要 SHA- 2 哈希,可能连同 SHA-1 哈希一起提供给旧的 Gits。一旦我们有了这个对象,我们就可以用它来计算 其他散列,但是如果我们只有两个散列之一,它需要是正确的。
处理这个问题的直接方法是将计算“其他人的散列”的负担放在具有对象的 Git 上,如果对象存在于使用不同的存储库中OID 命名空间。但是 SHA-1 Gits 不能改变,所以我们不能使用那个方法。负担必须放在新的 SHA-2 Gits.
上
2请注意,“SHA”本身是一个 TLA:三个字母的首字母缩写词。 TLAS 代表 TLA 综合症,是一个 ETLA:扩展的三字母首字母缩写词。
一个超级项目如何 Git ch设置一个子模块 Git commit?
git submodule
命令是 currently still a big shell script,但它的大部分操作使用 C 语言帮助程序。虽然它是一个复杂的 shell 脚本,但它的核心是 运行:
(cd $path && git $command)
为了在每个子模块中做事。 $path
是子模块的路径,$command
是该子模块内 运行 的命令。
虽然这里有一些 chicken-and-egg 东西,因为 $path
最初只是一个空目录:在克隆超级项目之后还没有实际的 clone .在 是 克隆之前,任何 Git 命令都不起作用!好吧,除了 git clone
本身,什么也没有,就是
同时,每个超级项目提交都有两个项目:
- 一个
.gitmodules
文件,列出子模块的名称和任何配置数据,以及克隆它的说明if/when;和
- a gitlink 用于子模块。
gitlink 包含指令:此提交要求 将子模块 S 作为提交哈希检出 hash-value
。在下面一个有趣的地方,我们有机会使用或忽略这个哈希值,但现在请注意,每个提交实际上都在说:我需要一个克隆,在那个克隆中,我需要一个特定提交,通过其哈希 ID。
克隆子模块存储库
要克隆一个子模块,我们需要它的 URL。我们将 运行:
git clone $url $path
或者也许:
git clone --depth $N --no-single-branch $url $path
或类似的。 URL 和路径是最重要的部分。它们在 .gitmodules
文件中,但那不是 Git 想要它们的地方:Git 想要它们在 Git 存储库的配置文件中。
运行 git submodule init
将数据从 .gitmodules
文件复制到 Git 需要的位置。否则这个命令不会做任何有趣的事情,真的。似乎没有人使用它,因为 git submodule update --init
每次都会为您这样做。存在单独的 init
命令,因此您可以像文档中所说的那样“自定义 ... 子模块位置”(调整 URLs)。
运行git submodule update
(有无--remote
、--init
、and/or--depth
)会注意克隆是否存在。它确实需要 git submodule init
会保存的信息,所以如果您还没有完成 git submodule init
,您需要 --init
选项来实现它。如果子模块本身 丢失——如果超级项目还没有子模块的克隆——git submodule update
现在将 运行 git clone
。它实际上是 运行s git clone
的子模块助手;参见 line 558 ff.,尽管行号无疑会在未来的 Git 版本中改变。
注意这些 git clone
:
- 如果您使用
--depth
,它会得到一个 --depth
参数。
- 如果它得到一个
--depth
参数,它默认设置 --single-branch
,除非你使用 --no-single-branch
.
- 它为子模块创建实际的存储库,但它总是被告知
--no-checkout
所以它从不执行任何提交的初始 git checkout
。
- 它 永远不会得到
-b
/ --branch
参数 。这让我感到惊讶,而且可能是错误的,但请参阅 clone_submodule
in the submodule--helper.c
source.
现在,将第 2 项与第 4 项合并。使用 --depth
进行克隆意味着 --single-branch
,这会将子模块存储库设置为:
remote.origin.fetch=+refs/heads/<name>:refs/remotes/origin/<name>
作为其 pre-configured fetch
设置。但是 Git 没有在这里提供分支名称 所以默认的 name
是 [=254 推荐的=]other Git,即您正在克隆的 Git。这不是你自己配置的任何名字,在你的超级项目中。
在 git submodule update --init
行上使用 --no-single-branch
会强制进行克隆 无 --single-branch
模式。这让你 --depth
从 所有 分支的提示提交中提交,并将 fetch
行配置为:
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
这样您的子模块存储库中就会包含所有分支名称(加上 depth-50,或者您指定的任何深度,都可以从这些名称访问提交)。或者,正如我在顶部提到的,此时您可以在子模块中使用 git config
来修复 remote.origin.fetch
设置。
检查正确的提交
一旦我们有了克隆,剩下的任务就是 运行 子模块中的正确 git checkout
或(其他 Git 命令)。即:
(cd $path; git $command)
命令,我们现在有了子模块的路径work-tree;我们只需要找到一个哈希 ID 和 运行 git checkout
在该哈希 ID 上。
哈希 ID 存储在 gitlink 中。通常,这就是 Git 在这里使用的内容。但是,使用 --remote
,git submodule
脚本现在将 运行 子模块助手找出“正确的”分支名称。也就是说,子模块助手将找到您配置的名称,如果您配置了一个,或者使用超级项目的分支名称,如果您没有。
请注意,这已经相当晚了:子模块已经被克隆,并且已经将其 remote.origin.fetch
设置为 其他名称 。 (除非,也许,你是 luky:也许另一个 Git 推荐了 相同的 名称,您将在这里使用 --remote
。但可能不是。)
这是我在上面链接的那些源代码行中有趣的代码:
# enter here with:
# $sm_path: set to the submodule path
# $sha1: set to the hash from the gitlink
# $just_cloned: a flag set to 1 if we just ran `git clone`
if test $just_cloned -eq 1
then
subsha1= # i.e., set this to the empty string
else
subsha1=(...find hash ID that is currently checked out...)
fi
if test -n "$remote"
then
branch=(...find the branch you want...)
... fetch_in_submodule "$sm_path" $depth ...
sha1=(...use git rev-parse to find the hash ID for origin/$branch...)
fi
if test "$subsha1" != "$sha1" || test -n "$force"; then
... do stuff to the submodule ...
... in this case, git checkout -q $sha1 ...
fi
(我省略了一些不相关的部分并替换了一些 $(...)
部分,其中描述了它们所做的事情,而不是实际代码)。
所有这些工作的目的是:
子模块存储库通常处于 分离 HEAD 模式,通过哈希 ID 检出一个特定的提交。即使它处于另一种模式——在一个分支上,或者使用明显相反的附加 HEAD 模式——它仍然有一个特定的提交哈希 ID 被检出。
(这里唯一真正的例外是在初始克隆之后,实际上什么都没有签出。)
subsha1
代码部分计算出这是哪个哈希 ID。
代码的其余部分确定应该 检出哪个哈希 ID 。使用 --remote
选项,您告诉超级项目 Git:完全忽略 gitlink 设置。所有其他选项都使用 gitlink 设置,其中 any 可能会导致 --depth 1
.
出现问题
这里触发了你的错误信息
您正在使用 --remote
告诉您的超级项目 Git:忽略 gitlink 哈希 ID。这使用 branch=(...)
然后 sha1=(...)
赋值来覆盖 gitlink 哈希 ID。
那个sha1=
赋值字面上就是这段代码:
sha1=$(sanitize_submodule_env; cd "$sm_path" &&
git rev-parse --verify "${remote_name}/${branch}") ||
die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '$sm_path'")"
在这里您会发现收到的错误消息:
Unable to find current origin/version/3.2.0-era revision in submodule path '...'
现在,一个 git fetch
命令 should,一个人可能希望,已经获取了 commit 命名的 branch-nameversion/3.2.0-era
。如果它确实获取了该提交,人们会希望它会更新正确的 remote-tracking 名称,在本例中为 origin/version/3.2.0-era
.
然而,唯一的候选 git fetch
命令是由以下命令调用的:
fetch_in_submodule "$sm_path" $depth
此命令 运行s git fetch
带有您提供的 --depth
参数。它不提供任何分支名称! 其他 fetch_in_submodule
调用,特别是 this one on line 628,提供一个 原始散列 ID(仍然不是分支名称),但是这个仅在您提供 --depth
参数时提供。
没有 refspec,例如分支名称,git fetch origin
只获取 remote.origin.fetch
中配置的任何内容。这是来自 other Git.
的名称
如果 fetch=
设置 没有 获取所需的分支名称 - 并且使用 single-branch 克隆,这很可能在这里 - git fetch
不会获取我们想要的提交,随后 git rev-parse
将 remote-tracking 名称 origin/$branch
转换为哈希 ID 将失败。这就是您看到的错误。
我不会尝试说出 bug 的确切位置——因此,如何修复它,根据设置正确的配置 and/or 发出带有适当参数的 git fetch
——在这里,但显然 current Git 设置不适用于您的情况。不过,最终 Git 在这里尝试做的是 找到正确的 OID,或者在这种情况下,找不到它。
找到正确的 OID——针对你的特定情况使用 git rev-parse origin/version/3.2.0-era
——你的超级项目 Git 将 运行:
(cd $path; git checkout $hash)
在子模块中,留下一个分离的 HEAD 指向您通过 branch-name 请求的相同哈希 ID。解决问题后,您 将 处于 commit-by-OID detached-HEAD 模式。 唯一的 方法是手动的:你必须自己做 (cd $path; git checkout branch-name)
操作。
如果你不使用git submodule update --remote
——如果你有CI系统构建超级项目存储库说要构建的提交,而不是而不是依赖于其他人控制下的某个分支名称——浅克隆 必须 在 git fetch
之后包含该提交。这就是深度问题的脆弱之处:N 应该有多深?没有正确答案,这就是为什么你必须自己设置它。
如果将 origin
Git 配置为 uploadpack.allowReachableSHA1InWant
或 uploadpack.allowAnySHA1InWant
设置为 true
,则 git fetch
-by-hash-ID 可以获取任意提交,允许 --depth 1
工作,但您需要控制 origin
Git 存储库才能执行此操作(请参阅 [=127= 中的注意事项) ] 关于这些设置)。
您好,我有一个包含子模块的超级项目。子模块由分支名称而不是 sha 提交号跟踪。在我们的构建服务器上,我想尽可能少地拉动。所以我尝试了
git submodule update --remote --init
但这并不肤浅。好像把所有东西都拉出来然后切换到 branch
git submodule update --remote --init --depth 1
这行不通,它失败了:
git submodule update --remote --init --depth 1 ThirdParty/protobuf
Submodule 'ThirdParty/protobuf' (ssh://myrepo/thirdparty/protobuf.git)
registered for path 'ThirdParty/protobuf'
Cloning into '/home/martin/jenkins/workspace/test_log_service/repo/ThirdParty/protobuf'...
fatal: Needed a single revision
Unable to find current origin/version/3.2.0-era revision in submodule path 'ThirdParty/protobuf'
关于浅层子模块有一个不同的问题,但是我没有看到它适用于分支,只适用于 sha 提交
TL;DR
我认为您遇到了 Git 中的错误。要解决此问题,请使用 --no-single-branch
或手动配置分支。
其他需要知道的事情:
如果您有递归子模块,请确保您的 Git 是最新的,并使用
--recommend-shallow
递归启用浅层子模块,或使用--no-recommend-shallow
禁用它们。您可能需要分两步完成。我将在下面将其显示为 two-step 序列。我知道这段代码在 Git 1.7 和当前(2.26 左右)Git 之间发生了很大变化,我希望 two-step 序列也适用于大多数旧版本。
两步是:
N=... # set your depth here, or expand it in the two commands
git submodule update --init --depth $N --no-single-branch
git submodule update --remote --depth $N
Git 人员最近一直在修复各种 shallow-clone 子模块错误,作为添加 --recommend-shallow
递归子模块的一部分,因此这可能作为一个命令全部起作用。根据下面的分析,它应该在当前Git中都作为一个命令工作。但是,--no-single-branch
比 --single-branch
获取更多的对象。
另一个选项可能是允许 single-branch 模式但修复子模块中的 fetch
refspec。这需要三个步骤——嗯,三个单独的 Git 命令,无论如何:
branch=... # set this to the branch you want
git submodule update --init --depth $N
(cd path/to/submodule &&
git config remote.origin.fetch +refs/heads/$branch:refs/remotes/origin/$branch)
git submodule update --remote --depth $N
(您可以在所有子模块中使用 git submodule foreach
执行此操作,但请记住选择正确的分支名称 per-submodule。)
总的来说——这不是你的错误所特有的——我建议避免浅层子模块:它们往往不能很好地工作。如果您真的想使用它们,请使用 pretty-big 深度:例如,50、100 或更多。根据您自己的存储库和需求调整它。 (您当前的设置确实允许 --depth 1
,前提是您解决了其他问题。)
长:这可能是 Git
中的错误请注意,以下分析均基于源代码。我还没有真正测试过这个,所以我可能错过了一些东西。不过,这些原则都是合理的。
所有 子模块总是“sha 提交”,或者可能是“sha1”提交——Git 用于称呼它们那,但现在称它们为 OID,其中 OID 代表对象 ID。未来 Git 可能会使用 SHA-2。1 因此,如果希望避免 TLA 综合症,请使用“OID”或“哈希 ID”,2当然是一个更好的术语。所以让我这样说吧:所有子模块都使用 OID / hash-ID commits.
“所有子模块始终使用 OID/哈希 ID”是什么意思?嗯,这是浅层子模块的关键之一。浅子模块本质上是脆弱的,要 Git 在所有情况下都正确使用它们是很棘手的。此声明:
The submodule is tracked by a branch name and not by a sha commit number.
在一个重要方面是错误的。无论您多么努力,子模块——或者更准确地说,子模块提交——都会被哈希 ID 跟踪。
现在,子模块中确实有个分支名称参与克隆和获取。当您将 --shallow
与子模块一起使用时,这会变得 非常 重要,因为大多数服务器不允许 hash-ID 获取(附注,2021 年 1 月:this正在发生变化,因为 Git 中的一些新功能需要它——GitHub 已经允许通过 ID 获取——所以随着时间的推移,这种情况应该会有所改善)。您选择的深度——以及单个分支名称,因为 --depth
意味着 --single-branch
——因此必须足够深以达到 commit 超级项目 Git选择。
如果您覆盖 Git 的hash-ID 跟踪子模块提交跟踪,您可以绕过一个脆弱性问题。这就是你正在做的,但你遇到了一个错误。
1那不是很有趣吗? Git 在很大程度上取决于每个提交都有一个唯一的 OID;引入新的 OID 命名空间,因此每个 Git 都有 两个 OID,每个 OID 在其命名空间内都是唯一的,这意味着提交 不会 必须具有 适当的 OID。所有协议都变得更加复杂:任何仅支持旧方案的 Git 都需要(单个)OID 的 SHA-1 哈希,而任何使用新方案的 Git 都需要 SHA- 2 哈希,可能连同 SHA-1 哈希一起提供给旧的 Gits。一旦我们有了这个对象,我们就可以用它来计算 其他散列,但是如果我们只有两个散列之一,它需要是正确的。
处理这个问题的直接方法是将计算“其他人的散列”的负担放在具有对象的 Git 上,如果对象存在于使用不同的存储库中OID 命名空间。但是 SHA-1 Gits 不能改变,所以我们不能使用那个方法。负担必须放在新的 SHA-2 Gits.
上2请注意,“SHA”本身是一个 TLA:三个字母的首字母缩写词。 TLAS 代表 TLA 综合症,是一个 ETLA:扩展的三字母首字母缩写词。
一个超级项目如何 Git ch设置一个子模块 Git commit?
git submodule
命令是 currently still a big shell script,但它的大部分操作使用 C 语言帮助程序。虽然它是一个复杂的 shell 脚本,但它的核心是 运行:
(cd $path && git $command)
为了在每个子模块中做事。 $path
是子模块的路径,$command
是该子模块内 运行 的命令。
虽然这里有一些 chicken-and-egg 东西,因为 $path
最初只是一个空目录:在克隆超级项目之后还没有实际的 clone .在 是 克隆之前,任何 Git 命令都不起作用!好吧,除了 git clone
本身,什么也没有,就是
同时,每个超级项目提交都有两个项目:
- 一个
.gitmodules
文件,列出子模块的名称和任何配置数据,以及克隆它的说明if/when;和 - a gitlink 用于子模块。
gitlink 包含指令:此提交要求 将子模块 S 作为提交哈希检出 hash-value
。在下面一个有趣的地方,我们有机会使用或忽略这个哈希值,但现在请注意,每个提交实际上都在说:我需要一个克隆,在那个克隆中,我需要一个特定提交,通过其哈希 ID。
克隆子模块存储库
要克隆一个子模块,我们需要它的 URL。我们将 运行:
git clone $url $path
或者也许:
git clone --depth $N --no-single-branch $url $path
或类似的。 URL 和路径是最重要的部分。它们在 .gitmodules
文件中,但那不是 Git 想要它们的地方:Git 想要它们在 Git 存储库的配置文件中。
运行 git submodule init
将数据从 .gitmodules
文件复制到 Git 需要的位置。否则这个命令不会做任何有趣的事情,真的。似乎没有人使用它,因为 git submodule update --init
每次都会为您这样做。存在单独的 init
命令,因此您可以像文档中所说的那样“自定义 ... 子模块位置”(调整 URLs)。
运行git submodule update
(有无--remote
、--init
、and/or--depth
)会注意克隆是否存在。它确实需要 git submodule init
会保存的信息,所以如果您还没有完成 git submodule init
,您需要 --init
选项来实现它。如果子模块本身 丢失——如果超级项目还没有子模块的克隆——git submodule update
现在将 运行 git clone
。它实际上是 运行s git clone
的子模块助手;参见 line 558 ff.,尽管行号无疑会在未来的 Git 版本中改变。
注意这些 git clone
:
- 如果您使用
--depth
,它会得到一个--depth
参数。 - 如果它得到一个
--depth
参数,它默认设置--single-branch
,除非你使用--no-single-branch
. - 它为子模块创建实际的存储库,但它总是被告知
--no-checkout
所以它从不执行任何提交的初始git checkout
。 - 它 永远不会得到
-b
/--branch
参数 。这让我感到惊讶,而且可能是错误的,但请参阅clone_submodule
in thesubmodule--helper.c
source.
现在,将第 2 项与第 4 项合并。使用 --depth
进行克隆意味着 --single-branch
,这会将子模块存储库设置为:
remote.origin.fetch=+refs/heads/<name>:refs/remotes/origin/<name>
作为其 pre-configured fetch
设置。但是 Git 没有在这里提供分支名称 所以默认的 name
是 [=254 推荐的=]other Git,即您正在克隆的 Git。这不是你自己配置的任何名字,在你的超级项目中。
在 git submodule update --init
行上使用 --no-single-branch
会强制进行克隆 无 --single-branch
模式。这让你 --depth
从 所有 分支的提示提交中提交,并将 fetch
行配置为:
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
这样您的子模块存储库中就会包含所有分支名称(加上 depth-50,或者您指定的任何深度,都可以从这些名称访问提交)。或者,正如我在顶部提到的,此时您可以在子模块中使用 git config
来修复 remote.origin.fetch
设置。
检查正确的提交
一旦我们有了克隆,剩下的任务就是 运行 子模块中的正确 git checkout
或(其他 Git 命令)。即:
(cd $path; git $command)
命令,我们现在有了子模块的路径work-tree;我们只需要找到一个哈希 ID 和 运行 git checkout
在该哈希 ID 上。
哈希 ID 存储在 gitlink 中。通常,这就是 Git 在这里使用的内容。但是,使用 --remote
,git submodule
脚本现在将 运行 子模块助手找出“正确的”分支名称。也就是说,子模块助手将找到您配置的名称,如果您配置了一个,或者使用超级项目的分支名称,如果您没有。
请注意,这已经相当晚了:子模块已经被克隆,并且已经将其 remote.origin.fetch
设置为 其他名称 。 (除非,也许,你是 luky:也许另一个 Git 推荐了 相同的 名称,您将在这里使用 --remote
。但可能不是。)
这是我在上面链接的那些源代码行中有趣的代码:
# enter here with:
# $sm_path: set to the submodule path
# $sha1: set to the hash from the gitlink
# $just_cloned: a flag set to 1 if we just ran `git clone`
if test $just_cloned -eq 1
then
subsha1= # i.e., set this to the empty string
else
subsha1=(...find hash ID that is currently checked out...)
fi
if test -n "$remote"
then
branch=(...find the branch you want...)
... fetch_in_submodule "$sm_path" $depth ...
sha1=(...use git rev-parse to find the hash ID for origin/$branch...)
fi
if test "$subsha1" != "$sha1" || test -n "$force"; then
... do stuff to the submodule ...
... in this case, git checkout -q $sha1 ...
fi
(我省略了一些不相关的部分并替换了一些 $(...)
部分,其中描述了它们所做的事情,而不是实际代码)。
所有这些工作的目的是:
子模块存储库通常处于 分离 HEAD 模式,通过哈希 ID 检出一个特定的提交。即使它处于另一种模式——在一个分支上,或者使用明显相反的附加 HEAD 模式——它仍然有一个特定的提交哈希 ID 被检出。
(这里唯一真正的例外是在初始克隆之后,实际上什么都没有签出。)
subsha1
代码部分计算出这是哪个哈希 ID。代码的其余部分确定应该 检出哪个哈希 ID 。使用
出现问题--remote
选项,您告诉超级项目 Git:完全忽略 gitlink 设置。所有其他选项都使用 gitlink 设置,其中 any 可能会导致--depth 1
.
这里触发了你的错误信息
您正在使用 --remote
告诉您的超级项目 Git:忽略 gitlink 哈希 ID。这使用 branch=(...)
然后 sha1=(...)
赋值来覆盖 gitlink 哈希 ID。
那个sha1=
赋值字面上就是这段代码:
sha1=$(sanitize_submodule_env; cd "$sm_path" &&
git rev-parse --verify "${remote_name}/${branch}") ||
die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '$sm_path'")"
在这里您会发现收到的错误消息:
Unable to find current origin/version/3.2.0-era revision in submodule path '...'
现在,一个 git fetch
命令 should,一个人可能希望,已经获取了 commit 命名的 branch-nameversion/3.2.0-era
。如果它确实获取了该提交,人们会希望它会更新正确的 remote-tracking 名称,在本例中为 origin/version/3.2.0-era
.
然而,唯一的候选 git fetch
命令是由以下命令调用的:
fetch_in_submodule "$sm_path" $depth
此命令 运行s git fetch
带有您提供的 --depth
参数。它不提供任何分支名称! 其他 fetch_in_submodule
调用,特别是 this one on line 628,提供一个 原始散列 ID(仍然不是分支名称),但是这个仅在您提供 --depth
参数时提供。
没有 refspec,例如分支名称,git fetch origin
只获取 remote.origin.fetch
中配置的任何内容。这是来自 other Git.
如果 fetch=
设置 没有 获取所需的分支名称 - 并且使用 single-branch 克隆,这很可能在这里 - git fetch
不会获取我们想要的提交,随后 git rev-parse
将 remote-tracking 名称 origin/$branch
转换为哈希 ID 将失败。这就是您看到的错误。
我不会尝试说出 bug 的确切位置——因此,如何修复它,根据设置正确的配置 and/or 发出带有适当参数的 git fetch
——在这里,但显然 current Git 设置不适用于您的情况。不过,最终 Git 在这里尝试做的是 找到正确的 OID,或者在这种情况下,找不到它。
找到正确的 OID——针对你的特定情况使用 git rev-parse origin/version/3.2.0-era
——你的超级项目 Git 将 运行:
(cd $path; git checkout $hash)
在子模块中,留下一个分离的 HEAD 指向您通过 branch-name 请求的相同哈希 ID。解决问题后,您 将 处于 commit-by-OID detached-HEAD 模式。 唯一的 方法是手动的:你必须自己做 (cd $path; git checkout branch-name)
操作。
如果你不使用git submodule update --remote
——如果你有CI系统构建超级项目存储库说要构建的提交,而不是而不是依赖于其他人控制下的某个分支名称——浅克隆 必须 在 git fetch
之后包含该提交。这就是深度问题的脆弱之处:N 应该有多深?没有正确答案,这就是为什么你必须自己设置它。
如果将 origin
Git 配置为 uploadpack.allowReachableSHA1InWant
或 uploadpack.allowAnySHA1InWant
设置为 true
,则 git fetch
-by-hash-ID 可以获取任意提交,允许 --depth 1
工作,但您需要控制 origin
Git 存储库才能执行此操作(请参阅 [=127= 中的注意事项) ] 关于这些设置)。