"rebase --preserve-meres" ของ git ทำอะไรได้บ้าง (และทำไม)


355

เอกสารrebaseของ Git สำหรับคำสั่งนั้นค่อนข้างสั้น:

--preserve-merges
    Instead of ignoring merges, try to recreate them.

This uses the --interactive machinery internally, but combining it
with the --interactive option explicitly is generally not a good idea
unless you know what you are doing (see BUGS below).

ดังนั้นสิ่งที่เกิดขึ้นจริงเมื่อคุณใช้--preserve-merges? มันแตกต่างจากพฤติกรรมเริ่มต้น (ไม่มีธงนั้น) อย่างไร การ "สร้าง" การผสานหมายความว่าอย่างไร


20
คำเตือน: เริ่มต้นด้วย Git 2.18 (Q2 2018 5 ปีต่อมา) ในที่สุดจะแทนที่เดิมgit --rebase-merges git --preserve-mergesดูคำตอบของฉันด้านล่าง
VonC

คำตอบ:


464

เช่นเดียวกับการรีบูต Git ปกติ Git ที่มี--preserve-mergesอันดับแรกจะระบุรายการการคอมมิทที่ทำในส่วนหนึ่งของกราฟการคอมมิท ความแตกต่างกับ--preserve-mergesข้อกังวลที่เลือกคอมมิตสำหรับการเล่นซ้ำและวิธีการเล่นซ้ำนั้นเหมาะสำหรับการรวมคอมมิท

เพื่อให้ชัดเจนยิ่งขึ้นเกี่ยวกับความแตกต่างที่สำคัญระหว่าง rebase ปกติและการผสานผสาน:

  • การรีบูตรีแบบผสานการรักษายินดีที่จะเล่นซ้ำ (บางส่วน) รวมการคอมมิชชันในขณะที่การรีบูตแบบปกติละเว้นการคอมมิทการรวม
  • เนื่องจากยินดีที่จะเล่นซ้ำรวมกันทำ rebase รักษารวมต้องกำหนดสิ่งที่หมายถึงการเล่นซ้ำผสานกระทำและจัดการกับริ้วรอยพิเศษบางอย่าง
    • ส่วนที่น่าสนใจที่สุดในแนวความคิดอาจจะเป็นในการเลือกสิ่งที่ผู้ปกครองผสานความมุ่งมั่นใหม่ควรจะเป็น
    • การคอมไพล์การเล่นซ้ำนั้นต้องมีการตรวจสอบคอมมิชชัน ( git checkout <desired first parent>) อย่างชัดเจนในขณะที่การรีบูตแบบปกติไม่ต้องกังวล
  • การรีบูตรักษาแบบผสานจะพิจารณาชุดคำสั่งที่ตื้นกว่าสำหรับการเล่นซ้ำ:
    • โดยเฉพาะอย่างยิ่งจะพิจารณาเฉพาะการเล่นซ้ำที่กระทำเนื่องจากฐานการผสานล่าสุดคือเวลาล่าสุดที่แยกจากกันทั้งสองสาขา - ในขณะที่การรีบูตปกติอาจเล่นซ้ำกลับไปเป็นครั้งแรกที่แยกสองสาขา
    • เพื่อเป็นการชั่วคราวและไม่ชัดเจนฉันเชื่อว่านี่เป็นวิธีที่ดีที่สุดในการคัดกรองการเล่นซ้ำ "การกระทำเก่า ๆ " ที่ได้รับการ "รวมเข้ากับ" การรวมการกระทำ

ครั้งแรกฉันจะพยายามอธิบาย "พอเพียง" สิ่งที่ rebase --preserve-mergesทำแล้วจะมีตัวอย่าง แน่นอนหนึ่งสามารถเริ่มต้นด้วยตัวอย่างหากดูเหมือนว่ามีประโยชน์มากขึ้น

อัลกอริทึมใน "บทสรุป"

git-rebase--interactive.shหากคุณต้องการจริงๆได้รับในวัชพืชดาวน์โหลดแหล่งที่มาคอมไพล์และสำรวจไฟล์ (Rebase ไม่ได้เป็นส่วนหนึ่งของแกน C ของ Git แต่เขียนด้วยภาษาทุบตีและหลังฉากแล้วจะมีการแชร์รหัสกับ "rebase เชิงโต้ตอบ")

แต่ที่นี่ฉันจะร่างสิ่งที่ฉันคิดว่าเป็นสาระสำคัญของมัน เพื่อที่จะลดจำนวนสิ่งที่ต้องคิดลงไป (เช่นฉันไม่พยายามที่จะจับภาพด้วยความแม่นยำ 100% ลำดับที่แม่นยำที่ใช้ในการคำนวณและไม่สนใจหัวข้อที่ดูเหมือนอยู่ตรงกลางน้อยกว่าเช่นสิ่งที่ต้องทำเกี่ยวกับการกระทำที่ได้รับการเลือกระหว่างเชอร์รี่แล้ว)

ก่อนอื่นให้สังเกตว่า rebase ที่ไม่ได้ทำการผสานนั้นค่อนข้างง่าย มันมากหรือน้อย:

Find all commits on B but not on A ("git log A..B")
Reset B to A ("git reset --hard A") 
Replay all those commits onto B one at a time in order.

