git bisect run으로 버그 커밋 찾기: 종료 코드 0, 125, 127의 의미

git bisect가 이진 탐색으로 첫 bad 커밋을 찾는 방식과, bisect run이 종료 코드를 0은 good, 1~124와 126~127은 bad, 125는 skip, 그 밖의 값은 중단으로 읽는 규칙을 Git 공식 문서로 정리했습니다.

개발Git버전 관리테스트

지난 릴리스에서는 통과하던 테스트가 지금은 실패하고, 그 사이에 커밋이 수백 개 있다면 하나씩 확인하기는 어렵습니다. git bisect는 이 범위를 이진 탐색으로 좁혀 문제를 처음 만든 커밋을 찾습니다. git bisect run에 판정 스크립트를 넘기면 good과 bad를 직접 입력할 필요도 없습니다. 다만 Git이 스크립트의 종료 코드를 읽는 규칙을 모르면 엉뚱한 커밋이 범인으로 나올 수 있습니다.

이 글은 Git 2.56.0(2026년 9월 28일) 기준 git-bisect 공식 문서와, git-scm.com에 함께 실린 해설 문서 git-bisect-lk2009를 따릅니다.

기본 흐름

버그가 있는 커밋(bad) 하나와 버그가 생기기 전 커밋(good) 하나를 알려 주면 시작됩니다.

git bisect start
git bisect bad                 # 지금 버전은 버그가 있음
git bisect good v2.6.13-rc2    # 이 버전은 정상이었음

Git은 그 사이 범위의 가운데쯤에 있는 커밋을 체크아웃하고 다음처럼 알려 줍니다.

Bisecting: 675 revisions left to test after this (roughly 10 steps)

그 버전을 빌드하고 테스트한 뒤 git bisect good이나 git bisect bad로 답하면 남은 후보가 대략 절반씩 줄어듭니다. 676개 남짓한 후보가 약 10단계로 끝나는 것은 2¹⁰ = 1024이기 때문입니다. 더 확인할 커밋이 없으면 Git은 첫 bad 커밋을 출력하고, refs/bisect/bad가 그 커밋을 가리킨 채로 남습니다. 끝나면 git bisect reset으로 시작 전의 HEAD로 돌아갑니다.

이력이 한 줄이고 판정이 good에서 bad로 한 번만 바뀐다면, 이 문제는 정렬된 배열에서 조건이 처음 참이 되는 위치를 찾는 문제와 같습니다. 이진 탐색의 삽입 위치에서 다룬 bisect_left가 같은 경계를 찾습니다.

bisect run이 종료 코드를 읽는 규칙

git bisect start HEAD v1.2 --   # HEAD는 bad, v1.2는 good
git bisect run make             # make가 앱을 빌드함
git bisect reset

git bisect run은 커밋마다 명령을 실행하고, 종료 코드로 그 커밋을 판정합니다.

종료 코드 처리
0 good(old)
1~127, 125 제외 bad(new)
125 테스트할 수 없는 커밋으로 보고 건너뜀(skip)
그 밖의 값 bisect 자체를 중단

여기서 실수가 잘 나옵니다. 첫째, exit(-1)로 끝난 프로그램은 값이 & 0377로 잘려 종료 코드가 255가 되므로 bad가 아니라 중단으로 처리됩니다. 둘째, 126과 127은 POSIX 셸이 “실행할 수 없음”과 “명령을 찾을 수 없음”에 쓰는 값이지만, bisect run에게는 1~127 범위의 평범한 실패일 뿐입니다. 스크립트가 127로 끝나면 그 커밋은 bad로 집계됩니다. 테스트 도구가 옛 커밋에 아직 없어서 실패했다면 Git은 그것을 버그로 받아들입니다.

빌드조차 안 되는 커밋은 125로 빼야 합니다. 공식 문서의 예는 이렇습니다.

#!/bin/sh
make || exit 125                     # 빌드가 안 되는 커밋은 건너뜀
~/check_test_case.sh                 # 테스트 케이스가 통과하는가

공식 문서는 테스트 스크립트를 저장소 밖에 두라고 권하고, 예제도 ~/에 둡니다. bisect는 커밋을 바꿀 때마다 작업 트리를 바꾸므로, 저장소 안에서 추적되는 스크립트라면 옛 커밋에는 없거나 내용이 다를 수 있습니다.

건너뛴 커밋이 많을 때

skip은 공짜가 아닙니다. 찾는 커밋 바로 옆의 커밋을 건너뛰면 Git은 그중 어느 것이 첫 bad 커밋인지 정확히 말하지 못합니다. 이때는 후보 커밋 여러 개를 보여 주고 멈춥니다.

이진 탐색이 성립하는 조건

git-bisect-lk2009는 커밋 그래프에 첫 bad 커밋이 여러 개 있을 수 있다고 밝히고, 범위를 나눌 가장 좋은 커밋을 고르는 알고리즘을 설명할 때만 첫 bad 커밋이 하나뿐이라고 가정합니다. 실제로 중요한 것은 판정 기준이 탐색하는 이력을 따라 good에서 bad로 바뀌는 지점을 쓸모 있게 보여 주는가입니다. 버그가 생겼다가 고쳐지고 다시 생기거나 빌드 결과에 따라 나타났다 사라지면 판정이 오락가락하고, bisect가 알려 주는 경계는 원인과 무관하거나 실행할 때마다 달라질 수 있습니다. 판정을 잘못 입력했다면 git bisect log를 파일로 저장해 틀린 줄을 지우고, git bisect reset 뒤 git bisect replay <파일>로 되돌릴 수 있습니다.

몇 가지 옵션도 알아 두면 좋습니다.

  • 버그를 고친 커밋처럼 “good/bad”가 어색한 변화를 찾을 때는 인자 없이 git bisect start를 실행한 뒤 git bisect old, git bisect new로 표시합니다.
  • --first-parent는 병합 커밋에서 첫 번째 부모만 따라갑니다. 병합된 브랜치 안에 빌드가 안 되는 커밋이 있어도 병합 결과는 괜찮았다면, 병합 커밋을 범인으로 지목해 이런 오탐을 피합니다.
  • --reset-when-found는 첫 bad 커밋을 찾으면 정리까지 해 줍니다. 2.56.0 문서에는 있지만 2.55.0 문서에는 없는 옵션이므로 쓰기 전에 git --version을 확인하세요.

커밋 범위를 적는 A..B 표기는 git log와 git diff의 ..과 …에서 설명했습니다.

참고한 문서

링크와 내용은 2026년 10월 5일에 확인했습니다.

블로그로