Là đối tượng cây trống bán bí mật của git có đáng tin cậy không, và tại sao không có tên tượng trưng cho nó?


125

Git có một cây trống nổi tiếng, hoặc ít nhất là nổi tiếng, có SHA1 là:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(bạn có thể thấy điều này trong bất kỳ repo nào, ngay cả một cái mới được tạo, với git cat-file -tgit cat-file -p).

Nếu bạn làm việc chăm chỉ và rất cẩn thận, bạn có thể sử dụng cây trống này để lưu trữ một thư mục không có tệp (xem câu trả lời Làm thế nào để tôi thêm một thư mục trống vào kho git ), mặc dù đó không thực sự là một ý tưởng tuyệt vời.

Nó hữu ích hơn khi là một đối số git diff-tree, mà một trong các hook mẫu thực hiện.

Điều tôi đang tự hỏi là,

  1. Làm thế nào đáng tin cậy này, tức là một số phiên bản tương lai của git sẽ không có đối tượng git được đánh số 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. Tại sao không có tên tượng trưng cho cây trống (hoặc có một cái?).

(Một cách nhanh chóng và bẩn thỉu để tạo một cái tên tượng trưng là đặt SHA1 vào, vd .git/Nulltree. Thật không may, bạn phải làm điều này cho mỗi repo. Có vẻ tốt hơn là chỉ đưa số ma thuật vào các tập lệnh, v.v. Tôi chỉ có ác cảm chung đến số ma thuật.)


3
chỉ cần nhớ hàm băm ;-) sử dụng SHA1 ("cây 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 là ký tự NUL)
Thomas

4
@Thomas: git hash-object -t tree /dev/nullphương thức (từ câu trả lời của VonC bên dưới) có ưu điểm là không mã hóa cứng SHA-1, trong trường hợp một số phiên bản tương lai của git chuyển sang SHA-2 chẳng hạn. (Tôi sẽ không dự đoán khi nào điều đó có thể xảy ra. :-) Việc chuyển Mercurial sang SHA-2 sẽ dễ dàng hơn, vì họ đã rời khỏi phòng để lấy nó.)
torek

vì bạn đúng nhưng đó là một phần tốt của "Kiến thức vô dụng" và nó có thể hữu ích trong mọi trường hợp cho bất kỳ ai khác không?!
Thomas

2
@Thomas: có vẻ như việc thay đổi thuật toán băm có thể xảy ra sớm hơn dự kiến . :-)

Nói về "một số phiên bản tương lai của Git", tôi nghĩ rằng bạn sẽ quan tâm đến bản chỉnh sửa mới nhất (tháng 12 năm 2017) của tôi theo câu trả lời năm 2012 của tôi: stackoverflow.com/revutions/9766506/7
VonC

Câu trả lời:


104

Chủ đề này đề cập:

Nếu bạn không nhớ cây trống sha1, bạn luôn có thể lấy nó bằng:

git hash-object -t tree /dev/null

Hoặc, như Ciro Santilli đề xuất trong các bình luận :

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

Hoặc, như đã thấy ở đây , từ Colin Schimmelfing :

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

Vì vậy, tôi đoán sẽ an toàn hơn khi định nghĩa một biến với kết quả của lệnh đó là cây sha1 trống của bạn (thay vì dựa vào "giá trị nổi tiếng").

Lưu ý: Git 2.25.1 (tháng 2 năm 2020) đề xuất trong cam kết 9c8a294 :

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

Và thêm:

Như một ghi chú lịch sử, hàm bây giờ được gọi là repo_read_object_file()cây trống trong 346245a1bb ("mã cứng đối tượng cây trống", 2008/02/13, Git v1.5.5-rc0 - hợp nhất ) và hàm hiện được biết đến như oid_object_info()đã được dạy về cây trống trong c4d9986f5f (" sha1_object_info: kiểm tra cached_objectcửa hàng quá", 2011/02/07, Git v1.7.4.1).


Lưu ý, bạn sẽ thấy SHA1 bật lên trên một số repo GitHub khi tác giả muốn cam kết đầu tiên của mình trống (xem bài đăng trên blog " Cách tôi khởi tạo kho Git của tôi "):

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

Sẽ cung cấp cho bạn:

Cây rỗng SHA1

(Xem cây SHA1?)

Bạn thậm chí có thể khởi động lại lịch sử hiện tại của mình trên đầu trang của cam kết trống đó (xem " git: làm thế nào để chèn một cam kết đầu tiên, thay đổi tất cả các cam kết khác? ")

