Git Across WSL and Windows: Line Endings, File Modes, and Repository Fidelity
Keep Git history stable across Windows and WSL by defining line endings, testing executable bits, and assigning each checkout a clear filesystem owner.
Using Git from both Windows and WSL can be convenient, but a repository has more than file contents. Git records normalized content, executable mode for supported files, symlink state, case-sensitive path names, and attributes that affect checkout and check-in. Windows and Linux filesystems present different defaults for line endings and metadata. If two Git installations operate on the same working tree, they can produce status changes or tool behavior that looks like a source-code edit even when the intended change was only an environment transition.
Choose an owner for each checkout. If Linux compilers and tests are primary, keep the repository in the WSL Linux filesystem and use Linux Git. If Windows tools own the project, use a Windows checkout and Windows Git. Sharing a checkout across /mnt/c is possible, but it needs explicit repository attributes, per-filesystem tests, and a policy about which side edits the working tree.
Distinguish committed content from working-tree representation
Git stores a repository representation in its index and object database. Checkout configuration and attributes can transform text files into working-tree form. The core.autocrlf, core.eol, and .gitattributes settings influence line-ending conversion. Changing one user’s global setting can make a clean checkout appear modified or can introduce a repository-wide line-ending diff if the project’s policy is not explicit.
Start with a read-only inventory from the Git executable that owns the checkout:
git --version
git rev-parse --show-toplevel
git config --show-origin --get core.autocrlf || true
git config --show-origin --get core.eol || true
git config --show-origin --get core.filemode || true
git status --short
The –show-origin output matters because repository, user, system, and environment settings can all affect behavior. Do not change global configuration to fix one repository without understanding the effect on other checkouts.
Put normalization policy in repository attributes
For teams with mixed Windows and Linux contributors, a committed .gitattributes policy is generally more reproducible than relying on each user’s global conversion settings. A baseline can mark text files for normalization while specifying important platform-specific exceptions:
* text=auto
*.sh text eol=lf
*.bash text eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
This is a starting point, not a universal policy for every repository. Review generated files, fixtures, shell scripts, batch files, and binary assets. Explicitly classify binary formats where Git should never perform text conversion. The eol attribute only has its intended effect when text conversion is enabled. Use git check-attr –all – path/to/file to inspect the rule applied to a path.
Avoid broad conversion of the entire repository without a plan. Before adding or changing attributes, inspect current object and working-tree line endings, run the repository’s test suite, and review the diff by file. If a normalization commit is necessary, keep it isolated from behavior changes so review tools can distinguish content normalization from logic changes.
Treat executable mode as filesystem-dependent metadata
Git can represent the executable bit for tracked files, but the working filesystem and Git’s core.filemode setting determine whether chmod changes are detected. A Linux filesystem normally supports executable mode as expected. A Windows-mounted DrvFs tree may expose mode according to mount options and metadata configuration. Do not assume that chmod +x will always create a stable tracked mode change from every mount.
Inspect the index mode and the working tree separately:
git ls-files --stage -- path/to/script.sh
stat -c '%A %a %n' path/to/script.sh
git diff --summary
git config --show-origin --get core.filemode || true
The index reports the tracked mode, such as regular non-executable or executable. If an intended script mode change is not recognized, first determine whether the repository is on Linux ext4 or DrvFs and whether the mounted drive has metadata enabled. For a deliberate repository change, use Git’s supported index operation and review its result:
git update-index --chmod=+x path/to/script.sh
git diff --cached --summary
Do not set core.filemode=false as a blanket cure without documenting that mode changes will be ignored. Do not recursively chmod a tree to make Git status clean. That can alter meaningful permissions and hide the original distinction between filesystem presentation and intended repository metadata.
Case sensitivity and symlinks remain separate concerns
Windows filesystems often default to case-insensitive path lookup, while Linux filesystems are case-sensitive. A repository containing both Config.yml and config.yml can behave differently depending on the checkout filesystem and application. Git’s case-sensitive index does not guarantee that every checkout can represent colliding names. Detect such paths before deciding that one platform’s behavior is authoritative.
Symlink support also varies with Windows settings and filesystem location. A repository’s symlink entry is not equivalent to a text file containing a target path. Test creation and checkout behavior on each supported side. Keep symlink-specific workflows in the Linux filesystem when Linux is the target, or document a Windows-side configuration that supports the repository’s required links.
These issues are related but not solved by line-ending attributes. Run focused checks for path case collisions, symlink checkout, executable scripts, and Git status after switching environments. A clean git status from one side does not prove that the other side can faithfully materialize every path.
Avoid simultaneous writers to one working tree
Do not run two Git processes or editors that rewrite the same files concurrently from Windows and WSL. Even if one Git operation succeeds, concurrent checkout, branch switch, package install, or generated-file writes can leave the index and working tree inconsistent. Use one active Git owner at a time, and close editor tasks before a branch or checkout operation performed by the other side.
A safer integration model is a Linux-owned working tree opened through a WSL-aware editor, or separate Windows and Linux worktrees that exchange commits through the repository. Separate worktrees cost more disk space but make each filesystem and runtime explicit. For build tools that generate many files, separate worktrees also prevent platform-specific build output from contaminating the other environment.
Keep cache directories and dependencies outside shared source where possible. Add environment-specific outputs to .gitignore, but review ignore rules so generated artifacts do not hide source files or fixtures that should be tracked. Always inspect git status before staging.
Recover from a line-ending or mode surprise
If a large diff appears after switching shells, stop and inspect before staging. Check whether a formatter, editor, Git conversion setting, or generated build touched the files. Use git diff –ignore-space-at-eol as a diagnostic comparison, not as proof that a change is safe to discard. Check attributes, global config origins, index mode, and working-tree filesystem.
Do not use a hard reset or blanket checkout to recover a mixed tree if there may be user changes. Preserve a patch or copy of the affected files, then isolate the mechanical conversion and inspect the intended content diff. If only mode bits changed, review the staged summary and restore only specific paths after verifying their tracked mode.
For an ongoing project, capture a known-clean baseline on its owning filesystem and compare status after each cross-boundary change. Add a lightweight CI check for whitespace errors and repository metadata where practical. A test that runs only in Windows cannot prove that the Linux checkout retains executable scripts or case-sensitive paths.
Acceptance checks for a shared development workflow
Accept the workflow when .gitattributes defines the repository’s text policy, both Git installations see the intended repository root, and a clean checkout remains clean after switching only in the supported direction. Verify that shell scripts remain executable, required symlinks work, case-sensitive paths do not collide, and a no-op build produces no unintended changes.
If one checkout must be shared across Windows and WSL, run these checks on the actual drive mount and WSL configuration used by the team. Record the designated Git owner, permitted editor access, core.filemode policy, and normalization rules. If repository fidelity cannot be guaranteed on the shared mount, move the Linux-owned checkout to ext4 and open it through the supported Windows integration path.
Related:
- DrvFs Metadata and Case Sensitivity: When Windows Files Behave Like Linux Files
- WSL, Dev Drive, and Filesystem Placement: Choosing Performance Without Losing Interop
Sources: