gitの半秘密の空のツリーオブジェクトは信頼できますか。また、そのシンボル名がないのはなぜですか。


125

Gitには、よく知られている、または少なくともよく知られている、空のツリーがあり、そのSHA1は次のとおりです。

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(これは、どのリポジトリでも、新しく作成されたリポジトリでも、git cat-file -tおよびで確認できますgit cat-file -p)。

懸命に働き、非常に注意深い場合は、この空のツリーを使用して、ファイルのないディレクトリを保存することができます(Gitリポジトリに空のディレクトリを追加する方法の回答を参照してください)。

これgit diff-treeは、サンプルフックの1つが行うの1つの引数としてより便利です。

私が思っているのは、

  1. これはどの程度信頼できます4b825dc642cb6eb9a060e54bf8d69288fbee4904か?つまり、将来のバージョンのgitでは、gitオブジェクトに番号が付けられなくなりますか?
  2. 空のツリーにシンボリック名がないのはなぜですか(またはあるのですか?)。

(シンボリック名を作成するすばやくて汚い方法は、たとえばSHA1をに置く.git/Nulltreeことです。残念ながら、すべてのリポジトリに対してこれを行う必要があります。マジックナンバーをスクリプトなどに入れる方がよいようです。私は一般的な嫌悪感を持っていますマジックナンバーに。)


3
ハッシュを覚えておくだけです;-) SHA1( "tree 0 \ 0")= 4b825dc642cb6eb9a060e54bf8d69288fbee4904(\ 0はNUL文字)
Thomas

4
@Thomas:git hash-object -t tree /dev/null将来のバージョンのgitがSHA-2に切り替わった場合に備えて、この方法(以下のVonCの回答から)にはSHA-1をハードコーディングしないという利点があります。(私はそれが起こる可能性がある場合を予測しようとするつもりはない:-)彼らはそれのために部屋を出たので、SHA-2にはMercurialを切り替えることが容易になるだろう。。)
torek

あなたの言う通りですが、それは「役に立たない知識」の良い部分であり、どんな場合でも他の人に役立つでしょうか?!
トーマス

2
@Thomas:ハッシュアルゴリズムの切り替えが予想よりも早く発生する可能性があるようです。:-)
torek 2017年

:「Gitリポジトリのいくつかの将来のバージョン」といえば、私はあなたが私の2012年の答えに私の最新(2017年12月)の編集に興味があるだろうと思いますstackoverflow.com/revisions/9766506/7
VonC

回答:


104

このスレッドは次のように述べています:

空のツリーsha1を覚えていない場合は、いつでもそれを派生させることができます。

git hash-object -t tree /dev/null

または、Ciro Santilliコメントで提案しているように

printf '' | git hash-object --stdin -t tree

または、ここ見られるようにコリン・シムルフィングから:

git hash-object -t tree --stdin < /dev/null

したがって、そのコマンドの結果を変数に空のsha1ツリーとして定義する方が(「既知の値」に依存するのではなく)より安全だと思います。

注:Git 2.25.1(2020年2月)は、コミット9c8a294で提案しています

empty_tree=$(git mktree </dev/null)
# Windows:
git mktree <NUL

そして追加:

歴史的な注記として、346245a1bbでrepo_read_object_file()空のツリーを教えられたように現在知られている関数( "空のツリーオブジェクトのハードコード"、2008-02-13、Git v1.5.5-rc0- merge)、および現在知られている関数c4d9986f5fで空のツリーを教えられたように( " :ストアも調べて"、2011-02-07、Git v1.7.4.1)。 oid_object_info()sha1_object_infocached_object


注:著者が最初のコミットを空にしたい場合、SHA1がGitHubリポジトリにポップアップ表示されることがわかります(ブログ投稿「Gitリポジトリを初期化する方法」を参照):

$ GIT_AUTHOR_DATE="Thu, 01 Jan 1970 00:00:00 +0000" GIT_COMMITTER_DATE="Thu, 01 Jan 1970 00:00:00 +0000" git commit --allow-empty -m 'Initial commit'

あなたに与えるでしょう:

空の木SHA1