การ Rebase --preserve-mergesค่อนข้างซับซ้อน นี่เป็นเรื่องง่ายอย่างที่ฉันสามารถทำได้โดยไม่สูญเสียสิ่งที่ดูเหมือนว่าสำคัญ:

Find the commits to replay:
  First find the merge-base(s) of A and B (i.e. the most recent common ancestor(s))
    This (these) merge base(s) will serve as a root/boundary for the rebase.
    In particular, we'll take its (their) descendants and replay them on top of new parents
  Now we can define C, the set of commits to replay. In particular, it's those commits:
    1) reachable from B but not A (as in a normal rebase), and ALSO
    2) descendants of the merge base(s)
  If we ignore cherry-picks and other cleverness preserve-merges does, it's more or less:
    git log A..B --not $(git merge-base --all A B)
Replay the commits:
  Create a branch B_new, on which to replay our commits.
  Switch to B_new (i.e. "git checkout B_new")
  Proceeding parents-before-children (--topo-order), replay each commit c in C on top of B_new:
    If it's a non-merge commit, cherry-pick as usual (i.e. "git cherry-pick c")
    Otherwise it's a merge commit, and we'll construct an "equivalent" merge commit c':
      To create a merge commit, its parents must exist and we must know what they are.
      So first, figure out which parents to use for c', by reference to the parents of c:
        For each parent p_i in parents_of(c):
          If p_i is one of the merge bases mentioned above:
            # p_i is one of the "boundary commits" that we no longer want to use as parents
            For the new commit's ith parent (p_i'), use the HEAD of B_new.
          Else if p_i is one of the commits being rewritten (i.e. if p_i is in R):
            # Note: Because we're moving parents-before-children, a rewritten version
            # of p_i must already exist. So reuse it:
            For the new commit's ith parent (p_i'), use the rewritten version of p_i.
          Otherwise:
            # p_i is one of the commits that's *not* slated for rewrite. So don't rewrite it
            For the new commit's ith parent (p_i'), use p_i, i.e. the old commit's ith parent.
      Second, actually create the new commit c':
        Go to p_1'. (i.e. "git checkout p_1'", p_1' being the "first parent" we want for our new commit)
        Merge in the other parent(s):
          For a typical two-parent merge, it's just "git merge p_2'".
          For an octopus merge, it's "git merge p_2' p_3' p_4' ...".
        Switch (i.e. "git reset") B_new to the current commit (i.e. HEAD), if it's not already there
  Change the label B to apply to this new branch, rather than the old one. (i.e. "git reset --hard B")

Rebase ด้วย--onto Cข้อโต้แย้งควรจะคล้ายกันมาก เพียงแค่เริ่มเล่นที่ HEAD of B คุณก็จะเริ่มเล่นที่ HEAD of C แทน (และใช้ C_new แทน B_new)

ตัวอย่างที่ 1

ตัวอย่างเช่นจดกราฟ

  B---C <-- master
 /                     
A-------D------E----m----H <-- topic
         \         /
          F-------G

m คือการรวมกับผู้ปกครอง E และ G

สมมติว่าเรา rebased หัวข้อ (H) ที่ด้านบนของ master (C) โดยใช้ rebase ปกติที่ไม่ผสานการรักษา (ตัวอย่างเช่นหัวข้อเช็คเอาต์; rebase master ) ในกรณีนั้น git จะเลือกคอมมิตต่อไปนี้เพื่อเล่นซ้ำ:

  • เลือก D
  • เลือก E
  • เลือก F
  • เลือก G
  • เลือก H

จากนั้นอัปเดตกราฟการกระทำดังนี้:

  B---C <-- master
 /     \                
A       D'---E'---F'---G'---H' <-- topic

