Git: วิธีการลดการกระทำที่เฉพาะเจาะจง


154

ฉันต้องการลดการกระทำที่เฉพาะเจาะจงไม่ใช่หัวหน้าของสาขาอื่น:

A --- B --- C          master
 \
  \-- D                topic

ถึง

A --- B --- C          master
       \
        \-- D          topic

แทน

A --- B --- C          master
             \
              \-- D    topic

ฉันจะบรรลุสิ่งนั้นได้อย่างไร


4
คุณเคยลองทำgit checkout Bมาแล้วgit rebaseหรือยัง?
Peter-Paul van Gemerden

ไม่นั่นควรจะช่วยเหรอ? ฉันเดาเฉพาะการอ้างอิงของrebaseคำสั่งเท่านั้นที่สำคัญ
Ondra Žižka

คำตอบ:


98

คุณสามารถหลีกเลี่ยงการใช้พารามิเตอร์ --onto โดยสร้าง temp branch บนคอมมิทที่คุณชอบจากนั้นใช้ rebase ในรูปแบบที่เรียบง่าย:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp

5
ฉันชอบแนวทางคล้าย RISC มากกว่านี้ :) จะลอง ขอบคุณ
Ondra Žižka

10
ผมสงสัยว่าทำไมมันไม่ทำงานสำหรับฉันในสถานการณ์ที่แตกต่างกันเล็กน้อย ฉันต้องการกลุ่มข้ามpep8และต้องอยู่บนพื้นฐานหลัก git rebase temp(เมื่ออยู่ในกลุ่ม ) ยอมแพ้กับ "กลุ่มสาขาปัจจุบันเป็นปัจจุบัน"
Alois Mahdal

4
วิธีนี้ไม่สามารถใช้งานได้กับสถานการณ์ที่หัวข้อได้ถูกนำไปใช้กับมาสเตอร์แล้ว แต่คุณต้องการรีบาวบนบรรพบุรุษของคุณเป็นหลัก ในกรณีนั้นคุณต้องใช้git rebase --onto <target> <from> <to>เพื่อให้คุณสามารถระบุ <from> การส่งมอบ
mirzmaster

นี่เป็นตัวเลือกที่ง่ายที่สุดถ้าคุณต้องการที่จะรีบาวน์บนคอมมิชชันเดิม
Ash

1
ฉันดูเหมือนว่าจะทำงาน แต่ GitLab พูดอย่างอื่น ถ้าฉันมี 10 คอมมิชชันข้างหลัง 5 คอมมิตล่วงหน้าความคาดหวังของฉันคือการมี 8 คอมมิชชันด้านหลัง 5 คอมมิชชันข้างหน้าหลังจากได้รับ 2 คอมมิชชัน แต่แทนที่จะเพิ่มสิ่งนี้ลงไปในข้อผูกพัน 5 ข้อเหล่านั้น
ROMANIA_engineer

67

คุณสามารถใช้แนวทางโดยตรง:

git checkout topic
git rebase <commitB>

6
สำหรับฉันมันไม่ได้ทำสิ่งที่ตั้งใจ เท่าที่ผมสามารถบอกได้ว่าจะพยายาม rebase ลงบน "บรรพบุรุษร่วมกันล่าสุด" ของและtopic commitB
Dan Lenski

2
@DanLenski ที่ไม่ได้เป็นวิธี rebase works.Quoting เอกสาร , It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. ฉันพยายามอีกครั้งในขณะนี้และดูเหมือนจะทำงานได้ดี
r0hitsharma

1
ง่ายสุด ๆ และใช้งานได้! ตอนนี้ฉันมี: commitB_from_master-> topicCommit1-> topicCommit2
Martin Konicek

มันไม่ได้ผลสำหรับฉัน ก่อนหน้านี้ GitLab กล่าวว่า "n กระทำไปข้างหน้า" และตอนนี้ก็กล่าวว่า "m กระทำไปข้างหน้า" m > nที่
ROMANIA_engineer

48

ใช้ตัวเลือก "สู่":

git rebase --onto master^ D^ D

2
DและD^จะแฮชของการกระทำสุดท้ายและครั้งต่อไปของ "หัวข้อ" หรือไม่
Ondra Žižka

39
git rebase --onto <new-parent> <old-parent>ไวยากรณ์เป็นเหมือน ดูการตั้งค่าตัวชี้ผู้ปกครองคอมไพล์ไปยังผู้ปกครองที่แตกต่างกัน ในกรณีของคุณ <new-parent> คือ B และ <old-parent> คือ A.
jsz

