วิธีการแยกความมุ่งมั่นล่าสุดเป็นสองอย่างใน Git


277

ฉันมีสองสาขาทำงานต้นแบบและฟอรั่มและฉันได้ทำเพียงการปรับเปลี่ยนบางอย่างในฟอรั่มสาขาที่ผมอยากจะเชอร์รี่เลือกเข้าต้นแบบ แต่น่าเสียดายที่ความมุ่งมั่นที่ฉันต้องการที่จะเลือกเชอร์รี่ยังมีการปรับเปลี่ยนบางอย่างที่ฉันไม่ต้องการ

วิธีแก้ปัญหาอาจเป็นการลบการกระทำที่ผิดและแทนที่ด้วยการคอมมิทที่แยกกันสองอันอันที่มีการเปลี่ยนแปลงที่ฉันต้องการเลือกในมาสเตอร์และอื่น ๆ ที่ไม่ได้อยู่ที่นั่น

ฉันพยายามทำ

git reset --hard HEAD^

ซึ่งลบการเปลี่ยนแปลงทั้งหมดดังนั้นฉันต้องกลับไปด้วย

git reset ORIG_HEAD

ดังนั้นคำถามของฉันคือวิธีที่ดีที่สุดในการแยกความมุ่งมั่นล่าสุดออกเป็นสองคอมมิชชันคืออะไร?

คำตอบ:


332

คุณควรใช้ดัชนี หลังจากทำการรีเซ็ตแบบผสม (" git reset HEAD ^") ให้เพิ่มการเปลี่ยนแปลงชุดแรกลงในดัชนีจากนั้นจึงคอมมิท จากนั้นส่งมอบส่วนที่เหลือ

คุณสามารถใช้ " git add " เพื่อวางการเปลี่ยนแปลงทั้งหมดในไฟล์ไปยังดัชนี หากคุณไม่ต้องการให้ทุกการดัดแปลงที่ทำในไฟล์มีเพียงการแก้ไขบางอย่างคุณสามารถใช้ "git add -p"

ลองดูตัวอย่าง สมมุติว่าฉันมีไฟล์ชื่อ myfile ซึ่งมีข้อความต่อไปนี้:

something
something else
something again

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

1
something
something else
something again
2

ตอนนี้ฉันตัดสินใจแล้วว่าฉันต้องการแยกมันออกเป็นสองส่วนและฉันต้องการให้การแทรกของบรรทัดแรกอยู่ในการคอมมิชชันแรกและการแทรกของบรรทัดสุดท้ายให้อยู่ในการคอมมิชชันที่สอง

ก่อนอื่นฉันกลับไปที่ผู้ปกครองของ HEAD แต่ฉันต้องการเก็บการแก้ไขในระบบไฟล์ดังนั้นฉันจึงใช้ "git reset" โดยไม่มีการโต้แย้ง (ซึ่งจะทำการรีเซ็ตแบบ "ผสม"):

$ git reset HEAD^
myfile: locally modified
$ cat myfile
1
something
something else
something again
2

ตอนนี้ฉันใช้ "git add -p" เพื่อเพิ่มการเปลี่ยนแปลงที่ฉันต้องการส่งไปยังดัชนี (= ฉันจัดลำดับพวกเขา) "git add -p" เป็นเครื่องมือโต้ตอบที่ถามคุณเกี่ยวกับการเปลี่ยนแปลงของไฟล์ที่ควรเพิ่มในดัชนี

$ git add -p myfile
diff --git a/myfile b/myfile
index 93db4cb..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,5 @@
+1
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,s,e,?]? s    # split this section into two!
Split into 2 hunks.
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? y  # yes, I want to stage this
@@ -1,3 +2,4 @@
 something
 something else
 something again
+2
Stage this hunk [y,n,a,d,/,K,g,e,?]? n   # no, I don't want to stage this

จากนั้นฉันยอมรับการเปลี่ยนแปลงครั้งแรก:

$ git commit -m "Added first line"
[master cef3d4e] Added first line
 1 files changed, 1 insertions(+), 0 deletions(-)