(D 'เป็นค่าที่เล่นซ้ำของ D เป็นต้น)

โปรดทราบว่าการผสานกระทำ m ไม่ได้ถูกเลือกสำหรับเล่นซ้ำ

ถ้าเราทำการ--preserve-mergesrebase ของ H แทนC (ตัวอย่างเช่นหัวข้อ checkout; rebase --preserve-merges master .) ในกรณีใหม่นี้ git จะเลือกข้อผูกพันต่อไปนี้สำหรับการเล่นซ้ำ:

  • เลือก D
  • เลือก E
  • เลือก F (ไปยัง D 'ในสาขา' subtopic ')
  • เลือก G (ไปยัง F 'ในสาขา' subtopic ')
  • เลือกสาขาย่อย 'หัวข้อย่อย' เป็นหัวข้อ
  • เลือก H

ตอนนี้ m ถูกเลือกให้เล่นซ้ำ นอกจากนี้โปรดทราบว่าการรวมผู้ปกครอง E และ G ถูกเลือกสำหรับการรวมก่อนที่จะรวมการกระทำ m

นี่คือกราฟความมุ่งมั่นที่เกิดขึ้น:

 B---C <-- master
/     \                
A      D'-----E'----m'----H' <-- topic
        \          / 
         F'-------G'

อีกครั้ง D 'เป็นเวอร์ชั่นเชอร์รี่ที่ถูกเลือก (เช่นสร้างขึ้นใหม่) ของ D. Same for E' และอื่น ๆ ทุกการกระทำที่ไม่ได้ทำในมาสเตอร์ได้รับการเล่นซ้ำ ทั้ง E และ G (ผู้ปกครองที่รวมกันของ m) ได้รับการสร้างใหม่เป็น E 'และ G' เพื่อทำหน้าที่เป็นผู้ปกครองของ m '(หลังจากรีบูตประวัติของต้นไม้ยังคงเหมือนเดิม)

ตัวอย่างที่ 2

ซึ่งแตกต่างจาก rebase ปกติ rebase รักษาผสานสามารถสร้างลูกหลายคนของหัวต้นน้ำ

ตัวอย่างเช่นพิจารณา:

  B---C <-- master
 /                     
A-------D------E---m----H <-- topic
 \                 |
  ------- F-----G--/ 

หากเราทำการ rebase H (หัวข้อ) ที่ด้านบนของ C (master) ดังนั้นการคอมมิทที่เลือกสำหรับ rebase คือ:

  • เลือก D
  • เลือก E
  • เลือก F
  • เลือก G
  • เลือก m
  • เลือก H

และผลลัพธ์ก็เป็นเช่นนั้น:

  B---C  <-- master
 /    | \                
A     |  D'----E'---m'----H' <-- topic
       \            |
         F'----G'---/

ตัวอย่างที่ 3

ในตัวอย่างข้างต้นทั้งการรวมการกระทำและผู้ปกครองทั้งสองจะมีการเล่นซ้ำกระทำมากกว่าผู้ปกครองเดิมที่ผสานเดิมกระทำ อย่างไรก็ตามใน rebases อื่น ๆ ความมุ่งมั่นที่เล่นซ้ำอาจจบลงด้วยผู้ปกครองที่อยู่ในกราฟการกระทำก่อนที่จะรวม

ตัวอย่างเช่นพิจารณา:

  B--C---D <-- master
 /    \                
A---E--m------F <-- topic

หากเรา rebase หัวข้อไปยังต้นแบบ (รักษาการผสาน) แล้วความมุ่งมั่นที่จะเล่นซ้ำจะเป็น

  • เลือกผสานกระทำ m
  • เลือก F

กราฟการกระทำที่เขียนซ้ำจะมีลักษณะดังนี้:

                     B--C--D <-- master
                    /       \             
                   A-----E---m'--F'; <-- topic

การผสาน replayed commit m 'ทำให้ผู้ปกครองที่มีอยู่แล้วในกราฟการกระทำคือ D (หัวหน้าหัวหน้า) และ E (หนึ่งในผู้ปกครองของต้นฉบับผสานกระทำ m)

ตัวอย่างที่ 4

การรีบูตรักษาผสานอาจทำให้สับสนในบางกรณี อย่างน้อยนี่เป็นจริงเพียงบาง git รุ่นเก่ากว่า (เช่น 1.7.8.)

ใช้กราฟการกระทำนี้:

                   A--------B-----C-----m2---D <-- master
                    \        \         /
                      E--- F--\--G----/
                            \  \
                             ---m1--H <--topic

โปรดทราบว่าทั้งสองยอมรับ m1 และ m2 ควรรวมการเปลี่ยนแปลงทั้งหมดจาก B และ F

หากเราพยายามทำgit rebase --preserve-mergesH (หัวข้อ) ไปยัง D (หลัก) แล้วคอมมิตต่อไปนี้จะถูกเลือกเพื่อเล่นซ้ำ:

  • เลือก m1
  • เลือก H

โปรดทราบว่าการเปลี่ยนแปลง (B, F) รวมกันใน m1 ควรรวมอยู่ใน D แล้ว (การเปลี่ยนแปลงเหล่านั้นควรรวมอยู่ใน m2 แล้วเนื่องจาก m2 ผสานรวมกับลูกของ B และ F) ดังนั้นจึงมีแนวคิดที่แสดง m1 ที่ด้านบนของ D น่าจะเป็น no-op หรือสร้างการมอบหมายที่ว่างเปล่า (เช่นที่ซึ่งความแตกต่างระหว่างการแก้ไขต่อเนื่องนั้นว่างเปล่า)

อย่างไรก็ตามอย่างไรก็ตาม git อาจปฏิเสธความพยายามที่จะเล่น m1 ซ้ำบน D. คุณสามารถรับข้อผิดพลาดดังนี้:

error: Commit 90caf85 is a merge but no -m option was given.
fatal: cherry-pick failed

ดูเหมือนว่าหนึ่งลืมที่จะส่งธงไปยังคอมไพล์ แต่ปัญหาพื้นฐานคือคอมไพล์ไม่ชอบการสร้างความมุ่งมั่นที่ว่างเปล่า


6
ฉันพบว่าgit rebase --preserve-mergesเป็นมากช้ากว่าโดยไม่ต้องrebase --preserve-mergesนั่นคือผลข้างเคียงของการค้นหาสิ่งที่ถูกต้องหรือไม่? มีสิ่งใดบ้างที่สามารถทำได้เพื่อเร่งความเร็ว? (โดยวิธีการ…ขอบคุณสำหรับคำตอบที่ละเอียดมาก!)
David Alan Hjelle

7
ดูเหมือนว่าคุณควรใช้ --preserve-merges มิฉะนั้นมีความเป็นไปได้ที่จะเสียประวัตินั่นคือการรวมการกระทำ
DarVar

19
@DarVar คุณมักจะสูญเสียประวัติในการรีบูตเพราะคุณอ้างว่าการเปลี่ยนแปลงที่เกิดขึ้นกับฐานข้อมูลที่แตกต่างจากที่พวกเขาอยู่ที่ไหน
Chronial

5
นี่ยังเป็น "คำตอบชั่วคราว" หรือไม่?
Andrew Grimm

5
@ ลำดับเหตุการณ์แน่นอนว่าการที่คุณทำการรีบูตนั้นจะเป็นการรวมประวัติที่สูญเสียเสมอไป แต่บางที DarVar กำลังพูดถึงความจริงที่ว่าคุณไม่เพียง แต่จะหลวมประวัติศาสตร์เท่านั้น แต่ยังเปลี่ยนเป็นรหัสฐานด้วย การแก้ไขความขัดแย้งมีข้อมูลที่สูญหายในวิธีที่เป็นไปได้ทั้งหมดที่สามารถทำการรีบูตได้ คุณต้องทำซ้ำเสมอ ไม่มีทางที่จะยอมให้แก้ไขความขัดแย้งของคุณซ้ำได้ไหม? ทำไมไม่คอมไพล์เชอร์รี่รับผสานกระทำ
Nils_M

94

Git 2.18 (Q2 2018) จะปรับปรุง--preserve-mergeตัวเลือกอย่างมากโดยการเพิ่มตัวเลือกใหม่

" git rebase" เรียนรู้ " --rebase-merges" เพื่อปลูกโครงสร้างทั้งหมดของการกระทำอื่น ๆ กราฟ

(หมายเหตุ: Git 2.22, Q2 2019 จริงเลิกใช้แล้ว --preserve-mergeและ Git 2.25, Q1 2020 หยุดโฆษณาในgit rebase --helpเอาต์พุต "" )

ดูกระทำ 25cff9f ,กระทำ 7543f6f , กระทำ 1131ec9 , กระทำ 7ccdf65 , กระทำ 537e7d6 , กระทำ a9be29c , กระทำ 8f6aed7 , กระทำ 1644c73 , กระทำ d1e8b01 , กระทำ 4c68e7d , กระทำ 9055e40 , กระทำ cb5206e , กระทำ a01c2a5 , กระทำ 2f6b1d1 , กระทำ bf5c057 (25 เมษายน 2018) โดยโยฮันเน Schindelin (dscho )
ดูกระทำ f431d73 (25 เมษายน 2018) โดยสเตฟานสนั่น (stefanbeller )
ดูกระทำ 2429335 (25 เมษายน 2018) โดยฟิลลิปไม้ (phillipwood )
(รวมโดยJunio C Hamano - gitster-ในการกระทำ 2c18e6a , 23 พฤษภาคม 2018)

pull: ยอมรับ--rebase-mergesเพื่อสร้างโทโพโลยีสาขาใหม่

คล้ายกับpreserveโหมดเพียงแค่ส่ง--preserve-merges ตัวเลือกไปยังrebaseคำสั่งmergesโหมดก็จะส่ง --rebase-mergesตัวเลือก

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


git rebaseหน้าคนตอนนี้มีส่วนเต็มทุ่มเทให้กับการ rebasing ประวัติศาสตร์ที่มีการผสาน

สารสกัดจาก:

มีเหตุผลที่ถูกต้องว่าทำไมนักพัฒนาอาจต้องการสร้างการรวมซ้ำ: เพื่อรักษาโครงสร้างของสาขา (หรือ "กระทำโทโพโลยี") เมื่อทำงานกับสาขาที่เกี่ยวข้องกันหลายสาขา

ในตัวอย่างต่อไปนี้ผู้พัฒนาทำงานบนแขนงหัวข้อที่ refactors วิธีการกำหนดปุ่มและบนอีกหัวข้อสาขาที่ใช้การปรับโครงสร้างใหม่นั้นเพื่อใช้ปุ่ม "รายงานข้อบกพร่อง"
ผลลัพธ์ของgit log --graph --format=%s -5อาจมีลักษณะเช่นนี้:

*   Merge branch 'report-a-bug'
|\
| * Add the feedback button
* | Merge branch 'refactor-button'
|\ \
| |/
| * Use the Button class for all buttons
| * Extract a generic Button class from the DownloadButton one

ผู้พัฒนาอาจต้องการรีบูตสิ่งที่ผูกมัดให้ใหม่กว่าmaster ในขณะที่รักษาโทโพโลยีสาขาเช่นเมื่อคาดว่าสาขาหัวข้อแรกจะรวมเข้ากับmasterเร็วกว่าอันที่สองพูดเพื่อแก้ไขความขัดแย้งผสานกับการเปลี่ยนแปลง DownloadButtonคลาสที่สร้างขึ้น masterมันกลายเป็น

การรีบูตนี้สามารถทำได้โดยใช้--rebase-mergesตัวเลือก


ดูคอมมิชชัน 1644c73สำหรับตัวอย่างเล็ก ๆ :

rebase-helper --make-script: แนะนำการตั้งค่าสถานะเพื่อลดการรวม

ซีเควนเซอร์เพิ่งเรียนรู้คำสั่งใหม่ที่มีวัตถุประสงค์เพื่อสร้างโครงสร้างสาขาใหม่ ( คล้ายกับวิญญาณ--preserve-mergesแต่มีการออกแบบที่หักน้อยกว่าอย่างมาก )

มาอนุญาตให้rebase--helperสร้างรายการสิ่งที่ต้องทำใช้ประโยชน์จากคำสั่งเหล่านี้ที่ถูกกระตุ้นโดย--rebase-mergesตัวเลือกใหม่
สำหรับทอพอโลยีแบบนี้ (ที่ HEAD ชี้ไปที่ C):

- A - B - C (HEAD)
    \   /
      D

รายการสิ่งที่สร้างขึ้นจะมีลักษณะเช่นนี้:

# branch D
pick 0123 A
label branch-point
pick 1234 D
label D

reset branch-point
pick 2345 B
merge -C 3456 D # C

ความแตกต่าง--preserve-mergeคืออะไร?
Commit 8f6aed7อธิบาย:

กาลครั้งหนึ่งนักพัฒนานี่คิดว่าจะไม่เป็นการดีถ้าพูดว่า Git สำหรับ Windows 'ติดตั้งอยู่บนแกน Git อาจแสดงเป็นกิ่งก้านหนาและถูกนำกลับมาใช้ใหม่บนแกน Git เพื่อ คงชุดของชุดแพทช์เชอร์รี่เลือก?

ความพยายามเริ่มแรกที่จะตอบคำถามนี้คือ: git rebase --preserve-merges.

อย่างไรก็ตามการทดลองนั้นไม่ได้มีจุดประสงค์เพื่อเป็นตัวเลือกแบบโต้ตอบเท่านั้นและมีการสำรองหมูไว้เท่านั้นgit rebase --interactiveเนื่องจากการใช้งานคำสั่งนั้นดูคุ้นเคยและคุ้นเคยเป็นอย่างมาก: ถูกออกแบบโดยบุคคลเดียวกันกับผู้ออกแบบ--preserve-merges: ของคุณอย่างแท้จริง

และโดย "ของคุณอย่างแท้จริง" ผู้เขียนอ้างถึงตัวเอง: Johannes Schindelin ( dscho)ซึ่งเป็นเหตุผลหลัก (มีฮีโร่อีกสองสามคน - Hannes, Steffen, Sebastian, ... ) ที่เรามี Git สำหรับ Windows (แม้ว่าย้อนกลับไปในวัน - 2009 - นั่นไม่ใช่เรื่องง่าย )
เขาทำงานที่ Microsoft มาตั้งแต่เดือนกันยายน 2558ซึ่งสมเหตุสมผลเมื่อพิจารณาว่า Microsoft ใช้ Git อย่างหนักและต้องการบริการของเขา
ว่าแนวโน้มเริ่มต้นในปี 2013 จริงกับ TFS ตั้งแต่นั้นมา Microsoft จัดการพื้นที่เก็บข้อมูล Git ที่ใหญ่ที่สุดในโลก ! และตั้งแต่ตุลาคม 2018 ไมโครซอฟท์ได้รับ GitHub

คุณสามารถเห็นโยฮันเนสพูดในวิดีโอนี้สำหรับ Git Merge 2018 ในเดือนเมษายน 2018

หลังจากนั้นไม่นานนักพัฒนาคนอื่น (ฉันกำลังมองหาคุณ Andreas! ;-)) ตัดสินใจว่าจะเป็นการดีที่จะอนุญาตให้--preserve-mergesรวมกับ--interactive(กับ caveats!) และผู้ดูแล Git (ดีผู้ดูแล Git ชั่วคราว) ในช่วงที่ไม่อยู่ของจูนิโอนั่นคือ) เห็นด้วยและนั่นคือเมื่อความเย้ายวนใจของการ--preserve-mergesออกแบบเริ่มกระจุยค่อนข้างเร็วและไร้เสน่ห์

โยนาธานกำลังพูดถึงที่นี่ Andreas Schwabจาก Suse
คุณสามารถเห็นบางส่วนของการสนทนาของพวกเขากลับในปี 2012

เหตุผล? ใน--preserve-mergesโหมดผู้ปกครองผสานกระทำ (หรือว่าเรื่องของใด ๆ ที่กระทำ) ที่ไม่ได้ระบุไว้อย่างชัดเจน แต่ โดยนัยตามชื่อกระทำการส่งผ่านไปยังpickคำสั่ง

นี่เองที่ทำให้มันเป็นไปไม่ได้เช่นการกระทำการสั่งซื้อ
ไม่ต้องพูดถึงที่จะย้ายกระทำระหว่างกิ่งไม้หรือเทพห้ามแยกสาขาหัวข้อออกเป็นสอง

อนิจจาข้อบกพร่องเหล่านี้ยังป้องกันโหมดนั้น (ซึ่งมีจุดประสงค์ดั้งเดิมเพื่อตอบสนองความต้องการของ Git สำหรับ Windows ด้วยความหวังเพิ่มเติมว่ามันอาจเป็นประโยชน์ต่อผู้อื่นเช่นกัน) จากการให้บริการความต้องการของ Git สำหรับ Windows

ห้าปีต่อมาเมื่อมันไม่สามารถป้องกันได้จริง ๆ ที่มีชุดแพทช์ฮ็อดจ์ - พอดจ์ขนาดใหญ่ที่เกี่ยวข้องบางส่วนแพทช์ที่ไม่เกี่ยวข้องบางส่วนใน Git สำหรับ Windows ที่ถูก rebased ลงบนแท็ก Git หลักเป็นครั้งคราว ของโชคไม่ดี git-remote-hgซีรีส์ที่ที่ Git ล้าสมัยครั้งแรกสำหรับวิธีการแข่งขันของ Windows เพียงเพื่อถูกทอดทิ้งโดยไม่ต้องดูแลในภายหลัง) ไม่สามารถป้องกันได้จริง ๆ " Git garden shears " เกิดมา : สคริปต์ลูกหมูบนหลังรีบูต ที่แรกจะกำหนดสาขาโทโพโลยีของแพทช์ที่จะ rebased สร้างรายการสิ่งที่ต้องทำหลอกสำหรับการแก้ไขเพิ่มเติมเปลี่ยนผลเป็นรายการสิ่งที่ต้องทำจริง (การใช้งานหนักของexec คำสั่งเพื่อ "ใช้" คำสั่งรายการสิ่งที่ต้องทำที่หายไป) และสร้างชุดข้อมูลแก้ไขที่ด้านบนสุดของการคอมมิทฐานใหม่

(สคริปต์ Git garden shears อ้างอิงในโปรแกรมแก้ไขนี้ในการคอมมิชชัน 9055e40 )

นั่นคือในปี 2013
และใช้เวลาประมาณสามสัปดาห์ในการออกแบบและนำมันมาใช้เป็นสคริปต์นอกกรอบ จำเป็นต้องพูดการใช้งานต้องใช้เวลาไม่กี่ปีในการทำให้มีเสถียรภาพในขณะที่การออกแบบนั้นได้รับการพิสูจน์ว่ามีเสียง

ด้วยแพทช์นี้คุณงามความดีของ Git สวนกรรไกรมาถึงgit rebase -iตัวเอง
การส่งผ่าน--rebase-mergesตัวเลือกจะสร้างรายการสิ่งที่ต้องทำซึ่งสามารถเข้าใจได้อย่างง่ายดายและในกรณีที่เห็นได้ชัดว่าจะจัดลำดับใหม่ได้อย่างไร
สาขาใหม่ที่สามารถนำมาใช้โดยการใส่คำสั่งและเรียกlabel และเมื่อโหมดนี้มีเสถียรภาพและเป็นที่ยอมรับในระดับสากลเราสามารถคัดค้านข้อผิดพลาดในการออกแบบที่เกิดขึ้นได้merge <label>
--preserve-merges