(ツリーSHA1を参照してください?)

その空のコミットの上に既存の履歴をリベースすることもできます(「git:最初にコミットを挿入し、他のすべてをシフトする方法」を参照)。

どちらの場合も、その空のツリーの正確なSHA1値には依存しません。ベストプラクティス
に従って、最初の空のcommitでリポジトリを初期化します


それを行うには:

git init my_new_repo
cd my_new_repo
git config user.name username
git config user.email email@com

git commit --allow-empty -m "initial empty commit"

これにより、リポジトリ、ユーザー名、メールアドレス、作成日に固有のSHA1を持つコミットが生成されます(つまり、コミット自体のSHA1は毎回異なります)。
しかし、そのコミットによって参照されるツリー4b825dc642cb6eb9a060e54bf8d69288fbee4904は、空のツリーSHA1になります。

git log --pretty=raw

commit 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904      <====
author VonC <vonc@laposte.net> 1381232247 +0200
committer VonC <vonc@laposte.net> 1381232247 +0200

    initial empty commit

コミットのツリーのみを表示するには(コミットツリーSHA1を表示):

git show --pretty=format:%T 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
4b825dc642cb6eb9a060e54bf8d69288fbee4904

空のツリーを参照するそのコミットが確かに最初のコミットである場合、その空のツリーSHA1を次のように表示できます。

git log --pretty=format:%h --reverse | head -1 | xargs git show --pretty=format:%T
4b825dc642cb6eb9a060e54bf8d69288fbee4904

(そして、それはWindowsでGnu On Windowsコマンドを使って動作します)


以下のコメント使用して、git diff <commit> HEADこれは現在のブランチのHEAD内のすべてのファイルが表示されます:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

注:空のツリー値はで正式に定義されていcache.hます。

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Git 2.16(2018年第1四半期)以降、コミットeb0ccfdに見られるように、SHA1(のみ)に関連付けられなくなった構造で使用されます

空のツリーとblobルックアップを切り替えてハッシュ抽象化を使用する

使用を切り替えempty_tree_oidしてempty_blob_oid使用するようにcurrent_hash現在使用中のハッシュアルゴリズムを表して抽象化を。

詳しくは、「Gitが最新のSHAを使用しない理由」をご覧ください。Git2.19(2018年第3四半期)以降はSHA-2です。


Git 2.25(2020年第1四半期)では、テストはSHA-2移行の準備中であり、空のツリーが関係しています。

参照fa26d5eをコミットしcf02be8をコミットし38ee26bをコミットし37ab8ebをコミットし0370b35をコミットし0253e12をコミットし45e2ef2をコミットし79b0edcをコミットし840624fをコミットし32a6707をコミットし440bf91をコミットし0b408caをコミットし2eabd38をコミット(2019年10月28日)、および1bcef51をコミットしコミットecde49b(2019年10月5日)by brian m。カールソン(bk2204
(合併によりJunio C浜野- gitster-28014c1コミット、2019年11月10日)

t/oid-info:空のツリーと空のblob値を追加します

サインオフ:brian m。カールソン

テストスイートは最終的にSHA-1以外のアルゴリズムを使用して実行する方法を学習します。これに備えてtest_oid、関数のファミリーに、空のblobと空のツリーの値を調べて使用できるようにする方法を教えます。

だからt/oid-info/hash-info今含まれています:

rawsz sha1:20
rawsz sha256:32

hexsz sha1:40
hexsz sha256:64

zero sha1:0000000000000000000000000000000000000000
zero sha256:0000000000000000000000000000000000000000000000000000000000000000

algo sha1:sha1
algo sha256:sha256

empty_blob sha1:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813

empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321

SHA2 " 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321"は新しいSHA1 " 4b825dc642cb6eb9a060e54bf8d69288fbee4904"空のツリーです。


@torek:空のツリーSHA1を説明するために、最初の空のコミットのベストプラクティスにいくつかの例を追加しました。
VonC 2012年

さて、目標の1つは、git diff-tree私が書いているスクリプトの引数として「空のツリー」ハッシュを使用することです。リポジトリに最初の空のコミットがあるという保証はありません。これらのスクリプトがいつか壊れるのではないかと思っています。
torek 2012年

1
に渡し-wた場合git hash-object、オブジェクトが実行されるリポジトリにオブジェクトが作成されます。これにより、実行されているリポジトリに空のツリーが再作成され、将来はなくなるでしょう。
javawizard 2014年

rebaseを使用して最初のコミットの前に移動したい場合は、git rebase --root
GergelyPolonkai

1
または、/dev/nullprintf '' | git hash-object --stdin -t tree:) の魔法ではなくパイプの魔法を好む場合
Ciro Santilli郝海东冠状病六四事件法轮功

