คุณจะลบการแก้ไขเฉพาะในประวัติคอมไพล์ได้อย่างไร?


213

สมมติว่าประวัติคอมไพล์ของคุณเป็นดังนี้:

1 2 3 4 5

1–5 เป็นการแก้ไขแยกต่างหาก คุณต้องลบ 3 ในขณะที่ยังรักษา 1, 2, 4 และ 5 สิ่งนี้จะทำอย่างไร

มีวิธีที่มีประสิทธิภาพหรือไม่เมื่อมีการแก้ไขหลายร้อยรายการหลังจากที่ลบไปแล้ว?


คำถามนี้ไม่ชัดเจน ไม่ได้บอกอย่างชัดเจนว่าผู้เขียนต้องการ 1-2- (3 + 4) -5 หรือ 1-2-4-5
RandyTek

14
8 ปีต่อมาฉันไม่สามารถบอกคุณได้ว่าปัญหาใดที่ฉันพยายามแก้ไข แต่ในคอมไพล์มีหลายวิธีที่จะทำบางสิ่งบางอย่างเสมอและมีคำตอบมากมายที่ผู้คนหลากหลายชอบดังนั้นฉันเดาว่าความกำกวมไม่ได้ทำให้คนจำนวนมากมีปัญหามากมาย
1800 ข้อมูล

1
git rebase --onto 2 3 HEADสวยมากหมายถึงการลดระดับเป็น 2 โดยมีคอมมิตระหว่าง 3 และ HEAD (HEAD เป็นทางเลือกหรือ 5 ในกรณีนี้)
neaumusic

คำตอบ:


76

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

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

จากเอกสาร git :

เริ่มต้นด้วยการคอมมิชชันที่เก่าที่สุดที่คุณต้องการให้เป็น:

git rebase -i <after-this-commit>

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

เลือก deadbee ออนไลน์ของการกระทำนี้
pick fa1afe1 oneline ของการส่งครั้งต่อไป
...

คำอธิบายออนไลน์มีความบริสุทธิ์ใจเพื่อคุณ git-rebase จะไม่ดูพวกมัน แต่อยู่ที่ชื่อ commit ("deadbee" และ "fa1afe1" ในตัวอย่างนี้) ดังนั้นอย่าลบหรือแก้ไขชื่อ

ด้วยการแทนที่คำสั่ง "เลือก" ด้วยคำสั่ง "แก้ไข" คุณสามารถบอกให้ git-rebase หยุดหลังจากใช้การคอมมิทนั้นเพื่อให้คุณสามารถแก้ไขไฟล์และ / หรือข้อความคอมมิท, แก้ไขการคอมมิชชันและทำการรีบูตต่อ

หากคุณต้องการพับการคอมมิทสองรายการขึ้นไปให้แทนที่คำสั่ง "pick" ด้วย "squash" สำหรับการคอมมิทที่สองและการคอมมิชชันต่อมา หากคอมมิชชันมีผู้แต่งคนอื่นมันจะแสดงถึงการคอมมิตสควอชให้กับผู้สร้างคอมมิชชันแรก


42
-1 คำถามที่กำหนดไว้ดี แต่คำตอบนี้ไม่ชัดเจน ผู้เขียนไม่ได้บอกว่าทางออกที่แท้จริงคืออะไร
Aleksandr Levchuk

1
นี่เป็นการนำที่ผิด ส่วนคำสั่งการแยกนั้นไม่ใช่ส่วนที่ถูกต้อง คุณต้องการอ่านมากขึ้นในคู่มือ - ดูคำตอบของ @Rares Vernica
Aleksandr Levchuk

