예전에 테스트 커버리지도 없고 추상화도 엉망인 거대한 코드베이스에서 일할 때, git bisect는 거의 유일하게 쓸 만한 도구였음
코드가 너무 복잡해서 논리적으로 버그를 추적하는 게 불가능했기 때문에, 어떤 커밋에서 문제가 생겼는지를 찾는 게 훨씬 쉬웠음
하지만 품질이 높은 코드베이스에서는 굳이 bisect가 필요하지 않았음. 각 컴포넌트를 독립적으로 테스트할 수 있고, 관측성(observability) 도 잘 되어 있어서 어디를 봐야 할지 명확했음
git bisect는 절대 불필요하지 않다고 생각함. 단순히 버그를 찾는 것뿐 아니라 왜 그 버그가 생겼는지를 이해하게 해줌
커밋 메시지가 충실한 프로젝트라면, bisect를 통해 과거 커밋의 맥락을 파악하고 그 내용을 버그 수정 커밋에 반영할 수 있음. 이런 순환이 커밋 문화 자체를 강화시킴
예전에 OSS 프로그램에서 이상한 문자열이 들어간 버그를 찾은 적이 있음. C 코드였고 초기화되지 않은 변수 때문이었음
직접 추적은 불가능했지만, bisect 스크립트를 작성해 30분 정도 돌리니 문제의 커밋을 정확히 찾아냈음
git bisect는 원래 Linux 커널 회귀(regression) 를 찾기 위해 도입된 도구였음
하드웨어 드라이버처럼 테스트가 불가능한 경우에도, 일반 사용자가 직접 커널을 bisect해서 문제 커밋을 특정할 수 있게 되었음
예전에는 이메일로 개발자에게 도움을 요청해야 했지만, 이제는 사용자가 스스로 문제를 좁혀갈 수 있게 되었음
단순히 버그를 고치는 게 목적이라면 bisect가 필요 없을 수도 있음. 하지만 버그가 언제부터 존재했는지 알아야 할 때가 있음
예를 들어 잘못 처리된 데이터의 범위를 추적하거나, “이게 버그인가 기능인가”를 판단할 때 유용함
때로는 버그가 언제 수정되었는지 알아야 할 때도 있음
예를 들어 고객이 6년 전 버전에서 문제를 겪고 있을 때, 4년 전 버전으로 업그레이드하면 해결되는지 확인할 수 있음
혹은 코드가 크게 리팩터링되었을 때, 버그 수정이 의도적이었는지 우연이었는지도 파악 가능함
git bisect는 잘 작동할 때는 훌륭하지만, 모든 버그를 찾을 수 있는 건 아님
어떤 버그는 도입 당시에는 증상이 없고, 나중에 다른 변경으로 인해 드러나기도 함
이런 경우 bisect의 전제(좋은 커밋과 나쁜 커밋 사이에 단 한 번만 버그가 등장함)가 깨짐
테스트 불가능한 커밋은 skip할 수 있지만, 그게 문제 커밋이면 결과가 애매해짐
최근 처음으로 진지하게 git bisect를 써봤는데, 거의 마법 같음
두 개의 동일한 이름의 함수가 있었고, 코드 포매팅 작업 중에 올바른 함수의 import가 제거되면서 문제가 발생했음
여러 번 코드를 검토했지만 bisect로 문제 커밋을 특정하기 전까지는 원인을 전혀 몰랐음
나는 보통 버그가 생긴 파일이나 함수의 범위를 이미 알고 있어서 bisect를 자주 쓰진 않음
대신 git log -L :func_name:path/to/file.c 명령으로 특정 함수의 변경 이력을 추적함 .gitattributes 설정이 필요함
.gitattributes 설정이 궁금하다는 질문이 있었음. 어떤 내용이 필요한지 더 알고 싶다는 반응이었음
매일 bisect를 쓴다는 사람도 있었음. 워크플로우가 완전히 다름
C++처럼 다형성 함수가 있는 경우에는 git log -L이 약함. 동일한 이름의 오버로드 함수 중 특정 버전을 추적하기 어렵기 때문임
.gitattributes가 없다면 git log -S로 특정 문자열이 포함된 커밋을 찾는 것도 방법임
테스트 스크립트에서 exit code 125를 알아두면 좋음
빌드 실패처럼 테스트 결과를 판별할 수 없는 경우, 125를 반환하면 bisect가 해당 커밋을 건너뜀
관련 내용을 내 블로그 글에 정리했음
병합 커밋이 CI 통과 지점을 의미하는 저장소에서는 git bisect --first-parent를 쓰면 유용함
이렇게 하면 “어떤 PR이 버그를 도입했는가”를 빠르게 찾을 수 있고, 이후 해당 브랜치에서 세부 bisect를 한 번 더 돌리면 됨
플레이키 테스트(flaky test) 가 생겼을 때 bisect가 빛을 발함
레이스 컨디션으로 인해 테스트를 수십만 번 돌려야 확신할 수 있는 경우, bisect 스크립트를 백그라운드로 돌려두면 현실적으로 해결 가능함
이런 경우에는 베이지안 이진 탐색을 적용하면 테스트 횟수를 훨씬 줄일 수 있을 것 같음
최근 Svelte로 만든 음악 플레이어 프로젝트(lets-make-sweet-music.com)에서 bisect로 버그 원인을 찾았음
테스트도 없고, 오류 로그도 없었는데, dependabot 업데이트로 커밋이 많아져서 추적이 어려웠음
bisect 덕분에 문제의 커밋을 찾아냈고, 원인은 내가 교체한 파일이 이벤트 다중 바인딩 기능을 구현하지 않았던 것임
커밋을 작게 유지하면 bisect로 찾은 문제의 원인을 빠르게 좁힐 수 있음
누군가 “면접에서 이진 탐색(binary search) 을 배워야 한다는 건 억지”라고 했는데, git bisect는 그 개념을 실제로 보여주는 좋은 예임
하지만 직접 구현할 필요는 없음. 대부분의 언어에는 이미 표준 라이브러리로 제공됨
흥미롭게도, 이진 탐색이 1940년대에 처음 제안됐지만 버그 없는 구현이 1960년대에야 나왔다는 이야기가 있음
중간 인덱스를 계산할 때 (low + high) / 2로 하면 오버플로가 생길 수 있음
개인적으로는 모든 개발자가 한 번쯤 임의 정밀도 정수 언어(예: Python)로 이진 탐색을 직접 구현해보는 게 좋다고 생각함 불변식(invariant) 기반 사고를 훈련하는 최고의 연습임
Git에는 bisect 외에도 log -L, log -S, blame 같은 훌륭한 코드 탐색 도구가 있음
예전에 이 주제로 블로그 글을 쓴 적이 있음
Hacker News 의견
예전에 테스트 커버리지도 없고 추상화도 엉망인 거대한 코드베이스에서 일할 때,
git bisect는 거의 유일하게 쓸 만한 도구였음코드가 너무 복잡해서 논리적으로 버그를 추적하는 게 불가능했기 때문에, 어떤 커밋에서 문제가 생겼는지를 찾는 게 훨씬 쉬웠음
하지만 품질이 높은 코드베이스에서는 굳이 bisect가 필요하지 않았음. 각 컴포넌트를 독립적으로 테스트할 수 있고, 관측성(observability) 도 잘 되어 있어서 어디를 봐야 할지 명확했음
git bisect는 절대 불필요하지 않다고 생각함. 단순히 버그를 찾는 것뿐 아니라 왜 그 버그가 생겼는지를 이해하게 해줌커밋 메시지가 충실한 프로젝트라면, bisect를 통해 과거 커밋의 맥락을 파악하고 그 내용을 버그 수정 커밋에 반영할 수 있음. 이런 순환이 커밋 문화 자체를 강화시킴
직접 추적은 불가능했지만, bisect 스크립트를 작성해 30분 정도 돌리니 문제의 커밋을 정확히 찾아냈음
git bisect는 원래 Linux 커널 회귀(regression) 를 찾기 위해 도입된 도구였음하드웨어 드라이버처럼 테스트가 불가능한 경우에도, 일반 사용자가 직접 커널을 bisect해서 문제 커밋을 특정할 수 있게 되었음
예전에는 이메일로 개발자에게 도움을 요청해야 했지만, 이제는 사용자가 스스로 문제를 좁혀갈 수 있게 되었음
예를 들어 잘못 처리된 데이터의 범위를 추적하거나, “이게 버그인가 기능인가”를 판단할 때 유용함
예를 들어 고객이 6년 전 버전에서 문제를 겪고 있을 때, 4년 전 버전으로 업그레이드하면 해결되는지 확인할 수 있음
혹은 코드가 크게 리팩터링되었을 때, 버그 수정이 의도적이었는지 우연이었는지도 파악 가능함
git bisect는 잘 작동할 때는 훌륭하지만, 모든 버그를 찾을 수 있는 건 아님어떤 버그는 도입 당시에는 증상이 없고, 나중에 다른 변경으로 인해 드러나기도 함
이런 경우 bisect의 전제(좋은 커밋과 나쁜 커밋 사이에 단 한 번만 버그가 등장함)가 깨짐
테스트 불가능한 커밋은
skip할 수 있지만, 그게 문제 커밋이면 결과가 애매해짐최근 처음으로 진지하게
git bisect를 써봤는데, 거의 마법 같음두 개의 동일한 이름의 함수가 있었고, 코드 포매팅 작업 중에 올바른 함수의 import가 제거되면서 문제가 발생했음
여러 번 코드를 검토했지만 bisect로 문제 커밋을 특정하기 전까지는 원인을 전혀 몰랐음
나는 보통 버그가 생긴 파일이나 함수의 범위를 이미 알고 있어서 bisect를 자주 쓰진 않음
대신
git log -L :func_name:path/to/file.c명령으로 특정 함수의 변경 이력을 추적함.gitattributes설정이 필요함.gitattributes설정이 궁금하다는 질문이 있었음. 어떤 내용이 필요한지 더 알고 싶다는 반응이었음git log -L이 약함. 동일한 이름의 오버로드 함수 중 특정 버전을 추적하기 어렵기 때문임.gitattributes가 없다면git log -S로 특정 문자열이 포함된 커밋을 찾는 것도 방법임테스트 스크립트에서 exit code 125를 알아두면 좋음
빌드 실패처럼 테스트 결과를 판별할 수 없는 경우, 125를 반환하면 bisect가 해당 커밋을 건너뜀
관련 내용을 내 블로그 글에 정리했음
git bisect --first-parent를 쓰면 유용함이렇게 하면 “어떤 PR이 버그를 도입했는가”를 빠르게 찾을 수 있고, 이후 해당 브랜치에서 세부 bisect를 한 번 더 돌리면 됨
플레이키 테스트(flaky test) 가 생겼을 때 bisect가 빛을 발함
레이스 컨디션으로 인해 테스트를 수십만 번 돌려야 확신할 수 있는 경우, bisect 스크립트를 백그라운드로 돌려두면 현실적으로 해결 가능함
최근 Svelte로 만든 음악 플레이어 프로젝트(lets-make-sweet-music.com)에서 bisect로 버그 원인을 찾았음
테스트도 없고, 오류 로그도 없었는데,
dependabot업데이트로 커밋이 많아져서 추적이 어려웠음bisect 덕분에 문제의 커밋을 찾아냈고, 원인은 내가 교체한 파일이 이벤트 다중 바인딩 기능을 구현하지 않았던 것임
커밋을 작게 유지하면 bisect로 찾은 문제의 원인을 빠르게 좁힐 수 있음
누군가 “면접에서 이진 탐색(binary search) 을 배워야 한다는 건 억지”라고 했는데,
git bisect는 그 개념을 실제로 보여주는 좋은 예임하지만 직접 구현할 필요는 없음. 대부분의 언어에는 이미 표준 라이브러리로 제공됨
중간 인덱스를 계산할 때
(low + high) / 2로 하면 오버플로가 생길 수 있음불변식(invariant) 기반 사고를 훈련하는 최고의 연습임
Git에는 bisect 외에도
log -L,log -S,blame같은 훌륭한 코드 탐색 도구가 있음예전에 이 주제로 블로그 글을 쓴 적이 있음