Fix Git Permission Drift (Windows + WSL)

The Clean Way to Stop “mode 100755 → 100644” Permission Drift in Git When Sharing Repos Between Windows and WSL on NTFS

Windows WSL Git Permissions Shared NTFS EOL LF CRLF

Seeing old mode 100755 → new mode 100644 and CRLF/LF warnings when working on Git repos from both Windows PowerShell and WSL via a shared NTFS drive? This guide explains the root cause and gives a one-click PowerShell fix script, recommended global Git settings, .gitattributes, and WSL mount options to permanently stop permission drift—without breaking anything.


Why this matters (1-minute story)

You switch between Windows and WSL on the same repo and Git starts showing permission diffs you never intended. Reviews get noisy; CI fails on scripts that “lost” execute bits. The fix is straightforward: make Windows ignore Unix permission changes, help WSL remember them on NTFS, and normalize line endings. Copy, paste, done.


TL;DR


Prerequisites


Step 1 — One-click PowerShell (global defaults + auto-fix all repos under a root)

Save as fix-git-permission-drift.ps1 and run in PowerShell. It sets global Git defaults and then finds every Git repo under a chosen root, fixing each automatically.

Code Language
 1# --- fix-git-permission-drift.ps1 ---
 2# Purpose: Stop Git permission-bit drift on shared NTFS (Windows + WSL)
 3
 4param(
 5  [Parameter(Mandatory=$false)]
 6  [string]$Root = "D:\Code"   # ← change this to your repos root folder
 7)
 8
 9# 1) Global Git defaults (affects future clones)
10git config --global core.fileMode false       # ignore Unix exec-bit changes on Windows
11git config --global core.autocrlf input       # keep LF in repo; avoid CRLF churn
12git config --global core.symlinks false       # consistent behavior on NTFS
13
14Write-Host "`n[OK] Global Git config set." -ForegroundColor Green
15Write-Host "[i] Scanning for Git repos under: $Root`n" -ForegroundColor Cyan
16
17# 2) Find all .git folders under $Root (non-recursive .git detection)
18$gitDirs = Get-ChildItem -Path $Root -Recurse -Force -Directory -ErrorAction SilentlyContinue `
19           | Where-Object { $_.Name -eq ".git" }
20
21if (-not $gitDirs) {
22  Write-Host "[!] No Git repositories found under $Root" -ForegroundColor Yellow
23  exit 0
24}
25
26foreach ($gitDir in $gitDirs) {
27  $repo = $gitDir.Parent.FullName
28  Write-Host "Fixing repo: $repo" -ForegroundColor Cyan
29  Set-Location $repo
30
31  # 3) Ignore permission changes in this repo
32  git config core.fileMode false
33
34  # 4) Ensure shell scripts are executable in index (avoid mode flip-flop)
35  #    Safe no-op if none exist
36  git update-index --chmod=+x *.sh 2>$null
37
38  # 5) If .gitattributes exists, renormalize line endings (optional but recommended)
39  if (Test-Path ".gitattributes") {
40    git add --renormalize . | Out-Null
41  }
42
43  # 6) Quick status for visibility
44  git status --short
45  Write-Host "[OK] Done: $repo" -ForegroundColor Green
46  Write-Host "------------------------------"
47}
48
49# 7) Return to original location
50Pop-Location 2>$null
51Write-Host "`nAll repositories processed. Permission drift should now be resolved ✅" -ForegroundColor Green

Run it:

Code Language
1Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
2.\fix-git-permission-drift.ps1 -Root "D:\Code"

Step 2 — Add a minimal .gitattributes (once per repo)

Keeps scripts on LF and prevents CRLF noise across platforms.

Code Language
 1# Keep Unix line endings for scripts
 2*.sh text eol=lf
 3
 4# Normalize common text files
 5*.js  text
 6*.ts  text
 7*.jsx text
 8*.tsx text
 9*.css text
10*.html text
11*.md  text
12
13# Never normalize binaries
14*.png binary
15*.jpg binary
16*.jpeg binary
17*.gif binary
18*.pdf binary
19*.zip binary

Then normalize the index (PowerShell or WSL):

Code Language
1git add --renormalize .
2git status

Step 3 — Help WSL remember Unix perms on NTFS (one-time)

Enables metadata so WSL can persist Unix permission bits on NTFS.

In WSL:

Code Language
1sudo sh -c 'printf "[automount]\noptions = \"metadata\"\n" > /etc/wsl.conf'

Restart WSL from Windows:

Code Language
1wsl --shutdown

Verify mount options (in WSL):

Code Language
1mount | grep -E "/mnt/|drvfs"

You should see metadata in the options for your NTFS mounts.


Step 4 — Verify

In both Windows PowerShell and WSL, inside any repo:

Code Language
1git config --show-origin core.fileMode   # should show false at repo level
2git status                               # should be clean (no mode flips)

If a script still shows mode changes:

Code Language
1git update-index --chmod=+x path/to/script.sh
2git commit -m "Normalize script execute bit"

Common pitfalls


Copy-paste checklist


Final word

With core.fileMode=false, a sane .gitattributes, and WSL metadata, cross-platform work on shared NTFS is quiet and predictable. Clean diffs, fewer surprises, smoother reviews.


Keywords for SEO

git permission drift, 100755 to 100644, windows wsl git, shared ntfs git, core.fileMode false, gitattributes eol lf, crlf warnings git, wsl.conf metadata, fix executable bit git windows, git mode change issue