What just happened
You ran git add, git commit, or git pull and got slapped with this:
fatal: Unable to create '/path/to/repo/.git/index.lock': File exists.
Another git process seems to be running in this repository, e.g.
an editor opened by 'git commit'. Please make sure all processes
are stopped then try again. If it still fails, a git process
may have crashed in this repository earlier: remove the file manually
and restart.
Git uses .git/index.lock as a mutex. At the start of any operation that modifies the index, Git creates this file. When the operation finishes cleanly, Git deletes it. If Git crashed mid-operation, got killed with Ctrl+C, or is still running in another terminal โ the file stays. Every subsequent Git command finds it and refuses to touch the index until it's gone.
Step 1: Check if another Git process is actually running
Don't delete anything yet. First confirm the lock is genuinely stale and not protecting a live operation.
On Linux / macOS
# Check for any git processes touching your repo
ps aux | grep git
Look for a git process whose working directory matches your repo. Also check your editor โ VS Code, Vim, and JetBrains IDEs all spawn background git processes for status bars, blame gutters, and diff panels. Any of these can be the culprit.
On Windows (PowerShell)
Get-Process | Where-Object { $_.Name -like "*git*" }
On Windows (Git Bash / WSL)
ps aux | grep git
# or
tasklist | grep git
Found a live Git process? Kill it or wait for it to finish, then retry your original command. Nothing running? Move on to removing the lock.
Step 2: Verify the lock file exists
ls -la .git/index.lock
You'll see output like this:
-rw-r--r-- 1 ubuntu ubuntu 0 May 8 02:14 .git/index.lock
Check the timestamp. A file sitting there from 20 minutes ago โ or from yesterday โ with no Git process running is a stale lock. Safe to delete.
Step 3: Remove the stale lock file
Linux / macOS
rm -f .git/index.lock
Windows (PowerShell)
Remove-Item .git\index.lock -Force
Windows (Git Bash)
rm -f .git/index.lock
The -f flag prevents an error if the file disappeared between your check and the delete โ harmless either way.
Step 4: Confirm Git is working again
# Read-only check โ no side effects
git status
Working tree output with no errors? You're good. Re-run whatever you were originally trying to do:
git add .
git commit -m "your message"
# or
git pull origin main
Edge case: multiple lock files
A hard crash can sometimes leave lock files scattered across other Git objects, not just the index. Still getting errors after removing index.lock? Sweep the whole .git directory:
# Find all .lock files in the .git directory
find .git -name "*.lock" -type f
Common ones you might find:
.git/index.lockโ the usual suspect, blocks index operations.git/refs/heads/main.lockโ blocks ref updates (left by a failed push or commit).git/HEAD.lockโ rare, blocks HEAD writes.git/packed-refs.lockโ blocks pack-ref operations
With no live Git process running, nuke them all at once:
find .git -name "*.lock" -type f -delete
Why this keeps happening (and how to stop it)
Editor plugins are the #1 culprit
VS Code's Git panel, JetBrains' version control window, Sublime Merge โ they all poll git status every few seconds in the background. One crash while refreshing and you've got a stale lock. Two quick fixes: close your editor before running Git from the terminal, or point the editor's Git integration at a separate worktree so it never touches your main .git directory.
Interrupted terminal sessions
Hitting Ctrl+C mid-commit, an SSH disconnect, or a container restart all kill Git without cleanup. If you work over SSH on a remote machine, run your Git commands inside tmux or screen โ your session survives a disconnect and Git finishes cleanly:
tmux new -s work
# run your git commands here
# reconnect later with: tmux attach -t work
CI/CD pipelines running in parallel
Two pipeline jobs hitting git fetch on the same cloned directory simultaneously will race. One wins, one throws this exact error. The fix is simple: give each job its own fresh clone. In GitHub Actions, this usually bites teams using shared self-hosted runners with a persistent workspace. Add a concurrency group to serialize runs on the same branch:
# .github/workflows/deploy.yml
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
Quick reference
- Check running processes:
ps aux | grep git - Find all lock files:
find .git -name "*.lock" - Remove index lock:
rm -f .git/index.lock - Remove all locks (no live process):
find .git -name "*.lock" -delete - Verify:
git status

