背景

之前的几周一直在给 AList 做贡献,AList 是一个聚合存储中间件,它支持将本地磁盘、各类网盘、对象存储、FTP、WebDAV 等存储介质集中管理在一个虚拟文件系统中,并以网页端或 WebDAV、S3、FTP 等协议的方式提供访问接口。它非常适合网盘重度用户(尤其是那些因为资料太多而不得不分多个网盘存储的用户)摆脱网盘 APP 进行文件下载,免会员在线播放,跨网盘大文件复制等场景,感兴趣的人可以看看在线文档项目仓库

AList 有一个古早 issue 希望可以将 git 仓库挂载在 AList 中,这样就可以白嫖 GitHub 用作图床。尽管按照 git 的设计思想,如是的场景应该将作为图床的仓库完整克隆到本地,再返回被请求的资源(因为 git 本来就不是做网盘而是做版本管理的),但这样的方案无法实现节省本地磁盘空间的目的,完全退化为了多此一举的本地存储。因此要实现这样的功能,必须找到一种办法能够克隆仓库的一部分,并且让 git 只跟踪被克隆部分的修改来实现文件上传功能。为此我对 git 一些稀奇古怪的命令进行了探索,但本人最近有一些课题组那边的工作,所以暂时没有时间完整实现 git 仓库的挂载功能,在此将这部分探索的结果记录在此,供未来的我或有意完成这部分功能的人参考。

在不克隆仓库的情况下通过 git 查看远程仓库的目录结构

clone指令实际上是如下四个指令的简写:

git init
git remote add origin <repo-url>
git fetch origin
git checkout main

其中 fetch指令会将远程仓库的完整内容(包括 blobcommittreetag等几种对象,简单的说就是文件内容和提交记录)下载到本地,因此想要在不完整克隆远程仓库的情况下操作远程仓库,fetch就是首先要动手脚的一步。

首先“引用”但不真正克隆一个仓库:

mkdir linux
cd linux
git init
git remote add origin https://github.com/torvalds/linux.git

然后使用如下指令,只下载主分支最近一次提交记录和目录结构:

git fetch --depth 1 --filter=blob:none origin master

--depth 1实现的是只下载最近一次提交,忽视更早以前的提交,节省本地的存储空间,--filter=blob:none指的是不要下载 blob对象,也就是文件内容。

在这样的状态下,远程仓库没有被 fetch的内容会在之后第一次被使用到时下载,比如如果现在执行 checkout,本地文件中就会出现仓库最新版本的内容,这显然就会用到 blob对象,于是 git 就会下载 blob对象,这显然是不符合我们的期望的,因此现在一定不能执行 checkout。这也是为什么在 clone命令中使用 --filter=blob:noneblob对象依然会被下载。

查看远程仓库的目录结构

ls-tree命令允许我们查看远程仓库的目录结构,使用

git ls-tree origin/master

会得到如下输出

100644 blob fe1aa1a30d40267b71a2c90b23e16f7cfe473eea    .clang-format
100644 blob e4c4eef10b28c1ff0c48792d095e647c4a8bfb28    .clippy.toml
100644 blob 43967c6b20151ee126db08e24758e3c789bcb844    .cocciconfig
...
040000 tree 70208637ececd5e37e1e42dfdec924315da59a53    Documentation
100644 blob 464b34a08f51ef9d2ae12e6902c92690b5dfa03b    Kbuild
100644 blob 745bc773f567067a85ce6574fb41ce80833247d9    Kconfig
040000 tree 1b67f4f4f889154b0174bd3e531081e44aa60156    LICENSES
...

这里 blob类型的对象是文件,tree类型的对象是文件夹。

使用下面这样的命令继续查看子文件夹

git ls-tree origin/master arch/

ls-tree命令的 -l参数可以查看文件的大小,比如执行

git ls-tree -l origin/master

会得到

100644 blob fe1aa1a30d40267b71a2c90b23e16f7cfe473eea   22878    .clang-format
100644 blob e4c4eef10b28c1ff0c48792d095e647c4a8bfb28     335    .clippy.toml
100644 blob 43967c6b20151ee126db08e24758e3c789bcb844      59    .cocciconfig
...
040000 tree 70208637ececd5e37e1e42dfdec924315da59a53       -    Documentation
100644 blob 464b34a08f51ef9d2ae12e6902c92690b5dfa03b    2573    Kbuild
100644 blob 745bc773f567067a85ce6574fb41ce80833247d9     555    Kconfig
040000 tree 1b67f4f4f889154b0174bd3e531081e44aa60156       -    LICENSES
...

但是要注意:tree对象不会储存文件的大小,因此 git 首次查看一个文件的大小时,必须先将其 blob对象下载到本地,再统计文件的长度,故而第一次执行上述指令会有很多这样的输出:

remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 4.95 KiB | 2.48 MiB/s, done.
100644 blob fe1aa1a30d40267b71a2c90b23e16f7cfe473eea   22878    .clang-format
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 1 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 285 bytes | 142.00 KiB/s, done.
100644 blob e4c4eef10b28c1ff0c48792d095e647c4a8bfb28     335    .clippy.toml
...

下载 blob对象是我们极力想要避免的,因此非必要情况下应当避免使用 -l参数。

查看远程仓库内的文件内容

可以使用 cat-files查看文件的内容,执行

git cat-file blob 43967c6b20151ee126db08e24758e3c789bcb844

可以得到

[spatch]
        options = --timeout 200
        options = --use-gitgrep

这里 43967c6b20151ee126db08e24758e3c789bcb844是文件 .cocciconfig的哈希。

当然,首次使用 cat-file查看某文件时,需要先下载对应的 blob对象。

cat-file还可以用于查看大小(-s)、类型(-t)等

检出仓库的一部分

git 有一个叫“稀疏检出”的特性可以检出仓库的一部分,关于该特性的详细内容在此不过多赘述,只谈该特性在本需求中的应用。

初始化稀疏检出特性

git config --local core.sparseCheckout true
git sparse-checkout init

设置检出哪些文件,这里每项格式和 .gitignore中的一行是一样的,比如放弃检出除 MAINTAINERS以外的所有文件

git sparse-checkout set --no-cone '!/*' 'MAINTAINERS'

也通过修改 .git/info/sparse-checkout设置检出哪些文件。

检出

git checkout master

之后就可以看到仓库里只有 MAINTAINERS一个文件,但是 git status的如下输出表面我们确实是在仓库的最新版本的

On branch master
Your branch is up to date with 'origin/master'.

You are in a sparse checkout with 1% of tracked files present.

nothing to commit, working tree clean

做出一些修改并推送,对应到 AList 中的上传、重命名、删除等写入操作,这里为了保证成功推送换了仓库地址。

rm MAINTAINERS
git commit -am 'MAINTAINERS: Remove some entries due to various compliance requirements.'
git remote set-url origin https://github.com/KirCute/linux.git
git push origin master

删除本地多余的对象

由于在以上操作中我们下载下来的 blob对象都是历史版本中被引用的对象,所以我们不能通过 git gc删除它们,目前我还没有找到合适的方法删除它们,所以只能选择手动删除 .git/objects/pack下的文件。

在删除前,最好先放弃检出所有文件

git sparse-checkout set --no-cone '!/*'

封面:https://upyun.kircute.top/cover/9_78265281_p0.png

文章作者: 卡比三卖萌KirCute
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 卡比三卖萌KirCute的博客
开发记录
喜欢就支持一下吧