git merge를 실행했다가 처음 보는 <<<<<<<, =======, >>>>>>> 마커로 뒤덮인 파일을 마주치면 당황하기 쉽다. 하지만 이 마커는 Git이 스스로 판단하지 못한 부분을 사람에게 넘겨주는 명확한 신호일 뿐이며, 구조를 알면 침착하게 해결할 수 있다. 이 장은 병합이 성공하는 경우와 충돌하는 경우를 나눠 다룬다.
개요
git merge는 현재 브랜치에 다른 브랜치의 변경 사항을 합친다.
| |
이 명령은 main 브랜치에 feature/login 브랜치의 커밋들을 반영한다. 두 브랜치가 겹치는 부분 없이 수정됐다면 Git은 자동으로 병합을 완료하고 새로운 병합 커밋을 만든다(겹치지 않는 경우 중에서도 한 브랜치가 다른 브랜치의 조상일 때는 14장에서 다루는 fast-forward가 일어나 병합 커밋조차 생기지 않는다).
기본 개념
병합 충돌(merge conflict)은 두 브랜치가 같은 파일의 같은 부분을 서로 다르게 수정했을 때 발생한다. Git은 자동으로 어느 쪽 변경을 채택해야 할지 판단할 수 없으므로, 해당 부분을 충돌 마커로 감싸 파일에 남기고 병합을 일시 중단한다.
| |
<<<<<<< HEAD부터 =======까지가 현재 브랜치의 내용, =======부터 >>>>>>> feature/login까지가 병합해오는 브랜치의 내용이다. 해결 절차는 이 마커들을 지우고 최종적으로 남길 내용만 남긴 뒤, 그 파일을 다시 스테이징(05장)하고 커밋하는 것이다.
| |
종류/세부
충돌 상태에서 쓰는 도구
충돌이 여러 파일에 걸쳐 있을 때 상황을 파악하는 데 쓰는 명령들이다.
| |
--abort는 충돌 해결이 너무 복잡해 보이거나 애초에 병합을 잘못 시작했다고 판단될 때 유용하다 — 병합 시도 이전 상태로 정확히 되돌아가므로, 처음부터 다시 계획을 세워 시도할 수 있다.
병합 커밋의 구조
일반 커밋(08장)은 부모 커밋이 하나지만, 병합 커밋은 부모가 둘(또는 그 이상, octopus merge의 경우)이다. git log --graph(09장)에서 봤던 갈라졌다 합쳐지는 그래프 모양이 바로 이 다중 부모 구조에서 나온다.
| |
| |
병합 전략 옵션
특정 파일에서 항상 한쪽 브랜치의 내용을 우선하고 싶을 때 전략 옵션을 쓸 수 있다.
| |
이 옵션은 충돌이 발생한 hunk에만 적용되며, 병합해오는 브랜치의 다른 변경 사항 자체를 무시하는 것은 아니다.
주의사항·함정
충돌 마커를 지우지 않고 그대로 커밋하는 실수: 충돌 해결 중 <<<<<<<, =======, >>>>>>> 마커 중 일부를 실수로 남긴 채 커밋하면, 그 마커가 코드의 일부로 그대로 포함돼 문법 오류나 예상치 못한 동작을 일으킨다. 커밋 전에 파일 전체에서 마커 문자열이 남아 있지 않은지 검색하는 습관이 필요하다.
병합 커밋이 히스토리를 지저분하게 만든다는 불만: 짧은 수명의 기능 브랜치를 자주 병합하면 git log --graph 출력에 병합 커밋이 많이 쌓여 읽기 번거로워질 수 있다. 이 불만에 대한 대안이 16장에서 다루는 리베이스 기반 워크플로다 — 다만 리베이스는 병합과 다른 트레이드오프를 가지므로, 어느 쪽이 항상 옳다고 단정할 수 없다.
같은 두 브랜치를 실수로 여러 번 병합하는 경우: 이미 병합된 브랜치를 다시 병합하려 하면, Git은 변경 사항이 없다는 것을 인식하고 “Already up to date"를 출력하며 아무 일도 하지 않는다. 이는 오류가 아니라 정상 동작이다.
![Featured image of post [Git] 13. git merge — 병합과 충돌 해결](/post/git/git-merge-command-and-conflict-resolution/wordcloud_hu_b7f628a57794e7f7.webp)
![[Git] 11. 브랜치 개념과 git branch](/post/git/git-branch-concept-and-command/wordcloud_hu_d0c627d2bdd2eb50.webp)
![[Git] 12. git switch/checkout — 브랜치 전환](/post/git/git-switch-checkout-branch-transition/wordcloud_hu_cfa8ddb8839c8503.webp)
![[Git] 13. git merge — 병합과 충돌 해결](/post/git/git-merge-command-and-conflict-resolution/wordcloud_hu_fd1543b374ae452a.webp)
![[Git] 14. Fast-forward vs 3-way merge](/post/git/fast-forward-vs-three-way-merge/wordcloud_hu_c86e279c96ef44f2.webp)
![[Git] 15. git rebase — 히스토리 재배치](/post/git/git-rebase-command-rewriting-history/wordcloud_hu_326a73b9e72fabfc.webp)
![[Git] 08. git commit — 커밋 작성 규칙](/post/git/git-commit-command-writing-good-commits/wordcloud_hu_b27461e0d97eff2a.webp)
![[Git] 16. merge vs rebase 선택 기준](/post/git/merge-vs-rebase-choosing-criteria/wordcloud_hu_2d0667c2865989d3.webp)
![[Git] 27. git rebase -i — 인터랙티브 리베이스](/post/git/git-rebase-interactive-mode/wordcloud_hu_31933fe2155d80d4.webp)
![[Git] 07. git diff — 변경 비교](/post/git/git-diff-command-compare-changes/wordcloud_hu_3b0ce1ca4361f1f6.webp)