Mailo Světel
Mailo Světel

Reputation: 26051

Remove tracking branches no longer on remote

Is there a simple way to delete all tracking branches whose remote equivalent no longer exists?

Example:

Branches (local and remote)

Locally, I only have a master branch. Now I need to work on bug-fix-a, so I check it out, work on it, and push changes to the remote. Next I do the same with bug-fix-b.

Branches (local and remote)

Now I have local branches master, bug-fix-a, bug-fix-b. The Master branch maintainer will merge my changes into master and delete all branches he has already merged.

So the current state is now:

Branches (local and remote)

Now I would like to call some command to delete branches (in this case bug-fix-a, bug-fix-b), which are no longer represented in the remote repository.

It would be something like the existing command git remote prune origin, but more like git local prune origin.

Upvotes: 2314

Views: 1157608

Answers (30)

dlsso
dlsso

Reputation: 8111

Most of these answers do not actually answer the original question. I did a bunch of digging and this was the cleanest solution I found. Here is a slightly more thorough version of that answer:

  1. Switch to your default branch. Usually git checkout main
  2. Run git fetch -p && git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -d

Explanation:

Works by pruning your tracking branches then deleting the local ones that show they are "gone" in git branch -vv.

Notes:

  • The warnings in the comments only apply if you skip step 1.
  • If your language is set to something other than English you will need to change gone to the appropriate word.
  • Branches that are local only will not be touched.
  • Branches that have been deleted on remote but were not merged will show a notification but not be deleted on local. If you want to delete those as well change -d to -D.

Upvotes: 560

ResponsiblyUnranked
ResponsiblyUnranked

Reputation: 1856

Git Town is a wrapper tool for git which can do this very simply using the git town sync --all command.

AFAIK it uses git native commands for syncing, so it's not doing anything you couldn't do manually. It just does it a lot faster, and all in one go!

Upvotes: 1

Andrew Spencer
Andrew Spencer

Reputation: 16504

I wouldn't normally answer a question that already has 16 answers, but all the other answers are wrong, and the right answer is so simple. The question says, "Is there a simple way to delete all tracking branches whose remote equivalent no longer exists?"

If "simple" means deleting them all in one go, not fragile, not dangerous, and without reliance on tools that not all readers will have, then the right answer is: no.

Some answers are simple, but they don't do what was asked. Others do what was asked, but are not simple: all rely on parsing Git output through text-manipulation commands or scripting languages, which may not be present on every system. On top of that, most of the suggestions use porcelain commands, whose output is not designed to be parsed by script ("porcelain" refers to the commands intended for human operation; scripts should use the lower-level "plumbing" commands).

Further reading:


If you want to do this safely, for the use case in the question (garbage-collect tracking branches which have been deleted on the server but still exist as local branches) and with high-level Git commands only, you have to

  1. git fetch --prune (or git fetch -p, which is an alias, or git remote prune origin which does the same thing without fetching, and is probably not what you want most of the time).
  2. Note any remote branches that are reported as deleted. Or, to find them later on, git branch -v (any orphaned tracking branch will be marked "[gone]").
  3. git branch -d [branch_name] on each orphaned tracking branch

(which is what some of the other answers propose).

If you want to script a solution, then for-each-ref is your starting point, as in Mark Longair's answer here and this answer to another question, but I can't see a way to exploit it without writing a shell script loop, or using xargs or something.


Background explanation

To understand what's happening, you need to appreciate that, in the situation of tracking branches, you have not one branch, but three. (And recall that "branch" means simply a pointer to a commit.)

Given a tracking branch feature/X, the remote repository (server) will have this branch and call it feature/X. Your local repository has a branch remotes/origin/feature/X which means, "This is what the remote told me its feature/X branch was, last time we talked," and finally, the local repository has a branch feature/X which points to your latest commit, and is configured to "track" remotes/origin/feature/X, meaning that you can pull and push to keep them aligned.

