【问题标题】:C# Compute GIT Commit HashC# 计算 GIT 提交哈希
【发布时间】:2018-02-24 21:56:56
【问题描述】:

我正在尝试手动获取 GIT 提交的 SHA1 提交哈希,但有些东西无法正常工作。

首先,我们有如下所示的标准提交消息:

tree f594b3f6d9ae291c83902f3992aa36872aa70d68

parent 0000004bf6d464667df5150b4526083886947d92

author User <foo@bar.com> 1390620460.46263 +0000
committer User <foo@bar.com> 1390620460.46263 +0000

Commit Message

我们称之为“commitMessage”

规范说要获得提交哈希,我们必须 sha1:

  • 字符串“提交”
  • 加一个空格“”
  • 加上 commitMessage 中的字节数
  • 加上一个空字节
  • 加上commitMessage

so(伪代码obv)

SHA1( "commit" + " " + numBytes(commitMessage) + '\0' + commitMessage );

这是我在 C# 中的实现 (我知道这很笨重)

    var commitBody = "tree " + treeHash + "\n\n" +
                     "parent " + parentHash + "\n\n" +
                     "author User <foo@bar.com> " + date + "\n" +
                     "committer User <foo@bar.com> " + date + "\n\n" +
                     "My Commit Message\n";

    var blob = "commit " + Encoding.UTF8.GetByteCount(commitBody);

    // This is the string "commit " (with a space) + byte count
    var first = Encoding.UTF8.GetBytes(blob);

    // This is just the null byte
    var second = new byte[1];
    second[0] = (byte)0;

    // This is the commitMessage
    var third = Encoding.UTF8.GetBytes(commitBody);

    // Merge first, second, third into bytez as a byte array
    var bytez = new byte[first.Length + second.Length + third.Length];
    Buffer.BlockCopy(first, 0, bytez, 0, first.Length);
    Buffer.BlockCopy(second, 0, bytez, first.Length, second.Length);
    Buffer.BlockCopy(third, 0, bytez, first.Length + second.Length, third.Length);

    // Debug Print
    Console.WriteLine(Encoding.UTF8.GetString(bytez));

    // Compute the hash and print it
    var sss = SHA1.Create();
    var myssh = GetString(sss.ComputeHash(bytez));
    Console.WriteLine(myssh);

返回的哈希值与从 GIT 返回的不同。我实际上并不指望任何人知道如何做到这一点,因为这不是通常会做的事情,但我想我会问。

感谢您的帮助:D

【问题讨论】:

    标签: c# git sha


    【解决方案1】:

    如果您在字符串中使用 UTF-8 字符,请不要使用 string.Length 保留字节数组。如果字符串仅包含 ASCII 字符,则这是正确的,但如果字符串中有 UTF-8 字符,则 .Length 将小于实际字节大小。

    由于您使用.Length 分配数组,因此该数组可能很小,并且并非所有字符串数据都可以复制。

    我建议您使用StringBuilder 构建您的字符串,然后使用System.Text.Encoding.UTF8.GetBytes(stringbuilder.ToString()) 以字节形式获取数据。

    StringBuilder sb = new StringBuilder();
    sb.Append("commit "+ Encoding.UTF8.GetByteCount(commitBody));
    sb.Append("\0");
    sb.Append(commitBody);
    
    var sss = SHA1.Create();
    var bytez = Encoding.UTF8.GetBytes(sb.ToString());
    var myssh = GetString(sss.ComputeHash(bytez));
    Console.WriteLine(myssh);
    

    【讨论】:

      【解决方案2】:

      每个对象的哈希实际上是“长度 + ' ' + 内容”的哈希 - 这可以防止 SHA1 哈希冲突(因为现在您必须在 SHA1 长度上发生冲突,这不太可能)

      【讨论】:

      • nitpick:我认为您的意思是对象,而不是 blob,因为这与提交有关
      【解决方案3】:

      treeparent 行之后不应有空行,即提交正文应为:

      tree f594b3f6d9ae291c83902f3992aa36872aa70d68
      parent 0000004bf6d464667df5150b4526083886947d92
      author User <foo@bar.com> 1390620460.46263 +0000
      committer User <foo@bar.com> 1390620460.46263 +0000
      
      Commit Message
      

      查看原始的 C 实现; commit.c 中的 commit_tree_extended()。

      【讨论】:

        【解决方案4】:

        不是 C#,但您可以从 bash 提示符计算 git 提交哈希:

        commit_len=$(git cat-file commit HEAD | wc -c)
        (echo -ne "commit $commit_len\0"; git cat-file commit HEAD) | sha1sum
        

        检查哈希是否正确:

        git show HEAD | grep commit
        

        【讨论】:

          猜你喜欢
          • 2018-06-21
          • 2019-05-31
          • 2016-06-23
          • 2011-11-05
          • 1970-01-01
          • 2020-11-01
          • 1970-01-01
          相关资源
          最近更新 更多