LabHub
学习 学习路径 课程

Git 实战

同一个冲突,正在解第四遍

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

충돌 표시에서 공통 조상을 보고, 한 번 푼 해결을 git 이 기억하게 만들고, -X ours-s ours 가 결과 파일을 어떻게 다르게 만드는지 내용으로 확인합니다.

왜 중요한가

충돌 해결은 기술이 아니라 판단입니다. 그런데 기본 충돌 표시는 판단에 필요한 정보 하나를 빠뜨립니다 — 원래 무엇이었는지입니다. 그것을 모르면 "한쪽이 지운 것" 과 "양쪽이 다르게 고친 것" 을 구별할 수 없고, 잘못 고르면 남의 수정이 조용히 사라집니다. zdiff3 는 그 칸을 채워 줍니다. 그리고 같은 판단을 반복해야 하는 상황 — 오래 사는 브랜치를 계속 리베이스하거나 릴리스를 정기적으로 되돌려 합치는 상황 — 에서는 rerere 가 그 판단을 한 번만 하게 해 줍니다. 마지막 두 단계는 이름이 닮아 자주 뒤바뀌는 두 손잡이를 갈라 놓습니다. -s ours 를 내용을 가져오려는 의도로 쓰면 이력에는 합쳐졌다고 적히고 코드에는 아무것도 안 들어오는 상태가 됩니다.

단계

  1. /root/gitx8/repo 에 같은 줄을 서로 다르게 고친 브랜치들을 만듭니다.
  2. 기본 충돌 표시와 zdiff3 표시를 나란히 notes/zdiff3.txt 에 남깁니다.
  3. rerere 를 켜고 충돌을 한 번 풀어 병합을 마칩니다.
  4. 그 병합을 되돌리고 다시 병합해, git 이 같은 해결을 스스로 적용하는 것을 notes/replay.txt 에 남깁니다.
  5. sidefix-X ours 로 병합하고 결과를 notes/xours.txt 에 남깁니다.
  6. legacy-s ours 로 병합하고 결과를 notes/sours.txt 에 남깁니다.
  7. 두 손잡이의 차이를 notes/strategies.txt 에 정리합니다.
  8. notes/report.md 에 반복 충돌을 줄이는 방법을 정리합니다.

참고

같은 줄을 서로 다르게 고친 브랜치들

/root/gitx8/repo 에 같은 줄을 서로 다르게 고친 브랜치들을 만듭니다.

handler.txta 부터 g 까지 일곱 줄로 만들고, topic·sidefix·legacy 세 브랜치를 첫 커밋에서 땁니다. topicmain 은 둘째 줄을 서로 다르게 고치고, sidefix 는 둘째 줄과 여섯째 줄을 고칩니다. 여섯째 줄이 떨어져 있어야 5단계에서 충돌하지 않는 변경이 따로 보입니다.

원래 무엇이었는지 보여 주는 칸

기본 충돌 표시와 zdiff3 표시를 나란히 notes/zdiff3.txt 에 남깁니다.

git merge topic 으로 충돌을 낸 뒤 handler.txt 를 그대로 옮겨 적고 git merge --abort 합니다. 그다음 git -c merge.conflictStyle=zdiff3 merge topic 으로 같은 충돌을 다시 내면 칸이 하나 더 생깁니다. 그 칸이 무엇인지 한 줄 덧붙이세요.

해결을 한 번 기록시킨다

rerere 를 켜고 충돌을 한 번 풀어 병합을 마칩니다.

git config rerere.enabled true 로 켠 뒤 git merge topic 을 합니다. 충돌한 handler.txt 의 둘째 줄을 양쪽을 살린 b by main and topic 으로 고치고 git add 후 커밋하세요. 커밋할 때 git 이 무엇을 기록했다고 말하는지, 그리고 .git/rr-cache 에 무엇이 생겼는지 보세요.

두 번째부터는 git 이 대신 푼다

그 병합을 되돌리고 다시 병합해, git 이 같은 해결을 스스로 적용하는 것을 notes/replay.txt 에 남깁니다.

git reset --hard HEAD~1 로 병합 커밋을 지우고 git merge topic 을 다시 합니다. 출력에 한 줄이 더 붙고, handler.txt 는 이미 해결된 상태입니다. 그런데 git status 를 보면 아직 UU 입니다 — 적용만 하고 스테이징은 하지 않기 때문입니다. git add 하고 커밋해서 마치세요.

충돌한 자리만 우리 것으로

sidefix-X ours 로 병합하고 결과를 notes/xours.txt 에 남깁니다.

git merge -X ours -m '<메시지>' sidefix 를 합니다. 둘째 줄은 충돌했으니 우리 것이 남고, 여섯째 줄은 충돌하지 않았으니 sidefix 의 변경이 그대로 들어옵니다. sidefix-only.txt 도 들어옵니다. 결과 파일을 그대로 옮겨 적고 무엇이 남고 무엇이 들어왔는지 한 줄 덧붙이세요.

합쳤다고만 적고 아무것도 가져오지 않기

legacy-s ours 로 병합하고 결과를 notes/sours.txt 에 남깁니다.

git merge -s ours -m '<메시지>' legacy 를 합니다. 끝나고 legacy.txt 를 찾아보면 없습니다. git diff --name-only HEAD^1 HEAD 가 아무것도 내지 않는지도 보세요 — 결과 트리가 병합 전과 한 글자도 다르지 않다는 뜻입니다. 그런데 git merge-base --is-ancestor legacy main 은 참입니다.

이름만 닮은 두 손잡이

두 손잡이의 차이를 notes/strategies.txt 에 정리합니다.

표로 쓰세요. 무엇인가(옵션 대 전략), 충돌하지 않은 상대 변경은 어떻게 되는가, 상대가 더한 새 파일은 어떻게 되는가, 언제 쓰는가 네 줄이면 됩니다. 5·6단계에서 직접 본 파일 이름을 근거로 들어야 합니다.

반복 충돌을 줄이는 방법

notes/report.md 에 반복 충돌을 줄이는 방법을 정리합니다.

설정 두 줄(rerere.enabled, merge.conflictStyle)이 왜 기본으로 켜 둘 만한지, rerere 가 기억하는 것과 기억하지 않는 것, 잘못 푼 해결을 지우는 법, 그리고 애초에 충돌을 덜 만드는 습관까지 적으세요.