วัตถุต้นไม้ว่างเปล่ากึ่งลับของ git เชื่อถือได้หรือไม่และเหตุใดจึงไม่มีชื่อเชิงสัญลักษณ์


125

Git มีต้นไม้ว่างเปล่าที่รู้จักกันดีหรืออย่างน้อยก็เป็นที่รู้จักกันดีซึ่งมี SHA1 คือ:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(คุณสามารถเห็นสิ่งนี้ใน repo ใด ๆ แม้แต่ที่สร้างขึ้นใหม่ด้วยgit cat-file -tและgit cat-file -p)

หากคุณทำงานหนักและระมัดระวังมากคุณสามารถใช้โครงสร้างว่างนี้เพื่อจัดเก็บไดเร็กทอรีที่ไม่มีไฟล์ได้ (ดูคำตอบของฉันจะเพิ่มไดเร็กทอรีว่างลงในที่เก็บ git ได้อย่างไร ) แม้ว่าจะไม่ใช่ความคิดที่ดีจริงๆ

มันมีประโยชน์มากกว่าในการเป็นอาร์กิวเมนต์git diff-treeเดียวซึ่งหนึ่งในตัวอย่าง hooks ทำ

สิ่งที่ฉันสงสัยคือ

  1. สิ่งนี้เชื่อถือได้มากน้อยเพียงใดนั่นคือ git ในอนาคตบางรุ่นจะไม่มีเลขวัตถุคอมไพล์4b825dc642cb6eb9a060e54bf8d69288fbee4904หรือไม่
  2. เหตุใดจึงไม่มีชื่อเชิงสัญลักษณ์สำหรับต้นไม้ว่างเปล่า (หรือมีชื่อนี้?)

(วิธีที่รวดเร็วและสกปรกในการสร้างชื่อเชิงสัญลักษณ์คือการใส่ SHA1 เข้าไปเช่น.git/Nulltreeน่าเสียดายที่คุณต้องทำสิ่งนี้สำหรับทุก repo ดูเหมือนจะดีกว่าที่จะใส่หมายเลขวิเศษในสคริปต์เป็นต้นฉันแค่มีความเกลียดชังทั่วไป เป็นตัวเลขวิเศษ)


3
เพื่อจำแฮช ;-) ใช้ SHA1 ("tree 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 คืออักขระ NUL)
Thomas

4
@ โทมัส: git hash-object -t tree /dev/nullวิธีการนี้ (จากคำตอบของ VonC ด้านล่าง) มีข้อดีของการไม่ฮาร์ดโค้ด SHA-1 ในกรณีที่ git เวอร์ชันอนาคตบางรุ่นจะเปลี่ยนเป็น SHA-2 (ฉันจะไม่พยายามทำนายว่าจะเกิดขึ้นเมื่อใด :-) การเปลี่ยน Mercurial เป็น SHA-2 จะง่ายกว่าเนื่องจากพวกเขาออกจากที่ว่าง)
torek

เพราะคุณพูดถูก แต่มันเป็น "ความรู้ที่ไร้ประโยชน์" ที่ดีและอาจเป็นประโยชน์กับคนอื่น ๆ ในกรณีใด ๆ !
Thomas

2
@ โทมัส: ดูเหมือนว่าการเปลี่ยนแปลงอัลกอริทึมแฮชอาจเกิดขึ้นเร็วกว่าที่คาดไว้ :-)
torek

เมื่อพูดถึง "Git เวอร์ชันอนาคต" ฉันคิดว่าคุณจะสนใจการแก้ไขล่าสุดของฉัน (ธันวาคม 2017) สำหรับคำตอบปี 2012 ของฉัน: stackoverflow.com/revisions/9766506/7
VonC

คำตอบ:


104

หัวข้อนี้กล่าวถึง:

หากคุณจำต้นไม้ที่ว่างเปล่า sha1 คุณสามารถได้รับมันด้วย:

git hash-object -t tree /dev/null

