main에 다른 브랜치를 병합할 때 쓰이는 방법이 두가지인데, 하나는
merge
가 있고, 다른 하나는 rebase
가 있다.
여기서는 rebase
에 대해서 공부하며 글을 써보도록 하겠다.
git을 사용하면서 커밋을 할때 지난 커밋을 취소하거나 수정하고 싶을 때도 있고, 잘못된 브랜치에서 커밋해서 다른 브랜치로 커밋을 옮기고 싶은 경우도 있을 것이다.
이와 같이 이미 해버린 커밋을 수정하는 데에 유용하게 사용되는 커맨드가 바로 git rebase
이다.
git rebase
는 이전 커밋을 수정하고, 커밋을 다른 브랜치로 옮기는 일을 할 수 있다. 또 지저분한 커밋이력을 남기고싶지않고, 하나의 커밋으로 깔끔하게 만들고 싶을때 사용할 수 있다.
그렇기 때문에 협업을 할 때에는 반드시 git rebase
를 알아야 하고 , 또 git rebase
를 잘 쓸 수 있어야 비로소 git의 기능을 잘 활용할수 있다고 생각한다.
예를 들어 A라는 프로젝트를 clone받아서 수정을 하게 된다면
main에서 직접 수정하지 않고 브랜치를 분리해서 작업을 하고나서 커밋하기전에 git rebase
를 하고 푸시를 하는 방식을 취할 수 있다.
이렇게 할 경우 같은 내역을 다른 커미터가 먼저 커밋했을때 해당 커밋을 유지시키고 자신이 수정한 커밋은 생략되어서 히스토리를 깔끔하게 유지할 수 있고 불 필요한 merge commit 같은 것이 남지 않게된다.
merge와 rebase를 했을 때 가장 큰 차이는 바로 "commit의 깔끔함" 이다.
쉽게 이해하기 위해서 아래 이미지를 참고해보자.
merge를 사용하면 모든 commit을 남기게 되지만, rebase를 이용하면 필요없는 commit을 생략시키기 때문에 master 브랜치에 commit은 항상 깔끔하게된다.
만약 feature/test라는 branch를 만들어서 열심히 작업을 하고 master에 merge를 사용해서 병합한다고하면, feature/test에서 기록한 모든 commit이 master의 commit으로 기록된다.
rebase 방식을 사용해서 병합한다면, 내 작업하면서 남겼던 commit 중 불필요한 것들은 생략시키고 필요한 commit만 남겨서 master에 병합하기 때문에 master의 commit은 항상 깔끔하게 관리된다는 장점이 있다.
똑같은 병합이지만, 나중에 master의 commit을 볼 때 깔끔하게 볼 수 있어서 여러 명이 협업할 때 유용하다.
main > git checkout -b feature/signup
feature/test > git add .
feature/test > git commit -m '[ADD]: Sign up 기능구현'
feature/test > git add .
feature/test > git commit -m 'wip' # WIP는 work in progress 의 약자로 작업중이라는 뜻이다
feature/test > git add .
feature/test > git commit -m '[Modify]: bug fix'
git rebase
로 불필요한 commit을 squash 해준다.feature/test > git rebase -i main
pick f7f3f6d [ADD]: Sign up 기능구현
pick 310154e wip // 여기서 pick을 s로 바꿔준다.
pick a5f4a0d [Modify]: bug fix // 여기서 pick을 s로 바꿔준다.
# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
pick f7f3f6d [ADD]: Sign up 기능구현
s 310154e wip
s a5f4a0d [Modify]: bug fix
# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
# This is a combination of 3 commits.//이 위치에서 dd를 눌러 줄을 삭제한다.
# The first commit's message is: //이 위치에서 dd를 눌러 줄을 삭제한다.
[ADD]: Sign up 기능구현 //이 위치에서 dd를 눌러 줄을 삭제한다.
# This is the 2nd commit message: //이 위치에서 dd를 눌러 줄을 삭제한다.
wip //이 위치에서 dd를 눌러 줄을 삭제한다.
# This is the 3rd commit message: //이 위치에서 dd를 눌러 줄을 삭제한다.
[Modify]: bug fix //나는 이 commit만 남기고 싶으므로 이건 냅둔다.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress;
[Modify]: bug fix
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress;
이렇게 git rebase
를 하면 여러 커밋을 남겼어도 꼭 필요한 커밋만 남길 수 있다. rebase를 잘 성공시킨 후 main브랜치의 커밋기록을 보면 선형방식으로 깔끔하게 한줄로 정리되어있는것을 볼 수 있다.
주의할 점
(참고: conflict는 commit과 commit 사이에서 일어나는 작업 내용 사이의 충돌을 말한다.)
새로운 작업을 모두 마치고 push 하기 전에는
Main branch로 이동하여 remote main을 pull 받는다.
내가 push할 Feature branch 로 이동한다.
git rebase -i main
를 진행한다.
Rebase 하는 동안 squash 진행할 때에는
가장 오래된 commit을 pick 한다.
다른 커밋메세지는 가장 오래된 commit을 기준으로 squash 한다.
squash를 한다고 해서 다른 커밋의 작업 내역이 없어지는 것이 아니다.
ESC 를 누른다음 :wq! 로 vi에서 빠져나온다.
빠져나오게되면 수정용 vi에디터가 하나 더 나타난다.
수정용 에디터에서의 작업
수정용 vi에디터는 최종적으로 이 rebase된 커밋의 내용을 작성하는 부분이다.
현재까지 적은 커밋메세지가 전부 나타난다.
불 필요한 내용은 제거하고, 현재 수정 내역에 대한 커밋메세지를 정성껏 작성한다.
ESC 를 누른다음 :wq! 저장하고 에디터에서 빠져나온다.
성공했다면 Successfully rebased
라는 문장을 터미널에서 볼 수 있다. 이후 push 하면된다!
rebase 이후 push 하기
rebase는 commit history를 정리하는 역할을 한다.
같은 브랜치에서 rebase를 할 때 마다 history가 달라질 수 있다.
수정 사항이 추가로 생긴 후 다시 rebase하면 history가 무조건 달라진다.
git은 history가 다른 branch의 push를 허용하지 않는다.
git push origin feature/signup -f
-f 의 옵션을 사용하여 force push를 진행한다.
rebase 중 충돌 해결하기
충돌이 일어난 경우 rebase가 진행되지도, 끝나지도 않고 도중에 멈춰있어, 당혹스러울때가 있다.
터미널이 아래와 같은 메세지로 rebase가 진행중임을 알려주니 너무 겁먹지는 않도록 하자!
충돌은 충돌일 뿐, 해당하는 코드를 수정 후 git add .
한다.
git commit
은 하지않는다.(수정 사항이 없으니까)
git rebase --continue
를 진행하면 멈춰있던 리베이스가 다시 진행된다.
충돌이 여러번 나면 그 때마다 충돌을 해결하고
git add .
/ git rebase --continue
를 반복한다.
계속 해결이 안될경우, git rebase -- abort
로 아예 rebase를 진행 하기 전 상황으로 돌아갈 수도 있다.(하지만 이 명령어를 사용할 때는 작업하던 내용들이 모두 사라지며 돌아갈 수도 있으니 확실하게 알아보고 사용하자.일종의 컴퓨터가 고장났을때 복원시키는방법과 유사한 느낌이다.)
rebase에 공부하고 복습하는 차원에서 이 글을 작성하긴 하였지만,
무조건 merge 보다 rebase를 사용해야한다고는 생각하진 않는다.
rebase를 사용하는것이 더 적절한 상황에서는 rebase를 사용하고,
merge를 사용하는것이 더 적절한 상황에서는 merge를 사용하는것이다 맞다고 생각한다.
rebase는 무엇이고 어떤역할을 하는지, 어떤 상황에서 써야하는지,
merge는 무엇이고 어떤역할을 하는지, 어떤 상황에서 써야하는지,
각각의 쓰임새를 정확하게 파악하여 알고 적절한 상황에서 알맞게 쓰는 것이 중요하다고 생각한다.
아래 글은 이글을 쓸 때 참고한 git-scm에서 나온 내용이다.
더 자세한 내용은 이글을 참고하길 권장한다.
. Reference
https://git-scm.com/book/ko/v2/Git-%EB%B8%8C%EB%9E%9C%EC%B9%98-Rebase-%ED%95%98%EA%B8%B0
https://flyingsquirrel.medium.com/git-rebase-%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-ce6816fa859d