Churn
Churn is how many files Curb rewrites on its first run over a repository that already passes its existing formatter. The first Curb commit is a diff. This page says what is in it.
Most repositories will see files changed on the first run — even those that are already
dotnet format-clean. The diff is confined to ground dotnet format does not cover: blank-line
normalisation, using-directive sorting, chain breaking, comment alignment. With max_line_length
set, add reflow on top.
How many files depends on the repository. The benchmark table shows the spread across twelve real .NET projects.
The table below is from one corpus (elastic/docs-builder, 1,196 files, already 100% IDE0055-clean), measured across three configurations:
| Configuration | Files changed |
|---|---|
no max_line_length |
669 of 1,196 |
max_line_length = 160 |
892 of 1,196 |
max_line_length = 160 + csharp_keep_existing_linebreaks = true |
685 of 1,196 |
File count understates what a width does — the line count in the max_line_length = 160 case is
roughly 3× higher, because only that mode rewraps anything. If you're deciding whether to set a
width, the line count is the honest number.
Churn is a one-time cost. Once the initial commit is in, idempotency takes over.
Curb's output is a fixed point of dotnet format whitespace: run dotnet format over Curb-formatted
code and nothing changes. This is not a claim — it is a CI-gated measurement, checked against
1,196 files on every push.
The practical consequence: Format Document in your IDE won't undo what Curb wrote. dotnet format
in CI won't produce a diff. The two tools genuinely agree, and they stay that way. That is what
"plays nice with existing tooling" means in practice.
This is the number of files where dotnet format whitespace disagrees with Curb's output — ideally
zero. A non-zero count means Format Document in the IDE can partially undo Curb's work on those files.
| repo | not-fixpt files |
|---|---|
| serilog | 4 |
| FluentValidation | 216 |
| RestSharp | 15 |
| logging-log4net | 11 |
| AutoMapper | 15 |
| Humanizer | 10 |
| quartznet | 1 |
| Newtonsoft.Json | 17 |
| ServiceStack | 290 |
| MassTransit | 62 |
| efcore | 533 |
| roslyn | 0 |
The roslyn case — 17,167 files, already exactly dotnet format-clean — shows zero. Most of the
non-zero numbers come from a known limitation with multi-line trivia line endings. See
known limitations. The efcore number (533) is the most affected because
efcore uses XML doc comments heavily.
This is how many files change when Curb is run a second time over its own output. The number must always be zero — a non-zero count means the formatter has not reached a fixed point. Across all twelve repositories the count is zero.
| repo | 2nd-run changes |
|---|---|
| serilog | 0 |
| FluentValidation | 0 |
| RestSharp | 0 |
| logging-log4net | 0 |
| AutoMapper | 0 |
| Humanizer | 0 |
| quartznet | 0 |
| Newtonsoft.Json | 0 |
| ServiceStack | 0 |
| MassTransit | 0 |
| efcore | 0 |
| roslyn | 0 |
The first-run churn is ground dotnet format doesn't cover:
- Blank-line normalisation
- Using-directive sorting
- Chain breaking
- Comment alignment
- Reflow to
max_line_length(only if set — see below) - BOM handling
Nothing forces you to take the full diff in one commit.
Omitting max_line_length means no reflow happens. Curb becomes a whitespace formatter: it fixes
indentation, spacing, brace placement, and blank lines, and leaves every line exactly as long as it
was. That makes the first-run diff smaller — confined to whitespace-within-lines and blank-line
normalisation. See Reflow for what the key does and what each mode
costs.
You can adopt Curb with no max_line_length, and add a width later as its own commit when the churn
is convenient.
curb print-config Foo.cs prints every resolved option and says which mode you are in. Worth running
before the reformatting commit rather than after.
To reproduce these numbers for your own repository:
./build.sh churn --corpus /path/to/repo