在 Internet 的许多部分(例如此处),暗示 git 提交必须在完成时签名或从不签名。
但是,从技术上讲,提交上的签名只不过是提交对象上的签名(如此处所示),它由“树”文件的哈希(即 git 对象的哈希列表)、哈希父级和一些元数据。
因此,似乎没有什么可以阻止提交在事后签署,而无需重写整个历史。
真的有可能吗?有推荐的方法吗?这样的事后签名是否可以很好地与推拉配合使用?
我不相信你可以在不重写历史的情况下做到这一点。我刚刚克隆了两次相同的存储库进行测试。我在两个存储库中进行了相同的更改,然后使用相同的日志消息(“foobar”)提交,唯一的区别是一个已签名而一个未签名。
# unsigned test
parent 50c6dd65f1d7a240cf6b5c9585ce363ef4708d1e
new b3ff731922f80a417b84ed492537c1f7ba74715e
# signed test
parent 50c6dd65f1d7a240cf6b5c9585ce363ef4708d1e
new 688b3be2e55558c45b00b6a6c02086a03768e02d
如您所见,从同一个父级(50c6dd65)开始,结果是两个不同的提交哈希。因此,对于未推送的提交,它与任何其他历史重写没有任何不同(因此它承担相同的责任)。
在回应您询问哈希是否仅仅因为时间戳不同而改变的评论时,我不这么认为。如果您检查使用cat-file
:
$ git cat-file -p 688b3be2e55558c45b00b6a6c02086a03768e02d
tree 074e53e54670dea3502229e9494f3d571f5dcc16
parent 50c6dd65f1d7a240cf6b5c9585ce363ef4708d1e
author Dan Lowe <dan@XXXXXXXX.com> 1448768563 -0500
committer Dan Lowe <dan@XXXXXXXX.com> 1448768563 -0500
gpgsig -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQEVAwUAVlp0N1rGfrtJ2k+kAQIbYQf7BLx3/jqU/vwvoJOcbq5MPK0ok7B8mOaF
VWhNCbAeOBMzXdrn8IQxY2xYcPsy+d6pNx6ZOF70L3VZm6rWFxNzZQRrjS4ByOAM
VyoL8bXceMcrb/sQUHM5yTCaDcfoYx40K0q91XsGew2EIzNKcOraK1Ee4hEtPg1D
ojyPVjiWz2qMJJ0YYAATSvWwlKFO2ShTC6tGZDHrx0e6BAEN5QS4KdGhNech/vpU
IPFDjIKWtGPTbYY8Z95vKLAMYWPZDJ8j/x1gRytN8PDjRufRtpRnZMccB6JQoXNZ
5L23WQFfUFeXRdWf0MtkrbrSwzuaaIF8l1oGYnEtYT6nOIktPT47Fw==
=/U9b
-----END PGP SIGNATURE-----
foobar
据我了解,此元数据是哈希算法输入的所有部分,从而产生提交 ID。如果是这种情况,gpgsig
此处数据的存在意味着它将始终为您提供与提交的未签名版本不同的哈希值。
每个 git 对象都由它自己的 SHA-1 哈希标识。所以它们都是不可变的。
如果你签署了一个你之前已经完成的提交,你实际上是在复制那个提交,但是是以一种签名的方式。所以原始提交仍然存在 -git show
如果你记得它是哈希或有任何 ref 指向它,你甚至可以它。您要做的是创建一个具有等效更改和元数据的新提交,但添加了签名,并使您当前所在的 ref - 即您当前的分支 - 指向它。
提交的树和所有其他依赖对象将是相同的。但是,随着提交本身发生了变化,你得到了一个新的提交对象——所以你正在重写历史。