Deleting Git branches in bulk
Deleting several Git branches at once with commands like git branch -D
can become a real headache. At any time, I have between 5-15 branches per project, all of which I may complete at random times based on priority and state. If you forget to delete your local branches after merging your work, you may end up having weeks of stale branches living within your project.
Thankfully, there’s an easier alternative to this, and we can run a single command using commands like a pipe (|
) and grep
to delete several branches at once.
The command
Deleting branches in bulk can be done in a single line:
git branch | grep -iE 'FIX|FEAT' | xargs git branch -D
Let’s take a look at what’s being done here:
- First, we define our first command,
git branch
, on the left side of the first pipe. This lists all of the branches within your project. - Second, we define a
grep
command which will search the output of our branch command. Since we are using a pipe, the output ofgit branch
is passed to grep. To match several branch patterns at once, we define the-E
flag. This allows us to match several strings while also supporting extended regular expressions. We also may have case variations between “FIX”, and “fix”, meaning our grep should be case-insensitive. We do this with the-i
flag. - Third, we define the normal branch deletion command on the right side of the last pipe, and also supply
xargs
as part of this command. xargs, or extended arguments, will take the left side of the last pipe (our grep command) and pass it as arguments togit branch -D
, calling the branch deletion command for each matched branch. - As a result, all branches that start with “FEAT” or “FIX” (case insensitive) are automatically deleted.
Visualizing the deletion
To visualize this, let’s run each of these commands individually:
$ git branch FEAT/RS-123 FIX/RS-456 IMPROV/RS-13 feat/RS-96 fix/RS-56 REF/RS-250
Running git branch
returns all of the existing local branches. Now, let’s try to find all of the branches that we want to delete:
$ git branch | grep -iE 'FIX|FEAT' * FEAT/RS-123 * FIX/RS-456 * feat/RS-96 * fix/RS-56
Our grep
command highlighted the matching branches and each matched phrase. Using the pipe operator, we passed the output of git branch
into the grep
.
If we typed the deletions manually for each branch, instead of using the full command, it would look something like this:
$ git branch -D FEAT/RS-123 Deleted branch FEAT/RS-123 (was 1b39b22). $ git branch -D FIX/RS-456 Deleted branch FIX/RS-456 (was ea9973f). $ git branch -D feat/RS-96 Deleted branch feat/RS-96 (was 6c114fb). $ git branch -D fix/RS-56 Deleted branch fix/RS-56 (was 5a82faa).
We can now see that instead of a single command that takes advantage of pipes, we had to type 6 different commands, each of which has a standard output of its own – making it tougher to keep track of what was deleted, and what still needs to be cleaned up.
Passing data using pipes
Many resources exist on pipes and their usages, but it may help to visualize usage in the context of deleting this branch deletion command. In our case, the condensed data flow looks something like this:
git branch -> grep -iE 'FIX|FEAT' -> xargs git branch -D
Each side of our pipe has a “standard input” (STDIN) and “standard output” (STDOUT). The STDOUT is the result of running a command and is passed in as STDIN to the next command. In our flow:
git branch (STDOUT) -> (STDIN) grep -iE 'FIX|FEAT' (STDOUT) -> (STDIN) xargs git branch -D (STDOUT)
While this is an overly simplistic explanation of what is happening behind the scenes, it hopefully provides an idea of how commands and their outputs can be spliced together using pipes.