Skip to content

Changed mode

alint check --changed layers a diff filter on top of the walk so a per-file rule sees only the files you touched, which is what a pre-commit hook or a PR check wants. Most cross-file rules, and the existence rules, deliberately opt out and keep evaluating the whole tree, because an invariant they enforce can be broken by a file you changed even when its partner did not.

--changed filters per-file rules to the diff while cross-file and existence rules stay whole-tree A repository of four files has two changed. Per-file rules receive only the two changed files. Cross-file and existence rules receive all four files, so a whole-tree invariant is still checked. repository 2 of 4 files changed src/parser.rschanged docs/guide.mdchanged src/lib.rsunchanged api.hunchanged per-file rules see the changed set src/parser.rs docs/guide.md 2 of 4 evaluated no_trailing_whitespace, file_header, filename_case cross-file + existence see the whole tree src/parser.rs docs/guide.md src/lib.rs api.h pair, file_exists cross-file and existence rules stay whole-tree for correctness

--changed picks its diff from git in one of two shapes:

InvocationDiff sourceFits
alint check --changedworking tree: modified plus untracked, --exclude-standardpre-commit, local dev
alint check --changed --base=mainmain...HEAD (three-dot, merge-base)PR checks

The three-dot <base>...HEAD form diffs against the merge-base of <base> and HEAD, which is exactly what a GitHub PR calls “your changes.” --base=<ref> implies --changed, so the ref is the verb’s argument; you never pass both --changed and a bare --base awkwardly. The same flags work on alint fix --changed.

The filter narrows the file set for per-file rules only. Two families opt out, on purpose:

  • Whole-tree rules always evaluate against the whole tree, because their verdict depends on files outside your diff: the relational rules (pair, for_each_dir, for_each_file, every_matching_has, unique_by, dir_contains, dir_only_contains), the manifest and graph rules (cross_file, file_graph, registry_paths_resolve, pair_hash), and the single-shot rules (generated_file_fresh, command_idempotent). A pair rule that requires every api.h to have an api.c must still fire when you delete api.c, even though the surviving api.h is not itself in your diff.
  • Existence rules also consult the whole tree, but the two file-existence rules (file_exists, file_absent) are skipped when their paths: scope does not intersect the diff, so a missing LICENSE fails only the PRs that touch a LICENSE-shaped path. The two directory-existence rules (dir_exists, dir_absent) always evaluate, since a directory scope never intersects a file-path diff.
  • Diff-oriented rules are the mirror image: a few cross-file rules exist because of the diff. pair_changed_together asks whether a file’s partner changed alongside it, and changeset_requires_path gates on what a commit range touched, so they read the changed set by design; markdown_paths_resolve is likewise a per-file rule and follows the diff filter like any other.
  • Empty diff (nothing modified, nothing untracked): the run short-circuits to an empty report in milliseconds, so a no-op pre-commit is nearly free.
  • Outside a git repo (or git missing from PATH): --changed hard-errors rather than silently falling back to a full check, because a silent full run would betray the intent the flag expressed.
  • Deleted files appear in the diff. A LICENSE you deleted is in the changed set, the walker no longer sees it on disk, and an existence rule for LICENSE evaluates the whole tree (which now lacks it) and fires.

--changed pairs naturally with git_tracked_only:: the changed set is a working-tree concept and the tracked set is an index concept, so a rule with both fires only on tracked entries that are part of this diff.

A PR check that lints only the files the branch touched, against the merge-base:

Terminal window
alint check --changed --base=origin/main

If the branch added a trailing-whitespace line to src/parser.rs and deleted api.c (leaving api.h orphaned), one report carries both a per-file finding and a whole-tree one:

warning no-trailing-whitespace trailing whitespace
error header-source-pair api.h has no matching api.c

The no-trailing-whitespace finding came through the diff filter; the pair finding came from the whole-tree pass, even though api.h was never in the diff.

  • The walker and git is the whole-tree index this filter sits on top of.
  • Scoping covers changed_since:, the per-rule scope_filter counterpart to the run-wide --changed.
  • Configuration documents the rule kinds and their cross-file versus per-file classification.