นี่ไม่ใช่คำตอบที่แท้จริง แต่ฉันต้องการเข้าถึงการจัดรูปแบบและมีพื้นที่ว่างมากมาย ฉันจะพยายามที่จะอธิบายทฤษฎีที่อยู่เบื้องหลังสิ่งที่ผมคิดว่าทั้งสองคำตอบที่ดีที่สุด: ได้รับการยอมรับอย่างใดอย่างหนึ่งและ(อย่างน้อยในขณะนี้) ติดอันดับหนึ่ง แต่ในความเป็นจริงพวกเขาตอบที่แตกต่างกันคำถาม
การคอมมิตใน Git มักจะ "เปิด" มากกว่าหนึ่งสาขาในแต่ละครั้ง อันที่จริงนั่นคือสิ่งที่คำถามเกี่ยวกับ ให้:
...--F--G--H <-- master
\
I--J <-- develop
ที่ตัวอักษรตัวพิมพ์ใหญ่ยืนอยู่ในที่เกิดขึ้นจริงรหัสกัญชา Git เรามักจะมองหาเพียงกระทำHหรือเพียงมุ่งมั่นI-Jของเราในgit logการส่งออก การยอมรับผ่านGมีอยู่ในทั้งสองสาขาดังนั้นเราจึงขอยกเว้นไว้
(โปรดสังเกตว่าในกราฟที่วาดเช่นนี้การคอมมิตที่ใหม่กว่าจะไปทางขวาชื่อจะเลือกการกระทำที่อยู่ขวาสุดในบรรทัดนั้นแต่ละคอมมิตเหล่านั้นมีคอมมิตพาเรนต์ซึ่งเป็นคอมมิตทางซ้าย: พาเรนต์ของHคือGและพาเรนต์ของJคือพาเรนIต์ของIคือGอีกครั้งพาเรนต์ของGคือFและFมีพาเรนต์ที่ไม่ได้แสดงที่นี่: เป็นส่วนหนึ่งของ...ส่วน)
สำหรับกรณีง่ายๆนี้เราสามารถใช้:
git log master..develop # note: two dots
เพื่อดูI-Jหรือ:
git log develop..master # note: two dots
เพื่อดูHเท่านั้น ชื่อด้านขวาหลังจากที่ทั้งสองจุดบอก Git: ใช่กระทำเหล่านี้ ชื่อทางด้านซ้ายก่อนที่จะจุดสองจุดบอก Git: ไม่มีไม่กระทำเหล่านี้ Git เริ่มต้นที่ปลาย -At กระทำHหรือกระทำJและอื่นทำงานย้อนหลัง สำหรับ (มาก) มากขึ้นเกี่ยวกับเรื่องนี้ดูคิดเช่น (ก) Git
วิธีการใช้วลีของคำถามดั้งเดิมความปรารถนาคือการค้นหาการกระทำที่สามารถเข้าถึงได้จากชื่อใดชื่อหนึ่ง แต่ไม่ใช่จากชื่ออื่นใดในหมวดหมู่ทั่วไปเดียวกันนั้น นั่นคือถ้าเรามีกราฟที่ซับซ้อนมากขึ้น:
O--P <-- name5
/
N <-- name4
/
...--F--G--H--I---M <-- name1
\ /
J-----K <-- name2
\
L <-- name3
เราสามารถเลือกหนึ่งในชื่อเหล่านี้เช่นname4หรือname3และถาม: การกระทำใดที่สามารถพบได้ในชื่อนั้น แต่ไม่สามารถใช้ชื่ออื่นได้? ถ้าเราเลือกคำตอบคือการกระทำname3 Lถ้าเราเลือกname4คำตอบคือไม่มีการกระทำเลย: การกระทำที่name4ชื่อเป็นคอมมิตNแต่Nสามารถพบได้โดยเริ่มต้นที่name5และทำงานย้อนกลับ
คำตอบที่ยอมรับจะทำงานร่วมกับชื่อการติดตามระยะไกลแทนที่จะเป็นชื่อสาขาและช่วยให้คุณกำหนดชื่อที่สะกดorigin/merge-only- ชื่อที่เลือกและดูชื่ออื่น ๆ ทั้งหมดในเนมสเปซนั้น นอกจากนี้ยังหลีกเลี่ยงการแสดงการผสาน: หากเราเลือกname1เป็น "ชื่อที่น่าสนใจ" และพูดว่าแสดงความมุ่งมั่นที่เข้าถึงได้จากname1ชื่ออื่น แต่ไม่ใช่ชื่ออื่นเราจะเห็นการรวมคอมมิตMและคอมมิตปกติIเช่นเดียวกับการกระทำปกติ
คำตอบยอดนิยมค่อนข้างแตกต่างกัน มันเป็นเรื่องภายในกระทำกราฟโดยไม่ต้องต่อไปนี้ขาทั้งสองข้างของการผสานและโดยไม่แสดงใด ๆ กระทำที่มีการผสาน ถ้าเราเริ่มต้นด้วยname1ตัวอย่างเช่นเราจะไม่แสดงM(มันผสาน) แต่สันนิษฐานได้ว่าครั้งแรกที่แม่ของผสานMจะกระทำIเราจะไม่แม้แต่จะมองกระทำและJ Kเราจะจบลงด้วยการแสดงการกระทำIและยังกระทำH, G, F, และอื่น ๆ ไม่มีของเหล่านี้จะกระทำการผสานและทั้งหมดจะสามารถเข้าถึงได้โดยเริ่มต้นที่Mและการทำงานหลังการเยี่ยมชมเพียงครั้งแรกที่ผู้ปกครองของแต่ละผสานกระทำ
คำตอบที่ได้รับความนิยมมากที่สุดนั้นค่อนข้างเหมาะสำหรับตัวอย่างเช่นการพิจารณาว่าmasterเมื่อmasterใดที่ตั้งใจจะให้เป็นสาขาที่รวมเท่านั้น หาก "งานจริง" ทั้งหมดทำบนกิ่งก้านด้านข้างซึ่งรวมเข้าด้วยกันในภายหลังmasterเราจะมีรูปแบบดังนี้:
I---------M---------N <-- master
\ / \ /
o--o--o o--o--o
โดยที่การoกระทำที่ไม่มีชื่อเป็นตัวอักษรทั้งหมดเป็นการกระทำแบบธรรมดา (ไม่รวม) และMและNรวมเข้าด้วยกัน Commit Iคือการคอมมิตเริ่มต้น: คอมมิตแรกที่สร้างขึ้นและเป็นคอมมิตเดียวที่ควรอยู่บนมาสเตอร์ที่ไม่ใช่คอมมิตการผสาน หากการgit log --first-parent --no-merges masterแสดงมีการกระทำนอกเหนือจาก Iนั้นเรามีสถานการณ์ดังนี้:
I---------M----*----N <-- master
\ / \ /
o--o--o o--o--o
ที่เราต้องการเห็นการกระทำ*ที่สร้างขึ้นโดยตรงmasterไม่ใช่โดยการรวมสาขาคุณลักษณะบางอย่าง
ในระยะสั้นคำตอบที่ได้รับความนิยมนั้นยอดเยี่ยมสำหรับการพิจารณาว่าmasterเมื่อmasterใดควรรวมเข้าด้วยกันเท่านั้น แต่ไม่เหมาะสำหรับสถานการณ์อื่น ๆ คำตอบที่ยอมรับใช้ได้กับสถานการณ์อื่น ๆ เหล่านี้
ชื่อการติดตามระยะไกลเช่นorigin/master สาขาชื่อหรือไม่
บางส่วนของ Git บอกว่าไม่ใช่:
git checkout master
...
git status
พูดon branch masterแต่:
git checkout origin/master
...
git status
พูดว่าHEAD detached at origin/master. ฉันชอบที่จะเห็นด้วยกับgit checkout/ git switch: origin/masterไม่ใช่ชื่อสาขาเนื่องจากคุณไม่สามารถ "เปิด" ได้
คำตอบที่ยอมรับใช้ชื่อการติดตามระยะไกลorigin/*เป็น "ชื่อสาขา":
git log --no-merges origin/merge-only \
--not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
grep -Fv refs/remotes/origin/merge-only)
บรรทัดกลางซึ่งเรียกใช้จะgit for-each-refวนซ้ำบนชื่อการติดตามระยะไกลสำหรับรีโมตที่ชื่อoriginมากกว่าชื่อระยะไกลติดตามสำหรับชื่อระยะไกล
เหตุผลนี้เป็นทางออกที่ดีสำหรับปัญหาเดิมคือเราสนใจที่นี่ในชื่อสาขาของคนอื่นมากกว่าชื่อสาขาของเรา แต่นั่นหมายความว่าเราได้กำหนดสาขาเป็นสิ่งอื่นที่ไม่ใช่ชื่อสาขาของเรา ไม่เป็นไร: โปรดทราบว่าคุณกำลังทำสิ่งนี้เมื่อคุณทำ
git log ข้ามผ่านบางส่วนของกราฟคอมมิต
สิ่งที่เรากำลังค้นหาอยู่นี้คือชุดของสิ่งที่ฉันเรียกว่าdaglets:ดูว่า "สาขา" หมายถึงอะไร? นั่นก็คือเรากำลังมองหาชิ้นส่วนภายในชุดย่อยบางส่วนของภาพรวมการกระทำของกราฟ
เมื่อใดก็ตามที่เรามี Git ดูชื่อสาขาเช่นชื่อmasterแท็กv2.1หรือชื่อการติดตามระยะไกลเช่นorigin/masterเรามักจะต้องการให้ Git บอกเราเกี่ยวกับการกระทำนั้นและทุกการกระทำที่เราสามารถเข้าถึงได้จากการกระทำนั้น: เริ่มต้นที่นั่น และทำงานย้อนกลับ
ในทางคณิตศาสตร์นี้จะเรียกว่าเดินกราฟ กราฟคอมมิตของ Git คือDirected Acyclic GraphหรือDAGและกราฟประเภทนี้เหมาะอย่างยิ่งสำหรับการเดิน เมื่อเดินกราฟดังกล่าวเราจะไปที่จุดยอดกราฟแต่ละจุดที่สามารถเข้าถึงได้ผ่านเส้นทางที่ใช้ จุดยอดในกราฟ Git คือคอมมิตโดยที่ขอบเป็นส่วนโค้ง - ลิงก์ทางเดียว - จากลูกแต่ละคนไปยังพ่อแม่แต่ละคน (นี่คือที่ที่Think Like (a) Gitมาลักษณะทางเดียวของ arcs หมายความว่า Git ต้องทำงานย้อนกลับตั้งแต่เด็กไปจนถึงผู้ปกครอง)
คำสั่ง Git หลักสองคำสั่งสำหรับการเดินกราฟคือgit logและgit rev-list. คำสั่งเหล่านี้มีความคล้ายคลึงกันมากโดยส่วนใหญ่สร้างขึ้นจากไฟล์ต้นฉบับเดียวกัน แต่เอาต์พุตต่างกัน: git logสร้างเอาต์พุตให้มนุษย์อ่านในขณะที่git rev-listสร้างเอาต์พุตสำหรับโปรแกรม Git อื่น ๆ เพื่ออ่าน 1 คำสั่งทั้งสองทำกราฟแบบนี้
เดินกราฟพวกเขาทำคือเฉพาะ: รับชุดของการเริ่มต้นกระทำบางจุด (อาจจะเพียงหนึ่งกระทำบางทีพวงของรหัสกัญชาบางทีพวงของชื่อที่มีมติให้รหัสกัญชา) เดินกราฟเยี่ยมกระทำ คำสั่งเฉพาะเช่น--notหรือคำนำหน้า^หรือ--ancestry-pathหรือ--first-parentปรับเปลี่ยนการเดินของกราฟในทางใดทางหนึ่ง
ขณะที่พวกเขาเดินตามกราฟพวกเขาจะไปเยี่ยมชมการกระทำ แต่พวกเขาพิมพ์ชุดย่อยของการเดินที่เลือกไว้เท่านั้น สั่งเช่น--no-mergesหรือ--before <date>บอกรหัสกราฟเดินที่มุ่งมั่นที่จะพิมพ์
เพื่อที่จะทำไปเยือนนี้หนึ่งกระทำในช่วงเวลาที่ทั้งสองคำสั่งใช้คิวลำดับความสำคัญ คุณเรียกใช้git logหรือgit rev-listให้จุดเริ่มต้นบางอย่าง พวกเขาใส่คอมมิตเหล่านั้นลงในคิวลำดับความสำคัญ ตัวอย่างง่ายๆ:
git log master
เปลี่ยนชื่อmasterเป็น ID แฮชดิบและใส่ ID แฮชหนึ่งตัวลงในคิว หรือ:
git log master develop
เปลี่ยนชื่อทั้งสองให้เป็น ID แฮชและสมมติว่าเป็น ID แฮชที่ต่างกันสองตัวใส่ทั้งสองลงในคิว
ลำดับความสำคัญของการคอมมิตในคิวนี้ถูกกำหนดโดยอาร์กิวเมนต์ที่ยังคงมีอยู่ ตัวอย่างเช่นอาร์กิวเมนต์--author-date-orderจะบอกgit logหรือgit rev-listใช้การประทับเวลาของผู้เขียนแทนการประทับเวลาของคอมมิตเตอร์ ค่าดีฟอลต์คือใช้การประทับเวลาคอมมิทเทอร์และเลือกคอมมิตใหม่ล่าสุดตามวันที่: อันที่มีวันที่เป็นตัวเลขสูงสุด ดังนั้นเมื่อmaster developสมมติว่าการแก้ไขเหล่านี้เป็นสองคอมมิตที่แตกต่างกัน Git จะแสดงรายการใดก็ตามที่เกิดขึ้นในภายหลังเพราะจะอยู่ที่ด้านหน้าของคิว
ไม่ว่าในกรณีใดตอนนี้รหัสการเดินการแก้ไขจะทำงานแบบวนซ้ำ:
- ในขณะที่มีการคอมมิตในคิว:
- ลบรายการคิวแรก
- ตัดสินใจว่าจะพิมพ์คอมมิตนี้เลยหรือไม่ ตัวอย่างเช่น
--no-merges: พิมพ์อะไรเลยถ้าเป็นการรวมคอมมิต --before: ไม่พิมพ์อะไรเลยหากวันที่ไม่มาก่อนเวลาที่กำหนด หากการพิมพ์ไม่ถูกระงับให้พิมพ์คอมมิต: สำหรับgit logแสดงบันทึก สำหรับgit rev-listพิมพ์ ID แฮช
- ใส่บางส่วนหรือทั้งหมดนี้กระทำของผู้ปกครองกระทำลงในคิว (ตราบใดที่มันไม่ได้มีในขณะนี้และยังไม่ได้รับการเข้าชมแล้ว2 ) ค่าเริ่มต้นปกติคือใส่พ่อแม่ทั้งหมด การใช้จะ
--first-parentระงับทั้งหมดยกเว้นพาเรนต์แรกของการผสานแต่ละครั้ง
(ทั้งสองอย่างgit logและgit rev-listสามารถทำให้ประวัติเข้าใจง่ายโดยมีหรือไม่มีการเขียนซ้ำของผู้ปกครองณ จุดนี้ก็ได้เช่นกัน แต่เราจะข้ามไปตรงนี้)
สำหรับห่วงโซ่ธรรมดาเช่นเริ่มต้นที่HEADและทำงานย้อนกลับเมื่อไม่มีการรวมคอมมิตคิวจะมีหนึ่งคอมมิตอยู่ที่ด้านบนของลูปเสมอ มีหนึ่งข้อผูกพันดังนั้นเราจึงเปิดมันออกและพิมพ์และวางพาเรนต์ (เดี่ยว) ลงในคิวและดำเนินการอีกครั้งและเราทำตามห่วงโซ่ย้อนกลับจนกว่าเราจะไปถึงคอมมิตแรกหรือผู้ใช้เบื่อกับgit logผลลัพธ์และออก โปรแกรม. ในกรณีนี้ตัวเลือกการสั่งซื้อไม่มีความสำคัญ: มีเพียงรายการเดียวที่จะแสดง
เมื่อมีการผสานและเราติดตามพ่อแม่ทั้งสอง - "ขา" ของการผสานทั้งคู่หรือเมื่อคุณให้git logหรือgit rev-listมากกว่าหนึ่งข้อตกลงเริ่มต้นตัวเลือกการเรียงลำดับจะมีความสำคัญ
สุดท้ายให้พิจารณาผลของ--notหรือ^ต่อหน้าตัวระบุการคอมมิต มีหลายวิธีในการเขียน:
git log master --not develop
หรือ:
git log ^develop master
หรือ:
git log develop..master
ล้วนหมายถึงสิ่งเดียวกัน --notเป็นเหมือนคำนำหน้า^ยกเว้นว่าจะนำไปใช้มากกว่าหนึ่งชื่อ:
git log ^branch1 ^branch2 branch3
หมายถึงไม่ใช่ branch1 ไม่ใช่ branch2 ใช่ branch3; แต่:
git log --not branch1 branch2 branch3
หมายถึงไม่ใช่ branch1 ไม่ใช่ branch2 ไม่ใช่ branch3และคุณต้องใช้วินาที--notในการปิด:
git log --not branch1 branch2 --not branch3
ซึ่งค่อนข้างอึดอัด คำสั่ง "ไม่" สองคำสั่งจะรวมกันผ่าน XOR ดังนั้นหากคุณต้องการจริงๆคุณสามารถเขียน:
git log --not branch1 branch2 ^branch3
หมายถึงไม่ branch1 ไม่ branch2 ใช่ branch3ถ้าคุณต้องการที่จะทำให้งงงวย
ทั้งหมดนี้ทำงานโดยส่งผลต่อการเดินของกราฟ ในขณะที่git logหรือgit rev-listเดินตามกราฟจะต้องไม่ใส่ลงในคิวลำดับความสำคัญการคอมมิตใด ๆ ที่สามารถเข้าถึงได้จากการอ้างอิงที่ถูกลบล้าง (ในความเป็นจริงมันมีผลต่อการตั้งค่าเริ่มต้นด้วยเช่นกันการกระทำที่ถูกลบไม่สามารถเข้าสู่คิวลำดับความสำคัญได้ทันทีจากบรรทัดคำสั่งดังนั้นจึงgit log master ^masterไม่แสดงอะไรเลย)
ทั้งหมดของไวยากรณ์แฟนซีที่อธิบายไว้ในเอกสาร gitrevisionsgit rev-parseทำให้การใช้งานนี้และคุณสามารถสัมผัสนี้ด้วยการเรียกง่ายๆในการ ตัวอย่างเช่น:
$ git rev-parse origin/pu...origin/master # note: three dots
b34789c0b0d3b137f0bb516b417bd8d75e0cb306
fc307aa3771ece59e174157510c6db6f0d4b40ec
^b34789c0b0d3b137f0bb516b417bd8d75e0cb306
ทั้งสามจุดไวยากรณ์หมายถึงการกระทำที่สามารถเข้าถึงได้จากทั้งด้านซ้ายหรือด้านขวา แต่ไม่รวมกระทำสามารถเข้าถึงได้จากทั้งสองฝ่าย ในกรณีนี้การorigin/masterกระทำ b34789c0bนั้นสามารถเข้าถึงได้เองจากorigin/pu( fc307aa37...) ดังนั้นorigin/masterแฮชจึงปรากฏขึ้นสองครั้งโดยมีการปฏิเสธ แต่ในความเป็นจริง Git บรรลุไวยากรณ์สามจุดโดยการใส่การอ้างอิงเชิงบวกสองรายการ - ID แฮชที่ไม่ถูกลบสองรายการและ ค่าลบหนึ่งตัวแสดงด้วย^คำนำหน้า
เหมือนกัน:
$ git rev-parse master^^@
2c42fb76531f4565b5434e46102e6d85a0861738
2f0a093dd640e0dad0b261dae2427f2541b5426c
^@ไวยากรณ์หมายถึงทั้งหมดของผู้ปกครองที่ได้รับกระทำและmaster^ตัวเองที่พ่อแม่เป็นครั้งแรกของการกระทำที่เลือกโดยสาขาชื่อmaster-is ผสานกระทำจึงมีพ่อแม่ทั้งสอง นี่คือพ่อแม่สองคน และ:
$ git rev-parse master^^!
0b07eecf6ed9334f09d6624732a4af2da03e38eb
^2c42fb76531f4565b5434e46102e6d85a0861738
^2f0a093dd640e0dad0b261dae2427f2541b5426c
^!ต่อท้ายหมายถึงการกระทำของตัวเอง แต่ไม่มีพ่อแม่ ในกรณีนี้คือmaster^ 0b07eecf6...เราได้เห็นทั้งพ่อและแม่พร้อม^@คำต่อท้ายแล้ว พวกเขาอยู่ที่นี่อีกครั้ง แต่ครั้งนี้ถูกลบล้าง
1โปรแกรม Git จำนวนมากทำงานgit rev-listด้วยตัวเลือกต่างๆอย่างแท้จริงและอ่านผลลัพธ์เพื่อทราบว่าสิ่งใดที่กระทำและ / หรือวัตถุ Git อื่น ๆ ที่จะใช้
2เนื่องจากกราฟเป็นแบบวงจรจึงเป็นไปได้ที่จะรับประกันได้ว่าไม่มีการเยี่ยมชมแล้วหากเราเพิ่มข้อ จำกัดจะไม่แสดงผู้ปกครองก่อนที่จะแสดงลูกทั้งหมดตามลำดับความสำคัญ --date-order, --author-date-orderและ--topo-orderเพิ่มข้อ จำกัด นี้ ลำดับการจัดเรียงเริ่มต้นซึ่งไม่มีชื่อไม่มี หากการประทับเวลาการคอมมิตนั้นไม่ปลอดภัยตัวอย่างเช่นหากมีการกระทำบางอย่างเกิดขึ้น "ในอนาคต" โดยคอมพิวเตอร์ที่นาฬิกาปิดอยู่สิ่งนี้อาจนำไปสู่ผลลัพธ์ที่ดูแปลกได้ในบางกรณี
ถ้าคุณมาไกลขนาดนี้ตอนนี้คุณก็รู้มากแล้ว git log
สรุป:
git log เป็นการแสดงการกระทำที่เลือกไว้ในขณะที่เดินบางส่วนหรือทั้งหมดของกราฟ
--no-mergesอาร์กิวเมนต์ที่พบในทั้งสองได้รับการยอมรับและคำตอบที่ปัจจุบันติดอันดับยับยั้งการแสดงกระทำบางอย่างที่จะเดิน
--first-parentโต้แย้งจากปัจจุบันติดอันดับคำตอบยับยั้งเดินบางส่วนของกราฟในช่วงกราฟเดินเอง
--notคำนำหน้าอาร์กิวเมนต์บรรทัดคำสั่งที่ใช้ในคำตอบที่ได้รับการยอมรับยับยั้งเคยเยี่ยมชมบางส่วนของกราฟที่ทุกคนตั้งแต่เริ่มต้น
เราได้รับคำตอบที่ถูกใจสำหรับคำถามสองข้อที่แตกต่างกันโดยใช้คุณสมบัติเหล่านี้
git log ^branch1 ^branch2 merge-only-branchไวยากรณ์ล่ะ