繁体   English   中英

如何制作浅的 git 子模块?

[英]How to make shallow git submodules?

是否可以有浅子模块? 我有一个包含几个子模块的超级项目,每个子模块都有很长的历史,所以它不必要地拖着所有的历史。

我发现的只是 这个未答复的线程

我应该hack git-submodule来实现这个吗?

即将推出的git1.8.4(2013 年 7 月)中的新内容

git submodule update ”可以选择浅层克隆子模块存储库。

(并且 git 2.10 Q3 2016 允许使用git config -f .gitmodules submodule.<name>.shallow true
请参阅此答案的结尾)

提交 275cd184d52b5b81cb89e4ec33e540fb2ae61c1f

--depth选项添加到“git submodule”的添加和更新命令中,然后传递给克隆命令。 当子模块很大并且除了最新提交之外你对任何东西都不感兴趣时​​,这很有用。

添加了测试并进行了一些缩进调整以符合测试文件的其余部分“子模块更新可以处理密码中的符号链接”。

签字人:Fredrik Gustafsson <iveqy@iveqy.com>
确认者:Jens Lehmann <Jens.Lehmann@web.de>

这意味着这有效:

# add shallow submodule
git submodule add --depth 1 <repo-url> <path>
git config -f .gitmodules submodule.<path>.shallow true

# later unshallow
git config -f .gitmodules submodule.<path>.shallow false
git submodule update <path>

命令可以按任何顺序运行。 git submodule命令执行实际的克隆(这次使用深度 1)。 并且git config命令使该选项对于稍后将递归克隆 repo 的其他人永久存在。

例如,假设您有 repo https://github.com/foo/bar并且您想将https://github.com/lorem/ipsum作为子模块添加到您的 repo 中的path/to/submodule 命令可能如下所示:

git submodule add --depth 1 git@github.com:lorem/ipsum.git path/to/submodule
git config -f .gitmodules submodule.path/to/submodule.shallow true

以下结果也相同(相反的顺序):

git config -f .gitmodules submodule.path/to/submodule.shallow true
git submodule add --depth 1 git@github.com:lorem/ipsum.git path/to/submodule

下次有人运行git clone --recursive git@github.com:foo/bar.git ,它会拉入https://github.com/foo/bar的整个历史,但它只会浅克隆子模块如预期。

和:

--depth

此选项对addupdate命令有效。
创建一个“浅”克隆,其历史记录被截断为指定数量的修订。


atwyman 在评论中补充道:

据我所知,此选项不适用于不能非常密切地跟踪master模块的子模块。 如果您设置深度 1,则只有当您想要的子模块提交是最新的主模块时, submodule update才能成功。 否则你会得到“ fatal: reference is not a tree

那是真实的。
也就是说,直到 git 2.8(2016 年 3 月)。 在 2.8 中,即使 SHA1 可以从远程仓库 HEAD 之一直接访问, submodule update --depth也有一次成功的机会。

请参阅Stefan Beller ( stefanbeller ) 提交的 fb43e31 (24 Feb 2016 )
帮助者: Junio C gitster ( gitster )
(由Junio C gitster合并-- gitster -- in commit 9671a76 ,2016 年 2 月 26 日)

子模块:通过直接获取 sha1 更努力地获取所需的 sha1

在审查同时更新 Gerrit 中的子模块的更改时,常见的审查做法是在本地下载并挑选补丁进行测试。
但是,在本地测试时,“ git submodule update ”可能无法获取正确的子模块 sha1,因为子模块中的相应提交还不是项目历史的一部分,而只是提议的更改。

如果$sha1不是默认提取的一部分,我们尝试直接提取$sha1 但是有些服务器不支持通过 sha1 直接获取,这会导致git-fetch快速失败。
我们可以在这里失败,因为仍然缺少的 sha1 无论如何都会导致稍后在结帐阶段失败,所以在这里失败是我们所能得到的。


MVG 在评论中指出提交 fb43e31 (git 2.9,2016年 2 月)

在我看来, 提交 fb43e31通过 SHA1 id 请求丢失的提交,因此服务器上的uploadpack.allowReachableSHA1InWantuploadpack.allowTipSHA1InWant设置可能会影响这是否有效。
今天在 git list 上写了一篇文章,指出如何使用浅子模块在某些情况下更好地工作,即如果提交也是一个标签。
让我们等着看。

