如何制作浅git子模块?

时间:2010-01-27 03:34:27

标签: git git-submodules

是否有可能有浅子模块?我有一个包含多个子模块的超级项目,每个子模块都有很长的历史记录,所以它会拖延所有历史记录。“

我找到的只有this unanswered thread

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

10 个答案:

答案 0 :(得分:104)

即将到来的git1.8.4 (July 2013)新增内容:

  

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

(2016年第3季度git 2.10允许用git config -f .gitmodules submodule.<name>.shallow true记录 见本回答的结尾)

请参阅commit 275cd184d52b5b81cb89e4ec33e540fb2ae61c1f

  

--depth选项添加到&#34; git submodule&#34;的add和update命令,然后将其传递给clone命令。当子模块很大并且你对除了最新提交之外的任何东西都不感兴趣时​​,这很有用。

     

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

     

签名:Fredrik Gustafsson <iveqy@iveqy.com>
  Acked-by:Jens Lehmann <Jens.Lehmann@web.de>

这意味着这有效:

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

使用:

--depth::
  

此选项对addupdate命令有效   创造一个浅薄的&#39;克隆历史记录被截断为指定的修订数。


atwyman添加in the comments

  

据我所知,这个选项对于不能非常密切跟踪master的子模块是不可用的。如果设置深度1,那么submodule update只有在您想要的子模块提交是最新的主设备时才能成功。 否则你会得到&#34; fatal: reference is not a tree&#34;

这是真的 也就是说,直到git 2。8(2016年3月)。使用2.8,submodule update --depth还有一次成功的机会,即使SHA1可以从其中一个远程repo HEAD直接访问。

commit fb43e31Stefan Beller (stefanbeller)(2016年2月24日) 帮助:Junio C Hamano (gitster)
(由Junio C Hamano -- gitster --合并于commit 9671a76,2016年2月26日)

  

子模块:尝试通过直接获取sha1

来更难获取所需的sha1      

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

     

如果$sha1不属于默认提取,我们会尝试直接提取$sha1 。但是,有些服务器不支持sha1直接提取,导致git-fetch快速失败   我们可以在这里失败,因为仍然缺少sha1会在结账阶段导致失败,所以在这里失败就像我们能得到的一样好。


MVG指出in the commentscommit fb43e31(git 2。9,2016年2月)

  

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

     

我想这就是为什么fb43e31在获取默认分支后对特定SHA1进行回退的原因。   然而,在“--depth 1”的情况下,我认为提前中止是有意义的:如果列出的引用中没有一个匹配所请求的引用,并且服务器不支持SHA1的请求,那么就没有指出任何东西,因为我们无法以任何方式满足子模块要求。


2016年8月更新(3年后)

使用Git 2.10(2016年第3季度),您将可以

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

参见&#34; Git submodule without extra weight&#34;更多。


Git 2.13(2017年第二季度)请commit 8d3047c添加Sebastian Schuberth (sschuberth)(2017年4月19日)。{ Sebastian Schuberth -- sschuberth --合并于commit 8d3047c,2017年4月20日)

  

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

执行

