Photo by Rahul Mishra on Unsplash
So there I was, trying to push a feature branch at the end of a long Friday, and Git just... refused. Threw this at me:
error: cannot lock ref 'refs/remotes/origin/feature/my-branch':
unable to resolve reference 'refs/remotes/origin/feature/my-branch':
reference broken
error: failed to push some refs to 'https://github.com/yourrepo.git'
I've seen this git error cannot lock ref pop up more times than I can count — on fresh dev machines, on CI pipelines, even on repos that were working perfectly fine the day before. It's one of those errors that looks scarier than it actually is, but it can absolutely block your workflow if you don't know what you're dealing with.
Here's the thing though — the root cause is almost always one of a few things. Either your local refs are out of sync with the remote, you've got a corrupted or stale lock file sitting around, or a branch was force-deleted on the remote and your local Git still has an old reference pointing at nothing. Git is very particular about reference integrity, and when something doesn't line up, it just refuses to move forward.
Why This Actually Happens
Git tracks remote branches using files inside .git/refs/remotes/. When you fetch or pull, it updates those files. But sometimes — especially if a fetch got interrupted, someone deleted a remote branch while you were mid-operation, or you've got multiple Git processes running at once (looking at you, IDEs that run background fetches) — those reference files get left in a broken or locked state.
The "lock" part specifically means Git is trying to write to a ref file but found a .lock file already sitting there from a previous operation that didn't clean up after itself. It's a safety mechanism, honestly a good one, but super annoying when it triggers unnecessarily.
Fix 1: Run git fetch with prune (do this first)
Nine times out of ten, this is all you need. The --prune flag tells Git to delete any local remote-tracking references that no longer exist on the remote. Dead refs, gone. Stale pointers, cleaned up.
git fetch --prune origin
If you want to make this automatic going forward so you never have to think about it again, you can set it globally:
git config --global fetch.prune true
Honestly, I don't know why this isn't the default. It should be. After running the prune, try your push again and there's a good chance everything just works.
Fix 2: Manually delete the stale lock file
If pruning didn't solve it, you've probably got a leftover .lock file. This happens a lot when a Git operation gets killed mid-run — like if your laptop died, you hit Ctrl+C at the wrong moment, or your terminal crashed during a fetch.
First, find the lock file. Based on the error message, you'll know which ref is broken. For our example above, you'd look here:
ls .git/refs/remotes/origin/feature/
You're looking for any file ending in .lock. Once you spot it, just delete it:
rm .git/refs/remotes/origin/feature/my-branch.lock
On Windows with Git Bash, same command works. If you're not comfortable navigating to that path, you can also just search the whole .git folder for lock files:
find .git -name "*.lock"
Delete whatever shows up (assuming no Git operations are actively running — don't do this while a fetch is in progress). Then try again.
Fix 3: Run git fsck and then pack-refs
If you're still stuck, it's time to do a little repo health check. The git fsck command checks the integrity of your Git object database and will surface any broken references explicitly:
git fsck --full
This might spit out a bunch of output. Look for anything that says "broken link" or "missing object" — those are your culprits. Now here's where it gets a bit more hands-on. Run this to compact and clean up your refs:
git pack-refs --all --prune
What this does is take all those loose ref files under .git/refs/ and pack them into a single file called packed-refs. It's essentially a housekeeping operation, and it often resolves lingering corruption that's causing the lock ref error. After this, do another git fetch --prune and you should be good.
(Side note: if you're running this on a really old repo that's been through multiple team members, CI systems, and probably a few botched merges — don't be surprised if git fsck shows a ton of warnings. Most of them are harmless. Focus on the "broken" ones.)
Fallback: Nuke the remote tracking refs and re-fetch
Alright, if none of the above worked, here's the nuclear option. You're just going to wipe out all your remote tracking references and let Git rebuild them fresh from the remote. You're not deleting any of your local branches or commits — just the remote-tracking pointers in .git/refs/remotes/.
rm -rf .git/refs/remotes/origin/
git fetch origin
I've had to do this exactly twice in my career — once on a repo that had been force-pushed to about forty times by a team that really didn't understand branching, and once after a botched migration between hosting providers. Both times it fixed everything immediately. It's not elegant, but it works.
Quick prevention tip
Set up automatic pruning on fetch and always make sure your team has a clear policy around deleting remote branches after merging PRs. The "cannot lock ref" error almost always traces back to deleted remote branches that local machines still remember. Keep things tidy on the remote side and you'll see this error a whole lot less.
Hope this saves you from a frustrating afternoon — and maybe a few choice words directed at your terminal.
๋๊ธ
๋๊ธ ์ฐ๊ธฐ