วิธีแก้ไขคอมมิทที่ระบุ?


2233

ฉันมักจะส่งรายการข้อผูกพันเพื่อตรวจสอบ หากฉันมีข้อผูกพันต่อไปนี้:

  1. HEAD
  2. Commit3
  3. Commit2
  4. Commit1

... git commit --amendฉันรู้ว่าฉันสามารถปรับเปลี่ยนหัวกระทำด้วย แต่ฉันจะแก้ไขCommit1ได้อย่างไรว่ามันไม่ได้HEADกระทำ


31
ดูคำตอบอื่น ๆ ได้ที่นี่: stackoverflow.com/a/18150592/520567คำตอบที่คุณยอมรับนั้นเป็นคำตอบที่แน่นอนสำหรับคำถามของคุณ แต่ถ้าคุณมีคำมั่นใหม่พร้อมก่อนตัดสินใจใช้การแก้ไขคำตอบนี้จะตรงไปตรงมามากขึ้น นอกจากนี้ยังสามารถทำงานกับหลายคอมมิทที่คุณต้องการผสาน / สควอชพร้อมกับคอมมิชชันที่เก่ากว่า
akostadinov

6
นอกจากนี้คุณยังสามารถเห็นการแบ่งการกระทำในGit Tools - ประวัติการเขียนซ้ำสำหรับข้อมูลเพิ่มเติม
hakre

มีความเป็นไปได้ที่ซ้ำกันของการแก้ไขคอมมิชชันเดิมที่ยังไม่ได้ทำ
tkruse

คำตอบ:


2955

คุณสามารถใช้rebase คอมไพล์ ตัวอย่างเช่นหากคุณต้องการแก้ไขคอมมิชชันbbc643cdให้รัน

$ git rebase --interactive 'bbc643cd^'

โปรดทราบลูกศร^ในตอนท้ายของคำสั่งเพราะคุณจะต้องจริง rebase กลับไปกระทำก่อนที่คนที่คุณต้องการแก้ไข

ในตัวแก้ไขเริ่มต้นปรับเปลี่ยนpickเป็นeditในบรรทัดที่กล่าวถึง 'bbc643cd'

บันทึกไฟล์และออก: git จะตีความและดำเนินการคำสั่งในไฟล์โดยอัตโนมัติ bbc643cdคุณจะพบว่าตัวเองอยู่ในสถานการณ์ก่อนหน้านี้ในที่คุณเพิ่งได้สร้างกระทำ

ณ จุดนี้bbc643cdเป็นความมุ่งมั่นครั้งสุดท้ายของคุณและคุณสามารถแก้ไขได้อย่างง่ายดาย : ทำการเปลี่ยนแปลงแล้วส่งคำสั่งด้วย:

$ git commit --all --amend --no-edit

หลังจากนั้นให้พิมพ์:

$ git rebase --continue

เพื่อกลับไปที่ HEAD ก่อนหน้านี้กระทำ

คำเตือน : โปรดทราบว่าสิ่งนี้จะเปลี่ยน SHA-1 ของการกระทำเช่นเดียวกับเด็กทุกคน - ในคำอื่น ๆ นี้จะเขียนประวัติจากจุดนั้นไปข้างหน้า คุณสามารถทำลาย repos การทำเช่นนี้ถ้าคุณกดใช้คำสั่งgit push --force


125
อีกตัวเลือกที่น่าสนใจภายในโฟลว์นี้คือเมื่อคุณย้ายไปที่การคอมมิทที่คุณต้องการแก้ไขแทนการแก้ไขไฟล์และแก้ไขการคอมมิตด้านบน (ตัวที่คุณกำลังแก้ไข) คุณอาจต้องการแยกการคอมมิทที่แตกต่างกันออกเป็นสองคอมมิชชัน (หรือมากกว่านั้น) ในกรณีดังกล่าวให้ย้ายกลับไปที่การคอมมิตเพื่อแก้ไขและเรียกใช้ "git reset HEAD ^" ที่จะนำไฟล์ที่แก้ไขแล้วของคอมมิชชันนั้นเข้าสู่สเตจ ตอนนี้เลือกและยอมรับไฟล์ใด ๆ ตามที่คุณต้องการ โฟลว์นี้อธิบายได้ค่อนข้างดีในหน้า man "git-rebase" ดูหัวข้อ "การแยกการผูกพัน" bit.ly/d50w1M
Diego Pino

200
ใน Git 1.6.6 และใหม่กว่าคุณสามารถใช้การrewordดำเนินการgit rebase -iแทนedit(จะเปิดตัวแก้ไขโดยอัตโนมัติและดำเนินการต่อด้วยขั้นตอนการ rebase ที่เหลือซึ่งจะเป็นการยกเลิกการใช้งานgit commit --ammendและgit rebase --continueเมื่อคุณจำเป็นต้องเปลี่ยนข้อความการกระทำไม่ใช่เนื้อหา )
Chris Johnsen

108
เป็นที่น่าสังเกตว่าคุณอาจจำเป็นต้องเรียกใช้git stashก่อนgit rebaseและgit stash popหลังหากคุณมีการเปลี่ยนแปลงที่ค้างอยู่
user123444555621

