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

Git

Git Problem Overview


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?

Git Solutions


Solution 1 - Git

After pruning, you can get the list of remote branches with git branch -r. The list of branches with their remote tracking branch can be retrieved with git branch -vv. So using these two lists you can find the remote tracking branches that are not in the list of remotes.

This line should do the trick (requires bash or zsh, won't work with standard Bourne shell):

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

This string gets the list of remote branches and passes it into egrep through the standard input. And filters the branches that have a remote tracking branch (using git branch -vv and filtering for those that have origin) then getting the first column of that output which will be the branch name. Finally passing all the branch names into the delete branch command.

Since it is using the -d option, it will not delete branches that have not been merged into the branch that you are on when you run this command.

Solution 2 - Git

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

If you are using main as your master branch, you should modify the command accordingly:

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

More info.

Solution 3 - Git

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...

Solution 4 - Git

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.
    

Solution 5 - Git

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.

Solution 6 - Git

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

Solution 7 - Git

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.

Solution 8 - Git

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)

Solution 9 - Git

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

Solution 10 - Git

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.

Solution 11 - Git

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/.)

Solution 12 - Git

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.

Solution 13 - Git

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.

Solution 14 - Git

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

Solution 15 - Git

To remove remote branches:

$git remote prune origin

To remove local branches that are already merged:

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

Solution 16 - Git

In Powershell:

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

Solution 17 - Git

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

Solution 18 - Git

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.

Solution 19 - Git

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

Solution 20 - Git

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

Solution 21 - Git

This command and the script below work in Linux and Windows with Git Bash (MinGW).

It is best to use git's internal commands so that comments or names don't accidentally match and delete a branch that you don't want to delete. There are many internal "atoms" that can be used with git for-each-ref's format option to output the desired information. This way we don't have to rely on piping to awk or grep to check a regular expression on output that may contain unnecessary information.

The command below uses only git for-each-ref's internal low level commands to list only orphaned local branches. Once you have these you can pipe to git branch -D. Also, don't forget to prune and fetch your remote references first, or it won't find any matches:

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

Here is a breakdown:

git fetch -p - prune removed references and fetch new ones from the remote

git for-each-ref --format - lists all references using a specific output format.

%(if:equals=[gone])%(upstream:track) - only output if the upstream tracking branch is "[gone]".

%(then)%(refname:short)%(end) - output the branch name (when tracking is gone).

refs/heads/** - limit to head references (for efficiency).

| xargs -r git branch -D - pipe output as parameters for deletion. The -r indicates to ignore blank input.


By itself, this solution is long, ridiculous to type, and hard to remember. Luckily, adding custom commands to git is easy. Below is a script that uses the same command above, but it allows the user to see which branches will be selected with a --dry-run option.

I named my file git-prune-local and dropped it in a folder that was included on my PATH. It also needs execution permissions (chmod 755 git-prune-local).

Git automatically looks for executable files like git-[command]. With this, you only need to type git prune-local for the correct branches to be deleted.

git-prune-local

#!/bin/sh

if [ $# -gt 1 ] || ([ ! -z $1 ] && [ $1 != "--dry-run" ])
then
    echo "Usage: git prune-local [--dry-run]"
    exit
fi

git fetch -p --quiet
branchesToDelete=$(git for-each-ref --format '%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' 'refs/heads/**')

while read -r branch
do
    if [ ! -z $branch ]
    then
        if [ ! -z $1 ]
        then
            echo $branch
        else
            git branch -D $branch
	    fi
    fi
done <<< "$branchesToDelete"

Solution 22 - Git

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

Solution 23 - Git

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.

Solution 24 - Git

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.

Solution 25 - Git

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.

Solution 26 - Git

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

Solution 27 - Git

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.

Solution 28 - Git

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.

Solution 29 - Git

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>

Solution 30 - Git

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

Solution 31 - Git

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.

Solution 32 - Git

In addition to Schleis answer (which works perfectly), this can be integrated into Visual Studio, so pruning local branches when a git repo is open in VS is really simple.

You can use the External Tools functionality to call sh.exe (git bash) with a specific directory and command. This is located in the Tools > External Tools menu option (in VS 2022 17.1.0). The parameters I use are as follows:

Command: {Path-To-Git-Installation-Location}\bin\sh.exe

Arguments: --cd=$(SolutionDir) -c "git fetch -p ; git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d"

Initial Directory: $(SolutionDir)

Screenshot of Git Prune Local Branches

It's worth noting that this will work only when the solution you have open in VS is within a git repo directory.

Final note - this can be keybound via the Visual Studio key binding user interface in Settings > General > Keyboard and searching for Tools.ExternalCommand[n] where n is the position in the External Command table that you have this external tool positioned at (they can be reordered in the Tools > External Tools dialog). See screenshot below.

Keybinding an External Tools Command

Solution 33 - Git

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

Solution 34 - Git

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.

Solution 35 - Git

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAlexView Question on Stackoverflow
Solution 1 - GitSchleisView Answer on Stackoverflow
Solution 2 - GitjackocnrView Answer on Stackoverflow
Solution 3 - GittwalbergView Answer on Stackoverflow
Solution 4 - GitwisbuckyView Answer on Stackoverflow
Solution 5 - GittzachsView Answer on Stackoverflow
Solution 6 - Gitchris31389View Answer on Stackoverflow
Solution 7 - GitwedesoftView Answer on Stackoverflow
Solution 8 - GitjohnshewView Answer on Stackoverflow
Solution 9 - Git027View Answer on Stackoverflow
Solution 10 - GitFelikZView Answer on Stackoverflow
Solution 11 - GitGnatView Answer on Stackoverflow
Solution 12 - GitKarl WilburView Answer on Stackoverflow
Solution 13 - GitS'chn T'gai SpockView Answer on Stackoverflow
Solution 14 - GitOliverView Answer on Stackoverflow
Solution 15 - GitAniketView Answer on Stackoverflow
Solution 16 - GitRaúl Salinas-MonteagudoView Answer on Stackoverflow
Solution 17 - GithidraliskView Answer on Stackoverflow
Solution 18 - GitNHDalyView Answer on Stackoverflow
Solution 19 - GitdaniilyarView Answer on Stackoverflow
Solution 20 - Gitsendon1982View Answer on Stackoverflow
Solution 21 - GitMacadameaneView Answer on Stackoverflow
Solution 22 - GitgagarwalView Answer on Stackoverflow
Solution 23 - GitKen WilliamsView Answer on Stackoverflow
Solution 24 - GitSam CView Answer on Stackoverflow
Solution 25 - GitKgeorgeView Answer on Stackoverflow
Solution 26 - GitspumeView Answer on Stackoverflow
Solution 27 - GitGuillermo GutiérrezView Answer on Stackoverflow
Solution 28 - Gitcyan-kinesinView Answer on Stackoverflow
Solution 29 - GitIngo KarkatView Answer on Stackoverflow
Solution 30 - GitMeredithView Answer on Stackoverflow
Solution 31 - GitPaulHView Answer on Stackoverflow
Solution 32 - GitJames van RossumView Answer on Stackoverflow
Solution 33 - GitJason WaldripView Answer on Stackoverflow
Solution 34 - GitJason AntmanView Answer on Stackoverflow
Solution 35 - GitPedro BallesterosView Answer on Stackoverflow