Trong cả hai trường hợp, bạn không dựa vào giá trị SHA1 chính xác của cây trống đó.
Bạn chỉ cần làm theo một thực tiễn tốt nhất, khởi tạo repo của bạn với một cam kết trống đầu tiên .


Để làm việc đó:

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"

Điều đó sẽ tạo ra một cam kết với SHA1 cụ thể cho repo, tên người dùng, email, ngày tạo của bạn (nghĩa là SHA1 của chính cam kết sẽ khác nhau mỗi lần).
Nhưng cây được tham chiếu bởi cam kết đó sẽ là 4b825dc642cb6eb9a060e54bf8d69288fbee4904, cây rỗng 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

Để chỉ hiển thị cây cam kết (hiển thị cây cam kết SHA1):

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

Nếu cam kết đó, tham chiếu một cây trống, thực sự là cam kết đầu tiên của bạn , bạn có thể hiển thị cây trống đó SHA1 với:

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

(và thậm chí hoạt động trên Windows, với các lệnh Gnu On Windows )


Như đã nhận xét bên dưới , bằng cách sử dụng git diff <commit> HEAD, điều này sẽ hiển thị tất cả các tệp của bạn trong nhánh hiện tại CHÍNH:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Lưu ý: giá trị cây trống được định nghĩa chính thức trong cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Kể từ Git 2.16 (Q1 2018), nó được sử dụng trong cấu trúc không còn gắn với (chỉ) SHA1, như đã thấy trong cam kết eb0ccfd :

Chuyển đổi cây rỗng và tra cứu blob để sử dụng trừu tượng băm

Chuyển đổi cách sử dụng empty_tree_oidempty_blob_oidsử dụng tính current_hashtrừu tượng đại diện cho thuật toán băm hiện tại đang sử dụng.

Xem thêm tại " Tại sao Git không sử dụng SHA hiện đại hơn? ": Đó là SHA-2 , kể từ Git 2.19 (quý 3 năm 2018)


Với Git 2.25 (Q1 2020), các thử nghiệm đang chuẩn bị cho quá trình chuyển đổi SHA-2 và liên quan đến cây trống.

Xem cam kết fa26d5e , cam kết cf02be8 , cam kết 38ee26b , cam kết 37ab8eb , cam kết 0370b35 , cam kết 0253e12 , cam kết 45e2ef2 , cam kết 79b0edc , cam kết 840624f , cam kết 32a6707 , cam kết 440bf91 , cam kết 0b408ca , cam kết 2eabd38 (Tháng 10 28, 2019), và cam kết 1bcef51 , cam kết ecde49b (ngày 05 tháng 10 năm 2019) bởi brian m. carlson ( bk2204) .
(Được hợp nhất bởi Junio ​​C Hamano - gitster- trong cam kết 28014c1, Ngày 10 tháng 11 năm 2019)

t/oid-info: thêm cây trống và giá trị blob trống

Đã ký tắt: brian m. carlson

Testsuite cuối cùng sẽ học cách chạy bằng thuật toán khác với SHA-1. Để chuẩn bị cho việc này, hãy dạy cho test_oidgia đình các hàm cách tìm kiếm các đốm trống và giá trị cây trống để chúng có thể được sử dụng.

Vì vậy, t/oid-info/hash-infobây giờ bao gồm:

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" là 4b825dc642cb6eb9a060e54bf8d69288fbee4904cây trống SHA1 " " mới.


@torek: Tôi đã thêm một số ví dụ về việc thực hành tốt nhất cam kết trống đầu tiên để minh họa cây rỗng SHA1 đó.
VonC

Chà, một trong những mục tiêu là sử dụng hàm băm "cây trống" làm đối số git diff-treetrong một số tập lệnh tôi đang viết. Không có gì đảm bảo rằng có một cam kết trống ban đầu trong repo. Vì vậy, tôi chỉ tự hỏi nếu các kịch bản này có thể kết thúc một ngày nào đó.

1
Nếu bạn chuyển -wđến git hash-object, nó sẽ tạo ra đối tượng trong kho lưu trữ mà nó chạy và điều đó sẽ tạo lại cây trống trong kho lưu trữ mà bạn đang chạy để nó biến mất trong tương lai.
javawizard

Nếu bạn muốn đi trước lần xác nhận đầu tiên bằng cách sử dụng rebase, bạn có thể sử dụng git rebase --root
GergelyPolonkai

