是否可以有浅层次的子模块?我有一个超级项目,其中包含几个子模块,每个子模块都有很长的历史记录,因此拖拽所有历史记录会使其变得不必要地大。
我找到的只有这个未回答的帖子。
我应该只是修改 git-submodule 以实现这一点吗?
是否可以有浅层次的子模块?我有一个超级项目,其中包含几个子模块,每个子模块都有很长的历史记录,因此拖拽所有历史记录会使其变得不必要地大。
我找到的只有这个未回答的帖子。
我应该只是修改 git-submodule 以实现这一点吗?
简而言之;
git clone --recurse-submodules --shallow-submodules
(但是请注意 Ciro Santilli 答案 中的警告)
或者:记录子模块应该被浅克隆:
git config -f .gitmodules submodule.<name>.shallow true
git clone --recurse-submodules
命令将浅克隆子模块 '<name>
'(深度为 1),即使没有使用 --shallow-submodules
参数。
git submodule
/git clone
的演变过程,从2013年的Git 1.8.4开始,并持续至今。
在即将发布的Git 1.8.4 (2013年7月)中新增功能:
"
git submodule update
" 可选择浅克隆子模块仓库。
(而在 git 2.10 Q3 2016 中,可以使用 git config -f .gitmodules submodule.<name>.shallow true
记录此操作。
详见本回答末尾的commit 275cd184d52b5b81cb89e4ec33e540fb2ae61c1f)
--depth
选项,然后传递给克隆命令。当子模块很大且您仅对最新提交感兴趣时,这很有用。 添加了测试,并进行了一些缩进调整,以符合“子模块更新可以处理 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
命令使选项对后续递归克隆存储库的其他人永久生效。https://github.com/foo/bar
,并且您想将 https://github.com/lorem/ipsum
添加为子模块,在存储库的 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
This option is valid for
add
andupdate
commands.
Create a 'shallow' clone with a history truncated to the specified number of revisions.
据我所知,这个选项对于不太密切地跟踪
master
的子模块是无法使用的。如果你设置深度为1,则submodule update
只能成功,如果子模块提交是最新的主提交。否则会出现“fatal:reference is not a tree
”的错误。
这是真的。
也就是说,在git 2.8(2016年3月)之前,submodule update --depth
即使SHA1直接可达到远程库HEADs之一,也将失败。但从2.8开始,submodule update --depth
有了更多的成功机会。
查看提交fb43e31 (2016年2月24日) 由Stefan Beller (stefanbeller
)完成。
协助者:Junio C Hamano (gitster
)。
(由Junio C Hamano -- gitster
--合并于提交9671a76,2016年2月26日)
在审查Gerrit中还更新子模块的更改时,常见的审查做法是下载并本地挑选补丁进行测试。然而,在本地测试时,“git submodule update”可能会失败,因为子模块中的相应提交尚未成为项目历史的一部分,而只是一个建议性的更改。 如果“$sha1”不是默认获取的一部分,则尝试直接获取“$sha1”。但是,有些服务器不支持按sha1直接获取,这会导致“git-fetch”快速失败。我们可以在此处自行失败,因为仍然缺少的sha1会在稍后的检出阶段导致失败,因此在此处失败与我们能够得到的一样好。子模块:尽力通过直接获取sha1来获取所需的sha1
MVG指出在评论中提交fb43e31(git 2.9,2016年2月)
我认为commit fb43e31请求缺失的SHA1 id提交,因此服务器上的uploadpack.allowReachableSHA1InWant
和uploadpack.allowTipSHA1InWant
设置可能会影响其是否有效。我今天写了一篇帖子发送到git列表中,指出如何更好地使用浅子模块以适应某些情况,即如果提交也是标签的情况下。让我们拭目以待。我猜这就是为什么fb43e31将针对特定SHA1的提取作为默认分支提取后备的原因。尽管如此,在“--depth 1”的情况下,我认为早点放弃是有意义的:如果没有列出的引用与所请求的引用匹配,并且服务器不支持通过SHA1进行查询,则没有获取任何内容的意义,因为无论如何我们都无法满足子模块要求。
2016年8月更新(3年后)
使用Git 2.10(2016年第3季度),您将能够执行以下操作
git config -f .gitmodules submodule.<name>.shallow true
请查看 "不带额外负担的Git子模块" 以获取更多信息。
Git 2.13(2017年第二季度)添加了commit 8d3047c(于2017年4月19日由Sebastian Schuberth(sschuberth
)添加)。
(由Sebastian Schuberth -- sschuberth
--于2017年4月20日合并到commit 8d3047c中)
此子模块的克隆将作为浅克隆执行(历史深度为1)
然而,Ciro Santilli在评论中添加了细节详见他的回答。
.gitmodules
上的shallow = true
仅影响使用--recurse-submodules
时由远程HEAD跟踪的引用,即使目标提交被分支指向,即使您也在.gitmodules
上放置了branch = mybranch
。
HEAD:.gitmodules
的 blob 中读取,当工作树中缺少 .gitmodules
文件时。
请查看提交 2b1257e, 提交 76e9bdc (2018年10月25日),以及提交 b5c259f, 提交 23dd8f5, 提交 b2faad4, 提交 2502ffc, 提交 996df4d, 提交 d1b13df, 提交 45f5ef3, 提交 bcbc780 (2018年10月05日) ,作者为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()
中发生,因此在涉及config_from_gitmodules()
的代码之前调用grep_read_lock()
。注意:存在一种罕见情况,这种新功能尚未正常工作:没有.gitmodules
的嵌套子模块在其工作树中。
auselen
)于2019年9月30日提交。(由Junio C Hamano -- gitster
--合并于提交记录678a9ca,2019年10月9日)
git submodule update
文档的内容。phil-blain
)于2019年11月24日提交)。
(由Junio C Hamano -- gitster
--在提交记录ef61045中合并,于2019年12月5日)
doc
:提到“git submodule update”会获取丢失的提交由Junio C Hamano协助
由Johannes Schindelin协助
由Philippe Blain签署
如果在超级项目中记录的SHA-1未找到,则'
git submodule
update'将从子模块远程获取新的提交。这在文档中没有提到。
git clone --recurse-submodules
”与备用对象存储之间的交互设计不当。请看提交4f3e57e,提交10c64a0(2019年12月2日),作者是Jonathan Tan (jhowtan
)。
(由Junio C Hamano -- gitster
--在提交5dd1d59中合并,2019年12月10日)
submodule--helper
:建议处理致命的备选错误Signed-off-by: Jonathan Tan(由Jonathan Tan签署)
Acked-by: Jeff King(由Jeff King确认)
When recursively cloning a superproject with some shallow modules defined in its
.gitmodules
, then recloning with "--reference=<path>
", an error occurs. For example:
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
fails with:
fatal: submodule '<snip>' cannot add alternate: reference repository '<snip>' is shallow
When a alternate computed from the superproject's alternate cannot be added, whether in this case or another, advise about configuring the "
submodule.alternateErrorStrategy
" configuration option and using "--reference-if-able
" instead of "--reference
" when cloning.
以下是详细内容:
在 Git 2.25 (2020 年第一季度) 中,“git clone --recurse-submodules” 和替代对象存储之间的交互设计存在问题。
文档
:解释 submodule.alternateErrorStrategy签名者:Jonathan Tan
认可者:Jeff King
如果在超级项目中将“submodule.alternateLocation”配置为“superproject”,则每当克隆该超级项目的子模块时,它会计算该子模块的类似备用路径,并从超级项目的“$GIT_DIR/objects/info/alternates”引用它。 “submodule.alternateErrorStrategy”选项确定如果无法引用该备用项会发生什么情况。但是,当该选项未设置为“die”时(如31224cbdc7中的测试所示),不清楚克隆是否会像没有指定备用项一样进行。因此,请相应地记录文档。配置子模块文档现在包括:提交31224cbdc7(“
clone
: recursive and reference option triggers submodule alternates”,2016年8月17日,Git v2.11.0-rc0 -- merge listed in batch #1)教会了Git支持在超级项目上配置选项“submodule.alternateLocation
”和“submodule.alternateErrorStrategy
”。
submodule.alternateErrorStrategy::
Specifies how to treat errors with the alternates for a submodule as computed via
submodule.alternateLocation
.
Possible values areignore
,info
,die
.
Default isdie
.
Note that if set toignore
orinfo
, and if there is an error with the computed alternate, the clone proceeds as if no alternate was specified.
git submodule update --quiet
"(man)没有将安静选项传递到底层的 git fetch
(man),这已经在 Git 2.32 (Q2 2021) 中得到了纠正。
查看 提交 62af4bd (2021年4月30日) 由 Nicholas Clark (nwc10
) 进行。
(合并自 Junio C Hamano -- gitster
-- 在 提交 74339f8, 2021年5月11日)
submodule update
: 使用 "--quiet
" 静默底层获取签名作者:Nicholas Clark
Commands such as
$ git submodule update --quiet --init --depth=1
involving shallow clones, call the shell function
fetch_in_submodule,
which in turn invokesgit fetch
.
Pass the--quiet
option onward there.
--depth
也应该带一个参数;) - bric3uploadpack.allowReachableSHA1InWant
和 uploadpack.allowTipSHA1InWant
设置可能会影响其是否有效。我今天写了一篇帖子发布到git列表中,指出如何更好地使用浅子模块来适用某些情况,特别是如果该提交也是一个标记。让我们等待并观察。 - MvG.gitmodules
中添加了浅层选项,那么--depth 1
选项是否适用于没有紧密跟踪主分支的分支? - CMCDragonkaiGit 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 2.14.1版本存在的错误/意外/烦人行为总结
shallow = true
in .gitmodules
only affects git clone --recurse-submodules
if the HEAD
of the remote submodule points to the required commit, even if the target commit is pointed to by a branch, and even if you put branch = mybranch
on the .gitmodules
as well.
Local test script. Same behaviour on GitHub 2017-11, where HEAD
is controlled by the default branch repo setting:
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
fails if the commit is neither referenced by a branch or tag with a message: error: Server does not allow request for unadvertised object
.
Local test script. Same behaviour on GitHub:
git clone --recurse-submodules --shallow-submodules https://github.com/cirosantilli/test-shallow-submodule-top-sha
# error
I also asked on the mailing list: https://marc.info/?l=git&m=151863590026582&w=2 and the reply was:
In theory this should be easy. :)
In practice not so much, unfortunately. This is because cloning will just obtain the latest tip of a branch (usually master). There is no mechanism in clone to specify the exact sha1 that is wanted.
The wire protocol supports for asking exact sha1s, so that should be covered. (Caveat: it only works if the server operator enables uploadpack.allowReachableSHA1InWant which github has not AFAICT)
git-fetch allows to fetch arbitrary sha1, so as a workaround you can run a fetch after the recursive clone by using "git submodule update" as that will use fetches after the initial clone.
TODO test: allowReachableSHA1InWant
.
git clone --recursive
仅获取该特定提交。 - CMCDragonkai阅读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 submodule foreach --recursive 'git rev-parse HEAD | xargs -I {} git fetch origin {} && git reset --hard FETCH_HEAD'
这个语句将获取子模块的引用版本。
它很快,但你不能提交对子模块的编辑(你必须在提交前将其取回完整版本 https://dev59.com/6mw15IYBdhLWcg3wJ4UR#17937889)。
完整版:
#!/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
git submodule update
的 --reference
选项(它在 Git 1.6.4 及更高版本中可用)来引用本地对象存储(例如,制作规范子模块仓库的 --mirror
克隆,然后在子模块中使用 --reference
指向这些本地克隆)。但是,在使用 --reference
之前,请务必阅读有关 git clone --reference
/git clone --shared
的内容。引用镜像可能唯一的问题是,如果它们最终获取了非快进更新(虽然您可以启用 reflogs 并扩展其过期时间窗口,以帮助保留任何可能导致问题的废弃提交),则不会有任何问题,只要:
# 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
git clone
的默认硬链接功能,而不是使用--reference
。在新的超级项目克隆中,执行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
浅克隆子模块非常完美,因为它们快照在特定的修订/变更集上。从网站下载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
中相同的路径。我使用perl将此信息连接起来,用冒号分隔,然后使用变量转换将值分离为mysha
和mysub
。
这些是关键的密钥,因为我们需要sha1来下载,需要路径来关联.gitmodules中的url
。
给定典型的子模块条目:
[submodule "label"]
path = localpath
url = https://github.com/repository.git
myurl
键在 path =
后面的 2 行中查找值。这种方法可能不一致,需要改进。url grep 通过匹配最后一个 /
和任何一个 .
来剥离任何剩余的 .git
类型的引用。
mydir
是 mysub
减去最后一个 /name
,这将是子模块名称前导的目录。
接下来是一个可下载 zip 归档文件 url 的 wget
格式。这可能会在未来发生变化。
将文件解压缩到 mydir
中,这将是子模块路径中指定的子目录。结果文件夹将是 url
-sha1
的最后一个元素。
检查子模块路径中指定的子目录是否存在,并删除它以允许重命名提取的文件夹。
mv
重命名包含我们 sha1 的提取文件夹到其正确的子模块路径。
删除已下载的 zip 文件。
子模块初始化。
这更像是一个概念验证的工作正在进行,而不是一个解决方案。当它起作用时,结果是在指定的变更集上浅克隆子模块。
如果存储库将子模块重新定位到不同的提交,请重新运行脚本以进行更新。
只有在非协作本地构建源项目时,才会有类似这样的脚本有用。
我需要一种解决方案,可以在无法对主存储库进行克隆的情况下浅克隆子模块。 基于上面的一个解决方案:
#!/bin/bash
git submodule init
for i in $(git submodule | sed -e 's/.* //'); do
git submodule update --init --depth 1 -- $i
done
git submodule add/update
"可以浅克隆子模块仓库了!请参见我的回答。 - VonC