chore: check out all text files with LF via .gitattributes - #4
Merged
Merged
Conversation
brush is a shell, and its test corpus is shell scripts. Shell is
newline-sensitive in ways most languages are not: a stray CR becomes part of
a here-document delimiter, a command name, or a `#!` interpreter path. On a
Windows checkout with core.autocrlf=true every text file lands as CRLF, and
several compat tests fail as a result -- not because brush is wrong, but
because the fixtures were mangled on their way to disk. Two examples of the
resulting noise:
/usr/bin/env: 'bash\r': Permission denied
process-helpers.sh: line 1: syntax error near unexpected token `$'{\r''
The repository had no .gitattributes at all, so the checkout representation
was left entirely to each contributor's local git configuration.
Add one that pins every text file to LF on all platforms. `text` rather than
`text=auto` states the intent outright instead of deferring to git's binary
heuristic, so a newly added file cannot be silently classified as binary and
escape normalization. The sole real binary in the tree, the docs screenshot,
is exempted with `binary` (shorthand for `-text -diff`).
No content is rewritten by this change: `git ls-files --eol` reports every one
of the 674 tracked text files as already `i/lf`, and `git add --renormalize .`
stages nothing beyond this file. CRLF only ever existed in the working tree,
injected at checkout. This commit makes that guarantee explicit and permanent
rather than a property of how any one contributor happens to be configured.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
cataggar
added a commit
that referenced
this pull request
Aug 22, 2026
brush is a shell, and its test corpus is shell scripts. Shell is
newline-sensitive in ways most languages are not: a stray CR becomes part of
a here-document delimiter, a command name, or a `#!` interpreter path. On a
Windows checkout with core.autocrlf=true every text file lands as CRLF, and
several compat tests fail as a result -- not because brush is wrong, but
because the fixtures were mangled on their way to disk. Two examples of the
resulting noise:
/usr/bin/env: 'bash\r': Permission denied
process-helpers.sh: line 1: syntax error near unexpected token `$'{\r''
The repository had no .gitattributes at all, so the checkout representation
was left entirely to each contributor's local git configuration.
Add one that pins every text file to LF on all platforms. `text` rather than
`text=auto` states the intent outright instead of deferring to git's binary
heuristic, so a newly added file cannot be silently classified as binary and
escape normalization. The sole real binary in the tree, the docs screenshot,
is exempted with `binary` (shorthand for `-text -diff`).
No content is rewritten by this change: `git ls-files --eol` reports every one
of the 674 tracked text files as already `i/lf`, and `git add --renormalize .`
stages nothing beyond this file. CRLF only ever existed in the working tree,
injected at checkout. This commit makes that guarantee explicit and permanent
rather than a property of how any one contributor happens to be configured.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
brush is a shell, and its test corpus is shell scripts. Shell is newline-sensitive in ways most languages are not — a stray CR becomes part of a here-document delimiter, a command name, or a
#!interpreter path. So on this repo the line ending is a correctness concern, not a formatting preference.The repository had no
.gitattributesat all, leaving the checkout representation entirely to each contributor's local git config. On a Windows checkout withcore.autocrlf=true, every text file lands as CRLF and tests fail for reasons that have nothing to do with brush:What
Pin every text file to LF on all platforms.
textrather thantext=autois deliberate: it states the intent outright instead of deferring to git's binary heuristic, so a newly added file cannot be silently classified as binary and escape normalization. The sole real binary in the tree (docs/extras/brush-screenshot.png) is exempted withbinary, shorthand for-text -diff.No content is rewritten
This is the part worth checking before merging — the change is a single new file, not a mass rewrite:
git ls-files --eolreported all 674 tracked text files as alreadyi/lfgit add --renormalize .staged nothing beyond.gitattributesitselfi/lf w/lf, plus the one-textPNG untouchedCRLF only ever existed in the working tree, injected at checkout. This commit makes the LF guarantee explicit and permanent rather than a property of how any one contributor is configured.
Effect on the suite
On a Windows checkout under WSL, the compat suite had 5 failures. This fixes the
Basic processfailure outright (itsprocess-helpers.shfixture was being mangled).The remaining 4 — the
Error: newline before ... in [[ ]]cases — are unrelated and pre-existing, and are not addressed here. They're a genuine semantic difference: given[[ afollowed by a newline, bash reports the error at execution time and the script still exits 0, while brush rejects it at parse time and exits 2. Both correctly refuse to run it; only the exit status differs.