Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git Branch Status via Plumbing Command

Tags:

git

Is there any way to get the output of git branch -v as a plumbing command? To be exact I'm only interested in the state of the branch, i.e. whether it is [gone] or not.

For example given the following git branch -v output:

> git branch -v 
  master            32c59ad4 Some other comment
  someDeletedBranch 6aacba47 [gone] Some Comment

How could I get the someDeletedBranch ref?

Note that this is not the same as git branch --merged, if for example you're squashing pull requests into your master, so this solution won't do.

This is mostly in relation to this question, since this would be the missing part for being able to create a reliable script to remove local branches that don't exist on the remote any longer.

like image 426
Voo Avatar asked Nov 27 '18 16:11

Voo


Video Answer


2 Answers

The plumbing substitute for git branch is usually git for-each-ref.

$ git fetch --prune
$ git for-each-ref --format '%(refname) %(upstream)' refs/heads refs/remotes/origin

This output will include an entry for each local branch, and an entry for each remote tracking ref pointing back to origin; so if run right after fetch --prune as shown here, it can show you what exists locally but not on the server.

Of course there's a gotcha: "exists locally but not on the server" might mean "removed from the server", or might mean "created locally and not yet pushed". To tell the difference, you need to also know whether your local branch "thinks" it has an upstream. (If it does, it's reasonably safe to assume it was removed from the server, in that git resists setting a nonexistent upstream; so for it to be wrong, someone would basically have to deliberately "trick" your script with a corrupt configuration.)

So that's what the --format option is for. You can process this output, looking for

refs/heads/somebranch refs/remotes/origin/somebranch

and, for each such entry, if there's not also a separate entry like

refs/remotes/origin/somebranch

then this is a branch that would be marked [gone]

like image 161
Mark Adelsberger Avatar answered Oct 22 '22 22:10

Mark Adelsberger


Git v2.13.2+ only

Building off of Mark's answer, and this post, here's a pretty clean solution (split lines for readability):

git for-each-ref \
    --format='%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' \
    refs/heads

Benefits:

  • only uses plumbing commands, see
    • "gone" as plumbing message
    • upstream:track displaying [gone]
  • no external dependencies
    • parsing and comparison logic is handled by --format
    • should work on all platforms that support git
  • short and clean one-liner

Simple way to delete local branches not on remote

A simple way to use this is with command substitution with git branch -D. Note that git will complain if there are no branches to delete.

git branch -D $(git for-each-ref --format='%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' refs/heads)

Here is a slightly longer version where git doesn't complain if there are no branches to delete.

for branch in $(git for-each-ref --format='%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' refs/heads); do git branch -D $branch; done

Alternatively, you can save the output of git for-each-ref into a variable, then conditionally delete the branch if it's not empty, or show a message if it is.

like image 22
Jerry Wu Avatar answered Oct 22 '22 22:10

Jerry Wu