但是,Ciro Santilli添加了in the comments(以及详细信息in his answer

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


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

commit 2b1257ecommit 76e9bdc(2018年10月25日),commit b5c259fcommit 23dd8f5commit b2faad4commit 2502ffc,{{3} },commit 996df4dcommit d1b13dfcommit 45f5ef3(2018年10月5日)commit bcbc780
(由Antonio Ospite (ao2)合并于Junio C Hamano -- gitster --,2018年11月13日)

  

submodule:支持在.gitmodules不在工作树中时阅读

     

如果工作树中没有.gitmodules文件,请尝试   使用索引和当前分支中的内容   这涵盖了当文件是存储库的一部分但对某些文件库而言的情况   原因是没有签出,例如因为结账稀疏。

     

这样就可以至少使用&#39; git submodule&#39;命令   哪个读取 gitmodules配置文件而没有完全填充   工作树。

     

写入.gitmodules仍然需要检出文件,   所以在致电config_set_in_gitmodules_file_gently之前检查一下。

     

git-submodule.sh::cmd_add()中添加类似的检查,以预测&#34; git submodule add&#34; .gitmodules无法安全写入时的命令;这可以防止命令将存储库置于虚假状态(例如,克隆了子模块存储库,但由于.gitmodules失败,config_set_in_gitmodules_file_gently未更新。

     

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

     

注意:有一种罕见的情况是这个新功能不起作用   正确地说:在工作树中没有.gitmodules的嵌套子模块。

答案 1 :(得分:21)

Git 2.9.0直接支持submodules浅层克隆,所以现在你可以调用:

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

答案 2 :(得分:15)

关注Ryan's answer我能够提出这个简单的脚本,它遍历所有子模块并浅显克隆它们:

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

答案 3 :(得分:8)

通过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

答案 4 :(得分:5)

Git 2.14.1中的越野车/意外/烦人行为摘要

    如果远程子模块的shallow = true指向所需的提交,.gitmodules中的{li>

    git clone --recurse-submodules仅影响HEAD,即使目标提交由分支,即使您将branch = mybranch放在.gitmodules上也是如此。

    Local test script。 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引用提交,则
  1. error: Server does not allow request for unadvertised object会失败。

    Local test script。 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,因此应该涵盖。   (警告:它仅在服务器操作员启用时有效   uploadpack.allowReachableSHA1InWant哪个github没有AFAICT)

         

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

  2. TODO测试:allowReachableSHA1InWant

答案 5 :(得分:2)

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

如果你想要浅克隆来保存本地磁盘空间,那么Ryan Graham的答案似乎是一个很好的方法。手动克隆存储库以使它们很浅。如果您认为它有用,请调整git submodule以支持它。发送电子邮件至the list询问(实施建议,界面建议等)。在我看来,那里的人们非常支持那些真正希望以建设性的方式增强Git的潜在贡献者。

如果您可以对每个子模块执行一次完整克隆(以及稍后提取以使其保持最新),您可以尝试使用--reference的{​​{1}}选项(它位于Git 1.6中) .4及更高版本)引用本地对象库(例如,对规范子模块存储库进行git submodule update个克隆,然后在子模块中使用--mirror指向这些本地克隆)。在使用--reference之前,请务必阅读git clone --reference / git clone --shared。引用镜像的唯一可能问题是它们是否最终获取非快进更新(尽管您可以启用reflog并扩展其过期窗口以帮助保留可能导致问题的任何放弃的提交)。只要

,你不应该有任何问题
  • 您不进行任何本地子模块提交,或
  • 任何由规范存储库可能发布的非快进方式悬挂的提交都不是您本地子模块提交的祖先,或者
  • 你很努力保持你的本地子模块提交能够在规范子模块存储库中发布的非快速更新的基础上进行重新设置。

如果您使用此类内容并且您可能在工作树中携带本地子模块提交,那么创建一个自动系统可能是一个好主意,该系统可确保检出引用的关键对象子模块不会悬挂在镜像存储库中(如果找到任何子模块,则将它们复制到需要它们的存储库中)。

并且,就像--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。您需要重新克隆任何现有的签出子模块以获取硬链接。您只需将一次下载到镜像中,然后从本地获取到已签出的子模块中,即可节省带宽。硬链接将节省磁盘空间(尽管提取将倾向于累积并在已签出的子模块的对象存储的多个实例中重复;您可以定期从镜像中重新检出已检出的子模块,以重新获得由hardlinking)。

答案 6 :(得分:1)

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

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

答案 7 :(得分:1)

参考How to clone git repository with specific revision/changeset?

我写了一个简单的脚本,当你的子模块引用远离主

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

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

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

完整:

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

答案 8 :(得分: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个字符sha1,后跟与.gitmodules中相同的路径。我使用perl连接此信息,并用冒号分隔,然后使用变量转换将值分成myshamysub

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

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

[submodule "label"]
    path = localpath
    url = https://github.com/repository.git
然后,myurl上的

path =键看上去在两行之后就得到了值。此方法可能无法始终如一地工作,需要完善。 url grep通过匹配最后一个.git和不超过/的所有内容来剥离所有剩余的.类型引用。

mydirmysub减去最后一个/name,该目录最后是子模块名称。

下一个是wget,格式为可下载的zip存档URL。将来可能会改变。

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

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

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

删除下载的zip文件。

子模块初始化

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

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

类似的脚本唯一有用的地方是用于源项目的非协作本地构建。

答案 9 :(得分:0)

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

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