7
ฉันมักจะใช้อาร์กิวเมนต์ 3 ข้อ: desitnation, start และ end of commits เพื่อ rebase
Adam Dymitruk

14
สิ่งนี้ใช้ได้กับฉัน:git rebase --onto <commit-ID> master

4
@ jsz ของความคิดเห็นที่ถูกต้องขัดกับความคิดเห็นของไซมอนใต้ของมันวิธีอื่น ๆ : git rebase --onto master <commit-ID-of-old-parent>สำหรับ git rebase --onto B AOP
gaborous

19

ความคิดเห็นโดย jsz ด้านบนช่วยฉันเจ็บปวดมากมายดังนั้นนี่คือผู้รับทีละขั้นตอนตามที่ฉันใช้เพื่อลด / ย้ายการกระทำใด ๆ

  1. ค้นหาจุดการแตกแขนงก่อนหน้าของกิ่งที่จะทำการ rebased (ย้าย) - เรียกมันว่า parent ในตัวอย่างข้างต้นนั่นคือA
  2. ค้นหาคอมมิชชันที่คุณต้องการย้ายสาขาไป - เรียกว่าพาเรนต์ใหม่ ใน exampe นั่นคือB
  3. คุณต้องอยู่ในสาขาของคุณ (ที่คุณย้าย):
  4. ใช้การรีบูทของคุณ: git rebase --onto <new parent> <old parent>

ในตัวอย่างข้างต้นนั้นง่ายเหมือน:

   git checkout topic
   git rebase --onto B A

6
นี่ควรเป็นคำตอบที่ถูกต้อง ยกเว้นว่าฉันใช้git rebase --onto B masterดูคำตอบของฉันสำหรับคำอธิบายที่ละเอียดยิ่งขึ้น
Zack Morris

มันไม่ได้ผลสำหรับฉัน ฉันเลือกคอมมิชชันต่อเนื่อง 2 อัน (อันสุดท้ายจากอาจารย์ที่อยู่ในสาขาปัจจุบันและอันแรกจากนายที่ไม่ได้อยู่ในสาขาปัจจุบัน) ฉันเริ่มต้นด้วย 100 หลัง - 10 ข้างหน้าและแทนที่จะมี 99 หลัง - 10 ข้างหน้าตอนนี้ฉันมี 105 หลัง - 13 หน้า
ROMANIA_engineer

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

11

โซลูชั่นหัวข้อ

คำสั่งที่ถูกต้องที่จะตอบคำถามที่โพสต์อาจจะเป็นที่ใด ๆ ของ (สาขาสมมติต่อไปนี้topicมีการตรวจสอบแล้วออก):

git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B

หากtopicไม่ได้เช็คเอาต์คุณเพียง แต่ต่อท้ายtopicคำสั่ง (ยกเว้นอันสุดท้าย) ดังนี้:

git rebase --onto B master topic

หรือตรวจสอบสาขาก่อนด้วย:

git checkout topic

Reboot String of Commits ใด ๆ เป็น Commit

รูปแบบพื้นฐานของคำสั่งที่เราต้องการคัดลอกมาจากเอกสารประกอบคือ:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch>เป็นทางเลือกและสิ่งทั้งหมดที่ทำคือตรวจสอบสาขาที่ระบุก่อนดำเนินการคำสั่งที่เหลือ หากคุณได้ตรวจสอบสาขาที่คุณต้องการรีบูตแล้วคุณไม่ต้องการสิ่งนี้ โปรดทราบว่าคุณจะต้องมีการระบุไว้<Upstream>ในการที่จะระบุหรือคอมไพล์จะคิดว่าคุณกำลังระบุ<Branch><Upstream>

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

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

ต้องการย้ายสาขาคุณลักษณะทั้งหมดคุณไม่สามารถเลือกX, Y, Zหรือfeatureเป็น<Target>ตั้งแต่เหล่านั้นทั้งหมดจะกระทำภายในกลุ่มที่ถูกย้าย

<Upstream>มีความพิเศษเพราะมันหมายถึงสองสิ่งที่แตกต่างกัน หากเป็นการกระทำที่เป็นบรรพบุรุษของสาขาที่เช็คเอาท์แล้วมันจะทำหน้าที่เป็นจุดตัด ในตัวอย่างที่ผมให้นี้จะเป็นอะไรที่ไม่C, หรือD masterทั้งหมดกระทำหลังจาก<Upstream>จนกระทั่งหัวหน้าสาขาที่เช็คเอาต์เป็นคนที่จะถูกย้าย