ตอนนี้ฉันสามารถคอมมิชชันการเปลี่ยนแปลงอื่น ๆ ทั้งหมด (เช่นตัวเลข "2" ใส่ในบรรทัดสุดท้าย):

$ git commit -am "Added last line"
[master 5e284e6] Added last line
 1 files changed, 1 insertions(+), 0 deletions(-)

ลองตรวจสอบบันทึกเพื่อดูว่าเรามีข้อผูกพันอะไรบ้าง:

$ git log -p -n2 | cat
Commit 5e284e652f5e05a47ad8883d9f59ed9817be59d8
Author: ...
Date: ...

    Added last line

Diff --git a/myfile b/myfile
Index f9e1a67..2f113ce 100644
--- a/myfile
+++ b/myfile
@@ -2,3 +2,4 @@
 something
 something else
 something again
+2

Commit cef3d4e0298dd5d279a911440bb72d39410e7898
Author: ...
Date: ...

    Added first line

Diff --git a/myfile b/myfile
Index 93db4cb..f9e1a67 100644
--- a/myfile
+++ b/myfile
@@ -1,3 +1,4 @@
+1
 something
 something else
 something again

1
ฉันเริ่มคุ้นเคยกับการใช้คอมไพล์ Mercurial ในช่วงสัปดาห์ที่แล้วและมีคำสั่งช็อตคัทที่สะดวกสบายgit reset [--patch|-p] <commit>ที่คุณสามารถใช้เพื่อช่วยให้คุณประหยัดปัญหาในการgit add -pรีเซ็ตหลังจากนั้น ฉันถูกไหม? ใช้ git 1.7.9.5
trojjer

2
ต่อไปนี้เป็นเทคนิคเพิ่มเติมเล็ก ๆ น้อย ๆ รวมถึงการรีบูตหากเป็นการกระทำที่เก่ากว่าหรือคุณต้องเปลี่ยนการยืนยัน N เป็น M กระทำ: emmanuelbernard.com/blog/2014/04/14/
Chris Westin

84

เป้าหมาย:

  • ฉันต้องการแบ่งการกระทำในอดีต ( splitme) เป็นสองข้อ
  • ฉันต้องการที่จะรักษากระทำข้อความ

วางแผน:

  1. rebase splitmeโต้ตอบจากหนึ่งก่อน
  2. splitmeแก้ไข
  3. รีเซ็ตไฟล์เพื่อแยกเป็นการกระทำที่สอง
  4. แก้ไขกระทำส่งข้อความแก้ไขตามความจำเป็น
  5. เพิ่มไฟล์ที่แยกออกจากการคอมมิทครั้งแรก
  6. กระทำด้วยข้อความใหม่
  7. รีบูตต่อไป

ขั้นตอนการรีบูต (1 & 7) สามารถข้ามได้หากsplitmeการคอมมิทล่าสุด

git rebase -i splitme^
# mark splitme commit with 'e'
git reset HEAD^ -- $files
git commit --amend
git add $files
git commit -m "commit with just some files"
git rebase --continue

หากฉันต้องการแยกไฟล์ให้มุ่งมั่นก่อนฉันจะรีบูต -i อีกครั้งและสลับลำดับ

git rebase -i splitme^
# swap order of splitme and 'just some files'

1
git reset HEAD^เป็นชิ้นส่วนปริศนาที่ขาดหายไป ใช้งานได้ดีด้วย-pเช่นกัน ขอบคุณ!
Marius Gedminas

10
มันเป็นสิ่งสำคัญที่จะต้องทราบอาร์กิวเมนต์-- $files git resetเมื่อพา ธ ถูกส่งgit resetคืนไฟล์เหล่านั้นกลับสู่สถานะของการคอมมิชชันที่อ้างอิง แต่จะไม่เปลี่ยนการคอมมิต หากคุณออกนอกเส้นทางคุณจะ "เสีย" การกระทำที่คุณต้องการแก้ไขในขั้นตอนต่อไป
เครื่องหมาย Duelin

