[英]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
此选项对
add
和update
命令有效。
创建一个“浅”克隆,其历史记录被截断为指定数量的修订。
据我所知,此选项不适用于不能非常密切地跟踪
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.allowReachableSHA1InWant
和uploadpack.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
合并gitster
在abb4824 提交中,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 4f3e57e和commit 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
计算的子模块的替代错误。
可能的值是ignore
、info
、die
。
默认为die
。
请注意,如果设置为ignore
或info
,并且如果计算的替代项存在错误,则克隆继续进行,就像未指定替代项一样。
注意:“ 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 起的错误/意外/烦人行为摘要
如果远程子模块的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.
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 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 连接这些信息,用冒号分隔,然后使用变量转换将值分成mysha
和mysub
。
这些是关键的键,因为我们需要下载 sha1 以及关联 .gitmodules 中的url
的路径。
给定一个典型的子模块条目:
[submodule "label"]
path = localpath
url = https://github.com/repository.git
path =
上的myurl
键然后查找 2 行之后以获取值。 此方法可能无法始终如一地工作,需要改进。 url grep 通过匹配最后一个/
和任何到.git
类型的引用来.git
任何剩余的.git
类型引用.
.
mydir
是mysub
减去最终的/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.