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


124

(によって返されるツリーオブジェクトに保存されているSHA1ハッシュgit ls-tree)(で返されるファイルの内容のSHA1ハッシュが一致しませんsha1sum

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

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



1
詳細については、progit.org
book /

5
現在、netvopeのリンクは機能していないようです。これは新しい場所だと思います:git-scm.com/bookの9.2であるgit-scm.com/book/en/Git-Internals-Git-Objects
Rhubbarb

回答:


122

Gitは、オブジェクトの前に「blob」を付け、その後に長さ(人間が読み取れる整数として)、NUL文字を続けます。

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

出典:http : //alblue.bandlem.com/2011/08/git-tip-of-week-objects.html


2
また、「\ r \ n」を「\ n」に置き換えますが、孤立した「\ r」はそのままにしておきます。
user420667

8
^上記のコメントへの修正:時々 gitの1のEOL / autocrlfの設定によっては、上記の交換を行います。
user420667

5
これをの出力と比較することもできecho 'Hello, World!' | git hash-object --stdinます。オプションで--no-filters、crlf変換が発生しないことを確認するように指定するか、(@ user420667を--path=somethi.ng介してgitattributes)指定したフィルターをgitに使用させることを指定できます。そして、-w実際にブロブを提出する.git/objects(あなたがいる場合です Gitのレポで)。
トビアスキエンツラー2017

意味を理解するために、等価性を表現:echo -e 'blob 16\0Hello, \r\nWorld!' | shasum == echo -e 'Hello, \r\nWorld!' | git hash-object --stdin --no-filters 、それはまたと同等になります\nと、15
ピーター・クラウス

1
echo出力に改行を追加します。これもgitに渡されます。そのため、その14文字です。改行なしでエコーを使用するには、次のように記述しますecho -n 'Hello, World!'
Bouke Versteegh

36

私は回答を@Leif Gruenwoldtさらに拡張し、以下によって提供される参考文献の内容を詳しく説明します@Leif Gruenwoldt

自分でやれ..

  • ステップ1.リポジトリーに空のテキスト文書(名前は問題ではない)を作成する
  • ステップ2.ドキュメントをステージングしてコミットする
  • ステップ3.実行してblobのハッシュを特定する git ls-tree HEAD
  • ステップ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オブジェクトがどのようにハッシュされるかについては言及されていません。私はそれはおそらく、すべてに基づいてハッシュを計算しかし私の観察から、アルゴリズムやパラメータの特定はないですblobsし、treesそれが含まれています(おそらく、そのハッシュ)


SHA1("blob" + <size_of_file>-BLOBとサイズの間に追加のスペース文字がありますか?サイズは10進数ですか?プレフィックスがゼロですか?
osgx

1
@osgxあります。リファレンスと私のテストはそう確認します。答えを修正しました。サイズは、プレフィックスのない整数としてのバイト数のようです。
サミュエルハーマー2017年

13

git hash-object

これは、テスト方法を確認する簡単な方法です。

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

出力:

f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f
f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f  -

どこ sha1sumGNU Coreutilsのあるか。

次に、各オブジェクトタイプの形式を理解します。私たちはすでにささいなことをカバーしましたblob、ここに他のものがあります:


前の回答で述べたように、長さはむしろとして計算されるべき$(printf "\0$s" | wc -c)です。追加された空の文字に注意してください。つまり、文字列が 'abc'で、前に空の文字が追加されている場合、長さは3ではなく4になります。その後、sha1sumの結果はgit hash-objectと一致します。
Michael Ekoka

あなたは正しい、彼らは一致しています。ここでは、echo -eではなく、printfを使用することにより、少し有害な副作用があるようです。文字列 'abc'を含むファイルにgit hash-objectを適用すると、8baef1b ... f903が得られます。これは、printfではなく、echo -eを使用したときに得られるものです。echo -eが文字列の最後に改行を追加する場合、動作をprintfと一致させると同じように実行できます(つまり、s = "$ s \ n")。
Michael Ekoka

3

Leif Gruenwoldtの回答に基づいて、以下のシェル関数の代替を示し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

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')、そこにもが必要になる場合があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.