1
@AleksandrLevchuk คำถามไม่ได้ถูกกำหนดไว้อย่างชัดเจน: มันไม่ชัดเจนจากวิธีการที่คำถามถูกระบุว่าเซ็ตการแก้ไขใน 3 นั้นจะถูกเก็บไว้หรือทิ้งไป ฉันจะยอมรับว่าหากการเปลี่ยนแปลงจะถูกยกเลิกคำตอบอื่น ๆ เสนอวิธีการที่ง่ายขึ้น อย่างไรก็ตามหากมีการเปลี่ยนแปลงจะต้องดำเนินการต่อไปนี่เป็นการดำเนินการด้านเครื่องสำอางอย่างแท้จริง ทั้งสองวิธีจะเขียนประวัติศาสตร์ใหม่ในลักษณะที่เป็นอันตรายหากคนอื่น ๆ อาจมีพื้นฐานการทำงานอยู่ด้านบนของประวัติศาสตร์ที่มีข้อบกพร่อง หากเป็นเช่นนั้นการล้างเครื่องสำอางไม่ควรทำและการลบการเปลี่ยนแปลงจะทำได้ดีกว่าผ่านการย้อนกลับ git
Theodore Murdock

125

ตามความคิดเห็นนี้ (และฉันตรวจสอบว่าสิ่งนี้เป็นจริง) คำตอบของ rado นั้นอยู่ใกล้มาก แต่ทำให้คอมไพล์อยู่ในสถานะที่แยกออกมา ให้ลบHEADและใช้สิ่งนี้เพื่อลบออก<commit-id>จากสาขาที่คุณอยู่:

git rebase --onto <commit-id>^ <commit-id>

1
หากคุณสามารถบอกฉันได้ว่าจะลบ commit-id นั้นออกจากประวัติโดยอิงจากเพิ่งทำการคอมมอน - id คุณจะเป็นฮีโร่ของฉัน
kayleeFrye_onDeck

คุณช่วยรวมคำอธิบายเกี่ยวกับสิ่งที่คำสั่งเวทย์มนตร์ทำไว้ในคำตอบได้ไหม? คือสิ่งที่แต่ละพารามิเตอร์หมายถึงอะไร
alexandroid

มันยอดเยี่ยม แต่ถ้าฉันต้องการลบการคอมมิทครั้งแรกในประวัติศาสตร์? (ซึ่งเป็นสาเหตุที่ฉันมาที่นี่: P)
สเตอร์ลิงแคมเดน

2
ด้วยเหตุผลบางอย่างไม่มีอะไรเกิดขึ้นเมื่อฉันเรียกใช้ อย่างไรก็ตามการเปลี่ยน^ไป~1ใช้งานได้
พลวง

ช้าไปหน่อย แต่ฉันจะเพิ่มว่าถ้าคุณเจอข้อขัดแย้งผสานยกเลิกการรีบูตแล้วรันใหม่อีกครั้ง--strategy-option theirsในตอนท้าย
LastStar007

122

นี่คือวิธีลบเฉพาะแบบไม่โต้ตอบ<commit-id>ซึ่งรู้เพียงว่า<commit-id>คุณต้องการลบเท่านั้น:

git rebase --onto <commit-id>^ <commit-id> HEAD

2
ทำงานให้ฉันด้วย ด้วยตัวดำเนินการ ^ ดำเนินการอย่างไร มันหมายถึงการส่งมอบครั้งต่อไปหลังจากการส่งมอบที่ระบุหรือไม่?
hopia

3
@hopia หมายถึงพาเรนต์ (แรก) ของการส่งข้อมูลที่ระบุ ดู "การแก้ไขความช่วยเหลือ git"
Emil Styrke

12
ดูคำแนะนำของ @ kareem ในคำตอบของเขาที่จะละเว้นHEADเพื่อหลีกเลี่ยงหัวเดี่ยว
mklement0

1
ง่ายกว่าการคิดจำนวนการตอบกลับที่จะค้นหา
Dana Woodman

1
มันยอดเยี่ยม แต่ถ้าฉันต้องการลบการคอมมิทครั้งแรกในประวัติศาสตร์? (ซึ่งเป็นสาเหตุที่ฉันมาที่นี่: P)
สเตอร์ลิงแคมเดน

76

ตามที่ระบุไว้ก่อนgit-rebase (1)คือเพื่อนของคุณ ถ้าคุณมีความมุ่งมั่นในmasterสาขาของคุณคุณจะทำ:

git rebase --onto master~3 master~2 master

ก่อน:

1---2---3---4---5  master

หลังจาก:

1---2---4'---5' master

จาก git-rebase (1):

ช่วงของการกระทำสามารถลบออกได้ด้วยการรีบูต หากเรามีสถานการณ์ต่อไปนี้:

E---F---G---H---I---J  topicA

จากนั้นคำสั่ง

git rebase --onto topicA~5 topicA~3 topicA

จะส่งผลให้มีการลบการกระทำ F และ G:

E---H'---I'---J'  topicA

สิ่งนี้มีประโยชน์หาก F และ G มีข้อบกพร่องในบางวิธีหรือไม่ควรเป็นส่วนหนึ่งของหัวข้อ A โปรดทราบว่าอาร์กิวเมนต์สำหรับ --onto และพารามิเตอร์สามารถเป็น commit-ish ใด ๆ ที่ถูกต้อง


3
ไม่ควรเป็นเช่นนี้--onto master~3 master~1?
แมทเธียส

3
หากคุณเพียงแค่ต้องการลบการคอมมิชชันล่าสุดนั่นคือ --onto master ~ 1 master
MikeHoss

ฉันต้องการพกโซลนี้ แต่ฉันได้รับMERGE CONFLICTข้อผิดพลาด ฉันใช้สถานการณ์จำลองที่กล่าวถึงในstackoverflow.com/questions/2938301/remove-specific-commitและฉันไม่สามารถลบการคอมมิชชันที่ 2 ในตัวอย่างนั้นได้
maan81

22

หากสิ่งที่คุณต้องการทำคือลบการเปลี่ยนแปลงที่ทำในการแก้ไข 3 คุณอาจต้องการใช้การคืนค่า git

Git ย้อนกลับเพียงสร้างการแก้ไขใหม่ที่มีการเปลี่ยนแปลงที่ยกเลิกการเปลี่ยนแปลงทั้งหมดในการแก้ไขที่คุณกำลังคืนค่า

สิ่งนี้หมายความว่าคือคุณเก็บข้อมูลเกี่ยวกับทั้งการกระทำที่ไม่พึงประสงค์และการกระทำที่ลบการเปลี่ยนแปลงเหล่านั้น

นี่อาจเป็นมิตรมากกว่านี้ถ้าเป็นไปได้ที่บางคนดึงจากที่เก็บของคุณในเวลาเฉลี่ยเนื่องจากการเปลี่ยนกลับเป็นเพียงการกระทำมาตรฐาน


4
น่าเสียดายที่ไม่ใช่วิธีแก้ปัญหาที่ดีสำหรับฉันเพราะมีคนตั้งใจทำอึ 100MB ให้กับ repo โดยทำให้มีขนาดใหญ่ขึ้นและทำให้เว็บอินเตอร์เฟสช้าลง
สตีเฟ่นสมิ ธ

18

คำตอบทั้งหมดจนถึงขณะนี้ไม่ได้ตอบข้อกังวลต่อท้าย:

มีวิธีที่มีประสิทธิภาพหรือไม่เมื่อมีการแก้ไขหลายร้อยรายการหลังจากที่ลบไปแล้ว?

ขั้นตอนต่อไปนี้ แต่สำหรับการอ้างอิงสมมติว่ามีประวัติต่อไปนี้:

[master] -> [hundreds-of-commits-including-merges] -> [C] -> [R] -> [B]

C : คอมมิชชันเพียงทำตามการคอมมิตที่จะลบ (ล้าง)

R : ความมุ่งมั่นที่จะถูกลบออก

B : กระทำก่อนหน้าที่จะส่งออก (ฐาน)

