ฉันจะแก้ไขข้อผิดพลาดในอดีตได้อย่างไร?


117

ฉันเพิ่งอ่าน แก้ไขไฟล์เดียวในการกระทำที่ผ่านมาในคอมไพล์แต่น่าเสียดายที่โซลูชันที่ยอมรับได้ 'เรียงลำดับ' คอมมิตใหม่ซึ่งไม่ใช่สิ่งที่ฉันต้องการ นี่คือคำถามของฉัน:

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

git stash                      # temporarily put my work aside
git rebase -i <bad_commit>~1   # rebase one step before the bad commit
                               # mark broken commit for editing
vim <affected_sources>         # fix the bug
git add <affected_sources>     # stage fixes
git commit -C <bad_commit>     # commit fixes using same log message as before
git rebase --continue          # base all later changes onto this

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

vim <affected_sources>             # fix bug
git add -p <affected_sources>      # Mark my 'fixup' hungs for staging
git fixup <bad_commit>             # amend the specified commit with staged changes,
                                   # rebase any successors of bad commit on rewritten 
                                   # commit.

อาจจะเป็นสคริปต์อัจฉริยะที่สามารถเขียนคอมมิตใหม่โดยใช้เครื่องมือประปาได้หรือไม่?


"เรียงลำดับใหม่" การคอมมิตหมายความว่าอย่างไร หากคุณกำลังจะเปลี่ยนประวัติศาสตร์แล้วทั้งหมดกระทำตั้งแต่กระทำการเปลี่ยนแปลงได้จะแตกต่างกัน แต่คำตอบที่ได้รับการยอมรับให้กับคำถามที่เชื่อมโยงไม่ได้สั่งซื้อใหม่กระทำในความรู้สึกที่มีความหมายใด ๆ
CB Bailey

1
@ ชาร์ลส์: ฉันหมายถึงการเรียงลำดับใหม่เหมือนใน: ถ้าฉันสังเกตว่า HEAD ~ 5 เป็นคอมมิตที่ไม่สมบูรณ์คำตอบที่ยอมรับต่อไปนี้ในคำถามที่เชื่อมโยงจะทำให้ HEAD (ส่วนปลายของสาขา) เป็นคอมมิตคงที่ อย่างไรก็ตามฉันต้องการให้ HEAD ~ 5 เป็นคอมมิตคงที่ซึ่งเป็นสิ่งที่คุณจะได้รับเมื่อใช้ rebase แบบโต้ตอบและแก้ไขคอมมิตเดียวเพื่อแก้ไข
Frerich Raabe

ใช่ แต่จากนั้นคำสั่ง rebase จะเช็คเอาต์มาสเตอร์อีกครั้งและสร้างฐานคอมมิตที่ตามมาทั้งหมดในคอมมิตคงที่ นี่ไม่ใช่วิธีที่คุณขับรถrebase -i?
CB Bailey

rebase --onto tmp bad-commit masterที่จริงแล้วมีปัญหาที่อาจเกิดขึ้นกับคำตอบที่ผมคิดว่ามันควรจะเป็น ตามที่เขียนไว้มันจะพยายามใช้การกระทำที่ไม่ดีกับสถานะการคอมมิตคงที่
CB Bailey

นี่คืออีกเครื่องมือหนึ่งสำหรับการทำให้กระบวนการ fixup / rebase อัตโนมัติ: stackoverflow.com/a/24656286/1058622
Mika Eloranta

คำตอบ:


166

คำตอบที่อัปเดต

ขณะที่ผ่านมาใหม่--fixupอาร์กิวเมนต์ถูกบันทึกอยู่ในที่สามารถนำมาใช้ในการสร้างกระทำด้วยข้อความเข้าสู่ระบบที่เหมาะสมสำหรับgit commit git rebase --interactive --autosquashดังนั้นวิธีที่ง่ายที่สุดในการแก้ไขการกระทำในอดีตคือตอนนี้:

$ git add ...                           # Stage a fix
$ git commit --fixup=a0b1c2d3           # Perform the commit to fix broken a0b1c2d3
$ git rebase -i --autosquash a0b1c2d3~1 # Now merge fixup commit into broken commit

คำตอบเดิม

