Stop Git showing file permission changes as modifications
Git says files changed but only the mode differs — how do I ignore that?
Short answer
Git tracks the executable bit, so a file going from 100644 to 100755 counts as a change. Set core.fileMode false to ignore mode differences, or fix the bit properly with git update-index --chmod when it genuinely matters.
git config core.fileMode false
Tell this repository to ignore executable-bit differences
Does this match your situation?
- git diff shows 'old mode 100644 / new mode 100755' and nothing else.
- Every file looks modified after copying the repo or using WSL.
- A shell script isn't executable after cloning on Linux.
- Working across Windows and a Unix container churns file modes.
Step-by-step fix
Confirm the mode is the only difference
A diff showing only mode lines means the content is byte-identical and Git is reporting a permissions change.
step 1git diff # diff --git a/build.sh b/build.sh # old mode 100644 # new mode 100755Ignore mode changes in this repository
The right setting when the churn comes from your environment — a Windows filesystem, a mounted volume, or WSL — rather than from a real intent to change permissions.
step 2git config core.fileMode false git status # the phantom changes are goneKeep it local to the repository. Setting it globally hides genuine permission changes in projects where they matter.
Discard mode changes already staged
Turning the setting off stops new noise but doesn't clear what's already recorded. Reset those entries.
step 3git config core.fileMode false git reset --hard HEAD git statusSet the executable bit deliberately
When a script genuinely needs to be executable, record it in the index directly — this works even from Windows, where the filesystem has no such bit.
step 4git update-index --chmod=+x scripts/build.sh git commit -m "Make build script executable" # on Linux/macOS the normal way also works: chmod +x scripts/build.sh git add scripts/build.shCheck what mode Git has recorded
The index is authoritative. 100755 is executable, 100644 is not.
step 5git ls-files -s scripts/build.sh
Why this works
Git records a simplified mode for each file — 100644 for regular, 100755 for executable — because the executable bit genuinely matters for scripts and hooks. Windows has no such bit, and mounted or copied filesystems often report it inconsistently, so Git sees a change that reflects nothing you did. core.fileMode false tells Git to keep the mode already in the index and stop comparing, which is why it eliminates the noise without altering what's committed.
How to stop it happening again
- Set core.fileMode false in repositories you use across Windows and WSL or containers.
- Use git update-index --chmod=+x to set the bit deliberately, so it's committed once and correct everywhere.
- Avoid copying repositories between filesystems that handle permissions differently.
Commands used in this guide
git configRead and write Git settings for one repo, your user, or the whole machine.
git update-indexManipulate the index directly - including telling Git to ignore changes to a tracked file.
git ls-filesList files in the index, with precise filters for state.
git diffShow exactly what changed - between your files, the index, commits or branches.
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
What does 'old mode 100644 new mode 100755' mean?
The file's executable bit changed. 100644 is a regular file and 100755 is executable. Git tracks this because it matters for scripts, so it reports the change even though the content is identical.
How do I make Git ignore file permission changes?
git config core.fileMode false in that repository. Git then keeps whatever mode is already in the index and stops comparing against the filesystem, which removes the phantom modifications.
How do I make a script executable from Windows?
git update-index --chmod=+x path/to/script.sh, then commit. This writes the executable bit into the index directly, so it's correct for everyone who clones even though Windows has no such filesystem bit.
Related rescue guides
git status says every file changed but I didn't touch them — how do I fix line endings?
Windows writes CRLF and Unix writes LF, and without a policy Git records whichever your editor produced. Commit a .gitattributes with `* text=auto`, then renormalise the repository once — this fixes it for everyone, unlike the per-machine core.autocrlf setting.
Read the fix →Always fixableI renamed a file's case and Git won't notice — how do I fix it?
Windows and macOS filesystems treat Player.cs and player.cs as the same file, so a case-only rename is invisible to Git. Use git mv -f to force it, or rename via a temporary name, then commit — otherwise the wrong case ships to your Linux CI and the build breaks.
Read the fix →Always fixableHow do I keep my own version of a tracked file without Git nagging me?
git update-index --skip-worktree <file> makes Git ignore your local modifications to a tracked file. The cleaner long-term answer is to commit an example file, ignore the real one, and have everyone copy it.
Read the fix →Always fixableWhy is .gitignore not working for files that are already tracked?
.gitignore only applies to untracked files. Once a file has been committed, Git keeps tracking it no matter what the ignore rules say. Remove it from the index with git rm --cached <file> — that stops tracking while leaving the file on your disk.
Read the fix →