3
มีคำสั่ง shortucut เพื่อแก้ไขการคอมมิทเฉพาะใน rebase เชิงโต้ตอบโดยไม่ต้องเปิดเอดิเตอร์, ค้นหาการคอมมิท, ทำเครื่องหมายการแก้ไข, จากนั้นกลับไปที่บรรทัดคำสั่ง?
sstur

15
โปรดทราบว่ามีคอมไพล์ใหม่ก็จะฉลาดที่จะปฏิบัติตามคำแนะนำพร้อมท์แทนการใช้สุ่มสี่สุ่มห้าgit commit --all --amend --no-editที่นี่ ทั้งหมดที่ฉันต้องทำหลังจากที่git rebase -i ...เป็นไปได้ตามปกติแล้วgit commit --amend git rebase --continue
Eric Chen

453

ใช้ rebase เชิงโต้ตอบที่ยอดเยี่ยม:

git rebase -i @~9   # Show the last 9 commits in a text editor

ค้นหาการกระทำที่คุณต้องการเปลี่ยนpickเป็นe( edit) และบันทึกและปิดไฟล์ Git จะย้อนกลับไปสู่การกระทำนั้น ๆ ซึ่งจะช่วยให้คุณ:

  • ใช้git commit --amendเพื่อทำการเปลี่ยนแปลงหรือ
  • ใช้git reset @~เพื่อละทิ้งการคอมมิทครั้งล่าสุด แต่ไม่ใช่การเปลี่ยนแปลงไฟล์ (เช่นพาคุณไปยังจุดที่คุณอยู่เมื่อคุณแก้ไขไฟล์ แต่ยังไม่ได้คอมมิท)

หลังมีประโยชน์สำหรับการทำสิ่งที่ซับซ้อนมากขึ้นเช่นการแบ่งออกเป็นหลายคอมมิชชัน

จากนั้นเรียกใช้git rebase --continueแล้ว Git จะเล่นซ้ำการเปลี่ยนแปลงที่ตามมาที่ด้านบนของการคอมมิทที่คุณแก้ไข คุณอาจถูกขอให้แก้ไขข้อขัดแย้งในการผสาน

หมายเหตุ: @เป็นการจดชวเลขHEADและ~เป็นการกระทำก่อนส่งมอบที่ระบุ

อ่านเพิ่มเติมเกี่ยวกับประวัติการเขียนใหม่ในเอกสาร Git


อย่ากลัวที่จะลดราคา

ProTip ™: อย่ากลัวที่จะทดสอบด้วยคำสั่ง "อันตราย" ที่เขียนประวัติใหม่ * - Git จะไม่ลบการกระทำของคุณเป็นเวลา 90 วันโดยค่าเริ่มต้น คุณสามารถค้นหาได้ใน reflog:

$ git reset @~3   # go back 3 commits
$ git reflog
c4f708b HEAD@{0}: reset: moving to @~3
2c52489 HEAD@{1}: commit: more changes
4a5246d HEAD@{2}: commit: make important changes
e8571e4 HEAD@{3}: commit: make some changes
... earlier commits ...
$ git reset 2c52489
... and you're back where you started

* ระวังตัวเลือกเช่น--hardและ--force- พวกเขาสามารถทิ้งข้อมูล
* นอกจากนี้อย่าเขียนประวัติในสาขาใด ๆ ที่คุณร่วมมือ



ในหลาย ๆ ระบบgit rebase -iจะเปิด Vim เป็นค่าเริ่มต้น เป็นกลุ่มไม่ทำงานชอบมากที่สุดการแก้ไขข้อความที่ทันสมัยเพื่อให้ดูที่วิธีการ rebase ใช้เป็นกลุ่ม git config --global core.editor your-favorite-text-editorถ้าคุณต้องการใช้โปรแกรมแก้ไขที่แตกต่างกันเปลี่ยนมันด้วย


29
ตรงกลางของคำตอบของคุณคือสถานที่แปลก ๆ ที่จะนำสิ่งที่ฉันสามารถอธิบายได้ว่าเป็นโฆษณา miniture สำหรับ VIM มันไม่เกี่ยวข้องกับคำถามและเพียงแค่ตอบคำถามของคุณ
Intentss

21
@ เนื้อหา: อ่าฉันเห็นได้ว่าทำไมมันดูแปลก ๆ เหตุผลเบื้องหลังคือ Vim เป็นตัวแก้ไขข้อความเริ่มต้นในหลาย ๆ ระบบดังนั้นประสบการณ์ครั้งแรกของคนจำนวนมากในการรีบูทแบบโต้ตอบคือหน้าจอที่พิมพ์ทำให้เคอร์เซอร์บินไปทั่วทุกที่ จากนั้นพวกเขาสลับบรรณาธิการเป็นอย่างอื่นและประสบการณ์ครั้งที่สองของพวกเขาในการรีบูตอินเทอร์แอกทีฟเป็นเรื่องปกติ แต่พวกเขาสงสัยว่าทำไมมันถึงใช้เท็กซ์ไฟล์แทน GUI เพื่อให้เกิดการไหลเวียนด้วยการรีบูตคุณต้องมีโหมด Vim หรือ Emacs 'rebase-mode
Zaz

9
ตกลง. เมื่อเห็นว่ามีคนจำนวนมากพบว่าส่วนที่ไม่เกี่ยวข้องฉันได้ย่อให้เหลือ 3 บรรทัดและอธิบายวิธีเปลี่ยนบรรณาธิการหากจำเป็น
Zaz

17
! น่ากลัว ฉันไม่รู้ว่าคุณสามารถใช้@เป็นชวเลขHEADได้ ขอขอบคุณที่โพสต์สิ่งนี้
James Ko

3
git reset @~git rebase ...สิ่งที่ผมอยากจะทำหลังจากเลือกกระทำกับ คุณคือฮีโร่ของฉัน)
18

79

การรีบูตด้วยอินเทอร์แอคทีฟ--autosquashเป็นสิ่งที่ฉันใช้บ่อยเมื่อต้องแก้ไขก่อนหน้านี้ให้ลึกลงไปในประวัติศาสตร์ มันช่วยเร่งกระบวนการที่คำตอบของ ZelluX แสดงให้เห็นและเป็นประโยชน์อย่างยิ่งเมื่อคุณมีมากกว่าหนึ่งข้อตกลงที่คุณต้องแก้ไข

จากเอกสาร:

--autosquash

เมื่อข้อความบันทึกการคอมมิชชันเริ่มต้นด้วย "squash! …" (หรือ "fixup! …") และมีการคอมมิชชันที่ชื่อขึ้นต้นด้วย…เดียวกันให้แก้ไขรายการสิ่งที่ต้องทำของ rebase -i โดยอัตโนมัติ ทำเครื่องหมายสำหรับการบีบมาทันทีหลังจากการคอมมิทที่จะแก้ไข

สมมติว่าคุณมีประวัติที่มีลักษณะเช่นนี้:

$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1

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

$ git commit -m "fixup! Commit2"

หรือคุณสามารถใช้ commit-sha แทนข้อความคอมมิชชันดังนั้น"fixup! e8adec4หรือแม้แต่คำนำหน้าของข้อความคอมมิชชัน

จากนั้นเริ่มต้นการปฏิเสธแบบโต้ตอบกับการกระทำก่อนหน้านี้

$ git rebase e8adec4^ -i --autosquash

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

pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3

สิ่งที่คุณต้องทำคือบันทึกและออก


21
นอกจากนี้คุณยังสามารถใช้แทนgit commit --fixup=@~ git commit -m "fixup! Commit2"สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อข้อความยืนยันของคุณยาวขึ้นและอาจเป็นเรื่องยากที่จะพิมพ์ข้อความทั้งหมด
Zaz

42

วิ่ง:

$ git rebase --interactive commit_hash^

แต่ละอัน^บ่งบอกว่าคุณต้องการแก้ไขกี่ครั้งหากมีเพียงหนึ่งรายการ (แฮชการกระทำที่คุณระบุ) จากนั้นคุณเพียงเพิ่ม^แล้วคุณก็เพิ่มอีกหนึ่ง

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

แต่ละข้อความส่งมอบคุณต้องบันทึกและออกจาก ( :wq) เพื่อไปที่ข้อความส่งข้อความถัดไป

หากคุณต้องการออกโดยไม่ใช้การเปลี่ยนแปลงให้กด :q!

แก้ไข : เพื่อนำทางvimคุณใช้jเพื่อขึ้น, kลง, hไปทางซ้ายและlไปทางขวา (ทั้งหมดนี้อยู่ในNORMALโหมดกดESCเพื่อไปที่NORMALโหมด) หากต้องการแก้ไขข้อความให้กดiเพื่อให้คุณเข้าสู่INSERTโหมดที่คุณแทรกข้อความ กดESCเพื่อกลับไปที่NORMALโหมด :)

UPDATE : นี่คือลิงค์ที่ยอดเยี่ยมจากรายชื่อ GitHub วิธีการยกเลิก (เกือบ) ทุกอย่างด้วย git


4
ทำงานอย่างสมบูรณ์แบบสำหรับฉัน น่าพูดถึงgit push --force?
u01jmg3

อะไรgit push --forceคือการเขียนทับรีโมตที่คอมมิตด้วยการคอมมิทท้องถิ่นของคุณ นั่นไม่ใช่กรณีของหัวข้อนี้ :)
betoharres

@BetuUuUu แน่นอนถ้าการคอมมิทของคุณถูกผลักไปที่รีโมตและคุณได้ทำการคอมมิตข้อความในเครื่อง
Sudip Bhandari

@ SudipBhandari นั่นคือความรู้สึกที่ฉันได้รับ ฉันไม่ได้บังคับและตอนนี้ฉันมีสาขาพิเศษสะท้อนถึงความมุ่งมั่นทั้งหมดกลับไปที่ข้อความที่ฉันเปลี่ยนซึ่งก็น่าเกลียดมาก
ruffin

2
@greenhouse หากคุณแก้ไขและบังคับให้สมาชิกในทีมคนอื่น ๆ ส่วนใหญ่อาจประสบกับความขัดแย้งที่เกิดขึ้น ดังนั้นคุณควรระมัดระวังอย่างยิ่ง แต่ถ้าคุณปรับเปลี่ยนบางสิ่งที่ยังไม่มีใครดึงข้อมูลมาก็ควรจะดี (จะไม่แจ้งให้ทราบ) ดังนั้นฉันจะพิจารณา - บังคับเป็นวิธีสุดท้ายและมักจะปรึกษาสถานะของ repo กับสมาชิกคนอื่น ๆ
Tomasz Kaczmarzyk

