170

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

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

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

4

10 回答 10

169

即将推出的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”的添加和更新命令中,然后将其传递给克隆命令。当子模块很大并且您对最新提交之外的任何内容都不感兴趣时​​,这很有用。

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

签字人: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,submodule update --depth即使 SHA1 可以从远程 repo HEAD 之一直接访问,也有更多成功的机会。

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

子模块:通过直接获取 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”的情况下,我认为提前中止是有意义的:如果列出的 refs 都与请求的引用不匹配,并且服务器不支持通过 SHA1 询问,那么在获取任何东西,因为无论哪种方式我们都无法满足子模块的要求。


2016 年 8 月更新(3 年后)

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

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

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


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

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

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

shallow = trueon.gitmodules仅影响使用时由远程 HEAD 跟踪的引用--recurse-submodules,即使目标提交由分支指向,即使您也放置branch = mybranch.gitmodules


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

请参阅提交 2b1257e提交 76e9bdc(2018 年 10 月 25 日)和提交 b5c259f提交 23dd8f5提交 b2faad4提交 2502ffc提交 996df4d提交 d1b13df提交 45f5ef3提交 bcbc780(2018 年 10 月 5 日),作者Antonio Ospite(ao2
(由Junio C Hamano 合并 -- gitster--提交 abb4824中,2018 年 11 月 13 日)

submodule.gitmodules: 支持不在工作树中的读取

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

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

写入.gitmodules仍然需要签出文件,因此在调用config_set_in_gitmodules_file_gently.

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

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

注意:在极少数情况下,此新功能还不能正常工作:嵌套子模块不在.gitmodules其工作树中。


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

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


Git 2.25(2020 年第一季度)澄清了git submodule update文档。

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

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

帮助者:Junio C Hamano
帮助者:Johannes Schindelin
签字者:Philippe Blain

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


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

文档和代码已被教导在用户看到失败时提出更清晰的建议。

请参阅Jonathan Tan ( ) 的提交 4f3e57e提交 10c64a0(2019 年 12 月 2 日(由Junio C Hamano 合并——提交 5dd1d59中,2019 年 12 月 10 日)jhowtan
gitster

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

签字人:Jonathan Tan
签字人:Jeff King

当递归地克隆一个在其 中定义了一些浅模块的超级.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
签字人:Jeff King

提交31224cbdc7 (" clone: recursive and reference option triggers submodule alternates", 2016-08-17, Git v2.11.0-rc0 --批次 #1中列出的合并) 教导 Git 支持超级项目上的配置选项 " " 和 " " .submodule.alternateLocationsubmodule.alternateErrorStrategy

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

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

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

submodule.alternateErrorStrategy::

指定如何使用通过计算的子模块的替代项来处理错误submodule.alternateLocation
可能的值为ignore, info, die
默认为die
请注意,如果设置为ignoreor info,并且如果计算出的替代品有错误,则克隆会继续进行,就好像没有指定替代品一样


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

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

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

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

命令如

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

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

于 2013-07-17T06:32:59.157 回答
34

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

git clone url://to/source/repository --recursive --shallow-submodules
于 2016-08-15T10:50:45.473 回答
16

按照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
于 2010-01-30T23:26:07.123 回答
8

阅读 git-submodule “源代码”,看起来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
于 2010-01-30T03:32:05.737 回答
8

从 Git 2.14.1 开始的错误/意外/烦人行为摘要

  1. shallow = truein.gitmodules仅影响远程子模块的git clone --recurse-submodules是否HEAD指向所需的提交,即使目标提交由分支指向,即使您也放置branch = mybranch.gitmodules

    本地测试脚本。GitHub 2017-11 上的相同行为,HEAD由默认分支 repo 设置控制:

    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如果提交没有被分支或带有消息的标签引用,则失败: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)。克隆中没有机制来指定所需的确切 sha1。

    有线协议支持询问确切的 sha1,因此应该涵盖这一点。(警告:它仅在服务器操作员启用了 github 没有 AFAICT 的 uploadpack.allowReachableSHA1InWant 时才有效)

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

待办事项测试:allowReachableSHA1InWant

于 2017-11-19T07:43:58.160 回答
2

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

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

如果您可以对每个子模块进行一个完整克隆(加上以后的获取以使它们保持最新),您可以尝试使用--reference选项git submodule update(在 Git 1.6.4 及更高版本中)来引用本地对象存储(例如制作--mirror规范子模块存储库的克隆,然后--reference在您的子模块中使用以指向这些本地克隆)。请务必在使用之前阅读有关git clone --reference/的内容。引用镜像的唯一可能问题是它们是否最终会获取非快进更新(尽管您可以启用 reflogs 并扩展它们的过期窗口以帮助保留任何可能导致问题的放弃提交)。只要你不应该有任何问题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,编辑子模块 URL.git/config以指向本地镜像,然后执行git submodule update. 您需要重新克隆任何现有的签出子模块以获取硬链接。您可以通过只下载一次到镜像中来节省带宽,然后从这些镜像中本地获取到您签出的子模块中。硬链接将节省磁盘空间(尽管提取往往会累积并在已签出子模块的对象存储的多个实例中重复;您可以定期从镜像中重新克隆已签出的子模块以重新获得由硬链接)。

于 2010-01-30T15:08:07.710 回答
2

参考如何克隆具有特定修订/变更集的 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之前获取 unshallow 它)

在全:

#!/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
于 2016-08-01T00:29:03.223 回答
1

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

#!/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 
于 2015-12-15T18:15:06.520 回答
0

子模块的浅层克隆是完美的,因为它们在特定的修订/变更集中进行快照。从网站下载 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 char sha1 后跟与 in 中相同的路径.gitmodules。我使用 perl 连接这些信息,用冒号分隔,然后使用变量转换将值分隔为myshamysub

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

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

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

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

mydirmysub减去一个final ,它/name由指向子模块名称的目录组成。

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

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

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

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

删除下载的 zip 文件。

子模块初始化

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

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

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

于 2019-06-06T15:08:40.927 回答
-1

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

#!/bin/bash
git submodule init
for i in $(git submodule | sed -e 's/.* //'); do
    git submodule update --init --depth 1 -- $i
done
于 2020-10-14T20:20:10.527 回答