Git 2.19 (ไตรมาสที่ 3 ปี 2018) ปรับปรุง--rebase-mergesตัวเลือกใหม่ด้วยการทำงานกับ--execมัน

"การ--exec" เลือกที่จะ " git rebase --rebase-merges" วางคำสั่ง exec ในสถานที่ที่ไม่ถูกต้องซึ่งได้รับการแก้ไข

ดูกระทำ 1ace63b (9 สิงหาคม 2018) และกระทำ f0880f7 (6 สิงหาคม 2018) โดยโยฮันเน Schindelin (dscho )
(รวมโดยJunio C Hamano - gitster-ในการกระทำ 750eb11 , 20 สิงหาคม 2018)

rebase --exec: ทำให้มันทำงานกับ --rebase-merges

ความคิดของ--execคือการผนวกโทรหลังจากที่แต่ละexecpick

ตั้งแต่การแนะนำfixup!/ s quash!กระทำความคิดนี้ถูกขยายเพื่อนำไปใช้กับ "เลือกอาจตามด้วย fixup / สควอชโซ่" กล่าวคือผู้บริหารจะไม่ถูกแทรกระหว่าง a pickและใด ๆ ที่เกี่ยวข้อง fixupหรือsquashบรรทัด

การดำเนินงานในปัจจุบันใช้เป็นเคล็ดลับสกปรกเพื่อให้บรรลุว่าจะอนุมานว่ามีเพียงรับ / fixup / สควอชคำสั่งแล้ว แทรกexecสายใด ๆ ก่อนpickแต่แรกและผนวกสุดท้าย

