Config-based Git hooks

설정 기반 Git 훅

Git configuration maps hook events to commands that can be composed.

···
html
<div class="demo branch"><div class="heading"><strong>CONFIGURED HOOKS</strong><span class="live">LIVE</span></div><div class="scene"><div class="diagram configured">
      <span class="box cue event" data-cue="0">pre-commit</span><span class="arrow" aria-hidden="true">→</span><div class="hook-list"><span class="box cue " data-cue="1">hook.lint → lint</span><span class="box cue " data-cue="2">hook.secret → scan</span></div>
      <span class="hook-result cue" data-cue="4">both pass → commit</span></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":"Git emits pre-commit"},{"focus":1,"label":"Configured linter runs"},{"focus":2,"label":"Secret check runs"},{"focus":4,"label":"Commit proceeds if checks pass"}];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 2.54 adds configuration entries that map an event to a command alongside traditional executable hooks. Multiple commands can share an event and configuration scopes can be composed.

The demo routes pre-commit to lint and secret checks. Inspect configured sources and enabled state to confirm local checks actually run.

When to use

Use it to share checks across repositories or compose independent checks for one event.

Open as page ↗