At some point, someone has deleted the feature/X on the remote. From that moment, you are left with your local feature/X (which you probably don't want any more, since work on feature X is presumably finished), and your remotes/origin/feature/X which is certainly useless because its only purpose was to remember the state of the server's branch.

And Git will let you automatically clean up the redundant remotes/origin/feature/X -- that's what git fetch --prune does -- but for some reason, it doesn't let you automatically delete your own feature/X... even though your feature/X still contains the orphaned tracking information, so it has the information to identify former tracking branches that have been fully merged. (After all, it can give you the information that lets you do the operation by hand yourself.)

Upvotes: 516

Leon S.
Leon S.

Reputation: 3687

We need more answers on this one, so here's my variation:

What I was missing was a way to make this less magical and more day-to-day friendly, so I've combined some of the other answers and ended up creating 2 aliases:

  1. gonelist that lists all the branches that have a remote which is now gone
  2. gonedelete that deletes those same branches gonelist returns.

You can add them to your global git config like this:

git config --global alias.gonelist '!git for-each-ref --format "%(refname:short) %(upstream:track)" refs/heads | grep -F " [gone]" | cut -d" " -f1'
git config --global alias.gonedelete '!tmp="$(mktemp)" && git gonelist >$tmp && vi $tmp && < $tmp xargs git branch -d'

the workflow

  1. git fetch --prune Git tells you that some branches where deleted remotely.
  2. git gonelist List if your local branches to see if any have missing remotes.
  3. git gonedelete And editor opens and and you can remove all listed branches except for the ones you wish to delete. Then save and close the editor (:wq) and those branches will be deleted. (You might need to switch branches first if you wish to delete the current branch.)
Explanation gonelist
  • git for-each-ref --format "%(refname:short) %(upstream:track)" - Lists all branch heads with optional '[gone]' suffix.
  • grep -F " [gone]" | cut -d" " -f1 - Gets only the [gone] branches and then removes the [gone] part.
Explanation gonedelete
  • tmp="$(mktemp)" && git gonelist >$tmp - create a tmp file and write the output of gonelist to it.
  • vi $tmp - Edit the file with vi
  • < $tmp xargs git branch -d - feed the contents of the tmp file to git branch -d.

gonedelete gives you a prompt to edit the branches that should be deleted using vi (but you can replace that with any other editor if you like), and then deletes those branches. If you get an error: The branch '...' is not fully merged. message, you can just add -D to the command and it will force-delete the branches you picked.

Upvotes: 5

Aleix Marin&#233;
Aleix Marin&#233;

Reputation: 305

I made this script condensing all the knowledge that I found in all the answers:

#!/usr/bin/env bash

bclean()
{
  if [ "$1" == "--interactive" ] || [ "$1" == "-i" ]; then
    true > /tmp/merged-branches

    # Append gone
    git fetch -p && \
    for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}'); do
       echo "${branch}" >> /tmp/merged-branches
    done

    # Append without remote
    IFS=$'\n'
    git fetch -p && \
    for line in $(git for-each-ref --format '%(refname) %(upstream)' refs/heads); do
      # If the second field is empty, it has no upstream
      if [ -z "$(echo "${line}" | cut -d " " -f2)" ]; then
        # Detect name and append it
        branch="$(echo "${line}" | cut -d " " -f1 | rev | cut -d "/" -f1 | rev)"
        if ! grep -q "${branch}" < /tmp/merged-branches; then
          echo "${branch}" >> /tmp/merged-branches
        fi
      fi
    done

    # Append merged
    current="$(git branch --show-current)"
    for line in $(git branch --merged | sed '/^\** *develop$/d' | sed '/^\** *master$/d' | sed "/^\** *${current}/d" ); do
      if ! grep -q "${branch}" < /tmp/merged-branches; then
        echo "${branch}" >> /tmp/merged-branches
      fi
    done

    if grep -q '[^[:space:]]' < "/tmp/merged-branches"; then
      nano "/tmp/merged-branches"
      xargs git branch -D < /tmp/merged-branches
    fi
    unset current

  else
    # Delete gone
    git fetch -p && \
    for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}'); do
      git branch -D "${branch}"
    done

    # Delete without remote
    IFS=$'\n'
    git fetch -p && \
    for line in $(git for-each-ref --format '%(refname) %(upstream)' refs/heads); do

      # If the second field is empty, it has no upstream
      if [ -z "$(echo "${line}" | cut -d " " -f2)" ]; then
        # Detect name and delete it
        git branch -D "$(echo "${line}" | cut -d " " -f1 | rev | cut -d "/" -f1 | rev)"
      fi
    done

    # delete merged
    current="$(git branch --show-current)"
    git branch --merged \
    | sed '/^\** *develop$/d' \
    | sed '/^\** *master$/d' \
    | sed "/^\** *${current}/d" >/tmp/merged-branches \
    && grep -q '[^[:space:]]' < "/tmp/merged-branches" \
    && xargs git branch -d </tmp/merged-branches
    unset current

  fi
}

