Baeldung Pro – Ops – NPI EA (cat = Baeldung on Ops)
announcement - icon

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.

Partner – Orkes – NPI EA (cat=Kubernetes)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

1. Overview

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.

2. Example Setup

Let’s start by creating two branches: we’ll merge the second but not the first.

2.1. Create a Branch That Won’t Be Merged

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.

2.2. Create Another Branch and Merge It

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.

3. Build the Deletion Command

Let’s now build the deletion command step-by-step and detail the steps.

3.1. List Fully Merged Branches

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:

  • -r : specifies that we want to list remote branches only
  • –merged : limits the list to branches that have been merged into the current local branch

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

3.2. Exclude Relevant Branches From the List

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.

3.3. Delete Fully Merged Branches on the Remote

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.

3.4. Delete Merged Branches Locally

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:

  • git branch -vv: lists all branches with the verbose option; this returns one detailed line per branch. In particular, the line about fully-merged-branch reads: fully-merged-branch 401eb4f [origin/fully-merged-branch: gone] commit on fully merged branch
  • grep ‘: gone]’: amongst all the lines, select the ones that contain : gone]: they indeed correspond to the local branches with deleted remote counterparts
  • awk ‘{print $1}’: extracts the first element of each line, in this case, fully-merged-branch
  • xargs -n 1 git branch -d: finally deletes the local branch

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.

4. Conclusion

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.