TIL how to delete local branches that are merged into HEAD
and deleted on remote.
I stumbled upon the issue that I had a lot of local branches, which I had already merged into HEAD
and deleted on remote. I went to Google and found the answer on Stack Overflow. The answer says that the following line—which requires bash
or Z shell
—should do the trick:
git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d
Before I am actually going to use it, let's see what every command does.
git branch -r
With git branch
you can list, create or delete branches. Option -r
causes the remote-tracking branches to be listed. If you run this command in your git repository, the output will be similar to:
$ git branch -r
origin/project-version-1.0.1
origin/HEAD -> origin/master
origin/dev-issue-123
origin/dev-issue-124
|
Pipes (|
) let you use the output of a command as the input of another command.
awk '{print $1}'
awk
searches files for text containing a pattern. {print $1}
prints the first item from each line of the output from git branch -r
. Combined with the previous command, the output is:
$ git branch -r | awk '{print $1}'
origin/project-version-1.0.1
origin/HEAD -> origin/master
origin/dev-issue-123
origin/dev-issue-124
Notice that first the command git branch -r
is executed, and it's output, instead of beining printed, is sent (piped) to the awk
command, which in turn, prints what is has to.
egrep -v -f /dev/fd/0 <(git branch -vv | grep origin)
egrep
searches for a pattern using extended regular expressions—esentially the same as running grep
with the -E
option. Option -v
inverts the sense of matching, to select non-matching lines. Option -f
obtains patterns from FILE
, one per line.
git branch
is already explained. Option -vv
shows sha1 and commit subject line for each head, along with relationship to upstream branch—if any and prints the name of the upstream branch.
grep
filters the output of git branch -vv
for those containing origin
.
Because git branch -vv | grep origin
are basically two commands, we can not use pipe to pass the output to egrep
. We have to place the two commands inside <()
to be able to pass the output to egrep
. It is important to note that before the output is passed to egrep
, the output is redirected and gets a virtual file discriptor inside /dev/fd/0
assigned. This acts like a temporary file, which contains the output. This output is eventually passed to egrep
.
Combining these command with the previous commands, the output would be:
$ git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin)
dev-issue-90 da39a3ee [origin/dev-issue-90: gone] Fix #90 - Implemented new feature
dev-issue-91 3255bfef [origin/dev-issue-91: gone] Closed #91 - Solved issue with login
dev-issue-92 5e6b4b0d [origin/dev-issue-92: gone] Fix #93 - Implemented new feature
dev-issue-93 95bd1890 [origin/dev-issue-93: gone] Fixed #93 - Refactored classes
dev-issue-94 afd80709 [origin/dev-issue-94: gone] Solved #94 - Implemented new feature
dev-issue-95 5e6b4b0c [origin/dev-issue-95: gone] Fix #95 - Do not use deprecated method
dev-issue-96 12abcd34 [origin/dev-issue-96: gone] Fix #96 - Corrected typo in comments
awk '{print $1}'
Already explained awk
. Notice that it now prints the first item from each line of the output of the previous commands combined:
$ git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}'
dev-issue-90
dev-issue-91
dev-issue-92
dev-issue-93
dev-issue-94
dev-issue-95
dev-issue-96
xargs git branch -d
xargs
typically contains the skeleton, or template, of another command. In this case the previous command—which lacks the argument to delete the listed branches. xargs
adds the argument git branch -d
from standard input to complete the command, then runs the resulting command.
git branch -d
on its turn deletes a branch. Notice that this branch must be fully merged in its upstream branch, or in HEAD
if no upstream was set with --track
or --set-upstream
.
If we now run the whole line, we have successfully deleted all local branches that are merged into HEAD
and deleted on remote.
So that is what all of the commands do. It may or may not be the best way to delete local branches that are merged into HEAD
and deleted on remote. Nevertheless, the main intention of this blogposts was to investigate the used commands. 🤓
How do you handle local branches vs remote branches? Let me know!