web-dev-qa-db-ja.com

gitはファイルハッシュをどのように計算しますか?

ツリーオブジェクトに格納されているSHA1ハッシュ(git ls-tree)ファイルコンテンツのSHA1ハッシュと一致しません(sha1sum

$ git cat-file blob 4716ca912495c805b94a88ef6dc3fb4aff46bf3c | sha1sum
de20247992af0f949ae8df4fa9a37e4a03d7063e  -

Gitはファイルハッシュをどのように計算しますか?ハッシュを計算する前にコンテンツを圧縮しますか?

119
netvope

Gitはオブジェクトに「blob」のプレフィックスを付け、その後に長さ(人間が読み取れる整数)、NUL文字が続きます

$ echo -e 'blob 14\0Hello, World!' | shasum 8ab686eafeb1f44702738c8b0f24f2567c36da6d

ソース: http://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html

114
Leif Gruenwoldt

私は@Leif Gruenwoldtによって答えを拡大し、@Leif Gruenwoldtによって提供される reference に何があるかを詳細に説明しています。

Do It Yourself ..

  • ステップ1.リポジトリーに空のテキスト文書(名前は関係ありません)を作成します
  • ステップ2.文書をステージングしてコミットする
  • ステップ3. git ls-tree HEADを実行してblobのハッシュを特定します
  • 手順4. BLOBのハッシュを見つけてe69de29bb2d1d6434b8b29ae775ad8c2e48c5391にする
  • ステップ5.驚きから抜け出し、以下をお読みください

GITはコミットハッシュをどのように計算しますか

    Commit Hash (SHA1) = SHA1("blob " + <size_of_file> + "\0" + <contents_of_file>)

テキストblob⎵は定数プレフィックスであり、\0も定数であり、NULL文字です。 <size_of_file>および<contents_of_file>は、ファイルによって異なります。

参照: git commitオブジェクトのファイル形式は?

そしてそれはすべての人々です!

しかし、待ってください!<filename>はハッシュ計算に使用されるパラメーターではないことに気づきましたか? 2つのファイルの内容が作成日時と名前に関係なく同じである場合、2つのファイルは同じハッシュを持つ可能性があります。これは、Gitが他のバージョン管理システムよりも移動と名前の変更をうまく処理する理由の1つです。

Do It Yourself(Ext)

  • ステップ6.同じディレクトリに別のfilenameを持つ別の空のファイルを作成します
  • ステップ7.両方のファイルのハッシュを比較します。

注:

リンクには、treeオブジェクトがどのようにハッシュされるかは記載されていません。私はアルゴリズムとパラメータに確信はありませんが、私の観察から、それはおそらくすべてのblobstrees(それらのハッシュ)に基づいてハッシュを計算します

34
Lordbalmon

git hash-object

これは、テストメソッドをすばやく確認する方法です。

s='abc'
printf "$s" | git hash-object --stdin
printf "blob $(printf "$s" | wc -c)\0$s" | sha1sum

出力:

f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f
f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f  -

どこ sha1sumはGNU Coreutilsにあります。

次に、各オブジェクトタイプの形式を理解します。ささいなblobについてはすでに説明しましたが、ここに他のものがあります。

Python 3のいくつかの単体テストでこれが必要だったので、ここに置いておきます。

def git_blob_hash(data):
    if isinstance(data, str):
        data = data.encode()
    data = b'blob ' + str(len(data)).encode() + b'\0' + data
    h = hashlib.sha1()
    h.update(data)
    return h.hexdigest()

私は\nの行末にこだわりますが、状況によってはGitが 行末を変更することもあります このハッシュを計算する前に .replace('\r\n', '\n') もそこにあります。

3
Samuel Harmer

Leif Gruenwoldt answerに基づいて、ここに git hash-object

git-hash-object () { # substitute when the `git` command is not available
    local type=blob
    [ "$1" = "-t" ] && shift && type=$1 && shift
    # depending on eol/autocrlf settings, you may want to substitute CRLFs by LFs
    # by using `Perl -pe 's/\r$//g'` instead of `cat` in the next 2 commands
    local size=$(cat $1 | wc -c | sed 's/ .*$//')
    ( echo -en "$type $size\0"; cat "$1" ) | sha1sum | sed 's/ .*$//'
}

テスト:

$ echo 'Hello, World!' > test.txt
$ git hash-object test.txt
8ab686eafeb1f44702738c8b0f24f2567c36da6d
$ git-hash-object test.txt
8ab686eafeb1f44702738c8b0f24f2567c36da6d
3
Lucas Cimon