프로젝트를 진행하던 중 git rebase 시 중복된 커밋이 존재 하는 상황을 맞이하였고, 이 원인과 해결하는 방법을 설명하고자 합니다.
다수의 인원이 동시에 개발하다보면 종종 현재 개발중인 특정 기능들이 필요한 상황이 발생하곤 합니다.
저희 프로젝트의 경우 Controller
구현 중, 이전 commit
의 취약점을 발견해 Service
, Persistence
계층의 규약 (interface)
이 변경되는 상황이 있었습니다.
저는 이 때 rebase
를 유용하게 사용한다 들었습니다. 현 브랜치의 base commit
을 변경해 필요한 commit
위에 현 브랜치를 다시 세울 수 있기 때문입니다.
그래서 rebase
를 진행했지만 아래 그림처럼 예상치 못한 상황을 맞이하였습니다.
위 그림을 보면 필요한 commit
들 (초록색)
이 포함되긴 했지만, rebase
이전의 commit
들 (빨간색)
이 중복된 것을 알 수 있습니다.
그래서 제가 rebase
를 잘 못 알고있는 건가 헷갈렸고 위 현상의 원인을 분석하였습니다.
결론부터 말하자면 rebase
의 개념을 잘 못 알고 있는 것은 아니었습니다.
다만 Local
에서의 rebase
가 반드시 remote
에서의 rebase
로 이뤄지는 것은 아니었습니다.
(한마디로 Local 과 Remote 는 "다른 것" 임을 까먹었던 것...)
다음과 같은 상황을 생각해 보겠습니다.
저희는 개발하며 commit
a1, 2, 3
를 생성해 remote
에 push
하였습니다. 그런데 작업 도중 다른 브랜치의 T1, T2
가 필요함을 느껴 Another Branch
에 rebase
를 하고자 합니다.
우리가 rebase
를 진행할 때는 대게 git bash
나 IDE
의 도움을 받습니다.
그런데 이들의 동작 그 자체는 remote branch
에 적용되지 않고 오직 local branch
에서만 작용합니다.
즉, 우리가 개발하며 remote branch
에 영향을 주는 행동은 commit
후 push
할 때 뿐입니다.
그래서 Local
을 Another Branch
위에 rebase
하면 Local
은 위 그림처럼 "새로운 형태"
를 갖게 됩니다.
문제는 Local
의 A1, A2, A3
commit
은 기존 a1, a2, a3
와 다른 commit
이라는 점 입니다.
commit
의 식별자로 사용되는 SHA
는 commit
생성 시각, 변경점, 생성자 등 다양한 정보를 포함해 생성됩니다.
즉, A1, ...
과 a1, ...
은 모두 같은 변경점을 담고 있지만 서로 다른 commit
들 인 것입니다.
이 점이 문제되는 원인입니다. 확실히 Local
에서는 rebase
로 a1, a2, a3
가 사라지고 A1, A2, A3
가 새로 만들어지는 것은 어떠한 문제가 되지 않습니다.
하지만 Remote
는 어떨까요?
Remote
는 Local
에서 push
시에만 변경되니 Remote
에는 기존 커밋 a1, a2, a3
가 그대로 남아 있습니다.
때문에 Local
을 rebase
후 push
를 진행하면 Remote
위 그림의 형태를 갖게 됩니다.
즉, 우리 눈에는 중복된 commit
으로 보였지만, Remote
입장 에서는 확연히 다른 commit
들이었던 것 입니다.
결국 문제의 원인은 a1, a2, a3
와 A1, A2, A3
가 다른 commit
이었기 때문에 발생했습니다.
이 원인을 파악했으므로 이를 해결하는 방식은 매우 다양합니다.
Rebase
후 push --force
push --force
를 이용할 경우 현재 Local branch
의 형태 그대로 Remote
에 push
하게 됩니다.
때문에 Remote
의 a1, a2, a3
는 삭제되고 T1 -> T2 -> A1 ...
형태로 Remote
가 변화됩니다.
이 방법은 가장 강력하지만 (당연하게도) 아주 조스럽게 행해져야 합니다. Remote
의 형태를 확인도 없이 변경하기 때문입니다.
때문에 이는 Remote
에서 여러명이 작업하거나 Rebase
중 conflict
이 일어날 경우 그렇게 추천하지 않습니다.
Rebase
대신 Cherry pick
or Merge
애초에 Rebase
를 사용하지 않는 방법 또한 존재합니다. 우리 Rebase
의 목적은 특정 기능이 포함된 commit
이 필요했기 때문 이었습니다.
그래서 꼭 필요한 commit
만 Cherry pick
하거나 Local
에 Another Branch
를 Merge
하는 것으로도 목적은 달성됩니다.
특히 Merge
는 브랜치간 병합이 일어났다는 commit
을 남길 수 있어 추후 commit tracking
에 용이할 수 있습니다.
Local branch
에서 rebase
& push
아니면 Remote
와 동일한 새로운 Local-2
브랜치를 만드는 방법도 존재합니다.
새로운 Local-2
브랜치를 만들게 되면 로컬 브랜치이기 때문에 아직 (이름 그대로)
Remote-2
브랜치가 존재하지 않습니다.
때문에 Local-2
브랜치에 Target branch
를 Rebase
하면 git fast-forward
로 인해 A1, A2, A3
같은 "새로운 commit"
이 생성되지 않습니다.
물론 처럼 진행할 경우, Remote-1
, Remote-2
2 개의 브랜치가 저장소에 만들어질 것입니다.
어떤 방법이든 말 그대로 방법이 다를 뿐 목적은 동일합니다.
각자 현재 놓여진 상황에 따라 어느 방법이든 잘 선택해 사용하면 될 듯 합니다.
개인적인 추천은 2 번