2

I'd like to list all the remote Git branches which don't have a corresponding local branch.

For example, if the output of git branch --all is:

  remotes/origin/alpha
* beta
  main
  remotes/origin/beta
  remotes/origin/main

What I'd like to end up with is:

remotes/origin/alpha

What's the best way to do that?

countermeasure
  • 442
  • 4
  • 10
  • Try `git remote show`, https://stackoverflow.com/a/15630478/7976758 May be `git remote show origin | grep -Fv tracked` Found in https://stackoverflow.com/search?q=%5Bgit%5D+list+remote+branches – phd Nov 27 '22 at 09:02
  • You may need to define exactly what you mean by "don't have a corresponding local branch". However, note that there's no need to have branch names in Git: it's possible to do work in Git with *no branch names at all*. Don't create branch names you don't need: there's no point. *Do* create branch names you want, as many as you want, because there's no disk-space cost to branch names, but don't create ones you don't need, because there's your-brain-space problems with having a ton of branch names. :-) – torek Nov 27 '22 at 09:08
  • 1
    @phd: Your suggestion seems to return local branches which track remote branches, but I'm looking to show remote branches without a local tracking branch. I've added an example to the question to help clarify. – countermeasure Nov 27 '22 at 09:46
  • @countermeasure `grep -v` filters out tracked so the entire command returns both local branches that don't have upstream and remote branches that are not yet fetched. – phd Nov 27 '22 at 10:11
  • Would you mind saying why you wish to see the list? – TTT Nov 27 '22 at 19:40
  • Does it have to be a git command? Or can it be a shell script? (Because I don't think there's a purely-`git`-command-only for this) – Gino Mempin Nov 29 '22 at 10:42
  • @TTT Not at all :) I want to make a wrapper around `git checkout` which shows me a list of branches I can check out. The list will contain all local branches (easy to get), plus the remote branches which don't have a local branch (hence this question). – countermeasure Nov 30 '22 at 09:03
  • 1
    @GinoMempin A Git command would be ideal, but I also suspect that there may not be one, so I'd be happy with a shell script. – countermeasure Nov 30 '22 at 09:05

1 Answers1

2

As far as I know and at the time of posting this answer, there is no direct Git command or option for this.

But, inspired by the answers from grep using output from another command, you can use grep to get a list of local branch refname's that are missing from the list of remote branch refname's:

$ grep -v -F -f <(git for-each-ref --format='%(refname:lstrip=2)' refs/heads) <(git for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin) | grep -vF HEAD

Example:

$ git branch
  feature/C
  feature/D           # local-only branch, not pushed to remote
* master

$ git branch -r
  origin/HEAD -> origin/master
  origin/develop      # no corresponding branch on local
  origin/feature/A    # no corresponding branch on local
  origin/feature/B    # no corresponding branch on local
  origin/feature/C    # is already on local
  origin/master       # is already on local

$ grep -v -F -f <(git for-each-ref --format='%(refname:lstrip=2)' refs/heads) <(git for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin) | grep -vF HEAD
develop
feature/A
feature/B

The structure of the command is:

$ grep -v -F -f <(command 2) <(command 1)
  • command 2 lists all the local branches
    $ git for-each-ref --format='%(refname:lstrip=2)' refs/heads
    feature/C
    feature/D
    master
    
  • command 1 lists all fetched remote branches
    $ git for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin
    HEAD
    develop
    feature/A
    feature/B
    feature/C
    master
    

The -f <(command 2) tells grep to search for the patterns from the output of command 2 (the local branch patterns) from the output of command 1 (the remote branch patterns). That should have returned remote branches that already have corresponding local branches, then the -v option inverts the result. The piped git -vF HEAD is just for excluding HEAD.

You can also use git branch but I prefer using git for-each-ref for scripting and automation. See related post on the difference between Git's "plumbing" vs "porcelain" commands.

This may not always work because:

  • This only compares branches by name, specifically refname's, and assumes that you always track a remote branch with a similarly named local branch, which may not always be the case.
  • The command uses Bash 5.x and it uses process substition (<(...)) to allow the command's output to be referred to as a filename to be used with grep's -f option. This makes it a bit non-portable and is quite problematic to be put into a git alias.
  • The target of for-each-ref for remote branches is refs/heads/remotes/origin, where origin is the commonly assumed remote name. Change it as necessary if origin is not your remote name.

For convenience, I turned it into a regular shell alias with a git fetch call:

$ alias git-new-branches="git fetch; grep -v -F -f <(git for-each-ref --format='%(refname:lstrip=2)' refs/heads) <(git for-each-ref --format='%(refname:lstrip=3)' refs/remotes/origin) | grep -vF HEAD"

$ git-new-branches 
develop
feature/A
feature/B

You might prefer turning that into a shell script instead, and embed that into a Git alias.

Gino Mempin
  • 25,369
  • 29
  • 96
  • 135