กับรายการสิ่งที่ต้องทำที่เกิดจากgit rebase --rebase-mergesการดำเนินงานที่เรียบง่ายนี้แสดงให้เห็นว่าปัญหาของมันก็ก่อให้เกิดสิ่งที่ผิดที่แน่นอนเมื่อมีlabel, resetและmergeคำสั่ง

ลองเปลี่ยนการใช้งานเพื่อทำสิ่งที่เราต้องการอย่างแน่นอน: มองหา pickสายข้ามโซ่การแก้ไข / สควอชแล้วแทรกexecเส้น นวดแล้วล้างซ้ำ

หมายเหตุ: เราใช้ความเจ็บปวดในการแทรกก่อนบรรทัดความคิดเห็นทุกครั้งที่เป็นไปได้เนื่องจากคอมมิทที่ว่างเปล่าจะถูกแสดงโดยสายการเลือกความคิดเห็น (และเราต้องการแทรกบรรทัด exec ของการเลือกก่อนหน้าก่อนที่จะสายดังกล่าวไม่ได้หลังจากนั้น)

ในขณะที่มันยังเพิ่มexecบรรทัดหลังจากmergeคำสั่งเพราะพวกเขามีความคล้ายคลึงกันในจิตวิญญาณกับpickคำสั่ง: พวกเขาเพิ่มการกระทำใหม่


