Skip to content

Source/DosCommand.Register.pas permanently shows as modified (CRLF blob vs. text attribute) #26

Description

@StatusPro-ziegelt

Source/DosCommand.Register.pas permanently shows as modified (CRLF blob vs. text attribute)

Summary

Dear all,

After a fresh checkout, Source/DosCommand.Register.pas can show up as modified even though its content is unchanged. Reverting it (git checkout -- <file> / git restore <file>) does not help: the file is reported as modified again right away.

The cause is that this file is stored in the repository with CRLF line endings, while .gitattributes marks it as text. Git always stores text files with LF endings, so the working copy never matches the stored blob.

Affected commit: 724d611 ("Removed the assertion.") - current master at the time of writing.

Environment

  • Windows 10 Pro, Git for Windows
  • core.autocrlf = true (Git for Windows default)
  • Reproduced both as a submodule and as a standalone clone

Evidence

git ls-files --eol shows that this is the only text file whose blob has CRLF line endings:

i/crlf  w/crlf  attr/text   Source/DosCommand.Register.pas
i/lf    w/crlf  attr/text   Source/DosCommand.pas
i/lf    w/crlf  attr/text   README.md
i/lf    w/crlf  attr/text   license.txt
  • i/crlf: the blob in the repository has CRLF line endings.
  • attr/text: .gitattributes tells Git to store the file as LF.

Hashing the working file the way git add would (with the CRLF to LF conversion applied) gives a different hash than the blob in the index, even in a clean, untouched clone:

$ git ls-files -s Source/DosCommand.Register.pas
100644 036ac03b08c421376c72a0d8e7a18e49900011f9 0   Source/DosCommand.Register.pas

$ git hash-object Source/DosCommand.Register.pas
8aa1e1a7c09541b03b8c8c9f912eff3b27bb06d2

A diff that ignores CR at end of line is empty, so the only difference is the line endings:

$ git diff --ignore-cr-at-eol --stat Source/DosCommand.Register.pas
(no output)

Why it doesn't always show up

git status only compares file content when a file's cached stat data (size, mtime, inode) no longer matches the index. In an untouched clone, the stats still match, so Git never reads the file and git status reports clean.

As soon as Git re-reads the file, it shows as modified. That can be triggered by:

  • racy timestamps right after checkout (common for submodule checkouts),
  • an IDE or Git GUI touching or re-saving the file,
  • a parent repository refreshing the submodule's index.

To reproduce in a clean clone:

git clone https://github.com/TurboPack/DOSCommand.git
cd DOSCommand
touch Source/DosCommand.Register.pas
git status        # -> modified: Source/DosCommand.Register.pas

Reverting writes the CRLF blob out again with a new timestamp, so the file is reported as modified again right away.

Likely origin

.gitattributes was added in commit 9fa911a ("Saved with 12.1 and added .gitattributes for CRLF.") without re-normalizing files that were already in the repository. Every other text file is stored as LF, so this one file seems to have been missed.

Suggested fix

Re-normalize the file once and commit the change. Only the line endings in the stored blob change. The code stays the same, and Windows checkouts still get CRLF.

git add --renormalize Source/DosCommand.Register.pas
git commit -m "Normalize line endings of DosCommand.Register.pas per .gitattributes"

To catch any other affected files at the same time:

git add --renormalize .
git status

Workaround for users (until fixed)

This hides the change in one local checkout only:

git update-index --skip-worktree Source/DosCommand.Register.pas

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions