如何在Go中进行与Git兼容的十六进制Sha压缩/压缩操作

时间:2019-05-23 06:25:20

标签: ruby git go

我正在阅读詹姆斯·科格兰(James Coglan)的书Building Git,其中詹姆斯将带您逐步学习如何在Ruby中实现Git的基本版本。我决定通过在Go中执行实现来使事情变得更加复杂。

我已经到了需要将压缩的文件内容散列存储到树中以写入磁盘的部分,但是我在执行Git寻找的这种十六进制压缩/打包时遇到了麻烦。

这是我正在使用的Ruby代码

ENTRY_FORMAT = "A7Z*H40"
MODE = "100644"
FILE_NAME = "tree.rb"
SHA = "baae99010b237a699ff0aba02fd5310c18903b1b"
[MODE, FILE_NAME , SHA].pack(ENTRY_FORMAT)

Ruby pack方法显然是:

  

Array#pack方法采用各种值的数组,并返回表示这些值的字符串。字符串中每个值的确切表示方式取决于我们传递给压缩包的格式字符串。

我认为MODEFILE_NAME的编码非常不错。这是编码我苦苦挣扎的sha的最后一部分。

  

•H40:通过将每对数字打包到一个字节中,它对40个十六进制数字的字符串entry.oid进行编码

这是“将每对数字打包成一个字节,我无法理解。这是我目前的尝试:

mode := 100644
fileName := "tree.go"
sha:= "baae99010b237a699ff0aba02fd5310c18903b1b"
// slice of strings for constructing the packed sha
var eid []string

// iterate through each character in id
for i := 0; i < len(sha); i += 2 {
    // gathering them in pairs of two
    one, two := sha[i], sha[i+1]
    // compress two digits into one byte
    // using bitwise or?? addition?? bit shifting?? not sure.
    eid = append(eid, string(one|two))
}
// concat the new packed id with the mode and file name.
stringRep := fmt.Sprintf("%-7d", mode) + fileName + "\x00" + strings.Join(eid, "")

Go playground for above code

由于某种原因,我无法弄清楚,函数生成的树条目的字符串表示形式与Git在磁盘上存储树的方式不兼容。在or之前,我曾尝试过对位进行移位,并且尝试过将字节加在一起,但似乎没有任何效果。我基本上需要以Git可以接受的方式复制Ruby Array#pack方法的行为。

非常感谢任何指导或建议。如果有必要,我很乐于解释更多或发布更多代码示例。非常感谢您的宝贵时间!

P.S。来自Building Git的更多有关打包git的上下文信息

  

Git以压缩格式存储每个条目的ID,每个条目使用20个字节。每个十六进制数字代表一个从零到十五的数字,其中十个由a表示,十一由b表示,依此类推,直到十五代表f。在一个四十位的对象ID中,每一位代表一个160位数字的四位。与其将这些位分成40个每个四位的块,不如将其分成二十个八位的块-八个位是一个字节。因此,这里发生的所有事情都是将160位对象ID以二进制形式存储为20个字节,而不是以40个字符代表十六进制数字。

1 个答案:

答案 0 :(得分:1)

可以在hex package中找到在二进制和十六进制字符串之间转换的函数。

例如:将输入的十六进制字符串转换为字节数组(每个字节包含两个初始十六进制字符串数字)的函数为hex.DecodeStringhex.Decode(如果输入为[]byte而不是string


如果要重新实现此功能:

  • 输入字符串的每个字符都应转换为其数值,
  • 每对值应以16为底的数字:var newByte byte = 16*one + two