Home > OS >  Using grep and xargs with git to delete multiple branches at once
Using grep and xargs with git to delete multiple branches at once

Time:12-02

I've been using some of the Linux tooling on my Windows machine for a little while now, since it comes with the git installation and it's a ton of fun to use. I've been particularly enamored with this command, which should theoretically allow me to delete all my extraneous git branches in one go:

git branch | grep -v 'master' | xargs git branch -d

A while ago, however, this stopped working. Instead I get a series of error messages for each branch along the following lines:

error: branch 'extraneous-branch-1?' not found.
error: branch 'extraneous-branch-2?' not found.
error: branch 'extraneous-branch-3?' not found.
...

Note that the question marks are not part of my branch names - those are apparently being added somehow when the values are piped from grep to xargs. When I run xargs in interactive mode to try to see what it's actually producing, I get an output that looks like this:

git branch -d 'extraneous-branch-1'$'\r' 'extraneous-branch-2'$'\r' 'extraneous-branch-3'$'\r' ...

It seems as if grep is piping the end-of-line and carriage-return entries as part of each match, though I don't know how to prevent it from doing that. What baffles me is that I definitely remember this working before - I have no idea what would have changed. Truthfully I know barely anything about the Linux command line tools, so I wouldn't be surprised if there's something obvious I'm overlooking here. Appreciate any advice either way.

Edit

When I run git branch | cat -A, I get the following result:

 extraneous-branch-1$
 extraneous-branch-2$
 extraneous-branch-3$

CodePudding user response:

Thanks to anubhava for pointing me in the right direction here. It appears grep is returning a bunch of non-printing characters that are visible when you run git branch | cat -A. It turns out just adding cat in there eliminates all those characters. Now it works perfectly.

git branch | grep -v master | cat | xargs git branch -d

a bit more verbose than before, but I'm not complaining.

CodePudding user response:

The root of the problem here is a specific example of a more general case: some Git commands produce output that is meant for a human to read, and other Git commands produce output that is meant for a computer to read. Reading human output with computer programs produces surprises, so: don't do that.

In particular, git branch may add color encoding to branch names, or run its output through a pager that does fancy things. This is because git branch is what Git calls a porcelain command, meaning it produces human-readable output. Fortunately, there's a similar plumbing command, git for-each-ref, that—because it is "plumbing"—produces computer-readable output by default.

The for-each-ref command is a little trickier to use because you must be more explicit. To get branch names, without further details, you need:

git for-each-ref --format="%(refname:short)" refs/heads

You can run this output through grep -v '^master$' to drop the name master. (Your existing grep is a little loose. It would also drop, e.g., the branch name stairmaster_bugfix.)

For more on the git for-each-ref command, see its documentation. Note that by default, git branch attempts to drop fancy features like colorization and use of the pager by default if its stdout is not a "tty device" as determined by the C library isatty function, but you can override these defaults with configuration settings. It is likely that you have done this, since git branch | grep generally causes the C library isatty to say "no, not a tty".

  • Related