我想这就是 fb43e31 将特定 SHA1 的提取作为默认分支提取后的回退的原因。
尽管如此,在“--depth 1”的情况下,我认为提前中止是有意义的:如果列出的所有引用都与请求的引用都不匹配,并且服务器不支持 SHA1 请求,则没有意义获取任何东西,因为无论如何我们都无法满足子模块的要求。


2016 年 8 月更新(3 年后)

使用 Git 2.10(2016 年第三季度),您将能够做到

 git config -f .gitmodules submodule.<name>.shallow true

有关更多信息,请参阅“没有额外重量的 Git 子模块”。


Git 2.13(2017 年第二季度)确实添加了Sebastian Schuberth ( sschuberth ) 提交的 8d3047c (2017 年 4 月 19 日
(由Sebastian Schuberth -- sschuberth --commit 8d3047c 中合并,2017 年 4 月 20 日)

此子模块的克隆将作为浅克隆执行(历史深度为 1)

但是, Ciro Santilli 在评论中添加(以及他的回答中的详细信息)

.gitmodules上的shallow = true仅影响使用--recurse-submodules时远程的 HEAD 跟踪的引用,即使目标提交是由分支指向的,即使您将branch = mybranch放在.gitmodules作为好。


Git 2.20(2018 年第 4 季度)改进了子模块支持,当工作树中缺少.gitmodules文件时,子模块支持已更新为从HEAD:.gitmodules处的 blob 读取。

请参阅提交 2b1257e提交 76e9bdc (2018 年 10 月 25 日)和提交 b5c259f提交 23dd8f5提交b2faad4提交 2502ffc提交 996df4d提交 d1b13df提交 4820f5c ( Antonio 4520f5c ) 提交 4520f5c (2018 年 10 月 2018 ao2 )
(由Junio C gitster合并gitsterabb4824 提交中,2018 年 11 月 13 日)

submodule :当它不在工作树中时支持读取.gitmodules

.gitmodules文件在工作树中不可用时,尝试使用来自索引和当前分支的内容。
这涵盖了文件是存储库的一部分但由于某种原因未检出的情况,例如由于稀疏检出。

这使得至少可以使用“ git submodule ”命令来读取gitmodules配置文件,而无需完全填充工作树。

写入.gitmodules仍然需要检出文件,因此在调用config_set_in_gitmodules_file_gently之前检查它。

git-submodule.sh::cmd_add()也添加一个类似的检查,以预测当.gitmodules不能安全写入时“ git submodule add ”命令的最终失败; 这可以防止命令使存储库处于虚假状态(例如,子模块存储库被克隆但.gitmodules未更新,因为config_set_in_gitmodules_file_gently失败)。

此外,由于config_from_gitmodules()现在访问全局对象存储,因此有必要保护调用该函数的所有代码路径免受对全局对象存储的并发访问。
目前这只发生在builtin/grep.c::grep_submodules() ,所以在调用涉及config_from_gitmodules()代码之前调用grep_read_lock() config_from_gitmodules()

注意:有一种罕见的情况,这个新特性还不能正常工作:在工作树中没有.gitmodules嵌套子模块。


注意:Git 2.24(2019 年第四季度)修复了浅层克隆子模块时可能出现的段错误。

请参阅Ali Utku Selen ( auselen ) 提交的 ddb3c85 (2019 年 9 月 30 日)
(由Junio C gitster合并-- gitster -- in commit 678a9ca ,2019 年 10 月 9 日)


Git 2.25(2020 年第一季度),阐明了git submodule update文档。

请参阅Philippe Blain ( phil-blain blain ) 提交的 f0e58b3 (2019 年 11 月 24 日
(由Junio C gitster -- gitster --提交 ef61045 中合并,2019 年 12 月 5 日)

doc : 提到“git submodule update”获取丢失的提交

帮助者:Junio C Hamano
帮助者:约翰内斯·辛德林
签字人:Philippe Blain

如果未找到记录在超级项目中的 SHA-1, ' git submodule update'将从子模块远程获取新提交 这在文档中没有提到。


警告:在 Git 2.25(2020 年第一季度)中,“ git clone --recurse-submodules ”和备用对象存储之间的交互设计不当。

当用户看到失败时,文档和代码已经被教导来提出更明确的建议。

请参阅Jonathan Tan ( jhowtan ) 的commit 4f3e57ecommit 10c64a0 (2019 年 12 月 2 日
(由Junio C gitster合并-- gitster -- in commit 5dd1d59 ,2019 年 12 月 10 日)

submodule--helper :就致命的替代错误提供建议

签字人:Jonathan Tan
确认者:杰夫·金

当递归克隆具有在其.gitmodules定义的一些浅层模块的超级.gitmodules ,然后使用“ --reference=<path> ”重新克隆时,会发生错误。 例如:

 git clone --recurse-submodules --branch=master -j8 \\
  https://android.googlesource.com/platform/superproject \
  master
git clone --recurse-submodules --branch=master -j8 \
  https://android.googlesource.com/platform/superproject \
  --reference master master2

失败:

 fatal: submodule '<snip>' cannot add alternate: reference repository
'<snip>' is shallow

当无法添加从超级项目的替代计算的替代时,无论是在这种情况下还是其他情况,建议在克隆时配置“ submodule.alternateErrorStrategy ”配置选项并使用“ --reference-if-able ”而不是“ --reference ” .

这在:

在 Git 2.25(2020 年第一季度)中,“git clone --recurse-submodules”和备用对象存储之间的交互设计不当。

Doc :解释 submodule.alternateErrorStrategy

签字人:Jonathan Tan
确认者:杰夫·金

提交31224cbdc7 (“ clone :递归和引用选项触发子模块替代”,2016 年 8 月 17 日,Git v2.11.0-rc0 -- 合并批处理 #1 中列出)教导 Git 支持配置选项“ submodule.alternateLocation ”和“ submodule.alternateErrorStrategy " 在一个超级项目上。

如果在superproject上将“ submodule.alternateLocation ”配置为“ superproject ”,则无论何时克隆该超级项目的子模块,它都会从超级项目的$GIT_DIR/objects/info/alternates和引用中计算该子模块的类似替代路径它。

submodule.alternateErrorStrategy ”选项决定了如果无法引用该替代品会发生什么。
但是,当该选项未设置为“死”时,克隆是否会像未指定替代项一样继续进行(如31224cbdc7中的测试所示),这一点尚不清楚。
因此,相应地记录它。

配置子模块文档现在包括:

 submodule.alternateErrorStrategy::

指定如何处理通过submodule.alternateLocation计算的子模块的替代错误。
可能的值是ignoreinfodie
默认为die
请注意,如果设置为ignoreinfo ,并且如果计算的替代项存在错误,则克隆继续进行,就像未指定替代项一样


注意:“ git submodule update --quiet( man )没有将 quiet 选项向下传播到底层git fetch ( man ) ,这已在 Git 2.32(2021 年第二季度)中得到纠正。

请参阅Nicholas Clark ( nwc10 ) 的commit 62af4bd (2021 年 4 月 30 日
(由Junio C gitster合并-- gitster -- in commit 74339f8 ,2021 年 5 月 11 日)

submodule update :使用“ --quiet ”静默底层获取

签字人:尼古拉斯·克拉克

命令如

$ git submodule update --quiet --init --depth=1

涉及浅克隆,调用 shell 函数fetch_in_submodule,它依次调用git fetch
在那里传递--quiet选项。

Git 2.9.0直接支持子模块浅克隆,所以现在你可以调用:

git clone url://to/source/repository --recursive --shallow-submodules

按照Ryan 的回答,我想出了这个简单的脚本,它遍历所有子模块并浅层克隆它们:

#!/bin/bash
git submodule init
for i in $(git submodule | sed -e 's/.* //'); do
    spath=$(git config -f .gitmodules --get submodule.$i.path)
    surl=$(git config -f .gitmodules --get submodule.$i.url)
    git clone --depth 1 $surl $spath
done
git submodule update

通读 git-submodule “source”,看起来git submodule add可以处理已经存在其存储库的子模块。 在这种情况下...

$ git clone $remote1 $repo
$ cd $repo
$ git clone --depth 5 $remotesub1 $sub1
$ git submodule add $remotesub1 $sub1
#repeat as necessary...

您需要确保所需的提交位于子模块存储库中,因此请确保设置适当的 --depth。

编辑:您可以使用多个手动子模块克隆,然后进行一次更新:

$ git clone $remote1 $repo
$ cd $repo
$ git clone --depth 5 $remotesub1 $sub1
#repeat as necessary...
$ git submodule update

自 Git 2.14.1 起的错误/意外/烦人行为摘要

  1. 如果远程子模块的HEAD指向所需的提交,则.gitmodules shallow = true仅影响git clone --recurse-submodules ,即使目标提交是由分支指向的,并且即使您将branch = mybranch放在.gitmodules也是如此。

    本地测试脚本 GitHub 2017-11 上的相同行为,其中HEAD由默认分支存储库设置控制:

     git clone --recurse-submodules https://github.com/cirosantilli/test-shallow-submodule-top-branch-shallow cd test-shallow-submodule-top-branch-shallow/mod git log # Multiple commits, not shallow.
  2. git clone --recurse-submodules --shallow-submodules如果提交既没有被分支引用,也没有被带有消息的标记引用,则git clone --recurse-submodules --shallow-submodules失败: error: Server does not allow request for unadvertised object

    本地测试脚本 GitHub 上的相同行为:

     git clone --recurse-submodules --shallow-submodules https://github.com/cirosantilli/test-shallow-submodule-top-sha # error

    我也在邮件列表上问过: https : //marc.info/? l =git&m=151863590026582&w=2 ,回复是:

    理论上这应该很容易。 :)

    不幸的是,实际上并没有那么多。 这是因为克隆只会获得分支(通常是 master)的最新提示。 clone 中没有机制来指定所需的确切 sha1。

    有线协议支持询问确切的 sha1,因此应该涵盖这一点。 (注意:只有服务器运营商启用了uploadpack.allowReachableSHA1InWant,github没有AFAICT,它才有效)

    git-fetch 允许获取任意 sha1,因此作为一种解决方法,您可以使用“git submodule update”在递归克隆之后运行获取,因为这将在初始克隆后使用获取。

TODO 测试: allowReachableSHA1InWant

您的子模块的规范位置是否远程? 如果是这样,你可以克隆它们一次吗? 换句话说,您是否想要浅层克隆只是因为您正在遭受频繁子模块(重新)克隆浪费的带宽?

如果您希望浅克隆来节省本地磁盘空间,那么 Ryan Graham 的答案似乎是一个不错的方法。 手动克隆存储库,使它们变浅。 如果您认为它有用,请调整git submodule以支持它。 列表发送一封电子邮件,询问它(实现它的建议、界面建议等)。 在我看来,那里的人们非常支持那些热切希望以建设性方式增强 Git 的潜在贡献者。

如果你对每个子模块做一个完整的克隆(加上以后的获取以保持它们最新),你可以尝试使用git submodule update--reference选项(它在 Git 1.6.4 和更高版本中)来引用到本地对象存储(例如,制作规范子模块存储库的--mirror克隆,然后在子模块中使用--reference指向这些本地克隆)。 在使用--reference之前,请务必阅读有关git clone --reference / git clone --shared --reference 引用镜像唯一可能的问题是它们是否最终获取非快进更新(尽管您可以启用引用日志并扩展其过期窗口以帮助保留任何可能导致问题的放弃提交)。 你应该没有任何问题,只要

  • 您没有进行任何本地子模块提交,或者
  • 任何因规范存储库可能发布的非快进而悬而未决的提交都不是您本地子模块提交的祖先,或者
  • 您努力保持本地子模块提交重新基于规范子模块存储库中可能发布的任何非快进。

如果你采用这样的方法,并且有可能在你的工作树中携带本地子模块提交,那么创建一个自动化系统来确保被检出的子模块引用的关键对象不是一个好主意。留在镜像存储库中(如果有的话,将它们复制到需要它们的存储库中)。

而且,就像git clone联机帮助页所说的那样,如果您不理解这些含义,请不要使用--reference

# Full clone (mirror), done once.
git clone --mirror $sub1_url $path_to_mirrors/$sub1_name.git
git clone --mirror $sub2_url $path_to_mirrors/$sub2_name.git

# Reference the full clones any time you initialize a submodule
git clone $super_url super
cd super
git submodule update --init --reference $path_to_mirrors/$sub1_name.git $sub1_path_in_super
git submodule update --init --reference $path_to_mirrors/$sub2_name.git $sub2_path_in_super

# To avoid extra packs in each of the superprojects' submodules,
#   update the mirror clones before any pull/merge in super-projects.
for p in $path_to_mirrors/*.git; do GIT_DIR="$p" git fetch; done

cd super
git pull             # merges in new versions of submodules
git submodule update # update sub refs, checkout new versions,
                     #   but no download since they reference the updated mirrors

或者,您可以使用本地镜像作为子模块的源,而不是--reference ,将镜像克隆与git clone的默认硬链接功能结合使用。 在新的超级项目克隆中,执行git submodule init ,编辑.git/config的子模块 URL 指向本地镜像,然后执行git submodule update 您需要重新克隆任何现有的检出子模块以获取硬链接。 您只需将一次下载到镜像中,然后从这些镜像本地获取到您检出的子模块中,就可以节省带宽。 硬链接将节省磁盘空间(尽管提取会在多个检出子模块的对象存储实例中累积和复制;您可以定期从镜像中重新克隆检出的子模块,以重新获得由硬链接)。

我创建了一个略有不同的版本,因为当它不在最前沿运行时,并非所有项目都这样做。 标准的子模块添加不起作用,上面的脚本也不起作用。 所以我为标签引用添加了一个哈希查找,如果它没有,它会回退到完整的克隆。

#!/bin/bash
git submodule init
git submodule | while read hash name junk; do
    spath=$(git config -f .gitmodules --get submodule.$name.path)
    surl=$(git config -f .gitmodules --get submodule.$name.url)
    sbr=$(git ls-remote --tags $surl | sed -r "/${hash:1}/ s|^.*tags/([^^]+).*\$|\1|p;d")
    if [ -z $sbr ]; then
        git clone $surl $spath
    else
        git clone -b $sbr --depth 1 --single-branch $surl $spath
    fi
done
git submodule update 

参考如何克隆具有特定修订版/变更集的 git 存储库?

我写了一个简单的脚本,当你的子模块引用远离主模块时它没有问题

git submodule foreach --recursive 'git rev-parse HEAD | xargs -I {} git fetch origin {} && git reset --hard FETCH_HEAD'

此语句将获取子模块的引用版本。

它很快,但你不能在子模块上提交你的编辑(你必须在https://stackoverflow.com/a/17937889/3156509之前获取非浅层)

在全:

#!/bin/bash
git submodule init
git submodule foreach --recursive 'git rev-parse HEAD | xargs -I {} git fetch origin {} && git reset --hard FETCH_HEAD'
git submodule update --recursive

子模块的浅克隆是完美的,因为它们在特定的修订/变更集上进行快照。 从网站下载 zip 很容易,所以我尝试了一个脚本。

#!/bin/bash
git submodule deinit --all -f
for value in $(git submodule | perl -pe 's/.*(\w{40})\s([^\s]+).*/\1:\2/'); do
  mysha=${value%:*}
  mysub=${value#*:}
  myurl=$(grep -A2 -Pi "path = $mysub" .gitmodules | grep -Pio '(?<=url =).*/[^.]+')
  mydir=$(dirname $mysub)
  wget $myurl/archive/$mysha.zip
  unzip $mysha.zip -d $mydir
  test -d $mysub && rm -rf $mysub
  mv $mydir/*-$mysha $mysub
  rm $mysha.zip
done
git submodule init

git submodule deinit --all -f清除允许脚本可重用的子模块树。

git submodule检索 40 个字符 sha1 后跟与.gitmodules相同的.gitmodules 我使用 perl 连接这些信息,用冒号分隔,然后使用变量转换将值分成myshamysub

这些是关键的键,因为我们需要下载 sha1 以及关联 .gitmodules 中的url的路径。

给定一个典型的子模块条目:

[submodule "label"]
    path = localpath
    url = https://github.com/repository.git

path =上的myurl键然后查找 2 行之后以获取值。 此方法可能无法始终如一地工作,需要改进。 url grep 通过匹配最后一个/和任何到.git类型的引用来.git任何剩余的.git类型引用. .

mydirmysub减去最终的/name ,该/name由通向子模块名称的目录组成。

接下来是一个wget ,格式为可下载的 zip 存档 url。 这在未来可能会改变。

将文件解压缩到mydir ,这将是子模块路径中指定的子目录。 结果文件夹将是url - sha1的最后一个元素。

检查子模块路径中指定的子目录是否存在并将其删除以允许重命名提取的文件夹。

mv将包含我们 sha1 的提取文件夹重命名为其正确的子模块路径。

删除下载的 zip 文件。

子模块初始化

这更像是概念的 WIP 证明,而不是解决方案。 当它工作时,结果是在指定变更集的子模块的浅克隆。

如果存储库将子模块重新归位到不同的提交,请重新运行脚本以进行更新。

像这样的脚本唯一有用的时候是源项目的非协作本地构建。

当我无法影响主 repo 的克隆时,我需要一个浅层克隆子模块的解决方案。 基于上述一种解决方案:

#!/bin/bash
git submodule init
for i in $(git submodule | sed -e 's/.* //'); do
    git submodule update --init --depth 1 -- $i
done

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM