You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
git-lfs interacts with case sensitivity across platforms poorly #6187
Git LFS pattern matching in .gitattributes is case-sensitive on Linux, but files may be pushed from case-insensitive systems (Windows, macOS) with varying case extensions. When checking out on Linux, files with uppercase extensions (e.g., .TGA, .PNG, .BMP) are not automatically fetched/smudged because the .gitattributes patterns use lowercase (e.g., *.tga).
To Reproduce
On Windows or macOS, create and push files with uppercase extensions (e.g., texture.TGA)
Ensure .gitattributes has lowercase patterns: *.tga filter=lfs diff=lfs merge=lfs -text
Clone the repository on Linux
Run git lfs ls-files - files with uppercase extensions show * (pointer, not downloaded)
Run git lfs pull - uppercase extension files remain as pointers
Unity (or other tools) fail to read these files: Could not create asset from .../T_Facade_02_N.TGA: File could not be read
Expected behavior
Either:
LFS pattern matching should be case-insensitive by default (or have an option for it)
git lfs pull should have a warning when patterns don't match due to case
Documentation should clearly warn about cross-platform case sensitivity issues
Workaround
Files can be pulled individually using the full path:
git lfs pull --include="path/to/file.TGA"
Or force checkout specific files:
git checkout HEAD -- "path/to/file.TGA"
Or add duplicate patterns with uppercase extensions to .gitattributes:
This is a common cross-platform collaboration issue. Teams working across Windows/macOS (case-insensitive) and Linux (case-sensitive) will silently have broken LFS checkouts unless both cases are explicitly listed in .gitattributes.
Related issue: #6156
Related: #3433 - this probably introduced the bug
As a Git contributor, I think it's worth pointing out that this actually also affects all uses of patterns in .gitattributes, not just Git LFS. For instance, the following would also produce similar results to those you've observed on Windows and Linux:
*.sh text eol=lf
*.bat text eol=crlf
foo.BAT would use CRLF on Windows but LF (or the configured default) on Linux or FreeBSD. Similar rules apply for working-tree-encoding and all of the other possible rules. Git LFS is not involved at all in this pattern matching.
Because tools like git checkout actually cause Git to decide which files to invoke Git LFS on and Git LFS doesn't get invoked at all on those files unless Git decides it should, I don't think it would be possible to have a deliberately case-insensitive mode unless Git added support for it since it wouldn't work in all cases. Note that setting core.ignorecase is not recommended because that's supposed to reflect the actual state on disk, which means that Git works correctly even with case-sensitive Windows and macOS repositories and case-insensitive Linux repositories (all of which exist).
I'll also point out that there are some cases in which case-insensitivity definitely breaks things. For instance, .c files are always C files, but on most Unix systems, .C files are C++ files and are compiled accordingly (GCC and clang have this behaviour).
I don't want to speak for the Git LFS maintainers in deciding what the right decision is here, but just to mention a relevant detail about how Git is involved and how Git LFS is mostly a victim of what Git itself has decided to do.
I'll also point out that there are some cases in which case-insensitivity definitely breaks things. For instance, .c files are always C files, but on most Unix systems, .C files are C++ files and are compiled accordingly (GCC and clang have this behaviour).
IMO, the paths for lfs in .gitattributes should be evaluated case insensitively. That's always what the user intends. there can be a separate config to make it case sensitive.
Hey, I'm sorry you're having trouble, and thanks for writing up the issue and proposing some options.
We would be happy to review a PR to update our documentation, and our git-lfs-faq(7) manual page might be the best place for some notes about case-sensitivity. As well, anyone can contribute to our wiki, and the Limitations page there might be a suitable spot for some cautions about case-sensitivity.
As @bk2204 noted, we're not able to affect the way Git applies attributes, and that's one of the key ways in which Git LFS is invoked by Git, so there are major limitations there in terms of how we operate on case-preserving but case-insensitive filesystems. We can illustrate this without Git LFS at all; for instance, on a macOS system with APFS in its default mode, by toggling core.ignoreCase we can still make Git ignore a foo.BIN file if the .gitattributes file only specifies *.bin:
$ git init test
$ cd test
$ git config core.ignoreCase
true
$ echo "*.bin myattr" >.gitattributes
$ echo foo >foo.bin
$ git check-attr -a foo.bin
foo.bin: myattr: set
$ git check-attr -a foo.BIN
foo.BIN: myattr: set
$ git config core.ignoreCase false
$ git check-attr -a foo.BIN
# no output
One option for users is to explicitly make their Git attribute patterns case-insensitive, e.g., *.[bB][iI][nN] or *.[tT][gG][aA]. Unfortunately we can't make this the default type of pattern created by the git lfs track command, at least not until the next major release of the Git LFS client, as it would be a backwards-incompatible change. However, if someone wanted to write up a PR to add a -i/--ignore-case option to the git lfs track command, we'd certainly consider it.
Regarding the git lfs checkout and git lfs pull commands, I think one of the reasons you're noticing this issue is that in recent versions of the Git LFS client these commands make use of the git ls-files command and in particular its ability to list only the files matching a given attribute, in our case the filter=lfs attribute. This allows the commands to operate efficiently in very large repositories where users may have performed only a partial clone or a sparse checkout, since we don't have to iterate through all the files in the repository. It does mean, however, that we depend on the Git configuration and the settings in the .gitattributes files to correctly identify all relevant Git LFS files.
I suppose one avenue which could be explored would be to run the git ls-files command twice when core.ignoreCase was set to false (as it typically is on a Linux system, for instance). Running it a second time with core.ignoreCase explicitly and temporarily set to true and then comparing the results with the first run would identify any files which might have unintentionally been skipped due to case-sensitivity differences between systems; these could then be listed in a warning message. If someone wants to draft a PR to implement this design (or another one) as an experiment, we'd of course take a look at it. We would want to be cautious, though, with regard to any potential performance penalty in a large repository.
Regarding my other suggestions, it occurs to me that instead of trying to detect possible oversights due to case-sensitivity issues in the git lfs checkout or git lfs pull commands, the git lfs fsck command might be better suited to that activity, since users have no particular performance expectations of that command, and do expect it to make a thorough analysis of the current repository state.
As well, if a -i/--ignore-case option were added to the git lfs track command, when that option was not provided and the user did not explicitly provide a mixed-case pattern, and the command was running on a case-insensitive filesystem, the command could output a message suggesting the use of the --ignore-option option if the user intended to share their repository with developers on case-sensitive filesystems.
(Note that to detect a case-insensitive filesystem, I would recommend not simply checking the operating system type, since both APFS and NTFS can be configured in case-sensitive modes. Instead, we would want to perform a check similar to the one Git implements, where we would try to open the .git/config file as .git/CoNfIG and assume case-insensitivity if that succeeds.)
Building on the workarounds already discussed in this thread (e.g., adding duplicate uppercase patterns like *.PNG alongside *.png) and broader community practice, the core issue is that .gitattributes pattern matching is case-sensitive (consistent with Git’s index semantics). On macOS/Windows, users commonly commit assets with uppercase extensions (.PNG, .TGA, etc.), but on case-sensitive filesystems (Linux CI/build servers), patterns like *.png won't match .PNG, so Git LFS may leave pointer files instead of smudging content (e.g., Unity pipelines break).
While duplicating patterns can work, bracket-glob patterns (e.g., *.[pP][nN][gG]) are more robust and portable: they are pure Git-supported syntax, avoid duplication, and behave consistently across all filesystems.
This could be addressed incrementally without changing existing behavior or adding overhead to hot paths (pull/checkout):
Docs (low risk / immediate): formalize the portable bracket-glob recommendation, e.g. *.[pP][nN][gG], *.[tT][gG][aA], with a short cross-platform note in git-lfs-faq(7) under Tracking and Migration.
git lfs track --ignore-case convenience option: git lfs track --ignore-case "*.png" writes the bracket glob in .gitattributes (pure Git syntax; no new LFS matching logic; consider for a major version to avoid compatibility issues).
Diagnostics in git lfs fsck: opt-in checks suitable for CI such as case-collisions and “risky patterns” (*.png but .PNG present), with flags like --json and --strict.
Case-insensitive filesystem detection: probe-based (like Git’s core.ignoreCase detection) rather than OS guessing — resolve the gitdir, then test opening the config file with alternate case.
Happy to submit a small docs PR first to git-lfs-faq(7) (addresses part of #6187) if that helps move things forward.
okay, i think another POV is, if i write git clone with a default lfs configuration / a valid one from the POV of git lfs, and it succeeds with error code 0, but my directory is populated with pointer files that also do not get checked out with git lfs pull (or whatever), i hate to use chatgpt turns of phrase here haha, but: that's a serious bug. it's not a small bug or a documentation fix.
the reason i ticketed this is because it affects backups. the whole point of git is that git clone does what it says it does or fails. this is a really bad failure. it has to be fixed in git too, or git lfs has to become part of git, or i don't know - there has to be a configuration that will raise an error in the scenario i reported, and it probably has to be the default configuration
I understand your concerns, @doctorpangloss, even if I don't fully share them.
We've described already some of the historical and contemporary limitations under which the Git LFS project operates, so I won't repeat them again here.
I would urge you to contact the Git mailing list to discuss how Git applies its attribute patterns in case-insensitive filesystems, since that's at the root of this issue. That would also be the right place to open a discussion as to whether Git would ever absorb the Git LFS project, but I suspect you'll be advised that the Git project is not interested in doing so.
the reason i ticketed this is because it affects backups. the whole point of git is that git clone does what it says it does or fails
A plain git clone command is certainly a reasonable way to download a regular Git repository's history and contents for backup purposes. However, it's important to note that a git clone operation effectively performs both git fetch and git checkout operations, and while the former may download the full history of a repository, the latter creates a working tree from a single commit on a single branch. Because Git LFS is invoked during the checkout phase, not the fetch phase, it can only download the objects for that single commit.
To make a backup copy of a repository and all of the related Git LFS objects, you need to run a command like git lfs fetch --all after cloning. This is especially true if you've made a bare clone with git clone --bare, since then no checkout phase is performed at all.
The git lfs fetch --all command will retrieve Git LFS objects for pointers even if those pointers don't match any filter=lfs Git attribute patterns, unlike git lfs pull and git lfs checkout. Therefore Git LFS objects for files which don't match any patterns due to case-sensitivity differences between filesystems will still be fetched. We can illustrate this difference:
If you then include the .git/lfs/objects directories in your archive of the repository, you would have a backup which contains all the Git LFS objects as well as the Git contents.
Now if you wanted a git clone operation to automatically download all related Git LFS objects, you'd need to advocate for Git to introduce a post-fetch hook (a topic which has come up in the discussion of PR #6171), so that Git LFS could be invoked even after a bare clone and would always know the full history of everything that had just been fetched.
However, not all users would view such an automatic download of Git LFS objects as a feature. For a repository with many large Git LFS objects throughout its history, a git clone operation may still be fast and cheap, but fetching all the related Git LFS objects, including those which are no longer referenced by any pointers in the tips of any branches, could be slow and expensive.
The conversation in #5969 also touched on some of these points, in case that's helpful.
Activity
bk2204 commentedon Jan 8, 2026
As a Git contributor, I think it's worth pointing out that this actually also affects all uses of patterns in
.gitattributes, not just Git LFS. For instance, the following would also produce similar results to those you've observed on Windows and Linux:foo.BATwould use CRLF on Windows but LF (or the configured default) on Linux or FreeBSD. Similar rules apply forworking-tree-encodingand all of the other possible rules. Git LFS is not involved at all in this pattern matching.Because tools like
git checkoutactually cause Git to decide which files to invoke Git LFS on and Git LFS doesn't get invoked at all on those files unless Git decides it should, I don't think it would be possible to have a deliberately case-insensitive mode unless Git added support for it since it wouldn't work in all cases. Note that settingcore.ignorecaseis not recommended because that's supposed to reflect the actual state on disk, which means that Git works correctly even with case-sensitive Windows and macOS repositories and case-insensitive Linux repositories (all of which exist).I'll also point out that there are some cases in which case-insensitivity definitely breaks things. For instance,
.cfiles are always C files, but on most Unix systems,.Cfiles are C++ files and are compiled accordingly (GCC and clang have this behaviour).I don't want to speak for the Git LFS maintainers in deciding what the right decision is here, but just to mention a relevant detail about how Git is involved and how Git LFS is mostly a victim of what Git itself has decided to do.
doctorpangloss commentedon Jan 8, 2026
let's look on github for .C files
https://github.com/search?q=path%3A%2F%28%3F-i%29%5C.C%24%2F&type=code
795k files
page after page, exactly zero are C++. using .C to mean C++, and then checking in those C++ files with LFS for some reason, is very HPE NonStop.
IMO, the paths for lfs in .gitattributes should be evaluated case insensitively. That's always what the user intends. there can be a separate config to make it case sensitive.
chrisd8088 commentedon Jan 9, 2026
Hey, I'm sorry you're having trouble, and thanks for writing up the issue and proposing some options.
We would be happy to review a PR to update our documentation, and our
git-lfs-faq(7)manual page might be the best place for some notes about case-sensitivity. As well, anyone can contribute to our wiki, and the Limitations page there might be a suitable spot for some cautions about case-sensitivity.As @bk2204 noted, we're not able to affect the way Git applies attributes, and that's one of the key ways in which Git LFS is invoked by Git, so there are major limitations there in terms of how we operate on case-preserving but case-insensitive filesystems. We can illustrate this without Git LFS at all; for instance, on a macOS system with APFS in its default mode, by toggling
core.ignoreCasewe can still make Git ignore afoo.BINfile if the.gitattributesfile only specifies*.bin:One option for users is to explicitly make their Git attribute patterns case-insensitive, e.g.,
*.[bB][iI][nN]or*.[tT][gG][aA]. Unfortunately we can't make this the default type of pattern created by thegit lfs trackcommand, at least not until the next major release of the Git LFS client, as it would be a backwards-incompatible change. However, if someone wanted to write up a PR to add a-i/--ignore-caseoption to thegit lfs trackcommand, we'd certainly consider it.Regarding the
git lfs checkoutandgit lfs pullcommands, I think one of the reasons you're noticing this issue is that in recent versions of the Git LFS client these commands make use of thegit ls-filescommand and in particular its ability to list only the files matching a given attribute, in our case thefilter=lfsattribute. This allows the commands to operate efficiently in very large repositories where users may have performed only a partial clone or a sparse checkout, since we don't have to iterate through all the files in the repository. It does mean, however, that we depend on the Git configuration and the settings in the.gitattributesfiles to correctly identify all relevant Git LFS files.I suppose one avenue which could be explored would be to run the
git ls-filescommand twice whencore.ignoreCasewas set tofalse(as it typically is on a Linux system, for instance). Running it a second time withcore.ignoreCaseexplicitly and temporarily set totrueand then comparing the results with the first run would identify any files which might have unintentionally been skipped due to case-sensitivity differences between systems; these could then be listed in a warning message. If someone wants to draft a PR to implement this design (or another one) as an experiment, we'd of course take a look at it. We would want to be cautious, though, with regard to any potential performance penalty in a large repository.star-ga commentedon Jan 10, 2026
Can confirm. This one keeps biting us too —
.PNG/.TGAcommitted on Windows/macOS, then Linux doesn’t match*.png/*.tgaand you get pointer files.I can write a docs PR with the bracket glob workaround (
*.[pP][nN][gG], etc). OK?chrisd8088 commentedon Jan 12, 2026
That would be much appreciated, thank you!
chrisd8088 commentedon Jan 12, 2026
Regarding my other suggestions, it occurs to me that instead of trying to detect possible oversights due to case-sensitivity issues in the
git lfs checkoutorgit lfs pullcommands, thegit lfs fsckcommand might be better suited to that activity, since users have no particular performance expectations of that command, and do expect it to make a thorough analysis of the current repository state.As well, if a
-i/--ignore-caseoption were added to thegit lfs trackcommand, when that option was not provided and the user did not explicitly provide a mixed-case pattern, and the command was running on a case-insensitive filesystem, the command could output a message suggesting the use of the--ignore-optionoption if the user intended to share their repository with developers on case-sensitive filesystems.(Note that to detect a case-insensitive filesystem, I would recommend not simply checking the operating system type, since both APFS and NTFS can be configured in case-sensitive modes. Instead, we would want to perform a check similar to the one Git implements, where we would try to open the
.git/configfile as.git/CoNfIGand assume case-insensitivity if that succeeds.)star-ga commentedon Jan 16, 2026
Building on the workarounds already discussed in this thread (e.g., adding duplicate uppercase patterns like
*.PNGalongside*.png) and broader community practice, the core issue is that.gitattributespattern matching is case-sensitive (consistent with Git’s index semantics). On macOS/Windows, users commonly commit assets with uppercase extensions (.PNG,.TGA, etc.), but on case-sensitive filesystems (Linux CI/build servers), patterns like*.pngwon't match.PNG, so Git LFS may leave pointer files instead of smudging content (e.g., Unity pipelines break).While duplicating patterns can work, bracket-glob patterns (e.g.,
*.[pP][nN][gG]) are more robust and portable: they are pure Git-supported syntax, avoid duplication, and behave consistently across all filesystems.For example:
Current (breaks on Linux when files are
.PNG):Bracket-glob (works everywhere):
This could be addressed incrementally without changing existing behavior or adding overhead to hot paths (
pull/checkout):*.[pP][nN][gG],*.[tT][gG][aA], with a short cross-platform note in git-lfs-faq(7) under Tracking and Migration.git lfs track --ignore-caseconvenience option:git lfs track --ignore-case "*.png"writes the bracket glob in.gitattributes(pure Git syntax; no new LFS matching logic; consider for a major version to avoid compatibility issues).git lfs fsck: opt-in checks suitable for CI such as case-collisions and “risky patterns” (*.pngbut.PNGpresent), with flags like--jsonand--strict.core.ignoreCasedetection) rather than OS guessing — resolve the gitdir, then test opening the config file with alternate case.Happy to submit a small docs PR first to git-lfs-faq(7) (addresses part of #6187) if that helps move things forward.
chrisd8088 commentedon Jan 17, 2026
Thanks for summarizing the discussion in this issue, @cputer. I think you've identified all the salient points and ideas!
doctorpangloss commentedon Jan 19, 2026
okay, i think another POV is, if i write
git clonewith a default lfs configuration / a valid one from the POV of git lfs, and it succeeds with error code 0, but my directory is populated with pointer files that also do not get checked out withgit lfs pull(or whatever), i hate to use chatgpt turns of phrase here haha, but: that's a serious bug. it's not a small bug or a documentation fix.the reason i ticketed this is because it affects backups. the whole point of git is that git clone does what it says it does or fails. this is a really bad failure. it has to be fixed in git too, or git lfs has to become part of git, or i don't know - there has to be a configuration that will raise an error in the scenario i reported, and it probably has to be the default configuration
chrisd8088 commentedon Jan 20, 2026
I understand your concerns, @doctorpangloss, even if I don't fully share them.
We've described already some of the historical and contemporary limitations under which the Git LFS project operates, so I won't repeat them again here.
I would urge you to contact the Git mailing list to discuss how Git applies its attribute patterns in case-insensitive filesystems, since that's at the root of this issue. That would also be the right place to open a discussion as to whether Git would ever absorb the Git LFS project, but I suspect you'll be advised that the Git project is not interested in doing so.
A plain
git clonecommand is certainly a reasonable way to download a regular Git repository's history and contents for backup purposes. However, it's important to note that agit cloneoperation effectively performs bothgit fetchandgit checkoutoperations, and while the former may download the full history of a repository, the latter creates a working tree from a single commit on a single branch. Because Git LFS is invoked during the checkout phase, not the fetch phase, it can only download the objects for that single commit.To make a backup copy of a repository and all of the related Git LFS objects, you need to run a command like
git lfs fetch --allafter cloning. This is especially true if you've made a bare clone withgit clone --bare, since then no checkout phase is performed at all.The
git lfs fetch --allcommand will retrieve Git LFS objects for pointers even if those pointers don't match anyfilter=lfsGit attribute patterns, unlikegit lfs pullandgit lfs checkout. Therefore Git LFS objects for files which don't match any patterns due to case-sensitivity differences between filesystems will still be fetched. We can illustrate this difference:Details
If you then include the
.git/lfs/objectsdirectories in your archive of the repository, you would have a backup which contains all the Git LFS objects as well as the Git contents.Now if you wanted a
git cloneoperation to automatically download all related Git LFS objects, you'd need to advocate for Git to introduce a post-fetch hook (a topic which has come up in the discussion of PR #6171), so that Git LFS could be invoked even after a bare clone and would always know the full history of everything that had just been fetched.However, not all users would view such an automatic download of Git LFS objects as a feature. For a repository with many large Git LFS objects throughout its history, a
git cloneoperation may still be fast and cheap, but fetching all the related Git LFS objects, including those which are no longer referenced by any pointers in the tips of any branches, could be slow and expensive.The conversation in #5969 also touched on some of these points, in case that's helpful.
Merge pull request #6192 from star-ga/docs/bracket-glob-case-safety-6187