Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: October 21, 2024
In this tutorial, we’ll address a common problem with merge branches sometimes polluting a repository. Sometimes, merged branches are kept in the repository, making navigating harder. Thus, we’ll find a solution to delete all merged branches for an appropriate clean-up.
Let’s start by creating two branches: we’ll merge the second but not the first.
First, we’ll create a new branch called not-merged-branch:
$ git checkout -b not-merged-branch
We want it to diverge from the main branch, so we’ll add a commit on top of it. However, the commit content is irrelevant. Hence, we’ll leave it empty for easiness:
$ git commit --allow-empty -m "commit on not merged branch"
Last but not least, we’ll push our branch to the remote:
$ git push -u origin not-merged-branch
And we’ll stop here: the branch not-merged-branch isn’t merged into the main one.
We’ll now want to create a new branch but merge it into the main one. First, we need to come back to the main branch:
$ git switch main
We can now create a branch fully-merged-branch locally and remotely, similar to what we did with not-merged-branch. This branch will contain an empty commit as well:
$ git checkout -b fully-merged-branch
$ git commit --allow-empty -m "commit on fully merged branch"
$ git push -u origin fully-merged-branch
We can now come back to the main branch:
$ git switch main
Let’s now merge the remote fully-merged-branch branch into the local main branch and then push the main branch to ensure it’s up-to-date with its remote counterpart:
$ git switch main
$ git merge origin/fully-merged-branch
$ git push
On the contrary, in real-life examples, we generally merge our remote branches via our source code management tool and then update our local branches accordingly.
Let’s now build the deletion command step-by-step and detail the steps.
A branch is fully merged into the main branch if its entire commit history is included in the main branch commit history, and there are no pending changes from the branch that haven’t been applied to the main branch.
To start, we can list the fully merged remote branches into our local main branch:
$ git branch -r --merged
origin/HEAD -> origin/main
origin/fully-merged-branch
origin/main
In particular, in the git branch -r –merged instruction:
As expected, the fully-merged-branch and the main branch appear in the list of merged branches. Moreover, the main branch appears twice because it’s the default branch. On the other hand, the not-merged-branch isn’t part of the list.
Let’s point out that this command works because we’re on main and want the branches merged into main; if we were on another branch and would like to get the fully merged branches into main, we could run:
$ git branch -r --merged main
Alternatively, we could also run:
$ git switch main | git branch -r --merged
Let’s notice this last command puts the pointer back to the main branch afterward. Let’s also notice that we could also compare the branches directly with the remote main branch if we don’t mind updating our local main branch:
$ git branch -r --merged origin/main
When we want to clean up the fully merged branches, we don’t want to delete the main branch. Thus, let’s make a simple usage of the grep command to remove the main branch from the results of the previous instruction:
$ git branch -r --merged | grep -v main
origin/fully-merged-branch
More generally, depending on the Git workflow we use, we need to protect a subset of branches from elimination. For instance, supposing our workflow includes a develop branch that needs to remain, our previous command would return:
$ git branch -r --merged | grep -v main
origin/develop
origin/fully-merged-branch
To exclude the develop branch along the main branch, we can update the command to:
$ git branch -r --merged | grep -v 'main\|develop'
origin/fully-merged-branch
We transformed the grep argument into a regular expression that includes all branches to escape.
We now have an exhaustive list of the fully merged branches. For the moment, we need to clean up the branch names by removing the remote-tracking branch prefix origin/:
$ git branch -r --merged | grep -v 'main\|develop' | sed 's/origin\///'
fully-merged-branch
We removed the prefix by calling sed on top of our previous result. In ‘s/origin\///’, s denotes a substitution operation: the first pattern origin/ is replaced with an empty pattern.
We can now pipe our last step: the concrete branch deletion. To do this, we’ll pass the output of the current command as an argument of the deletion through xargs:
$ git branch -r --merged | grep -v 'main\|develop' | sed 's/origin\///' | xargs -n 1 git push --delete origin
To ...repository address...
- [deleted] fully-merged-branch
We used the straight-forward git push –delete origin command to eventually delete the remote branches. Using -n 1 as an xargs argument ensures that each pipe output makes for a different deletion input.
Finally, let’s now check the existing branches on the remote repository:
$ git branch -r
origin/HEAD -> origin/main
origin/develop
origin/main
origin/not-merged-branch
As expected, fully-merged-branch was deleted on the remote repository and all the other branches remain existent.
Now, let’s check the existing branches in our local repository:
$ git branch
develop
fully-merged-branch
* main
not-merged-branch
As we can see, the fully-merged-branch still exists locally. The star in front of main denotes that we ran the command while pointing to the main branch. As a result, we still need to delete the merged branches in the local repository. In this example, there is only a branch to remove, and we could do it manually.
However, in real life, there could be lots of them, so it could be interesting to automate the process. First, let’s update our local repository:
$ git fetch --prune
The –prune option removes local references to remote-tracking branches deleted from the remote repository. We can now delete the local branches whose remote counterparts don’t exist anymore via:
$ git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -n 1 git branch -d
Deleted branch fully-merged-branch (was 401eb4f).
Let’s dive into this instruction:
We can verify the deletion of the branch in our local repository:
$ git branch
develop
* main
not-merged-branch
To conclude, we removed the merged branches locally after removing their remote counterparts. Did we need to remove only the merged branches locally, we’d adapt the previous command to the local repository:
$ git branch --merged | grep -v 'main\|develop' | xargs -n 1 git branch -d
In particular, we didn’t need to remove origin/ from the branch name because it’s not part of the local branch names, and we used git branch -d for local branch deletion.
In this article, we saw how to clean up our repository by deleting all fully merged branches. We first deleted them on the remote repository by comparing them to an up-to-date local main branch. Then, we automatically deleted the local branches without any remote counterparts to reproduce the operation locally. Lastly, we showed a simplified solution for cleaning up only the local repository.