จะเลิกทำ "git cherry-pick" ที่ประสบความสำเร็จได้อย่างไร


104

ใน repo ท้องถิ่นฉันเพิ่งดำเนินการgit cherry-pick SHAโดยไม่มีข้อขัดแย้งหรือปัญหาใด ๆ จากนั้นฉันก็ตระหนักว่าฉันไม่ต้องการทำสิ่งที่ฉันเพิ่งทำ ฉันไม่ได้ผลักดันสิ่งนี้ไปไหน

ฉันจะเอาเชอร์รี่แค่นี้ออกได้อย่างไร?

ฉันต้องการทราบว่ามีวิธีดำเนินการดังนี้

  • เมื่อฉันมีการเปลี่ยนแปลงอื่น ๆ ในท้องถิ่น
  • เมื่อฉันไม่มีการเปลี่ยนแปลงอื่น ๆ ในท้องถิ่น

ควรใช้คำสั่งเดียวสำหรับทั้งสองกรณีหากเป็นไปได้

คำตอบ:


142

การเลือกเชอร์รี่นั้นโดยพื้นฐานแล้วเป็นการกระทำดังนั้นหากคุณต้องการเลิกทำคุณก็แค่เลิกทำการคอมมิต

เมื่อฉันมีการเปลี่ยนแปลงอื่น ๆ ในท้องถิ่น

ซ่อนการเปลี่ยนแปลงปัจจุบันของคุณเพื่อให้คุณสามารถนำไปใช้ใหม่ได้หลังจากรีเซ็ตคอมมิต

$ git stash
$ git reset --hard HEAD^
$ git stash pop  # or `git stash apply`, if you want to keep the changeset in the stash

เมื่อฉันไม่มีการเปลี่ยนแปลงอื่น ๆ ในท้องถิ่น

$ git reset --hard HEAD^

6
นอกเหนือจากคำถามและคำตอบหากคุณไม่แน่ใจว่าคุณกำลังเลือกเชอร์รี่อะไรอยู่คุณสามารถ "SHA เลือกเชอร์รี่ - ไม่ผูกมัด" ได้ตลอดเวลาและจะไม่มีการเปลี่ยนแปลงเพียงอย่างเดียวซึ่งสามารถตรวจสอบได้อย่างง่ายดาย สิ่งนี้มีประโยชน์สำหรับการเก็บเชอร์รี่บางส่วน
kuskmen

2
windows: git reset - ฮาร์ด "HEAD ^"
บาง

25

git reset --hard HEAD~เพื่อยกเลิกการกระทำครั้งสุดท้ายของคุณเพียงแค่ทำ

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


2
นั่นควรจะเป็น HEAD ^ หรือ HEAD ~ 1
Andreas Wederbrand

ทั้งคู่เทียบเท่ากับ HEAD ~ (และ HEAD ^ 1 สำหรับเรื่องนั้น)
David Deutsch

1
@DavidDeutsch นั่นเป็นความจริง (แม้ว่าจะมีเฉพาะใน Git เวอร์ชันล่าสุด) แต่ตัวพิมพ์ใหญ่ ( HEAD) มีประสิทธิภาพมากกว่า: พิจารณากรณีที่โชคร้ายซึ่งheadเป็นชื่อของการอ้างอิงที่มีอยู่
jub0bs

1
@Jubobs - จุดดี; ฉันได้เปลี่ยนปลอกในคำตอบของฉันแล้ว
David Deutsch

1
@qwertzguy จับดี; ดูการประทับเวลาบิตเกี่ยวกับการเปลี่ยนแปลงในท้องถิ่นได้ถูกเพิ่มเข้าไปในคำถามหนึ่งนาทีหลังจากที่ฉันโพสต์คำตอบนี้ :)
David Deutsch

9

ถ้าเป็นไปได้หลีกเลี่ยงการรีเซ็ตยาก ฮาร์ดรีเซ็ตเป็นหนึ่งในปฏิบัติการทำลายล้างน้อยมากในคอมไพล์ โชคดีที่คุณสามารถยกเลิกการเลือกเชอร์รี่ได้โดยไม่ต้องรีเซ็ตและหลีกเลี่ยงสิ่งที่ทำลายล้าง

${bad_cherrypick}หมายเหตุกัญชาของเชอร์รี่รับคุณต้องการที่จะเลิกทำบอกว่ามันเป็น ทำgit revert ${bad_cherrypick}. ตอนนี้เนื้อหาของต้นไม้ทำงานของคุณเป็นเหมือนเดิมก่อนที่คุณจะเลือกเชอร์รี่ที่ไม่ดี

ทำซ้ำของคุณและเมื่อคุณมีความสุขอยู่กับใหม่เชอร์รี่รับทำgit cherry-pick ${wanted_commit} git rebase -i ${bad_cherrypick}~1ในระหว่างการ rebase ให้ลบทั้งสองอย่าง${bad_cherrypick}และเปลี่ยนกลับที่เกี่ยวข้อง

สาขาที่คุณทำงานจะมีเฉพาะเชอร์รี่ที่ดีเท่านั้น ไม่จำเป็นต้องรีเซ็ต!


7

git reflog สามารถช่วยคุณได้

พิมพ์ลงในคอนโซลของคุณและคุณจะได้รับรายการประวัติคอมไพล์ของคุณพร้อมกับ SHA-1 ที่แสดงถึงพวกเขา

เพียงชำระเงิน SHA-1 ที่คุณต้องการเปลี่ยนกลับ


HEADก่อนที่จะตอบให้เพิ่มพื้นหลังบางอธิบายสิ่งที่เป็นแบบนี้

First of all what is HEAD?

HEADเป็นเพียงการอ้างอิงถึงการกระทำปัจจุบัน (ล่าสุด) ในสาขาปัจจุบัน
มีได้เพียงรายการเดียวHEADในช่วงเวลาใดเวลาหนึ่ง (ไม่รวมgit worktree)

เนื้อหาของHEADจะถูกเก็บไว้ภายใน.git/HEADและมี SHA-1 ขนาด 40 ไบต์ของคอมมิตปัจจุบัน


detached HEAD

หากคุณไม่ได้อยู่บนล่าสุดกระทำ - ความหมายที่จะชี้ไปก่อนกระทำในประวัติศาสตร์ที่เรียกว่าHEADdetached HEAD

ป้อนคำอธิบายภาพที่นี่

ในบรรทัดคำสั่งจะมีลักษณะเช่นนี้ - SHA-1 แทนชื่อสาขาเนื่องจากHEADไม่ได้ชี้ไปที่ส่วนปลายของสาขาปัจจุบัน

ป้อนคำอธิบายภาพที่นี่

ป้อนคำอธิบายภาพที่นี่

ตัวเลือกบางอย่างในการกู้คืนจาก HEAD ที่แยกออกมา:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

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

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

คุณสามารถใช้reflogเช่นกัน
git reflogจะแสดงการเปลี่ยนแปลงใด ๆ ที่อัปเดตHEADและตรวจสอบรายการ reflog ที่ต้องการจะตั้งค่าHEADกลับเป็นคอมมิตนี้

ทุกครั้งที่มีการแก้ไข HEAD จะมีรายการใหม่ในไฟล์ reflog

git reflog
git checkout HEAD@{...}

สิ่งนี้จะทำให้คุณกลับไปสู่การกระทำที่คุณต้องการ

ป้อนคำอธิบายภาพที่นี่


git reset --hard <commit_id>

"ย้าย" HEAD ของคุณกลับไปที่คอมมิตที่ต้องการ

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.

git revert <sha-1>

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

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

สคีมานี้แสดงให้เห็นว่าคำสั่งใดทำอะไร
อย่างที่คุณเห็นมีการreset && checkoutปรับเปลี่ยนไฟล์HEAD.

ป้อนคำอธิบายภาพที่นี่


อาจจะเพิ่มgit reset --hard
wyx

4

ต้องเผชิญกับปัญหาเดียวกันนี้ฉันได้ค้นพบว่าคุณมีความมุ่งมั่นและ / หรือผลักดันไปยังรีโมตตั้งแต่การเลือกเชอร์รี่ที่ประสบความสำเร็จและคุณต้องการลบออกคุณสามารถค้นหา SHA ของเชอร์รี่โดยเรียกใช้:

git log --graph --decorate --oneline

จากนั้น (หลังจากใช้:wqเพื่อออกจากบันทึก) คุณสามารถลบเชอร์รี่โดยใช้

git rebase -p --onto YOUR_SHA_HERE^ YOUR_SHA_HERE

โดยที่YOUR_SHA_HEREเท่ากับ SHA 7 ตัวอักษร 40 หรือตัวย่อของเชอร์รี่ที่เลือก

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

git push --force origin YOUR_REPO_NAME

(ฉันปรับวิธีแก้ปัญหานี้จากSeth Robertson : โปรดดู "การลบคอมมิตทั้งหมด")


1

คำสั่งเดียวและไม่ใช้git resetคำสั่งทำลายล้าง:

GIT_SEQUENCE_EDITOR="sed -i 's/pick/d/'" git rebase -i HEAD~ --autostash

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


เพียงแค่ลดการกระทำ คุณคิดว่าgit reset HEAD^ยังไง?
ทิม

@TimCastelijns git resetยกเลิกการกระทำการกระทำโดยไม่ทิ้งการเปลี่ยนแปลงใด ๆ ผมขอแนะนำให้คุณอ่านgit-scm.com/docs/git-reset คำถามจาก OP คือการเลิกทำการเลือกเชอร์รี่ ลองทั้งสองคำสั่งของฉันจะทำให้คุณกลับมาอยู่ในสถานะเดิมก่อนที่จะเลือกเชอร์รี่ git resetจะทำให้ต้นไม้การทำงานของคุณยุ่งเหยิงเมื่อทิ้งการเปลี่ยนแปลงที่เลือกเชอร์รี่และพวกเขาจะแยกไม่ออกจากการเปลี่ยนแปลงในท้องถิ่นที่มีอยู่แล้วของคุณ
qwertzguy

reset --hardใช่ขอโทษฉันหมายถึง ไม่มีการเปลี่ยนแปลงใด ๆ
Tim

1
@TimCastelijns git reset --hardเป็นคำสั่งทำลายล้างที่จะลบการเปลี่ยนแปลงในเครื่องที่ไม่เกี่ยวข้องออกไปด้วยและจะทำเช่นนั้นด้วยวิธีที่ไม่สามารถกู้คืนได้! นั่นไม่ใช่สิ่งที่ OP ขอ
qwertzguy

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