การใช้ Git แสดงคอมมิตทั้งหมดที่มีอยู่ * เฉพาะ * ในสาขาหนึ่งที่เฉพาะเจาะจงไม่ใช่ * ใด ๆ * อื่น ๆ


87

กำหนดสาขาผมอยากจะเห็นรายการของกระทำที่มีอยู่เฉพาะในสาขาที่ ในคำถามนี้เราจะพูดถึงวิธีการดูว่าข้อตกลงใดอยู่ในสาขาหนึ่ง แต่ไม่ใช่สาขาอื่นที่ระบุอย่างน้อยหนึ่งสาขา

สิ่งนี้แตกต่างกันเล็กน้อย ฉันต้องการที่จะดูว่ากระทำอยู่ในสาขาหนึ่ง แต่ไม่ได้อยู่ในใด ๆสาขาอื่น

กรณีการใช้งานอยู่ในกลยุทธ์การแยกสาขาซึ่งควรรวมบางสาขาเข้าด้วยกันเท่านั้นและไม่เคยผูกมัดโดยตรง สิ่งนี้จะใช้เพื่อตรวจสอบว่ามีการคอมมิตโดยตรงในสาขา "ผสานเท่านั้น" หรือไม่

แก้ไข: ด้านล่างนี้เป็นขั้นตอนในการตั้งค่า dummy git repo เพื่อทดสอบ:

git init
echo foo1 >> foo.txt
git add foo.txt
git commit -am "initial valid commit"
git checkout -b merge-only
echo bar >> bar.txt
git add bar.txt
git commit -am "bad commit directly on merge-only"
git checkout master
echo foo2 >> foo.txt 
git commit -am "2nd valid commit on master"
git checkout merge-only 
git merge master

เฉพาะการคอมมิตที่มีข้อความ "การกระทำที่ไม่ถูกต้องโดยตรงกับการผสานเท่านั้น" ซึ่งสร้างขึ้นโดยตรงจากสาขาที่ผสานเท่านั้นควรจะปรากฏขึ้น


1
คำถามนี้ถือว่าสาขาที่รวมจากทั้งหมดมีอยู่ใน repo ในขณะนี้จะไม่ถูกลบเมื่อรวมเข้าด้วยกันอย่างสมบูรณ์และอาจไม่รวมเข้ากับการกรอไปข้างหน้า แจ้งให้ฉันทราบหากฉันขาดอะไรไป แต่สำหรับฉันแล้วสิ่งนี้ใช้ได้กับชุดการผสานจากสาขาที่ค่อนข้างเล็กเท่านั้นดังนั้นทำไมไม่ใช้git log ^branch1 ^branch2 merge-only-branchไวยากรณ์ล่ะ
Karl Bielefeldt

1
git log ^branch1 ^branch2 merge-only-branchต้องออกรายการทุกสาขาเดียว ที่สามารถหลีกเลี่ยงได้ด้วยการใช้ bash / grep อย่างชาญฉลาด (ดูคำตอบของฉันด้านล่าง) แต่ฉันหวังว่า git จะมีการสนับสนุนในตัวสำหรับสิ่งนี้ คุณถูกต้องที่ถือว่าสาขาทั้งหมดที่ผสานจากระยะไกล (เฉพาะโลคัลเท่านั้นที่ดีพอ ๆ กับที่ไม่มีอยู่กับ devs อื่น ๆ ) การใช้--no-mergesละเว้นคอมมิตใด ๆ ที่ถูกรวมเข้าแล้วลบการผสานจากสาขาดั้งเดิมออกไปดังนั้นจึงถือว่าการผสานจากสาขาจะถูกเก็บไว้จนกว่าจะรวมเข้ากับสาขาที่ไม่ใช่การผสานอย่างเดียว (เช่นมาสเตอร์)
jimmyorr

คำตอบ:


76

เราเพิ่งค้นพบโซลูชันที่สวยงามนี้

git log --first-parent --no-merges

ในตัวอย่างของคุณการกระทำเริ่มต้นยังคงปรากฏขึ้น

คำตอบนี้ไม่ได้ตอบคำถามอย่างตรงประเด็นเนื่องจากการกระทำเริ่มต้นยังคงปรากฏขึ้น ในทางกลับกันหลาย ๆ คนที่มาที่นี่ดูเหมือนจะพบคำตอบที่ต้องการ


1
เนื่องจากการกระทำครั้งแรกบนมาสเตอร์ยังคงปรากฏขึ้นจึงไม่ตอบคำถาม
jimmyorr

