들어가기에 앞서
발생한 이슈를 설명하기 전, 우리 팀의 브랜치 전략을 간단하게 이야기하겠다.
1.
각각 자신의 브랜치를 파고 작업을 한다
2.
작업이 끝나면 dev 브랜치로 PR을 보낸다
3.
작업물을 확인한 뒤 dev 브랜치로 merge한다
다음과 같은 순서로 진행되는데, 우석이가 만든 백엔드 작업물을 합치는 과정에서 문제가 발생했다.
먼저 우석이 작업물을 dev 브랜치에 Merge 시켜두고,
내가 작업한 feature 브랜치에서 dev 브랜치를 pull 땡겨왔다.
충돌한 파일들을 다 합쳐준 뒤 커밋을 하려고 보니, 전혀 손대지 않은 프론트엔드 코드들이 변경사항에 기록되어 있는걸 볼 수 있었다.
도저히 이유를 알 수 없어 Git 마스터 지원이에게 지원 요청을 보냈더니 Rebase 문제 같다고, 한 번 짚고 넘어가는 게 어떻냐는 조언을 받았다.
개발자로 살아가는 이상 앞으로 계속 마주칠 문제다. 이번 기회에 확실하게 잡고 넘어가야겠다 다짐하며 글을 작성한다.
왜 문제가 발생한걸까?
문제가 발생한 상황을 다이어그램으로 그려보았다.
빨간색: 지원이가 작업한 코드
초록색: 우석이가 작업한 코드
주황색: 내가 작업한 코드
JavaScript
복사
위 그림을 보면서 순서대로 흐름을 정리해보자.
1.
지원이(프론트)랑 우석이(백엔드)는 각각 자신의 브랜치에서 작업을 하고 dev 브랜치에 Merge했다.
2.
프론트 작업, 백엔드 작업이라 겹치는 파일이 없어 충돌이 일어나지 않는다.
3.
우석이랑 나는 겹치는 파일이 있어 파일을 합치는 과정이 필요했고, dev 브랜치를 pull 땡겨왔다.
4.
우석이와 내가 작업하며 충돌이 일어난 파일들을 합쳐주고 Commit을 하려고 보니, 기존 내 작업 브랜치(주황)에 프론트엔드 코드(빨간 동그라미)의 변경이 적용되어버린 걸 확인할 수 있었다.
어떻게 해결할까?
이 문제를 해결하기 위해서는 pull, fetch, merge, rebase에 대해 알아야만 한다.
Pull
•
Fetch + Merge 를 함께 실행하는 명령어
Fetch
•
원격 저장소의 최신 데이터들을 로컬 저장소에 반영한다. 새로고침이라 생각하면 이해가 쉽다.
Merge
•
서로 다른 두 개의 커밋을 하나의 커밋으로 합치는 명령이다.
•
말로 설명하면 어려우니, Rebase를 설명한 뒤 그림으로 설명하겠다.
Rebase
•
re-base, “베이스를 변경한다” 라고 사전적인 의미를 갖는다.
•
Base는 해당 commit 직전의 commit이다.
•
각 커밋들은 직전 Commit을 기억하고 있는데, 특정 commit의 직전 commit을 변경해주는 명령이다.
Merge와 Rebase의 차이
•
Merge는 master 브랜치와 feature 브랜치를 합치면서 새로운 커밋이 한 개 생기는 걸 확인할 수 있다.
•
Rebase는 Base를 옮기는 명령이라 했는데,
feature 브랜치의 첫 번째 작업을 master 브랜치에 이어붙이는 걸 확인할 수 있다.
내가 마주친 문제는 프론트엔드 코드의 변경을 내 변경사항으로 인식하고 있었기 때문에 발생한 문제였다.
따라서 프론트엔드 코드의 변경이 사전에 있었다는 걸 내 브랜치에게 가르쳐주면 된다.
바로 여기서 Rebase를 사용할 생각이다.
1.
git fetch 명령어로 3번 commit에 dev브랜치의 HEAD가 위치하도록 만든다.
2.
내 작업 브랜치에서 git rebase dev 명령을 실행해줘 7번 commit의 Base를 1 → 3 으로 바꾼다.
3.
태훈의 작업브랜치를 dev에 Merge하면 내 변경사항들만 PR에 올라가게 만들 수 있다!
충돌 해결
설명이 너무 길어지고 주제가 섞일까봐 이야기하지 않은 부분이 있다.
위에서 우석이 브랜치와 내 브랜치를 합치면서 충돌이 발생했다고 했는데,
Pull(Fetch + Merge) 에서 일어났던 충돌이 Fetch + Rebase 한다고 안일어날까?
Rebase를 한다 하더라도, 초록색 동그라미와 주황색 동그라미 간에 똑같은 파일을 건든 문제는 동일하다.
즉, 충돌이 일어난 파일은 존재한다.
따라서 1 → 7에서3 → 7 로 Rebase 하는 과정에서부터 충돌을 해결해줘야 한다.
충돌을 해결하는 건 간단하다.
Git 입장에서는 동일한 파일에서 수정이 일어났을 때, 어떤 코드를 살리고, 어떤 코드를 죽일지 결정할 수가 없다. 그래서 충돌이 일어난 파일들 하나하나 들어가서, 이렇게 결과물을 만들거라고 지정해주면 된다.
// TODO 충돌 합치는 거 발생하면 사진 캡쳐해서 여기 넣기
위의 그림에서 볼 수 있듯 Merge로 해결을 하게 되면 충돌 해결을 한 번만 하면 된다.
그러나 Rebase로 충돌을 해결하면, 위의 그림을 예로 들면 7번 커밋이 바뀌면 이후 연결된 파일이 영향을 받을 수 있다. 그러면 모든 커밋을 따라가며 다 수정해야 하기도 한다.
대신, Rebase를 사용하면 Git Flow가 깔끔어져 아래의 그림과 같은 지옥을 보지 않아도 된다.
Git Merge로 만들어진 브랜치 지옥…
이번에 rebase를 처음 써봤는데, 막상 공부를 해보니 그렇게 어려운 녀석은 아니었다. 앞으로 자주 애용하게 될 것 같다.
아무튼! 이 방식이 정답이다 하는건 없으니 상황에 맞는 병합 전략을 사용하자고 결론을 내며 글을 마무리하겠다.
참고