Skip to content

[Bug]: Git LFS is configured but the files were never migrated, so 34 binaries always show as modified #2312

Description

@contextablemark

Describe the Bug

Git LFS is switched on for images and other binary files in .gitattributes, but the files that were already in the repo were never converted to LFS. The two halves disagree, so 34 binary files show up as "modified" in a clean checkout even though nobody has touched them.

I hit this in a fresh workspace: git status listed 26 modified images, logos, and videos I had never opened. I checked every one of them byte by byte — all 26 are identical to what is committed. Nothing was actually changed.

What is actually going on

Two things have to agree for Git LFS to work, and here they don't:

  1. .gitattributes says these files should be stored in LFS. It lists *.png, *.jpg, *.jpeg, *.gif, *.webp, *.ico, *.mp4, *.woff, *.woff2, *.ttf, *.otf, *.eot, and *.dill.
  2. The files in the repo are stored the old way — as ordinary files, not as LFS.

When Git compares your working copy against what is committed, it first runs the file through LFS. LFS replaces the real image with a tiny 129-byte text stub (that stub is all LFS ever stores in Git; the real image lives on a separate server). Git then compares that 129-byte stub against the real image that is committed, sees two completely different things, and says "modified."

So the difference Git is reporting is not a change to the image. It is the gap between how the file is stored and how .gitattributes says it should be stored.

You can watch it happen:

$ git lfs clean -- ic_launcher.webp < ic_launcher.webp
version https://git-lfs.github.com/spec/v1
oid sha256:58ae87fa0c5b5d1562d27fd648d2c061553fe20e3ed570bde588162d01ea7a27
size 2884
→ 129 bytes

That 129-byte stub is exactly what git diff --stat reports as the "new" version:

.../mipmap-xxhdpi/ic_launcher.webp | Bin 2884 -> 129 bytes

Where it came from

9d72a1bb — "chore: add git lfs tracking and update ci workflows" (#1242), 2026-04-13. That commit added the LFS rules but did not convert the files that were already in the repo, and nothing has converted them since.

36 files match the LFS rules. Only 2 of them are actually stored in LFS:

  • apps/dojo/e2e/fixtures/test-image.png
  • sdks/dotnet/tests/AGUI.Hosting.AspNetCore.IntegrationTests/Samples/GettingStarted/ag-ui-logo.png

The other 34 are in the mismatched state. In other words, LFS has been switched on for about four months and has been used for two files.

(Only 26 of the 34 showed up in my git status. Git skips this comparison when a file's timestamp still matches what it recorded at checkout, so the other 8 are quietly in the same state — they just haven't been looked at yet.)

Steps to Reproduce

  1. Clone the repo fresh with git-lfs installed.
  2. Run git status — or touch any tracked image, e.g. touch docs/logo/AG-UI\ logo_dark.png.
  3. The file is listed as modified.
  4. Confirm nothing really changed: git show HEAD:"docs/logo/AG-UI logo_dark.png" | shasum and shasum "docs/logo/AG-UI logo_dark.png" return the same hash.

Expected Behavior

A clean checkout should report a clean tree. Either the files are stored in LFS and match the rules, or the rules should not claim they are.

Why this is worth fixing rather than ignoring

It is not just noise — it is a footgun:

  • Anyone running git add -A or git commit -a silently converts images to LFS stubs, mixed into whatever they were actually working on. On a docs-heavy repo where people regularly touch images, that will land eventually. (I scoped a docs commit to two named files today specifically to avoid it — docs(tools): document ToolMessage.error in tool result examples #2311.)
  • Contributors without git-lfs installed get the 129-byte text stub instead of the image, so docs and example apps render broken images with no obvious explanation.
  • Every new workspace starts dirty, which trains people to ignore git status — the exact habit that lets a real accidental change slip through.

Two ways to fix it, and a recommendation

Option A — remove the LFS rules (my recommendation). Delete the LFS block from .gitattributes and keep binaries as ordinary Git files. Reasons:

  • LFS is already effectively unused: 2 files out of 36.
  • Converting the files properly would not actually shrink anyone's clone. Git history still contains the original copies, so the download stays the same size unless the history is rewritten — and rewriting history on a public protocol repo with many forks is not worth it for this.
  • Contributors would no longer need git-lfs installed to see images.
  • GitHub charges LFS bandwidth per organization, and this repo gets cloned a lot.
  • The amount of data involved is small: 20.3 MB across 36 files. That is unremarkable as ordinary Git files.

Option B — finish the migration. Run git add --renormalize . and commit, which converts all 34 files to LFS in one go. This gives a consistent state, but it means a 34-file binary commit that every open PR and fork has to rebase through, and it keeps the git-lfs-required-to-see-images downside.

Either option fixes the phantom-modified problem. The important thing is that the rules and the files agree.

Two related things found while looking

Both are independent of the LFS decision:

  • sdks/community/dart/test/client/config_test.dill is 10.5 MB — a Dart build artifact, and the single largest binary in the repo. It is half of the 20.3 MB total. Build artifacts probably should not be committed at all; this one looks accidental and is a good candidate for deletion plus a .gitignore entry.
  • docs/videos/Dojo-overview.mp4 (3.2 MB) is the one file where LFS would genuinely earn its keep. If there is an appetite to keep LFS for large media only, that is the file to keep it for — but it should then be migrated properly rather than left in today's mismatched state.

Environment

.gitattributes as of a40b5c08
git-lfs installed locally; 36 files match the LFS patterns, 2 are stored in LFS
Introduced in 9d72a1bb (#1242), 2026-04-13

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions