기출노트 로고기출노트
시작하기
코딩 기초

Git 충돌을 풀어 쓴 한 줄

모임 시간을 바꾸는 사람과 장소를 바꾸는 사람이 같은 안내 파일을 고쳤습니다. 둘 다 필요한 변경이에요. 그런데 Git으로 합치려니 충돌이 났습니다.

이 글을 위해 임시 저장소에서 만든 예제입니다. 처음 notice.txt에는 다음 두 줄이 있었습니다.

이번 주 모임
토요일14시 / 도서관A

여기서 브랜치를 나눴습니다. time-change에서는 시간만 15시로 바꾸고 커밋했습니다. room-change에서는 장소만 도서관B로 바꾸고 커밋했습니다. 두 작업 모두 원래 안내에서 출발했으므로, 각 파일에는 상대방의 변경이 아직 없습니다.

time-change를 체크아웃한 상태에서 git merge room-change를 실행했습니다. Git 2.45.2에서 자동 병합이 멈췄고, notice.txt가 충돌 파일로 표시됐습니다. 같은 파일을 건드렸다는 사실만으로 항상 충돌하는 것은 아닙니다. 이 예제는 같은 줄을 서로 다르게 고쳐 Git이 그대로 합치지 못한 경우예요.1

이번 주 모임
<<<<<<< HEAD
토요일15시 / 도서관A
=======
토요일14시 / 도서관B
>>>>>>> room-change

HEAD 아래쪽, ======= 위쪽은 병합 명령을 실행할 때 체크아웃돼 있던 time-change의 내용입니다. 그 아래는 합쳐 오려는 room-change의 내용이고요. 여기서 위쪽을 ‘최신 정답’, 아래쪽을 ‘옛날 오답’으로 읽으면 안 됩니다. 각각 시간 변경과 장소 변경을 담은 두 버전입니다.1

표식 밖으로 꺼내야 할 두 변경

먼저 위쪽 내용만 남겨 봤습니다.

이번 주 모임
토요일15시 / 도서관A

표식은 없어졌고 문장도 자연스럽습니다. 이 파일을 git add notice.txt로 스테이징하자, Git이 관리하는 미해결 충돌 항목도 사라졌습니다. 하지만 장소가 도서관A로 남아 있어요. 장소를 옮기자는 변경은 빠졌습니다. Git에서 파일을 해결된 것으로 표시하는 절차와, 안내 내용이 요구사항을 충족하는지는 이렇게 어긋날 수 있습니다.2

원래 안내와 두 변경을 나란히 읽으면 남길 내용이 정해집니다. 원래 시간 14시는 15시로, 원래 장소 도서관A는 도서관B로 바뀌어야 합니다. 이 예제에서는 시간과 장소를 모두 바꾸기로 했으므로 완성할 파일은 다음과 같습니다.

이번 주 모임
토요일15시 / 도서관B

충돌 표식 사이의 두 줄을 그대로 이어 붙이면 안내가 두 개가 됩니다. 반대로 한쪽 줄을 통째로 고르면 다른 쪽 변경이 사라집니다. 필요한 조각을 가져와 한 줄을 다시 쓴 이유입니다. 공식 Git 책의 충돌 예제도 한쪽 전체를 선택하는 방법 외에, 양쪽 내용을 편집해 합치는 방법을 보여줍니다.1

여기서는 충돌 표시에 merge 형식을 사용했습니다. 이 형식의 =======는 두 버전 사이의 구분선이며, 그 밑에 공통 조상의 원문이 나오는 것은 아닙니다. diff3 같은 다른 표시 형식은 공통 조상 내용도 추가로 보여줍니다. 편집기에 보이는 줄 수가 다르다면 표시 형식부터 구별해야 해요.3

병합 결과를 기록합니다

이 예제처럼 병합이 충돌로 멈춰 있고, notice.txt를 완성한 상태라면 다음 순서로 해결 내용을 기록할 수 있습니다. 아래 명령은 그 연습 저장소의 파일 이름을 기준으로 합니다.2

git add notice.txt
git status
git commit -m "Merge time and room changes"

실제로 재현한 저장소에서는 최종 안내를 저장한 뒤 병합 커밋이 만들어졌습니다. 그 커밋은 시간 변경과 장소 변경의 이력을 부모로 함께 가졌습니다. 파일에는 모임 안내 한 줄만 남지만, 병합된 이력에는 두 작업이 연결됩니다.4

만약 두 사람이 각각 15시와 16시로 바꿨다면 어떨까요. 두 시간은 이 안내 한 줄에 동시에 넣을 수 없습니다. 그때는 어느 시간으로 모일지 결정해야 합니다. 이번에는 두 변경이 양립할 수 있어서 합쳤습니다. 충돌을 풀 때 남길 문장은, 함께 반영하기로 한 변경이 무엇인지에 따라 달라집니다.

Footnotes

  1. Pro Git — Basic Branching and Merging의 ‘Basic Merge Conflicts’는 같은 부분을 다르게 고친 충돌, HEAD와 병합 대상의 표시, 양쪽을 편집해 합친 예를 설명합니다. 모임 안내와 실행 결과는 이 글의 자체 예제입니다. ↩ ↩2 ↩3

  2. git-merge 공식 문서의 ‘HOW TO RESOLVE CONFLICTS’는 파일을 편집하고 git add로 인덱스에 넣은 뒤 git commit 또는 git merge --continue로 마치는 절차를 설명합니다. 안내의 의미까지 Git이 검증한다는 뜻은 아닙니다. ↩ ↩2

  3. git-merge 공식 문서의 ‘HOW CONFLICTS ARE PRESENTED’에서 기본 형식은 충돌 부분의 원문을 표시하지 않으며 diff3·zdiff3는 공통 조상 내용을 보여준다는 설명을 사용했습니다. ↩

  4. Pro Git — Basic Branching and Merging의 ‘Basic Merging’은 분기한 이력을 합치는 병합 커밋이 둘 이상의 부모를 갖는다고 설명합니다. 이 글의 재현 결과는 부모 커밋 두 개였습니다. ↩