เนื่องจากข้อ จำกัด "การแก้ไขหลายร้อย" ฉันถือว่าสมมติฐานล่วงหน้าดังต่อไปนี้:

  1. มีบางอย่างที่น่าละอายที่คุณไม่ต้องการมีอยู่จริง
  2. มีค่าคอมมิชชันที่ตามมาเป็นศูนย์ที่จริง ๆ แล้วขึ้นอยู่กับการกระทำที่น่าอาย (ไม่มีข้อขัดแย้งในการย้อนกลับ)
  3. คุณไม่สนใจว่าคุณจะถูกระบุว่าเป็น 'ผู้ส่ง' ของการกระทำหลายร้อยรายการ ('ผู้เขียน' จะได้รับการเก็บรักษาไว้)
  4. คุณไม่เคยแชร์ที่เก็บ
    • หรือคุณมีอิทธิพลต่อคนทุกคนที่เคยโคลนประวัติด้วยความมุ่งมั่นในนั้นเพื่อโน้มน้าวให้พวกเขาใช้ประวัติใหม่ของคุณ
    • และคุณไม่สนใจเกี่ยวกับการเขียนประวัติใหม่

นี่เป็นข้อ จำกัด ที่ค่อนข้าง จำกัด แต่มีคำตอบที่น่าสนใจที่ใช้งานได้จริงในกรณีมุมนี้

นี่คือขั้นตอน:

  1. git branch base B
  2. git branch remove-me R
  3. git branch save
  4. git rebase --preserve-merges --onto base remove-me

หากไม่มีความขัดแย้งจริง ๆ สิ่งนี้ควรดำเนินการต่อโดยไม่มีการขัดจังหวะเพิ่มเติม หากมีข้อขัดแย้งคุณสามารถแก้ไขได้และrebase --continueหรือตัดสินใจที่จะอยู่กับความลำบากใจและrebase --abortหรือตัดสินใจที่จะมีชีวิตอยู่เพียงกับความลำบากใจและ

ตอนนี้คุณควรอยู่ในmasterสิ่งที่ไม่ได้ทำR แล้วในนั้นอีกต่อไป saveจุดสาขาที่คุณได้ก่อนในกรณีที่คุณต้องการที่จะคืนดี

วิธีที่คุณต้องการจัดการการถ่ายโอนของคนอื่นไปยังประวัติใหม่ของคุณนั้นขึ้นอยู่กับคุณ คุณจะต้องมีการทำความคุ้นเคยกับstash, และreset --hard cherry-pickและคุณสามารถลบbase, remove-meและsaveสาขา


ฉันจะชอบ 150000 ครั้งนี้ได้อย่างไร
Gena Moroz

ทำงานให้ฉันเพื่อลบ 3 คอมมิทตามลำดับจากกลางประวัติศาสตร์ของสาขาที่มีคนทำไฟล์ 150MB
Marcos

3

ฉันยังตกอยู่ในสถานการณ์ที่คล้ายกัน ใช้การรีบูตแบบโต้ตอบโดยใช้คำสั่งด้านล่างและในขณะที่เลือกให้วางคอมมิทที่ 3

git rebase -i remote/branch

2

ดังนั้นนี่คือสถานการณ์ที่ฉันเผชิญและวิธีที่ฉันจะแก้ไข

[branch-a]

[Hundreds of commits] -> [R] -> [I]

นี่Rคือความมุ่งมั่นที่ฉันต้องการจะลบออกและIเป็นความมุ่งมั่นเดียวที่เกิดขึ้นหลังจากนั้นR

ฉันทำการแปลงกลับและบีบเข้าด้วยกัน

git revert [commit id of R]
git rebase -i HEAD~3

ในระหว่างการ rebase สควอชแบบโต้ตอบ 2 คอมมิตล่าสุด


0

คำตอบของ rado และ kareem ไม่ทำอะไรเลยสำหรับฉัน (เฉพาะข้อความ "สาขาปัจจุบันเป็นรุ่นล่าสุด" ปรากฏขึ้น) อาจเกิดขึ้นได้เนื่องจากสัญลักษณ์ '^' ไม่ทำงานในคอนโซล Windows อย่างไรก็ตามตามความคิดเห็นนี้การแทนที่ '^' โดย '~ 1' จะช่วยแก้ปัญหาได้

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