나는 일반적으로 검토를 위해 커밋 목록을 제출합니다. 다음 커밋이 있는 경우:
-
HEAD
-
Commit3
-
Commit2
-
Commit1
git commit --amend
하여 헤드 커밋을 수정할 수 있다는 것을 알고 있습니다. HEAD
커밋이 아닌 경우 Commit1
어떻게 수정할 수 있습니까?
질문자 :Sam Liao
나는 일반적으로 검토를 위해 커밋 목록을 제출합니다. 다음 커밋이 있는 경우:
HEAD
Commit3
Commit2
Commit1
git commit --amend
하여 헤드 커밋을 수정할 수 있다는 것을 알고 있습니다. HEAD
커밋이 아닌 경우 Commit1
어떻게 수정할 수 있습니까?
git rebase 를 사용할 수 있습니다. 예를 들어 commit bbc643cd
을 실행하십시오.
$ git rebase --interactive 'bbc643cd^'
수정하려는 커밋 이전에 실제로 다시 커밋해야 하기 때문에 명령 끝에 있는 캐럿 ^
에 유의하십시오.
기본 편집기에서 'bbc643cd'를 언급하는 행에서 edit
pick
을 수정하십시오.
파일을 저장하고 종료합니다. git은 파일의 명령을 해석하고 자동으로 실행합니다. bbc643cd
생성한 이전 상황에 처하게 될 것입니다.
이 시점에서 bbc643cd
는 마지막 커밋이며 쉽게 수정할 수 있습니다. 변경한 다음 다음 명령을 사용하여 커밋합니다.
$ git commit --all --amend --no-edit
그런 다음 다음을 입력합니다.
$ git rebase --continue
이전 HEAD 커밋으로 돌아갑니다.
경고 : 이것은 모든 자식뿐만 아니라 해당 커밋의 SHA-1도 변경한다는 점에 유의하십시오. git push --force
명령을 사용하여 푸시하면 이 작업을 수행하여 repos를 중단할 수 있습니다.
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을 엽니다. Vim은 대부분의 최신 텍스트 편집기처럼 작동하지 않으므로 Vim을 사용하여 리베이스하는 방법을 살펴보세요. 다른 편집기를 사용하려면 git config --global core.editor your-favorite-text-editor
.
--autosquash
를 사용한 대화형 리베이스 는 이전 커밋을 더 깊이 수정해야 할 때 자주 사용하는 것입니다. 그것은 본질적으로 ZelluX의 답변이 설명하는 프로세스의 속도를 높이고 편집해야 하는 커밋이 둘 이상 있을 때 특히 편리합니다.
문서에서:
--autosquash
커밋 로그 메시지가 "squash! … "(또는 "fixup! … ")로 시작하고 제목이 … 스쿼싱으로 표시된 것은 수정할 커밋 직후에 나타납니다.
다음과 같은 기록이 있다고 가정합니다.
$ 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
저장하고 종료하기만 하면 됩니다.
운영:
$ git rebase --interactive commit_hash^
각 ^
는 편집하려는 커밋 수를 나타냅니다. 단 하나(지정한 커밋 해시)인 경우 하나를 추가하면 됩니다. ^
빔을 사용하면 단어가 변경 pick
을 reword
(변경하려는 커밋에 대한 저장 및 종료 :wq
). 그런 다음 git은 커밋 메시지를 변경할 수 있도록 reword로 표시한 각 커밋에 대해 프롬프트를 표시합니다.
다음 커밋 메시지로 이동하려면 각 커밋 메시지를 저장하고 종료해야 합니다( :wq
변경 사항을 적용하지 않고 종료하려면 :q!
편집 : 탐색에 vim
사용 j
오르기 위해, k
, 아래로 이동하는 h
왼쪽으로 이동을하고, l
(모든이를 마우스 오른쪽 버튼으로 갈 NORMAL
모드 키를 눌러 ESC
로 이동 NORMAL
모드). 텍스트를 편집하려면 i
를 눌러 텍스트를 삽입 INSERT
모드로 들어갑니다. ESC
를 눌러 NORMAL
모드로 돌아갑니다. :)
업데이트 : 여기 github 목록의 훌륭한 링크 가 있습니다. git으로 (거의) 모든 것을 실행 취소하는 방법
문서 기반
이전 또는 여러 커밋 메시지의 메시지 수정
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.
커밋 목록 파일을 저장하고 닫으면 커밋 메시지를 변경하고 커밋 메시지를 변경하고 저장할 수 있는 새 편집기가 나타납니다.
마지막으로 수정된 커밋을 강제 푸시합니다.
git push --force
나는 이것을 위해 내가 사용하는 별칭을 공유하고 싶다고 생각했습니다. 비대화 형 인터랙티브 리베이스를 기반으로 합니다. git에 추가하려면 다음 명령을 실행하십시오(아래 설명 참조).
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'
또는 스테이징되지 않은 파일도 처리할 수 있는 버전(스태싱 및 언스태싱):
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'
이 명령의 가장 큰 장점은 no-vim 이라는 사실입니다.
(1) 물론 rebase 중에 충돌이 없다는 점을 감안할 때
git amend-to <REV> # eg 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이 아닌 명령 <COMMAND>
<NAME>
이라는 전역 git 별칭을 만듭니다.f() { <BODY> }; f
- "익명" bash 함수.SHA=`git rev-parse "$1"`;
- 인수를 git 개정으로 변환하고 결과를 변수 SHA
git commit --fixup "$SHA"
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
는 모든 것을 비대화형으로 만듭니다. 이 해킹 은 이 블로그 게시물에서 배웠습니다.어떤 이유로 대화형 편집기가 마음에 들지 않으면 git rebase --onto
사용할 수 있습니다.
Commit1
을 수정하고 싶다고 가정해 봅시다. Commit1
이전 에서 분기합니다.
git checkout -b amending [commit before Commit1]
둘째, cherry-pick
Commit1
을 잡습니다.
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
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" )
용법:
git add
git-amend-old $old_sha
나는 다른 관련 없는 수정을 스쿼시하지 않기 때문에 --autosquash
보다 이것을 좋아합니다.
스크립트를 작성할 정도로 과거 커밋을 자주 수정하는 자신을 발견했습니다.
워크플로는 다음과 같습니다.
git commit-edit <commit-hash>
편집하려는 커밋으로 이동합니다.
커밋을 처음에 원하는 대로 수정하고 스테이징합니다.
(커밋하지 않은 파일을 유지 git stash save
를 사용할 수 있습니다.)
--amend
하여 커밋을 다시 실행합니다. 예:
git commit --amend
리베이스 완료:
git rebase --continue
위의 작업을 수행하려면 아래 스크립트를 $PATH
git-commit-edit
라는 실행 파일에 넣으십시오.
#!/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
가장 좋은 옵션은 "대화형 리베이스 명령"을 사용하는 것 입니다.
git rebase
명령은 매우 강력합니다. 커밋 메시지 를 편집하고 커밋을 결합하고 재정렬할 수 있습니다.커밋을 리베이스할 때마다 내용이 변경되는지 여부에 관계없이 각 커밋에 대해 새로운 SHA가 생성됩니다! 이 명령을 사용할 때는 특히 다른 개발자와 공동 작업하는 경우 심각한 영향을 미칠 수 있으므로 주의해야 합니다. 일부를 리베이스하는 동안 커밋 작업을 시작할 수 있습니다. 커밋을 강제로 푸시하면 동기화되지 않고 나중에 지저분한 상황에서 알 수 있습니다. 그러므로 조심 해주시길 바랍니다!
제어할 수 없는 것을 발견할 때마다 이전 상태로 돌아갈 수 있도록 리베이스하기 전에
backup
분기를 만드는 것이 좋습니다.
git rebase -i <base>
-i
는 "대화형"을 나타 냅니다. 비대화식 모드에서 리베이스를 수행할 수 있습니다. 전:
#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"
와 같이 추가할 수 있습니다. 또는 Google에서 선호하는 코드 편집기를 GIT와 연결하는 방법을 검색할 수 있습니다.
git rebase
예내가 한 마지막 2개의 커밋을 변경하고 싶었기 때문에 다음과 같이 처리했습니다.
#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
이제 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개의 커밋에 대한 커밋 메시지를 변경하고 싶기 때문입니다. pick
대신에 r
또는 reword
를 입력할 것입니다. 그런 다음 파일을 저장하고 탭을 닫습니다. rebase
는 다단계 프로세스로 실행되므로 다음 단계는 메시지를 업데이트하는 것입니다. 또한 커밋은 시간 역순으로 표시되므로 마지막 커밋은 해당 항목에 표시되고 첫 번째 커밋은 첫 번째 줄에 표시되는 식입니다.
메시지 업데이트: 첫 번째 메시지 업데이트:
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. ...
저장하고 닫습니다.
rebase가 끝날 때 다음과 같은 메시지가 표시됩니다. 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
나는 그것이 새로운 사용자를 도울 수 있기를 바랍니다 :).
이 접근 방식에 도달했지만(그리고 아마도 대화형 rebase를 사용하는 것과 정확히 동일할 것입니다) 저에게는 일종의 간단합니다.
참고: 나는 일상적인 대안보다는 할 수 있는 일을 설명하기 위해 이 접근 방식을 제시합니다. 많은 단계가 있기 때문에 (및 일부 주의 사항이 있을 수 있습니다.)
0
을 변경하고 현재 feature-branch
에 있다고 가정합니다.
some-commit---0---1---2---(feature-branch)HEAD
이 커밋을 확인하고 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)
feature-branch
quick-branch
-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 커밋을 복제하지 않습니다(사실 어느 정도인지는 말할 수 없지만).
참고: 모든 커밋 해시는 원래 변경하려고 했던 커밋부터 변경됩니다.
비대화형 명령을 얻으려면 다음 내용이 포함된 스크립트를 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) 내가 원하는 변경 사항으로 새 커밋을 생성하여 ..
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
( 수정 메시지 없이 병합됨) 또는 s
( 커밋 메시지가 포함된 스쿼시 병합은 런타임에 변경될 수 있음)로 교체해야 합니다.
그런 다음 나무를 저장하십시오.
이제 기존 커밋으로 병합이 완료되었습니다.
참고: 자체적으로 유지 관리하지 않는 한 선호하지 않는 방법입니다. 팀 크기가 큰 경우 git 트리를 다시 작성하는 데 허용되지 않는 방법은 다른 사람들이 알고 있는 충돌로 끝날 것입니다. 적은 커밋으로 트리를 깨끗하게 유지하려면 이것을 시도하고 소규모 팀이 아니라면 바람직하지 않습니다.....
나를 위해 리포지토리에서 일부 자격 증명을 제거하기 위한 것이었습니다. 나는 rebase를 시도했고 rebase --continue를 시도할 때 그 과정에서 겉보기에 관련이 없어 보이는 수많은 충돌에 부딪쳤습니다. 자신을 리베이스하려고 시도하지 말고 Mac에서 BFG(brew install bfg)라는 도구를 사용하십시오.
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를 추가합니다.
글쎄, 이 솔루션은 매우 어리석게 들릴지 모르지만 특정 조건에서 당신을 구할 수 있습니다.
내 친구가 실수로 아주 큰 파일 (각각 3GB에서 5GB 사이의 자동 생성된 파일 4개)을 커밋한 다음 git push
가 더 이상 작동하지 않는다는 문제를 깨닫기 전에 그 위에 몇 가지 추가 코드 커밋을 했습니다. !
파일은 .gitignore
에 나열되었지만 컨테이너 폴더의 이름을 바꾼 후 노출되고 커밋되었습니다! 그리고 이제 그 위에 코드에 대한 커밋이 몇 개 더 있었지만 push
는 영원히 실행 중이었고(GB의 데이터를 업로드하려고 시도했습니다!) 마침내 Github의 파일 크기 제한 으로 인해 실패했습니다.
대화형 rebase 또는 이와 유사한 것의 문제는 이러한 거대한 파일을 파헤치는 것을 처리하고 무엇이든 하는 데 영원히 시간이 걸린다는 것입니다. 그럼에도 불구하고 CLI에서 거의 한 시간을 보낸 후 파일(및 델타)이 실제로 기록에서 제거되었는지 아니면 단순히 현재 커밋에 포함되지 않았는지 확신할 수 없었습니다. 푸시도 작동하지 않았고 내 친구는 정말 붙어있었습니다.
그래서 제가 생각해낸 해결책은 다음과 같습니다.
~/Project-old
로 바꿉니다.~/Project
).cp -r
~/Project-old
폴더의 파일을 ~/Project
.mv
.gitignore
제대로 포함되어 있는지 확인하십시오.~/Project
.git
폴더를 이전 폴더로 덮어쓰지 않도록 하십시오. 문제의 역사 기록이 있는 곳!push
'하는 것이 좋습니다.이 솔루션의 가장 큰 문제는 일부 파일을 수동으로 복사하는 작업을 처리하고 모든 최근 커밋을 하나로 병합한다는 것입니다(분명히 새 커밋 해시로). B
가장 큰 장점은 모든 단계에서 매우 명확하고 대용량 파일(민감한 파일 포함)에 잘 작동 하며 기록에 흔적을 남기지 않는다는 것입니다!
출처 : http:www.stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit