git merge
Join two or more development histories together
— Git 공식 매뉴얼(git-merge Manual Page)
"깃이 분명히 자동으로 합쳐준다고 했는데, 내 코드 반쪽이 날아가 있었다." 충돌 해결하다 빡쳐서 상대방 코드를 통째로 덮어씌워 버리고 싶은 충동
1. 개요
Git에서 분기되어 나간 서로 다른 브랜치들의 역사(History)를 병합하여 단일 타임라인으로 합쳐주는 핵심 명령어이다. 개발 단계에서 독립적으로 개발된 피처(Feature) 브랜치들을 통합 테스트를 위해 메인 브랜치에 합치는 등, 형상 관리 프로세스 전반에서 가장 중요한 핵심 고리로 기능한다. 그러나 인류가 협업을 시작한 이래 가장 많은 스트레스와 우울증을 유발하는 원천 중 하나이기도 하다.(...)
2. 두 가지 갈래길: Fast-Forward와 3-Way Merge
머지에는 크게 두 가지 양상이 존재한다. 합치려는 대상 브랜치 사이에 아무런 갈림길이 없었다면, 그저 HEAD 포인터를 앞으로 쭉 이동시키는 패스트 포워드(Fast-Forward)가 발생하여 잡음 없이 평화롭게 끝난다. 반면, 양쪽 브랜치 모두에서 각자 갈 길을 간 흔적이 있다면 깃은 둘의 공통 조상을 찾아 두 줄기를 이어주는 새로운 '머지 커밋(Merge Commit)'을 생성하는 3-Way Merge를 시도한다. 바로 이 3-Way Merge 단계에서 같은 라인을 건드렸다면 깃은 자포자기하며 구원 요청(Merge Conflict)을 던진다.1
3. Merge vs Rebase: 개발자들의 백년전쟁
프로젝트 히스토리를 관리하는 철학을 두고 개발자들은 크게 두 파벌로 나뉜다. 발생한 사실 그대로 머지 커밋을 촘촘히 남겨 철저한 기록 위주로 가자는 머지 옹호파와, 머지 커밋 따위는 지저분할 뿐이니 리베이스(git rebase)를 통해 선형적인 일자 히스토리를 고수해야 한다는 리베이스 순혈주의파가 날마다 키보드 배틀을 벌인다. 어느 쪽이든 뉴비 입장에서는 둘 다 머리 깨지게 복잡하다.2
4. 실제 사용 예시
기능 브랜치 작업을 메인 브랜치에 합치는 가장 흔한 흐름이다. 병합 전에 메인 브랜치를 최신 상태로 맞춰두는 편이 충돌을 줄인다.
git switch main
git pull --ff-only origin main
git merge feature/login
병합 커밋을 명시적으로 남기고 싶다면 --no-ff를 사용한다. 충돌이 나면 파일을 고친 뒤 스테이징하고 커밋하면 된다.
git merge --no-ff feature/login
# 충돌 해결 후
git add src/app.ts
git commit
5. 관련 밈 및 드립
5.1. The Merge Hell (머지 지옥)
기능 수백 개를 한 달 동안 혼자 개발하고 마침내 메인 브랜치에 머지하려고 올렸더니, 충돌 파일만 200개가 넘어가면서 결국 코드를 처음부터 다시 타이핑하는 게 더 빠르다는 사실을 깨닫고 오열하는 고통스러운 개발자의 상태를 뜻하는 은어.
6. 여담
- --no-ff 옵션의 애증: 패스트 포워드가 가능한 상황임에도 불구하고 브랜치의 흔적(둥글게 말려 들어가는 모양의 그래프)을 무조건 남겨야 한다며
--no-ff를 규정으로 강제하는 회사들도 꽤 많다. 이 경우 깃 그래프가 알록달록한 무지개 꽈배기처럼 변한다. - 문어발 머지(Octopus Merge): 깃은 이론상 3개 이상의 브랜치를 동시에 하나로 병합하는 간지 폭발하는 '옥토퍼스 머지' 기능도 지원한다. 물론 실무에서 이걸 쓰는 사람을 본다면 매우 비범한 고수이거나 아니면 정신 나간 짓을 벌이는 폭탄마 둘 중 하나다.(...)