6
สิ่งนี้เพียงอย่างเดียวไม่เป็นไปตามเงื่อนไข"การคอมมิตที่มีอยู่เฉพาะในสาขานั้น"มันแสดงให้เห็นinitial valid commitซึ่งเป็นส่วนหนึ่งของทั้งสองสาขาmerge-onlyและ masterอย่างไรก็ตามหากเราพยายามพัตชื่อสาขาปัจจุบันในตอนท้ายตามด้วย ^ - ชื่อสาขาที่มีคำนำหน้าซึ่งรู้ว่ากิ่งปัจจุบันเกิดจากมันจะแก้ปัญหาได้ครึ่งหนึ่ง (ยกเว้นสิ่งที่รวมเข้าด้วยกัน) ตัวอย่าง:git log --first-parent --no-merges merge-only ^master
Slipp D.Thompson

14
ฉันไม่แน่ใจว่าทำไมถึงได้รับการโหวตมากขนาดนี้ดูเหมือนว่าจะไม่ตรงกับคำถามเลย ไม่ได้ให้ข้อมูลที่ผู้โพสต์ต้องการอย่างแน่นอน
Chris Rasys

2
คำตอบนี้อาจไม่สมบูรณ์แบบ แต่มันง่ายและใช้ได้ผลอย่างแน่นอน ฉันคิดว่ามีประโยชน์ในการเพิ่มชื่อสาขา - นั่นคือตัวกรองการกระทำทั้งหมดเป็นของสาขาที่กำหนด:git log --first-parent --no-merges | grep <branch_name>
artm

1
ขอขอบคุณ. ทางออกที่ดีที่สุด imo
Jakub Keller

29

ได้รับความอนุเคราะห์จากRedmumbaเพื่อนรักของฉัน:

git log --no-merges origin/merge-only \
    --not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
    grep -Fv refs/remotes/origin/merge-only)

... origin/merge-onlyชื่อสาขาที่ผสานอย่างเดียวระยะไกลของคุณอยู่ที่ไหน หากทำงานกับ git repo แบบโลคัลเท่านั้นให้แทนที่refs/remotes/originด้วยrefs/headsและแทนที่ชื่อสาขาระยะไกลorigin/merge-onlyด้วยชื่อสาขาท้องถิ่นmerge-onlyเช่น:

git log --no-merges merge-only \
    --not $(git for-each-ref --format="%(refname)" refs/heads |
    grep -Fv refs/heads/merge-only)

2
ฉันหวังว่าคนอื่นจะสามารถให้โซลูชันที่ไม่ต้องใช้ grep โดยใช้คอมไพล์เพียงอย่างเดียว แต่ถ้าไม่เป็นเช่นนั้นมันก็ดูดีมาก
jimmyorr

1
ใช่สง่างาม ใช้git for-each-refเพื่อแสดงชื่ออ้างอิงทุกชื่อที่มาและgrep -vเพื่อละเว้นสาขาที่ผสานเท่านั้น git logใช้--notตัวเลือกซึ่งเราส่งผ่านรายการอ้างอิงทั้งหมดของเรา (ยกเว้นสาขาที่ผสานเท่านั้น) หากคุณมีคำตอบที่สวยหรูกว่านี้สำหรับปัญหาเรามาฟังกัน
jimmyorr

2
โอ้ฉันแน่ใจว่ามันเป็นคำตอบที่สวยหรูที่สุด ฉันแค่เถียงว่ามัน "ซับซ้อน / ซับซ้อน" เล็กน้อยเพื่อความสง่างามที่แท้จริง :-) ไม่ได้หมายความว่าจะลบหลู่แนวทางของคุณครับ!
Chris K