3

私はハッシュを見つける2つの異なる方法でブログの投稿を書きました:http : //colinschimmelfing.com/blog/gits-empty-tree/

なんらかの理由で変更された場合は、以下の2つの方法で見つけることができます。しかし、.bashrcエイリアスなどでハッシュを使用することにはかなり自信があると思いますし、すぐに変更されるとは思いません。少なくとも、それはおそらくgitのメジャーリリースでしょう。

2つの方法は次のとおりです。

  1. 上記の答え: git hash-object -t tree --stdin < /dev/null
  2. 空のリポジトリを初期化し、git write-treeその新しいリポジトリで実行するだけです。ハッシュはgit write-treeによって出力されます。

でコマンドを実行すると、–-stdin私を与えfatal: Cannot open '–-stdin': No such file or directorygitの2.7.2で。ただし、--stdinVonCの回答のように実行せずに実行すると、ハッシュ値が得られます
sigy

この答えは、ブログ投稿が死んでしまった今ではあまり役に立ちません。したがって、SOでこれらの回答を一般に承認しないのはなぜですか。
フィリップホワイトハウス2017

1
@PhilipWhitehouseブログ投稿は死んではいませんが、いずれの場合でも、2つの方法を私の回答に含めました-これら2つの方法を含めなければ、それは良い回答ではないことに同意します。
schimmy 2017

3

リポジトリがまだ空でない場合でも、空のツリーコミットを作成する方法についての答えは次のとおりです。 https://stackoverflow.com/a/14623458/9361507

しかし、私はタグではなく「ブランチ」ではなく「空」を好みます。簡単な方法は:

git tag empty $(git hash-object -t tree /dev/null)

なぜなら、タグはコミットせずにツリーっぽいものを直接指すことができるからです。次に、作業ツリー内のすべてのファイルを取得します。

git diff --name-only empty

またはstatと同じ:

git diff --stat empty

差分としてすべてのファイル:

git diff empty

すべてのファイルの空白を確認します。

git diff --check empty

...しかし、タグの作成でマジックナンバーを使用することは、問題の問題そのものであるラグの下をブラッシングするだけです(マジックナンバーSHA-1を使用しない
RomainValeri

違います。ツリーのようなオブジェクトを指すためにタグを使用しました。現在、このツリーのようなものはSHA-1によって定義されており、将来的には、たとえばSHA-256などに変更できるようになります(リポジトリの移行により)。しかしタグは同じになります。:)タグの主な機能は、オブジェクトを指すことです。タグはSHA-1を内部で使用することも、他の何かを使用することもできます。これはGit内部のみの問題です。
Olleg

わかった。しかし、あなた(またはこれを読んでいる人)(またはスクリプト、さらに悪いことに)が後でそれ(最初の行)を適用しようとすると、新しいハッシュアルゴリズムで失敗する可能性があります。このハッシュ)は引き続き成功します。
RomainValeri

これを空のツリーハッシュを自動的に生成する方法の1つと組み合わせると、これを将来にわたって保証できます(@RomainValeriが示唆しています)。しかし、それが私次第である場合git rev-parse、(a)空のツリーハッシュと(b)null-commitハッシュを生成するために、これらの行に沿って新しいフラグまたはキーワードまたは何かを持つことになります。これらはどちらもスクリプトで役立ち、提案されているSHA-256の変更から保護されます。
トレック

変更されました。しかし、これは「最も簡単な方法」ではありません。:)
Olleg
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.