หรือตามที่Ciro Santilliเสนอในความคิดเห็น :

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

หรือตามที่เห็นที่นี่จากColin Schimmelfing :

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

ดังนั้นฉันเดาว่าจะปลอดภัยกว่าถ้ากำหนดตัวแปรโดยให้ผลลัพธ์ของคำสั่งนั้นเป็นต้นไม้ sha1 ว่างของคุณ (แทนที่จะใช้ "ค่าที่รู้จักกันดี")

หมายเหตุ: Git 2.25.1 (กุมภาพันธ์ 2020) เสนอในการกระทำ 9c8a294 :

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

และเพิ่ม:

ในฐานะบันทึกทางประวัติศาสตร์ฟังก์ชันนี้เรียกว่าrepo_read_object_file()ได้รับการสอนต้นไม้ว่างใน346245a1bb ("hard-code the empty tree object", 2008-02-13, Git v1.5.5-rc0 - merge ) และฟังก์ชันที่รู้จักกันในขณะนี้ ตามที่oid_object_info()ได้รับการสอนเรื่องต้นไม้ว่างในc4d9986f5f (" sha1_object_info: ตรวจสอบcached_objectร้านค้าด้วย", 2011-02-07, Git v1.7.4.1)


หมายเหตุคุณจะเห็นว่า 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 ไหม)

คุณยังสามารถสร้างฐานข้อมูลประวัติที่มีอยู่ใหม่บนคอมมิตว่างนั้นได้อีกด้วย (ดู " คอมไพล์: วิธีแทรกคอมมิตเป็นอันดับแรกโดยเปลี่ยนข้อมูลอื่น ๆ ทั้งหมด ")

ในทั้งสองกรณีคุณไม่ต้องพึ่งพาค่า SHA1 ที่แน่นอนของทรีว่างนั้น
คุณเพียงแค่ทำตามวิธีปฏิบัติที่ดีเริ่มต้น repo ของคุณด้วยการกระทำครั้งแรกที่ว่างเปล่า


ในการทำเช่นนั้น:

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 เฉพาะสำหรับ repo ของคุณชื่อผู้ใช้อีเมลวันที่สร้าง (หมายถึง 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 บน Windows )


ตามความคิดเห็นด้านล่างการใช้git diff <commit> HEADสิ่งนี้จะแสดงไฟล์ทั้งหมดของคุณใน HEAD สาขาปัจจุบัน:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

หมายเหตุ: ค่าต้นไม้ว่างนั้นถูกกำหนดอย่างเป็นทางการในcache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

ตั้งแต่ Git 2.16 (Q1 2018) มันถูกใช้ในโครงสร้างที่ไม่ได้เชื่อมโยงกับ (เท่านั้น) SHA1 อีกต่อไปดังที่เห็นในการกระทำ eb0ccfd :

สลับการค้นหาต้นไม้ที่ว่างเปล่าและหยดเพื่อใช้สิ่งที่เป็นนามธรรมของแฮช

สลับการใช้empty_tree_oidและempty_blob_oidใช้current_hashนามธรรมที่แสดงอัลกอริทึมแฮชปัจจุบันที่ใช้อยู่

ดูเพิ่มเติมได้ที่ " ทำไม Git ไม่ใช้ SHA ที่ทันสมัยกว่านี้ " คือSHA-2ตั้งแต่ Git 2.19 (ไตรมาส 3 ปี 2018)


ด้วย Git 2.25 (Q1 2020) การทดสอบกำลังเตรียมพร้อมสำหรับการเปลี่ยนแปลง SHA-2และเกี่ยวข้องกับทรีว่าง

