git bisect를 아는 사람은
1%뿐이다
500개 커밋에서 범인을 9번 만에 찾고, 두 브랜치를 동시에 열고, 같은 충돌을 두 번 풀지 않는 방법. Git에는 대부분이 모르는 무기가 있다.
500개 커밋에서 범인을 9번 만에 찾는 법
버그가 발견됐다. 언제부터 있었는지 모른다. 지난 한 달간 쌓인 커밋은 500개. 하나씩 checkout해서 테스트하면 500번이다. 하지만 이진 탐색(binary search)을 적용하면 이야기가 달라진다. log2(500)은 약 9. 최대 9번의 확인으로 범인 커밋을 특정할 수 있다. 그것이 git bisect다.
O(log n)
100개 커밋이면 7번, 1,000개 커밋이면 10번, 10,000개 커밋이면 14번. 선형 탐색과 비교 자체가 불가능한 효율이다.
이진 탐색
정상 커밋과 버그 커밋 사이의 중간 지점을 반복적으로 테스트한다. 매 단계마다 탐색 범위가 절반으로 줄어든다.
범인 커밋 특정
최종 결과는 단 하나의 커밋 해시. 누가, 언제, 어떤 변경으로 버그를 유발했는지 정확히 알 수 있다.
사용법은 단순하다. 시작 → 좋은 커밋 지정 → 나쁜 커밋 지정 → 반복 판정 → 종료. Git이 중간 커밋을 자동으로 checkout해주고, 개발자는 good 또는 bad만 알려주면 된다.
수동으로 good/bad를 판정하는 것도 나쁘지 않지만, 자동화가 가능하다. 테스트 스크립트를 넘기면 Git이 알아서 모든 것을 처리한다. 개발자는 커피를 마시며 결과를 기다리면 된다.
종료 코드 125는 특별하다. 빌드가 깨진 커밋처럼 테스트 자체를 실행할 수 없는 경우에 사용한다. bisect는 해당 커밋을 건너뛰고 인접 커밋으로 이동한다. 또한 good/bad 용어 대신 old/new를 사용할 수도 있다. 버그가 아니라 특정 동작이 언제 도입됐는지 찾을 때 유용하다.
bisect는 디버깅의 grep이다.
존재를 모르면 쓸 수 없고, 한 번 쓰면 없이는 못 산다.
두 브랜치를 동시에 여는 마법
feature 브랜치에서 리팩토링 작업 중이다. 코드를 절반쯤 뜯어놓은 상태에서 슬랙 알림이 온다. "프로덕션 긴급 핫픽스 부탁합니다." 선택지는 세 가지. stash로 임시 저장하고 브랜치를 전환하거나, 작업을 커밋하거나, 아예 레포를 하나 더 클론하거나. 전부 불편하다.
git worktree는 네 번째 선택지다. 하나의 .git 디렉토리를 공유하면서 여러 개의 작업 디렉토리를 동시에 유지한다. 각 디렉토리는 독립된 브랜치를 checkout한 상태이므로, 리팩토링을 중단하지 않고 핫픽스 작업을 병렬로 진행할 수 있다.
리팩토링 중 긴급 핫픽스 요청이 들어왔다. stash 대신 worktree를 사용하면 현재 작업을 한 줄도 건드리지 않고 핫픽스 브랜치를 별도 디렉토리에서 작업할 수 있다. 핫픽스가 끝나면 그 디렉토리만 삭제하면 된다.
.git/objects 공유
클론과 달리 오브젝트 스토어를 공유한다. 디스크 공간이 두 배가 되지 않는다. 대형 레포에서 특히 유효하다.
독립된 빌드 환경
각 worktree는 독립된 node_modules, build 디렉토리를 가진다. 브랜치 전환 시 빌드 캐시가 날아가는 문제가 없다.
AI 기반 병렬 개발에서도 worktree는 핵심이다. 여러 AI 에이전트가 동시에 서로 다른 브랜치를 작업할 때, 각 에이전트에게 독립된 worktree를 할당하면 충돌 없이 병렬 작업이 가능하다. Claude Code의 멀티 에이전트 모드가 정확히 이 패턴을 사용한다.
같은 충돌을 두 번 풀지 않는다
feature 브랜치에서 main을 merge했다. 충돌이 발생해서 10분간 수동으로 해결했다. 다음 날 코드 리뷰에서 rebase로 바꿔달라는 요청이 왔다. merge를 취소하고 rebase를 실행했더니 똑같은 충돌이 다시 나왔다. 어제 풀었던 그 충돌을 또 풀어야 한다.
git rerere는 REuse REcorded REsolution의 줄임말이다. 충돌을 해결한 기록을 저장해두고, 동일한 충돌이 다시 발생하면 자동으로 같은 방식으로 해결한다. 설정 한 줄이면 된다.
이후 흐름은 이렇다. merge에서 충돌을 해결하면 rerere가 .git/rr-cache/에 해결 패턴을 기록한다. 나중에 rebase에서 동일한 충돌이 발생하면, rerere가 기록을 참조해 자동으로 해결한다. 개발자는 아무것도 할 필요가 없다.
.git/rr-cache/
해결 기록은 .git/rr-cache/ 디렉토리에 저장된다. preimage(충돌 상태)와 postimage(해결 후 상태)의 쌍으로 관리된다.
패턴 매칭
rerere는 충돌 영역의 텍스트 패턴을 기준으로 매칭한다. 완전히 같은 충돌이 아니어도 패턴이 일치하면 적용된다.
자동 커밋 안 함
rerere는 충돌을 해결할 뿐 자동으로 커밋하지 않는다. 반드시 개발자가 결과를 확인하고 add + commit해야 한다.
코드 고고학
git blame은 "누가 이 줄을 작성했는가"를 알려주는 도구다. 대부분의 개발자가 여기까지만 안다. 하지만 blame의 진짜 힘은 플래그 조합에 있다. 포매팅 변경을 무시하고, 파일 간 코드 이동을 추적하고, 특정 커밋을 아예 blame 대상에서 제외하는 것까지 가능하다.
Prettier나 ESLint로 전체 코드베이스를 리포매팅한 적이 있다면, git blame이 무용지물이 된다. 모든 줄이 리포매팅 커밋으로 표시되기 때문이다. .git-blame-ignore-revs 파일이 이 문제를 해결한다.
git log에도 대부분이 모르는 강력한 기능들이 숨어 있다. 특정 함수의 변경 히스토리를 추적하고, 코드 조각이 언제 추가됐는지 검색하고, 프로젝트 기여자 순위를 매기는 것까지 한 줄이면 된다.
git log -L은 함수 단위의 타임머신이다.
코드가 왜 지금의 모습이 됐는지, 한 줄로 추적한다.
스태시의 진짜 사용법, 그리고 더
git stash는 대부분의 개발자가 "잠깐 저장, 나중에 복원"으로만 쓴다. 하지만 stash에는 파일 단위 저장, 설명 메시지, 브랜치 변환까지 다양한 기능이 있다. 그리고 stash 너머에는 Interactive Rebase, archive, notes 같은 도구들이 기다리고 있다.
꺼내고 삭제
git stash pop은 가장 최근 stash를 워킹 디렉토리에 적용하고 stash 목록에서 삭제한다. 일회성 저장에 적합하다.
꺼내고 유지
git stash apply는 적용하되 stash 목록에서 삭제하지 않는다. 여러 브랜치에 같은 변경을 적용하고 싶을 때 유용하다.
stash가 쌓여서 돌아가기 어려워졌다면, 브랜치로 변환하는 것이 깔끔하다. git stash branch는 stash를 기반으로 새 브랜치를 생성하고, stash 내용을 적용한 뒤, 해당 stash를 삭제한다.
Interactive Rebase는 Git에서 가장 강력하면서도 가장 적게 활용되는 기능이다. 커밋 히스토리를 정리하고, 합치고, 순서를 바꾸고, 메시지를 수정하는 모든 것이 가능하다.
--fixup과 --autosquash를 조합하면 Interactive Rebase를 더 효율적으로 쓸 수 있다. 작업 중 발견한 작은 수정을 fixup 커밋으로 만들어두면, 나중에 autosquash가 알아서 올바른 위치에 합쳐준다.
마지막으로 두 가지 더. git archive는 .git 디렉토리를 제외한 깨끗한 소스 스냅샷을 만든다. 배포용 패키지나 코드 리뷰용 아카이브에 유용하다. git notes는 커밋 해시를 변경하지 않고 메타데이터를 추가한다. 코드 리뷰 코멘트, 배포 정보 등을 커밋에 붙일 수 있다.
- 이미 push한 커밋은 rebase하지 않는다. 공유된 히스토리를 변경하면 팀원 전원에게 영향이 간다.
- rebase 중 문제가 생기면
git rebase --abort로 원래 상태로 돌아올 수 있다. - force push가 필요한 상황이라면
--force-with-lease를 사용한다. 다른 사람의 커밋을 덮어쓰는 실수를 방지한다. git reflog는 최후의 안전망이다. 실수로 날린 커밋도 30일간 reflog에 남아있다.- squash/fixup 전에 항상
git log --oneline으로 커밋 순서를 확인한다.
대부분의 개발자는
Git의 10%만 사용한다
bisect, worktree, rerere, blame -C, interactive rebase. 이 다섯 가지만 알아도 상위 1%의 Git 사용자다. 매일 쓰는 도구의 숨겨진 힘을 꺼내라.