alias git-branch-clean="bclean"

Explanation

This script deletes all branches that do not have a remote counterpart. It does not matter that is gone, merged, not pushed yet or whatever. It just deletes all local branches that are not on the server anymore. It also ignores the branches master and develop.

The function works in two different modes: interactive and non-interactive.

If you use bclean --interactive or bclean -i all the branches are appended to a temporary file and then you have the option to decide which branch you want to actually delete.

Instead, if you use bclean without arguments all branches that do not have a remote counterpart are deleted without further interaction.

This is tested and working in Ubuntu 20.04.2 and git 2.25.1.

This script is version managed in a bigger project and any mistakes, corrections or updates will be available through this repository. Specifically in this file.

How to use

If you want to add this script manually to your system you can add this text directly to your .bashrc to make the function bclean always available in your terminal.

If you want to add this script automatically to your system you can use the Linux-Auto-Customizer project where this script is included as a single installation. To do so, you have to type these instructions in your terminal:

git clone https://github.com/AleixMT/Linux-Auto-Customizer
cd Linux-Auto-Customizer
git checkout develop  # Not needed when we do next release
bash src/core/install.sh -v -o bclean
bash  # To reload .bashrc and make the function available in the current terminal. Equivalent to source ~/.bashrc

After that, the function bclean can be used in a terminal.

Upvotes: 0

CervEd
CervEd

Reputation: 4272

Delete all local references where the previously remote tracking branch is now gone

#!/bin/sh
LC_ALL=C git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads/ |\
    sed -n '/ \[gone\]$/{s@@@;p}' |\
    xargs -I% git branch --delete %
  1. LC_ALL=C, avoid problems with localization by setting language to English
  2. Match for-each-ref output ending with [gone] and take the refname
  3. Prune those local references (change --delete to -D to force)

No bashisms and should work in BSD/GNU

Upvotes: 2

Asaf
Asaf

Reputation: 1261

You can use this:

git fetch --prune

then

git branch -vv | egrep -v "([origin/[a-zA-Z0-9/_-]+])" | awk "{print $1}" | xargs git branch -D

It removes all the local branches that are not linked to the remote

and I personally added an alias that does it for me 🙃

alias gbs='git fetch --prune; git branch -vv | egrep -v "(\[origin\/[a-zA-Z0-9/_-]+\])" | awk "{print \$1}" | xargs git branch -D'

Upvotes: 21

CervEd
CervEd

Reputation: 4272

List all local branches who's remote branch is now gone

#!/bin/sh
LC_ALL=C git for-each-ref --format='%(refname:short) %(upstream:track)' refs/heads/ |\
    sed -n '/ *\[gone\].*$/{s@@@;p}'

and then pipe that into xargs git branch -D or -d.

This solution should be POSIX compatible. No shell specific stuff and POSIX compliant sed/xargs only.

  • LC_ALL=C makes the script language neutral by forcing english
  • for-each-ref use plumbing command
  • --format='%(refname:short) %(upstream:track) output branch name tracking information
  • refs/heads only local branches
  • sed -n '/ *\[gone\].*$/ match the tracking information [gone] which is not part of a valid branch name so no need to worry about that
    • {s@@@;p} delete that tracking part and only print those lines

nb: branches cannot contain whitespace so we don't need to do any sanitation for xargs.

Upvotes: 3

jason.rickman
jason.rickman

Reputation: 15221

The safest way to do this is to use the "plumbing" command git for-each-ref with the interpolation variable %(upstream:track), which will be [gone] when the branch is no longer on the remote:

git fetch -p && for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}'); do git branch -D $branch; done

This approach is somewhat safer than using the "porcelain" command, because there is no risk of accidentally matching on part of the commit message. Here is a version using the "porcelain" git commands:

git fetch -p && for branch in $(git branch -vv | grep ': gone]' | awk '{print $1}'); do git branch -D $branch; done

The way this works is that after the command

git fetch -p

removes the remote references, when you run

git branch -vv

it will show 'gone' as the remote status. For example,

$ git branch -vv
  master                 b900de9 [origin/master: behind 4] Fixed bug
  release/v3.8           fdd2f4e [origin/release/v3.8: behind 2] Fixed bug
  release/v3.9           0d680d0 [origin/release/v3.9: behind 2] Updated comments
  bug/1234               57379e4 [origin/bug/1234: gone] Fixed bug

