Tutorials

Git Prune

The git prune command is an internal housekeeping utility that cleans up unreachable or "orphaned" Git objects. Unreachable objects are those that are inaccessible by any refs. Any commit that cannot be accessed through a branch or tag is considered unreachable. git prune is generally not executed directly. Prune is considered a garbage collection command and is a child command of the git gc command.

Git Prune Overview

In order to understand the effects of git prune we need to simulate a scenario where a commit becomes unreachable. The following is a sequence of command line executions that will simulate this experience.

The preceding sequence of commands will create a new repository in a directory named git-prune-demo. One commit consisting of a new file hello.text is added to the repo with the basic content of "hello git prune". Next, we will create modify hello.txt and create a new commit from those modifications.

~/git-prune-demo $ echo "this is second line txt" >> hello.txt~/git-prune-demo $ cat hello.txthello git prunethis is second line txt~/git-prune-demo $ git commit -am "added another line to hello.txt"[master 5178bec] added another line to hello.txt1 file changed, 1 insertion(+)

We now have a 2 commit history in this demo repo. We can verify by using git log:

The git log output displays the 2 commits and corresponding commit messages about the edits made to hello.txt. The next step is for us to make one of the commits unreachable. We will do this by utilizing the git reset command. We reset the state of the repo to the first commit. the "added hello.txt" commit.

The demo repository is now in a state that contains a detached commit. The second commit we made with the message "added another line to hello.txt" is no longer displayed in the git log output and is now detached. It may appear as though we have lost or deleted the commit, but Git is very strict about not deleting history. We can confirm it is still available, but detached, by using git checkout to visit it directly:

You are in 'detached HEAD' state. You can look around, make experimentalchanges and commit them, and you can discard any commits you make in thisstate without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you maydo so (now or later) by using -b with the checkout command again. Example:

git checkout -b <new-branch-name>

HEAD is now at 5178bec... added another line to hello.txt~/git-prune-demo $ git logcommit 5178becc2ca965e1728554ce1cb8de2f2c2370b1Author: kevzettler <kevzettler@gmail.com>Date: Sun Sep 30 14:49:59 2018 -0700

When we check out the detached commit, Git is thoughtful enough to give us a detailed message explaining that we are in a detached state. If we examine the log here we can see that the "added another line to hello.txt" commit is now back in the log output! Now that we know the repository is in a good simulation state with a detached commit we can practice using git prune. First though, let us return to the master branch using git checkout

If you want to keep it by creating a new branch, this may be a good timeto do so with:

git branch <new-branch-name> 5178bec

Switched to branch 'master'

When returning to master via git checkout, Git is again thoughtful enough to let us know that we are leaving a detached commit behind. It's now time to prune the detached commit! Next, we will execute git prune but we must be sure to pass some options to it. --dry-run and --verbose will display output indicating what is set to be pruned but not actually prune it.

~/git-prune-demo $ git prune --dry-run --verbose

This command will most likely return empty output. Empty output implies that the prune will not actually delete anything. Why would this happen? Well, the commit is most likely not fully detached. Somewhere Git is still maintaining a reference to it. This is a prime example of why git prune is not to be used stand-alone outside of git gc. This is also a good example of how it is hard to fully lose data with Git.

Most likely Git is storing a reference to our detached commit in the reflog. We can investigate by running git reflog. You should see some output describing the sequence of actions we took to get here. For more info on git reflog visit the git reflog page. In addition to preserving history in the reflog, Git has internal expiration dates on when it will prune detached commits. Again, these are all implementation details that git gc handles and git prune should not be used standalone.

The above command will force expire all entries to the reflog that are older than now. This is a brutal and dangerous command that you should never have to use as casual Git user. We are executing this command to demonstrate a successful git prune. With the reflog totally wiped we can now execute git prune.

Discussion

git remote prune and git fetch --prune do the same thing: delete the refs to branches that don't exist on the remote. This is highly desirable when working in a team workflow in which remote branches are deleted after merge to master. The second command, git fetch --prune will connect to the remote and fetch the latest remote state before pruning. It is essentially a combination of commands:

How Do I Clean Outdated Branches?

git fetch --prune is the best utility for cleaning outdated branches. It will connect to a shared remote repository remote and fetch all remote branch refs. It will then delete remote refs that are no longer in use on the remote repository.

Does Git Remote Prune Origin Delete the Local Branch?

No git remote prune origin will only delete the refs to remote branches that no longer exist. Git stores both local and remote refs. A repository will have local/origin and remote/origin ref collections. git remote prune origin will only prune the refs in remote/origin. This safely leaves local work in local/origin.

Git Prune Summary

The git prune command is intended to be invoked as a child command to git gc. It is highly unlikely you will ever need to invoke git prune in a day to day software engineering capacity. Other commands are needed to understand the effects of git prune. Some commands used in this article were git log, git reflog, and git checkout.