List all local branches without a remote

我的未来我决定 提交于 2019-11-27 17:26:02

I recently discovered git branch -vv which is the "very verbose" version of the git branch command.

It outputs the branches along with the remote branches if they exist, in the following format:

  25-timeout-error-with-many-targets    206a5fa WIP: batch insert
  31-target-suggestions                 f5bdce6 [origin/31-target-suggestions] Create target suggestion for team and list on save
* 54-feedback-link                      b98e97c [origin/54-feedback-link] WIP: Feedback link in mail
  65-digest-double-publish              2de4150 WIP: publishing-state

Once you have this nicely formatted output, it's as easy as piping it through cut and awk to get your list:

git branch -vv | cut -c 3- | awk '$3 !~/\[/ { print $1 }'

Results in the following output:

25-timeout-error-with-many-targets
65-digest-double-publish

The cut portion just normalizes the data by removing the first two characters (including *) from the output before passing it to awk.

The awk portion prints the first column if there is no square bracket in the third column.

Bonus: Create an alias

Make it easy to run by creating an alias in your global .gitconfig file (or wherever):

[alias]
  local-branches = !git branch -vv | cut -c 3- | awk '$3 !~/\\[/ { print $1 }'

Important: The backslash needs to be escaped in the alias or else you will have an invalid gitconfig file.

Bonus: Remote Filtering

If for some reason you have multiple tracking remotes for different branches, it's easy enough to specify which remote you want to check against. Just append the remote name to the awk pattern. In my case, it's origin so I can do this:

git branch -vv | cut -c 3- | awk '$3 !~/\[origin/ { print $1 }'

git branch (without any options) lists only local branches, but you don't know if they are tracking a remote branch or not.

Usually those local branches should be deleted once merged into master (as seen in this issue of git-sweep):

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

This isn't as complete as you want, but it is a start.

With --merged, only branches merged into the named commit (i.e. the branches whose tip commits are reachable from the named commit) will be listed.

I have a similar issue. I want to remove all local branches that were tracking remote branches that are now deleted. I am finding that git remote prune origin was insufficient to remove the branches that I want gone. Once the remote is deleted, I want the local to go away too. Here is what worked for me:

From my ~/.gitconfig:

[alias]
  prune-branches = !git remote prune origin && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -d

EDIT: As requested, here is a git config --global ... command for easily adding this as git prune-branches:

git config --global alias.prune-branches '!git remote prune origin && git branch -vv | grep '"'"': gone]'"'"' | awk '"'"'{print $1}'"'"' | xargs -r git branch -d'

NOTE: I changed the -d to -D in my actual config because I don't want to hear Git complain about unmerged branches. You may want this functionality as well. If so, simply use -D instead of -d at the end of that command.

Also, FWIW, your global config file would almost always be ~/.gitconfig.

EDIT:(OSX Fix) As written, this does not work on OSX because of the use of xargs -r (thanks, Korny).

The -r is to prevent xargs from running git branch -d with no branch name, which will result in an error message "fatal: branch name required". If you don't mind the error message, you can simply remove the -r argument to xargs and you're all set.

However, if you don't want to see an error message (and really, who could blame you) then you'll need something else that checks for an empty pipe. If you might be able to use ifne from moreutils. You would insert ifne before xargs, which will stop xargs from running with empty data. NOTE: ifne considers anything to be not empty, this includes blank lines, so you may still see that error message. I've not tested this on OSX.

Here is that git config line with ifne:

git config --global alias.prune-branches '!git remote prune origin && git branch -vv | grep '"'"': gone]'"'"' | awk '"'"'{print $1}'"'"' | ifne xargs git branch -d'

Late edit: better is

git for-each-ref  --format='%(refname:short) %(upstream)'  refs/heads \
| awk '$2 !~/^refs\/remotes/'

on GNU/anything

for b in `git branch|sed s,..,,`; do
    git config --get branch.$b.remote|sed Q1 && echo git branch -D $b
done

If more than a handful of branches were likely there'd be better ways, using comm -23 on the output of git branch|sed|sort and git config -l|sed|sort.

This works for me:

git branch -vv | grep -v origin

(if your remote is named anything other than origin, substitute that).

This will list all the branches that aren't tracking a remote, which sounds like what you're looking for.

Rough powershell implementation: (if origin is your only remote)

@($branches = git br -r; git branch | %{ echo $_ | sed 's/..//' }) `
   | %{ if (($branches | ss "origin/$_") -eq $null) { `
          echo "git branch -D $_" `
        } `
      }

Here is something I have used in PowerShell with comments to explain what it's doing. In an attempt to make it clear what's going on, I've not used any abbreviated PS commands. Feel free to compress it to your desired level of crypticness :)

$verboseList = @(git br -vv)
foreach ($line in $verboseList)
{    
    #get the branch name
    $branch = [regex]::Match($line, "\s(\S+)\s").Captures.Groups[1].Value
    #check if the line contains text to indicate if the remote is deleted
    $remoteGone = $line.Contains(": gone]")
    #check if the line does not contain text to indicate it is a tracking branch (i.e., it's local)
    $isLocal = !($line.Contains("[origin/"))
    if ($remoteGone -or $isLocal)
    {
        #print the branch name
        $branch
    }
}

I sinthetize my own git command to get the origin/***: gone branches:

git remote prune origin && git branch -vv | cut -c 3- | grep ': gone]' | awk '{print $1}' | xargs -n1 -r echo git branch -d

git remote prune origin && git branch -vv will print branches in verbose mode

cut -c 3- will remove very first chars

grep ': gone]' will print only the gone remote branches

awk '{print $1}' will print the branch name

xargs -n1 -r echo git branch -d will print the git branch -d command to remove branches (-n1 will manage one command per time, -r to avoid issuing command if no branch is present)

HINT: remove the "echo" command to run the commands instead of print only, I left this in the command to check commands before issuing to git.

HINT 2: issue git branch -D if and only if you are sure you want to remove unmerged branches

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!