git merge로 브랜치 병합하기

git merge로 브랜치 병합하기

Git을 사용한 개발에서 브랜치를 나누어 작업하는 건 기본 중의 기본인데요. 각자의 브랜치에서 작업을 마치고 나면 결국 그 변경 내용을 하나로 합쳐야 합니다. 이때 사용하는 명령어가 git merge입니다.

기본 사용법

git merge는 다른 브랜치의 변경 내용을 현재 브랜치에 합치는 명령어입니다.

$ git merge <브랜치명>

예를 들어, feature/login 브랜치의 작업을 main 브랜치에 합치려면 먼저 main으로 이동한 뒤 merge합니다.

$ git switch main
$ git merge feature/login

이렇게 하면 feature/login에서 작업한 모든 커밋이 main 브랜치에 반영됩니다.

Fast-forward Merge

가장 단순한 형태의 병합은 fast-forward merge인데요. main 브랜치에서 feature 브랜치를 만든 뒤, main 브랜치에는 새로운 커밋이 없고 feature 브랜치에만 새 커밋이 쌓인 경우에 발생합니다.

main:     A --- B
                 \
feature:          C --- D

이 상태에서 main에서 git merge feature를 실행하면, Git은 별도의 병합 커밋을 만들지 않고 main의 포인터를 feature의 최신 커밋으로 앞으로 이동시킵니다.

main:     A --- B --- C --- D

이력이 깔끔하게 일직선으로 유지되는 것이 fast-forward merge의 특징입니다.

$ git switch main
$ git merge feature/login
Updating b1c2d3e..f4a5b6c
Fast-forward
 src/auth/login.js | 45 ++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

3-way Merge

main 브랜치에도 새 커밋이 있고 feature 브랜치에도 새 커밋이 있으면 fast-forward가 불가능합니다.

main:     A --- B --- E
                 \
feature:          C --- D

이런 경우 Git은 두 브랜치의 변경 내용을 합친 새로운 “병합 커밋(merge commit)“을 생성합니다.

main:     A --- B --- E --- M
                 \         /
feature:          C --- D

M이 바로 병합 커밋이고, 두 브랜치의 이력이 합쳐지는 지점입니다.

$ git switch main
$ git merge feature/login
Merge made by the 'ort' strategy.
 src/auth/login.js | 45 ++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

병합 커밋이 만들어질 때 에디터가 열리면서 커밋 메시지를 수정할 수 있는데, 기본으로 Merge branch 'feature/login' 같은 메시지가 채워져 있으니 그대로 사용해도 됩니다.

—no-ff 옵션

fast-forward가 가능한 상황에서도 병합 커밋을 남기고 싶다면 --no-ff 옵션을 사용합니다.

$ git merge --no-ff feature/login
main:     A --- B ----------- M
                 \           /
feature:          C --- D ---

이렇게 하면 해당 기능이 언제 합쳐졌는지 이력에 명확하게 남기 때문에, 나중에 git log로 이력을 살펴볼 때 기능 단위로 구분하기가 쉬워집니다. 많은 팀에서 이 방식을 선호합니다.

충돌 해결

두 브랜치에서 같은 파일의 같은 부분을 다르게 수정했다면 Git이 자동으로 합치지 못하고 충돌(conflict)이 발생합니다.

$ git merge feature/login
Auto-merging src/auth/login.js
CONFLICT (content): Merge conflict in src/auth/login.js
Automatic merge failed; fix conflicts and then commit the result.

충돌이 발생한 파일을 열어보면 다음과 같은 표시가 있습니다.

function validateEmail(email) {
<<<<<<< HEAD
  return email.includes("@");
=======
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
>>>>>>> feature/login
}

<<<<<<< HEAD======= 사이가 현재 브랜치(main)의 내용이고, =======>>>>>>> feature/login 사이가 병합하려는 브랜치의 내용입니다.

원하는 코드를 선택하거나 양쪽을 적절히 합친 뒤, 충돌 표시를 지우고 저장합니다.

function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

그런 다음 수정한 파일을 스테이징하고 커밋하면 병합이 완료됩니다.

$ git add src/auth/login.js
$ git commit -m "feature/login 브랜치 병합 충돌 해결"

병합 취소

충돌이 너무 복잡해서 일단 병합을 취소하고 싶다면 --abort 옵션을 사용합니다.

$ git merge --abort

이 명령어는 병합 시도 전의 상태로 되돌려줍니다. 충돌 해결이 어려울 때 일단 원래 상태로 돌아가서 다시 전략을 세울 수 있어서 유용합니다.

이미 병합 커밋까지 만들어진 상태에서 취소하고 싶다면 git reset을 사용합니다.

$ git reset --hard HEAD~1

다만, --hard 옵션은 작업 디렉토리의 변경 내용까지 모두 날리므로 주의해서 사용해야 합니다.

병합된 브랜치 정리

기능 브랜치의 작업이 main에 성공적으로 합쳐졌다면, 더 이상 필요 없는 브랜치는 삭제하는 게 좋습니다.

$ git branch -d feature/login
Deleted branch feature/login (was f4a5b6c).

-d 옵션은 병합이 완료된 브랜치만 삭제할 수 있고, 병합되지 않은 브랜치를 강제로 삭제하려면 -D 옵션을 사용합니다.

git branch에 대한 더 자세한 내용은 별도의 글을 참고하세요.

마치며

git merge는 브랜치 기반 개발의 마지막 단계에 해당하는 중요한 명령어입니다. fast-forward와 3-way merge의 차이를 이해하고, 충돌이 발생했을 때 당황하지 않고 해결할 수 있게 되면 Git을 한층 자신 있게 다룰 수 있을 것입니다.

merge 대신 이력을 깔끔하게 정리하고 싶다면 git rebase도 살펴보시길 바랍니다.

더 자세한 내용은 Git 공식 문서를 참고하세요.

This work is licensed under CC BY 4.0 CC BY

Discord