bug(repo): a core.autocrlf=true checkout fails the fixture-parsing tests — there is no .gitattributes #1485

Closed
opened 2026-10-02 09:51:52 +02:00 by claude · 1 comment
Member

A checkout made with core.autocrlf=true (the Git for Windows default) writes every tracked text file into the working tree with CRLF, and the tests that parse fixtures or compare snapshots fail on it. The repository has no .gitattributes, so nothing overrides the checkout-side setting.

It is a working-tree fault, not an OS one: the same CRLF tree bind-mounted into a golang:1.25 Linux container fails the same way.

Measured 2026-10-02 at 02fae6a (v0.63.1 post-cut), Go 1.27.1 on Windows 11, Git 2.55.0.windows.3:

git ls-files --eol, fresh clone, core.autocrlf=true (system default)
  613  i/lf    w/crlf
    7  i/none  w/none        empty files
    0  paths with a CR byte in the index, 0 binary paths, 0 .bat/.cmd/.ps1
failing on the CRLF tree, passing on an LF tree of the same commit
  internal/decide  TestDecide_882PendingCutReturnsTypedRecoveryEvidence
                   cannot parse changelog "CHANGELOG.md": changelog: malformed version heading
  internal/gates   TestV035HistoricalSectionRemainsClean
                   check 2 (sections): missing section heade...
  cmd/rt           TestHelpSnapshot   (Linux container, mounted tree)
                   rt --help drifted from cmd/rt/testdata/help.txt

cmd/rt does not compile on Windows (internal/release/lock.go uses syscall.Flock), so TestHelpSnapshot is only reachable through the container. With an LF working tree, all 29 packages pass in that container.

Because the index is already LF everywhere, * text=auto eol=lf changes no stored content. git add --renormalize . stages only the attributes file.

Out of scope

The remaining native-Windows failures have Windows causes, not line-ending ones:

  • the Flock build failure
  • POSIX mode and executable-bit assertions (TestRepinFile_PreservesModeAndSkipsNoMatch, TestDiscoverUnreadableIsUngradeable, TestRunPostBumpHooks_theHookDoesNotReceiveTheGitCredential)
  • backslash paths in register.ScanTree (TestScanTree)
  • Git Bash locale behaviour (TestHeaderEmDashLocaleAndByteClassMutation)

Acceptance criteria

  • A root .gitattributes forces LF in the working tree for text files: * text=auto eol=lf, plus explicit text eol=lf for the types the tests parse — DONE @ #1489 (55a12b9, reviewed as 36aa902).
  • A fresh clone with core.autocrlf=true shows zero w/crlf in git ls-files --eol — DONE @ #1489 (55a12b9, reviewed as 36aa902). Fresh core.autocrlf=true clone: 615 i/lf w/lf, 7 i/none w/none, 0 w/crlf.
  • git add --renormalize . stages nothing besides .gitattributes — DONE @ #1489 (55a12b9, reviewed as 36aa902). Only .gitattributes was staged before the commit; 0 paths on the fresh clone.
  • The three tests above pass on that clone: the first two natively, TestHelpSnapshot in a Linux container on the mounted tree — DONE @ #1489 (55a12b9, reviewed as 36aa902). internal/decide and internal/gates ok natively, cmd/rt ok in golang:1.25; the reviewer reproduced all three on the tree merged with 29b541b.
  • The changelog.d fragment tells contributors with an existing CRLF checkout how to rewrite it: git rm -r --cached -q . && git reset --hard, after committing or stashing local work — DONE @ #1489 (55a12b9, reviewed as 36aa902). changelog.d/1485-gitattributes-eol.internal.md; recipe measured on a CRLF clone that pulled the change: all 615 files w/lf afterwards, clean status.

Anchor

Measured in throwaway clones: a baseline clone at HEAD and a fresh clone of a scratch commit that adds .gitattributes, both made under the system core.autocrlf=true.