This is what the scripts iterate over.

Upvotes: 1343

karlsebal
karlsebal

Reputation: 1761

Because some answers don’t prevent accidental deletion

git fetch -p && LANG=c git branch -vv | awk '/: gone]/&&!/^\*/{print $1}' | xargs git branch -d

filtering out the branch having * in the first column is important.

Upvotes: 14

Pascal H.
Pascal H.

Reputation: 1701

Yet Another Answer.

If you are looking for something fancy, here is the alias I use with fzf.

LANG=C git for-each-ref --sort=-committerdate --format '%(refname:short) %(upstream:track)' refs/heads \
  | awk '$2 == "[gone]" {print $1}' \
  | fzf --multi --bind ctrl-a:select-all,ctrl-t:toggle-all \
        --preview='git log --color=always --graph --format=compact {1}' \
        --header='Select [gone] branch to remove' \
        --header-first --reverse \
  | xargs --no-run-if-empty git branch -d

output example

Explanations:

  • Force LANG=C to avoid localization on [gone] pattern

  • Use git for-each-ref to be free from any format changes in git branch

  • Filter using awk. I tried to use --format="%(if:equals=[gone])%..." but it implies to manage blank lines.

  • Use fzf to select the branches to delete.

    Adds shortcuts: [ctrl-a] to select all, and [ctrl-t] to invert the selection (and [tab] to select as usual). You validate using [enter] and can abort using [esc]. I use this branch selector as a confirmation stage.

  • Display the selected branch's log on the right side (to remember the date and content...).

    I use a compact format listed below. You can use any (not too wide) log format.

  • --no-run-if-empty is only available with GNU xargs.

    It is not mandatory and helps to avoid errors when you abort the process (related to git branch -d without branch names).

The alias may look awful:

[alias]
      gone = "!f() { LANG=C git for-each-ref --sort=-committerdate --format '%(refname:short) %(upstream:track)' refs/heads | awk '$2 == \"[gone]\" {print $1}' | fzf --multi --bind ctrl-a:select-all,ctrl-t:toggle-all --preview='git log --color=always --graph --format=compact {1}' --header='Select [gone] branch to remove' --header-first --reverse | xargs --no-run-if-empty git branch -d ; }; f"
[pretty]
      compact = %Cred%h%Creset%C(auto)%d%Creset%n  Author: %an <%aE>%n  Date:  %cd%n     %s

