programing

어떻게 하면 과거의 약속을 쉽게 고칠 수 있습니까?

mailnote 2023. 6. 25. 20:25
반응형

어떻게 하면 과거의 약속을 쉽게 고칠 수 있습니까?

저는 방금 과거 커밋에서 단일 파일을 수정하는 것을 읽었지만 불행히도 받아들여진 솔루션은 커밋을 '재주문'합니다. 이것은 제가 원하는 것이 아닙니다.그래서 제 질문은 이렇습니다.

가끔 (관련 없는) 기능을 작업하는 동안 코드에 버그가 있는 것을 발견합니다. 잠깐.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

하지만, 이런 일이 너무 자주 일어나서 위의 순서가 성가시게 됩니다.특히 '대화형 리베이스'는 지루합니다.단계적 변경으로 과거의 임의 커밋을 수정할 수 있는 위 시퀀스에 대한 바로 가기가 있습니까?나는 이것이 역사를 바꾼다는 것을 완벽하게 알고 있지만, 나는 너무 자주 실수를 해서 나는 정말로 그런 것을 갖고 싶습니다.

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.

배관 도구 등을 사용하여 커밋을 다시 작성할 수 있는 스마트 스크립트가 아닐까요?

업데이트된 답변

전에 조금전에운새로▁new운▁a▁a새로.--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

원답

에 작성한 이 를 구현합니다. 이것을 구현합니다.git fixup내가 원래 질문에서 바라던 논리.이 스크립트는 사용자가 일부 변경 사항을 준비했다고 가정한 다음 해당 변경 사항을 지정된 커밋에 적용합니다.

참고: 이 스크립트는 Windows 전용이며 다음을 찾습니다.git.exe합니다.GIT_EDITOR을 사용한 set다른 운영 체제의 경우 필요에 따라 이를 조정합니다.

이 스크립트를 사용하면 제가 요청한 '손상된 소스 수정, 단계 수정, rugit 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)

제가 하는 일은:

git 추가...수정 사항을 추가합니다.git commit # 커밋되었지만 잘못된 위치에 있습니다.
gitrebase -i HEAD~5 # 최근 5개 커밋의 rebase를 검토합니다.

편집자가 최근 5개 커밋 목록과 함께 열려 간섭할 준비가 됩니다.변경:

08e833c 좋은 변화 1을 선택합니다.9134ac9 좋은 변화 2를 선택합니다.5adda55를 선택하세요 나쁜 변화!400bce4 좋은 변화 3을 선택합니다.2bc82n1 잘못된 변경 수정을 선택합니다.

...받는 사람:

08e833c 좋은 변화 1을 선택합니다.9134ac9 좋은 변화 2를 선택합니다.5adda55를 선택하세요 나쁜 변화!f2bc82n1 잘못된 변화의 수정.위로 이동하고 '픽'을 'fixup'으로 변경합니다.
400bce4 좋은 변화 3을 선택합니다.

편집자를 저장하고 종료하면 수정 내용이 해당 편집자가 속한 커밋으로 다시 압축됩니다.

몇 번을 하고 나면 잠자는 동안 몇 초 안에 그렇게 될 것입니다.인터랙티브 리베이스는 저에게 정말 큰 인기를 끌었던 기능입니다.이것에 엄청나게 유용하고 그 이상으로...

파티에 조금 늦었지만, 여기 저자의 상상대로 작동하는 해결책이 있습니다.

.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

경고를 주는 대신 별칭을 자동으로 저장하도록 수정할 수 있습니다.그러나 픽스업이 제대로 적용되지 않으면 충돌을 해결한 후 수동으로 스택을 팝업해야 합니다.저장과 팝업을 수동으로 모두 수행하는 것이 더 일관적이고 덜 혼란스러워 보입니다.

하나의 커밋을 수정하는 방법:

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

여기서 a0b1c2d3은 수정할 커밋이고, 여기서 2는 변경할 커밋 수 +1입니다.

참고: gitrebase - i를 사용하지 않는 자동 스퀴시 - i는 작동하지 않지만 -와 함께 작동했습니다. 이것은 이상합니다.

업데이트: 이제 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

이제 bash 기능은 매우 단순해집니다.

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
}

이 코드는 모든 현재 변경 사항을 먼저 준비합니다(파일을 직접 준비하려는 경우 이 부분을 제거할 수 있습니다).그런 다음 픽스업 커밋을 생성합니다(필요한 경우 스쿼시도 사용할 수 있습니다).그 후에 그것은 다음과 대화형 리베이스를 시작합니다.--autosquash인수로 지정한 커밋의 상위 플래그입니다.그러면 구성된 텍스트 편집기가 열리고 모든 것이 예상대로인지 확인할 수 있으며 편집기를 닫기만 하면 프로세스가 완료됩니다.

if [[ "$1" == HEAD* ]]예를 들어 HEAD~2를 커밋(현재 변경 내용을 수정하려는 커밋) 참조로 사용하면 픽스업 커밋이 생성된 후 HEAD가 대체되고 HEAD~3을 사용하여 동일한 커밋을 참조해야 하기 때문에 부품(여기서 차용)이 사용됩니다.

수정 작업 워크플로우에서 저를 정말로 괴롭힌 것은 매번 변경 사항을 밀어넣고자 하는 약속을 스스로 찾아야 한다는 것이었습니다.이것을 도와주는 "git fixup" 명령어를 만들었습니다.

