diff가 실제로 무엇을 비교하는가
한 글자만 바뀌어도 줄 전체가 바뀐 것으로 나오고, 블록을 옮기면 삭제와 추가로 잡힙니다. diff의 동작과 그 결과를 읽는 법을 정리했습니다.
diff는 두 텍스트의 차이를 알려 준다고 하지만, 실제로 하는 일은 한쪽을 다른 쪽으로 바꾸는 편집 목록 중 짧은 것을 찾는 일입니다. "무엇이 바뀌었는가"가 아니라 "어떻게 바꾸면 되는가"에 답합니다. 결과가 예상과 다르게 보이는 이유가 대부분 여기 있습니다.
비교 단위가 결과를 정한다
한 글자만 다른 두 문장을 줄 단위로 비교하면 이렇게 나옵니다.
-the quick brown fox jumps over the lazy dog
+the quick brown cat jumps over the lazy dog
줄 전체가 바뀐 것으로 표시됩니다. 같은 두 문장을 문자 단위로 비교하면 다릅니다.
유사도 : 0.930
바뀐 구간 : replace 'fox' → 'cat'
diff가 틀린 것이 아닙니다. 비교 단위가 줄이면 줄이 최소 단위이므로 그 안에서 무엇이 바뀌었는지는 표현할 수 없습니다.
Git이 기본으로 줄 단위를 쓰는 이유는 코드가 줄로 구성되고, 결과가 짧고, 병합에 쓰기 좋기 때문입니다. 산문이나 긴 한 줄짜리 파일에서는 잘 맞지 않습니다. git diff --word-diff가 단어 단위로 다시 보여 줍니다.
블록을 옮기면 삭제와 추가가 된다
함수 두 개의 순서만 바꿔 보겠습니다.
@@ -0,0 +1,3 @@
+def b():
+ return 2
+
@@ -3,3 +5,0 @@
-
-def b():
- return 2
내용은 하나도 바뀌지 않았는데 3줄 추가, 3줄 삭제로 나옵니다. diff는 이동이라는 개념을 갖고 있지 않습니다. 편집 목록에 "옮기기"라는 연산이 없으므로 지우고 다시 쓰는 것으로 표현합니다.
리뷰에서 이 차이를 줄이려면 이동과 수정을 다른 커밋으로 나누는 방법이 있습니다. 도구 쪽 대응도 있습니다 — git diff --color-moved는 옮겨진 블록을 다른 색으로 표시합니다.
JSON은 텍스트로 비교하면 커진다
키 순서만 다른 두 객체입니다.
{"name": "a", "id": 1, "tags": ["x"]}
{"id": 1, "name": "a", "tags": ["x"]}
두 객체가 같은가 : true
텍스트 diff 차이 : 2줄
키를 정렬하고 비교 : 0줄
파싱해서 비교하면 같고, 텍스트로 비교하면 다릅니다. API 응답을 비교하거나 설정 파일 변경을 볼 때 이 차이가 계속 나옵니다.
대응은 둘 중 하나입니다. 비교하기 전에 키를 정렬해 정규화하거나, 구조를 이해하는 비교 도구를 쓰는 것입니다. 후자는 배열 순서 같은 의미 있는 차이는 남기고 키 순서나 들여쓰기는 무시합니다.
알고리즘
대부분의 diff는 최장 공통 부분 수열(LCS)을 찾는 문제로 환원됩니다. 양쪽에 공통으로 남는 줄을 최대한 많이 찾고, 나머지를 삭제와 추가로 처리합니다.
Git이 기본으로 쓰는 것은 Myers 알고리즘입니다. 편집 거리가 작을 때 빠르게 동작하도록 설계됐습니다. 실제 변경은 대개 파일 전체에 비해 작으므로 잘 맞습니다.
Git에는 다른 선택지도 있습니다.
--diff-algorithm=patience— 양쪽에 딱 한 번씩만 나오는 줄을 기준점으로 삼습니다. 함수 본문이 비슷해서 앵커가 엉뚱하게 잡히는 경우에 결과가 나아집니다--diff-algorithm=histogram— patience의 개선판. 대체로 더 빠릅니다--diff-algorithm=minimal— 가장 짧은 diff를 찾습니다. 느립니다
기본값이 이상하게 나올 때 patience를 시도해 볼 만합니다.
공백과 줄바꿈
diff가 크게 나오는 흔한 원인입니다.
- 줄 끝 공백 — 편집기가 자동으로 지우면 손대지 않은 줄이 바뀐 것으로 나옵니다
- CRLF와 LF — 파일 전체가 바뀐 것으로 나옵니다.
.gitattributes로 정합니다 - 들여쓰기 폭 변경 — 내용은 그대로인데 모든 줄이 바뀝니다. 포매터를 도입하는 커밋은 따로 분리하고
.git-blame-ignore-revs에 등록합니다 - 파일 끝 개행 없음 —
\ No newline at end of file로 표시됩니다
git diff -w는 공백 차이를 무시하고 보여 줍니다. 커밋에는 반영되지 않고 보기만 바꿉니다.
확인 순서
- diff가 예상보다 크면 먼저 공백과 줄바꿈을 의심합니다.
git diff -w로 비교해 봅니다 - 줄 안의 작은 변경을 찾는다면 단어 단위로 다시 봅니다
- 블록 이동이 섞여 있으면
--color-moved로 확인합니다 - JSON이나 YAML은 파싱해서 비교합니다. 텍스트 diff의 차이 대부분이 순서와 들여쓰기입니다
- 앵커가 엉뚱하게 잡혔다면
--diff-algorithm=patience를 써 봅니다
tools.onuel.dev의 텍스트 비교는 두 텍스트를 줄 단위로 비교해 추가·삭제·변경되지 않은 부분을 보여 줍니다. JSON이라면 JSON 비교가 구조로 비교하므로 키 순서나 들여쓰기 차이는 변경으로 잡히지 않습니다. 비교 전에 형식을 맞춰 두려면 JSON 포매터에서 양쪽을 같은 형태로 정리하면 됩니다.
- #diff
- #버전관리
- #도구