# Review scopes

> How to scope a CommitBrief review — staged, unstaged, arbitrary diff ranges, commit-level filters by author and date, path allow/deny filters, and the three-layer ignore pipeline.

CommitBrief docs · v1.x · Reviewing

Canonical URL: https://commitbrief.com/docs/1.x/review-scopes

---

A review needs a diff. CommitBrief gives you four sources for
that diff plus two layers of filtering on top.

## The default — staged changes

```sh
commitbrief
commitbrief --staged   # explicit
commitbrief -s         # short form
```

Reviews `git diff --cached`. This is the default when no scope
flag is set — picked because it lines up with the pre-commit
moment.

## Working-tree changes

```sh
commitbrief --unstaged
commitbrief -u
```

Reviews `git diff` (working tree vs the index). Useful when you
have edits you have not staged yet.

`--staged` and `--unstaged` are mutually exclusive — passing both
is a cobra-level error before any work begins.

## Historic ranges — `commitbrief diff`

For anything outside the working tree, use the `diff` subcommand.
The positional arguments are forwarded **verbatim** to
`git diff --no-color --no-ext-diff <args>`, so anything `git diff`
accepts works.

| Invocation | Equivalent | What it reviews |
|-----------|------------|-----------------|
| `commitbrief diff HEAD` | `git diff HEAD` | Working tree vs `HEAD`. |
| `commitbrief diff HEAD~3 HEAD` | `git diff HEAD~3 HEAD` | The last three commits' net change. |
| `commitbrief diff main feature` | `git diff main feature` | One branch vs another. |
| `commitbrief diff main...feature` | `git diff main...feature` | Three-dot range — `feature`'s changes since branching off `main` (PR style). |
| `commitbrief diff <merge-sha>` | `git diff <merge-sha>` | First-parent diff of a merge commit. |
| `commitbrief diff HEAD -- '*.go'` | `git diff HEAD -- '*.go'` | Filter by git pathspec after `--`. |

At least one positional argument is required.

## Commit filters (v1.15.0)

The three scopes above all produce **one diff**. Commit filters
produce a **set of commits** instead — "everything Alice touched
since June", "every commit whose message mentions `payment`" —
and review the concatenation of their patches (ADR-0035).

`git diff` has no author or date options; those are `git log`
options. So setting any of these flags switches diff acquisition
to a **commit walk**, which is why they cannot be combined with
`--staged` / `--unstaged`: the index has no commits yet.

| Flag | Matches |
|------|---------|
| `--author` | author name **or** email, case-insensitive substring; repeatable, OR'd |
| `--committer` | committer name or email; repeatable, OR'd |
| `--start-date YYYY-MM-DD` | author date on or after this day (inclusive) |
| `--end-date YYYY-MM-DD` | author date on or before this day (**inclusive** — unlike git's bare `--until`, which stops at that day's midnight and silently drops it) |
| `--text` | the commit message, **plus** commits unique to a branch whose name contains the text |

```sh
commitbrief --author alice --author bob            # either person's commits
commitbrief --author alice@example.com             # name or email
commitbrief --start-date 2026-01-01                # on or after (inclusive)
commitbrief --text payment                         # message OR branch name
commitbrief diff main..develop --author alice      # bound the walk to a range
```

Different kinds are **AND**'d, multiple values of one kind are
**OR**'d: `--author alice --author bob --start-date 2026-06-01`
means "(Alice or Bob) **and** since June".

The revision range walked is `HEAD` by default, or the range you
give a subcommand. The resulting diff is the **concatenation of
the matching commits' patches**, not a cumulative range diff — so
a file changed in three of them appears three times, and no
unmatched commit's work leaks in.

Branch-name matching is best-effort by nature: a squash- or
rebase-merged branch no longer owns its commits, so nothing will
be found for it.

### Two modifiers

`--max-commits N` (default 200) caps the selection and always
**reports** truncation rather than silently reviewing a subset.
`--merges` keeps merge commits, which the walk excludes by
default. Neither *starts* a walk — passing one on a review with no
commit filter set is a usage error, since there is nothing to
modify. (On [`leaks`](/docs/1.x/leaks) and
[`map`](/docs/1.x/commit-graph), which always walk history,
`--max-commits` is an ordinary bound.)

### Where they apply