이 명령은 git-dep을 사용하여 관련 커밋을 자동으로 찾는 마법을 추가하여 픽스업 커밋을 생성하므로 워크플로우는 다음과 같은 작업을 수행하는 경우가 많습니다.

# 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

저는 https://github.com/tummychow/git-absorb 을 추천합니다.

엘리베이터 피치

몇 가지 커밋이 있는 피쳐 분기가 있습니다.당신의 팀 동료가 지점을 검토하고 몇 가지 버그를 지적했습니다.여러분은 버그를 수정할 수 있지만, 원자적 커밋을 믿기 때문에 수정이라고 하는 불투명한 커밋에 모든 버그를 밀어넣고 싶지는 않습니다. SHA에 대한 커밋 SHA를 .git commit --fixup수동 대화형 기본 재배치를 실행하려면 다음을 수행합니다.

  • git add $FILES_YOU_FIXED

  • git absorb --and-rebase

  • 또는:git rebase -i --autosquash master

git absorb에서는 수정해도 안전한 커밋과 해당 커밋 각각에 속하는 인덱싱된 변경 사항을 자동으로 식별합니다.그런 다음 각 변경 사항에 대해 수정! 커밋을 작성합니다.신뢰할 수 없는 경우 수동으로 출력을 확인한 다음 Git의 내장된 자동 스쿼시 기능을 사용하여 픽스업을 피쳐 분기에 접을 수 있습니다.

다음은 다음과 같이 작동하는 승인된 답변을 기반으로 한 Git 별칭입니다.

git fixup          # fixup staged & unstaged changes into the last commit
git fixup ac1dc0d3 # fixup staged & unstaged changes into the given commit

:~/.gitconfig파일 및 이 별칭 추가:

[alias]
    fixup = "!git add . && git commit --fixup=${1:-$(git rev-parse HEAD)} && GIT_EDITOR=true git rebase --interactive --autosquash ${1:-$(git rev-parse HEAD~2)}~1"

null 편집기를 사용하면 대화형 단계를 피할 수 있습니다.

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

다음을 사용합니다./bin/true대에편집 에./usr/bin/vim그것은 항상 git가 제안하는 모든 것을 프롬프트 없이 받아들입니다.

이 별칭을 사용하여 특정 파일에 대한 복구를 만들 수 있습니다.

[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.

commit --fixup그리고.rebase --autosquash훌륭하지만, 그들은 충분하지 않습니다.때 일의커있을때이밋련때▁when있.A-B-C기존의 중 이상에 트리에 몇 더 , 커밋에 사항을 하고, 하고, 그고기존커중밋작리몇속가다트더작업니합에리성지사을항변하나상경는하를 해야 합니다. 기록을 수동으로 보고, 어떤 변경 사항이 어떤 커밋에 속하는지 결정하고, 그것들을 준비하고, 생성해야 합니다.fixup!커밋하지만 Git은 이미 저를 위해 모든 것을 할 수 있을 만큼 충분한 정보에 접근할 수 있기 때문에, 저는 바로 그것을 하는 Perl 스크립트를 작성했습니다.

허크 대에해▁h의 각 git diff는 대에사는되용을 사용합니다.git blame으로 닿은 " 관련라에로닿은커밋다찾전니화합고을으마지막인다▁to니▁that합전화▁commit▁find▁the찾▁calls▁and고,"를 호출합니다.git commit --fixup 절한글을쓰를 fixup!커밋, 기본적으로 이전에 수동으로 하던 것과 동일한 작업을 수행합니다.

여러분이 유용하다고 한다면, 하면, 는 그런 을 얻을 수 있을 것입니다. 언젠가는 그런 기능이 제공될 것입니다.git▁i. 대화형 의해 되었을 때 되어야 하는지 할 수 있는 를 보고 대화형 리베이스에 의해 병합 충돌이 도입되었을 때 어떻게 해결되어야 하는지 이해할 수 있는 도구를 보고 싶습니다.

정해진

$ git log --oneline
123123 Add foo
234234 Fix biz
123113 Remove fong
123123 Modify bar
123143 Add bar

▁fix▁commit에 대한 픽스업 커밋을 할 수 .Modify bar를 사용하여

git commit --fixup ':/bar'

하위 문자열을 포함하는 마지막 커밋에 대한 픽스업 커밋을 생성합니다.bar한 구문을 있기 가 꽤 .

그럼 그냥 도망가rebase -i --autosquash ...당신이 편할 때 실제로 수리를 할 수 있습니다.

NB: 이것은 일종의 정규 표현을 사용합니다.(,)다른 특수 문자는 인용이 필요할 수 있습니다.

나는 작은 셸 함수를 썼습니다.gcf픽스업 커밋 및 기본 재배치를 자동으로 수행하려면 다음과 같이 하십시오.

$ 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에는 을합니다가 합니다.--interactive기본을 다시 적용하지만, 우리는 더미를 사용하여 상호 작용을 피합니다.EDITOR.

--no-fork-point드물게 새 분기를 분기하고 다른 사용자가 이미 과거의 커밋을 리베이스한 경우 커밋이 자동으로 삭제되지 않도록 보호합니다.

자동화된 방법에 대해서는 잘 모르지만, 인간이 보다 쉽게 로봇화할 수 있는 해결책이 있습니다.

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

언급URL : https://stackoverflow.com/questions/3103589/how-can-i-easily-fixup-a-past-commit

반응형