ดูกระทำ fa26d5e , กระทำ cf02be8 , กระทำ 38ee26b , กระทำ 37ab8eb , กระทำ 0370b35 , กระทำ 0253e12 , กระทำ 45e2ef2 , กระทำ 79b0edc , กระทำ 840624f , กระทำ 32a6707 , กระทำ 440bf91 , กระทำ 0b408ca , กระทำ 2eabd38 (28 ตุลาคม 2019) และกระทำ 1bcef51 , กระทำ ecde49b (05 ต.ค. 2019) โดยbrian m. คาร์ลสัน ( bk2204) .
(ผสานโดยJunio ​​C Hamano - gitster-ในการกระทำ 28014c1, 10 พ.ย. 2019)

t/oid-info: เพิ่มต้นไม้ว่างและค่าหยดว่าง

ลงนามโดย: brian m. คาร์ลสัน

ในที่สุด testsuite จะเรียนรู้วิธีเรียกใช้โดยใช้อัลกอริทึมอื่นที่ไม่ใช่ SHA-1 ในการเตรียมการนี้ให้สอนกลุ่มtest_oidฟังก์ชันเกี่ยวกับการค้นหาหยดว่างและค่าต้นไม้ว่างเพื่อให้สามารถใช้งานได้

ดังนั้น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" คือ4b825dc642cb6eb9a060e54bf8d69288fbee4904ต้นไม้ว่างSHA1 " " ใหม่


@torek: ฉันได้เพิ่มตัวอย่างบางส่วนพร้อมกับแนวทางปฏิบัติที่ดีที่สุดในการกระทำที่ว่างเปล่าเป็นครั้งแรกเพื่อแสดงให้เห็นถึงต้นไม้ที่ว่างเปล่า SHA1
VonC

เป้าหมายอย่างหนึ่งคือการใช้แฮช "ต้นไม้ว่าง" เป็นข้อโต้แย้งgit diff-treeในบางสคริปต์ที่ฉันกำลังเขียน ไม่มีการรับประกันว่าจะมีการคอมมิตว่างเปล่าเริ่มต้นใน repo ดังนั้นฉันแค่สงสัยว่าสคริปต์เหล่านี้อาจจบลงในสักวันหนึ่ง
torek

1
หากคุณส่งผ่าน-wไปgit hash-objectมันจะสร้างอ็อบเจ็กต์ในที่เก็บที่ถูกรันและนั่นจะสร้างทรีว่างเปล่าในที่เก็บที่คุณกำลังรันอยู่เพื่อที่มันจะหายไปในอนาคต
javawizard

หากคุณต้องการไปก่อนการคอมมิตครั้งแรกโดยใช้ rebase คุณสามารถใช้ git rebase --root
GergelyPolonkai

1
หรือถ้าคุณต้องการความมหัศจรรย์ของท่อแทนของเวทมนตร์ของ/dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli郝海东冠状病六四事件法轮功

3

ฉันเขียนบล็อกโพสต์ด้วยสองวิธีในการค้นหาแฮช: http://colinschimmelfing.com/blog/gits-empty-tree/

หากมีการเปลี่ยนแปลงด้วยเหตุผลบางประการคุณสามารถใช้สองวิธีด้านล่างเพื่อค้นหา อย่างไรก็ตามฉันรู้สึกค่อนข้างมั่นใจในการใช้แฮชในนามแฝง. bashrc ฯลฯ และฉันไม่คิดว่ามันจะเปลี่ยนไปเร็ว ๆ นี้ อย่างน้อยที่สุดก็น่าจะเป็นการปล่อยคอมไพล์ครั้งสำคัญ

สองวิธีคือ:

  1. คำตอบข้างต้น: git hash-object -t tree --stdin < /dev/null
  2. เพียงแค่ใส่ repo ที่ว่างเปล่าแล้วรันgit write-treeใน repo ใหม่นั้นแฮชจะถูกส่งออกโดย git write-tree

การรันคำสั่งด้วย–-stdinทำให้ฉันfatal: Cannot open '–-stdin': No such file or directoryมี git 2.7.2 อย่างไรก็ตามการเรียกใช้โดยไม่มี--stdinคำตอบของ VonC ให้ค่าแฮช
sigy

