Problem
.gitattributes pins working-tree line endings per file type: eol=crlf for .csproj, .props,
.slnx, .ps1 and others, LF for .json, .yml, .md. .cs is marked text with no eol, so C#
files check out CRLF on Windows and LF on Linux. .editorconfig deliberately sets no end_of_line.
This costs us:
- Every new file type needs a decision. The types nobody listed —
PublicAPI.*.txt, .svg,
.csharpierignore, .codex/*.toml — fall back to each contributor's core.autocrlf.
- Working trees differ per OS, so byte-level tooling can behave differently locally and in CI.
- Nothing verifies that stored files match the attributes, so drift stays invisible.
Proposal
One rule: * text=auto eol=lf, plus end_of_line = lf in .editorconfig and a CI step that proves the
repository stays normalized. The repository has no .cmd or .bat files, so there is no exception.
Blocker to fix first
C# does not normalize newlines inside string literals — a literal's value is the file's bytes. Seven
assertions in tests/DbConnectionPlus.UnitTests/SqlStatements/InterpolatedSqlStatementTests.cs compare
an SQL fragment taken verbatim from a multi-line raw string literal against Environment.NewLine.
They pass today only because .cs checks out platform-native, so both sides move together. Once .cs
is LF everywhere, the literal is LF while Environment.NewLine on Windows is CRLF, and these tests fail
on Windows. CI runs the unit tests on Linux only, so CI would stay green.
Fix: expect a plain LF escape instead of Environment.NewLine, in its own commit before renormalizing.
Not affected: the CommandText assertions compare two multi-line literals from the same file, and the
other Environment.NewLine uses assert messages built at runtime.
Scope
- Decouple those seven assertions from
Environment.NewLine.
- Rename
scripts/tidy-cs.ps1 to scripts/tidy-code.ps1 and update every reference.
.gitattributes: * text=auto eol=lf plus the binary list. No per-type text pins.
.editorconfig: end_of_line = lf.
- CI
lint: add a renormalization guard.
- Renormalize, and document the policy in
CONTRIBUTING.md.
Acceptance criteria
Suggested branch: chore/lf-line-endings.
Problem
.gitattributespins working-tree line endings per file type:eol=crlffor.csproj,.props,.slnx,.ps1and others, LF for.json,.yml,.md..csis markedtextwith noeol, so C#files check out CRLF on Windows and LF on Linux.
.editorconfigdeliberately sets noend_of_line.This costs us:
PublicAPI.*.txt,.svg,.csharpierignore,.codex/*.toml— fall back to each contributor'score.autocrlf.Proposal
One rule:
* text=auto eol=lf, plusend_of_line = lfin.editorconfigand a CI step that proves therepository stays normalized. The repository has no
.cmdor.batfiles, so there is no exception.Blocker to fix first
C# does not normalize newlines inside string literals — a literal's value is the file's bytes. Seven
assertions in
tests/DbConnectionPlus.UnitTests/SqlStatements/InterpolatedSqlStatementTests.cscomparean SQL fragment taken verbatim from a multi-line raw string literal against
Environment.NewLine.They pass today only because
.cschecks out platform-native, so both sides move together. Once.csis LF everywhere, the literal is LF while
Environment.NewLineon Windows is CRLF, and these tests failon Windows. CI runs the unit tests on Linux only, so CI would stay green.
Fix: expect a plain LF escape instead of
Environment.NewLine, in its own commit before renormalizing.Not affected: the
CommandTextassertions compare two multi-line literals from the same file, and theother
Environment.NewLineuses assert messages built at runtime.Scope
Environment.NewLine.scripts/tidy-cs.ps1toscripts/tidy-code.ps1and update every reference..gitattributes:* text=auto eol=lfplus the binary list. No per-type text pins..editorconfig:end_of_line = lf.lint: add a renormalization guard.CONTRIBUTING.md.Acceptance criteria
dotnet testpasses on Windows as well as Linux..gitattributescontains no per-type text pins..editorconfigsetsend_of_line = lf.git ls-files --eolshows noi/crlfori/mixedentries.scripts/tidy-code.ps1replacestidy-cs.ps1, with no reference to the old name left.CONTRIBUTING.mdexplains the policy.Suggested branch:
chore/lf-line-endings.