(same as above with escapes on ")

Upvotes: 1

lzap
lzap

Reputation: 17173

Looks like everyone has a solution. Well, if you prefer an interactive tool with TUI (text-based user interface), I wrote a tool called git xcleaner. It can find merged branches, rebased branches (commits with the same commit message), pruned branches or manually selected branches.

https://github.com/lzap/git-xcleaner

Upvotes: 0

aegatlin
aegatlin

Reputation: 2061

git remote prune origin
git branch -d bug-fix-a bug-fix-b

The risk of using scripts to do this is addressed here: https://stackoverflow.com/a/47939403/4592031

Upvotes: 1

Occasionally you simply want to have a clean slate, but need to be certain to be able to go back to previous work in a local branch.

If so, the easiest might simply be archiving the current local clone and create a new fresh one.

So for repository X just with the default branch locally, simply

move X X-2021-11-10
git clone http://...../X

If you need the exact same origin, ask git

git -C X remote -v

Upvotes: 0

Cameron Porter
Cameron Porter

Reputation: 939

None of the answers here seemed to work for me or do exactly as I wanted (delete local branches not in remote). So I wrote this, it's a bit of a mouthful but it works.

touch /tmp/remote-refs && touch /tmp/local-refs && touch /tmp/diff-refs;
while shortened=$(git for-each-ref refs/remotes) && echo ${${shortened}//"\/remotes\/origin"} > /tmp/remote-refs &&
shortened=$(git for-each-ref refs/heads | grep -v "master\|develop") && echo ${${shortened}//"\/heads"} > /tmp/local-refs &&
diff=$(comm -13 /tmp/remote-refs /tmp/local-refs) && echo ${${diff}##*refs/} > /tmp/diff-refs &&
xargs git branch -D </tmp/diff-refs; do :; done

Just have to make sure to run git remote prune origin first

Upvotes: 1

lacostenycoder
lacostenycoder

Reputation: 11226

I recently posted a solution which I've been using for quite some time, which relies on branch naming to include issue numbers, you can see this answer which links to my version of git_clean_branches which is an interactive script to delete branches who's issues have been closed (normally by a squash merge).

Upvotes: 0

Pranav Kasetti
Pranav Kasetti

Reputation: 9935

While the above answers cover how to prune branches manually, this answer adds automation to solve this. git now has a new setting to prune stale branches that are no longer on the remote for every fetch action. This is great because we no longer have to manually call remote prune every time we delete branches (git pull also calls git fetch).

Enable prune behaviour for every fetch

To enable this in the global config:

git config --global fetch.prune true

Making the thing happen automatically means you can forget to add this setting on new machines. It just works.

Enable prune behaviour for every fetch on specific remotes

git config --global remote.<name>.prune true

Local automated pruning

We can apply the same command for local pruning as well without the --global flag.

.gitconfig

The commands above apply to the global and local .gitconfig as follows:

...
[fetch]
    prune = true

I can recommend adding this to an ansible configuration or to your dotfiles repository (.gitconfig) to automate the setup for the future.

The configuration setting calls the below command on every fetch:

git remote prune <remote name>

Summary

To prune references as part of your normal workflow without needing to remember to run that, set fetch.prune globally or remote.<name>.prune per-remote in the config. See git-config.

Upvotes: 29

koppor
koppor

Reputation: 20531

The real challenge is when the maintainer squashes the commits. Then, the solutions using git built-in functionality such as --merged does not help.

The tool git-delete-merged-branches allows for a convenient deletion of branches. I especially like the interactive mode.

Installation (requires python3):

pip install git-delete-merged-branches

Then execute

git-delete-merged-branches --effort=3

--effort=3 is important to enable deletion of squashed branches.

Alternatives

  • @teppeis/git-delete-squashed: With node.js installed execute npx @teppeis/git-delete-squashed. Supports main branch.
  • git-delete-squashed: Not maintained: Misses functionality for main branch. @teppeis/git-delete-squashed is based on this.

Upvotes: 2

Tom Pietrosanti
Tom Pietrosanti

Reputation: 4294

A simpler solution for Windows or others who don't want to/can't script the command line or who don't want to bother with PowerShell.

Dump the branch list into a file git branch > branches.txt
(or git branch --merged > branches.txt, if you're the belt and suspenders type; git branch -d will protect against deleting unmerged branches)

Open that file in your editor and combine all the lines (I used sublime text, so highlight all and press ctrl+j)

Add git branch -d ahead of your branch list.

Select all, copy, and paste (right click in windows cmd window) into the command line.

Upvotes: 6

Sven Dhaens
Sven Dhaens

Reputation: 1996

You could do this:

git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d

P.S.: as pointed out by Sam H.

execute this first:

git remote prune origin

Upvotes: 57

Pxtl
Pxtl

Reputation: 964

Powershell-based solution that I find more legible than many of the implementations here.

# prune deleted remoted branches
git fetch -p

# get all branches and their corresponding remote status
# deleted remotes will be marked [gone]
git branch -v |
  #find ones marked [gone], capture branchName
  select-string -Pattern '^  (?<branchName>\S+)\s+\w+ \[gone\]' | 
  foreach-object{ 
     #delete the captured branchname.
     git branch -D $_.Matches[0].Groups['branchName']
  }

Upvotes: 12

Laobe
Laobe

Reputation: 121

May be this command is what you want.

After run:

git remote prune origin

then run:

diff <(git branch | sed -e 's/*/ /g') <(git branch -r | sed -e 's/origin\///g') | grep '^<'

this will show all branch which not in (git branch -r) but in (git branch)

This method have a problem, it will also show the branch in local which have not pushed before

Upvotes: 4

Chandan Sharma
Chandan Sharma

Reputation: 2529

This Below Commands Solved My problem.

// To Delete Branch Locally
git branch -d localBranchName


// To Delete Branch Remotely
git push origin --delete remoteBranchName

Upvotes: -4

Himura
Himura

Reputation: 1817

Yet another answer, because none of the solutions suit my needs on elegance and cross-platformness:

Command to delete local branches not on remote:

for b in $(git for-each-ref --format='%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' refs/heads); do git branch -d $b; done

To integrate it with gitconfig so it can be run with git branch-prune:

Bash

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format='\''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'\'' refs/heads); do git branch -d $b; done'

PowerShell

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format=''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'' refs/heads); do git branch -d $b; done'

(Need help in finding a universal command for PowerShell and bash)

Why this answer is the best?

  • Offers a complete solution: adds a git branch-prune command to your git
  • Works fine from Windows PowerShell
  • The core idea is @jason.rickman's bulletproof method using git for-each-ref
  • Parsing and filtering is done with --filter so no external dependencies needed

Explanation:

  • Adds a new alias to your ~\.gitconfig. After executing this you can simply do git branch-prune
  • Inside this alias:
    • Fetches branches with the --prune flag, which "prunes remote-tracking branches no longer on remote"
    • Uses git for-each-ref and --filter, to get a list of the branches are [gone] (no remote)
    • Loops through this list and deletes the branch safely

Upvotes: 48

espaciomore
espaciomore

Reputation: 348

I like using pipes because it makes the command easier to read.

This is my solution if you would like to remove all branches except master.

git branch | grep -v master | xargs -n 1 git branch -D

To delete other branches that match your criteria, modify the first and second block.

git branch --merged | grep feature_name | xargs -n 1 git branch -D

Upvotes: 6

Noah Gary
Noah Gary

Reputation: 960

If you are using zsh shell with Oh My Zsh installed then the easiest way to do this safely is to use the built in autocomplete.

First determine which branches you want to delete with:

~ git branch --merged

  branch1
  branch2
  branch3
* master

this will show you a list of already merged branches

After you know a few you want to delete then type:

~ git branch -d 

All you have to do is hit [tab] and it will show you a list of local branches. Use tab-complete or just hit [tab] again and you can cycle through them to select a branch with [enter].

Tab Select the branches over and over again until you have a list of branches you wnat to delete:

~ git branch -d branch1 branch2 branch3

Now just press enter to delete your collection of branches.

If you aren't using zsh on your terminal... Get it here.

Upvotes: 0

bxm
bxm

Reputation: 524

Yet-another-answer for the pile, drawing heavily from Patrick's answer (which I like because it seems to do away with any ambiguity about where gone] will match in the git branch output) but adding a *nix bent.

In its simplest form:

git branch --list --format \
  "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" \
  | xargs git branch -D

I have this wrapped up in a git-gone script on my path:

#!/usr/bin/env bash

action() {
  ${DELETE} && xargs git branch -D || cat
}

get_gone() {
  git branch --list --format \
    "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)"
}

main() {
  DELETE=false
  while [ $# -gt 0 ] ; do
    case "${1}" in
      (-[dD] | --delete) DELETE=true ;;
    esac
    shift
  done
  get_gone | action
}

main "${@}"

NB - The --format option seems to be fairly new; I needed to upgrade git from 2.10.something to 2.16.3 to get it.

EDIT: tweaked to include suggestion about refname:short from Benjamin W.

NB2 - I've only tested in bash, hence the hashbang, but probably portable to sh.

Upvotes: 20

aubreypwd
aubreypwd

Reputation: 22939

git remote prune origin prunes tracking branches not on the remote.

git branch --merged lists branches that have been merged into the current branch.

xargs git branch -d deletes branches listed on standard input.

Be careful deleting branches listed by git branch --merged. The list could include master or other branches you'd prefer not to delete.

To give yourself the opportunity to edit the list before deleting branches, you could do the following in one line:

git branch --merged >/tmp/merged-branches && \
  vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches

Upvotes: 2228

noraj
noraj

Reputation: 4642

TL;DR:

Remove ALL local branches that are not on remote

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

Remove ALL local branches that are not on remote AND that are fully merged AND that are not used as said in many answers before.

git fetch -p && git branch --merged | grep -v '*' | grep -v 'master' | xargs git branch -d

Explanation

  • git fetch -p will prune all branches no longer existing on remote
  • git branch -vv will print local branches and pruned branch will be tagged with gone
  • grep ': gone]' selects only branch that are gone
  • awk '{print $1}' filter the output to display only the name of the branches
  • xargs git branch -D will loop over all lines (branches) and force remove this branch

Why git branch -D and not git branch -d else you will have for branches that are not fully merged.

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

Upvotes: 73

Here's the simple answer that worked for me using a git client:

Delete the repository altogether from your machine then check out again.

No mucking around with risky scripts.

Upvotes: 1

Related Questions