본문 바로가기
빌드노트 검색

빌드노트

git diff --no-index가 종료 1일 때는 오류 출력도 읽는다

git diff --no-index의 정상 차이는 종료 1이지만, Git 2.45.1에서는 없는 경로도 1이었다. 저장소 밖 합성 파일로 출력과 종료 코드를 비교하고, 오류 출력을 보존해 동일·차이·확인 필요를 나누는 자동화 예제를 제시한다.

두 파일을 git diff --no-index로 비교했는데 차이까지 출력한 명령이 자동화에서는 실패로 처리될 수 있다. 이 비교는 차이가 있으면 종료 코드 1, 차이가 없으면 0을 반환한다. --no-index에 --exit-code가 포함되기 때문이다. 일반적인 “0이 아니면 실행 장애” 규칙을 그대로 적용하면 정상적인 차이 발견도 실패로 묶인다. Git diff 공식 문서 (새 탭에서 열림)

그렇다고 1을 무조건 정상 차이로 허용해도 안 된다. 이 글의 Git 2.45.1 실험에서는 없는 경로를 비교했을 때도 1이었다. 자동화가 구분해야 하는 것은 숫자 두 개가 아니라, 비교에서 차이를 찾지 못한 경우·차이를 찾은 경우·비교를 완료하지 못한 경우다. 종료 코드와 표준 출력, 오류 출력을 함께 남겨야 한다.

내용 차이와 없는 경로가 같은 코드였다

Windows 11, Git 2.45.1.windows.1에서 저장소 밖의 새 텍스트 파일 두 개를 비교했다. before.txt에는 mode=dev 한 줄을 넣었다. 동일 조건에서는 after.txt도 mode=dev, 차이 조건에서는 mode=prod로 바꿨다. 모든 파일은 ASCII 문자와 줄 끝 LF로 만들었다. 마지막에는 두 번째 인자만 존재하지 않는 absent.txt로 바꿨다. 실제 서비스 파일이나 운영 장애 기록을 사용한 실험은 아니다.

자기 파일에서도 먼저 차이 출력과 Git의 종료 코드를 한 번에 확인한다. 다음은 Git이 PATH에 있는 PowerShell에서 두 파일이 있는 폴더를 기준으로 실행하는 명령이다. 파일 이름을 자기 경로로 바꾼다. $LASTEXITCODE는 직전에 실행한 외부 프로그램의 종료 코드이므로 다른 외부 명령을 실행하기 전에 읽는다. PowerShell 자동 변수 문서 (새 탭에서 열림)

git --no-pager diff --no-index --no-color --no-ext-diff --no-textconv -- before.txt after.txt
$LASTEXITCODE

--no-pager와 --no-color는 결과를 바로 읽기 위한 옵션이다. --no-ext-diff와 --no-textconv는 외부 diff 도구와 텍스트 변환 필터를 사용하지 않도록 한다. 이 글에서는 두 일반 텍스트 파일의 비교에 범위를 맞췄다. Git diff 옵션 (새 탭에서 열림)

실제로 수집한 결과는 다음과 같다. 조건명과 exit·stdout·stderr 표시는 결과를 구분하려고 붙였고, (비어 있음)은 출력이 없었다는 뜻이다. 없는 경로 조건은 위 명령의 after.txt만 absent.txt로 바꿔 실행했다.

동일: exit=0
stdout: (비어 있음)
stderr: (비어 있음)

내용 차이: exit=1
stdout:
diff --git a/before.txt b/after.txt
index 5069812..a8502b9 100644
--- a/before.txt
+++ b/after.txt
@@ -1 +1 @@
-mode=dev
+mode=prod
stderr: (비어 있음)

없는 경로: exit=1
stdout: (비어 있음)
stderr:
error: Could not access 'absent.txt'

내용 차이 조건에서는 -mode=dev와 +mode=prod가 나왔다. 비교가 이루어졌고 값이 달랐다. 없는 경로 조건에서는 차이 출력 없이 접근 오류가 나왔다. 종료 코드만 보면 두 조건을 나눌 수 없었다.

공식 문서가 설명하는 0·1의 비교 결과를 근거로 “실행 오류는 반드시 2 이상”이라고 가정하면 이 조건을 놓친다. 업스트림 Git v2.45.1의 구현을 읽어도 경로 접근 실패가 전달되면 초기 반환값 1을 유지한 채 빠져나오는 흐름이 있다. 이는 이번 관찰과 맞는 구현 근거이며, 모든 버전과 모든 오류가 1이라는 보장은 아니다. Git v2.45.1의 no-index 구현 (새 탭에서 열림)