นี่คือสคริปต์ Python เล็ก ๆ ที่ฉันเขียนเมื่อสักครู่ที่ผ่านมาซึ่งใช้git fixupตรรกะนี้ที่ฉันหวังไว้ในคำถามเดิมของฉัน สคริปต์จะถือว่าคุณจัดเตรียมการเปลี่ยนแปลงบางอย่างจากนั้นใช้การเปลี่ยนแปลงเหล่านั้นกับคอมมิตที่กำหนด

หมายเหตุ : สคริปต์นี้ใช้เฉพาะ Windows ค้นหาgit.exeและตั้งค่าGIT_EDITORตัวแปรสภาพแวดล้อมโดยใช้setตัวแปรสภาพแวดล้อมการใช้ปรับตามความจำเป็นสำหรับระบบปฏิบัติการอื่น

การใช้สคริปต์นี้ฉันสามารถใช้เวิร์กโฟลว์ 'แก้ไขซอร์สที่เสีย, การแก้ไขสเตจ, รันเวิร์กโฟลว์ git fixup' ที่ฉันขอได้อย่างแม่นยำ:

#!/usr/bin/env python
from subprocess import call
import sys

# Taken from http://stackoverflow.com/questions/377017/test-if-executable-exists-in python
def which(program):
    import os
    def is_exe(fpath):
        return os.path.exists(fpath) and os.access(fpath, os.X_OK)

    fpath, fname = os.path.split(program)
    if fpath:
        if is_exe(program):
            return program
    else:
        for path in os.environ["PATH"].split(os.pathsep):
            exe_file = os.path.join(path, program)
            if is_exe(exe_file):
                return exe_file

    return None

if len(sys.argv) != 2:
    print "Usage: git fixup <commit>"
    sys.exit(1)

git = which("git.exe")
if not git:
    print "git-fixup: failed to locate git executable"
    sys.exit(2)

broken_commit = sys.argv[1]
if call([git, "rev-parse", "--verify", "--quiet", broken_commit]) != 0:
    print "git-fixup: %s is not a valid commit" % broken_commit
    sys.exit(3)

if call([git, "diff", "--staged", "--quiet"]) == 0:
    print "git-fixup: cannot fixup past commit; no fix staged."
    sys.exit(4)

if call([git, "diff", "--quiet"]) != 0:
    print "git-fixup: cannot fixup past commit; working directory must be clean."
    sys.exit(5)

call([git, "commit", "--fixup=" + broken_commit])
call(["set", "GIT_EDITOR=true", "&&", git, "rebase", "-i", "--autosquash", broken_commit + "~1"], shell=True)

2
คุณสามารถใช้git stashและgit stash popรอบ ๆ ฐานข้อมูลของคุณเพื่อไม่ต้องใช้ไดเร็กทอรีการทำงานที่สะอาดอีกต่อไป
Tobias Kienzler

@TobiasKienzler: เกี่ยวกับการใช้git stashและgit stash pop: คุณขวา แต่โชคร้ายที่git stashเป็นมากช้าลงบน Windows มากกว่าที่เป็นอยู่บน Linux หรือ OS / X เนื่องจากไดเรกทอรีการทำงานของฉันมักจะสะอาดฉันจึงข้ามขั้นตอนนี้เพื่อไม่ให้คำสั่งช้าลง
Frerich Raabe

ฉันสามารถยืนยันได้โดยเฉพาะอย่างยิ่งเมื่อทำงานกับเครือข่ายแชร์: - /
Tobias Kienzler

1
ดี ฉันทำโดยไม่ได้ตั้งใจgit rebase -i --fixupและมันปรับฐานจากการคอมมิตคงที่เป็นจุดเริ่มต้นดังนั้นจึงไม่จำเป็นต้องใช้อาร์กิวเมนต์ sha ในกรณีของฉัน
fwielstra

1
สำหรับคนที่ใช้ --autosquash บ่อยๆการตั้งค่าเป็นพฤติกรรมเริ่มต้นอาจเป็นประโยชน์: git config --global rebase.autosquash true
taktak004

31

สิ่งที่ฉันทำคือ:

git เพิ่ม ... # เพิ่มการแก้ไข
git กระทำ # Committed แต่ผิดที่
git rebase -i HEAD ~ 5 # ตรวจสอบการคอมมิต 5 ครั้งสุดท้ายสำหรับการ rebasing

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

pick 08e833c การเปลี่ยนแปลงที่ดี 1.
เลือก 9134ac9 การเปลี่ยนแปลงที่ดี 2.
pick 5adda55 เปลี่ยนไม่ดี!
เลือก 400bce4 การเปลี่ยนแปลงที่ดี 3.
เลือก 2bc82n1 แก้ไขการเปลี่ยนแปลงที่ไม่ดี

...ถึง:

pick 08e833c การเปลี่ยนแปลงที่ดี 1.
เลือก 9134ac9 การเปลี่ยนแปลงที่ดี 2.
pick 5adda55 เปลี่ยนไม่ดี!
f 2bc82n1 การแก้ไขการเปลี่ยนแปลงที่ไม่ดี # เลื่อนขึ้นและเปลี่ยน 'pick' เป็น 'f' สำหรับ 'fixup'
เลือก 400bce4 การเปลี่ยนแปลงที่ดี 3.

บันทึกและออกจากโปรแกรมแก้ไขของคุณจากนั้นการแก้ไขจะถูกย้อนกลับไปที่การคอมมิตที่เป็นของ

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


9
เห็นได้ชัดว่าคุณสามารถเปลี่ยน HEAD ~ 5 เป็น HEAD ~ n เพื่อย้อนกลับไปอีก คุณจะไม่ต้องการยุ่งเกี่ยวกับประวัติใด ๆ ที่คุณผลักดันให้อัปสตรีมดังนั้นฉันมักจะพิมพ์ 'git rebase -i origin / master' เพื่อให้แน่ใจว่าฉันจะเปลี่ยนเฉพาะประวัติที่ไม่ได้ผลักดันเท่านั้น
Kris Jenkins

4
นี่ก็เหมือนกับสิ่งที่ฉันทำมาตลอด FWIW คุณอาจสนใจ--autosquashสวิตช์git rebaseซึ่งเรียงลำดับขั้นตอนในโปรแกรมแก้ไขใหม่ให้คุณโดยอัตโนมัติ ดูคำตอบของฉันสำหรับสคริปต์ที่ใช้ประโยชน์จากสิ่งนี้เพื่อใช้git fixupคำสั่ง
Frerich Raabe

ฉันไม่รู้ว่าคุณสามารถสั่งคอมมิตแฮชใหม่ได้ดี!
Aaron Franke

เป็นสิ่งที่ดี! เพียงเพื่อให้แน่ใจว่างาน rebase ทั้งหมดเสร็จสิ้นแล้วนั้นแยกสาขาคุณลักษณะ และไม่ไปยุ่งกับสาขาทั่วไปอย่างอาจารย์
Jay Modi

22

สายไปหน่อย แต่นี่เป็นวิธีแก้ปัญหาที่ได้ผลตามที่ผู้เขียนจินตนาการไว้

เพิ่มสิ่งนี้ใน. gitconfig ของคุณ:

[alias]
    fixup = "!sh -c '(git diff-files --quiet || (echo Unstaged changes, please commit or stash with --keep-index; exit 1)) && COMMIT=$(git rev-parse $1) && git commit --fixup=$COMMIT && git rebase -i --autosquash $COMMIT~1' -"

ตัวอย่างการใช้งาน:

git add -p
git fixup HEAD~5

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

git add -p
git stash --keep-index
git fixup HEAD~5
git stash pop

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


นี่เป็นประโยชน์มาก สำหรับฉันแล้ว usecase ที่พบบ่อยที่สุดคือการแก้ไขการเปลี่ยนแปลงในการกระทำก่อนหน้านี้ดังนั้นgit fixup HEADสิ่งที่ฉันสร้างนามแฝงสำหรับ ฉันยังสามารถใช้การแก้ไขสำหรับสิ่งที่ฉันคิดว่า
ตั๊กแตน