คำตอบนี้ไม่มีประโยชน์มากนักในตอนนี้บล็อกโพสต์ได้ตายไปแล้ว ด้วยเหตุนี้เราจึงไม่เห็นด้วยกับคำตอบเหล่านี้ใน SO
Philip Whitehouse

1
@PhilipWhitehouse บล็อกโพสต์ยังไม่ตาย แต่ในกรณีใด ๆ ฉันได้รวมสองวิธีไว้ในคำตอบของฉัน - ฉันยอมรับว่าหากไม่รวมสองวิธีนี้จะไม่ใช่คำตอบที่ดี
schimmy

3

นี่คือคำตอบเกี่ยวกับวิธีสร้างการคอมมิตทรีว่างแม้ในกรณีที่ที่เก็บไม่ว่างเปล่า https://stackoverflow.com/a/14623458/9361507

แต่ฉันชอบแท็ก "ว่าง" มากกว่า แต่ไม่ใช่สาขา วิธีง่ายๆคือ:

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

เนื่องจากแท็กสามารถชี้ไปที่ tree-ish ได้โดยตรงโดยไม่ต้องคอมมิต ตอนนี้เพื่อรับไฟล์ทั้งหมดในโครงสร้างการทำงาน:

git diff --name-only empty

หรือเหมือนกันกับ stat:

git diff --stat empty

ไฟล์ทั้งหมดที่แตกต่างกัน:

git diff empty

ตรวจสอบช่องว่างในไฟล์ทั้งหมด:

git diff --check empty

... แต่การใช้หมายเลขวิเศษในการสร้างแท็กของคุณเป็นเพียงการแปรงใต้พรมซึ่งเป็นประเด็นสำคัญของคำถาม ( ไม่ใช้เลขวิเศษ SHA-1)
RomainValeri

ไม่จริง. ฉันใช้แท็กเพื่อชี้ไปที่วัตถุที่เป็นต้นไม้ ถึงตอนนี้ tree-ish นี้ถูกกำหนดโดย SHA-1 ในอนาคตก็สามารถเปลี่ยนได้เช่น SHA-256 เป็นต้น (ด้วยการย้ายที่เก็บ) แต่แท็กจะเหมือนกัน. :) คุณสมบัติหลักของแท็กคือการชี้ไปที่วัตถุ แท็กสามารถใช้ SHA-1 ภายในหรืออย่างอื่นได้เป็นเรื่องของ Git ภายในเท่านั้น
Olleg

ฉันเข้าใจ. แต่ถ้าคุณ (หรือใครก็ตามที่อ่านสิ่งนี้) (หรือสคริปต์ยิ่งแย่ไปกว่านั้น) พยายามที่จะใช้มัน (บรรทัดแรกของคุณ) ในภายหลังมันอาจล้มเหลวในอัลกอริทึมแฮชใหม่ซึ่งการแทนที่บรรทัดแรกของคุณด้วยนิพจน์ที่ดำเนินการ (การสร้าง แฮชนี้) จะประสบความสำเร็จ
RomainValeri

หากคุณรวมสิ่งนี้เข้ากับหนึ่งในวิธีการสร้างแฮชต้นไม้ว่างเปล่าโดยอัตโนมัติคุณสามารถพิสูจน์ได้ในอนาคต (ตามที่ @RomainValeri แนะนำ) อย่างไรก็ตามถ้ามันขึ้นอยู่กับฉันgit rev-parseจะมีแฟล็กหรือคีย์เวิร์ดใหม่หรืออะไรบางอย่างตามบรรทัดเหล่านั้นเพื่อสร้าง (a) แฮชทรีว่างและ (b) แฮช null-คอมมิต ทั้งสองอย่างนี้จะมีประโยชน์ในสคริปต์และจะป้องกันการเปลี่ยนแปลง SHA-256 ที่เสนอ
torek

โอเคเปลี่ยนไป แต่นี่จะไม่ใช่ "วิธีที่ง่ายที่สุด" :)
Olleg
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.