如何通过分支机构名称跟踪的浅拉子模块 [英] How to shallow pull submodule that is tracked by branch name

查看:5
本文介绍了如何通过分支机构名称跟踪的浅拉子模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

嗨,我有一个包含子模块的超级项目。该子模块通过分支机构名称而不是通过SHA提交号来跟踪。在我们的构建服务器上,我想拉得尽可能少。所以我试了试

git submodule update --remote --init 

然而,这并不肤浅。看起来好像把所有东西都拉出来了,然后切换到分支机构

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禁用它们。

  • 可能需要分两步完成此操作。下面我将以两个步骤的顺序来展示这一点。我知道这段代码在Git1.7和当前(2.26左右)的Git之间有了很大的发展,我希望这个两步序列也能适用于大多数较老的版本。

这两个步骤是:

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人员最近一直在修复各种浅克隆子模块错误,作为添加--recommend-shallow递归子模块的一部分,因此这些都可以作为一个命令工作。根据下面的分析,在当前的Git中,它都应该都作为一个命令工作。但是,--no-single-branch提取的对象多于--single-branch

另一种选择可能是允许单分支模式,但修改子模块中的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在所有子模块中执行此操作,但请记住为每个子模块选择正确的分支名称。)

总的来说-这不是针对您的错误-我建议避免浅子模块:它们往往不能很好地工作。如果你真的想使用它们,使用一个相当大的深度:例如,50,或100,或更多。根据您自己的存储库和需求对其进行调整。(您当前的设置允许--depth 1,前提是您解决了另一个问题。)

Long:可能是Git中的错误

请注意,下面的分析基于源代码。我实际上还没有测试过这个,所以我可能遗漏了一些东西。不过,这些原则都是合理的。

所有子模块都是总是总是所有子模块都是总是所有子模块都是总是>所有子模块都是总是>所有子模块都是总是所有子模块都是总是<这么说吧:所有子模块都使用OID/HASH-ID提交。

我所说的所有子模块始终使用OID/散列ID是什么意思?这是浅子模的关键之一。浅子模块本质上是脆弱的,要让Git在所有情况下都正确使用它们是很困难的。此声明:

该子模块由分支名称跟踪,而不是由SHA提交号跟踪。

是错误的,在一个重要的方面。无论您多么努力,子模块--或者更准确地说,子模块提交--都会被哈希ID跟踪。

现在,确实有分支名称涉及到子模块中的克隆和获取。当您对子模块使用--shallow时,这可能会变得非常,因为大多数服务器不允许按Hash-ID获取(旁注,2021年1月:这种情况正在改变,因为Git中的一些新功能需要它--GitHub已经允许按ID获取--所以随着时间的推移,这种情况应该会改善)。因此,您选择的深度--以及单个分支名称,因为--depth暗示--single-branch--必须足够深,才能达到Git选择的提交

如果用子模块覆盖Git-by-Hash-ID提交跟踪,则可以绕过一个脆弱性问题。这就是你正在做的,但你碰到了一个错误。


1,这不是很有趣吗?Git在很大程度上依赖于每个提交都有唯一的OID;引入了新的OID命名空间,这样每个Git都有两个OID,每个OID在其命名空间中都是唯一的,这意味着提交不一定具有适当的OID。所有的协议都变得更加复杂:任何只支持旧方案的Git都需要(单个)OID的SHA-1散列,而任何使用新方案的Git都需要SHA-2散列,可能还会连同SHA-1散列一起提供给旧的Git。一旦我们有了对象,我们就可以使用它来计算另一个哈希,但如果我们只有两个哈希中的一个,则它需要是正确的一个。

处理这一问题的直接方法是将计算其他人的散列的负担放在具有该对象的Git上,如果对象存在于使用不同OID命名空间的存储库中。但SHA-1 Gits不能改变,所以我们不能使用这种方法。负担必须落在新的SHA-2 Git身上。

2请注意,";SHA";本身就是TLA:三个字母的缩写。TLAS代表TLA综合征,是ETLA:一个扩展的三个字母的首字母缩写。😀


超级项目Git如何选择子模块Git Commit?

git submodule命令是currently still a big shell script,但它的大部分操作使用C语言助手。虽然它是一个复杂的外壳脚本,但它的核心是运行:

(cd $path && git $command)

以便在每个子模块中执行操作。$path是子模块的路径,$command是在该子模块中运行的命令。

这里有一些鸡和蛋的东西,因为$path最初只是一个空目录:在克隆超级项目之后还没有实际的克隆。在出现克隆之前,任何Git命令都不起作用!嗯,除了git clone本身,什么都没有。

同时,每个超级项目提交有两项:

  • 一个.gitmodules文件,列出子模块的名称和任何配置数据,以及在需要时克隆它的说明;和
  • 子模块的Gitlink

Gitlink包含指令:This Commit要求子模块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命令,这样您就可以,正如文档中所说的那样,定制...子模块位置(调整URL)。

运行git submodule update(带或不带--remote--init和/或--depth)将注意到克隆是否存在。它确实需要git submodule init将保存的信息,所以如果您还没有执行git submodule init,则需要--init选项来实现。如果子模块本身丢失-如果超级项目还没有子模块的克隆-git submodule update现在将运行git clone。它实际上是运行git clone的子模块帮助器;请参阅line 558ff.,尽管行号无疑会在未来的Git版本中更改。