Available on the default review, [`diff`](#historic-ranges--commitbrief-diff),
[`summary`](/docs/1.x/summary), `dry-run`, the
[MCP `review` tool](/docs/1.x/mcp-server#tool-arguments),
[`guard`](/docs/1.x/policy-gate), [`leaks`](/docs/1.x/leaks), and
[`map`](/docs/1.x/commit-graph).

They are **rejected** by [`commit`](/docs/1.x/commit), which
describes the staged index and therefore has no commits, and by
[`remote pr`](/docs/1.x/remote-pr), whose diff comes from
`gh pr diff` rather than local git.

`commitbrief map --author alice` is the fastest way to check a
filter before paying for a review — it draws the graph with the
matches highlighted and the rest dimmed as context.

## Path filters — `--file` and `--dir`

Repeatable filter flags that compose on top of any scope:

```sh
commitbrief --staged -f src/main.go -f src/util.go
commitbrief --unstaged -d database/seeder -d app/Models
commitbrief diff HEAD~3 HEAD --dir docs
```

- `--file <path>` (`-f`) matches a file path exactly (the diff's
  `b/` path relative to repo root). Renamed files match on either
  the new path or the old path.
- `--dir <path>` (`-d`) matches any file whose path begins with
  the given directory plus a slash. `database/seed` does **not**
  match `database/seedother/*`.
- Both flags repeat. Multiple instances are unioned — a file
  matching any one of them is kept.
- They combine: `--file foo.go --dir handlers/` keeps `foo.go`
  AND everything under `handlers/`.

Path filters apply **after** the ignore layers below.

### Glob patterns (v1.7.0)

Both flags also accept gitignore-style glob patterns, not just exact
paths (ADR-0026). A value containing `*`, `?`, or `[` is compiled as
a glob; the slash decides how it anchors:

- **Slash-less** patterns match the **basename at any depth** —
  `--file '*.go'` keeps every changed `.go` file, anywhere in the
  tree.
- **Slash-bearing** patterns are **anchored to the repo root** —
  `--file 'internal/**/*.ts'` matches only under `internal/`, and
  `--dir 'app/**'` scopes to the `app/` subtree.

```sh
commitbrief --staged --file '*.go'                 # any Go file, any depth
commitbrief --staged --file 'internal/**/*.ts'     # root-anchored
commitbrief diff main...feature --dir 'app/**'     # globbed directory scope
```

Quote the pattern so your shell doesn't expand `*` before
CommitBrief sees it.

**Backward compatible:** a value with **no** glob metacharacter keeps
its exact pre-v1.7 behavior — `--file src/main.go` still matches that
one path (and its rename pair). An invalid glob errors clearly up
front instead of silently mis-filtering.

### Denylists — `--exclude-file` and `--exclude-dir` (v1.15.0)

The inverse of `--file` / `--dir`. They share the **exact same**
matching rules — literal path or gitignore-style glob — and are
applied **after** the allowlist, so an exclusion always wins:

```sh
commitbrief --staged --exclude-file '*_test.go'    # everything but tests
commitbrief --staged --dir internal --exclude-dir internal/cli
commitbrief diff main...feature -d 'app/**' --exclude-file '*.generated.ts'
```

Both repeat, both accept globs, and an invalid pattern errors
before any provider call — same as the allowlist.

Use the pair when the set you want is "this subtree, minus one
corner of it". Reaching for `.commitbriefignore` instead makes the
exclusion permanent and team-wide; these flags are per-run.

## The three-layer filter pipeline

The full pipeline that decides which files reach the LLM:

1. **Built-in ignore patterns** — hardcoded into the binary. Lock
   files (`*.lock`, `go.sum`, `package-lock.json`, …), vendored
   code (`vendor/**`, `node_modules/**`), generated code
   (`*.pb.go`, `*.gen.go`), mocks, build artefacts, editor
   detritus, and binary blobs (`*.png`, `*.zip`, `*.pdf`).
2. **`.commitbriefignore`** — repo-local gitignore-syntax overlay.
   Drop a file at the repo root. Glob syntax is the same as
   `.gitignore`; `*`, `**`, and a leading `!` to re-include a
   previously-excluded path all work.
3. **`--file` / `--dir`** path allowlists from the command line,
   then **`--exclude-file` / `--exclude-dir`** denylists on top of
   whatever survived.

Later layers can override earlier ones. To force review on a path
the built-in layer would exclude:

```gitignore
# .commitbriefignore
!vendor/**
```

The `!` re-inclusion overrides the built-in `vendor/**` pattern.

## Inspecting filter behavior

`commitbrief dry-run` reports the per-layer counts:

```text
Commits (walked):  50
Commits (matched): 12
Files (input): 12
  built-in ignore filtered:        3
  .commitbriefignore net filtered: 1
  --file/--dir path filter:        2
  --exclude-file/--exclude-dir:    1
Files (review): 5
```

A negative `.commitbriefignore net filtered` means a `!pattern`
reverted a built-in exclusion. The two `Commits` lines appear only
when a [commit filter](#commit-filters-v1150) made the run walk
history; `commitbrief map` shows *which* commits those were.

## Mutually exclusive scope flags

Cobra enforces these mutex groups before any pipeline work runs:

| Mutually exclusive | Why |
|---------------------|-----|
| `--staged` × `--unstaged` | Two scopes for the same run is ambiguous. |
| `--staged` / `--unstaged` × any [commit filter](#commit-filters-v1150) | A commit filter walks history; the index has no commits. |
| `--provider` × `--cli` | Only one backend can serve a review. |
| `--cli` × `--json` × `--markdown` | CLI providers emit pre-formatted plain text; you cannot also ask for JSON. |

## See also

- [Commit graph — map](/docs/1.x/commit-graph) — see exactly which
  commits a filter selected, before paying for a review.
- [Review a GitHub PR](/docs/1.x/remote-pr) — `commitbrief remote
  pr` reviews a pull request and posts findings back to GitHub.
- [Output formats](/docs/1.x/output-formats) — how findings render
  once a scope is fixed.
- [Severity and CI gating](/docs/1.x/severity) — `--fail-on` and
  exit codes.
- [Configuration](/docs/1.x/configuration) — where flags slot into
  the broader config surface.