18

git rebase --ontoหากมีเหตุผลบางอย่างที่คุณไม่ชอบบรรณาธิการโต้ตอบคุณสามารถใช้

Commit1สมมติว่าคุณต้องการที่จะแก้ไข ก่อนอื่นสาขาจากก่อน Commit1 :

git checkout -b amending [commit before Commit1]

ประการที่สองคว้าCommit1ด้วยcherry-pick:

git cherry-pick Commit1

ตอนนี้แก้ไขการเปลี่ยนแปลงของคุณสร้างCommit1':

git add ...
git commit --amend -m "new message for Commit1"

และในที่สุดหลังจากที่ได้ทำการเปลี่ยนแปลงอื่น ๆ แล้วให้ทำการโยกย้ายส่วนที่เหลือของคุณไปจนถึงmasterการกระทำใหม่:

git rebase --onto amending Commit1 master

อ่าน: "rebase สู่สาขาamendingทั้งหมดกระทำระหว่างCommit1(ไม่รวม) และmaster(รวม)" นั่นคือ Commit2 และ Commit3 ตัด Commit1 เก่าออกทั้งหมด คุณสามารถเลือกพวกเชอร์รี่ได้ แต่วิธีนี้ง่ายกว่า

อย่าลืมล้างสาขาของคุณ!

git branch -d amending

4
คุณสามารถใช้git checkout -b amending Commit1~1เพื่อรับการกระทำก่อนหน้า
Arin Taylor

สองขั้นตอนแรกเทียบเท่าgit checkout -b amending Commit1หรือไม่
Haoshu

16

ขึ้นอยู่กับเอกสาร

การแก้ไขข้อความของข้อความที่เก่ากว่าหรือหลายข้อความที่ส่งมอบ

git rebase -i HEAD~3 

ด้านบนแสดงรายการ 3 การกระทำล่าสุดในสาขาปัจจุบันเปลี่ยน 3 เป็นอย่างอื่นถ้าคุณต้องการมากขึ้น รายการจะมีลักษณะคล้ายกับต่อไปนี้:

pick e499d89 Delete CNAME
pick 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

แทนที่pickด้วยrewordก่อนที่จะส่งข้อความยอมรับที่คุณต้องการเปลี่ยน สมมติว่าคุณเปลี่ยนคอมมิชชันที่สองในรายการไฟล์ของคุณจะมีลักษณะดังนี้:

pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.

บันทึกและปิดไฟล์ commit list ซึ่งจะปรากฎตัวแก้ไขใหม่ให้คุณเปลี่ยนข้อความคอมมิชชันเปลี่ยนข้อความ commit และบันทึก

Finaly Force-push the commits ที่แก้ไขแล้ว

git push --force

ฉันได้รับข้อผิดพลาดต่อไปนี้: ข้อผิดพลาด: มีปัญหากับตัวแก้ไข 'vi' โปรดระบุข้อความโดยใช้ตัวเลือก -m หรือ -F
Erick Maynard

12

คำสั่งที่ไม่โต้ตอบทั้งหมด(1)

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

git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'

ประโยชน์ที่ใหญ่ที่สุดของคำสั่งนี้เป็นความจริงที่ว่ามันไม่มีเสียงเรียกเข้า


(1)ระบุว่าไม่มีความขัดแย้งระหว่างการรีบูตแน่นอน

การใช้

git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111

ชื่อamend-toดูเหมือนว่าเหมาะสม IMHO เปรียบเทียบการไหลด้วย--amend:

git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>

คำอธิบาย

  • git config --global alias.<NAME> '!<COMMAND>'- สร้างนามแฝง git ทั่วโลกที่มีชื่อว่า<NAME>จะดำเนินการคำสั่งที่ไม่ใช่ git<COMMAND>
  • f() { <BODY> }; f - ฟังก์ชันทุบตี "ไม่ระบุชื่อ"
  • SHA=`git rev-parse "$1"`; - แปลงอาร์กิวเมนต์เป็น git revision และกำหนดผลลัพธ์ให้กับตัวแปร SHA
  • git commit --fixup "$SHA"- fixup SHAกระทำสำหรับ ดูgit-commitเอกสาร
  • GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
    • git rebase --interactive "$SHA^" ส่วนหนึ่งได้รับการคุ้มครองโดยคำตอบอื่น ๆ
    • --autosquashคือสิ่งที่ใช้ร่วมกับgit commit --fixupดูgit-rebaseเอกสารสำหรับข้อมูลเพิ่มเติม
    • GIT_SEQUENCE_EDITOR=trueเป็นสิ่งที่ทำให้สิ่งทั้งหมดไม่ใช่แบบโต้ตอบ แฮ็คนี้ฉันได้เรียนรู้จากโพสต์บล็อกนี้

1
ท่านสามารถamend-toจัดการไฟล์ที่ไม่มีการจัดการ: git config --global alias.amend-to '!f() { SHA=git rev-parse "$ 1"; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'
Dethariel

