如何制作浅git子模块?


Answers:


133

即将发布的git1.8.4(2013年7月)的新功能

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

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

参见commit 275cd184d52b5b81cb89e4ec33e540fb2ae61c1f

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

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

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

这意味着这有效:

git submodule add --depth 1 -- repository path
git submodule update --depth -- [<path>...]

带有:

--depth::

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


atwyman添加了评论

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

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

参见Stefan Beller()的commit 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 2月2016)

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

我想这就是为什么fb43e31将对特定SHA1的获取作为对默认分支的获取之后的回退的原因。
不过,在“深度1”的情况下,我认为尽早中止是有意义的:如果列出的引用均与请求的引用不匹配,并且服务器不支持SHA1的询问,则没有意义。获取任何东西,因为我们将无法以任何一种方式满足子模块的要求。


2016年8月更新(3年后)

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

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

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


Git 2.13(Q2 2017)确实添加了Sebastian Schuberth()的提交8d3047c(2017年4月19日(通过合并塞巴斯蒂安Schuberth - -提交8d3047c 4月20日2017)sschuberth
sschuberth

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

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

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


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

请参阅提交2b1257e提交76e9bdc(2018年10月25日)和提交b5c259f提交23dd8f5提交b2faad4提交2502ffc提交996df4d提交d1b13df提交45f5ef3提交bcbc780(2018年10月5日),由安东尼奥Ospite( )ao2
(通过合并JUNIOÇ滨野- gitster-提交abb4824 11月13日2018)

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(Q4 2019)修复了克隆浅子模块时可能出现的段错误。

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


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

请参阅Philippe Blain()的commit f0e58b3(2019年11月24日(通过合并JUNIOÇ滨野- -提交ef61045,2019年12月5日)phil-blain
gitster

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

帮助人:Junio C Hamano
帮助人:Johannes Schindelin
签名人:Philippe Blain

如果找不到在超级项目中记录的SHA-1,则 ' git submoduleupdate' 将从远程子模块获取新的提交。文档中未提及。


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

教给文档和代码以在用户看到故障时提出更清晰的建议。

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

submodule--helper:建议致命替代错误

签字人:Jonathan Tan
签名人:杰夫·金

当用其中定义的一些浅层模块递归克隆一个超级项目时.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年第1季度)中,“ git clone --recurse-submodules”与备用对象存储之间的交互设计不良。

Doc:解释submodule.alternateErrorStrategy

签字人:Jonathan Tan
签名人:杰夫·金

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

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

submodule.alternateErrorStrategy”选项确定如果无法引用该替代项会发生什么。
但是,尚不清楚克隆是否继续进行,就好像未将该选项未设置为“ die”时未指定其他选项一样(如在31224cbdc7中的测试中可以看到的)。
因此,请相应地记录下来。

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

submodule.alternateErrorStrategy::

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


2
哇,太快了!顺便问一下答案。哦,还--depth应该吵一架;)
Brice

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

2
随着最近在中添加了浅选项.gitmodules,该--depth 1选项对不紧密跟踪master的分支有效吗?
CMCDragonkai '17

2
@CiroSantilli刘晓波死六四事件法轮功感谢您的精确度和测试。我已将您的评论添加到答案中以提高可见度,并投票支持您的答案。
VonC

2
答案尚不清楚,目前的解决方法是什么。此外,尚不清楚每次有人克隆新副本还是这些稀疏子模块设置成为引用这些子模块的仓库的一部分时是否都需要所有这些(例如,每个新克隆和子模块更新都会导致稀疏子模块检出)
Pavel P

26

Git 2.9.0直接支持子模块浅表克隆,因此现在您可以调用:

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

2
这个选项是最有前途的,但是它在git 2.14.1上失败,分支或标签都无法跟踪子模块的提交:stackoverflow.com/a/47374702/895245
Ciro Santilli郝海东冠状病六四事件法轮功

1
@CiroSantilli刘晓波死六四事件法轮功确保您的git服务器也已更新
KindDragon