1
การต่อท้าย/*ในgit for-each-refsคำสั่งขึ้นอยู่กับการไม่จับคู่ไฟล์ที่มีอยู่และไม่มีfailglobหรือnullglobตั้งค่า ( ตัวเลือกbash , เชลล์อื่น ๆ แตกต่างกันไป) คุณควรพูด / หลีกเลี่ยงเครื่องหมายดอกจันหรือเพียงแค่ปล่อยให้ต่อท้าย/*( git for-each-refรูปแบบสามารถจับคู่“ ตั้งแต่จุดเริ่มต้นจนถึงเครื่องหมายทับ”) อาจใช้grep -Fv refs/remotes/origin/foo( refs/heads/foo) เพื่อให้เข้มงวดมากขึ้นเกี่ยวกับการอ้างอิงที่ถูกตัดออก
Chris Johnsen

3
สามารถทำให้ง่ายขึ้นได้หากคุณต้องการดูข้อตกลงที่มีอยู่ในสาขาหนึ่งไม่ใช่ในสาขาอื่นgit log --no-merges B1 --not B2โดยที่ B1 คือสาขาที่คุณสนใจและ B2 คือสาขาที่คุณต้องการเปรียบเทียบ B1 กับ ทั้ง B1 และ B2 สามารถเป็นสาขาในพื้นที่หรือระยะไกลดังนั้นคุณสามารถระบุgit log --no-merges master --not origin/masterหรือระบุสาขาระยะไกลได้สองสาขา
mr.b

21
git log origin/dev..HEAD

สิ่งนี้จะแสดงให้คุณเห็นการกระทำทั้งหมดที่เกิดขึ้นในสาขาของคุณ


2
@Prakash origin/branchNameจะชี้ไปที่หัวหน้าสาขาที่ห่างไกลและHEADจะชี้ให้เห็นถึงการกระทำสุดท้ายในท้องถิ่นในสาขานั้น ดังนั้นสิ่งนี้จะไม่ทำงานเมื่อคุณใช้ git push
Bharat

คุณสามารถใช้สิ่งนี้เพื่อเปรียบเทียบสาขาในพื้นที่อื่นได้ การตั้งค่าสถานะ - ไม่รวมอาจมีประโยชน์ในการตอบคำถามดั้งเดิมของ OP
Paul Whipp

15

คำตอบ @Prakash ใช้งานได้ เพียงเพื่อความชัดเจน ...

git checkout feature-branch
git log master..HEAD

แสดงรายการคอมมิตใน feature-branch แต่ไม่ใช่ upstream branch (โดยทั่วไปคือ master ของคุณ)


12

บางทีนี่อาจช่วยได้:

git แสดงสาขา


2
ในขณะที่สิ่งนี้อาจตอบคำถามในทางทฤษฎีแต่ควรรวมส่วนสำคัญของคำตอบไว้ที่นี่และระบุลิงก์สำหรับการอ้างอิง
Vladimir Panteleev

นี่เป็นประโยชน์มาก ดูstackoverflow.com/a/7623339/874188สำหรับตัวอย่างที่ละเอียดยิ่งขึ้นและคำตอบที่เกี่ยวข้อง
tripleee

7

ลองสิ่งนี้:

git rev-list --all --not $(git rev-list --all ^branch)

โดยทั่วไปgit rev-list --all ^branchรับการแก้ไขทั้งหมดที่ไม่ได้อยู่ในสาขาจากนั้นคุณจะทำการแก้ไขทั้งหมดใน repo และลบรายการก่อนหน้าซึ่งเป็นการแก้ไขเฉพาะในสาขาเท่านั้น

ความคิดเห็นของ After @ Brian:

จากเอกสารของ git rev-list:

List commits that are reachable by following the parent links from the given commit(s)

ดังนั้นคำสั่งเช่นgit rev-list Aโดยที่ A เป็นคอมมิตจะแสดงรายการคอมมิตที่เข้าถึงได้จาก A รวมถึง A

ด้วยเหตุนี้จึงมีบางอย่างเช่น

git rev-list --all ^A

จะแสดงรายการคอมมิตที่ไม่สามารถเข้าถึงได้จาก A

ดังนั้นgit rev-list --all ^branchจะแสดงรายการการกระทำทั้งหมดที่ไม่สามารถเข้าถึงได้จากปลายสาขา ซึ่งจะลบคอมมิตทั้งหมดในสาขาหรือกล่าวอีกนัยหนึ่งคอมมิตที่อยู่ในสาขาอื่นเท่านั้น

ตอนนี้เรามาที่ git rev-list --all --not $(git rev-list --all ^branch)

จะเป็นเช่นนี้ git rev-list --all --not {commits only in other branches}

ดังนั้นเราจึงต้องการรายชื่อallที่ไม่สามารถเข้าถึงได้all commits only in other branches

ซึ่งเป็นชุดคอมมิตที่มีเฉพาะในสาขา ลองดูตัวอย่างง่ายๆ:

             master

             |

A------------B

  \

   \

    C--------D--------E

                      |

                      branch

เป้าหมายคือการได้รับ D และ E ซึ่งไม่ได้อยู่ในสาขาอื่น

git rev-list --all ^branch ให้เฉพาะ B

ตอนนี้git rev-list --all --not Bเป็นสิ่งที่เราลงมา ซึ่งก็เช่นกันgit rev-list -all ^B- เราต้องการให้การกระทำทั้งหมดไม่สามารถเข้าถึงได้จาก B ในกรณีของเรามันคือ D และ E ซึ่งเป็นสิ่งที่เราต้องการ

หวังว่านี่จะอธิบายวิธีการทำงานของคำสั่งอย่างถูกต้อง

แก้ไขหลังความคิดเห็น:

git init
echo foo1 >> foo.txt
git add foo.txt
git commit -am "initial valid commit"
git checkout -b merge-only
echo bar >> bar.txt
git add bar.txt
git commit -am "bad commit directly on merge-only"
git checkout master
echo foo2 >> foo.txt 
git commit -am "2nd valid commit on master"

หลังจากขั้นตอนข้างต้นหากคุณทำgit rev-list --all --not $(git rev-list --all ^merge-only)คุณจะได้รับสิ่งที่คุณกำลังมองหา - ขั้นตอนที่"bad commit directly on merge-only"หนึ่ง

แต่เมื่อคุณทำขั้นตอนสุดท้ายในขั้นตอนของคุณ git merge masterคำสั่งจะไม่ให้ผลลัพธ์ที่คาดหวัง เนื่องจากในตอนนี้ไม่มีคอมมิตที่ไม่มีในการผสานเท่านั้นเนื่องจากคอมมิตพิเศษหนึ่งตัวในมาสเตอร์ได้ถูกรวมเข้ากับการรวมเท่านั้น ดังนั้นgit rev-list --all ^branchให้ผลลัพธ์ที่ว่างเปล่าและด้วยเหตุนี้git rev-list -all --not $(git rev-list --all ^branch)จะให้คอมมิตทั้งหมดในการรวมเท่านั้น


1
หืม ... ไม่แน่ใจว่าทำไม แต่มันก็ไม่ได้ผล การวางเอาต์พุตคำสั่งของคุณเพื่อxargs -L 1 -t git branch -a --containsแสดงผลบวกที่ผิดพลาดจำนวนมาก (การกระทำที่เกิดขึ้นจริงในสาขาอื่น ๆ ) --no-mergesฉันพยายามที่มีและไม่มี ขอบคุณสำหรับคำตอบ!
jimmyorr

ดูเหมือนว่าจะทำงานได้ดีสำหรับฉันเท่าที่ฉันเห็นใน repo git จำลอง
manojlds

ฉันได้เพิ่มขั้นตอนในการสร้าง repo git จำลองเพื่อช่วยแสดงปัญหาด้วยคำตอบของคุณ
jimmyorr

อ๊ะอ๊ะ โหวตขึ้นก่อนที่ฉันจะคิดว่ามันมาตลอด จะทำให้คุณกระทำทั้งหมดที่ไม่ได้อยู่ในgit rev-list --all ^branch branchจากนั้นคุณจะลบสิ่งนั้นออกจากรายการที่อยู่ในbranch; แต่ตามความหมายการกระทำทั้งหมดที่ไม่ได้อยู่ในbranchนั้นไม่ได้อยู่ในbranchนั้นคุณจึงไม่ได้ลบอะไรเลย สิ่งที่จิมเมียร์กำลังมองหาคือความมุ่งมั่นที่อยู่ในbranchแต่ไม่ใช่masterหรือสาขาอื่นใด คุณไม่ต้องการลบการกระทำที่ไม่ได้อยู่ในbranch; คุณต้องการลบคอมมิตที่อยู่ในสาขาอื่น ๆ
Brian Campbell

1
@manojlds "(การแก้ไขทั้งหมด) - (การแก้ไขทั้งหมดที่ไม่ได้อยู่ในสาขา) = การแก้ไขในสาขา" ใช่นั่นใช้งานได้เพื่อรับการแก้ไขทั้งหมดbranchแต่ก็ทำได้เช่นgit rev-list branchกัน คุณแค่เขียนgit rev-list branchด้วยวิธีที่ซับซ้อนกว่า (และช้ากว่า) มันไม่ทำงานที่จะตอบคำถามซึ่งเป็นวิธีการที่จะหากระทำทั้งหมดในที่ไม่อยู่ในสาขาอื่นbranch
Brian Campbell

2

รูปแบบอื่นของคำตอบที่ยอมรับเพื่อใช้กับ master

git log origin/master --not $(git branch -a | grep -Fv master)

กรองคอมมิตทั้งหมดที่เกิดขึ้นในสาขาอื่นที่ไม่ใช่มาสเตอร์


1

นี่ไม่ใช่คำตอบที่แท้จริง แต่ฉันต้องการเข้าถึงการจัดรูปแบบและมีพื้นที่ว่างมากมาย ฉันจะพยายามที่จะอธิบายทฤษฎีที่อยู่เบื้องหลังสิ่งที่ผมคิดว่าทั้งสองคำตอบที่ดีที่สุด: ได้รับการยอมรับอย่างใดอย่างหนึ่งและ(อย่างน้อยในขณะนี้) ติดอันดับหนึ่ง แต่ในความเป็นจริงพวกเขาตอบที่แตกต่างกันคำถาม

การคอมมิตใน 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คำนำหน้าอาร์กิวเมนต์บรรทัดคำสั่งที่ใช้ในคำตอบที่ได้รับการยอมรับยับยั้งเคยเยี่ยมชมบางส่วนของกราฟที่ทุกคนตั้งแต่เริ่มต้น

เราได้รับคำตอบที่ถูกใจสำหรับคำถามสองข้อที่แตกต่างกันโดยใช้คุณสมบัติเหล่านี้

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