대화형 리베이스 (Interactive Rebasing)
대화형 리베이스는 Git 에서 가장 강력하면서도 복잡한 기능 중 하나입니다. 이를 통해 커밋 히스토리를 매우 유연하게 재작성할 수 있습니다. 커밋 순서를 바꾸거나, 여러 커밋을 하나로 합치고 (squash), 메시지를 수정하거나, 아예 삭제할 수도 있습니다. 마치 커밋 히스토리를 위한 정교한 편집기를 사용하는 것과 같습니다.
경고: 대화형 리베이스는 히스토리를 재작성하므로, 특히 공유 브랜치에서 사용할 때는 위험할 수 있습니다. 이미 공유 저장소에 푸시된 커밋은 리베이스하지 마세요. 리베이스는 작업을 공유하기 전, 자신의 로컬 커밋들을 깔끔하게 정리할 때 사용하는 것이 가장 안전하고 적절합니다.
리베이스의 효과를 확인하기 위해 연속된 커밋들을 몇 개 만들어 봅시다.
echo "First change" >> hello.txt
git commit -am "First change"
echo "Second change" >> hello.txt
git commit -am "Second change"
echo "Third change" >> hello.txt
git commit -am "Third change"
이 명령어들은 "hello.txt"에 한 줄씩 추가하며 세 개의 커밋을 빠르게 생성합니다. git commit -am에서 -am은 수정되거나 삭제된 파일을 자동으로 스테이징 (-a) 하고 메시지를 추가 (-m) 하는 단축 명령어입니다. 이미 추적 중인 파일의 변경 사항을 바로 커밋할 때 편리합니다.
이제 이 세 개의 커밋을 정리해 봅시다. "First change"와 "Second change"는 사실 하나의 논리적인 작업이므로 하나로 합치고 싶고, "Third change"의 메시지는 좀 더 구체적으로 바꾸고 싶다고 가정해 봅시다. 대화형 리베이스가 이 작업에 딱 제격입니다.
다음 명령어로 대화형 리베이스 세션을 시작합니다.
git rebase -i HEAD~3
명령어의 의미는 다음과 같습니다.
git rebase -i: 리베이스를 실행하되 -i(interactive) 플래그를 통해 대화형 모드로 진행합니다.
HEAD~3: 리베이스할 커밋의 범위를 지정합니다. HEAD로부터 뒤로 세 번째 커밋까지, 즉 최근 3 개의 커밋을 선택합니다. 특정 커밋 해시를 입력하여 그 지점부터 리베이스를 시작할 수도 있습니다.
이 명령어를 실행하면 기본 텍스트 편집기가 열리며 최근 3 개의 커밋 목록이 나타납니다. 이 화면에서 Git 에게 히스토리를 어떻게 수정할지 지시하게 됩니다. 다음과 유사한 내용이 보일 것입니다 (해시는 다를 수 있습니다).
1 pick 63c95db First change
2 pick 68e7909 Second change
3 pick 5371424 Third change
4
5 ## Rebase 3bf348d..5371424 onto 3bf348d (3 commands)
6 #
7 ## Commands:
8 ## p, pick <commit> = use commit
9 ## r, reword <commit> = use commit, but edit the commit message
10 ## e, edit <commit> = use commit, but stop for amending
11 ## s, squash <commit> = use commit, but meld into previous commit
12 ## f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
13 ## commit's log message, unless -C is used, in which case
14 ## keep only this commit's message; -c is same as -C but
15 ## opens the editor
16 ## x, exec <command> = run command (the rest of the line) using shell
17 ## b, break = stop here (continue rebase later with 'git rebase --continue')
18 ## d, drop <commit> = remove commit
19 ## l, label <label> = label current HEAD with a name
20 ## t, reset <label> = reset HEAD to a label
21 ## m, merge [-C <commit> | -c <commit>] <label> [## <oneline>]
22 ## . create a merge commit using the original merge commit's
23 ## . message (or the oneline, if no original merge commit was
24 ## . specified); use -c <commit> to reword the commit message
25 #
26 ## These lines can be re-ordered; they are executed from top to bottom.
27 #
28 ## If you remove a line here THAT COMMIT WILL BE LOST.
29 #
30 ## However, if you remove everything, the rebase will be aborted.
대화형 리베이스 편집기 이해하기:
- 커밋 목록: 상단에는 리베이스 대상 커밋들이 나열됩니다. 기본적으로 오래된 순서대로 (위에서 아래로) 정렬되어 있습니다.
- 명령어 (Commands): 커밋 목록 아래에는 각 커밋에 사용할 수 있는 명령어 설명이 있습니다. 자주 쓰이는 것들은 다음과 같습니다.
pick (또는 p): 커밋을 그대로 사용합니다. 기본값입니다.
reword (또는 r): 커밋을 사용하되, 커밋 메시지만 수정합니다.
edit (또는 e): 커밋을 사용하되, 리베이스 도중 멈춰서 파일 내용을 수정하거나 추가 작업을 할 수 있게 합니다.
squash (또는 s): 이 커밋을 이전 (위) 커밋과 합칩니다. 두 커밋 메시지도 하나로 합쳐집니다.
fixup (또는 f): squash와 비슷하지만, 이 커밋의 메시지는 버리고 이전 커밋의 메시지만 유지합니다.
drop (또는 d): 이 커밋을 히스토리에서 완전히 삭제합니다.
우리의 리베이스 계획:
우리는 다음을 수행할 것입니다.
- "Second change"를 "First change"에 합치기 (squash)
- "Third change"의 메시지를 더 좋게 수정하기 (reword)
이를 위해 편집기 내용을 다음과 같이 수정하세요.
pick abc1234 First change
squash def5678 Second change
reword ghi9101 Third change
주의: 편집기 내의 커밋 해시 값은 건드리지 마세요.
Vim 편집기 사용법 (Vim 이 열린 경우):
- 입력 모드 전환:
i 키를 누릅니다. 이제 텍스트를 수정할 수 있습니다.
- 내용 수정: 화살표 키로 이동하여 "pick"을 "squash"와 "reword"로 바꿉니다.
- 입력 모드 종료:
Esc 키를 누릅니다.
- 저장 및 종료:
:wq를 입력하고 Enter를 누릅니다.
편집기를 저장하고 닫으면 Git 이 지시사항에 따라 리베이스를 시작합니다.
첫 번째 단계: 커밋 합치기 (Squash)
Git 은 먼저 squash 명령을 처리합니다. "Second change"의 변경 사항을 "First change"에 통합한 뒤, 합쳐진 커밋의 메시지를 작성할 수 있도록 편집기를 다시 엽니다. 다음과 같은 내용이 보일 것입니다.
## This is a combination of 2 commits.
## This is the 1st commit message:
First change
## This is the 2nd commit message:
Second change
## Please enter the commit message for your changes. Lines starting
## with '#' will be ignored, and an empty message aborts the commit.
...
기존의 두 메시지가 모두 보입니다. 이를 지우고 하나의 일관된 메시지로 수정하세요. 예를 들어 다음과 같이 바꿀 수 있습니다.
Combined first and second changes: Initial setup of hello.txt
수정 후 저장하고 닫습니다.
두 번째 단계: 메시지 수정 (Reword)
다음으로 "Third change"에 대한 reword 명령을 처리합니다. 편집기가 또 한 번 열리며 원래 메시지가 나타납니다.
Third change
...
이를 더 설명적인 메시지로 수정합니다.
Improved third change: Added a more descriptive line to hello.txt
저장하고 닫습니다.
리베이스 완료:
모든 지시사항이 처리되면 Git 은 리베이스를 마무리합니다. 보통 "Successfully rebased and updated refs/heads/master."라는 메시지가 출력됩니다.
리베이스된 히스토리 확인:
이제 커밋 로그를 다시 확인해 봅시다.
git log --oneline
다음과 유사한 히스토리가 보일 것입니다.
<commit_hash_third_revised> Improved third change: Added a more descriptive line to hello.txt
<commit_hash_combined> Combined first and second changes: Initial setup of hello.txt
<commit_hash_cherry_pick> Add new feature
<commit_hash_revert> Revert "Add line to be reverted"
<commit_hash_original_reverted> Add line to be reverted
<commit_hash_initial_amended> Initial commit with important note
확인할 점:
- 커밋 개수가 줄어들었습니다 (두 개를 하나로 합쳤기 때문).
- "First change"와 "Second change"가 합쳐져 새로운 메시지를 갖게 되었습니다.
- "Third change"의 메시지가 수정되었습니다.
- 히스토리를 재작성했으므로 리베이스된 커밋들의 해시 값이 모두 바뀌었습니다.
대화형 리베이스 핵심 요약:
- 로컬 정리 도구: 대화형 리베이스는 주로 로컬 커밋 히스토리를 깔끔하게 다듬는 용도입니다.
- 공유 브랜치 주의: 이미 푸시된 브랜치는 리베이스하지 마세요. 팀원들에게 큰 혼란을 줄 수 있습니다.
git rebase --abort: 리베이스 도중 실수를 했거나, 충돌 해결이 어렵거나, 상황이 꼬였다면 언제든지 이 명령어로 리베이스를 취소하고 시작 전 상태로 안전하게 돌아갈 수 있습니다.