我会事先说明这个问题在本质上与this类似。有一个关键的区别使这个独特:我想使用原始git协议(如果您不熟悉基本包网络协议,请参阅here和here。)
我正在编写一个使用Scala和JGit的应用程序,它将连接到一个匿名的git存储库。我想请求一个blob(想想“/path/to/file.txt”@“refs / heads / branch1”)。最终,我的目标是以编程方式从远程存储库中检索单个文件。看起来像是一件非常有用的事情。
Anywho,我一直在钻研这个协议的内部。似乎这个的基本版本是“我想要这些对象,我有这些对象” - 而bam,有一个包含你没有的东西的包文件。我的问题的核心是:如何以非递归的方式向git-upload-packfile请求单个对象?我可以下载一个提交对象,然后查询树,然后是一个子树,然后是另一个子树,最后是blob本身。速度在这里并不重要,主要是我试图节省带宽。但似乎根本没有办法告诉git-upload-packfile,“请只给我一个我要求的对象”。
是的,有“有”列表,它基本上会排除对象下降,但是这需要事先了解存储库的内容(我没有本地存储库,请记住)。我可以生成一个包含所有可能sha1的列表,并发送除了我想要的所有sha1之外的所有sha1,但这超出了荒谬(耗时,带宽消耗,并且对所有程序员的犯罪)
我一直在研究的另一个可能的解决方案是在远程端使用git-upload-archive,但我承认我还没有花太多时间来研究它。
我非常愿意重写JGit,所以请不要将其视为“我如何让JGit做......”。我只是想知道协议本身是否能够做到这一点。我觉得有一种非常聪明的方式滥用协议来实现我想要的东西。有什么想法吗?
答案 0 :(得分:10)
回答我自己的问题。我找到了一个可接受的(虽然几乎没有记录)答案。我不得不通过大量的C代码来解决这个问题。
首先,使用git-upload-packfile
无法实现上述要求,因为这根本不是程序设计的目的。我怀疑的正确答案是git-upload-archive
。可悲的是,协议几乎没有记录在案。所以这里有我的笔记,以防其他人有类似的要求。
基本上我在这里(在scala中)模拟的是以下命令:
git archive --format=tar --remote=ssh://dave@ssh.mycompany.com/cornballer.git \
> master plans/documents/cornballer-blueprint.pdf | tar -x
除了软件,希望使用JGit。可悲的是,JGit还没有(还)支持git archive命令。所以这里是一个关于如何添加支持的非常高级的概述(我可能会分叉JGit并在以后添加它)。
让我们看看协议(来自Documentation / technical / pack-protocol.txt):
git-proto-request = request-command SP pathname NUL [ host-parameter NUL ]
request-command = "git-upload-pack" / "git-receive-pack" /
"git-upload-archive" ; case sensitive
pathname = *( %x01-ff ) ; exclude NUL
host-parameter = "host=" hostname [ ":" port ]
所以协议的第一部分是这样的:
git-upload-archive
或使用匿名git协议)git-upload-archive /cornballer.git\0host=ssh.mycompany.com\0
(作为数据包行)此时建立连接。如果不支持该命令或者存在任何类型的问题,则可能会返回错误。我还没弄清楚如何检查这个。
接下来是未记录的部分。我们基本上通过线路发送git-archive
的命令行参数。它们与git-archive
命令完全相同,但有一个例外:它们都以argument[SPACE]
为前缀。每个参数都作为单独的数据包行写入(至少在参考实现中)。所以对于上面的例子:
argument --format=tar
(作为数据包行)argument master
(作为数据包行)argument plans/documents/cornballer-blueprint.pdf
(作为数据包行)0000
)此时我们已经为远程git-archive过程提供了整个命令。现在我们阅读回复。我们从服务器读回一个数据包行,这将是以下响应之一:
ACK
(意味着成功 - 准备好发送档案)NACK [message]
- 某种错误,只发现了一个使用它的实例 - “无法生成子进程”ERR [message]
- 发生错误如果发送ACK
,则会跟随一个刷新数据包(0000
),然后是原始tar数据。此时,您反复读取进入边带#1(主数据通道)的数据包线。当您到达同花包时,您将停止阅读。很简单。
所以现在你有了远程文件,但是如果你想做某种聪明的缓存呢?我在使用git-upload-packfile
时非常感兴趣的一个原因是,它会让我记录提交ID,从而在本地缓存它,并且只在需要时刷新。 tar文件没告诉我们这个信息对吗?错!
来自git-archive的手册页:
此外,如果tar格式为,则提交ID存储在全局扩展pax标头中 用过的;它可以使用git get-tar-commit-id提取。在ZIP文件中,它存储为 档案评论。
那是个好消息!这就是我想要的一切。如果您想知道标题是什么样的,这里有一个示例(不,我不打算解析pax标题):
pax_global_header00006660000000000000000000000064121002672560014513gustar00rootroot0000000000000052 comment=326756f834865880c9832b64238e7665632e9b67
因此,从我的角度来看,我只需要设置一个管道来自动运行上述步骤,通过一个解压缩步骤(以编程方式)运行它来执行所需的“从git获取单个文件”功能。