Apakah objek pohon kosong semi-rahasia git dapat diandalkan, dan mengapa tidak ada nama simbolis untuk itu?

125

Git memiliki pohon kosong yang terkenal, atau setidaknya terkenal, yang SHA1-nya adalah:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(Anda dapat melihat ini di repo mana pun, bahkan yang baru dibuat, dengan git cat-file -tdan git cat-file -p).

Jika Anda bekerja keras dan sangat berhati-hati, Anda dapat menggunakan pohon kosong ini untuk menyimpan direktori yang tidak memiliki file (lihat jawaban Bagaimana cara menambahkan direktori kosong ke repositori git ), meskipun itu bukan ide yang bagus.

Ini lebih berguna sebagai satu argumen git diff-tree, yang mana dari salah satu contoh hook.

Yang saya ingin tahu adalah,

  1. seberapa andal hal ini — misalnya, apakah beberapa versi git mendatang tidak akan memiliki objek git yang diberi nomor 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. Mengapa tidak ada nama simbolis untuk pohon kosong (atau apakah ada?).

(Cara cepat dan kotor untuk membuat nama simbolis adalah dengan memasukkan SHA1, misalnya .git/Nulltree,. Sayangnya Anda harus melakukan ini untuk setiap repo. Tampaknya lebih baik hanya memasukkan angka ajaib dalam skrip, dll. Saya hanya enggan secara umum ke angka ajaib.)

torek
sumber
3
hanya untuk mengingat hash ;-) gunakan SHA1 ("tree 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 adalah karakter NUL)
Thomas
4
@ Thomas: git hash-object -t tree /dev/nullmetode (dari jawaban VonC di bawah) memiliki keuntungan dari tidak hard-coding SHA-1, dalam kasus beberapa versi git beralih ke SHA-2 misalnya. (Saya tidak akan mencoba untuk memprediksi kapan itu mungkin terjadi. :-) Akan lebih mudah untuk mengganti Mercurial ke SHA-2, karena mereka meninggalkan ruang untuk itu.)
torek
karena sebab Anda benar, tetapi itu adalah bagian yang baik dari "Pengetahuan yang Tidak Berguna" dan semoga bermanfaat bagi orang lain ?!
Thomas
2
@ Thomas: sepertinya perubahan algoritme hash mungkin terjadi lebih cepat dari yang diharapkan . :-)
torek
Berbicara tentang "beberapa versi Git yang akan datang", saya pikir Anda akan tertarik dengan hasil edit terbaru saya (Desember 2017) pada jawaban 2012 saya: stackoverflow.com/revisions/9766506/7
VonC

Jawaban:

104

Utas ini menyebutkan:

Jika Anda tidak ingat pohon kosong sha1, Anda selalu dapat memperolehnya dengan:

git hash-object -t tree /dev/null

Atau, seperti yang diusulkan Ciro Santilli di komentar :

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

Atau, seperti yang terlihat di sini , dari Colin Schimmelfing :

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

Jadi saya kira lebih aman untuk mendefinisikan variabel dengan hasil perintah itu sebagai pohon sha1 kosong Anda (daripada mengandalkan "nilai terkenal").

Catatan: Git 2.25.1 (Feb. 2020) mengusulkan dalam commit 9c8a294 :

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

Dan menambahkan:

Sebagai catatan sejarah, fungsi yang sekarang dikenal sebagai repo_read_object_file()yang diajarkan pohon kosong di 346245a1bb ("kode-keras objek pohon kosong", 2008-02-13, Git v1.5.5-rc0 - merge ), dan fungsi yang sekarang dikenal seperti oid_object_info()yang diajarkan pohon kosong di c4d9986f5f (" sha1_object_info: memeriksa cached_objectpenyimpanan juga", 2011-02-07, Git v1.7.4.1).


Perhatikan, Anda akan melihat bahwa SHA1 muncul di beberapa repo GitHub saat penulis ingin komit pertamanya dikosongkan (lihat entri blog " Bagaimana saya menginisialisasi repositori Git saya "):

$ 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'

Akan memberimu:

Pohon kosong SHA1

(Lihat pohon SHA1?)

