- Published on
Interactive Git Rebase
- Authors
- Name
- Eunsu Kim
- @eunsukimme
저번 포스팅에서 rebase 가 무엇이고, 어떻게 활용하는지 알아보았습니다. 간단하게 정리해보자면, 브랜치를 병합하는 merge와는 또 다른 방법으로 각 커밋들의 변경 사항들을 차례대로 basebranch
에 반영하여 결과적으로 선형적인 커밋 히스토리를 만들어 내는 기능이었습니다. 이번 포스팅에서는 이러한 작업을 대화형(interactive) 인터페이스를 제공함으로써 좀 더 정교한 작업을 가능하게 해주는 -i
옵션에 대해 알아보도록 합시다.
git rebase -i
git-scm에서는 -i
옵션에 대해 다음과 같이 설명하고 있습니다.
Make a list of the commits which are about to be rebased. Let the user edit that list before rebasing. This mode can also be used to split commits
직역하면 rebase 될 커밋들의 리스트를 사용자가 편집할 수 있게 해 준다는 뜻입니다. 아직 그 개념이 잘 잡히지 않는데, 다음과 같은 Git 환경을 만들고 실습하면서 더 자세히 알아보도록 해봅시다. 현재 Git 프로젝트의 커밋 히스토리가 다음과 같다고 가정해보죠.

현재 커밋 히스토리. Add apple, Add banana, Add cherry 를 순서대로 추가한 모습.
이제 이 커밋 리스트를 rebase 해보도록 해봅시다. 이때 rebase 할 커밋의 범위를 지정해야 하는데, 이를 일반화한 명령은 다음과 같습니다.
git rebase -i <작업할 커밋의 직전커밋>
이 말의 의미는 git rebase -i
의 파라미터로 주어진 커밋의 바로 다음 커밋부터 HEAD까지 rebase를 진행한다는 뜻입니다. 만약 HEAD~3
을 파라미터로 준다면 HEAD~2
, HEAD~1
, HEAD
를 rebase 하겠다는 의미입니다. HEAD~3
을 파라미터로 준 결과 다음과 같이 편집기가 실행됩니다.

편집기의 상단에 rebase할 커밋들의 리스트가 나타납니다. 아래에는 주석으로 몇 가지 명령어들이 나열돼 있는데, 이 명령들을 활용하여 커밋들을 자신의 입맛대로 rebase 할 수 있습니다. 지금부터 각 명령어들의 기능이 무엇인지 알아보도록 해봅시다.
pick
pick
은 고른다, 선택한다는 의미로 해당 커밋을 히스토리에 반영하겠다는 말인데요. 현재 기본적으로 모든 커밋들이 pick 된 상태로 히스토리에 존재하고 있습니다.

이 상태에서 커밋의 순서를 바꿀 수도 있고, 해쉬값을 이용하여 커밋을 가져올 수도 있습니다. 2번째(Add banana
)와 3번째(Add cherry
) 커밋 순서를 서로 바꿔보도록 해볼까요?

저장하고 종료한 뒤 로그를 살펴보면 변경된 내용으로 커밋 히스토리가 작성되어 있는 걸 확인할 수 있습니다.

여기서 주목할 것은, 맨 처음 커밋 히스토리와 비교해서 보면 Add banana
와 Add cherry
의 커밋 해쉬값이 둘 다 변경된 것을 확인할 수 있습니다. 이는 내용은 같지만 완전히 새로운 커밋이란 걸 의미합니다.
앞에서 커밋의 해쉬값을 이용해 커밋을 가져올 수 있다고 하였는데, 이를 테스트하기 위해 새로운 커밋을 작성하도록 해보겠습니다. Add dragonFruit
이란 커밋을 새롭게 생성해 보았습니다.

새롭게 Add dragonFruit 커밋이 추가된 커밋 히스토리
지금 제가 하고자 하는 것은 방금 생성한 커밋을 git reset
명령으로 되돌린 뒤 rebase의 작업 목록에 해당 커밋 해쉬값을 가져와서 히스토리에 추가하는 것입니다. 아래 명령을 실행해서 방금 추가한 커밋을 삭제해주세요.
git reset --hard HEAD~
로그를 다시 확인해보면 방금 생성한 커밋이 삭제된 걸 확인할 수 있습니다. 이제 git rebase -i HEAD~3
명령으로 편집창을 실행시켜 봅시다. 조금 전에 생성한 커밋의 해쉬값은 d3816e0
이므로 이를 pick 하는 줄을 추가해줍시다(해쉬값이 기억이 안 난다면 git reflog
명령으로 확인할 수 있습니다). 저는 Add banana
와 Add cherry
커밋 사이에 추가해보았습니다.

이렇게 rebase 작업 목록을 설정하고 저장한 뒤 커밋 히스토리를 확인해보면 삭제했던 커밋이 히스토리에 추가된 것을 확인할 수 있습니다. 또한 커밋 해쉬값 역시 변경된 것을 확인할 수 있죠.

reword
reword
는 커밋의 메시지를 변경하는 명령입니다. 메시지를 변경할 커밋의 해쉬값 앞에 reword
키워드를 적어주면 해당 커밋의 메시지를 다시 작성하는 창이 열립니다. 여기서는 Add apple
커밋의 커밋 메시지를 Add apple^.^
로 변경해보도록 하겠습니다.

