How do you renormalize an existing Git repo to LF after adding `* text=auto`?
answer
- Attributes only govern content from now on
- One deliberate commit beats a hundred accidental ones
- A git add flag re-applies the conversion
- Plan for blame and open branches
- A merge strategy option defuses old branches
basics
~20 sCommit the .gitattributes rule, then run git add --renormalize . and commit the result. Git re-applies the new conversion to every tracked file, producing one commit that rewrites the stored line endings without changing any visible text.
solid answer
~40 sAdding `* text=auto` only changes what happens to content from that point on; blobs already stored with CRLF stay as they are. To make the stored bytes match the policy, commit the attributes file first, then run `git add --renormalize .`, which re-runs the check-in conversion for every tracked path and stages the differences. Commit that as a single, clearly-named normalisation change and verify with `git ls-files --eol` that the index now reports `i/lf`. Plan the fallout: the commit touches almost every file, so land it when few branches are open, record its hash in a `blame.ignoreRevsFile` so blame stays useful, and set `merge.renormalize` to true — or merge with `-X renormalize` — so in-flight branches created before the sweep do not conflict on every line.
code
bash · 11 linesgit add .gitattributes
git commit -m "build: declare line-ending policy"
git add --renormalize .
git status --short | head
git commit -m "chore: normalise line endings to LF"
git ls-files --eol | head
git rev-parse HEAD >> .git-blame-ignore-revs
git config blame.ignoreRevsFile .git-blame-ignore-revs
git config merge.renormalize truego deeper
Know that a .gitattributes rule applies going forward only, and that git add --renormalize . is the command that brings existing files into line.
Explain the ordering — policy commit first, renormalisation commit second — and how you would verify the result with git ls-files --eol before pushing.
Show you have run this on a live repository: isolate the commit, protect blame with an ignore-revs file, enable the renormalize merge option for in-flight branches, and time the sweep when few branches are open.
Treat it as a migration with a blast radius: sequence it against release branches and open work, communicate the one-time diff, and reject history-rewriting alternatives that invalidate every clone for no additional benefit.
## Why a separate step is needed A `.gitattributes` rule governs conversion at check-in and checkout. It says nothing about blobs that are already in the object database. If half the repository was committed from Windows machines with CRLF in the blobs, adding `* text=auto` changes nothing about those blobs: the next person to touch such a file normalises it and produces the whole-file diff you were trying to eliminate — just once per file, unpredictably, spread over months. Renormalisation collapses all of that into one deliberate commit. ## The procedure 1. **Commit the policy first.** Add `.gitattributes` with `* text=auto` plus any per-path `eol=lf` / `eol=crlf` / `binary` exceptions, and commit it on its own. Doing this separately keeps the policy commit reviewable, because the next commit will be unreadable. 2. **Renormalise.** `git add --renormalize .` re-applies the check-in conversion to every tracked file and stages the result. It is the modern replacement for the old "delete the index and check everything out again" recipes, and it is safe to run repeatedly — files already stored correctly produce no change. 3. **Inspect before committing.** `git status` will list a large number of modified paths. Confirm the change is only line endings: `git diff --cached --stat` gives the scale, and `git diff --cached -w` should be close to empty for files where only terminators moved. Watch for any binary you did not exclude appearing in the list — that is a mis-classified path and a signal to add an explicit `binary` or `-text` rule and redo the step. 4. **Commit with an obvious subject**, e.g. "normalise line endings to LF", and nothing else in that commit. 5. **Verify.** `git ls-files --eol` should now report `i/lf` for text paths, and `git check-attr text eol -- <path>` should confirm the intended rule for the exceptions. After the sweep, working-tree files still hold whatever endings they had; they get the policy-conformant form the next time they are checked out (a fresh clone, a branch switch, or an explicit re-checkout of the paths). ## Managing the fallout **Blame.** One commit now sits on top of every line in the repository. Record its full hash in a file — conventionally something like `.git-blame-ignore-revs` — and point `blame.ignoreRevsFile` at it in the repository's config, or pass `git blame --ignore-rev <sha>` ad hoc. Blame then skips past the normalisation and attributes each line to the commit that last changed it meaningfully. **In-flight branches.** Any branch created before the sweep still holds the old endings, so merging it produces a conflict on every line of every touched file. Git has a purpose-built answer: the merge strategy option `renormalize` re-normalises the three merge inputs before comparing them, which makes pure line-ending differences disappear. Enable it once with the `merge.renormalize` config set to true, or pass `-X renormalize` on the individual merges. Even so, the kinder plan is to land the sweep when the number of open branches is small, and tell branch owners to rebase or merge promptly afterwards. **History searches.** Tools that grep raw blobs across history will see the discontinuity, and any external system that pinned a blob hash for a text file will find it changed. Neither is usually a problem, but say so out loud when proposing the change. ## Guarding against a repeat Renormalising once is only worth doing if the policy holds afterwards. The attributes file is what holds it, because it travels with every clone. If you also want a warning when someone commits a file with mixed endings — content that cannot round-trip through the conversion — set `core.safecrlf` to `warn` (or `true` to refuse outright); that fires at check-in, before the bad content lands. ## What not to do Do not hand-run a stream editor over the tree to strip carriage returns: it touches binaries, misses files with mixed endings, and leaves the repository without a rule so the churn returns. Do not rewrite history with a filter tool to retroactively normalise every past commit — it changes every commit hash in the repository, invalidates every clone, breaks signatures and tags, and buys nothing that a single forward commit does not. ## Interview framing Name `git add --renormalize .` as the mechanism, sequence it after the attributes commit, and then spend most of your answer on the operational consequences — blame noise, branch conflicts, the `renormalize` merge option — because that is what distinguishes someone who has actually done it from someone who read the flag.
- What does the renormalize merge strategy option do for branches created before the sweep?It re-normalises the merge base and both sides before comparing them, so differences that exist only in line endings vanish and the merge sees just the real edits. Enable it with the `merge.renormalize` config or pass `-X renormalize` per merge; without it a pre-sweep branch conflicts on every line of every normalised file.
- Why not rewrite history so past commits also carry LF?Because it changes every commit hash from the rewrite point onward, invalidating every existing clone, breaking tags and signatures, and forcing everyone to re-clone. A single forward normalisation commit gives you the same clean present state at a fraction of the cost. History is allowed to record what actually happened.
- How do you keep the normalisation commit from dominating git blame?Write its full hash into a file such as `.git-blame-ignore-revs` and set the `blame.ignoreRevsFile` config to that path, or pass `git blame --ignore-rev <sha>`. Blame then looks through the sweep to the commit that last changed each line for a real reason.
saying these in an interview costs you the question
- Thinks adding .gitattributes retroactively fixes stored blobs
- Proposes a history rewrite to normalise past commits
- Runs a stream editor over the tree instead
- Ignores that every open branch will now conflict
- Mixes the normalisation with functional changes in one commit