Anda bahkan dapat mendasarkan kembali riwayat yang ada di atas komit kosong itu (lihat " git: bagaimana cara memasukkan komit sebagai yang pertama, menggeser semua yang lain? ")

Dalam kedua kasus tersebut, Anda tidak bergantung pada nilai SHA1 yang tepat dari pohon kosong tersebut.
Anda cukup mengikuti praktik terbaik, menginisialisasi repo Anda dengan komit kosong pertama .


Untuk melakukannya:

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"

Itu akan menghasilkan komit dengan SHA1 khusus untuk repo Anda, nama pengguna, email, tanggal pembuatan (artinya SHA1 dari komit itu sendiri akan berbeda setiap saat).
Tapi pohon yang direferensikan oleh komit itu adalah 4b825dc642cb6eb9a060e54bf8d69288fbee4904, pohon kosong SHA1.

git log --pretty=raw

commit 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904      <====
author VonC <[email protected]> 1381232247 +0200
committer VonC <[email protected]> 1381232247 +0200

    initial empty commit

Untuk memperlihatkan hanya pohon komit (tampilkan pohon komit SHA1):

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

Jika komit itu, merujuk pada pohon kosong, memang merupakan komit pertama Anda, Anda dapat menunjukkan SHA1 pohon kosong itu dengan:

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

(dan itu bahkan berfungsi di Windows, dengan perintah Gnu On Windows )


Seperti yang dikomentari di bawah ini , menggunakan git diff <commit> HEAD, ini akan menampilkan semua file Anda di cabang HEAD saat ini:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Catatan: nilai pohon kosong secara resmi didefinisikan di cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Sejak Git 2.16 (Q1 2018), ini digunakan dalam struktur yang tidak lagi terikat dengan (hanya) SHA1, seperti yang terlihat di commit eb0ccfd :

Ganti pencarian pohon dan blob kosong untuk menggunakan abstraksi hash

Ganti penggunaan empty_tree_oiddan empty_blob_oiduntuk menggunakan current_hashabstraksi yang mewakili algoritme hash saat ini yang digunakan.

Lihat selengkapnya di " Mengapa Git tidak menggunakan SHA yang lebih modern? ": Ini adalah SHA-2 , sejak Git 2.19 (Q3 2018)


Dengan Git 2.25 (Q1 2020), pengujian sedang mempersiapkan transisi SHA-2 , dan melibatkan pohon kosong.

Lihat komit fa26d5e , komit cf02be8 , komit 38ee26b , komit 37ab8eb , komit 0370b35 , komit 0253e12 , komit 45e2ef2 , komit 79b0edc , komit 840624f , komit 32a6707 , komit 440bf91 , komit 0b408ca , komit 2eabd38 (28 Oktober 2019), dan komit 1bcef51 , komit ecde49b (05 Okt 2019) oleh brian m. carlson ( bk2204) .
(Digabung oleh Junio ​​C Hamano - gitster- di commit 28014c1, 10 Nov 2019)

t/oid-info: tambahkan pohon kosong dan nilai blob kosong

Ditandatangani oleh: brian m. carlson

Testsuite pada akhirnya akan mempelajari cara menjalankan menggunakan algoritme selain SHA-1. Dalam persiapan untuk ini, ajarkan test_oidkeluarga fungsi cara mencari gumpalan kosong dan nilai pohon kosong agar dapat digunakan.

Jadi t/oid-info/hash-infosekarang termasuk:

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" adalah 4b825dc642cb6eb9a060e54bf8d69288fbee4904pohon kosong SHA1 " " baru.

VonC
sumber
@torek: Saya telah menambahkan beberapa contoh di sekitar praktik terbaik komit kosong pertama untuk menggambarkan pohon kosong SHA1 itu.
VonC
Nah, salah satu tujuannya adalah menggunakan hash "pohon kosong" sebagai argumen git diff-treedalam beberapa skrip yang saya tulis. Tidak ada jaminan bahwa ada komit kosong awal di repo. Jadi saya hanya ingin tahu apakah skrip ini mungkin akan rusak suatu hari nanti.
torek
1
Jika Anda meneruskan -wke git hash-object, itu akan membuat objek dalam repositori tempat menjalankannya, dan itu akan membuat ulang pohon kosong di repositori yang Anda hadapi seandainya itu akan hilang di masa depan.
javawizard
Jika Anda ingin pergi sebelum komit pertama menggunakan rebase, Anda dapat menggunakan git rebase --root
GergelyPolonkai
1
Atau jika Anda lebih suka keajaiban pipa daripada keajaiban /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
3