ขอบคุณ! ฉันมักจะใช้มันในการกระทำครั้งล่าสุด แต่ฉันมีนามแฝงอื่นสำหรับการแก้ไขด่วน amend = commit --amend --reuse-message=HEADจากนั้นคุณสามารถพิมพ์git amendหรือgit amend -aข้ามตัวแก้ไขสำหรับข้อความคอมมิต
dschlyter

3
ปัญหาในการแก้ไขคือฉันจำวิธีสะกดไม่ได้ ฉันต้องคิดเสมอว่ามันแก้ไขหรือแก้ไขและนั่นไม่ดี
ตั๊กแตน

12

ในการแก้ไขปัญหาหนึ่งข้อ:

git commit --fixup a0b1c2d3 .
git rebase --autosquash -i HEAD~2

โดยที่ a0b1c2d3 เป็นคอมมิตที่คุณต้องการแก้ไขและโดยที่ 2 คือจำนวนคอมมิต +1 ที่คุณต้องการเปลี่ยน

หมายเหตุ: git rebase --autosquash ที่ไม่มี -i ใช้งานไม่ได้ แต่ใช้งาน -i ได้ซึ่งแปลก


2016 และ--autosquashโดยไม่ต้อง-iยังคงไม่ทำงาน
Jonathan Cross

1
ตามที่หน้าคนบอก: ตัวเลือกนี้ใช้ได้เฉพาะเมื่อใช้ตัวเลือก --interactive แต่มีวิธีง่ายๆในการข้ามตัวแก้ไข:EDITOR=true git rebase --autosquash -i
joeytwiddle

ขั้นตอนที่ 2 ใช้ไม่ได้กับฉันโดยพูดว่า: โปรดระบุสาขาที่คุณต้องการตั้งฐานใหม่
djangonaut

