28
==> git branch -a
* master
  test
  remotes/origin/master
  remotes/origin/test

when someone delete the remotes/origin/test,I still can see it on my computer.

I know I can do this and remove the test

==> git remote prune
==> git branch -d test
==> git branch -a
* master
  remotes/origin/master

But if I have more local branch, and they are not on remote, so how can I remove them quickly?

Dozer
  • 4,319
  • 11
  • 33
  • 52
  • 2
    Possible duplicate of [Remove tracking branches no longer on remote](https://stackoverflow.com/questions/7726949/remove-tracking-branches-no-longer-on-remote) – rds Oct 03 '18 at 19:02

8 Answers8

41

This is how I remove local branches that are not longer relevant:

git branch --merged origin/master | xargs git branch -d

You may need to tweak it according to your specific configuration (e.g. see comments below to exclude particular branches), but the first command here before the pipe should give you a list of all your local branches that have been merged into your master branch.

Michael Freidgeim
  • 21,559
  • 15
  • 127
  • 153
eck
  • 555
  • 4
  • 6
  • 24
    For fear of removing local master, I modified this to be: `git branch --merged origin/master | grep -v master | xargs git branch -d` – thexfactor Feb 17 '15 at 01:15
  • 6
    Nitpicky change to the above comment, but to avoid removing other branches besides "master" accidentally (i.e. "develop" in Gitflow), it would probably be better to not invert the grep command and instead do something like: `git branch --merged origin/develop | grep "^\s*feature/" | xargs git branch -d` – Steve Ardis Apr 02 '15 at 17:16
18

According to the git-fetch manual page, git fetch -p will "After fetching, remove any remote-tracking branches which no longer exist on the remote.` If you have local branches tracking those remote branches, you may need to prune those manually.

twalberg
  • 52,775
  • 9
  • 80
  • 80
  • 5
    but how to remove local branch not on remote? the `-p` only remove the remote-tracking – Dozer May 16 '13 at 14:47
  • 6
    Can you reliably distinguish between a local branch that was set up to track a remote branch vs. one that is for local development? If my local branch is named `foo`, was it originally created to track `remotes/origin/foo`, or is it a local branch that I created to test some new idea? If you can answer that question reliably for every local branch, you can `git branch -D` the ones you don't want. If you can't, trying to do it "automatically" will be destructive. – twalberg May 16 '13 at 15:10
16

A simple prune will not delete the local branch.

Here is another approach to achieve a real deletion. Be sure to execute "git fetch -p" first to get the latest status of the remote repositories.

git branch -vv | grep ': gone]'|  grep -v "\*" | awk '{ print $1; }' | xargs -r git branch -d

This will check all local branches and their origin and will delete all local branches whose origin was deleted.

In detail:

git branch -vv

will list your local branches and show information about the remote branch, saying “gone” if it is not present anymore.

grep ': gone]'

will fetch the branches that match the “ gone]” phrase.

grep -v "\*"

will fetch only lines that do not contain an asterisk. This will ignore the branch you are currently on and also prevent that the “git branch -d” is executed with a “*” at the end which would result in deleting all your local branches

awk '{print $1}'

will fetch the output until the first white space, which will result in the local branch name.

xargs git branch -d

will use the output (branch name) and append it to the “git branch -d” command to finally delete the branch. If you also want to delete branches that are not fully merged, you can use a capital “D” instead of “d” to force delete.

kcm
  • 545
  • 1
  • 5
  • 14
  • Worked on macOS, but some branches which had no remote source has been left. Interesting.. – Johnny Five Jan 23 '19 at 08:59
  • @JohnnyFive just execute a plain "git branch -vv" and check if the branches are marked with "gone". you might have to do a git fetch again. – kcm Feb 14 '19 at 09:11
  • Getting `xargs: illegal option -- r` – Leo Aug 22 '19 at 15:46
  • @Leo This error occurs if you are executing the command on a non-GNU Linux system. Only GNU Linux based systems have the -r option which is responsible for skipping the execution if the input arguments (in this case the branch name) is empty. You can remove the option from the command, but might get some invalid argument errors that you can ignore. – kcm Aug 23 '19 at 09:55
  • Had exactly this issue, worked than with git branch -vv | grep ': gone]'| grep -v "\*" | awk '{ print $1; }' | xargs git branch -D – Denis Mar 19 '21 at 05:43
4

I wrote a simple shell script called git-dangling-branches for this purpose. If you specify -D option, it will delete all local branches which don't have refs/remotes/origin/<branch_name>. Of course, you should be careful when you do that.

#!/bin/bash -e
if [[ "$1" == '-D' ]]; then
  DELETE=1
