สมมติว่าฉันมีประวัติการกระทำต่อไปนี้ในสาขาในพื้นที่ของฉันเท่านั้น:
A -- B -- C
ฉันจะแทรกคอมมิตใหม่ระหว่างAและได้Bอย่างไร?
สมมติว่าฉันมีประวัติการกระทำต่อไปนี้ในสาขาในพื้นที่ของฉันเท่านั้น:
A -- B -- C
ฉันจะแทรกคอมมิตใหม่ระหว่างAและได้Bอย่างไร?
คำตอบ:
ง่ายกว่าในคำตอบของ OP
git rebase -i <any earlier commit>. ซึ่งจะแสดงรายการคอมมิตในโปรแกรมแก้ไขข้อความที่กำหนดค่าไว้a1b2c3d) ในการแก้ไขของคุณสำหรับสายที่เปลี่ยนไปpickedita1b2c3d) ราวกับว่ามันได้รับเพียงแค่มุ่งมั่นgit commit( ไม่แก้ไขซึ่งแตกต่างจากส่วนใหญ่edit) สิ่งนี้จะสร้างคอมมิตใหม่หลังจากที่คุณเลือกgit rebase --continue. สิ่งนี้จะเล่นซ้ำการกระทำที่ต่อเนื่องกันโดยปล่อยให้คอมมิตใหม่ของคุณแทรกในตำแหน่งที่ถูกต้องระวังว่าสิ่งนี้จะเขียนประวัติศาสตร์ซ้ำและทำลายใครก็ตามที่พยายามดึง
A -- B -- C -- D A -- D -- B -- C
Dอาจเป็นการกระทำที่ใดก็ได้ สมมติว่าเรามีA - B - Cและเรามีภาระกิจบางอย่างDที่ไม่ได้อยู่ในสาขานี้ เรารู้ว่า SHA มัน git rebase -i HEAD~3แต่เราสามารถทำ ตอนนี้ระหว่างAและB pickเส้นที่เราใส่ใหม่ pickบรรทัดที่บอกว่าให้กัญชาของที่ต้องการpick SHA Dไม่จำเป็นต้องเป็นแฮชแบบเต็ม แต่เป็นแฮชที่สั้นลง git rebase -iเพียงแค่เชอร์รี่เลือกสิ่งที่กระทำตามpickบรรทัดในบัฟเฟอร์ ไม่จำเป็นต้องเป็นของดั้งเดิมที่ระบุไว้สำหรับคุณ
breakคีย์เวิร์ดในตัวแก้ไขในบรรทัดของมันเองระหว่างสองคอมมิต (หรือในบรรทัดแรกเพื่อแทรกคอมมิตก่อนคอมมิตที่คุณระบุ)
จะเปิดออกจะค่อนข้างง่ายคำตอบที่พบที่นี่ สมมติว่าคุณอยู่ในสาขาbranchหนึ่ง ทำตามขั้นตอนเหล่านี้:
สร้างสาขาชั่วคราวจากคอมมิตหลังจากที่คุณต้องการแทรกคอมมิตใหม่ (ในกรณีนี้คอมมิตA):
git checkout -b temp A
ทำการเปลี่ยนแปลงและคอมมิตสร้างคอมมิตเรียกว่าN:
git commit -a -m "Message"
(หรือgit addตามด้วยgit commit)
rebase คอมมิตที่คุณต้องการหลังจากคอมมิตใหม่ (ในกรณีนี้คอมมิตBและC) เข้าคอมมิตใหม่:
git rebase temp branch
(เป็นไปได้ว่าคุณต้องใช้-pเพื่อรักษาการผสานหากมี - ต้องขอบคุณความคิดเห็นที่ไม่มีอยู่แล้วโดยciekawy )
ลบสาขาชั่วคราว:
git branch -d temp
หลังจากนี้ประวัติมีลักษณะดังนี้:
A -- N -- B -- C
เป็นไปได้แน่นอนที่ความขัดแย้งบางอย่างจะปรากฏขึ้นในขณะที่กำลังทำการรีไฟแนนซ์
ในกรณีที่สาขาของคุณไม่ใช่เฉพาะในพื้นที่เท่านั้นสิ่งนี้จะแนะนำประวัติการเขียนใหม่ดังนั้นอาจทำให้เกิดปัญหาร้ายแรง
git push --forceเปลี่ยน repo ระยะไกล
git rebase temp branch -Xtheirsแก้ไขตัวเลือกอัตโนมัติความขัดแย้งได้อย่างถูกต้องดังนั้น คำตอบที่เป็นประโยชน์สำหรับการฉีดในสคริปต์!
git rebase temp branchนั้น แต่ก่อนหน้าgit branch -d tempนี้สิ่งที่คุณต้องทำคือแก้ไขและจัดการกับความขัดแย้งและปัญหาที่รวมเข้าด้วยกันgit rebase --continueนั่นคือไม่จำเป็นต้องกระทำอะไรเลย ฯลฯ
วิธีแก้ปัญหาที่ง่ายยิ่งขึ้น:
สร้างคอมมิตใหม่ในตอนท้าย D. ตอนนี้คุณมี:
A -- B -- C -- D
จากนั้นเรียกใช้:
$ git rebase -i hash-of-A
Git จะเปิดโปรแกรมแก้ไขของคุณและจะมีลักษณะดังนี้:
pick 8668d21 B
pick 650f1fc C
pick 74096b9 D
เพียงแค่เลื่อน D ไปด้านบนแบบนี้จากนั้นบันทึกและออก
pick 74096b9 D
pick 8668d21 B
pick 650f1fc C
ตอนนี้คุณจะมี:
A -- D -- B -- C
สมมติว่าประวัติการกระทำคือpreA -- A -- B -- Cหากคุณต้องการแทรกการกระทำระหว่างAและBขั้นตอนจะเป็นดังนี้:
git rebase -i hash-of-preA
Git จะเปิดตัวแก้ไขของคุณ เนื้อหาอาจเป็นเช่นนี้:
pick 8668d21 A
pick 650f1fc B
pick 74096b9 C
เปลี่ยนคนแรกpickเป็นedit:
edit 8668d21 A
pick 650f1fc B
pick 74096b9 C
บันทึกและออก.
แก้ไขรหัสของคุณแล้ว git add . && git commit -m "I"
git rebase --continue
ตอนนี้ประวัติการคอมมิต Git ของคุณคือ preA -- A -- I -- B -- C
หากคุณพบข้อขัดแย้ง Git จะหยุดที่คอมมิตนี้ คุณสามารถใช้git diffเพื่อค้นหาเครื่องหมายความขัดแย้งและแก้ไขได้ หลังจากแก้ไขปัญหาความขัดแย้งทุกอย่างที่คุณจำเป็นต้องใช้git add <filename>จะบอกว่า Git git rebase --continueความขัดแย้งได้รับการแก้ไขแล้ววิ่ง
หากคุณต้องการที่จะยกเลิกการ rebase git rebase --abortการใช้
นี่คือกลยุทธ์ที่หลีกเลี่ยงการ "แก้ไขแฮ็ก" ในระหว่าง rebase ที่เห็นในคำตอบอื่น ๆ ที่ฉันได้อ่าน
โดยใช้git rebase -iคุณได้รับรายการคอมมิตตั้งแต่คอมมิตนั้น เพียงแค่เพิ่ม "ตัวแบ่ง" ที่ด้านบนของไฟล์ซึ่งจะทำให้ rebase แตกที่จุดนั้น
break
pick <B's hash> <B's commit message>
pick <C's hash> <C's commit message>
เมื่อเปิดตัวgit rebaseแล้วตอนนี้จะหยุดที่จุด "พัก" ตอนนี้คุณสามารถแก้ไขไฟล์และสร้างคอมมิตได้ตามปกติ จากนั้นคุณสามารถดำเนินการต่อ rebase git rebase --continueด้วย สิ่งนี้อาจทำให้เกิดความขัดแย้งที่คุณต้องแก้ไข git rebase --abortหากคุณได้รับหายไปอย่าลืมคุณสามารถยกเลิกการใช้
กลยุทธ์นี้สามารถสรุปได้ทั่วไปเพื่อแทรกคอมมิตที่ใดก็ได้เพียงแค่ใส่ "แบ่ง" ตรงจุดที่คุณต้องการแทรกคอมมิต
git push -fหลังจากที่เขียนประวัติศาสตร์ไม่ลืมที่จะ คำเตือนตามปกติเกี่ยวกับบุคคลอื่นที่ดึงสาขาของคุณมาใช้
rebaseที่นี่ ไม่มีความแตกต่างมากนักไม่ว่าคุณจะสร้างคอมมิตระหว่างการรีเบสหรือทำล่วงหน้า
คำตอบที่ดีมากมายที่นี่แล้ว ฉันแค่อยากจะเพิ่มโซลูชัน "no rebase" ใน 4 ขั้นตอนง่ายๆ
สรุป
git checkout A
git commit -am "Message for commit D"
git cherry-pick A..C
git branch -f master HEAD
คำอธิบาย
(หมายเหตุ: ข้อดีอย่างหนึ่งของโซลูชันนี้คือคุณจะไม่แตะต้องสาขาของคุณจนกว่าจะถึงขั้นตอนสุดท้ายเมื่อคุณแน่ใจ 100% ว่าคุณพอใจกับผลลัพธ์สุดท้ายคุณจึงมีขั้นตอน "การยืนยันล่วงหน้า" ที่สะดวกมากอนุญาตให้ทำการทดสอบ AB )
สถานะเริ่มต้น (ฉันสันนิษฐานว่าmasterเป็นชื่อสาขาของคุณ)
A -- B -- C <<< master <<< HEAD
1) เริ่มต้นด้วยการชี้ HEAD ไปที่ตำแหน่งที่ถูกต้อง
git checkout A
B -- C <<< master
/
A <<< detached HEAD
(ทางเลือกที่นี่แทนที่จะแยก HEAD เราสามารถสร้างสาขาชั่วคราวด้วยgit checkout -b temp Aซึ่งเราจะต้องลบเมื่อสิ้นสุดกระบวนการตัวแปรทั้งสองทำงานได้ตามที่คุณต้องการเนื่องจากทุกอย่างยังคงเหมือนเดิม)
2) สร้างคอมมิต D ใหม่ที่จะแทรก
# at this point, make the changes you wanted to insert between A and B, then
git commit -am "Message for commit D"
B -- C <<< master
/
A -- D <<< detached HEAD (or <<< temp <<< HEAD)
3) จากนั้นนำสำเนาของการกระทำ B และ C ที่หายไปล่าสุด (จะเป็นบรรทัดเดียวกันหากมีการกระทำมากกว่านี้)
git cherry-pick A..C
# (if any, resolve any potential conflicts between D and these last commits)
B -- C <<< master
/
A -- D -- B' -- C' <<< detached HEAD (or <<< temp <<< HEAD)
(การทดสอบ AB แบบสบาย ๆ ที่นี่หากจำเป็น)
ตอนนี้เป็นช่วงเวลาในการตรวจสอบโค้ดของคุณทดสอบทุกสิ่งที่ต้องทดสอบและคุณยังสามารถแตกต่าง / เปรียบเทียบ / ตรวจสอบสิ่งที่คุณมีและสิ่งที่คุณจะได้รับหลังจากการดำเนินการ
4)ขึ้นอยู่กับการทดสอบของคุณระหว่างCและC'ว่าตกลงหรือเป็น KO
(อย่างใดอย่างหนึ่ง) 4-OK) สุดท้ายย้ายการอ้างอิงของmaster
git branch -f master HEAD
B -- C <<< (B and C are candidates for garbage collection)
/
A -- D -- B' -- C' <<< master
(หรือ) 4-KO) ปล่อยให้masterไม่เปลี่ยนแปลง
หากคุณสร้างสาขาชั่วคราวให้ลบออกด้วยgit branch -d <name>แต่ถ้าคุณไปตามเส้นทาง HEAD ที่แยกออกมาคุณไม่จำเป็นต้องดำเนินการใด ๆ ในตอนนี้การคอมมิตใหม่จะมีสิทธิ์ได้รับการรวบรวมขยะหลังจากที่คุณHEADเชื่อมต่ออีกครั้งด้วยgit checkout master
ในทั้งสองกรณีนั้น (ตกลงหรือ KO) ณ จุดนี้ให้ชำระเงินmasterอีกครั้งเพื่อติดตั้งHEADใหม่