อย่างไรก็ตามหาก<Upstream>ไม่ใช่บรรพบุรุษแล้วคอมไพล์สำรองจากการคอมมิทที่ระบุไว้จนกว่าจะพบบรรพบุรุษที่มีสาขาที่เช็คเอาต์ (และยกเลิกหากไม่พบ) ในกรณีของเราเป็น<Upstream>ของB, C, Dหรือmasterจะส่งผลในการกระทำBที่ทำหน้าที่เป็นจุดตัด เป็นตัวเองคำสั่งตัวเลือกและถ้ามันไม่ได้ระบุลักษณะแล้วคอมไพล์ที่ผู้ปกครองของการตรวจสอบออกสาขาซึ่งเป็นเทียบเท่าเข้า<Upstream>master

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

ตัวอย่างและผลลัพธ์ที่น่าสนใจ

ใช้จุดเริ่มต้นนี้:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    จะนำไปใช้กระทำB, C, X, Y, Zที่จะกระทำDและสิ้นสุดการกระโดดข้ามBและCเพราะพวกเขามีอยู่แล้วถูกนำมาใช้

  • git rebase --onto C X feature
    จะใช้การคอมมิทYและZการคอมมิทC, ลบคอมมิทX


4

git rebase <SHA1 of B> topicวิธีการแก้ปัญหาที่ง่ายคือ สิ่งนี้ใช้ได้ผลไม่ว่าคุณHEADจะอยู่ที่ไหน

เราสามารถยืนยันพฤติกรรมนี้จากgit rebase doc

<upstream>สาขาต้นน้ำเพื่อเปรียบเทียบ อาจเป็นการกระทำที่ถูกต้องไม่ใช่เฉพาะชื่อสาขาที่มีอยู่ ค่าเริ่มต้นของ upstream ที่กำหนดค่าไว้สำหรับสาขาปัจจุบัน


คุณอาจจะคิดว่าจะเกิดอะไรขึ้นถ้าฉันพูดถึง SHA1 topicในคำสั่งด้านบนด้วย?

git rebase <SHA1 of B> <SHA1 of topic>

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


3

ฉันใช้ส่วนผสมของโซลูชั่นที่อธิบายไว้ข้างต้น:

$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp

ฉันพบว่าการอ่านและทำความเข้าใจง่ายขึ้นมาก โซลูชันที่ได้รับการยอมรับทำให้ฉันมีความขัดแย้งในการรวม (ขี้เกียจเกินกว่าที่จะแก้ไขด้วยมือ):

$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M       pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
    <some code>
.git/rebase-apply/patch:17: trailing whitespace.
        <some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

1
เหมือนกันที่นี่หลายไฟล์มีความขัดแย้งเมื่อฉันใช้คำตอบที่นิยมมากที่สุด 2 (เช่นโดย r0hitsharma และ Dymitruk)
โอลิเวอร์

3

ตั้งแต่ rebasing เป็นพื้นฐานดังนั้นนี่คือการขยายตัวของคำตอบ Nestor Milyaev ของ การรวมความคิดเห็นของ jszและSimon Southจากคำตอบของ Adam Dymitrukทำให้คำสั่งนี้ทำงานบนtopicกิ่งไม้โดยไม่คำนึงว่าแยกจากการmasterกระทำของสาขาAหรือC:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

โปรดทราบว่าอาร์กิวเมนต์สุดท้ายจะต้อง (มิฉะนั้นมัน rewinds สาขาที่ท่านจะกระทำB)

ตัวอย่าง:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

ดังนั้นคำสั่งสุดท้ายคือคำสั่งที่ฉันมักใช้


-2

มีวิธีอื่นในการทำเช่นนี้หรือหากคุณต้องการย้ายกลับไปมากกว่าหนึ่งการกระทำ

นี่คือตัวอย่างการย้ายกลับไปที่nจำนวนการคอมมิท:

git branch topic master~n

เพื่อประโยชน์ของคำถามนี้สิ่งนี้สามารถทำได้:

git branch topic master~1

git version 2.7.4คำสั่งในการทำงานอย่างสมบูรณ์แบบ ยังไม่ได้ทดสอบกับรุ่นอื่น


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