Add apple의 커밋의 rebase 명령어를 reword 로 바꾼 모습
저장하고 종료하면 Add apple
커밋의 메시지를 변경할 수 있는 편집창이 열립니다. 메시지를 변경하고 저장하면 커밋 히스토리에서 변경된 커밋 메시지를 확인할 수 있습니다.

단순히 HEAD 커밋의 메시지를 변경하고 싶으면 git commit --amend
명령을 사용해도 됩니다.
edit
reword
가 메시지만 변경하는 작업이었다면 edit
은 커밋의 작업한 내용까지 변경할 수 있는 명령입니다. 여기서 저는 git rebase -i HEAD~4
명령을 실행한 후 dragonFruit 커밋의 명령을 edit
으로 바꿔주었습니다.

저장하고 종료하면 해당 커밋으로 checkout 되고, 작업을 수행할 수 있게 됩니다. 저는 dragonFruit.txt를 삭제하고 donut.txt를 추가해 주었습니다.

dragonFruit.txt 를 삭제하고 donut.txt 를 추가한 상태
그리고 다음 명령을 실행하여 커밋을 완료해줍시다.
git add .
git commit --amend
커밋 메시지도 변경하고 싶다면 마지막 명령 이후에 열리는 편집창에서 변경하면 됩니다. 저는 Add donut
으로 커밋 메시지를 변경해 주었습니다. rebase를 계속하려면 다음 명령을 실행해주면 됩니다.
git rebase --continue
마지막으로 커밋 히스토리를 확인해보면 다음과 같이 Add donut
커밋이 Add dragonFruit
대신 히스토리에 나타나는 것을 확인할 수 있습니다.

Add dragonFruit 커밋이 Add donut 커밋으로 변경된 커밋 히스토리
squash
squash
는 해당 커밋을 이전의 커밋과 하나로 합치는 명령입니다. 즉, 여기서 Add donut
커밋에 squash
명령을 적어주면 이전 커밋인 Add cherry
와 하나로 합쳐집니다.

저장하고 종료하면 커밋 메시지를 변경할 수 있는 편집창이 나타납니다. 그대로 저장하면 기존의 메시지가 그대로 하나의 커밋으로 히스토리에 나타나는 것을 확인할 수 있습니다.

Add cherry와 Add donut의 커밋이 합쳐진 커밋 히스토리
fixup
fixup
은 squash
와 동일하게 이전 커밋과 합치는 명령입니다. squash
와 다른 점은 메시지는 합치지 않는다는 것으로, 이전 커밋 메시지만 히스토리에 남게 됩니다. 이 점을 제외하곤 squash
와 동일합니다.
exec
exec
명령은 각 커밋이 rebase 된 후 실행될 쉘 명령을 지정할 수 있습니다. 이해를 돕기 위해 저는 다음과 같이 각 커밋 리스트 사이에 exec
명령을 추가해서 커밋의 해쉬값과 메시지를 콘솔에 출력하도록 하였습니다.

저장하고 종료하면 다음과 같은 결과를 출력합니다.

drop
drop
은 커밋 히스토리에서 해당 커밋을 삭제하는 명령입니다. 필자와 같이 편집창에서 커밋 앞에 drop
키워드를 적어주면 해당 커밋은 rebase 된 히스토리에 포함되지 않습니다.

저장하고 종료한 뒤 커밋 히스토리를 보면 해당 커밋이 삭제된 걸 확인할 수 있습니다.

사실 커밋 리스트 편집창에서 특정 커밋이 위치한 줄을 지워줘도 같은 기능을 하게 됩니다.
Recap
지금까지 git rebase -i
를 활용하는 방법과 관련된 명령어들을 알아보았습니다. 이번 포스팅에서 배운 내용을 정리해보죠.
> git rebase -i # rebase 될 커밋 리스트를 직접 편집할 수 있게 해주는 명령입니다.
> git rebase -i commit_sha # 주어진 해쉬값의 커밋 이후부터 HEAD까지 rebase 합니다.
> git commit --amend # HEAD 커밋을 수정합니다. 또한 rebase 중 커밋의 편집을 완료합니다.
> git rebase --continue` # 커밋을 편집한 뒤 rebase를 계속합니다.
또한 커밋 리스트를 편집하는 여러 rebase 명령어들을 알아보았는데, 각 명령어들의 기능은 다음과 같습니다.
pick
- 해당 커밋을 히스토리에 넣습니다.reword
- 해당 커밋의 메시지를 변경합니다.edit
- 해당 커밋의 메시지와 작업 내용을 변경합니다.squash
- 해당 커밋을 이전 커밋과 하나로 합칩니다.fixup
- 해당 커밋을 이전 커밋과 하나로 합칩니다. 단, 메시지는 이전 커밋의 메시지로 합쳐집니다.exec
- 쉘 명령을 실행합니다.drop
- 커밋을 히스토리에서 삭제합니다.
처음에는 rebase의 개념이 잘 잡히지 않고 사용하기도 꺼려지지만 조금만 연습해보면 브랜치를 관리할 때 정말 유용하게 사용할 수 있단 것을 알게 됩니다. 로컬에서 작업하고 커밋 히스토리를 정리하고 싶을 때 rebase가 유용하게 쓰이고, -i
옵션을 이용해서 원하는 작업을 수행할 수 있을 것입니다.