diff函数打印每个修改过的文件中变动的地方,显示索引中的内容与当前工作副本中的内容的不同点(使用Python的difflib模块来完成这个功能) git对索引的操作和这些命令的执行在效率上比我这个程序要高很多。我使用os.walk()函数来列出目录中的所有文件的完整路径,做一些设置操作,然后比较他们散列值。例如,这个是我用来获取有过修改的路径列表的代码:
最后还有一个write_index函数用于回写索引。它调用了add()函数将一个或多个路径添加到索引中。add()函数首先读取整个索引,将路径添加进去,然后重新排序并回写索引。 此时,我们已经将文件添加到索引中了,下面,我们可以开始实现commit操作了。 提交 执行提交操作需要编写两个对象: 首先是树对象,它是提交时当前目录(或者是索引)的一个快照。这棵树递归列出了目录中的文件和子目录的散列。 所以每个提交都是整个目录树的快照。 这种使用散列值来存储东西的好处是,如果树中的任意一个文件发生改变,则整个树的散列也会跟着发生改变。相反,如果一个文件或子目录没有改变,则散列也不会改变。所以你可以高效地存储目录树中的变更。 这是一个用cat-file pretty 2226命令打印出来的树对象的示例(每一行打印的内容为:文件模式、对象类型、散列和文件名):
函数write_tree用于写树对象。Git文件格式的奇怪之处在于它混合了二进制和文本,例如,树对象中的每一“行”首先是文本:“模式、空格、路径”,然后是NUL字节,然后是二进制SHA-1散列。 这是我们的write_tree()函数:
其次是提交对象。 它记录了树的散列值、父提交、作者、时间戳,以及提交信息。合并功能是Git的优点之一,但是pygit只支持单一的线性分支,所以只有一个父提交(如果是第一次提交,则没有父提交)。 这是一个提交对象的例子,再次使用cat-file pretty aa8d命令打印出来:
这个是我们的提交函数,再次感谢Git的对象模型,相当的简单:
与服务器交互 接下来是稍微有点困难的部分了,因为我们要让pygit与一个真正的Git服务器进行通信(我将把pygit自身推送到GitHub,但它也适用于Bitbucket和其他服务器)。 其基本思想是首先查询服务器上即将要提交的主分支,然后确定等待提交的本地对象集,最后,更新远程的提交散列值,并发送包含所有缺少的对象的“打包文件”。 这被称为“智能协议”。直到2011年,GitHub才停止了对“愚蠢”传输协议的支持,该协议是将.git目录中的文件直接传输过去,所以实现起来更加容易。这里,我们必须得使用“智能协议”将对象打包到一个文件中。 在最后的工作阶段,我使用了Python的http.server模块实现了一个小型的HTTP服务器,这样,我就可以运行其他的git客户端与这个服务器进行交互,以此来查看真正的请求与相应数据。 pkt-line格式 传输协议的关键部分之一是“pkt-line”格式,它是用于发送元数据(如提交散列)的数据报文格式。报文的开头是长度值。每“行”开头是4个十六进制字符表示的长度值(所表示的长度要包含这个长度值字段),所以,包的长度必须小于这4个字符表示的数值。 每行的最后都有一个LF字符。数据结尾的0000是段结束标记。 例如,这个是GitHub对git-receive-pack GET请求的响应报文。请注意,额外的换行符和缩进并不是报文的一部分。 (责任编辑:本港台直播) |