Git 2.22 (Q2 2019) แก้ไขการใช้ refs / rewritten / ลำดับชั้นเพื่อเก็บสถานะกลาง rebase ซึ่งโดยเนื้อแท้ทำให้ลำดับชั้นต่อ worktree

ดูกระทำ b9317d5 , กระทำ 90d31ff , กระทำ 09e6564 (7 มีนาคม 2019) โดยNguyễnTháiNgọc Duy (pclouds )
(ผสานโดยJunio ​​C Hamano - gitster- in 917f2cd , 09 Apr 2019)

ตรวจสอบให้แน่ใจ refs / rewritten / เป็นต่อ worktree

a9be29c (sequencer: สร้าง refs ที่สร้างโดยlabelคำสั่ง worktree-local, 2018-04-25, Git 2.19) เพิ่มrefs/rewritten/เป็นพื้นที่อ้างอิงต่อ worktree
น่าเสียดายที่ (ไม่ดีของฉัน) มีสถานที่สองแห่งที่จำเป็นต้องมีการอัปเดตเพื่อให้แน่ใจว่าเป็นจริงต่อการทำงาน

- add_per_worktree_entries_to_dir()มีการอัปเดตเพื่อให้แน่ใจว่ารายชื่อผู้อ้างอิงจะดูที่รายการต่อการทำงานrefs/rewritten/แทนการซื้อต่อรายการหนึ่งรายการ

  • common_list[]มีการอัปเดตเพื่อให้git_path()ส่งคืนตำแหน่งที่ถูกต้อง ซึ่งรวมถึง "rev-parse --git-path "

ความยุ่งเหยิงนี้สร้างโดยฉัน
ฉันเริ่มพยายามแก้ไขด้วยการแนะนำrefs/worktree,ที่อ้างอิงทั้งหมดจะเป็นต่อ worktree โดยไม่ต้องรักษาพิเศษ
โชคไม่ดีอ้างอิง / เขียนใหม่มาก่อน refs / worktree ดังนั้นนี่คือทั้งหมดที่เราสามารถทำได้


ด้วย Git 2.24 (Q4 2019) "git rebase --rebase-merges " เรียนรู้ที่จะผลักดันกลยุทธ์การผสานที่แตกต่างกันและผ่านตัวเลือกเฉพาะของกลยุทธ์ให้กับพวกเขา

ดูกระทำ 476998d (4 กันยายน 2019) โดยเอลียาห์ Newren (newren )
ดูกระทำ e1fac53 , กระทำ a63f990 , กระทำ 5dcdd74 , กระทำ e145d99 , กระทำ 4e6023b , กระทำ f67336d , กระทำ a9c7107 , กระทำ b8c6f24 , กระทำ d51b771 , กระทำ c248d32 , กระทำ 8c1e240 , กระทำ 5efed0e , กระทำ 68b54f6 , กระทำ 2e7bbac , กระทำ 6180b20 , กระทำ d5b581f (31 ก.ค. 2562) โดยโยฮันเน Schindelin (dscho )
(ผสานโดยJunio ​​C Hamano - gitster- in 917a319 , 18 Sep 2019)


ด้วย Git 2.25 (ไตรมาสที่ 1 ปี 2020) ตรรกะที่ใช้ในการบอก worktree local และ repository ทั่วโลกของ repository ได้รับการแก้ไขเพื่อช่วยในการผสาน

ดูกระทำ f45f88b , กระทำ c72fc40 , กระทำ 8a64881 , กระทำ 7cb8c92 , กระทำ e536b1f (21 ตุลาคม 2019) โดยSzeder Gábor (szeder )
(ผสานโดยJunio ​​C Hamano - gitster- in db806d7 , 10 Nov 2019)

path.c: อย่าเรียกใช้matchฟังก์ชันโดยไม่มีค่าtrie_find()

ลงชื่อออกโดย: SZEDER Gábor

'logs / refs' ไม่ใช่เส้นทางเฉพาะของต้นไม้ที่ทำงานได้ แต่เนื่องจากกระทำ b9317d55a3 (ตรวจสอบให้แน่ใจว่า refs / rewritten / เป็น per-worktree, 2019-03-07, v2.22.0-rc0) ' git rev-parse --git-path' ได้คืนพา ธ ปลอม ถ้า '' ต่อท้าย/เป็นปัจจุบัน:

$ git -C WT/ rev-parse --git-path logs/refs --git-path logs/refs/
/home/szeder/src/git/.git/logs/refs
/home/szeder/src/git/.git/worktrees/WT/logs/refs/