注意以下事项git clone

  1. 如果使用--depth,则它将获得--depth参数。
  2. 如果它确实获得了--depth参数,则默认设置为--single-branch,除非您使用--no-single-branch
  3. 它为子模块创建实际的存储库,但它总是被告知--no-checkout,因此它永远不会执行任何提交的初始git checkout
  4. 从不获取-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>

作为其预配置的fetch设置。但是Git在这里没有提供分支名称,因此默认的name其他Git推荐的名称,即您要克隆的Git。它不是您在超级项目中自己配置的任何名称。

git submodule update --init行上使用--no-single-branch强制在不使用--single-branch模式下进行克隆。这将从所有分支的提示提交中获取--depth提交,并将fetch行配置为:

remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*

以致子模块存储库中包含所有分支名称(加上深度-50,或者您指定的深度,可以从这些名称访问提交)。或者,正如我在顶部提到的,您可以在子模块中使用git config,在这一点上修复remote.origin.fetch设置。

正在签出正确的提交

一旦有了克隆,剩下的任务就是运行子模块中的右git checkout或(Other Git命令)。即:

(cd $path; git $command)

命令,我们现在有了子模块工作树的路径;我们所需要的就是找到一个散列ID并对该散列ID运行git checkout

散列ID存储在Gitlink中。通常,Git在这里会用到这一点。不过,使用--remote时,git submodule脚本现在将运行子模块帮助器以确定";right";分支名称。也就是说,子模块帮助器将找到您配置的名称(如果您配置了一个名称),或者使用超级项目的分支名称(如果您没有配置)。

注意,这是相当晚的:子模块已经被克隆,并且已经将其remote.origin.fetch设置为其他名称。(除非您很幸运:也许其他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

(我省略了一些不相关的部分,将一些$(...)部分替换为对它们功能的描述,而不是实际的代码)。

所有这些工作都是这样的:

  • 子模块存储库通常处于分离的头模式,其中一个特定的提交由哈希ID签出。即使它处于另一种模式--在分支上,或使用明显相反的附加头模式--它仍签出一个特定的提交哈希ID。

    (这里唯一真正的例外是在初始克隆之后,实际上没有签出任何东西。)

  • subsha1代码节确定这是哪个哈希ID。

  • 代码的其余部分确定应该签出哪个哈希ID。使用--remote选项,您可以告诉超级项目Git:完全忽略Gitlink设置。所有其他选项都使用Gitlink设置,任何都可能导致--depth 1出现问题。

此处触发您的错误消息

您正在使用--remote告诉您的超级项目Git:忽略Gitlink散列ID。它使用branch=(...)赋值,然后使用sha1=(...)赋值来覆盖Gitlink哈希ID。

<2-68]>赋值实际上是这样代码:
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命令应该已经获取了由分支名称version/3.2.0-era命名的提交。如果它确实获取了提交,人们会希望它已经更新了正确远程跟踪名称,在本例中为origin/version/3.2.0-era

但是,唯一的候选git fetch命令是由:

调用的命令
fetch_in_submodule "$sm_path" $depth
此命令使用您提供的--depth参数运行git fetch。它不提供任何分支名称!其他fetch_in_submodule调用,特别是this one on line 628,提供了原始散列ID(仍然不是分支名称),但这只提供了--depth参数(如果您提供了一个参数)。

没有refspec,例如分支名称,git fetch origin只获取remote.origin.fetch中配置的任何内容。这是来自其他Git的名称。

如果fetch=设置没有获取所需的分支名称--对于单分支克隆,这里很可能是这样--git fetch将不会获取我们想要的提交,并且将远程跟踪名称origin/$branch转换为散列ID的后续git rev-parse将失败。这就是您看到的错误。

我不打算在这里准确地说出错误在哪里-因此,如何修复它,就设置正确的配置和/或使用适当的参数发出git fetch-但很明显,当前的Git设置不适用于您的情况。不过,最终,Git在这里尝试做的是找到正确的OID,或者在本例中无法找到它。

找到正确的OID后-针对您的特定情况使用git rev-parse origin/version/3.2.0-era-然后您的超级项目Git将运行:

(cd $path; git checkout $hash)

在子模块中,留下一个分离的头部,指向您通过分支名称请求的相同散列ID。当您修复该问题时,处于这种按OID提交的分离头部模式。摆脱它的唯一方法是手动:您必须自己进行(cd $path; git checkout branch-name)操作。

如果您曾经不使用git submodule update --remote-如果您让您CI系统构建超级项目存储库要求构建的提交,而不是依赖于其他人控制的某个分支名称-浅克隆必须在git fetch之后包含提交。这就是深度问题的脆弱之处:N应该有多深?没有正确的答案,这就是为什么您必须自己设置它。

如果您将originGit配置为uploadpack.allowReachableSHA1InWantuploadpack.allowAnySHA1InWant设置为truegit fetch-by-hash-ID可以获取任意提交,从而允许--depth 1工作,但您需要控制originGit存储库才能执行此操作(请参阅the git config documentation中有关这些设置的注意事项)。

这篇关于如何通过分支机构名称跟踪的浅拉子模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