// docs · v1.x

Git hooks

Block bad commits at pre-commit, commit-msg, or pre-push with a one-command CommitBrief hook scaffolder.

Updated June 20, 2026

commitbrief install-hook drops a small shell script at .git/hooks/<name> that runs CommitBrief against the relevant change set, blocking the git operation on a critical-severity finding.

Quick install

commitbrief install-hook              # default: pre-commit
commitbrief install-hook --hook=pre-push
commitbrief install-hook --uninstall  # remove (only if we wrote it)

Supported hooks

HookDefault?What the body does
pre-commit✓Runs commitbrief --staged --fail-on=critical --quiet --no-cost-check. Blocks the commit on any critical-severity finding.
commit-msg—Same body as pre-commit. Useful when you want the review to happen after the commit message is composed.
pre-push—Reads git’s per-ref stdin protocol and runs commitbrief diff <remote>..<local> --fail-on=critical --quiet --no-cost-check for each ref being pushed. Skips branch deletions; for new branches reviews the tip commit (<sha>^..<sha>). The push is blocked on the first critical finding. Requires v1.18.1 or later for the new-branch behavior; see Pre-push specifics.

post-commit, post-receive, and other hooks are intentionally not supported.

Flags

FlagNotes
--hook=<name>Which hook to install. One of pre-commit, commit-msg, pre-push. Default pre-commit.
--uninstallRemove a hook previously written by install-hook. Refuses to touch a hook that does not carry our generated-marker comment.
--yes (global)Overwrite an existing hook file. The previous content is backed up to <name>.bak.<UTC timestamp>.

Conflict handling

SituationBehavior
Target hook does not existWrite it. Print Installed <path>.
Hook exists, our marker, no --yesRefuse with install-hook: <path> already exists; re-run with --yes to back it up and overwrite.
Hook exists, our marker, with --yesRename existing to <path>.bak.<UTC timestamp> (compact form, for example 20260101T150405Z). Write fresh.
Hook exists, NO marker, no --yesRefuse with the same already exists; re-run with --yes message.
Hook exists, NO marker, with --yesSame backup + overwrite — explicit opt-in.
--uninstall, file missingNo-op success.
--uninstall, marker presentRemove. Print Removed <path>.
--uninstall, marker missingRefuse — was not written by commitbrief; refusing to remove.

The generated marker

Every hook this command writes contains the verbatim comment:

Generated by `commitbrief install-hook`

--uninstall greps for it before removing. A hand-written hook of the same name is never silently clobbered.

Embedded absolute path

The generated hook embeds the absolute path of the running commitbrief binary as a single-quoted shell token instead of relying on $PATH lookup. Resolved via os.Executable() plus filepath.EvalSymlinks so the path survives brew upgrade (which swaps the keg symlink target).

This makes the hook work under GUI git clients (Tower, GitHub Desktop, Fork, JetBrains IDEs) that strip the user’s shell $PATH and would otherwise fail to find commitbrief if it sits under /opt/homebrew/bin/.

Pre-push specifics

The pre-push body parses git’s per-ref stdin protocol:

<local-ref> <local-sha> <remote-ref> <remote-sha>

per line. For each ref:

  • Branch deletion (<local-sha> is 0000…) → skip.
  • New branch (<remote-sha> is 0000…) → review the tip commit only, as <sha>^..<sha>. A root commit is reviewed against the empty tree. If the parent is not available locally (for example a shallow clone), the ref is skipped with a warning; fetch more history to review that push.
  • Annotated tag → peeled to the commit it points at first; a tag that does not resolve to a commit is skipped.
  • Normal push → review <remote-sha>..<local-sha>.

Every range has two ends, so uncommitted changes in your working tree are never part of the reviewed diff.

Requires v1.18.1 or later. Earlier versions passed the bare commit to commitbrief diff for a new branch, which compared that commit with the working tree instead of reviewing the pushed commit. The hook is a generated file, so re-run commitbrief install-hook --hook=pre-push --yes after upgrading to regenerate it; an already installed hook keeps the old body.

--fail-on=critical exits 1 on the first critical finding, which blocks the push.

Examples

# Default: install pre-commit hook.
commitbrief install-hook

# Pre-push hook for catch-on-push semantics.
commitbrief install-hook --hook=pre-push

# Overwrite an existing hook (backup auto-created).
commitbrief install-hook --yes

# Remove the hook we installed earlier.
commitbrief install-hook --uninstall

# Pre-push variant.
commitbrief install-hook --hook=pre-push --uninstall

pre-commit framework integration

If your team already uses the pre-commit framework to manage hooks, CommitBrief ships a .pre-commit-hooks.yaml so you can add it to any repo’s .pre-commit-config.yaml in one entry — no install-hook run, no hand-written shell script.

This is distinct from install-hook above. install-hook writes a git-native script straight into .git/hooks/ and embeds the absolute binary path; the pre-commit framework owns .git/hooks/ itself and dispatches to its configured repos. Pick one approach per repo — don’t run both against the same hook.

Two hook ids

Hook idlanguageHow the binary is provided
commitbriefgolangThe framework builds and pins CommitBrief from source for you — nothing to install separately.
commitbrief-systemsystemUses an already-installed commitbrief on PATH (e.g. from Homebrew, Scoop, or go install).

Reach for commitbrief when you want the framework to manage the toolchain end-to-end, and commitbrief-system when CommitBrief is already installed and you’d rather not rebuild it.

Example .pre-commit-config.yaml

repos:
  - repo: https://github.com/CommitBrief/commitbrief
    rev: v1.18.1                # pin a released tag
    hooks:
      - id: commitbrief         # framework builds + pins the binary

Or, to use a commitbrief you’ve already installed:

repos:
  - repo: https://github.com/CommitBrief/commitbrief
    rev: v1.18.1
    hooks:
      - id: commitbrief-system  # uses the commitbrief on PATH

Then install the framework’s git hook once per clone:

pre-commit install

Both ids review the staged change set and run with args: [--staged, --fail-on=high], so they block the commit on a high-severity or worse finding. This differs from the git-native pre-commit body, which gates on critical. To change the gate, override args in your config:

    hooks:
      - id: commitbrief
        args: [--staged, --fail-on=critical]

See also