เราใช้trieโครงสร้างข้อมูลเพื่อตัดสินใจได้อย่างมีประสิทธิภาพว่าเส้นทางเป็นของ dir ทั่วไปหรือทำงานเฉพาะต้นไม้

ตามที่มันเกิดขึ้นb9317d55a3เรียกจุดบกพร่องที่เก่าแก่เหมือนกับการtrieติดตั้งเพิ่มใน4e09cf2acf (" path: เพิ่มประสิทธิภาพการตรวจสอบ dir ทั่วไป", 2015-08-31, Git v2.7.0-rc0 - ผสานอยู่ในชุดที่ 2 )

  • ตามความคิดเห็นที่อธิบายtrie_find()มันควรจะเรียกฟังก์ชั่นการจับคู่ที่กำหนด 'fn' สำหรับคำนำหน้า
    สิ่งนี้ไม่เป็นความจริง: มีสามสถานที่ซึ่ง trie_find () เรียกฟังก์ชันจับคู่ แต่หนึ่งในนั้นขาดการตรวจสอบการมีอยู่ของค่า

  • b9317d55a3เพิ่มสองปุ่มใหม่ในtrie:

    • ' logs/refs/rewritten' และ
    • ' logs/refs/worktree' ถัดจากที่มีอยู่แล้ว ' logs/refs/bisect'
      สิ่งนี้ส่งผลให้trieโหนดที่มีพา ธ ' logs/refs/' ซึ่งไม่เคยมีมาก่อนและไม่มีค่าที่แนบมา
      แบบสอบถามสำหรับ ' logs/refs/' พบโหนดนี้แล้วพบว่าไซต์หนึ่งไซต์ของmatchฟังก์ชันที่ไม่ตรวจสอบการมีอยู่ของค่าและจะเรียกใช้matchฟังก์ชันที่มีNULLค่า
  • เมื่อ matchฟังก์ชั่นcheck_common()ถูกเรียกใช้โดยมีNULLค่ามันจะคืนค่า 0 ซึ่งบ่งชี้ว่าเส้นทางที่สอบถามไม่ได้อยู่ในไดเรกทอรีทั่วไปทำให้เกิดเส้นทางปลอมในท้ายที่สุด

เพิ่มเงื่อนไขที่หายไปtrie_find()เพื่อที่จะไม่เรียกใช้ฟังก์ชันการจับคู่ด้วยค่าที่ไม่มีอยู่

check_common() จะไม่ต้องตรวจสอบอีกต่อไปว่าได้รับค่าที่ไม่ใช่ค่า NULL ดังนั้นให้ลบเงื่อนไขนั้น

ฉันเชื่อว่าไม่มีเส้นทางอื่นที่สามารถก่อให้เกิดการส่งออกปลอมที่คล้ายกัน

AFAICT เฉพาะคีย์อื่นที่ทำให้เกิดฟังก์ชันการจับคู่ที่ถูกเรียกด้วยNULLค่าคือ ' co' (เพราะคีย์ ' common' และ ' config')

อย่างไรก็ตามเนื่องจากไม่ได้อยู่ในไดเรกทอรีที่เป็นของไดเรกทอรีทั่วไปจึงต้องใช้เส้นทางเฉพาะของต้นไม้ทำงาน


3
ฉันคิดว่านี่น่าจะเป็นคำตอบที่ดีที่สุด--preserve-mergesไม่ได้ "รักษา" การผสานตามที่คุณต้องการมันไร้เดียงสามาก สิ่งนี้จะช่วยให้คุณรักษาความมุ่งมั่นและผู้ปกครองของพวกเขาผูกพันความสัมพันธ์ในขณะที่ให้ความยืดหยุ่นของการปฏิเสธการโต้ตอบ ฟีเจอร์ใหม่นี้ยอดเยี่ยมมาก & ถ้าไม่ใช่สำหรับคำตอบที่เขียนขึ้นนี้ฉันก็คงไม่รู้จัก!
egucciar

@egucciar ขอบคุณ และไม่ใช่คุณสมบัติเฉพาะของ Git 2.18 ( stackoverflow.com/search?q=user%3A6309+%22git+2.18%22 ) และ Git 2.19 ( stackoverflow.com/search?q=user%3A6309+%22git+2.19% 22 )
VonC

1
มีประโยชน์อย่างยิ่งหากคุณพยายามที่จะย้ายนักล่าที่มีความมุ่งมั่นเช่นนี้ใน Q / A, stackoverflow.com/questions/45059039//
okovko

1
โอ้นั่นคือสิ่งที่ฉันต้องการมานานแล้ว! ฉันมีวิธีแก้ปัญหาด้วยตนเองสำหรับกรณีเช่นเดียวกับที่ควรสร้างความมุ่งมั่นที่สมมติขึ้นเข้าร่วมการรวมทั้งหมด
carnicer

Git ทั่วไป คุณกล้าถามคำถามง่าย ๆ และคุณอาจต้องเรียนรู้ประวัติของ Git อัลกอริธึมภายในรายละเอียดการใช้งานที่ยุ่งเหยิงทั้งหมดและคุณต้องมีวิชาทฤษฎีกราฟเพื่อเข้าใจสิ่งที่เกิดขึ้น
Dimitris
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.