Skip to content

Paths and scopes

File selection changes the question, not the observed comparison. First make one exclusion visible, then use named scopes for questions you want to repeat.

These examples need a working CLI. Install it with npm i -D @wolfsblvt/diffdevil. The commands use the repository’s example paths; outside a checkout, save the linked complete files at those paths or change the arguments to their saved locations. No example in this chapter contacts GitHub.

Save the complete path policy and use the same payments patch:

version: 1
presets: [size@1]
defaults:
paths:
exclude: ['**/package-lock.json']
scopes:
source:
includeOnly: ['src/**']
tests:
includeOnly: ['tests/**']
lockfiles:
includeOnly: ['**/package-lock.json']
Terminal window
npx diffdevil analyze --diff-file docs/examples/diffs/review.diff --config docs/examples/policies/story/paths.yml --format human
npx diffdevil query --diff-file docs/examples/diffs/review.diff --config docs/examples/policies/story/paths.yml --metric changed --format value
npx diffdevil query --diff-file docs/examples/diffs/review.diff --config docs/examples/policies/story/paths.yml --metric changed --scope source --format value
npx diffdevil query --diff-file docs/examples/diffs/review.diff --config docs/examples/policies/story/paths.yml --metric changed --scope tests --format value

The included result is 6 Changed and 8 raw churn, with 3 Changed in source and 2 in tests. The documentation file contributes the remaining included Changed. The excluded lockfile still exists in the observed files collection; whole-comparison Changed is 10 when selected explicitly with --all-files. Exclusion is not deletion from the report, and files is not shorthand for only included files.

For each available endpoint, global forceInclude wins, then includeOnly restricts the eligible set, then exclude removes matches, then default inclusion applies. For a rename, a file is included when either endpoint is included. A move from ordinary source into generated output therefore does not silently erase the change.

Use explain --policy to inspect effective patterns and their origins before trusting the total. Invocation path overrides follow the documented append/replace modes; changing a list without noticing its mode can answer a different question.

A scope starts from globally included files, then applies its own selection. A scope’s includeOnly is a hard boundary. Local forceInclude can override a local exclusion only inside that boundary; it cannot reintroduce a globally excluded file. The lockfiles scope above is consequently empty even though the full report observes a lockfile. This is an exact empty selection for this complete input, not an unknown value made into zero.

Global forceInclude wins; otherwise includeOnly restricts, then exclude removes; otherwise default inclusion applies. Every scope starts with globally eligible files, so a globally excluded lockfile remains observed but cannot enter the lockfiles scope. Global forceInclude wins; otherwise includeOnly restricts, then exclude removes; otherwise default inclusion applies. Every scope starts with globally eligible files, so a globally excluded lockfile remains observed but cannot enter the lockfiles scope.
Scoped `forceInclude` can repair a local exclusion. It cannot cross global eligibility or the scope's own hard `includeOnly` boundary.

The lockfile has not disappeared: it is still an observed record and remains available to an explicit all-files question. It simply never enters the population from which named scopes select. See the editable Mermaid source.

Scopes can overlap. Adding two scoped totals can double-count the same file; named scopes are not automatically a partition. Inspect selected identities when combining facts. Standard global file counters such as total/excluded are not valid per-scope measure shortcuts. A named metric already owns its declared scope; do not combine that metric shortcut with a second invocation scope, path or all-files selector.

Match repository paths, not filesystem paths

Section titled “Match repository paths, not filesystem paths”

diffdevil-glob/1 is case-sensitive and anchored to the whole repository-relative path on every operating system. / separates segments. * matches within one segment, ? matches one non-separator Unicode scalar, and a whole ** segment matches zero or more segments. Classes such as [a-z] and [!x] are supported. **/*.md matches root README.md; dotfiles are not hidden by a separate rule.

Backslash is a literal character, not a Windows separator or escape. Use classes [*], [?] or [[] for literal wildcard characters. Absolute paths, empty or ./.. segments, malformed classes, embedded ab**cd, brace expansion, extglob, and standalone leading negation are rejected. Use the exclude field instead of inventing a negative-pattern language. Matching never consults the filesystem.

glob(f.path, pattern) intentionally tests the current endpoint only. pathMatches(f, pattern) tests either available rename endpoint. A deletion’s path identifies the removed path. A pure rename has zero line change only when content identity was established; selection does not prove that identity.

An incomplete source may contain definite observed files, possible selected files and an unseen remainder. A filter does not turn the remainder into an empty array. A total can be bounded or unknown even when every observed record looks ordinary. Strict path output refuses unresolved membership rather than printing an apparently complete list. certain(...) or --certain explicitly asks the narrower question of definite observed members; it is not a way to acquire stronger evidence.

The real lockfile example compares selected views of an authentic frozen change. Use its actual catalogue variants when exploring; the Examples catalogue also contains bounded and incomplete file-set lessons. A live refresh is a different source edition, not a silent replacement of the frozen example.

Continue with From measurements to rules. The detail reference keeps collection membership, ordering and aggregation rules available for exact lookup.