Git默认支持http, https, ssh协议,同时也提供了扩展私有协议的方法,文档git-remote-helpers给出了详细的spec。
比如要实现一种协议,把git repository存储(可加密)到私人的email邮箱中,以存储一些不便于host到GitHub的私人repo,同时免去购买主机/服务器的成本和维护带来的麻烦,Repository的clone url格式定义为mail://your@email.com:repo_name
。
调用
使用git命令clone, 现在什么都没实现,所以理所当然的报错:1
$ git clone mail://akfish@gmail.com:foo
fatal: Unable to find remote helper for 'mail'
根据文档的描述:
When git encounters a URL of the form <transport>://<address>, where <transport> is a protocol that it cannot handle natively, it automatically invokes git remote-<transport> with the full URL as the second argument. If such a URL is encountered directly on the command line, the first argument is the same as the second, and if it is encountered in a configured remote, the first argument is the name of that remote.
即git会把url中mail://
映射到调用命令git-remote-mail
,所以只需要用任何开发语言实现一个标准输入输出的命令行程序,满足文档中定义的命令格式,放在git能搜寻到的位置,就能让git支持私有协议(注意python在windows下存在stdout无法被重定向的问题,无法和git正确通信)。本例中用C#实现,创建控制台程序git-remote-mail:
1 | using System; |
再次执行clone,错误提示消失,由于这个脚本什么事情也没做,所以当然也就什么都不会发生。stdin和stdout被用于与git通信,不会显示出来。如果需要输出消息,write到stderr,或者产生一个日志文件来记录,Logger类实现了相应的功能,具体代码见Logger.cs。执行clone命令后,输出为:1
[2013/10/20 22:07:02]Open log file git-remote-mail.txt
[2013/10/20 22:07:02]args: origin, mail://akfish@gmail.com:foo
[2013/10/20 22:07:02]Close log file
可以看到,在调用的时候,还传入了两个参数:origin和mail://akfish@gmail.com:foo,根据文档:
Remote helper programs are invoked with one or (optionally) two arguments. The first argument specifies a remote repository as in git; it is either the name of a configured remote or a URL. The second argument specifies a URL; it is usually of the form <transport>://<address>, but any arbitrary string is possible.
参数的数量为1~2个,第一个参数为repo的名字或者url,第二个参数如果存在,为repo的url。
命令流
Git通过stdin向remote helper发送命令,一行一个,第一个命令总是capabilities。Remote helper需要通过stdout返回支持的capabilities,每行一个,以空行结束。Capabilities代表helper支持哪些命令子集,如fetch需要支持connect, fetch, import,详细的列表在文档里有列出。
命令流通常以空行结束,但在某些情况下空行后会跟着其它协议的payload(如pack),具体参见command的具体说明。要注意的是命令流用的是linux-style line ending,即以\n结尾,如果使用Console.WriteLine产生的是DOS line ending(\r\n),则不能正确工作。
增加代码响应capabilities命令:
1 | //.... |
输出为:
1 | [2013/10/20 22:28:53]Open log file git-remote-mail.txt [2013/10/20 22:28:53]args: origin, mail://akfish@gmail.com:foo [2013/10/20 22:28:53]>>capabilities [2013/10/20 22:28:53]<<connect, fetch, import [2013/10/20 22:28:53]>>connect git-upload-pack [2013/10/20 22:28:53]Unhandled command. Exit [2013/10/20 22:28:53]Close log file |
表明命令流已经成功初始化,git继续发出connect命令开始clone的工作。
接口部分就这么简单,接下来的工作就是根据文档的描述,响应具体的命令,完成协议的具体设计。
更多参考资料
Git的repo中包含了大量文档,都是很好的参考资料
1 | $ git clone https://github.com/git/git $ cd git/Documentation $ grep -nRHI "receive-pack" * |
会给出这些文档:
以下文档是相关的后端命令,作为补充:
查看源代码中与传输协议相关的commit:
1 | $ git clone https://github.com/git/git $ cd git $ git log -Stransfer |
可以参见以下commits:
- commit 4bc444 (Support FTP-over-SSL/TLS for regular FTP)
- commit daebaa (upload/receive-pack: allow hiding ref hierarchies )
- commit 745f7a (fetch-pack: move core code to libgit.a)
- commit fe0435 (Add persistent-https to contrib)