자동화는 차이와 확인이 필요한 결과를 나눈다

Python에서 subprocess.run(..., check=True)로 실행하면 정상 차이의 종료 1도 예외가 된다. Python 문서에서 check=True는 0이 아닌 종료 코드를 예외로 바꾸는 옵션이다. 따라서 비교 결과를 직접 분기할 때는 check=False로 받고 returncode를 검사한다. Python subprocess 문서 (새 탭에서 열림)

아래 코드는 표준 출력과 오류 출력을 모두 보존해 보여 주고, 오류 출력이 있는 결과는 확인 대상으로 남긴다. Python 3.12와 Git이 설치되어 있어야 한다. 코드를 compare.py로 저장하고, 비교할 before.txt와 after.txt가 있는 폴더에서 python compare.py로 실행한다. 두 입력 이름은 코드에서 실제 파일 경로로 바꿀 수 있다.

import subprocess
import sys

result = subprocess.run(
    ["git", "--no-pager", "diff", "--no-index", "--no-color",
     "--no-ext-diff", "--no-textconv", "--", "before.txt", "after.txt"],
    capture_output=True, text=True, encoding="utf-8", errors="replace",
    check=False, timeout=5,
)
print(result.stdout, end="")
print(result.stderr, end="", file=sys.stderr)

if result.stderr or result.returncode not in (0, 1):
    raise SystemExit(f"비교 확인 필요: git 종료 코드 {result.returncode}")
if result.returncode == 0:
    print("SAME: 비교에서 차이를 찾지 못했습니다.")
elif result.stdout:
    print("DIFFERENT: 두 파일의 차이를 확인하세요.")
else:
    raise SystemExit("비교 확인 필요: 종료 1인데 차이 출력이 없습니다.")

이 코드는 오류 출력이 비어 있을 때만 0을 SAME, 차이 출력이 있는 1을 DIFFERENT로 처리한다. 그 밖의 종료 코드, 오류 출력이 있는 결과, 차이 출력 없는 1은 자동 확정하지 않는다. stderr의 경고도 검토 대상으로 보내는 보수적인 정책이다. 오류 메시지의 영문 문장에 맞춘 문자열 비교는 하지 않는다.

Python 3.12.10에서 위 코드도 직접 실행했다. 동일 조건은 SAME, 내용 차이 조건은 패치와 DIFFERENT를 출력했으며 두 경우 모두 Python 프로그램은 0으로 끝났다. after.txt를 없앤 조건에서는 Git의 접근 오류와 비교 확인 필요: git 종료 코드 1이 나왔고, Python 프로그램은 1로 끝났다. Git의 종료 코드와 그것을 해석한 Python 프로그램의 종료 코드는 별개다.

여기서는 파일이 달라도 비교를 정상 수행한 자동화는 계속 진행하게 정했다. 산출물이 반드시 같아야 하는 검사라면 DIFFERENT 분기에서 검사 실패를 반환하면 된다. 그때도 이유는 “내용 불일치”로 남겨 경로 접근 오류와 구분한다. 모든 비정상 종료를 무시하는 방식으로 바꾸면 없는 파일까지 통과시킬 수 있다.

출력 방식까지 고정한 범위에서 사용한다

위 분기는 패치를 출력하는 현재 명령에 맞춘 것이다. --quiet처럼 출력을 숨기는 옵션을 추가하면 “1인데 출력이 없다”는 조건의 의미도 바뀐다. 옵션을 바꿀 때는 동일·차이·없는 경로 세 입력으로 다시 확인한다. 공식 문서에서 --quiet는 출력을 끄며 --exit-code를 포함하는 별도 옵션이다. Git diff 출력 제어 (새 탭에서 열림)

실험은 사용자 Git 설정을 배제한 자식 프로세스에서 진행했고, PowerShell 7.6.6의 새 프로필 없는 세션에서도 위 조회 명령을 실행했다. 다른 Git 버전, 디렉터리·바이너리 비교, 권한 거부, 비교 도중 파일 변경은 실행하지 않았다. Git 실행 파일을 찾지 못하거나 제한 시간을 넘기면 Python에서 예외가 발생하며, 이것도 파일 차이로 처리하지 않는다. 실제 오류가 언제나 특정 숫자로 끝난다는 전제 대신, 사용하는 명령의 출력과 종료 상태를 함께 확인해야 한다.

← 목록으로 돌아가기