2
ข้อกังวลอย่างหนึ่งของวิธีนี้คือสามารถใช้โปรแกรมแก้ไขปัญหาที่ไม่เกี่ยวข้องได้
Ciro Santilli 冠状病毒审查六四事件法轮功

ไม่ใช่คำถามที่ต้องการเปลี่ยนข้อความยืนยันใช่ไหม เพราะคำตอบนี้ไม่ได้อยู่ที่อย่างนั้นหรืออย่างน้อยก็ไม่โดยตรง
wytten

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

ฉันสับสนดังนั้นเราจะเปลี่ยนข้อความยืนยันได้อย่างไร
ggb667

8

การแก้ไขการรีบูตแบบโต้ตอบอัตโนมัติตามด้วยการส่งคืนพร้อมสำหรับการทำซ้ำ

ฉันพบว่าตัวเองแก้ไขการกระทำที่ผ่านมาบ่อยครั้งมากพอที่ฉันจะเขียนสคริปต์ให้มัน

นี่คือขั้นตอนการทำงาน:

  1. git commit-edit <commit-hash>
    

    สิ่งนี้จะทำให้คุณอยู่ในความมุ่งมั่นที่คุณต้องการแก้ไข

  2. แก้ไขและแสดงความมุ่งมั่นตามที่คุณต้องการในตอนแรก

    (คุณอาจต้องการใช้git stash saveเพื่อเก็บไฟล์ที่คุณไม่ได้ทำไว้)

  3. ทำซ้ำการกระทำด้วย--amendเช่น:

    git commit --amend
    
  4. ทำ rebase ให้สมบูรณ์:

    git rebase --continue
    

เพื่อให้การทำงานด้านบนวางสคริปต์ด้านล่างนี้ลงในไฟล์ที่สามารถเรียกทำงานได้ซึ่งมีชื่อgit-commit-editอยู่ใน$PATH:

#!/bin/bash

set -euo pipefail

