Systems


How a Stuck Git Branch Taught Me About Git Garbage Collection


June 30, 2025




The issue

My local master branch wasn’t integrating the latest updates, even though git pull --rebase completed without errors. Even when I updated master on GitHub by merging a PR, my local branch would download the changes but not actually reflect them in the branch history.



Initial attempts

I tried the following commands to force synchronization:

but the problem remained: the pull fetched changes, yet my local branch didn’t show them.



The clue

Then I tried pulling using the GitHub desktop app ...and it failed too. But al least this time I got an error message: error: cannot lock ref 'refs/remotes/origin/BRANCH-1016': is at ...


A quick search led me to this Stack Overflow thread, which explained that the error was likely due to a corrupted or stale remote reference.


The suggested fix involved these three commands:

and after running those, everything worked. My local master finally included the commit I had merged earlier, confirmed by grepping the name of the last commit I saw on remote master: git log --grep="Error toast and dialog keys" --oneline.




But... what actually did I just do?

Even though the solution worked, I realized I didn’t fully understand what I had just done, especially what these Git commands really do. After a quick search into the Git official guide for git prune I had more questions then answers. The definition for the command is: "git-prune - Prune all unreachable objects from the object database"



Cool, thank you official guide.


Now... what's the 'object database' in Git?




The Git object database

It’s a folder inside your .git directory, specifically at .git/objects/. This is where Git stores all the internal objects of your repository: commits, file contents (blobs), directory structures (trees), and more. Each object is identified by a SHA-1 (or SHA-256) hash.


git-object-database


When you prune you will remove every unreachable reference that still lives in this database, and here the things gets a bit more complex, because...




...git prune is dangerous!

As we said git prune is risky because it immediately and permanently, and if used without caution, deletes all unreachable objects that not only are present in your Git object database, but also those that are not protected from the reflog. These include commits and other data that aren’t referenced by any tag, branch, or even reflog.


This is the reason whey you will see many guides online recommending not using git prune on its own, unless you’re absolutely sure you don’t need anything that might still be floating around as unreachable data.


Instead, it’s safer to use git gc, which calls git prune internally but in a more controlled way.



What does git gc do that makes it safer?

git gc stands for 'garbage collection', removes the unreachable objects like git prune with the difference that it does it by respecting the reflog retention period, typically 90 days for recent history and 30 days for orphaned data.


Basically, the reflog is your safety net in Git. It keeps a log of changes to references like HEAD, so you can go back to previous states, even after a reset hard, rebase, or branch deletion.



git-reflog


Ok, let's not play with git prune, but when to use it, then?


Almost never! Unless:


And since git gc runs prune as part of its process, it’s almost always better to just run git gc.



And when should you use git gc?

Use it regularly for safe cleanup and maintenance:


So what about git remote prune origin?

Despite the name, git remote prune origin also works entirely locally. It doesn't touch the remote (origin), and it doesn’t delete any branches on GitHub or other remotes. What it actually does is clean up your local references to remote branches that no longer exist. These are stored under .git/refs/remotes/origin/


If someone deletes a branch on GitHub, it might still appear in your local copy as origin/feature-xyz, so this command removes it.


In my case, the pull was failing because one of these references was corrupted or stale. Pruning it locally resolved the issue.



In summary:

Git keeps your objects for:

but it will remove them immediately if you use: