개발 도구 ·

Git 병합 충돌에서 두 변경을 잃지 않고 해결하는 순서

먼저 답부터

Git 병합 충돌은 두 작업 흐름이 같은 부분을 다르게 바꿔 Git이 자동 선택을 못한 상태입니다. 충돌 파일과 양쪽 변경을 먼저 보존하고 표시된 두 내용을 비교해 최종 문장을 직접 만든 뒤, 표시 제거와 테스트를 확인하고 git add와 commit으로 해결을 기록해야 합니다.

Git 병합 충돌에서 두 변경을 잃지 않고 해결하는 순서 대표 이미지

병합 충돌이라는 문구를 보면 코드가 망가졌다고 느끼기 쉽습니다. 실제로는 Git이 두 변경 중 어느 쪽이 맞는지 판단하지 않고 사람에게 선택을 돌려준 상태입니다. 자동으로 한쪽을 버리지 않았다는 뜻이기도 합니다.

같은 종이 문서의 같은 문장을 두 사람이 서로 다르게 고쳤다고 생각해 보세요. 편집자는 두 원고를 펼쳐 놓고 어느 문장을 남길지, 둘을 합칠지 확인해야 합니다. Git도 충돌 위치와 양쪽 내용을 표시해 이 판단을 돕습니다.

충돌은 같은 위치의 의도가 겹칠 때 생긴다

브랜치는 같은 프로젝트에서 서로 다른 작업 흐름을 이어 가는 갈래입니다. 두 브랜치가 서로 다른 파일을 고쳤다면 Git이 자동으로 합칠 가능성이 큽니다. 같은 파일이라도 떨어진 줄을 바꿨다면 자동 병합될 수 있습니다. 그러나 같은 줄 주변을 다르게 편집하면 어느 결과가 맞는지 알 수 없습니다.

충돌은 실수한 사람을 찾는 경고가 아닙니다. 작업이 동시에 진행됐다는 정보입니다. 중요한 건 누가 이겼는지가 아니라 양쪽이 해결하려던 문제를 모두 확인하는 일입니다. 제목 문구와 연결된 테스트, 설정 이름과 사용 위치처럼 함께 바뀌어야 하는 파일도 살펴봅니다.

먼저 현재 상태와 충돌 파일을 기록한다

충돌이 나면 새로운 pull이나 merge를 반복하지 않습니다. git status를 실행해 병합 중인지, 어떤 파일이 unmerged 상태인지 읽습니다. unmerged는 아직 합쳐지지 않았다는 뜻입니다. 현재 브랜치와 합치려던 브랜치 이름도 메모합니다.

  • 충돌 메시지 전체를 저장합니다.
  • git status의 파일 목록을 확인합니다.
  • 양쪽 작업의 커밋 제목을 읽습니다.
  • 수정 전에 중요한 파일의 원본이 Git에 기록돼 있는지 확인합니다.
  • 비밀키나 고객 자료가 섞였으면 화면 공유를 멈춥니다.

작업 트리에 커밋하지 않은 사용자 변경이 있다면 삭제나 reset으로 치우치지 않습니다. 해당 변경을 만든 사람과 먼저 범위를 확인합니다. 충돌 해결을 서두르려고 파일 전체를 다른 버전으로 덮어쓰면 자동 병합이 보존한 정상 변경까지 잃을 수 있습니다.

충돌 표시는 위와 아래의 두 제안을 나눈다

Git 공식 사용자 설명서는 충돌 파일에 HEAD, 구분선, 다른 커밋을 나타내는 표시가 남는 예를 보여 줍니다. HEAD 쪽은 보통 현재 체크아웃한 브랜치의 내용이고, 아래쪽은 합치려는 다른 흐름의 내용입니다.

 <<<<<<< HEAD
버튼을 누르면 초안을 저장합니다.
 =======
버튼을 누르면 검토 뒤 공개합니다.
 >>>>>>> feature-review

최종 결과는 위 문장 하나나 아래 문장 하나일 수도 있고, 두 의도를 합친 새 문장일 수도 있습니다. 예를 들어 초안 저장과 검토 후 공개가 모두 필요하다면 버튼을 나누는 설계가 맞을 수 있습니다. 표시만 지우고 중복 문장을 모두 남기면 문법은 통과해도 기능이 틀릴 수 있습니다.

양쪽 의도를 확인하고 작은 구간부터 고친다

충돌 파일을 위에서 아래로 한꺼번에 고치지 말고 구간별로 처리합니다. 각 구간에서 현재 브랜치가 하려던 일, 다른 브랜치가 하려던 일, 최종 요구사항을 적습니다. 관련 테스트나 이슈가 있다면 함께 읽습니다.

편집기의 Accept Current나 Accept Incoming 버튼은 빠르지만 의미를 대신 판단하지 않습니다. 이름만 보고 누르면 한쪽 변경을 통째로 버릴 수 있습니다. Accept Both도 항상 안전하지 않습니다. 두 함수나 설정이 중복돼 오류를 만들 수 있으므로 결과 파일을 직접 읽어야 합니다.

표시가 사라졌다고 해결이 끝난 것은 아니다

충돌 표시인 꺾쇠와 등호가 파일에 남지 않았는지 검색합니다. 그다음 포맷 검사, 타입 검사, 관련 테스트를 실행합니다. 웹 화면 변경이라면 실제 브라우저에서 두 기능이 모두 기대대로 움직이는지 봅니다.

git add는 해당 파일의 해결 결과를 다음 커밋 대상으로 올리는 명령입니다. 파일 내용이 맞다는 검토 후에 실행합니다. 모든 충돌을 해결하고 테스트한 뒤 merge commit을 만들면 두 흐름을 어떻게 합쳤는지 기록됩니다. commit 전에 diff를 다시 읽어 충돌과 무관한 삭제가 없는지 확인합니다.

중단과 되돌리기도 현재 작업을 보존한 뒤 선택한다

병합을 계속할 근거가 없거나 잘못된 브랜치를 합쳤다면 abort 기능을 검토할 수 있습니다. 하지만 커밋하지 않은 변경이 섞여 있으면 원래 상태 복원이 복잡할 수 있습니다. 명령의 뜻을 모른 채 reset --hard나 강제 checkout을 사용하지 않습니다.

공유 저장소에서는 충돌 해결 후 바로 강제 push하지 않습니다. 원격이 다시 바뀌었는지 fetch로 확인하고 팀의 브랜치 규칙을 따릅니다. 데이터베이스 마이그레이션, 결제 설정, 배포 설정 충돌은 문장 선택만으로 끝나지 않으므로 담당자 검토와 별도 테스트 환경이 필요합니다.

같은 충돌을 줄이려면 작업 범위를 작게 나눈다

충돌을 완전히 없앨 수는 없지만 빈도를 줄일 수 있습니다. 큰 파일 하나에 여러 기능을 몰아넣지 않고, 작은 커밋으로 자주 공유하며, 같은 설정을 동시에 고칠 때는 먼저 알립니다. 자동 생성 파일은 생성 원본을 고치고 다시 만드는 규칙을 정합니다.

충돌 해결 커밋 메시지에는 어떤 의도를 합쳤는지 적습니다. 나중에 문제가 생겼을 때 단순히 conflict fix라고만 적힌 기록보다 훨씬 유용합니다. 해결 뒤 두 브랜치의 테스트가 모두 통과했다는 증거도 남깁니다.

오늘 바로 해볼 작은 실습

실제 저장소를 바꾸지 않고 종이에 충돌을 해결합니다.

  1. 위 예시의 두 문장이 각각 원하는 행동을 적습니다.
  2. 두 행동이 동시에 필요한지 하나만 필요한지 판단 근거를 씁니다.
  3. 최종 문장을 한 줄로 다시 작성합니다.
  4. 꺾쇠, 등호, 브랜치 이름 표시를 모두 제거했다고 가정합니다.
  5. 저장, 검토, 공개 중 어떤 테스트가 필요한지 세 가지 적습니다.
  6. 실제 충돌에서는 git status와 diff를 먼저 보겠다고 메모합니다.

고객 데이터, 결제, 삭제, 배포 설정 충돌은 혼자 한쪽을 선택하지 마세요. 모르는 명령으로 작업 트리를 지우지 말고 두 커밋과 충돌 파일을 보존한 채 검토를 요청하는 것이 가장 안전한 다음 행동입니다.

확인한 자료