ทำความเข้าใจ git rev-list
ในขณะที่กำลังมองหาตัวอย่าง git hook ฉันเจอโพสต์ต่อไปนี้: https://github.com/Movidone/git-hooks/blob/master/pre-receive และฉันต้องการเข้าใจคำสั่งต่อไปนี้:
git rev-list $new_list --not --all
ที่ได้รับ new_list จาก:
NULL_SHA1="0000000000000000000000000000000000000000" # 40 0's
new_list=
any_deleted=false
while read oldsha newsha refname; do
case $oldsha,$newsha in *,$NULL_SHA1) # it's a delete
any_deleted=true;;
$NULL_SHA1,*) # it's a create new_list="$new_list $newsha";; *,*) # it's an update new_list="$new_list $newsha";;
esac
done
ฉันคิดว่ารายการ rev แสดงการกระทำตามลำดับเวลาย้อนกลับ
แต่ใครบางคนสามารถแบ่งปันข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับสิ่งที่-notและ-allตัวเลือกต่างๆมีไว้เพื่อ?
ตามเอกสาร:
--not
Reverses the meaning of the ^ prefix (or lack thereof) for all following revision specifiers, up to the next --not.
--all
Pretend as if all the refs in refs/ are listed on the command line as <commit>.
ฉันไม่สามารถเข้าใจตัวเลือกเหล่านี้ได้ทั้งหมด
[อัปเดต] หลังจากทำการทดสอบแล้วพบว่าถ้าฉันไม่ใช้--notและ--allตัวเลือกแล้วให้git rev-listแสดงรายการการกระทำทั้งหมดในสาขาไม่ใช่รายการที่ฉันตั้งใจจะผลักดัน
อย่างไรก็ตามต้องการทำความเข้าใจว่าเหตุใดจึงไม่พิมพ์ค่า sha บนเทอร์มินัลเมื่อ--allส่งผ่านตัวเลือก
คำตอบ
git rev-listคำสั่งเป็นอย่างมากซับซ้อนมากกลางคำสั่งใน Git เป็นสิ่งที่มันไม่เดินกราฟ คำว่ากราฟในที่นี้หมายถึงทั้งกราฟการกระทำเองและในบางกรณีระดับถัดไปลงมา (ออบเจ็กต์ Git ที่เข้าถึงได้จากคอมมิต)
ฉันคิดว่ารายการ rev แสดงการกระทำตามลำดับเวลาย้อนกลับ
ไม่ตรง แต่ปิด:
- คำสั่งสามารถเปลี่ยนแปลงได้ เริ่มต้นคือย้อนกลับตามลำดับ
- ค่าเริ่มต้นคือการเดินคอมมิชชัน แต่คุณสามารถ
rev-listลงลึกได้มากขึ้นเพื่อรวมวัตถุต้นไม้และหยดและแม้แต่แท็กวัตถุ สำหรับโปรแกรมเช่นgit fetchและgit push(ซึ่งเรียกใช้git pack-objects) และgit pack-objects. ฉันวางแผนที่จะเพิกเฉยต่อความเป็นไปได้ทั้งหมดที่นี่ แต่อย่างน้อยฉันก็ควรพูดถึงมัน 😀
ดังนั้นค่าเริ่มต้นคือการแสดงรายการการกระทำบางอย่างตามลำดับเวลาย้อนกลับ มันเป็นทั้งความสำคัญและน้อยบิตหากินเพื่อระบุว่าส่วนไหนของกราฟเราจะมีgit rev-listการเดินเท้าที่: บางในกระทำบางอย่าง
แต่ใครบางคนสามารถแบ่งปันข้อมูลเชิงลึกเพิ่มเติมเกี่ยวกับสิ่งที่
--notและ--allตัวเลือกต่างๆมีไว้เพื่อ?
ในฐานะที่เป็นบันทึกย่อของ VonCเอฟเฟกต์ที่นี่คือการแสดงรายการคอมมิตที่ใหม่สำหรับที่เก็บการรับ ซึ่งขึ้นอยู่กับความเป็นจริงที่ว่านี้git rev-listคำสั่งทำงานในก่อนได้รับเบ็ด โดยทั่วไปแล้วจะไม่ทำอะไรที่เป็นประโยชน์นอกเบ็ดนี้ ดังนั้นอย่างที่คุณเห็นสภาพแวดล้อมรันไทม์ของ hook ใน Git มักจะมีความพิเศษเล็กน้อย (นี่เป็นความจริงสำหรับมากกว่า hook ที่รับไว้ล่วงหน้า: เราต้องคิดถึงบริบทการเปิดใช้งานของแต่ละเบ็ด)
เพิ่มเติมเกี่ยวกับ --not --all
--allตัวเลือกที่ไม่เพียงสิ่งที่คุณยกมาจากเอกสาร:
ทำเป็นราวกับว่าการอ้างอิงทั้งหมดใน
refs/รายการอยู่ในบรรทัดคำสั่ง ...
สิ่งนี้จึงเทียบเท่ากับ a git for-each-ref refs: มันวนซ้ำอยู่เหนือการอ้างอิงแต่ละรายการ ที่มีชื่อสาขา ( masterหรือmain, develop, feature/tallและอื่น ๆ ซึ่งทั้งหมดนี้เป็นจริงในrefs/heads/) ชื่อแท็ก ( v1.2ซึ่งจริงๆrefs/tags/v1.2) ระยะไกลติดตามชื่อ ( origin/developที่เป็นจริงrefs/remotes/origin/develop) refs ทดแทน (ในrefs/replace/) ที่ซ่อน ( refs/stash) Bisection refs, Gerrit refs หากคุณใช้ Gerrit และอื่น ๆ โปรดทราบว่าจะไม่วนซ้ำรายการ reflog
--notคำนำหน้าคือการดำเนินการบูลีนที่เรียบง่าย ในไวยากรณ์ gitrevisions - ดูเอกสาร gitrevisions - เราสามารถเขียนสิ่งต่างๆเช่นdevelopหมายความว่าฉันบอกให้คุณเริ่มต้นdevelopและทำงานย้อนกลับและรวมความมุ่งมั่นเหล่านี้ไว้ด้วยเช่น^developกันหมายความว่าฉันบอกให้คุณเริ่มต้นdevelopและทำงานย้อนหลังและยกเว้นการกระทำเหล่านี้ . ดังนั้นถ้าฉันเขียน:
git rev-list feature1 feature2 ^main
ผมขอให้ Git จะเดินกระทำเข้าถึงได้จากกระทำที่ระบุโดยชื่อfeature1และfeature2แต่ยกเว้นmainกระทำเข้าถึงได้จากกระทำที่ระบุโดย สำหรับ (มาก) เพิ่มเติมเกี่ยวกับความคิดทั่วไปของการเชื่อมและกราฟเดินดูคิดเช่น (ก) Git
ตัว--notดำเนินการพลิกข้อมูล^อ้างอิงแต่ละรายการได้อย่างมีประสิทธิภาพ:
git rev-list --not feature1 feature2 ^main
เป็นชวเลขเหมือนเดิมสำหรับ:
git rev-list ^feature1 ^feature2 main
นี้จะแนะนำรายการกระทำสามารถเข้าถึงได้จากmainแต่ไม่รวมผู้ที่สามารถเข้าถึงได้จากทั้งสองหรือfeature1feature2
โดยปกติแล้วการกระทำทั้งหมดจะพบได้ด้วย--all
หากคุณใช้ Git ในชีวิตประจำวันตามปกติและไม่มี "HEAD แยก" ในขณะนี้โหมด HEAD แบบแยกไม่ได้ผิดปกติแต่ไม่ใช่วิธีการทำงานปกติ - --allตัวเลือกในการgit rev-listบอกให้รวมคอมมิตทั้งหมดเพราะกระทำทั้งหมดเป็นเข้าถึงได้จากการอ้างอิงทั้งหมด 1ดังนั้นจึงไม่รวมการกระทำทั้งหมด--not --allอย่างมีประสิทธิภาพ ดังนั้นการเพิ่มรายการใด ๆที่จะแสดงการกระทำบางอย่างจึงมีผลในการยับยั้งรายการ ผลลัพธ์ว่างเปล่า: ทำไมเราถึงรำคาญ?--not --allgit rev-list
ถ้าคุณอยู่ในโหมด HEAD แฝดและได้ทำใหม่หลายกระทำนี้สามารถเกิดขึ้นได้เมื่อคุณอยู่ในกลางของ rebase โต้ตอบหรือขัดแย้งสำหรับอินสแตนซ์แล้วgit rev-list HEAD --not --allจะรายการกระทำเหล่านั้นที่มีความสามารถเข้าถึงได้จากHEADแต่ไม่ได้มาจากชื่อสาขาใดก็ได้ ตัวอย่างเช่นใน rebase นั้นจะเป็นเพียงการกระทำที่คุณได้คัดลอกจนถึงตอนนี้
ดังนั้นโหมด "detached HEAD" จะกลายเป็นตำแหน่งที่git rev-list --not --allสามารถใช้ประโยชน์ได้จากบรรทัดคำสั่ง แต่สำหรับสถานการณ์ที่คุณกำลังตรวจสอบ - เบ็ดรับล่วงหน้า -เราไม่ได้อยู่ในบรรทัดคำสั่งจริงๆ
รับตะขอล่วงหน้า
เมื่อมีคนใช้git pushส่งคอมมิตไปยัง Git ของคุณ Git ของคุณ:
- ตั้งค่าพื้นที่กักกันเพื่อเก็บวัตถุใหม่ใด ๆ (คอมมิตใหม่และบล็อบเป็นต้น) 1
- เจรจากับผู้ส่งเพื่อตัดสินใจว่าผู้ส่งควรส่งอะไร
- ได้รับวัตถุเหล่านี้ และ
- รับรายการของการร้องขอการปรับปรุงโทษ คำขออัปเดตเหล่านี้โดยพื้นฐานแล้วเพียงแค่บอกว่าทำให้ชื่อนี้มี ID แฮชนี้ 2
ก่อนที่จะทำการอัปเดตที่ร้องขอจริง Git ของคุณ:
- ดึงข้อมูลรายการทั้งหมดไปยังเบ็ดก่อนรับ เบ็ดนั้นสามารถพูดว่า "ไม่"; ถ้าเป็นเช่นนั้นการผลักดันทั้งหมดโดยรวมจะถูกปฏิเสธ
- หากมีข้อความว่า "ตกลง" ให้ดึงข้อมูลรายการทีละคำขอไปยัง hook การอัปเดต เมื่อท่อนฮุคบอกว่า "ตกลง" จะทำการอัปเดต หากตะขอระบุว่า "ไม่" แสดงว่า Git ของคุณจะปฏิเสธการอัปเดตเดียว แต่จะตรวจสอบผู้อื่นต่อไป
- หลังจากการอัปเดตทั้งหมดได้รับการยอมรับหรือปฏิเสธในขั้นตอนที่ 2 ให้ฟีดรายการที่ยอมรับไปยังเบ็ดหลังการรับ
อ็อบเจ็กต์ที่จำเป็นซึ่งถูกเพิ่มไปยังการอ้างอิงบางส่วนในขั้นตอนที่ 2 จะถูกย้ายจากการกักเก็บไปยังฐานข้อมูลอ็อบเจ็กต์ของ Git ผู้ที่ถูกปฏิเสธไม่ได้
git pushตอนนี้คิดว่าเป็นปกติ เราได้รับบางส่วนใหม่กระทำ (s) และคำขอ: สร้างชื่อสาขาใหม่feature/shortหรือเราได้รับบางกระทำใหม่ (s) และคำขอ: ชื่อสาขาการปรับปรุงที่มีอยู่developจะรวมใหม่กระทำเหล่านี้พร้อมกับคนเก่า
ในขั้นตอนที่ 1 ข้างต้นเรามี ID แฮชใหม่เพียงตัวเดียว เราวิ่งห่วงอ่านทั้งหมดชื่อเตะและปัจจุบันของพวกเขาและรหัสกัญชาเสนอใหม่และวงวิ่งเพียงครั้งเดียวเพราะเพียงหนึ่งชื่อถูกgit push-ed รหัสแฮชนั้นหมายถึงการกระทำหรือคอมมิตใหม่ซึ่งจะถูกเพิ่มไปยังสาขาที่มีอยู่นี้หรือเป็นทิปและคอมมิตอื่น ๆ ที่เป็นเอกสิทธิ์ของสาขาใหม่
ตอนนี้เราต้องการตรวจสอบการกระทำเหล่านี้ไม่ใช่การกระทำใด ๆ ที่มีอยู่ซึ่งสามารถเข้าถึงได้จากสาขาที่มีอยู่ เพื่อความเรียบง่ายแทนที่จะเป็น$new_listคำตอบอื่นของฉันสมมติว่าเราเป็นเพียง ID แฮชใหม่หนึ่ง ID $newและ ID แฮชเก่าสำหรับชื่อสาขา$old: ค่าศูนย์ทั้งหมดหากสาขาเป็นแบบใหม่ทั้งหมดหรือคอมมิตที่มีอยู่ที่ถูกต้องหากเป็น ชื่อสาขาที่มีอยู่
หากคอมมิตใหม่อยู่ในสาขาใหม่ทั้งหมดให้:
git rev-list $new ^master ^develop ^feature/short ^feature/tall
จะครอบคลุมพวกเขาเช่นถ้าเรารู้ว่ามีสาขาที่มีอยู่เพียงสาขาเดียวคือสี่แห่งนี้ (และไม่มีแท็กอื่น ๆ ให้ต้องกังวล) แต่ถ้าเพิ่มเข้ามาdevelopล่ะพูดล่ะ? แล้วเราต้องการยกเว้นกระทำที่มีอยู่ในปัจจุบันdevelopบน เราสามารถใช้$oldแฮช ID เพื่อทำสิ่งนั้น:
git rev-list $new ^master ^$old ^feature/short ^feature/tall
อีกครั้งจะแสดงรายการเฉพาะการกระทำใหม่ที่ใครก็ตามที่ทำงานอยู่git push origin developต้องการเพิ่มในdevelopไฟล์.
แต่ลองคิดดู$oldสิ นี่คือรหัสแฮช Git เอามาจากไหน Git มี ID กัญชานี้จากชื่อ developนี่คือก่อนได้รับเบ็ด ; ชื่อ ยังไม่ได้รับการปรับปรุงเลยdevelop ดังนั้นชื่อdevelop เป็นชื่อสำหรับ $oldID นั่นหมายความว่า:
git rev-list $new ^master ^develop ^feature/short ^feature/tall
จะยังทำผลงานได้
หากgit rev-list $newตามด้วย "และไม่ใช่ทั้งหมดที่มีอยู่" จะทำงานได้ดังนี้:
git rev-list $new --not --branches
จะทำงาน นั่นคือเกือบทั้งหมดที่เรามีที่นี่
ข้อบกพร่องในการใช้เพียงแค่--branchesไม่ได้รับแท็กใด ๆ หรือการอ้างอิงอื่น ๆ เราสามารถใช้ได้--not --branches --tagsแต่--not --allสั้นกว่าและยังได้รับการอ้างอิงอื่น ๆ ทั้งหมด
นี่คือที่--not --allมา: มันขึ้นอยู่กับกรณีพิเศษของตะขอรับล่วงหน้า เราแสดงรายการรหัสแฮชใหม่ตามที่เสนอโดยใครก็ตามที่ใช้งาน a git pushซึ่ง Git ของเราได้ส่งถึงเราเป็นรายการบรรทัด เราได้git rev-listดำเนินการตามกราฟการคอมมิตที่เสนอเพื่ออัปเดตโดยดูที่คอมมิตใหม่ในพื้นที่กักกัน แต่ไม่รวมคอมมิตทั้งหมดที่มีอยู่แล้วในที่เก็บของเรา คำสั่ง rev-list สร้าง ID แฮชเหล่านี้หนึ่งรายการต่อบรรทัดจากนั้นเราจะอ่านในเชลล์ลูปและทำสิ่งที่เราต้องการเพื่อตรวจสอบแต่ละคอมมิต
1พื้นที่กักกันเป็นของใหม่ใน Git 2.11 ก่อนหน้านั้นอ็อบเจ็กต์ใหม่อาจยังคงอยู่ในที่เก็บชั่วขณะแม้ว่าการพุชจะถูกปฏิเสธก็ตาม พื้นที่กักกันไม่ได้จริงๆจัดการที่ยิ่งใหญ่สำหรับคนส่วนใหญ่ แต่สำหรับเซิร์ฟเวอร์ขนาดใหญ่เช่น GitHub จะสามารถประหยัดพวกเขาจำนวนมากพื้นที่ว่างในดิสก์
2คำขอสามารถบังคับหรือไม่บังคับและหากถูกบังคับอาจเป็นการบังคับด้วยสัญญาเช่าหรือไม่ก็ได้ ข้อมูลนี้ไม่มีอยู่ใน pre-receiver hook (หรือใน update hook) ซึ่งก็คืออืมสมมติว่าไม่ค่อยดีนัก แต่มีปัญหาความเข้ากันได้กับการเพิ่ม ทุกอย่างน่าอยู่เป็นส่วนใหญ่ เบ็ดสามารถบอกได้ว่าเป็นการสร้างการอ้างอิงใหม่หรือลบคำขออ้างอิงที่มีอยู่เพราะในกรณีนี้ ID แฮชหนึ่งในสองรหัสเก่าหรือใหม่จะเป็น "null hash" ทั้งหมดเป็นศูนย์ (ซึ่งสงวนไว้ไม่อนุญาตให้ใช้แฮช ID เป็นศูนย์ทั้งหมด)
หมายความว่า:
- แสดงรายการคอมมิตที่เข้าถึงได้โดยทำตามลิงก์พาเรนต์จากคอมมิตที่กำหนดที่นี่คอมมิต
$new_listใหม่แก้ไขหรือลบ - แต่ไม่รวมคอมมิตที่เข้าถึงได้จากการคอมมิตที่ให้ไว้
^ข้างหน้านี่คือ " ทั้งหมด " นั่นคือ HEADS คอมมิตทั้งหมดหรือคอมมิตที่ติดแท็ก
ที่ จำกัด rev-list ไว้เฉพาะคอมมิตใหม่ที่ได้รับไม่ใช่คอมมิตทั้งหมด (ได้รับแล้วและมีอยู่แล้วในที่เก็บการรับ)