This error occurs when Git cannot delete or replace a file during operations like checkout, pull, or merge because the file is locked by another process. On Windows, this commonly happens when an IDE, editor, antivirus, or another program has the file open. The fix involves identifying and closing the process holding the file lock.
The "unable to unlink old" error with "Permission denied" indicates that Git is trying to replace or delete a file but the operating system is refusing the operation. The word "unlink" is the Unix term for deleting a file's directory entry - essentially removing the file. This error is particularly common on Windows because of how Windows handles file locking. Unlike Unix-based systems where files can often be deleted while open, Windows enforces mandatory file locking. When any program opens a file, Windows may prevent other programs from modifying or deleting it until the handle is released. **What triggers this error:** - Git needs to update a file during `git checkout`, `git pull`, `git merge`, `git rebase`, or `git reset` - The file exists in your working directory and needs to be replaced with a different version - Another process has the file open, holding a lock that prevents deletion This is a system-level file access conflict, not a Git-specific problem. Git is simply reporting that the OS refused its request to remove the old file before writing the new version.
First, find out what program is preventing Git from accessing the file. There are several ways to do this on Windows:
Using Process Explorer (recommended):
1. Download Process Explorer from Microsoft Sysinternals: https://docs.microsoft.com/sysinternals/downloads/process-explorer
2. Run Process Explorer as Administrator
3. Press Ctrl+F to open "Find Handle or DLL"
4. Enter the filename (e.g., "file.txt")
5. Click "Search" - it will show which process has the file open
Using Handle utility (command-line):
# Download Handle from Sysinternals and run:
handle.exe file.txtUsing PowerShell (Windows 10/11):
# Find processes with matching file handles
Get-Process | ForEach-Object {
$process = $_
try {
$_.Modules | Where-Object { $_.FileName -like "*file.txt*" } |
ForEach-Object { Write-Host "$($process.Name) [$($process.Id)]" }
} catch {}
}Using Resource Monitor:
1. Press Win+R, type resmon, press Enter
2. Go to the "CPU" tab
3. Expand "Associated Handles" section
4. In the search box, type the filename
5. See which processes have handles to that file
Once you've identified the culprit, close that application or release the file lock:
Common culprits and solutions:
IDE/Editor (VS Code, IntelliJ, etc.):
- Close the specific file in the editor
- Or close the entire project/folder
- Some IDEs have a "Reload from Disk" feature that releases handles
Windows Explorer Preview Pane:
- Click on a different file
- Or disable the preview pane: Alt+P in Explorer
- The preview pane commonly locks PDF, image, and document files
Antivirus Software:
- Add your repository folder to the antivirus exclusions list
- This is especially important for Windows Defender real-time protection
Build/Test processes:
- Stop any running builds or test suites
- Check for background watchers (npm run watch, webpack --watch, etc.)
- Kill orphaned node.exe, java.exe, or python.exe processes
After closing the application, retry your Git command.
Git Bash handles file operations differently than Windows Command Prompt and can sometimes work around locking issues:
# In Git Bash, navigate to your repo
cd /c/path/to/your/repo
# Retry the operation
git checkout branch-name
# or
git pull origin mainGit Bash uses MSYS2's file handling which can be more forgiving with file locks in some situations.
Why this can help:
- Git Bash may handle file path translations differently
- Some Windows-specific locking behavior is avoided
- The shell environment is more Unix-like in its file handling
If you can't close the application holding the file, you can forcibly release the handle:
Using Handle from Sysinternals:
# First, find the handle (run as Administrator)
handle.exe file.txt
# Output shows something like:
# explorer.exe pid: 1234 type: File ABC: C:\path\to\file.txt
# Close the handle (use the hex handle value shown)
handle.exe -c ABC -p 1234Warning: Force-closing handles can cause data loss or application crashes in the program that had the file open. Save your work in that application first if possible.
Using Process Explorer:
1. Find the process in the list
2. Click on it, then press Ctrl+H to show handles
3. Find the file handle in the lower pane
4. Right-click and select "Close Handle"
If nothing else works, a reboot will release all file locks:
# Save your work, then reboot
shutdown /r /t 0After rebooting:
1. Open Git Bash or Command Prompt immediately (before any IDE)
2. Navigate to your repository
3. Run the Git command that was failing
4. Only then open your IDE or other applications
This works because the reboot clears all file handles. The key is to run Git before anything else opens the files again.
For persistent issues:
If the same files are always locked after reboot, check your startup programs:
- Press Win+R, type shell:startup
- Remove or disable startup items that might open those files
- Check Windows Services for background processes
Some Windows features aggressively lock files. Consider disabling them for your development folders:
Disable Windows Search Indexer for your repo:
1. Right-click the repository folder
2. Click "Properties"
3. Click "Advanced..."
4. Uncheck "Allow files in this folder to have contents indexed"
5. Apply to all subfolders
Disable Windows Defender real-time scanning (temporarily):
# In elevated PowerShell
Set-MpPreference -DisableRealtimeMonitoring $true
# Remember to re-enable after your Git operation
Set-MpPreference -DisableRealtimeMonitoring $falseOr add exclusions (preferred):
1. Open Windows Security
2. Go to Virus & threat protection > Manage settings
3. Under Exclusions, click "Add or remove exclusions"
4. Add your repository folders and .git directories
Disable Superfetch/SysMain (extreme):
net stop SysMainThis is rarely necessary but can help if Windows prefetch is causing issues.
Sometimes the error really is about file permissions, not locks:
Check file ownership:
# View current permissions
icacls "path\to\file.txt"
# Take ownership
takeown /f "path\to\file.txt"
# Grant full control to your user
icacls "path\to\file.txt" /grant "%USERNAME%":FFor the entire repository:
# Take ownership of entire repo (run as Administrator)
takeown /f "C:\path\to\repo" /r
# Reset permissions
icacls "C:\path\to\repo" /reset /tCommon permission scenarios:
- Cloning a repo as Administrator but running Git as regular user
- Copying a repo from another machine or external drive
- Repository on a network drive with different permissions
- Files extracted from a zip/archive with restrictive permissions
On Windows, very long file paths can cause permission-like errors. Enable long path support:
# Enable long paths in Git
git config --global core.longpaths trueEnable in Windows (Windows 10 1607+):
1. Press Win+R, type gpedit.msc
2. Navigate to: Computer Configuration > Administrative Templates > System > Filesystem
3. Enable "Enable Win32 long paths"
Or via Registry:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem]
"LongPathsEnabled"=dword:00000001This is especially relevant for node_modules, deeply nested project structures, or projects cloned from Unix systems with long paths.
Understanding Windows File Locking:
Windows uses mandatory file locking by default, unlike Unix systems which use advisory locking. When a process opens a file on Windows with certain sharing modes, other processes may be completely blocked from modifying or deleting that file.
The sharing mode flags determine what operations other processes can perform:
- FILE_SHARE_READ - Others can read
- FILE_SHARE_WRITE - Others can write
- FILE_SHARE_DELETE - Others can delete or rename
Many Windows applications open files without FILE_SHARE_DELETE, preventing Git from unlinking them.
Why This Is Worse on Windows:
On Unix-like systems (Linux, macOS):
- Files can be "unlinked" (removed from directory) while still open
- The file data persists until all handles are closed
- Git operations rarely encounter this issue
On Windows:
- Files cannot be deleted while any handle is open (unless FILE_SHARE_DELETE was specified)
- Even reading a file can block deletion depending on the sharing mode
- This is a fundamental design difference in the operating systems
Git's Core.Filemode Setting:
On Windows, Git often sets core.filemode to false because Windows doesn't support Unix file permissions. This can sometimes interact with "Permission denied" errors:
# Check current setting
git config --get core.filemode
# Explicitly set it (usually automatic on Windows)
git config core.filemode falseWSL as an Alternative:
If you frequently encounter these issues, consider using Windows Subsystem for Linux (WSL) for Git operations:
# Install WSL
wsl --install
# Then use Git from within WSL
wsl
cd /mnt/c/path/to/repo
git pullWSL has Unix-style file handling and doesn't suffer from Windows locking issues (though you may encounter line ending differences).
OneDrive and Cloud Sync Issues:
If your repository is in a OneDrive-synced folder, you may experience:
- Files locked by OneDrive sync processes
- "Files On-Demand" feature causing access issues
- Conflicts between Git and cloud sync
Solutions:
- Move repositories outside of cloud-synced folders
- Exclude .git folder from cloud sync
- Disable "Files On-Demand" for development folders
Antivirus Exclusions:
For development work, consider adding these exclusions to your antivirus:
- Your source code directories
- .git folders
- node_modules, vendor, target, and other dependency folders
- Build output directories
- Your IDE's cache directories
This significantly reduces file locking conflicts and improves performance.
Git for Windows Configuration:
Git for Windows has some specific settings that can help:
# Use Windows credential manager
git config --global credential.helper manager
# Handle line endings
git config --global core.autocrlf true
# Handle long paths
git config --global core.longpaths true
# Improve performance on Windows
git config --global core.fscache true
git config --global core.preloadindex truekex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server
fatal: unable to access: Proxy auto-configuration failed
How to fix 'Proxy auto-configuration failed' in Git
fatal: unable to access: Authentication failed (proxy requires basic auth)
How to fix 'Authentication failed (proxy requires basic auth)' in Git
fatal: unable to access: no_proxy configuration not working
How to fix 'no_proxy configuration not working' in Git
fatal: unable to read tree object in treeless clone
How to fix 'unable to read tree object in treeless clone' in Git