script_name=${0##*/}

warn () { printf '%s: %s\n' "$script_name" "$*" >&2; }
die () { warn "$@"; exit 1; }

[[ $# -ge 2 ]] && die "Expected single commit to edit. Defaults to HEAD~"

# Default to editing the parent of the most recent commit
# The most recent commit can be edited with `git commit --amend`
commit=$(git rev-parse --short "${1:-HEAD~}")
message=$(git log -1 --format='%h %s' "$commit")

if [[ $OSTYPE =~ ^darwin ]]; then
  sed_inplace=(sed -Ei "")
else
  sed_inplace=(sed -Ei)
fi

export GIT_SEQUENCE_EDITOR="${sed_inplace[*]} "' "s/^pick ('"$commit"' .*)/edit \\1/"'
git rebase --quiet --interactive --autostash --autosquash "$commit"~
git reset --quiet @~ "$(git rev-parse --show-toplevel)"  # Reset the cache of the toplevel directory to the previous commit
git commit --quiet --amend --no-edit --allow-empty  #  Commit an empty commit so that that cache diffs are un-reversed

echo
echo "Editing commit: $message" >&2
echo

7

มาถึงวิธีนี้ (และมันก็อาจจะเหมือนกับการใช้ rebase เชิงโต้ตอบ) แต่สำหรับฉันมันค่อนข้างตรงไปตรงมา

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

สมมติว่าคุณต้องการเปลี่ยนการมอบหมาย0และคุณเปิดอยู่feature-branch

some-commit---0---1---2---(feature-branch)HEAD

Checkout quick-branchเพื่อกระทำนี้และสร้าง นอกจากนี้คุณยังสามารถโคลนสาขาคุณลักษณะของคุณเป็นจุดกู้คืน (ก่อนที่จะเริ่ม)

?(git checkout -b feature-branch-backup)
git checkout 0
git checkout -b quick-branch

ตอนนี้คุณจะมีสิ่งนี้:

0(quick-branch)HEAD---1---2---(feature-branch)

การเปลี่ยนแปลงในเวทีเก็บทุกอย่างไว้ที่อื่น

git add ./example.txt
git stash

ยอมรับการเปลี่ยนแปลงและเช็คเอาต์กลับเป็น feature-branch

git commit --amend
git checkout feature-branch

ตอนนี้คุณจะมีสิ่งนี้:

some-commit---0---1---2---(feature-branch)HEAD
           \
             ---0'(quick-branch)

Rebase feature-branchสู่quick-branch(แก้ไขข้อขัดแย้งระหว่างทาง) quick-branchสมัครซ่อนและลบ

git rebase quick-branch
git stash pop
git branch -D quick-branch

และคุณท้ายด้วย:

some-commit---0'---1'---2'---HEAD(feature-branch)

Git จะไม่ซ้ำกัน (แม้ว่าฉันจะไม่สามารถพูดได้จริง ๆ ว่าขอบเขตใด) 0 ที่กระทำเมื่อทำการรีบูต

หมายเหตุ: แฮชการคอมมิชชันทั้งหมดมีการเปลี่ยนแปลงเริ่มจากคอมมิชชันที่เราตั้งใจจะเปลี่ยนแปลง


6

หากต้องการรับคำสั่งที่ไม่โต้ตอบให้วางสคริปต์พร้อมเนื้อหานี้ใน PATH ของคุณ:

#!/bin/sh
#
# git-fixup
# Use staged changes to modify a specified commit
set -e
cmt=$(git rev-parse $1)
git commit --fixup="$cmt"
GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"

ใช้มันโดยการแสดงละครการเปลี่ยนแปลงของคุณ (กับgit add) git fixup <commit-to-modify>และเรียกใช้แล้ว แน่นอนว่ามันจะยังคงมีการโต้ตอบหากคุณได้รับความขัดแย้ง


1
มันใช้งานได้ดี ฉันได้เพิ่มฟังก์ชั่นพิเศษบางอย่างเพื่อทำการฟิกซ์เจอร์ทีละน้อยของต้นไม้สกปรกเพื่อทำให้ชุดคอมมิทสมบูรณ์แบบ `dirtydiff = $ (ต่าง git); ถ้า ["$ {dirtydiff}"! = ""]; จากนั้นเสียงก้อง "หยุดต้นไม้สกปรก"> & 2; git stash; Fi;
Simon Feltman

6

git stash+ rebaseระบบอัตโนมัติ

สำหรับเมื่อฉันต้องการแก้ไขเก่ากระทำหลายครั้งสำหรับความคิดเห็น Gerrit ฉันได้ทำ:

git-amend-old() (
  # Stash, apply to past commit, and rebase the current branch on to of the result.
  current_branch="$(git rev-parse --abbrev-ref HEAD)"
  apply_to="$1"
  git stash
  git checkout "$apply_to"
  git stash apply
  git add -u
  git commit --amend --no-edit
  new_sha="$(git log --format="%H" -n 1)"
  git checkout "$current_branch"
  git rebase --onto "$new_sha" "$apply_to"
)

GitHub ต้นน้ำ

การใช้งาน:

  • แก้ไขไฟล์ต้นฉบับไม่จำเป็นต้องgit addมีอยู่แล้วใน repo
  • git-amend-old $old_sha

ฉันชอบสิ่งนี้มากกว่า--autosquashเพราะมันไม่ได้แก้ไขปัญหาที่ไม่เกี่ยวข้องอื่น ๆ


วิธีแก้ปัญหาที่ดีมากนี่ควรเป็นตัวเลือกเริ่มต้น git amendเพื่อใช้การเปลี่ยนแปลงกับคอมมิทเฉพาะกับการใช้การซ่อนปัจจุบันฉลาดมาก!
caiohamamura

5

ฉันแก้ไขสิ่งนี้

1) โดยการสร้างคอมมิทใหม่ด้วยการเปลี่ยนแปลงที่ฉันต้องการ ..

r8gs4r commit 0

2) ฉันรู้ว่าการกระทำใดที่ฉันต้องรวมเข้าด้วย ซึ่งกระทำ 3

ดังนั้นgit rebase -i HEAD~4# 4 หมายถึงการกระทำ 4 ครั้งล่าสุด (ที่นี่การส่งมอบ 3 อยู่ในอันดับที่ 4)

3) ในการตอบโต้ rebase ล่าสุดกระทำจะอยู่ที่ด้านล่าง มันจะดูเหมือนกัน

pick q6ade6 commit 3
pick vr43de commit 2
pick ac123d commit 1
pick r8gs4r commit 0

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

parent
|_child

pick q6ade6 commit 3
f r8gs4r commit 0
pick vr43de commit 2
pick ac123d commit 1

หลังจากจัดเรียงใหม่คุณจะต้องแทนที่ p pickด้วยf( fixupจะผสานโดยไม่ต้องส่งข้อความ) หรือs( สควอชผสานกับข้อความคอมมิชชันสามารถเปลี่ยนแปลงได้ในเวลาทำงาน)

แล้วบันทึกต้นไม้ของคุณ

ตอนนี้ผสานทำกับกระทำที่มีอยู่

หมายเหตุ: เป็นวิธีที่ไม่ดีกว่าเว้นแต่คุณจะดูแลด้วยตัวเอง หากคุณมีขนาดใหญ่ทีมมันไม่ใช่วิธีที่ยอมรับได้ในการเขียนแผนผัง git จะสิ้นสุดลงในความขัดแย้งที่คุณรู้ว่าเคยชินอื่น ๆ ถ้าคุณต้องการรักษาต้นไม้ให้สะอาดด้วยความมุ่งมั่นที่น้อยลงสามารถลองสิ่งนี้และหากเป็นทีมขนาดเล็กมิฉะนั้นจะไม่ดีกว่า .....


นี่เป็นทางออกที่ดีหากคุณไม่ต้องการทำการแก้ไขสดระหว่างการรีบูตแบบโต้ตอบ
Dunatotatos

4

ตัวเลือกที่ดีที่สุดคือการใช้คำสั่ง "rebase Interactive"

git rebaseคำสั่งที่มีประสิทธิภาพอย่างไม่น่าเชื่อ มันช่วยให้คุณสามารถแก้ไข การส่งข้อความรวมคอมมิทสั่งซื้อใหม่ ...

ทุกครั้งที่คุณปฏิเสธการส่งมอบ SHA ใหม่จะถูกสร้างขึ้นสำหรับการส่งมอบแต่ละรายการโดยไม่คำนึงถึงเนื้อหาที่จะมีการเปลี่ยนแปลงหรือไม่! คุณควรระวังเวลาที่จะใช้คำสั่งนี้เพราะอาจมีผลกระทบรุนแรงโดยเฉพาะถ้าคุณทำงานร่วมกับนักพัฒนาอื่น ๆ พวกเขาอาจเริ่มทำงานกับความมุ่งมั่นของคุณในขณะที่คุณกำลังรีบูตบางอย่าง หลังจากที่คุณบังคับให้ส่งการคอมมิทพวกเขาจะไม่ซิงค์และคุณอาจพบภายหลังในสถานการณ์ที่ยุ่งเหยิง ดังนั้นระวัง!

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

ตอนนี้วิธีการใช้คำสั่งนี้

git rebase -i <base> 

-iยืนสำหรับ"การโต้ตอบ" โปรดทราบว่าคุณสามารถดำเนินการ rebase ในโหมดที่ไม่โต้ตอบ อดีต:

#interactivly rebase the n commits from the current position, n is a given number(2,3 ...etc)
git rebase -i HEAD~n 

HEADระบุที่ตั้งปัจจุบันของคุณ (สามารถเป็นชื่อสาขาหรือการมอบหมาย SHA) ~nหมายถึง "n beforeéดังนั้นHEAD~nจะมีรายการของ 'n' กระทำก่อนที่หนึ่งที่คุณมีอยู่ในขณะนี้

git rebase มีคำสั่งที่แตกต่างกันเช่น:

  • pหรือpickเพื่อให้การกระทำเหมือนเดิม
  • rหรือreword: เพื่อเก็บเนื้อหาของการกระทำ แต่แก้ไขข้อความการส่ง
  • sหรือsquash: เพื่อรวมการเปลี่ยนแปลงของคอมมิทนี้เข้ากับคอมมิชชันก่อนหน้า (คอมมิตข้างบนในรายการ)
  • ... ฯลฯ

    หมายเหตุ: จะเป็นการดีกว่าถ้าให้ Git ทำงานร่วมกับเครื่องมือแก้ไขรหัสของคุณเพื่อทำให้สิ่งต่าง ๆ ง่ายขึ้น git config --global core.editor "code --wait"อย่างเช่นถ้าคุณใช้รหัสภาพคุณสามารถเพิ่มเช่นนี้ หรือคุณสามารถค้นหาวิธีเชื่อมโยงที่คุณต้องการในโปรแกรมแก้ไขรหัสด้วย GIT ใน Google

ตัวอย่างของ git rebase

ฉันต้องการเปลี่ยน 2 คำตอบสุดท้ายที่ฉันทำดังนั้นฉันจึงดำเนินการดังนี้:

  1. แสดงความมุ่งมั่นปัจจุบัน:
    #This to show all the commits on one line
    $git log --oneline
    4f3d0c8 (HEAD -> documentation) docs: Add project description and included files"
    4d95e08 docs: Add created date and project title"
    eaf7978 (origin/master , origin/HEAD, master) Inital commit
    46a5819 Create README.md
    
  2. ตอนนี้ฉันใช้git rebaseเพื่อเปลี่ยนข้อความที่ส่งล่าสุด 2 ข้อความ: $git rebase -i HEAD~2 มันเปิดตัวแก้ไขรหัสและแสดงสิ่งนี้:

    pick 4d95e08 docs: Add created date and project title
    pick 4f3d0c8 docs: Add project description and included files
    
    # Rebase eaf7978..4f3d0c8 onto eaf7978 (2 commands)
    #
    # Commands:
    # p, pick <commit> = use commit
    # r, reword <commit> = use commit, but edit the commit message
    ...
    

    เนื่องจากฉันต้องการเปลี่ยนข้อความการส่งมอบสำหรับ 2 การกระทำ ดังนั้นผมจะพิมพ์rหรือในสถานที่ของreword pickจากนั้นบันทึกไฟล์และปิดแท็บ โปรดทราบว่าrebaseจะดำเนินการในกระบวนการหลายขั้นตอนดังนั้นขั้นตอนต่อไปคือการปรับปรุงข้อความ โปรดทราบว่าคอมมิชชันจะแสดงตามลำดับเวลาย้อนกลับดังนั้นการคอมมิทล่าสุดจะแสดงในคอมมิชชันแรกและคอมมิชชันแรกในบรรทัดแรกและอื่น ๆ

  3. อัปเดตข้อความ: อัปเดตข้อความแรก:

    docs: Add created date and project title to the documentation "README.md"
    
    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    ...
    

    บันทึกและปิดแก้ไขข้อความที่สอง

    docs: Add project description and included files to the documentation "README.md"
    
    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    ...
    

    บันทึกและปิด

  4. คุณจะได้รับข้อความเช่นนี้ในตอนท้ายของการรีบูต: Successfully rebased and updated refs/heads/documentationซึ่งหมายความว่าคุณจะประสบความสำเร็จ คุณสามารถแสดงการเปลี่ยนแปลง:

    5dff827 (HEAD -> documentation) docs: Add project description and included files to the documentation "README.md"
    4585c68 docs: Add created date and project title to the documentation "README.md"
    eaf7978 (origin/master, origin/HEAD, master) Inital commit
    46a5819 Create README.md
    

    ฉันหวังว่าจะช่วยผู้ใช้ใหม่ :)


2

สำหรับฉันมันคือการลบข้อมูลประจำตัวบางส่วนจาก repo ฉันพยายามลดระดับใหม่และพบกับความขัดแย้งที่ดูเหมือนไม่เกี่ยวข้องระหว่างทางเมื่อพยายามลดระดับ - ยุติ ไม่ต้องพยายามรีบูตตัวเองใช้เครื่องมือที่เรียกว่า BFG (ชงติดตั้ง bfg) บน mac


0

หากคุณยังไม่ได้ส่งการผูกมัดคุณสามารถกลับไปใช้การคอมมิชชันก่อนหน้าได้ git reset HEAD^[1,2,3,4...]

ตัวอย่างเช่น

git commit <file1> -m "Updated files 1 and 2"
git commit <file3> -m "Updated file 3"

โอ๊ะลืมเพิ่ม file2 ไปที่การส่งครั้งแรก ...

git reset HEAD^1 // because I only need to go back 1 commit

git add <file2>

สิ่งนี้จะเพิ่ม file2 ในการส่งครั้งแรก


0

วิธีนี้อาจฟังดูงี่เง่ามาก แต่สามารถช่วยคุณได้ในเงื่อนไขบางประการ

เพื่อนของฉันเพิ่งพบเจอไฟล์บางไฟล์ขนาดใหญ่โดยไม่ตั้งใจ ( ไฟล์ที่สร้างอัตโนมัติสี่ไฟล์ระหว่าง 3GB ถึง 5GB แต่ละรายการ) จากนั้นทำโค้ดเพิ่มเติมให้พร้อมก่อนที่จะตระหนักถึงปัญหาที่git pushไม่ได้ทำงานอีกต่อไป!

ไฟล์ถูกแสดงรายการไว้.gitignoreแต่หลังจากเปลี่ยนชื่อโฟลเดอร์คอนเทนเนอร์แล้วไฟล์จะถูกเปิดเผยและถูกคอมมิท! และตอนนี้ก็มีอีกไม่กี่กระทำของรหัสที่ด้านบนของที่ แต่pushทำงานได้ตลอดไป (พยายามอัปโหลด GB ของข้อมูล) และในที่สุดก็จะล้มเหลวเนื่องจากข้อ จำกัด ขนาดไฟล์ Github ของ

ปัญหาของการรีบูตเดอร์อินเทอร์แอคทีฟหรือสิ่งที่คล้ายกันคือพวกเขาจะจัดการกับไฟล์ขนาดใหญ่เหล่านี้และจะใช้เวลาตลอดไปในการทำทุกอย่าง อย่างไรก็ตามหลังจากใช้เวลาเกือบหนึ่งชั่วโมงใน CLI เราไม่แน่ใจว่าไฟล์ (และเดลต้า) ถูกลบออกจากประวัติจริงหรือไม่รวมอยู่ในคอมมิชชันปัจจุบัน แรงผลักก็ไม่ได้ผลเช่นกันและเพื่อนของฉันก็ติดอยู่จริงๆ

ดังนั้นวิธีแก้ปัญหาที่ฉันพบคือ:

  1. ~/Project-oldเปลี่ยนชื่อโฟลเดอร์คอมไพล์ปัจจุบัน
  2. โคลนโฟลเดอร์ git อีกครั้งจาก github (ถึง~/Project)
  3. ชำระเงินไปที่สาขาเดียวกัน
  4. ด้วยตนเองcp -rไฟล์จากโฟลเดอร์ ~/Project-old~/Project
  5. ตรวจสอบให้แน่ใจว่ามีไฟล์ขนาดใหญ่ที่ไม่จำเป็นต้องเช็คอินmvและรวมอยู่ใน.gitignoreอย่างเหมาะสม
  6. นอกจากนี้ตรวจสอบให้แน่ใจว่าคุณไม่ได้เขียนทับ.gitโฟลเดอร์ในการโคลนล่าสุด~/Projectเก่าที่นั่นคือสิ่งที่บันทึกของประวัติศาสตร์ที่มีปัญหาอาศัยอยู่!
  7. ตรวจสอบการเปลี่ยนแปลง มันควรจะเป็นสหภาพของการกระทำที่ผ่านมาทั้งหมดไม่รวมไฟล์ที่มีปัญหา
  8. ในที่สุดยอมรับการเปลี่ยนแปลงและมันก็ดีที่จะpush'ed

ปัญหาที่ใหญ่ที่สุดในการแก้ปัญหานี้คือมันเกี่ยวกับการคัดลอกไฟล์บางไฟล์ด้วยตนเองและยังรวมการคอมมิทล่าสุดทั้งหมดไว้ในไฟล์เดียว

ประโยชน์ที่สำคัญคือมันชัดเจนมากในทุกขั้นตอนใช้งานได้ดีสำหรับไฟล์ขนาดใหญ่ (รวมถึงไฟล์ที่ละเอียดอ่อน)และไม่ทิ้งร่องรอยใด ๆ ในประวัติศาสตร์ไว้เบื้องหลัง!

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