git rebase --autosquash -i HEAD ~ 2 (โดยที่ 2 คือจำนวนคอมมิต +1 ที่คุณต้องการเปลี่ยนแปลง
Sérgio

6

อัปเดต:ขณะนี้สามารถพบสคริปต์เวอร์ชันที่สะอาดกว่าได้ที่นี่: https://github.com/deiwin/git-dotfiles/blob/docs/bin/git-fixup https://github.com/deiwin/git-dotfiles/blob/docs/bin/git-fixup

ฉันมองหาสิ่งที่คล้ายกัน แม้ว่าสคริปต์ Python นี้จะซับซ้อนเกินไปดังนั้นฉันจึงใช้วิธีแก้ปัญหาของตัวเอง:

ขั้นแรกนามแฝง git ของฉันมีลักษณะเช่นนั้น (ยืมมาจากที่นี่ ):

[alias]
  fixup = !sh -c 'git commit --fixup=$1' -
  squash = !sh -c 'git commit --squash=$1' -
  ri = rebase --interactive --autosquash

ตอนนี้ฟังก์ชั่นทุบตีกลายเป็นเรื่องง่าย:

function gf {
  if [ $# -eq 1 ]
  then
    if [[ "$1" == HEAD* ]]
    then
      git add -A; git fixup $1; git ri $1~2
    else
      git add -A; git fixup $1; git ri $1~1
    fi
  else
    echo "Usage: gf <commit-ref> "
  fi
}

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

if [[ "$1" == HEAD* ]]ส่วนหนึ่ง (ยืมมาจากที่นี่ ) ถูกนำมาใช้เพราะถ้าคุณใช้ตัวอย่างเช่น HEAD ~ 2 เป็นกระทำของคุณ (กระทำคุณต้องการแก้ไขเปลี่ยนแปลงในปัจจุบันขึ้นมาด้วย) อ้างอิงแล้วหัวจะถูกแทนที่หลังจากกระทำได้ถูกสร้างขึ้น fixup และคุณจะต้องใช้ HEAD ~ 3 เพื่ออ้างถึงคอมมิตเดียวกัน


ทางเลือกที่น่าสนใจ +1
VonC

4

คุณสามารถหลีกเลี่ยงขั้นตอนการโต้ตอบได้โดยใช้ตัวแก้ไข "null":

$ EDITOR=true git rebase --autosquash -i ...

นี้จะใช้เป็นบรรณาธิการแทน/bin/true /usr/bin/vimยอมรับทุกสิ่งที่คอมไพล์แนะนำโดยไม่ต้องแจ้งให้ทราบ


อันที่จริงนี่คือสิ่งที่ฉันทำในคำตอบของสคริปต์ Python 'คำตอบดั้งเดิม' ของฉันตั้งแต่วันที่ 30 กันยายน 2010 (สังเกตว่าที่ด้านล่างของสคริปต์มีข้อความว่าอย่างไรcall(["set", "GIT_EDITOR=true", "&&", git, "rebase", "-i" ...)
Frerich Raabe

4

สิ่งที่ทำให้ฉันรำคาญจริงๆเกี่ยวกับเวิร์กโฟลว์การแก้ไขคือฉันต้องหาตัวเองว่าฉันต้องการเอาชนะการเปลี่ยนแปลงในทุกครั้ง ฉันสร้างคำสั่ง "git fixup" ที่ช่วยในเรื่องนี้

คำสั่งนี้จะสร้าง fixup commits ด้วยเวทมนตร์เพิ่มเติมที่ใช้git-depsเพื่อค้นหาคอมมิตที่เกี่ยวข้องโดยอัตโนมัติดังนั้นเวิร์กโฟลว์จึงลงมาที่:

# discover and fix typo in a previously committed change
git add -p # stage only typo fix
git fixup

# at some later point squash all the fixup commits that came up
git rebase --autosquash master

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

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

https://github.com/Valodim/git-fixup


2

คุณสามารถสร้างโปรแกรมฟิกซ์อัพสำหรับไฟล์ใดไฟล์หนึ่งโดยใช้นามแฝงนี้

[alias]
...
# fixup for a file, using the commit where it was last modified
fixup-file = "!sh -c '\
        [ $(git diff          --numstat $1 | wc -l) -eq 1 ] && git add $1 && \
        [ $(git diff --cached --numstat $1 | wc -l) -eq 1 ] || (echo No changes staged. ; exit 1) && \
        COMMIT=$(git log -n 1 --pretty=format:"%H" $1) && \
            git commit --fixup=$COMMIT && \
            git rebase -i --autosquash $COMMIT~1' -"

หากคุณได้ทำการเปลี่ยนแปลงบางอย่างmyfile.txtแต่คุณไม่ต้องการใส่ไว้ในคอมมิตใหม่git fixup-file myfile.txtจะfixup!สร้างคอมมิตที่myfile.txtถูกแก้ไขล่าสุดจากนั้นจะทำการrebase --autosquashเปลี่ยนแปลง


ฉลาดมากฉันไม่ต้องการที่git rebaseจะเรียกโดยอัตโนมัติ
hurikhan77

2

commit --fixupและrebase --autosquashยอดเยี่ยมมาก แต่ก็ไม่เพียงพอ เมื่อฉันมีลำดับของการกระทำA-B-Cและฉันเขียนการเปลี่ยนแปลงเพิ่มเติมในโครงสร้างการทำงานของฉันซึ่งอยู่ในข้อตกลงที่มีอยู่อย่างน้อยหนึ่งรายการฉันต้องดูประวัติด้วยตนเองตัดสินใจว่าการเปลี่ยนแปลงใดเป็นของข้อตกลงจัดเวทีและสร้างfixup!กระทำ แต่คอมไพล์มีการเข้าถึงข้อมูลเพียงพอที่จะทำสิ่งนั้นให้ฉันได้อยู่แล้วดังนั้นฉันจึงเขียนสคริปต์ Perlซึ่งทำได้แค่นั้น

สำหรับก้อนใหญ่แต่ละตัวในgit diffสคริปต์จะใช้git blameเพื่อค้นหาการกระทำที่สัมผัสกับบรรทัดที่เกี่ยวข้องครั้งสุดท้ายและเรียกร้องgit commit --fixupให้เขียนข้อfixup!ตกลงที่เหมาะสมโดยพื้นฐานแล้วจะทำสิ่งเดียวกับที่ฉันทำด้วยตนเองก่อนหน้านี้

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


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

1

ฉันเขียนฟังก์ชั่นเชลล์เล็ก ๆ ที่เรียกว่าgcfเพื่อดำเนินการแก้ไขปัญหาและ rebase โดยอัตโนมัติ:

$ git add -p

  ... select hunks for the patch with y/n ...

$ gcf <earlier_commit_id>

  That commits the fixup and does the rebase.  Done!  You can get back to coding.

ตัวอย่างเช่นคุณสามารถแก้ไขคอมมิตที่สองก่อนล่าสุดด้วย: gcf HEAD~~

นี่คือฟังก์ชั่น คุณสามารถวางลงในไฟล์~/.bashrc

git_commit_immediate_fixup() {
  local commit_to_amend="$1"
  if [ -z "$commit_to_amend" ]; then
    echo "You must provide a commit to fixup!"; return 1
  fi

  # Get a static commit ref in case the commit is something relative like HEAD~
  commit_to_amend="$(git rev-parse "${commit_to_amend}")" || return 2

  #echo ">> Committing"
  git commit --no-verify --fixup "${commit_to_amend}" || return 3

  #echo ">> Performing rebase"
  EDITOR=true git rebase --interactive --autosquash --autostash \
                --rebase-merges --no-fork-point "${commit_to_amend}~"
}

alias gcf='git_commit_immediate_fixup'

ใช้--autostashเพื่อซ่อนและป๊อปการเปลี่ยนแปลงที่ไม่ได้ผูกมัดหากจำเป็น

--autosquashต้องมี--interactiverebase EDITORแต่เราหลีกเลี่ยงการทำงานร่วมกันโดยใช้หุ่น

--no-fork-pointปกป้องการกระทำจากการถูกทิ้งอย่างเงียบ ๆ ในสถานการณ์ที่หายาก (เมื่อคุณแยกสาขาใหม่และมีคนยกเลิกการกระทำที่ผ่านมาแล้ว)


0

ฉันไม่รู้วิธีอัตโนมัติ แต่นี่คือวิธีแก้ปัญหาที่อาจทำให้มนุษย์ยุ่งได้ง่ายขึ้น:

git stash
# write the patch
git add -p <file>
git commit -m"whatever"   # message doesn't matter, will be replaced via 'fixup'
git rebase -i <bad-commit-id>~1
# now cut&paste the "whatever" line from the bottom to the second line
# (i.e. below <bad-commit>) and change its 'pick' into 'fixup'
# -> the fix commit will be merged into the <bad-commit> without changing the
# commit message
git stash pop

ดูคำตอบของฉันสำหรับสคริปต์ที่ใช้ประโยชน์จากสิ่งนี้เพื่อใช้git fixupคำสั่ง
Frerich Raabe

@Frerich Raabe: ฟังดูดีฉันไม่รู้--autosquash
Tobias Kienzler

0

ฉันขอแนะนำhttps://github.com/tummychow/git-absorb :

สนามลิฟต์

คุณมีสาขาคุณลักษณะที่มีข้อผูกมัดไม่กี่ข้อ เพื่อนร่วมทีมของคุณตรวจสอบสาขาและชี้ให้เห็นข้อบกพร่องบางประการ คุณได้รับการแก้ไขสำหรับจุดบกพร่อง แต่คุณไม่ต้องการผลักดันให้ทั้งหมดกลายเป็นข้อตกลงทึบแสงที่ระบุว่าการแก้ไขเพราะคุณเชื่อในการกระทำของปรมาณู แทนที่จะค้นหาคอมมิต SHAs สำหรับgit commit --fixupหรือเรียกใช้ rebase เชิงโต้ตอบด้วยตนเองให้ทำสิ่งนี้:

  • git add $FILES_YOU_FIXED

  • git absorb --and-rebase

  • หรือ: git rebase -i --autosquash master

git absorbจะระบุโดยอัตโนมัติว่าการกระทำใดที่สามารถแก้ไขได้อย่างปลอดภัยและการเปลี่ยนแปลงใดที่จัดทำดัชนีเป็นของการกระทำเหล่านั้น จากนั้นจะเขียน fixup! ตกลงสำหรับการเปลี่ยนแปลงแต่ละอย่าง คุณสามารถตรวจสอบผลลัพธ์ด้วยตนเองหากคุณไม่ไว้วางใจจากนั้นพับการแก้ไขลงในสาขาคุณลักษณะของคุณด้วยฟังก์ชัน autosquash ในตัวของ git

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