我会事先说这个问题在本质上与此类似.有一个关键区别使得这个独特:我想使用原始git协议(如果您不熟悉基本包网络协议,请参阅此处和此处).
我正在编写一个使用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做......".我只是想知道协议本身是否能够做到这一点.我觉得有一种非常聪明的方式滥用协议来实现我想要的东西.有什么想法吗?
Chr*_*rle 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
Run Code Online (Sandbox Code Playgroud)
除了在软件中,希望使用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 ]
Run Code Online (Sandbox Code Playgroud)
所以协议的第一部分是这样的:
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发送了一个,则会跟随一个flush packet(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
Run Code Online (Sandbox Code Playgroud)
因此,从我的角度来看,我只需要设置一个管道来自动运行上述步骤,通过一个解压缩步骤(以编程方式)运行它来执行所需的"从git获取单个文件"功能.
| 归档时间: |
|
| 查看次数: |
2763 次 |
| 最近记录: |