[git] How to prune local tracking branches that do not exist on remote anymore

With git remote prune origin I can remove the local branches that are not on the remote any more.

But I also want to remove local branches that were created from those remote branches (a check if they are unmerged would be nice).

How can I do this?

This question is related to git

The answer is


Based on the answers above I'm using this shorter one liner:

git remote prune origin | awk 'BEGIN{FS="origin/"};/pruned/{print $2}' | xargs -r git branch -d

Also, if you already pruned and have local dangling branches, then this will clean them up:

git branch -vv | awk '/^ .*gone/{print $1}' | xargs -r git branch -d

If using Windows and Powershell, you can use the following to delete all local branches that have been merged into the branch currently checked out:

git branch --merged | ? {$_[0] -ne '*'} | % {$_.trim()} | % {git branch -d $_}

Explanation

  • Lists the current branch and the branches that have been merged into it
  • Filters out the current branch
  • Cleans up any leading or trailing spaces from the git output for each remaining branch name
  • Deletes the merged local branches

It's worth running git branch --merged by itself first just to make sure it's only going to remove what you expect it to.

(Ported/automated from http://railsware.com/blog/2014/08/11/git-housekeeping-tutorial-clean-up-outdated-branches-in-local-and-remote-repositories/.)


You can use this command:

git branch --merged master | grep -v "\* master" | xargs -n 1 git branch -d

Git Clean: Delete Already-Merged Branches including break down of command


Here is my solution :

git fetch -p
git branch -vv | grep ": gone" | awk '{print $1}' | xargs git branch -d
  • -p is to remove any remote-tracking references that no longer exist on the remote. So first step will remove references to remote branches.

  • -vv is for showing sha1 and commit subject line for each head, along with relationship to upstream branch (if any). 2nd step will get all the local branches and grep command will filter out branches that have been deleted.


There doesn't seem to be a safe one-liner, too many edge cases (like a branch having "master" as part of its name, etc). Safest is these steps:

  1. git branch -vv | grep 'gone]' > stale_branches.txt
  2. view the file and remove lines for branches you want to keep (such as master branch!); you don't need to edit the contents of any line
  3. awk '{print $1}' stale_branches.txt | xargs git branch -d

As @tzacks notes... there is an npm package that is handy for this. Just do:

npx git-removed-branches --prune

(I would have commented but not enough reputation)


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 an 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: Use of the -D flag to git branch can be very dangerous. So, in the config command above 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.


The Powershell Version of git branch --merged master | grep -v '^[ *]*master$' | xargs git branch -d

git branch --merged master | %{ if($_ -notmatch '\*.*master'){ git branch -d "$($_.Trim())" }}

This will remove any local branches that have been merged into master, while you are on the master branch.

git checkout master to switch.


There's a neat NPM package that does it for you (and it should work cross platform).

Install it with: npm install -g git-removed-branches

And then git removed-branches will show you all the stale local branches, and git removed-branches --prune to actually delete them.

More info here.


I'm pretty sure that git remote prune origin is what you want.

You can run it as git remote prune origin --dry-run to see what it would do without making any changes.


I reached this page seeking the answer for "how do I delete locally checked out branches that no longer have an upstream branch"

I also did not care whether or not the local branch had been merged in yet, since piping into git branch -d will simply warn instead of deleting unmerged local branches.

git branch -a | grep origin | tr -s ' ' | cut -d '/' -f3 | egrep -v -f /dev/fd/0 <(git branch -a | grep -v origin) | grep branch_prefix_that_I_care_about | xargs git branch -d

# translation
# git branch -a | grep origin | tr -s ' ' | cut -d '/' -f3
## this finds all remote branch names minus the "remote/origin/" part
#
# <(git branch -a | grep -v origin)
## this finds all local branch names and redirects it into the previous command
#
# egrep -v -f /dev/fd/0 STUFF
## this is doing some weird magic that I'm grokking as "take the set difference from whatever was piped in"
#
#
# overall translation: (STUFF TO CONSIDER) | egrep magic <(STUFF TO REMOVE FROM CONSIDERATION) | do cool things with resulting stuff

Amidst the information presented by git help fetch, there is this little item:

 -p, --prune
        After fetching, remove any remote-tracking branches which no longer exist on the remote.

So, perhaps, git fetch -p is what you are looking for?

EDIT: Ok, for those still debating this answer 3 years after the fact, here's a little more information on why I presented this answer...

First, the OP says they want to "remove also those local branches that were created from those remote branches [that are not any more on the remote]". This is not unambiguously possible in git. Here's an example.

Let's say I have a repo on a central server, and it has two branches, called A and B. If I clone that repo to my local system, my clone will have local refs (not actual branches yet) called origin/A and origin/B. Now let's say I do the following:

git checkout -b A origin/A
git checkout -b Z origin/B
git checkout -b C <some hash>

The pertinent facts here are that I for some reason chose to create a branch on my local repo that has a different name than its origin, and I also have a local branch that does not (yet) exist on the origin repo.

Now let's say I remove both the A and B branches on the remote repo and update my local repo (git fetch of some form), which causes my local refs origin/A and origin/B to disappear. Now, my local repo has three branches still, A, Z, and C. None of these have a corresponding branch on the remote repo. Two of them were "created from ... remote branches", but even if I know that there used to be a branch called B on the origin, I have no way to know that Z was created from B, because it was renamed in the process, probably for a good reason. So, really, without some external process recording branch origin metadata, or a human who knows the history, it is impossible to tell which of the three branches, if any, the OP is targeting for removal. Without some external information that git does not automatically maintain for you, git fetch -p is about as close as you can get, and any automatic method for literally attempting what the OP asked runs the risk of either deleting too many branches, or missing some that the OP would otherwise want deleted.

There are other scenarios, as well, such as if I create three separate branches off origin/A to test three different approaches to something, and then origin/A goes away. Now I have three branches, which obviously can't all match name-wise, but they were created from origin/A, and so a literal interpretation of the OPs question would require removing all three. However, that may not be desirable, if you could even find a reliable way to match them...


One can configure Git to automatically remove references to deleted remote branches when fetching:

git config --global fetch.prune true

When calling git fetch or git pull afterwards, references to deleted remote branches get removed automatically.


In Powershell:

git branch -D (git branch --merged |% { $_.trim() } )

This works for me using git 2.21.0 - it deletes local tracking branches which are merged into HEAD where I have previously --set-upstream on push (I use push.default=upstream because it works best with multiple remotes) and that upstream branch has since been deleted by a fetch --prune (or implicitly if fetch.prune=true in git config):

git branch -vv --merged | grep ': gone]' | awk '{print $1}' | xargs git branch -d

The use of --merged and -d make this a very 'safe' delete. A more aggressive version could drop the --merged and use -D


I did not find the answers here usefull when the remote itself does not exist anymore. I kept seing branches of remotes that did not exist anymore, but did not find a git command to delete them.

The solution for me was to go to the .git\refs\remotes directory and directly delete the files that are not relevant any more. The file structure is very easy to understand. It is the same structure as what you see with git branch -r.


not sure how to do it all at once, but git git branch -d <branchname> will delete a local branch ONLY if it is completely merged. Note the lowercase d.

git branch -D <branchname> (note the capital D) will delete a local branch regardless of its merged status.


Try this in git bash, to fetch and prune references to deleted branches, and then prune the local branches that were tracking the removed ones:

git fetch -p && git branch -d `git branch -vv | grep ': gone]' | awk '{print $1}' | xargs`

Remember to checkout first a branch that won't be deleted, so that does not block the deleting of the branch.


Delete any branch that isn't up to date with master

git co master && git branch | sed s/\*/\ / | xargs git branch -d 2> /dev/null

If you want to delete all local branches that are already merged into master, you can use the following command:

git branch --merged master | grep -v '^[ *]*master$' | xargs git branch -d

More info.


Using a variant on @wisbucky's answer, I added the following as an alias to my ~/.gitconfig file:

pruneitgood = "!f() { \
    git remote prune origin; \
    git branch -vv | perl -nae 'system(qw(git branch -d), $F[0]) if $F[3] eq q{gone]}'; \
}; f"

With this, a simple git pruneitgood will clean up both local & remote branches that are no longer needed after merges.


Even shorter and safer one-liner:

git branch -d $(git branch --merged | cut -c 3- | grep -v master)

Be sure to checkout to branch that is not merged yet, before run it. Because you can not delete branch that you are currently checked in.


Following is an adaptation of @wisbucky's answer for Windows users:

for /f "tokens=1" %i in ('git branch -vv ^| findstr ": gone]"') DO git branch %i -d

I use posh-git and unfortunately PS doesn't like the naked for, so I created a plain 'ol command script named PruneOrphanBranches.cmd:

@ECHO OFF
for /f "tokens=1" %%i in ('git branch -vv ^| findstr ": gone]"') DO CALL :ProcessBranch %%i %1

GOTO :EOF

:ProcessBranch
IF /I "%2%"=="-d" (
    git branch %1 %2
) ELSE (
    CALL :OutputMessage %1
)
GOTO :EOF

:OutputMessage
ECHO Will delete branch [%1] 
GOTO :EOF

:EOF

Call it with no parameters to see a list, and then call it with "-d" to perform the actual deletion or "-D" for any branches that are not fully merged but which you want to delete anyway.


To remove remote branches:

$git remote prune origin

To remove local branches that are already merged:

$git branch -D $(git branch --merged)

Schleis' variant does not work for me (Ubuntu 12.04), so let me propose my (clear and shiny :) variants:

Variant 1 (I would prefer this option):

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

Variant 2:

a. Dry-run:

comm -23 <( git branch | grep -v "/" | grep -v "*" | sort ) <( git br -r | awk -F '/' '{print $2}' | sort ) | awk '{print "git branch -D " $1}'

b. Remove branches:

comm -23 <( git branch | grep -v "/" | grep -v "*" | sort ) <( git br -r | awk -F '/' '{print $2}' | sort ) | xargs git branch -D

This will delete the local branches for which the remote tracking branches have been pruned. (Make sure you are on master branch!)

git checkout master
git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -d

Details:

  • git branch -vv displays "gone" for local branches that the remote has been pruned.

    mybranch abc1234 [origin/mybranch: gone] commit comments
    
  • -d will check if it has been merged (-D will delete it regardless)

    error: The branch 'mybranch' is not fully merged.
    

check for targets

for target in $(git branch | grep -Eiv "master|develop|branchYouWantToLive"); do echo $target; done

run with for & subcommands

for target in $(git branch | grep -Eiv "master|develop|branchYouWantToLive"); do git branch -D $target; done

you can extend other something works about branches.


It will list the local branches whose remote tracking branch is deleted from remote

$ git remote prune origin --dry-run

If you want to de-reference these local branches from local which are un tracked

$ git remote prune origin

I have turned the accepted answer into a robust script. You'll find it in my git-extensions repository.

$ git-prune --help
Remove old local branches that do not exist in <remote> any more.
With --test, only print which local branches would be deleted.
Usage: git-prune [-t|--test|-f|--force] <remote>

Based on the answers above I came with this one line solution:

git remote prune origin; git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d

Windows Solution

For Microsoft Windows Powershell:

git checkout master; git remote update origin --prune; git branch -vv | Select-String -Pattern ": gone]" | % { $_.toString().Trim().Split(" ")[0]} | % {git branch -d $_}

Explaination

git checkout master switches to the master branch

git remote update origin --prune prunes remote branches

git branch -vv gets a verbose output of all branches (git reference)

Select-String -Pattern ": gone]" gets only the records where they have been removed from remote.

% { $_.toString().Split(" ")[0]} get the branch name

% {git branch -d $_} deletes the branch


Using the GUI? Manual procedure, but quick and easy.

$ git gui

Select "Branch -> Delete". You can select multiple branches with ctrl-click (windows) and remove all of them.