정찬명

git 커밋 합치기, 순서 바꾸기, 삭제하기

하나의 이슈를 해결하는데 지저분하게 여러 번 커밋을 하게 됐다. 연결된 또는 분리된 여러 커밋을 하나의 커밋으로 정리하는 방법을 설명한다.

인접한 2개 커밋을 하나로 합치기

마지막 3개의 커밋 중 1번, 2번 커밋이 같은 이슈를 해결하기 위한 커밋이라서 1번 커밋으로 합치려 한다. 3번 커밋은 내 커밋이 아니다. vi 편집기에서 아래와 같이 대화형(-i, interactive) 리베이스를 시도한다.

$ git rebase -i HEAD~3

아래와 같이 편집 가능한 커밋 목록을 볼 수 있다.

pick 9c97a74f 1번 커밋(내 커밋)
pick 895bf53d 2번 커밋(내 커밋)
pick 56c91403 3번 커밋(동료 커밋)

3번 커밋은 동료의 커밋이므로 유지한다. 2번 커밋을 1번 커밋으로 합치려고 한다. i 명령으로 편집 모드에 진입하고 2번 커밋의 pickfixup으로 고친 다음 :wq 명령으로 저장한다.(편집을 취소하고 빠져나오려면 :q! 명령을 사용)

pick 9c97a74f 1번 커밋(내 커밋)
fixup 895bf53d 2번 커밋(내 커밋)
pick 56c91403 3번 커밋(동료 커밋)

2번 커밋은 1번 커밋으로 합쳐지고 1번 커밋 로그만 남는다. 만약 이미 푸시한 커밋이라면 아래와 같이 강제 푸시를 시도할 수 있다. 이 경우 동료들은 원격 저장소로부터 갱신된 트리를 받아와야 한다.

$ git push -f

git 가이드라인에는 “이미 공개 저장소에 Push한 커밋을 Rebase 하지 마라“는 금기 사항이 있다. 리베이스한 커밋을 강제로 푸시할 때에는 동료들과 사전 합의가 필요하다.

동료가 리베이스한 커밋을 푸시했다면 갱신된 트리를 받아오기 위해 현재 작업 중인 내용을 임시 저장한다. 그리고 오리진 기준으로 브랜치를 초기화한 다음 내 작업 내용을 복원한다. stash 명령은 작업 내용이 스테이지에 있는지 여부와 무관하게 모든 변경 사항을 저장하고 복원해 준다.

$ git stash
$ git reset --hard origin/xxx
$ git stash apply

떨어진 2개 커밋을 하나로 합치기

마지막 3개의 커밋 중 1번, 3번 커밋이 같은 이슈를 해결하기 위한 커밋이라서 1번 커밋으로 합치려 한다. 2번 커밋은 내 커밋이 아니다. vi 편집기에서 아래와 같이 대화형(-i, interactive) 리베이스를 시도한다.

$ git rebase -i HEAD~3

아래와 같이 편집 가능한 커밋 목록을 볼 수 있다.

pick 9c97a74f 1번 커밋(내 커밋)
pick 895bf53d 2번 커밋(동료 커밋)
pick 56c91403 3번 커밋(내 커밋)

2번 커밋은 동료의 커밋이므로 유지해야 한다. 3번 커밋을 1번 커밋으로 합치려고 한다. 편집 모드 진입 전 yy 명령으로 3번 행을 복사한 다음 dd 명령으로 삭제한다. 1번 행에 커서를 옮겨 놓고 p 명령으로 다시 삽입한다. 이렇게 하면 3번 커밋의 위치가 1번 커밋 다음으로 이동한다. i 명령으로 편집 모드에 진입하고 3번 커밋의 pickfixup으로 고친 다음 :wq 명령으로 저장한다.(편집을 취소하고 빠져나오려면 :q! 명령을 사용)

pick 9c97a74f 1번 커밋(내 커밋)
fixup 56c91403 3번 커밋(내 커밋)
pick 895bf53d 2번 커밋(동료 커밋)

3번 커밋은 1번 커밋으로 합쳐지고 1번 커밋 로그만 남는다. 만약 이미 푸시한 커밋이라면 아래와 같이 강제 푸시를 시도할 수 있다. 이 경우 동료들은 원격 저장소로부터 갱신된 트리를 받아와야 한다.

$ git push -f

git 가이드라인에는 “이미 공개 저장소에 Push한 커밋을 Rebase 하지 마라“는 금기 사항이 있다. 리베이스한 커밋을 강제로 푸시할 때에는 동료들과 사전 합의가 필요하다.

동료가 리베이스한 커밋을 푸시했다면 갱신된 트리를 받아오기 위해 현재 작업 중인 내용을 임시 저장한다. 그리고 오리진 기준으로 브랜치를 초기화한 다음 내 작업 내용을 복원한다. stash 명령은 작업 내용이 스테이지에 있는지 여부와 무관하게 모든 변경 사항을 저장하고 복원해 준다.

$ git stash
$ git reset --hard origin/xxx
$ git stash apply

fixup 대신 squash를 사용하는 경우

fixup은 합치는 커밋의 로그 메시지를 제거하여 남기지 않는다. 한편 squash는 합치는 커밋의 로그 메시지를 커밋 로그에 그대로 남기거나 편집할 수 있다. squash로 제거한 커밋의 메시지는 이전 커밋에 포함한다.

revert 대신 커밋을 삭제하기

인터렉티브 리베이스 과정에서 편집 모드에 진입하기 전 행 삭제 명령 dd를 이용하면 해당 커밋을 삭제하는 것도 가능하다.

Git 도구 - 히스토리 단장하기, Git 도구 - Stashing과 Cleaning 문서를 참고했다.