Saya menulis entri blog dengan dua cara berbeda untuk menemukan hash: http://colinschimmelfing.com/blog/gits-empty-tree/

Jika ingin berubah karena suatu alasan, Anda dapat menggunakan dua cara di bawah ini untuk menemukannya. Namun, saya akan merasa cukup percaya diri menggunakan hash dalam alias .bashrc, dll., Dan menurut saya itu tidak akan berubah dalam waktu dekat. Setidaknya itu mungkin rilis utama dari git.

Kedua cara tersebut adalah:

  1. Jawaban diatas: git hash-object -t tree --stdin < /dev/null
  2. Cukup initing repo kosong dan kemudian jalankan git write-treedi repo baru itu - hash akan dikeluarkan oleh git write-tree.
licik
sumber
Menjalankan perintah dengan –-stdinmemberi saya fatal: Cannot open '–-stdin': No such file or directorygit 2.7.2. Namun, menjalankannya tanpa --stdinseperti dalam jawaban VonC memberikan nilai hash
sigy
Jawaban ini tidak terlalu berguna sekarang posting blog sudah mati. Oleh karena itu mengapa kami umumnya tidak menyetujui jawaban ini di SO.
Philip Whitehouse
1
@PhilipWhitehouse posting blog tidak mati, tetapi dalam kasus apa pun saya menyertakan dua cara dalam jawaban saya - Saya setuju bahwa tanpa menyertakan kedua cara itu, itu tidak akan menjadi jawaban yang baik.
schimmy
3

Berikut adalah jawaban tentang cara membuat komit pohon kosong bahkan dalam kasus ketika repositori belum kosong. https://stackoverflow.com/a/14623458/9361507

Tapi saya lebih suka "kosong" untuk diberi tag, tapi bukan cabang. Cara sederhananya adalah:

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

Karena tag bisa menunjuk ke tree-ish secara langsung, tanpa komit. Sekarang untuk mendapatkan semua file di pohon kerja:

git diff --name-only empty

Atau sama dengan stat:

git diff --stat empty

Semua file sebagai diff:

git diff empty

Periksa spasi putih di semua file:

git diff --check empty
Olleg
sumber
... tetapi menggunakan nomor ajaib dalam pembuatan tag Anda hanya menyikat di bawah permadani masalah pertanyaan ( tidak menggunakan nomor ajaib SHA-1)
RomainValeri
Tidak benar. Saya menggunakan tag untuk menunjuk ke objek pohon. Saat ini tree-ish ini didefinisikan oleh SHA-1, di masa mendatang dapat diubah, misalnya, menjadi SHA-256 dan seterusnya (dengan migrasi repositori). Tapi tagnya akan sama. :) Fitur utama dari sebuah tag adalah menunjuk ke objek. Sebuah tag dapat menggunakan SHA-1 secara internal atau yang lainnya, ini hanya masalah internal Git.
Olleg
Saya mengerti. Tetapi jika Anda (atau siapa pun yang membaca ini) (atau skrip , bahkan lebih buruk) mencoba menerapkannya (baris pertama Anda) di kemudian hari, ini bisa gagal pada algoritme hash baru, di mana mengganti baris pertama Anda dengan ekspresi yang dieksekusi (menghasilkan hash ini) akan terus berhasil.
RomainValeri
Jika Anda menggabungkan ini dengan salah satu metode untuk menghasilkan hash pohon kosong secara otomatis, Anda dapat membuktikannya di masa mendatang (seperti yang disarankan @RomainValeri). Namun, jika terserah saya, git rev-parseakan memiliki flag atau kata kunci baru atau sesuatu di sepanjang baris tersebut, untuk menghasilkan (a) hash pohon kosong dan (b) hash komit null. Keduanya akan berguna dalam skrip dan akan melindungi dari perubahan SHA-256 yang diusulkan.
torek
Oke, berubah. Tapi ini bukan "cara yang paling sederhana". :)
Olleg