A checkout made with `core.autocrlf=true` (the Git for Windows default) writes every tracked text file into the working tree with CRLF, and the tests that parse fixtures or compare snapshots fail on it. The repository has no `.gitattributes`, so nothing overrides the checkout-side setting. It is a working-tree fault, not an OS one: the same CRLF tree bind-mounted into a `golang:1.25` Linux container fails the same way. Measured 2026-10-02 at `02fae6a` (v0.63.1 post-cut), Go 1.27.1 on Windows 11, Git 2.55.0.windows.3: ``` git ls-files --eol, fresh clone, core.autocrlf=true (system default) 613 i/lf w/crlf 7 i/none w/none empty files 0 paths with a CR byte in the index, 0 binary paths, 0 .bat/.cmd/.ps1 ``` ``` failing on the CRLF tree, passing on an LF tree of the same commit internal/decide TestDecide_882PendingCutReturnsTypedRecoveryEvidence cannot parse changelog "CHANGELOG.md": changelog: malformed version heading internal/gates TestV035HistoricalSectionRemainsClean check 2 (sections): missing section heade... cmd/rt TestHelpSnapshot (Linux container, mounted tree) rt --help drifted from cmd/rt/testdata/help.txt ``` `cmd/rt` does not compile on Windows (`internal/release/lock.go` uses `syscall.Flock`), so `TestHelpSnapshot` is only reachable through the container. With an LF working tree, all 29 packages pass in that container. Because the index is already LF everywhere, `* text=auto eol=lf` changes no stored content. `git add --renormalize .` stages only the attributes file. ## Out of scope The remaining native-Windows failures have Windows causes, not line-ending ones: - the `Flock` build failure - POSIX mode and executable-bit assertions (`TestRepinFile_PreservesModeAndSkipsNoMatch`, `TestDiscoverUnreadableIsUngradeable`, `TestRunPostBumpHooks_theHookDoesNotReceiveTheGitCredential`) - backslash paths in `register.ScanTree` (`TestScanTree`) - Git Bash locale behaviour (`TestHeaderEmDashLocaleAndByteClassMutation`) ## Acceptance criteria - [x] A root `.gitattributes` forces LF in the working tree for text files: `* text=auto eol=lf`, plus explicit `text eol=lf` for the types the tests parse — DONE @ #1489 (`55a12b9`, reviewed as `36aa902`). - [x] A fresh clone with `core.autocrlf=true` shows zero `w/crlf` in `git ls-files --eol` — DONE @ #1489 (`55a12b9`, reviewed as `36aa902`). Fresh `core.autocrlf=true` clone: 615 `i/lf w/lf`, 7 `i/none w/none`, 0 `w/crlf`. - [x] `git add --renormalize .` stages nothing besides `.gitattributes` — DONE @ #1489 (`55a12b9`, reviewed as `36aa902`). Only `.gitattributes` was staged before the commit; 0 paths on the fresh clone. - [x] The three tests above pass on that clone: the first two natively, `TestHelpSnapshot` in a Linux container on the mounted tree — DONE @ #1489 (`55a12b9`, reviewed as `36aa902`). `internal/decide` and `internal/gates` ok natively, `cmd/rt` ok in `golang:1.25`; the reviewer reproduced all three on the tree merged with `29b541b`. - [x] The `changelog.d` fragment tells contributors with an existing CRLF checkout how to rewrite it: `git rm -r --cached -q . && git reset --hard`, after committing or stashing local work — DONE @ #1489 (`55a12b9`, reviewed as `36aa902`). `changelog.d/1485-gitattributes-eol.internal.md`; recipe measured on a CRLF clone that pulled the change: all 615 files `w/lf` afterwards, clean status. ## Anchor Measured in throwaway clones: a baseline clone at HEAD and a fresh clone of a scratch commit that adds `.gitattributes`, both made under the system `core.autocrlf=true`.
Author
Member

#1489 landed as 55a12b9. git range-diff shows it is patch-identical (=) to the reviewed head 36aa902.

  • Approval: alex, review 7150, bound to 36aa902.
  • Checks at merge: combined status success, with 28 contexts green. The two pending go-ci contexts are jobs gated to push on main.
  • Acceptance criteria: all five carry DONE @ #1489.

What actually shut this issue was a PR comment, not the PR body. The body says Refs #1485, and its earlier keyword reference shows as neutered in this timeline.

The close came from comment 113711 on #1489. That comment has a plain-text closing keyword directly before this issue's number, and the timeline records it with ref_action: closes. Forgejo honoured it at merge time: the close event is at 11:11:59, the same second as the PR's merged_at.

Comment 113708 has the same keyword inside inline code. It was recorded as ref_action: none.

ac-closure-check grades the PR body and the commit messages only, so a closing reference carried by a PR comment is outside what it sees. The outcome here is the intended one, because every AC is done. The mechanism is a gap in the gate, though, and worth a follow-up.

**#1489 landed as `55a12b9`.** `git range-diff` shows it is patch-identical (`=`) to the reviewed head `36aa902`. - **Approval:** alex, review 7150, bound to `36aa902`. - **Checks at merge:** combined status `success`, with 28 contexts green. The two pending `go-ci` contexts are jobs gated to `push` on `main`. - **Acceptance criteria:** all five carry `DONE @ #1489`. **What actually shut this issue was a PR comment, not the PR body.** The body says `Refs #1485`, and its earlier keyword reference shows as `neutered` in this timeline. The close came from comment 113711 on #1489. That comment has a plain-text closing keyword directly before this issue's number, and the timeline records it with `ref_action: closes`. Forgejo honoured it at merge time: the close event is at 11:11:59, the same second as the PR's `merged_at`. Comment 113708 has the same keyword inside inline code. It was recorded as `ref_action: none`. `ac-closure-check` grades the PR body and the commit messages only, so a closing reference carried by a PR comment is outside what it sees. The outcome here is the intended one, because every AC is done. The mechanism is a gap in the gate, though, and worth a follow-up.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
frankenbit/release-toolkit#1485
No description provided.