브랜치를 합치는 방법은 두 가지가 있다. 병합(merge)과 리베이스(rebase)다.
이번에는 커밋 순서를 재배열하는 리베이스에 대해 알아보겠다.
Base
브랜치는 커밋 하나를 기준으로 새로운 작업을 진행할 수 있는 분리된 작업 경로를 말한다.
새로운 브랜치가 파생되는 커밋을 베이스라고 한다. 병합에서는 이를 공통 조상 커밋이라고 한다.
아래 그림에서는 커밋 2가 베이스이자, 공통 조상 커밋이다.
Rebase
리베이스는 베이스 앞에 '다시'를 의미하는 re가 붙은 단어다.
파생된 브랜치의 기준이 되는 베이스 커밋을 변경하는 것이다.
그럼 브랜치의 베이스는 왜 변경하는 것일까?
커밋의 진행 모습을 단순화하기 위해서다.
브랜치가 많아지면 커밋을 관리하고 파악하기 어렵다.
꼬여 있는 기찻길처럼 단계별로 커밋을 따라가면서 코드를 확인해야 한다.
복잡한 브랜치의 수많은 경로는 코드의 진행 상황을 한눈에 파악하기 어렵게 한다.
리베이스는 코드의 베이스 분기점을 변경하여 마치 하나의 기찻길처럼 만든다.
여러 갈래로 갈라지지 않아 커밋의 진행 사항을 좀 더 쉽게 파악할 수 있다.
아래는 예시다. 위 그림에서는 커밋2가 베이스였지만 리베이스를 통해 커밋6을 베이스로 변경한다.
그리고 모든 브랜치의 커밋들을 리베이스된 커밋6 이후로 재정렬한다.
리베이스는 머지와는 다르게 두 브랜치를 서로 비교하지 않고 순차적으로 커밋 병합을 시도한다.
리베이스 과정
- 리베이스를 하면 공통 조상 커밋을 찾는다.
- 리베이스는 베이스 커밋을 변경하여 두 브랜치의 커밋 위치를 바꾼다.
- 그리고 파생된 브랜치의 diff를 임시 공간에 잠시 보관한다.
- master 브랜치의 커밋1 -> 커밋2 -> 커밋5 -> 커밋6까지 진행한다.
- 기존 베이스 커밋2에서 커밋6으로 베이스 기준점을 변경한다.
- 변경하는 기준 브랜치의 마지막 커밋에서 차례로 임시 공간에 저장한 diff를 하나씩 적용한다.
- 새로운 베이스 기준점을 기반으로 한 브랜치에서 커밋3 -> 커밋4를 커밋6에서 연장하여 수정 재배치한다.
- 결과적으로 브랜치의 커밋4는 최종 코드로 모든 코드 내용이 반영되어 있다. 커밋4 입장에서는 두 브랜치를 병합한 결과물이다.
리베이스의 결과물은 머지와 두 가지 차이점이 있다.
첫째로 3-way 병합은 병합 커밋이 있지만, 리베이스를 하면 병합 커밋이 없다.
둘째로는 브랜치의 마지막을 가리키는 커밋 위치가 다르다는 것이다.
실습
리베이스 작업은 rebase 명령어를 사용한다.
실습을 해보자. description 브랜치를 만들고 실습을 진행하겠다.
mkdir rebase
vi dscription.txt
아무 내용이나 5줄정도 작성
git commit -m "start rebase"
git branch description
git switch description
vi dscription.txt
// 첫째 줄 수정
git commit -m "update first description"
//master 브랜치로 이동
git switch master
2번째 줄과 3번째 줄을 각각 수정하고 2번 커밋을 진행한다.
vi dscription.txt
git commit -m "update second description"
vi dscription.txt
git commit -m "update second description"
그럼 이제 본격적으로 리베이스를 진행해보자.
리베이스는 병합 기준 브랜치가 merge 명령어와 반대다.
merge 명령어를 사용한 병합은 현재의 기준 브랜치에서 다른 브랜치를 읽어와서 결합한다.
하지만 리베이스는 병합되는 브랜치 방향이 반대다.
리베이스를 할 수 있게 description 브랜치로 스위치해야한다.
git switch description
giut rebase master
성공하면 아래와 같은 문자가 출력된다.
Successfully rebased and updated refs/heads/feat.
리베이스 명령이 실행되면 파생 브랜치들의 커밋들은 기준 브랜치의 마지막 커밋으로 재정렬된다.
git log 명령어를 통해 살펴보면 master 브랜치의 마지막 커밋 뒤에 파생 커밋이 연결된 것을 볼 수 있다.
리베이스는 베이스 커밋을 변경하여 병합한다.
베이스 커밋을 변경하는 과정에서 커밋들은 재배치 작업을 한다. 이과정에서 커밋의 해시 값이 변경된다.
커밋 위치가 변경될 때 해시 값 중복을 방지하려고 새로운 커밋 해시를 생성한다.
리베이스 후 브랜치
일반적으로 병합을 한 후 두 브랜치는 같은 커밋 ID를 가리킨다.
하지만 확인해보면 브랜치 위치는 서로 다르다.
리베이스는 커밋 위치를 조정할 뿐 브랜치의 HEAD 포인터까지 옮겨 주지는 않는다.
리베이스한 후에는 이러한 병합 브랜치의 HEAD를 맞추어야 한다.
즉, 리베이스된 브랜치를 병합해야 한다.
병합을 위해 master 브랜치로 스위치하고, merge 명령어를 사용해 description 브랜치와 병합하자.
git switch master
git merge description
병합을 하면 Fast-forward 라고 병합 방식이 메시지로 출력된다.
리베이스를 통해 선형적인 구조가 되었으므로 Fast-Forward 병합이 진행되는 것이다.
*master 브랜치는 추가 커밋이 없고 description은 master 브랜치의 마지막 커밋을 기준으로 브랜치가 나누어졌으므로
이후 필요 없는 브랜치는 git branch -d description과 같은 명령어로 삭제한다.
리베이스 충돌과 해결
리베이스 또한 충돌이 발생하면 직접 문제를 해결해야 한다.
충돌을 수정하면 rebase 명령어와 --continue 옵션을 사용해야 한다.
# 하나의 파일에서 충돌이 나게 커밋을 진행했다고 가정
git rebase master #충돌이 발생!
#병합을 직접 해결해 아래 명령어를 입력
git rebase --continue
git add .
git rebase --continue #리베이스 성공
git switch master
git merge description #Fast-forward 병합 성공
git branch -d description
수정한 파일을 다시 스테이지 상태로 변경해야한다.
리베이스를 이용하여 병합할 때는 충돌된 부분들을 한 단계씩 해결해 나가면서 병합할 수 있다.
즉 --continue 옵션을 사용해 병합을 더 진행한다.
리베이스 충돌을 해결했다면 마찬가지로 master 브랜치로 스위치한 후 브랜치를 병합해야 한다.
그리고 병합된 브랜치는 삭제한다.
리베이스 주의할점
리베이스는 커밋 위치와 해시 값을 변경한다.
저장소를 외부에 공개했다면 공개된 순간부터 커밋은 리베이스를 사용하지 않는 것이 원칙이다.
리베이스는 외부로 코드를 푸시하거나 공개하기 전에 로컬에서만 실행하는 것이 좋다.
외부에 공개된 커밋을 리베이스하면 커밋 위치와 해시 값이 변경되어 너무 혼란스럽다.
공개된 커밋을 변경할 때는 revert 명령어를 사용해야 한다.
참고자료
GIT 교과서(이호진님)
https://product.kyobobook.co.kr/detail/S000001834368
댓글