2
วิธีนี้ทำให้คุณไม่ต้องคัดลอกและวางข้อความส่งข้อความแรกอีกครั้งเทียบกับคำตอบที่ยอมรับ
Calvin

นอกจากนี้: git reset HEAD^ -- .ถ้าคุณต้องการที่จะตั้งค่าไฟล์ทั้งหมดเพียงแค่ใช้ มากที่น่าประหลาดใจนี้เป็นไม่ได้git reset HEAD^ว่าพฤติกรรมของ
allidoiswin

52

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

ทั้ง:

git reset --soft HEAD^

นี่เป็นการยกเลิกการคอมมิทล่าสุด แต่ทิ้งทุกอย่างไว้ในฉาก จากนั้นคุณสามารถ unstage ไฟล์บางไฟล์:

git reset -- file.file

เลือกที่จะคืนค่าบางส่วนของไฟล์เหล่านั้น:

git add -p file.file

ทำคอมมิชชันใหม่ก่อน:

git commit

เวทีและส่งมอบการเปลี่ยนแปลงที่เหลือในครั้งที่สอง

git commit -a

หรือ:

เลิกทำและเลิกการเปลี่ยนแปลงทั้งหมดจากการกระทำครั้งสุดท้าย:

git reset HEAD^

คัดเลือกรอบการเปลี่ยนแปลงรอบแรก:

git add -p

Commit:

git commit

ยอมรับการเปลี่ยนแปลงที่เหลือ:

git commit -a

(ในขั้นตอนใดขั้นตอนหนึ่งหากคุณเลิกการคอมมิชชันที่เพิ่มไฟล์ใหม่และต้องการเพิ่มสิ่งนี้ในการคอมมิชชันที่สองคุณจะต้องเพิ่มด้วยตนเองเนื่องจากcommit -aการเปลี่ยนแปลงของไฟล์ที่ติดตามแล้วเท่านั้น)


22

เรียกใช้git guiเลือกปุ่มตัวเลือก "แก้ไขการส่งมอบล่าสุด" และยกเลิกการเปลี่ยนแปลง (Commit> Unstage From Commit หรือCtrl- U) ที่คุณไม่ต้องการใช้ในการส่งครั้งแรก ฉันคิดว่านั่นเป็นวิธีที่ง่ายที่สุดที่จะไปเกี่ยวกับเรื่องนี้

อีกสิ่งที่คุณสามารถทำได้คือเลือกการเปลี่ยนแปลงโดยไม่ยอมรับ ( git cherry-pick -n) จากนั้นเลือกด้วยตนเองหรือgit guiเลือกการเปลี่ยนแปลงที่ต้องการก่อนที่จะทำ



13

git cherry-pick -n forumฉันไม่มีใครแปลกใจที่แนะนำ ขั้นตอนนี้จะทำให้การเปลี่ยนแปลงจากการคอมมิชชันล่าสุดforumแต่ไม่ใช่การคอมมิท - คุณสามารถresetยกเลิกการเปลี่ยนแปลงที่คุณไม่ต้องการและทำสิ่งที่คุณต้องการเก็บไว้


3

เมธอดสควอชสองครั้ง

  1. ทำการคอมมิชชันอื่นที่ลบการเปลี่ยนแปลงที่ไม่ต้องการ (ถ้ามันต่อไฟล์นี้เป็นเรื่องง่าย: git checkout HEAD~1 -- files with unwanted changesและgit commitถ้าไม่ได้ไฟล์ที่มีการเปลี่ยนแปลงผสมสามารถฉากบางส่วน. git reset fileและgit add -p fileในฐานะที่เป็นขั้นตอนกลาง.) โทรนี้ย้อนกลับ
  2. git revert HEAD- ดำเนินการอีกอย่างที่จะเพิ่มการเปลี่ยนแปลงที่ไม่ต้องการกลับคืนมา นี่คือการคืนค่าสองครั้ง
  3. จาก 2 คอมมิชชันที่คุณทำตอนนี้ให้สควอชแรกเข้าสู่การคอมมิตที่จะแยก ( git rebase -i HEAD~3) การคอมมิชชันตอนนี้กลายเป็นอิสระจากการเปลี่ยนแปลงที่ไม่พึงประสงค์สำหรับสิ่งที่อยู่ในการคอมมิทที่สอง

ประโยชน์ที่ได้รับ

  • เก็บรักษาข้อความการกระทำ
  • ทำงานได้แม้ว่าการส่งมอบจะไม่ใช่ครั้งสุดท้าย เพียงต้องการให้การเปลี่ยนแปลงที่ไม่พึงประสงค์ไม่ขัดแย้งกับการกระทำในภายหลัง

1

เนื่องจากคุณกำลังเก็บเชอร์รี่คุณสามารถ:

  1. cherry-pickมันมี--no-commitตัวเลือกเพิ่ม
  2. resetและการใช้งานadd --patch, add --editหรือเพียงแค่addไปยังขั้นตอนสิ่งที่คุณต้องการที่จะเก็บ
  3. commit การเปลี่ยนแปลงฉาก
    • เมื่อต้องการใช้ข้อความการส่งดั้งเดิมอีกครั้งคุณสามารถเพิ่ม--reuse-message=<old-commit-ref>หรือ--reedit-message=<old-commit-ref>ตัวเลือกในcommitคำสั่ง
  4. พัดไปเปลี่ยนแปลง unstaged reset --hardกับ

อีกวิธีหนึ่งการเก็บรักษาหรือแก้ไขข้อความการส่งต้นฉบับ:

  1. cherry-pick ต้นฉบับกระทำตามปกติ
  2. กลับรายการการเปลี่ยนแปลงที่คุณไม่ต้องการและใช้addเพื่อหยุดการกลับรายการ
    • ขั้นตอนนี้จะง่ายถ้าคุณลบสิ่งที่คุณเพิ่ม แต่ค่อนข้างยุ่งยากหากคุณเพิ่มสิ่งที่คุณลบออกหรือย้อนกลับการเปลี่ยนแปลง
  3. commit --amend เพื่อให้เกิดการกลับรายการในการกระทำที่เลือกโดยเชอร์รี่
    • คุณจะได้รับข้อความยืนยันซ้ำอีกครั้งซึ่งคุณสามารถเก็บหรือแก้ไขตามความจำเป็น

0

นี่อาจเป็นโซลูชันอื่นสำหรับกรณีที่มีการส่งข้อมูลจำนวนมากและจำเป็นต้องย้ายไฟล์จำนวนเล็กน้อยไปสู่การส่งข้อมูลใหม่ วิธีนี้จะได้ผลหากชุดของ<path>ไฟล์ถูกแยกออกจากการคอมมิทครั้งล่าสุดที่ HEAD และทั้งหมดถูกย้ายไปยังการคอมมิทใหม่ หากจำเป็นต้องใช้หลายคอมมิชชันสามารถใช้โซลูชันอื่นได้

ก่อนอื่นให้ทำการปะเข้าไปในพื้นที่จัดฉากและที่ไม่มีการจัดเรียงซึ่งจะมีการเปลี่ยนแปลงเพื่อเปลี่ยนรหัสกลับเป็นก่อนที่จะทำการดัดแปลงและหลังจากการแก้ไขตามลำดับ:

git reset HEAD^ <path>

$ git status
On branch <your-branch>
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   <path>

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   <path>

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

git diff --cached   -> show staged changes to revert <path> to before HEAD
git diff            -> show unstaged changes to add current <path> changes

ย้อนกลับ<path>การเปลี่ยนแปลงในการส่งครั้งล่าสุด:

git commit --amend  -> reverts changes on HEAD by amending with staged changes

สร้างการมอบหมายใหม่พร้อม<path>การเปลี่ยนแปลง:

git commit -a -m "New Commit" -> adds new commit with unstaged changes

นี่คือผลของการสร้างการคอมมิทใหม่ที่มีการเปลี่ยนแปลงที่แยกออกมาจากการคอมมิทล่าสุด

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