이분 탐색 디버깅

Git bisect

정상 커밋과 문제 커밋 사이를 반씩 시험해 최초 문제 커밋을 좁힙니다.

···
html
<div class="demo path"><div class="heading"><strong>GIT BISECT</strong><span class="live">LIVE</span></div><div class="scene"><div class="diagram bisect-timeline">
      <div class="bisect-ends"><span>known good</span><span>known bad</span></div>
      <div class="bisect-commits"><span class="box cue " data-cue="0">G</span><span class="box cue " data-cue="0">1</span><span class="box cue " data-cue="0">2</span><span class="box cue " data-cue="1">3</span><span class="box cue " data-cue="2,3">4</span><span class="box cue " data-cue="0">5</span><span class="box cue " data-cue="0">B</span></div>
      <div class="bisect-window"><span class="cue" data-cue="1">test middle</span><span class="cue" data-cue="2">keep bad half</span><span class="cue" data-cue="3">first bad → C4</span></div></div></div><div class="caption" id="caption"></div><div class="ticks"><i></i><i></i><i></i><i></i></div></div>
css
.demo{box-sizing:border-box;width:min(96vw,760px);height:min(94vh,330px);padding:clamp(9px,2vmin,18px);display:flex;flex-direction:column;gap:clamp(4px,1.2vmin,8px);border:1px solid var(--line);border-radius:13px;background:var(--surface);color:var(--fg);font:500 clamp(12px,3.4vmin,16px)/1.2 var(--font-sans,-apple-system),sans-serif}
.demo *{box-sizing:border-box}.heading{display:flex;align-items:center;justify-content:space-between;gap:8px;flex:none;min-height:18px}.heading strong{color:var(--accent);font-size:1em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.live{flex:none;font:600 12px ui-monospace,monospace;color:var(--accent);letter-spacing:.08em}.live:before{content:'';display:inline-block;width:5px;height:5px;margin-right:4px;border-radius:50%;background:var(--accent);animation:blink 1.2s infinite}@keyframes blink{50%{opacity:.2}}
.scene{flex:1;min-height:0;overflow:hidden}.diagram{width:100%;height:100%;min-height:0;display:flex;align-items:center;justify-content:center;gap:4px}.box{display:flex;align-items:center;justify-content:center;min-width:0;min-height:25px;padding:3px 5px;border:1px solid var(--line);border-radius:6px;background:var(--bg);text-align:center;white-space:pre-line;overflow-wrap:anywhere;transition:background .25s,border-color .25s,transform .25s}.box.active,.cue.active{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 16%,var(--surface));color:var(--fg)}.box.active{transform:scale(1.03)}.arrow{flex:none;color:var(--accent);font:700 17px/1 var(--font-sans),sans-serif}.caption{flex:none;min-height:2.3em;display:flex;align-items:center;justify-content:center;text-align:center;font-size:clamp(12px,3vmin,15px)}.ticks{flex:none;display:flex;justify-content:center;gap:4px}.ticks i{display:block;width:14px;height:3px;background:var(--line);border-radius:3px}.ticks i.active{background:var(--accent)}
.diagram b{font:700 12px/1.2 var(--font-sans),sans-serif;color:var(--muted)}.diagram small{font:500 12px/1.2 var(--font-sans),sans-serif}.empty{color:var(--muted)}
.worktrees{flex-direction:column;gap:0}.worktrees .store{width:82%;min-height:26px}.fork-line{width:53%;height:12px;border-left:2px solid var(--accent);border-right:2px solid var(--accent);border-top:2px solid var(--accent);transform:rotate(180deg);margin-top:1px}.folders{display:flex;width:100%;gap:7px;min-height:0;flex:1}.folder{display:flex;align-items:center;justify-content:center;gap:5px;flex:1;min-width:0;padding:3px;border:1px solid var(--line);border-radius:7px;background:var(--bg)}.folder .box{padding:2px 4px}.folder small{color:var(--muted)}
.stack{flex-direction:column;align-items:flex-start;gap:2px}.stack .layer{height:22px;min-height:0;justify-content:flex-start;padding-left:9px}.stack .layer:nth-child(1){width:70%}.stack .layer:nth-child(2){width:76%;margin-left:8%}.stack .layer:nth-child(3){width:76%;margin-left:16%}.stack .layer:nth-child(4){width:76%;margin-left:24%}.stack-note{position:absolute;opacity:0}
.context-map{display:grid;grid-template-columns:1.25fr 12px 1fr 12px 1.3fr;grid-template-rows:1fr 1fr;gap:3px}.context-map .diff{grid-column:1;grid-row:1/3}.context-map .arrow-a{grid-column:2;grid-row:1/3}.context-map .arrow-b{grid-column:4;grid-row:1/3}.context-map .arrow{display:grid;place-items:center}.context-map .upload{grid-column:3;grid-row:1}.context-map .sync{grid-column:3;grid-row:2}.context-map .test{grid-column:5;grid-row:1}.context-map .finding{grid-column:5;grid-row:2}.context-map .box{height:100%}
.policy{display:grid;grid-template-columns:1fr 16px 1fr 1.25fr;grid-template-rows:1fr 1fr;gap:3px}.policy .approval{grid-column:1;grid-row:1/3;height:100%}.policy>.arrow{grid-column:2;grid-row:1/3}.policy .ruleset{grid-column:3;grid-row:1/3;height:100%}.decisions{grid-column:4;grid-row:1/3;display:flex;flex-direction:column;gap:4px;height:100%}.decisions .box{flex:1;width:100%}.policy-note{display:none}
.rewrite{flex-direction:column;gap:2px}.history-row{display:flex;align-items:center;justify-content:center;gap:3px;width:100%;flex:1}.history-row>span:first-child{width:45px;flex:none;color:var(--muted)}.history-row .box{flex:1;min-height:22px}.patch{padding:2px 8px;border:1px dashed var(--accent);border-radius:6px;color:var(--accent)}
.configured{display:grid;grid-template-columns:1fr 16px 1.45fr;grid-template-rows:1fr 20px;gap:4px}.configured .event{height:100%}.configured>.arrow{grid-column:2}.hook-list{display:flex;flex-direction:column;gap:4px;height:100%}.hook-list .box{flex:1}.hook-result{grid-column:1/4;text-align:center;color:var(--muted)}
.parallel{flex-direction:column;gap:5px}.lane{display:flex;align-items:center;gap:4px;width:100%;flex:1}.lane>span:first-child{width:28px;color:var(--muted)}.lane .box{width:58px}.run{flex:1;height:4px;border-radius:3px;background:linear-gradient(90deg,var(--accent),var(--line));animation:run 1.2s linear infinite}@keyframes run{50%{background-position:100px 0}}.join{color:var(--accent);min-height:15px}
.transfer{gap:4px}.tray{display:flex;flex-direction:column;align-items:stretch;gap:3px;flex:1;height:100%;min-width:0}.tray b{text-align:center}.tray .box{flex:1;min-height:0}.tray .empty{flex:1;text-align:center;padding-top:5px}.transfer-arrow{flex:none;text-align:center;color:var(--accent)}
.compare{display:grid;grid-template-columns:1fr 16px 1fr;grid-template-rows:1fr 21px;gap:3px}.pane{display:flex;flex-direction:column;gap:3px;padding:4px;border:1px solid var(--line);border-radius:6px;min-width:0;background:var(--bg);font-weight:700;color:var(--muted)}.pane .box{min-height:0;flex:1}.pane code{font:500 12px/1.2 ui-monospace,monospace;color:var(--fg);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pane .changed{color:var(--accent)}.compare-arrow{align-self:center;text-align:center;color:var(--accent)}.status{grid-column:1/4;text-align:center;color:var(--muted)}
.staging{gap:3px}.bucket{display:flex;flex-direction:column;gap:3px;align-items:stretch;justify-content:flex-start;flex:1;height:calc(100% - 4px);min-width:0;text-align:center}.bucket .box{flex:1;min-height:0}.bucket .empty{flex:1}
.object{gap:5px}.object-fields{display:grid;grid-template-columns:1fr 1fr;gap:4px;flex:1;height:100%}.object-fields .box:last-child{grid-column:1/3}.object .object-id{width:34%;height:100%;font-family:ui-monospace,monospace;font-size:12px}
.pointer,.references,.revert,.graph,.pr-review,.queue,.conflict{flex-direction:column}.pointer-label{border:1px solid var(--accent);border-radius:6px;padding:2px 10px;color:var(--accent)}.commits,.reference-row,.graph-row,.review-steps,.queue-base,.queue-checks,.result,.sides,.state-line{display:flex;align-items:center;justify-content:center;gap:3px;width:100%;flex:1;min-height:0}.commits .box,.reference-row .box,.graph-row .box,.review-steps .box,.queue-base .box,.queue-checks .box,.result .box,.sides .box{flex:1;min-height:22px}.pointer-note{color:var(--muted)}.references{gap:4px}.reference-row{width:100%;padding:2px}.reference-row.detached{border:1px dashed var(--accent);border-radius:6px}.reference-row>span:last-child:not(.box){color:var(--accent)}
.graph{gap:2px}.graph-row b{width:45px;flex:none}.graph-row>span:last-child:not(.box){color:var(--muted)}.replay-arrow{color:var(--accent);line-height:1}.cherry .graph-row .box{flex:1}.revert .state-line{border-top:1px dashed var(--line);color:var(--muted)}.state-line strong{font-weight:600;padding:2px 5px;border-radius:5px}
.conflict{gap:1px}.conflict .sides{flex:1}.conflict-down{color:var(--accent);height:17px}.conflict .result{flex:1}
.pr-review{gap:5px}.diff-card{width:100%;flex:1;min-height:0;padding:4px 8px;border:1px solid var(--line);border-radius:6px;background:var(--bg);display:flex;align-items:center;justify-content:space-between;gap:5px}.diff-card code{font:500 12px ui-monospace,monospace;color:var(--accent)}.review-steps{flex:1;gap:1px}.review-steps .box{padding:3px 1px}.review-steps .arrow{font-size:12px}
.queue{gap:4px}.queue-base,.queue-checks{flex:1}.queue-checks{padding-left:20%;padding-right:4%}.fallback{gap:4px}.fallback .box{flex:1}
.midx,.pickaxe,.hashes,.branch-cleanup{position:relative;flex-direction:column;gap:3px}.midx-row,.cleanup-row{display:flex;align-items:center;gap:4px;width:100%;flex:1;min-height:0}.midx-row b{width:57px;flex:none}.midx-row .box{flex:1;height:100%;min-height:0}.midx-row span:last-child:not(.box){flex:1;color:var(--muted);text-align:center}.midx-old{background:color-mix(in srgb,var(--accent-2) 18%,var(--surface))}.midx-combined{border-style:dashed}
.pickaxe{display:grid;grid-template-columns:1.2fr 1fr;grid-template-rows:1fr 23px;gap:4px}.pickaxe-code,.pickaxe-log{display:flex;flex-direction:column;gap:3px;min-width:0;height:100%;padding:4px;border:1px solid var(--line);border-radius:7px;background:var(--bg)}.pickaxe-code code{font:500 12px/1.2 ui-monospace,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pickaxe-code code:first-of-type{color:var(--accent)}.pickaxe-log .box{flex:1;min-height:0}.pickaxe-match{border-style:dashed}.pickaxe-filter{grid-column:1/3;text-align:center;color:var(--accent);padding:2px}
.hashes{flex-direction:row;flex-wrap:wrap;align-content:center}.hash-lane{display:flex;flex-direction:column;justify-content:center;gap:5px;width:37%;min-width:0;height:62%;padding:4px;border:1px solid var(--line);border-radius:7px;background:var(--bg);text-align:center}.hash-lane .box{font:500 12px/1.2 ui-monospace,monospace;min-height:32px}.hash-bridge{width:22%;text-align:center;border-bottom:2px dashed var(--accent);color:var(--muted);font-size:12px}.hash-note{width:100%;text-align:center;color:var(--muted)}
.cleanup-row b{width:53px;flex:none}.cleanup-row>span:not(.box){flex:1;min-width:0;text-align:center;color:var(--muted)}.cleanup-row .box{width:36%;height:100%;min-height:0}.cleanup-preview{width:100%;text-align:center;color:var(--accent);min-height:18px}
.reset-grid{display:flex;flex-direction:column;gap:2px}.reset-head,.reset-row{width:100%;display:grid;grid-template-columns:48px repeat(3,1fr);gap:3px;text-align:center}.reset-head b{width:auto}.reset-row{flex:1;align-items:center;border:1px solid var(--line);border-radius:5px;background:var(--bg)}.reset-row strong{font-weight:600}.reset-row .changed{color:var(--accent)}.reset-row.active{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 14%,var(--surface))}
.stash-shelf{position:relative;gap:5px}.stash-zone{flex:1;align-self:flex-start;min-width:0;height:calc(100% - 23px);display:flex;flex-direction:column;align-items:stretch;justify-content:center;gap:4px;padding:4px;border:1px solid var(--line);border-radius:7px;background:var(--bg);text-align:center}.stash-zone .box{flex:1;min-height:0}.stash-zone .box:not(.active){opacity:.7}.stash-transfer{flex:none;width:48px;text-align:center;color:var(--accent)}.stash-floor{position:absolute;bottom:0;width:100%;text-align:center;color:var(--muted);font-size:12px}
.tracking{flex-direction:column;gap:3px}.tracking-lane{display:flex;align-items:center;gap:4px;width:100%;flex:1;min-height:0}.tracking-lane b{width:52px;flex:none}.tracking-lane .box{width:17%;height:100%;min-height:0}.tracking-lane>span:last-child:not(.box){margin-left:auto;color:var(--muted)}.tracking-note{width:100%;text-align:center;color:var(--accent);min-height:16px}
.bisect-timeline{flex-direction:column;gap:3px}.bisect-ends,.bisect-window{display:flex;justify-content:space-between;width:100%;color:var(--muted)}.bisect-commits{display:flex;align-items:center;justify-content:space-between;gap:2px;width:100%;flex:1}.bisect-commits .box{flex:0 0 clamp(27px,8vmin,43px);min-width:0;min-height:clamp(27px,8vmin,43px);border-radius:50%}.bisect-window{justify-content:center;gap:5px}.bisect-window span{border:1px solid var(--line);border-radius:5px;padding:2px 4px;font-size:12px}.bisect-window span.active{color:var(--accent);border-color:var(--accent)}
@media(max-width:350px){.demo{padding:8px}.diagram{font-size:12px}.folder{gap:2px}.folder small{display:none}.arrow{font-size:15px}.graph-row b{width:38px}.diff-card{padding:3px 5px}}
js
const moments=[{"focus":0,"label":"Set known good and bad endpoints"},{"focus":1,"label":"Test midpoint C3: good"},{"focus":2,"label":"Narrow to bad half; test C4"},{"focus":3,"label":"C4 is the first bad commit"}];let n=0;const cues=[...document.querySelectorAll('[data-cue]')],ticks=[...document.querySelectorAll('.ticks i')],caption=document.getElementById('caption'),dynamic=document.querySelector('.dynamic');function show(){const m=moments[n];cues.forEach(el=>el.classList.toggle('active',el.dataset.cue.split(',').includes(String(m.focus))));if(dynamic&&m.cells)dynamic.textContent=m.cells[m.cells.length-1];ticks.forEach((el,i)=>el.classList.toggle('active',i===n));caption.textContent=m.label;n=(n+1)%moments.length}show();let timer=setInterval(show,1050);document.querySelector('.demo').addEventListener('pointerdown',()=>{clearInterval(timer);show()})

git bisect는 정상으로 확인한 커밋과 문제가 있는 커밋을 경계로 잡고 중간 커밋을 체크아웃합니다. 매 단계에서 같은 재현 검사를 실행해 good 또는 bad를 표시하면 탐색 구간이 줄어듭니다.

데모는 중간 커밋 3을 정상으로 판단하고 나쁜 절반에서 4를 시험해 최초 문제 커밋을 찾습니다. 테스트가 간헐적으로 실패하면 판단이 흐려질 수 있으므로 재현 가능한 검사와 git bisect reset으로 작업 위치 복귀가 필요합니다.

언제 쓰나

회귀가 언제 들어왔는지 변경 이력이 길어 수작업 확인이 느릴 때 사용합니다.

페이지로 열기 ↗