else
  DELETE=0
fi

REMOTE_BRANCHES="`mktemp`"
LOCAL_BRANCHES="`mktemp`"
DANGLING_BRANCHES="`mktemp`"
git for-each-ref --format="%(refname)" refs/remotes/origin/ | \
  sed 's#^refs/remotes/origin/##' > "$REMOTE_BRANCHES"
git for-each-ref --format="%(refname)" refs/heads/ | \
  sed 's#^refs/heads/##' > "$LOCAL_BRANCHES"
grep -vxF -f "$REMOTE_BRANCHES" "$LOCAL_BRANCHES" | \
  sort -V > "$DANGLING_BRANCHES"
rm -f "$REMOTE_BRANCHES" "$LOCAL_BRANCHES"

if [[ $DELETE -ne 0 ]]; then
  cat "$DANGLING_BRANCHES" | while read -r B; do
    git branch -D "$B"
  done
else
  cat "$DANGLING_BRANCHES"
fi
rm -f "$DANGLING_BRANCHES"
trustin
  • 11,651
  • 6
  • 36
  • 49
  • This is the answer that I wanted. I'm interested in clearing out local branches that that don't exist with the same name in github. The other answers remove remote-tracking branches, and remove all merged branches, but that's not what I wanted. So thanks for this. I changed -D to -d before running it, just in case. – Chris Lear Nov 06 '14 at 10:57
  • This is fantastic. I had to change `mktemp` to touch with actual filenames because `mktemp` wasn't supported in my OS, but this is absolutely wonderful. – earl3s Jul 07 '16 at 20:02
  • I also added this: `git remote prune origin $DRY_RUN` at the top where `$DRY_RUN` is set to `--dry-run` if `-D` is not passed. Cleans my system up nicely. – earl3s Jul 07 '16 at 20:03
2

I ended up with something very similar to kcm's approach. I wanted something that would purge all local branches that were tracking a remote branch, on origin, where the remote branch has been deleted (gone). I did not want to delete local branches that were never set up to track a remote branch (i.e.: my local dev branches). Also I wanted a simple one-liner that just uses git, or other simple CLI tools, rather than writing custom scripts. I ended up using a bit of grep and awk to make this simple command then added it as a alias in my ~/.gitconfig.

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

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: As Matteo pointed out in his earlier comment on another answer, use of the -D flag to git branch can be very dangerous. So, in the config command I use the -d option to git branch rather than -D; I use -D in my actual config. I use -D because I don't want to hear Git complain about unmerged branches, I just want them to go away. You may want this functionality as well. If so, simply use -D instead of -d at the end of that config command.

Karl Wilbur
  • 4,826
  • 2
  • 36
  • 45
1

You can do this by iterating over the refs, I used following command to remove all the local branches which dont have remote branches and it worked. Warning: this operation does a force delete non fully merged branches, use with care.

git branch -D `git for-each-ref --format="%(fieldName)" refs/heads/<branch-name-pattern>`

%(fieldName) = refname:short)

refs/heads/ = can be suffixed if you have a common prefix/suffix in branch names ex: refs/heads/*abc*

Refer this for more information git-for-each-ref(1) Manual Page

Laksitha Ranasingha
  • 3,654
  • 1
  • 26
  • 33
  • With your solution you will forcibly remove also branches that are not fully merged, pretty dangerous! – Matteo Nov 30 '17 at 09:49
0

First, do:

git fetch && git remote prune origin

And then:

git branch -a | grep -v ${$(git branch -a | grep remotes | cut -d/" -f3-)/#/-e} | xargs git branch -D

  • List all remotes without remote/origin prefix: git branch -a | grep remotes | cut -d'/' -f3-
  • Remove all listed from git: git branch -a | grep v ${<all remotes without prefix>/#/-e}
  • Remove from local needs -D cause not all have to be marked as merged. For example, squash and merge in Github does not mark them as merged.
Pando85
  • 26
  • 4
  • I get `pipe braceparam cmdsubst pipe pipe dquote>` and have no idea what it means. – Leo Aug 22 '19 at 15:47
0

Fast

git fetch -p
git branch -v | grep "\[gone\]"

Then manually check and delete branches in the output.


Detailed

The simplest manual solution based on the answers in this thread as well as here, is to remove all remote-tracking references that no longer exist on the remote with

git fetch -p

then see what has been removed from remote-tracking, but is still in your local working tree with

git branch -v

and manually delete branches that have [gone] in the output.

To get a concise list run

git branch -v | grep "\[gone\]"
young_souvlaki
  • 1,382
  • 3
  • 18
  • 25