Align stage defaulting behavior with pre-commit#1788
Conversation
This change fixes stage defaulting to match pre-commit, especially around explicit empty stage lists. The key edge case is `default_stages: []`: in pre-commit, omitted `default_stages` means all stages explicit `default_stages: []` means no stages. Before: explicit empty stage configuration could be treated like the default-all case omitted and explicitly empty values were not distinguished correctly in effective behavior After: omitted `default_stages` still behaves as all stages explicit `default_stages: []` now stays empty
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1788 +/- ##
==========================================
+ Coverage 91.66% 91.69% +0.02%
==========================================
Files 98 98
Lines 19878 20005 +127
==========================================
+ Hits 18222 18344 +122
- Misses 1656 1661 +5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR adjusts how stages / default_stages are defaulted so prek can distinguish between an omitted default_stages (treat as “all stages”) vs an explicitly empty default_stages: [] (treat as “no stages”), matching upstream pre-commit semantics.
Changes:
- Change stage defaulting logic to preserve
default_stages: []as an explicit empty stage set. - Update
Stagesparsing and formatting sostages: []deserializes to an empty set (instead of “all”). - Add tests covering omitted vs explicitly empty stage lists.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
crates/prek/src/hook.rs |
Updates hook-stage defaulting during hook build and adds regression tests for default stage behavior. |
crates/prek/src/config.rs |
Changes Stages semantics so empty stage lists deserialize as empty and updates related tests/output formatting. |
📦 Cargo Bloat ComparisonBinary size change: +0.00% (24.7 MiB → 24.7 MiB) Expand for cargo-bloat outputHead Branch ResultsBase Branch Results |
⚡️ Hyperfine BenchmarksSummary: 0 regressions, 0 improvements above the 10% threshold. Environment
CLI CommandsBenchmarking basic commands in the main repo:
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base --version |
2.4 ± 0.1 | 2.2 | 2.7 | 1.01 ± 0.05 |
prek-head --version |
2.3 ± 0.0 | 2.3 | 2.5 | 1.00 |
prek list
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base list |
8.9 ± 0.1 | 8.6 | 9.4 | 1.00 ± 0.02 |
prek-head list |
8.8 ± 0.1 | 8.6 | 9.2 | 1.00 |
prek validate-config .pre-commit-config.yaml
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base validate-config .pre-commit-config.yaml |
3.1 ± 0.1 | 3.0 | 3.2 | 1.01 ± 0.02 |
prek-head validate-config .pre-commit-config.yaml |
3.1 ± 0.0 | 3.0 | 3.2 | 1.00 |
prek sample-config
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base sample-config |
2.6 ± 0.0 | 2.5 | 2.7 | 1.00 |
prek-head sample-config |
2.6 ± 0.0 | 2.5 | 2.7 | 1.00 ± 0.02 |
Cold vs Warm Runs
Comparing first run (cold) vs subsequent runs (warm cache):
prek run --all-files (cold - no cache)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --all-files |
150.7 ± 2.2 | 146.7 | 153.9 | 1.00 |
prek-head run --all-files |
151.0 ± 2.4 | 148.0 | 156.4 | 1.00 ± 0.02 |
prek run --all-files (warm - with cache)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --all-files |
150.7 ± 2.1 | 148.1 | 155.1 | 1.00 ± 0.02 |
prek-head run --all-files |
150.3 ± 2.4 | 146.6 | 155.0 | 1.00 |
Full Hook Suite
Running the builtin hook suite on the benchmark workspace:
prek run --all-files (full builtin hook suite)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --all-files |
149.6 ± 1.9 | 144.3 | 155.2 | 1.00 |
prek-head run --all-files |
153.2 ± 20.1 | 145.7 | 291.8 | 1.02 ± 0.14 |
Individual Hook Performance
Benchmarking each hook individually on the test repo:
prek run trailing-whitespace --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run trailing-whitespace --all-files |
21.9 ± 0.8 | 20.7 | 23.7 | 1.01 ± 0.04 |
prek-head run trailing-whitespace --all-files |
21.7 ± 0.4 | 20.8 | 22.5 | 1.00 |
prek run end-of-file-fixer --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run end-of-file-fixer --all-files |
28.0 ± 1.5 | 25.4 | 30.8 | 1.00 |
prek-head run end-of-file-fixer --all-files |
28.2 ± 1.7 | 25.2 | 31.9 | 1.01 ± 0.08 |
prek run check-json --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-json --all-files |
12.8 ± 0.5 | 11.9 | 13.7 | 1.03 ± 0.05 |
prek-head run check-json --all-files |
12.4 ± 0.4 | 11.8 | 13.7 | 1.00 |
prek run check-yaml --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-yaml --all-files |
12.0 ± 0.2 | 11.6 | 12.4 | 1.00 ± 0.03 |
prek-head run check-yaml --all-files |
11.9 ± 0.2 | 11.6 | 12.9 | 1.00 |
prek run check-toml --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-toml --all-files |
12.2 ± 0.3 | 11.7 | 13.2 | 1.00 |
prek-head run check-toml --all-files |
12.2 ± 0.4 | 11.5 | 13.3 | 1.00 ± 0.05 |
prek run check-xml --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-xml --all-files |
12.0 ± 0.4 | 11.6 | 13.0 | 1.01 ± 0.04 |
prek-head run check-xml --all-files |
12.0 ± 0.3 | 11.3 | 12.6 | 1.00 |
Installation Performance
Benchmarking hook installation (fast path hooks skip Python setup):
prek install-hooks (cold - no cache)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base install-hooks |
4.9 ± 0.0 | 4.9 | 4.9 | 1.00 ± 0.02 |
prek-head install-hooks |
4.9 ± 0.1 | 4.8 | 5.1 | 1.00 |
prek install-hooks (warm - with cache)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base install-hooks |
4.8 ± 0.1 | 4.8 | 4.9 | 1.00 ± 0.02 |
prek-head install-hooks |
4.8 ± 0.1 | 4.7 | 5.0 | 1.00 |
File Filtering/Scoping Performance
Testing different file selection modes:
prek run (staged files only)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run |
18.7 ± 0.2 | 18.4 | 19.1 | 1.00 |
prek-head run |
18.8 ± 0.3 | 18.5 | 19.7 | 1.01 ± 0.02 |
prek run --files '*.json' (specific file type)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --files '*.json' |
7.5 ± 0.1 | 7.3 | 7.6 | 1.00 ± 0.02 |
prek-head run --files '*.json' |
7.4 ± 0.1 | 7.3 | 7.6 | 1.00 |
Workspace Discovery & Initialization
Benchmarking hook discovery and initialization overhead:
prek run --dry-run --all-files (measures init overhead)
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run --dry-run --all-files |
12.4 ± 0.2 | 12.3 | 13.1 | 1.00 |
prek-head run --dry-run --all-files |
12.5 ± 0.5 | 12.0 | 14.3 | 1.00 ± 0.04 |
Meta Hooks Performance
Benchmarking meta hooks separately:
prek run check-hooks-apply --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-hooks-apply --all-files |
13.8 ± 1.0 | 12.6 | 15.3 | 1.10 ± 0.08 |
prek-head run check-hooks-apply --all-files |
12.6 ± 0.1 | 12.4 | 12.8 | 1.00 |
prek run check-useless-excludes --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run check-useless-excludes --all-files |
12.8 ± 0.3 | 12.4 | 13.7 | 1.01 ± 0.03 |
prek-head run check-useless-excludes --all-files |
12.6 ± 0.1 | 12.4 | 12.8 | 1.00 |
prek run identity --all-files
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
prek-base run identity --all-files |
11.3 ± 0.2 | 11.0 | 11.6 | 1.01 ± 0.02 |
prek-head run identity --all-files |
11.1 ± 0.1 | 11.0 | 11.3 | 1.00 |
I noticed this difference in behavior while looking into #1785.
This change fixes
stagedefaulting to match pre-commit, especially around explicit empty stage lists.The key edge case is
default_stages: []:in pre-commit, omitted
default_stagesmeans all stagesexplicit
default_stages: []means no stages.Before:
explicit empty stage configuration could be treated like the default-all case
omitted and explicitly empty values were not distinguished correctly in effective behavior
After:
omitted
default_stagesstill behaves as all stagesexplicit
default_stages: []now stays empty