- 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 프로젝트의 커밋 히스토리가 다음과 같다고 가정해보죠.
이제 이 커밋 리스트를 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
이란 커밋을 새롭게 생성해 보았습니다.
지금 제가 하고자 하는 것은 방금 생성한 커밋을 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
커밋의 메시지를 변경할 수 있는 편집창이 열립니다. 메시지를 변경하고 저장하면 커밋 히스토리에서 변경된 커밋 메시지를 확인할 수 있습니다.
단순히 HEAD 커밋의 메시지를 변경하고 싶으면 git commit --amend
명령을 사용해도 됩니다.
edit
reword
가 메시지만 변경하는 작업이었다면 edit
은 커밋의 작업한 내용까지 변경할 수 있는 명령입니다. 여기서 저는 git rebase -i HEAD~4
명령을 실행한 후 dragonFruit 커밋의 명령을 edit
으로 바꿔주었습니다.
저장하고 종료하면 해당 커밋으로 checkout 되고, 작업을 수행할 수 있게 됩니다. 저는 dragonFruit.txt를 삭제하고 donut.txt를 추가해 주었습니다.
그리고 다음 명령을 실행하여 커밋을 완료해줍시다.
git add .
git commit --amend
커밋 메시지도 변경하고 싶다면 마지막 명령 이후에 열리는 편집창에서 변경하면 됩니다. 저는 Add donut
으로 커밋 메시지를 변경해 주었습니다. rebase를 계속하려면 다음 명령을 실행해주면 됩니다.
git rebase --continue
마지막으로 커밋 히스토리를 확인해보면 다음과 같이 Add donut
커밋이 Add dragonFruit
대신 히스토리에 나타나는 것을 확인할 수 있습니다.
squash
squash
는 해당 커밋을 이전의 커밋과 하나로 합치는 명령입니다. 즉, 여기서 Add donut
커밋에 squash
명령을 적어주면 이전 커밋인 Add cherry
와 하나로 합쳐집니다.
저장하고 종료하면 커밋 메시지를 변경할 수 있는 편집창이 나타납니다. 그대로 저장하면 기존의 메시지가 그대로 하나의 커밋으로 히스토리에 나타나는 것을 확인할 수 있습니다.
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
옵션을 이용해서 원하는 작업을 수행할 수 있을 것입니다.