谢谢,我已经在本地进行测试,没有服务器,但是在GitHub上无法更新:-)
Ciro Santilli郝海东冠状病六四事件法轮功

1
我在使用git 2.20时遇到相同的问题,当子模块不在分支的尖端时,它不起作用。
Zitrax

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

fatal: reference is not a tree: 88fb67b07621dfed054d8d75fd50672fb26349df需要每个子模块
knocte 2014年


1
@knocte:我在2010年写下了答案。情况已经改变。您不能指望每个人都能保留所有答案。我确实将当前有效答案标记为已接受。
Mauricio Scheffer 2014年

13
@knocte这是我停止为Stackoverflow捐款的原因之一。人们有这些不切实际的期望。维持我的1637个答案中的每一个答案都是一项全职工作。然后还有意见,我想我也必须保留这些意见?看一下日期,这就是他们想要的。如果您从2002年开始读一些使用ArrayList而不是List编写的.NET博客,那么您会使用它吗?您是否要求作者更新其帖子?同样的原则在这里适用。
Mauricio Scheffer 2014年

1
s / statusquo / progress /
knocte 2014年

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

5
现在对于git 1.8.0,您不能再在存储库中克隆存储库了。因此,此解决方案不再起作用。
Bohr

7

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

  1. shallow = true.gitmodules仅影响git clone --recurse-submodules,如果HEAD远程子模块指向必需的承诺,即使目标承诺是指向一个分支,即使你把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 如果分支或标记均未使用消息引用提交,则失败: 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,回复是:

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

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

    有线协议支持询问确切的sha1,因此应予以覆盖。(注意:仅当服务器操作员启用github尚未使用AFAICT的uploadpack.allowReachableSHA1InWant时才有效)

    git-fetch允许获取任意sha1,因此,一种变通办法是,您可以使用“ git submodule update”在递归克隆之后运行获取,因为它将在初始克隆之后使用获取。

TODO测试:allowReachableSHA1InWant


似乎没有简单的方法来为子模块检出分离的HEAD提交哈希,并让下游用户git clone --recursive仅获取该特定提交。
CMCDragonkai

3

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

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

如果可以对每个子模块进行完整克隆(可以稍后再获取以使它们保持最新状态),则可以尝试使用--reference选项git submodule update(在Git 1.6.4和更高版本中)来引用本地对象存储(例如创建--mirror规范子模块存储库的克隆,然后--reference在子模块中使用以指向这些本地克隆)。只需确保使用前阅读git clone --reference/ 。引用镜像的唯一可能问题是它们是否最终获取非快速更新(尽管您可以启用reflog并扩展其过期窗口以帮助保留可能导致问题的所有放弃的提交)。只要你没有任何问题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。您将需要重新克隆任何现有的检出子模块以获取硬链接。您只需下载一次到镜像中,然后从本地下载到检出的子模块中,即可节省带宽。硬链接将节省磁盘空间(尽管提取会积累并在已检出子模块的对象存储的多个实例之间重复;您可以定期从镜像中重新克隆已检出的子模块,以重新获得由磁盘提供的磁盘空间节省)硬链接)。


2

我创建了一个略有不同的版本,用于当它没有在最前沿运行时,并非所有项目都在运行。标准子模块添加无效,以上脚本也不起作用。因此,我为标记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 

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之前取消对其进行浅化处理)

在全:

#!/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

1

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

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

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

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

myurl键,path =然后在获得值后看两行。此方法可能无法始终如一地工作,需要完善。url grep .git通过匹配最后一个/以及直到a的所有内容来剥离所有剩余的类型引用.

mydirmysub减去最终值/name的目录,该目录将指向子模块名称。

接下来是 wget格式为可下载的zip存档网址。将来可能会改变。

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

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

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

删除下载的zip文件。

子模块初始化

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

如果存储库将子模块重新放置到其他提交中,请重新运行脚本以进行更新。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.