브랜치 충돌 해결, 깃 충돌 해결
깃 충돌은 서로 다른 두 사용자가 동일한 부분을 수정했을 때 발생한다. 브랜치 간 충돌도 이와 마찬가지이다. 서로 다른 두 브랜치가 동일한 부분을 수정하고, 두 브랜치를 한 개의 브랜치에 병합할 때 충돌이 발생한다.
브랜치 간 충돌이 발생하면 머지가 되지 않는다.
사실 개발하면서 틈틈이 master(혹은 main) 브랜치에 업데이트되는 내용을 merge 해주면 충돌과 같은 문제는 거의 발생하지 않을 테지만, 실전에선 파일 전체가 덮어쓰기 되는 일이 발생할 수도 있기 때문에 ㅠ^ㅠ-전체 덮어쓰기 같은 건 일어나면 안 되는 일이지만 white space로 인해 더러 일어나기도 한다 알고 싶지 않았다- 충돌 해결 방법은 알아두면 좋다.
충돌 예시
현재 자신이 로그인 기능을 login 브랜치에 작성하고 있었다는 가정 하에, login 브랜치와 master 브랜치가 충돌이 발생했다.
충돌이 발생하면 충돌이 발생한 파일에 다음과 같은 내용이 작성된다.
<<<<<<< HEAD
[login Branch 내용]
=======
[master Branch 내용]
>>>>>>>
또한 gitlab에서 머지 리퀘스트가 요청되고 머지가 되지 않는 경우 아래와 같은 문구가 노출된다.
Merge blocked: merge conflicts must be resolved.
또는
Merge blocked: merge conflicts must be resolved. Users who can write to the source or target branches can resolve the conflicts.
새로운 브랜치 생성
병합 시 역 머지되는 문제가 발생할 수 있어 작업하던 브랜치를 직접적으로 사용하지 않고 새로운 브랜치를 생성한다.
새로운 브랜치 생성은 내가 작업하던 브랜치를 기준으로 잡는다.
예시로 내가 작업하던 브랜치가 login이라 가정하고, login 브랜치 소스 코드를 바탕으로 한 login-mr-fix 브랜치를 생성한다.
login-mr-fix 브랜치-충돌 해결 브랜치라 부른다-를 생성한 뒤 작성해야 하는 명령어를 순서대로 서술해본다. Git bash를 사용한다고 가정한다.
user@DESKTOP MINGW64 /d/devroot/workspace/myproject (master)
$ git pull
user@DESKTOP MINGW64 /d/devroot/workspace/myproject (master)
$ git checkout login-mr-fix
user@DESKTOP MINGW64 /d/devroot/workspace/myproject (login-mr-fix)
$
먼저 지금 내 브랜치와 충돌이 나는 브랜치를 최신화한다. 로컬에서 merge 되는 소스 코드는 Github 또는 Gitlab에 올라 가 있는 소스 코드가 아닌, 로컬 브랜치에 존재하는 코드이기 때문이다.
이후 새로 생성한 충돌 해결 브랜치(login-mr-fix)로 로컬 작업 브랜치를 변경한다.
user@DESKTOP MINGW64 /d/devroot/workspace/myproject (login-mr-fix)
$ git merge master
Auto-merging [충돌나는 파일]
CONFLICT (content): Merge conflict in [충돌나는 파일]
Automatic merge failed; fix conflicts and then commit the result.
user@DESKTOP MINGW64 /d/devroot/workspace/myproject (login-mr-fix|MERGING)
$
현재 로컬 작업 브랜치가 충돌 해결 브랜치로 설정된 상태에서, 충돌 난 브랜치(master)와 병합(merge)한다.
그럼 충돌 나는 파일 때문에 자동 병합이 실패했으니 이를 해결하고 커밋하라는 문구가 출력된다.
또한 현재 작업 브랜치를 알려주는 란에 |MERGING 문구가 추가된다.
충돌 난 파일을 확인해보면 맨 처음에 봤던 충돌 예시와 같이 << HEAD == >> 가 뜬다.
충돌 해결은 IDE를 사용하는 게 편하다. 여담이지만, 인텔리제이는 충돌 해결 시 소스 코드가 3분 할로 보인다.
[나의 작업 브랜치] | [병합되는 부분] | [충돌 나는 브랜치]
예시로 다음과 같이 3 분할되는 것이다.
login-mr-fix 브랜치(From. login 브랜치) | login-mr-fix 브랜치 | master 브랜치
이처럼 인텔리제이에서는 내가 작업했던 부분과 충돌 나는 브랜치에서 각각 필요한 코드를 끌어다 쓸 수 있다.
그러나 이클립스는 이상하게도 한쪽 변경 사항만 덮어쓸 수 있다. 반대의 경우도 가능하게 버튼이 있지만 클릭이 막혀있다. 해결법을 갈구했지만 찾지 못했다.
VSCode도 양쪽 수정 사항 모두 가져올 수 있기 때문에 필자는 보통 인텔리제이 아니면 VSCode를 쓴다.
user@DESKTOP MINGW64 /d/devroot/workspace/myproject (login-mr-fix|MERGING)
$git add .
user@DESKTOP MINGW64 /d/devroot/workspace/myproject (login-mr-fix|MERGING)
$git commit
user@DESKTOP MINGW64 /d/devroot/workspace/myproject (login-mr-fix)
$git push
충돌 해결이 완료된 후 commit 하면 merge는 완료된다.
이후 master 브랜치에 login-mr-fix 브랜치를 merge 하면 충돌이 해결된 상태로 정상 병합된다.
역 merge
충돌 해결 브랜치를 나의 작업 브랜치와 병합하면 안 된다.
Merge Request
- Source branch : login-mr-fix
- Target branch : login
절대 위처럼 하면 안 된다. 이제껏 들었던 예시로는 Target branch를 master로 설정해야 한다.
역 머지가 발생하는 상황을 가정해본다.
운영 서버(master)에 올리기 전, 개발 서버(dev)에 코드를 올려 테스트한다.
dev 브랜치 내용은 완벽하게 구현된 코드가 아니기 때문에 master 브랜치에 올라가면 안 되는 코드이다.
내가 작업한 코드도 dev에서 확인하기 위해 올렸는데, dev 브랜치 내용 중 다른 사람이 작업한 부분이 내가 작업한 부분과 겹쳐서 dev 브랜치와 나의 작업 브랜치가 충돌이 발생했다.
충돌 해결 시 다른 사람이 작업한 부분도 그대로 dev에 있어야 하니 해당 코드도 함께 병합한 뒤 dev에 올렸다.
이후 나의 작업 코드가 개발 서버(dev)에서 정상적으로 동작하는 걸 확인한 뒤, 충돌 해결 브랜치를 나의 작업 브랜치에 merge 하고 최종적으로 master 브랜치에 내 작업 브랜치를 merge 했다.
이 상황에서 문제 되는 부분은 충돌 해결 브랜치를 나의 작업 브랜치에 merge 한 것이다.
충돌 해결 브랜치에는 다른 사람이 테스트한 코드도 있다. 아직 온전하게 동작하지 않을 수도 있는 코드가 나의 작업 브랜치에 들어가게 되고, 나는 내가 구현한 기능에는 문제가 없는 걸 확인했으니 그대로 master 브랜치에 merge 해버린 것이다. 이 과정에서 운영 서버에 잘못된 코드가 들어가게 된다.
잘못된 코드가 운영 서버에 올라가면 높은 확률로 문제를 일으킬 수 있으므로 역 머지는 항상 조심해야 한다.
정확하게 개념을 이해하고 넘어갔다면 충돌 해결 브랜치를 나의 작업 브랜치에 merge 하는 일 자체가 없을 테지만 처음엔 충돌이 난 것 자체에 당황해서 한 적이 있다. (👀 머쓲)