1
Hoặc nếu bạn thích phép thuật của đường ống thay vì phép thuật của /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli 郝海东 冠状 病 六四

3

Tôi đã viết một bài đăng trên blog với hai cách khác nhau để tìm hàm băm: http://colinschimmelfing.com/blog/gits-empty-tree/

Nếu nó đã từng thay đổi vì một số lý do, bạn có thể sử dụng hai cách dưới đây để tìm thấy nó. Tuy nhiên, tôi sẽ cảm thấy khá tự tin khi sử dụng hàm băm trong các bí danh .bashrc, v.v., và tôi không nghĩ rằng nó sẽ thay đổi bất cứ lúc nào sớm. Ít nhất nó có lẽ sẽ là một bản phát hành chính của git.

Hai cách là:

  1. Câu trả lời ở trên: git hash-object -t tree --stdin < /dev/null
  2. Đơn giản chỉ cần nhập một repo trống và sau đó chạy git write-treetrong repo mới đó - hàm băm sẽ được xuất ra bởi git write-tree.

Chạy lệnh với –-stdincho tôi fatal: Cannot open '–-stdin': No such file or directoryvới git 2.7.2. Tuy nhiên, chạy nó mà không --stdinnhư trong câu trả lời của VonC mang lại giá trị băm
sigy

Câu trả lời này không hữu ích lắm khi bài viết trên blog đã chết. Do đó, tại sao chúng ta thường không chấp nhận những câu trả lời này trên SO.
Philip Whitehouse

1
@PhilipWhitehouse bài đăng trên blog không chết, nhưng trong bất kỳ trường hợp nào tôi đều đưa vào hai cách trong câu trả lời của mình - Tôi đồng ý rằng không bao gồm hai cách đó, nó sẽ không phải là một câu trả lời hay.
schimmy

3

Dưới đây là câu trả lời về cách tạo cam kết cây trống ngay cả trong trường hợp khi kho lưu trữ chưa trống. https://stackoverflow.com/a/14623458/9361507

Nhưng tôi thích "trống" hơn để được gắn thẻ, nhưng không phải là một nhánh. Cách đơn giản là:

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

Bởi vì thẻ có thể trỏ đến cây-ish trực tiếp, không có cam kết. Bây giờ để có được tất cả các tệp trong cây làm việc:

git diff --name-only empty

Hoặc tương tự với stat:

git diff --stat empty

Tất cả các tệp như diff:

git diff empty

Kiểm tra khoảng trắng trong tất cả các tệp:

git diff --check empty

... nhưng sử dụng số ma thuật trong việc tạo thẻ của bạn chỉ là đánh bóng dưới tấm thảm, vấn đề của câu hỏi ( không sử dụng số ma thuật SHA-1)
RomainValeri

Không đúng. Tôi đã sử dụng thẻ để trỏ đến đối tượng cây-ish. Cho đến nay, cây-ish này được xác định bởi SHA-1, trong tương lai, nó có thể được thay đổi thành SHA-256, v.v. (với di chuyển kho lưu trữ). Nhưng thẻ sẽ giống nhau. :) Tính năng chính của thẻ là trỏ đến đối tượng. Một thẻ có thể sử dụng SHA-1 trong nội bộ hoặc một cái gì đó khác, đó chỉ là vấn đề của nội bộ Git.
Olleg

Tôi hiểu rồi Nhưng nếu bạn (hoặc bất kỳ ai đọc nó) (hoặc một tập lệnh , thậm chí tệ hơn) cố gắng áp dụng nó (dòng đầu tiên của bạn) thì sau đó, nó có thể thất bại trên thuật toán băm mới, trong đó thay thế dòng đầu tiên của bạn bằng một biểu thức được thực thi (tạo ra hàm băm này) sẽ tiếp tục thành công.
RomainValeri

Nếu bạn kết hợp điều này với một trong các phương pháp tạo băm cây trống tự động, bạn có thể chứng minh điều này trong tương lai (như @RomainValeri gợi ý). Tuy nhiên, nếu tùy thuộc vào tôi, git rev-parsesẽ có cờ hoặc từ khóa mới hoặc thứ gì đó dọc theo các dòng đó, để tạo ra (a) hàm băm cây trống và (b) hàm băm xác thực null. Cả hai điều này sẽ hữu ích trong các tập lệnh và sẽ bảo vệ chống lại các thay đổi SHA-256 được đề xuất.

Okey, đã thay đổi. Nhưng đây sẽ không phải là "một cách đơn giản nhất". :)
Olleg
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.