765

I work from two different computers (A and B) and store a common git remote in the dropbox directory.

Let's say I have two branches, master and devel. Both are tracking their remote counterparts origin/master and origin/devel.

Now while on computer A, I delete branch devel, on local and remote.

git push origin :heads/devel
git branch -d devel

Running git branch -a on computer A, I get the following list of branches.

  • master
  • origin/HEAD
  • origin/master

Running git fetch on computer B, I can remove the local devel branch with git branch -d devel, but I can't remove the remote devel branch.

git push origin :heads/devel returns the following error messages.

error: unable to push to unqualified destination: heads/proxy3d
The destination refspec neither matches an existing ref on the remote nor begins with refs/, and we are unable to guess a prefix based on the source ref.
fatal: The remote end hung up unexpectedly

git branch -a still lists origin/devel in the remote branches.

How can I clean up the remote branches from computer B?

kiamlaluno
  • 24,790
  • 16
  • 70
  • 85
Jayesh
  • 46,729
  • 19
  • 69
  • 96
  • 5
    I've been told by one who tried it, that git repositories in Dropbox folders are a bit fragile (but without additional details). – Thorbjørn Ravn Andersen Apr 19 '13 at 09:23
  • 5
    @ThorbjørnRavnAndersen probably because you have to wait to ensure it syncs completely whenever you commit, before you can be sure it's safe to use on the other machine (and another sync required even then). – ataulm Aug 01 '13 at 14:54

9 Answers9

1364

First, what is the result of git branch -a on machine B?

Second, you have already deleted heads/devel on origin, so that's why you can't delete it from machine B.

Try

git branch -r -d origin/devel

or

git remote prune origin

or

git fetch origin --prune

and feel free to add --dry-run to the end of your git statement to see the result of running it without actually running it.

Docs for git remote prune and git branch.

Engineero
  • 10,387
  • 3
  • 41
  • 65
Jakub Narębski
  • 268,805
  • 58
  • 209
  • 228
  • 5
    `git branch -r -d origin/devel` worked for me. thanks. now that i read manpage of `git remote prune`, I guess that should work too. Will try it next time. – Jayesh Jul 07 '10 at 06:35
  • 60
    `git remote prune origin` worked for me => `* [pruned] origin/my-old-branch` – Hari Honor May 19 '12 at 16:28
  • Thank you. I got the following message "The destination refspec neither matches an existing ref on the remote nor..." when I tried to delete a remote branch. Then I used `git branch -r -d origin/devel` and the branch was deleted – Maksim Dmitriev Feb 08 '13 at 01:00
  • If you use `SourceTree` git client, check "Prune tracking branches no longer present on remote(s)" box when you fetch. – Jayesh Mar 20 '13 at 14:48
  • 133
    `git remote prune origin --dry-run` shows you what would be deleted w/o actually doing it. – Wolfram Arnold May 28 '13 at 15:53
  • 38
    `git fetch origin --prune` was perfect to remove deleted branches – Sébastien Barbieri Nov 22 '13 at 16:28
  • 10
    If you have a local branch tracking a remote that's gone, this won't delete anything. For those, it appears `git branch -vv` followed by `git branch -D branchname` and finally the prune is the best way. – Roman Starkov Mar 31 '14 at 09:42
  • 8
    You can configure pruning to happen automatically on pull/fetch by setting the following option: `git config remote.origin.prune true` – Patrick James McDougle Nov 07 '15 at 20:33
  • Or `gfa` with the oh-my-zsh git plugin: https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/git/git.plugin.zsh – Robin Winslow Apr 12 '16 at 13:43
  • **git fetch -p && for branch in `git branch -vv --no-color | grep ': gone]' | awk '{print $1}'`; do git branch -D $branch; done** I don't know what is does. But it worked for me. – Lal Krishna Jan 22 '19 at 12:40
  • I go with `git pull -p` because it's easier to remember :P – Jalal Jun 03 '20 at 16:35
  • 1
    to fetch + cleanup old branches from all remotes, use `git fetch --prune --all` (takes a while to run if there are too many remotes / branches) – y2k-shubham Apr 16 '21 at 12:28
  • Exactly what I needed. I kept just using prune without thinking about the remote :/ – Jacob May 03 '21 at 19:51
  • Actually the better option is `git fetch --prune` since it will merely delete local branches that have their remote counterparts deleted. `git fetch prune --all` on the other hand also pulls new branches from all remotes (likely not what you'd expect while *pruning*) – y2k-shubham May 06 '21 at 04:34
124

Consider to run :

git fetch --prune

On a regular basis in each repo to remove local branches that have been tracking a remote branch that is deleted (no longer exists in remote GIT repo).

This can be further simplified by

git config remote.origin.prune true

this is a per-repo setting that will make any future git fetch or git pull to automatically prune.

To set this up for your user, you may also edit the global .gitconfig and add

[fetch]
    prune = true

However, it's recommended that this is done using the following command:

git config --global fetch.prune true

or to apply it system wide (not just for the user)

git config --system fetch.prune true
SEoF
  • 809
  • 9
  • 24
Arif
  • 4,274
  • 3
  • 40
  • 64
14

This command will "dry run" delete all remote (origin) merged branches, apart from master. You can change that, or, add additional branches after master: grep -v for-example-your-branch-here |

git branch -r --merged | 
  grep origin | 
  grep -v '>' | 
  grep -v master | 
  xargs -L1 | 
  awk '{sub(/origin\//,"");print}'| 
  xargs git push origin --delete --dry-run

If it looks good, remove the --dry-run. Additionally, you may like to test this on a fork first.

ken
  • 3,413
  • 1
  • 25
  • 37
weston
  • 51,132
  • 20
  • 132
  • 192
  • Nice. My tweak using perl instead of the 1st xargs + awk: git branch -r --merged | grep origin | grep -v '>' | grep -v master | perl -lpe '($junk, $_) = split(/\//, $_,2)' | xargs git push origin --delete – Andrew Oct 28 '16 at 21:06
14

Here is bash script that can do it for you. It's modified version of http://snippets.freerobby.com/post/491644841/remove-merged-branches-in-git script. My modification enables it to support different remote locations.

#!/bin/bash

current_branch=$(git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')
if [ "$current_branch" != "master" ]; then
  echo "WARNING: You are on branch $current_branch, NOT master."
fi
echo -e "Fetching merged branches...\n"

git remote update --prune
remote_branches=$(git branch -r --merged | grep -v '/master$' | grep -v "/$current_branch$")
local_branches=$(git branch --merged | grep -v 'master$' | grep -v "$current_branch$")
if [ -z "$remote_branches" ] && [ -z "$local_branches" ]; then
  echo "No existing branches have been merged into $current_branch."
else
  echo "This will remove the following branches:"
  if [ -n "$remote_branches" ]; then
echo "$remote_branches"
  fi
  if [ -n "$local_branches" ]; then
echo "$local_branches"
  fi
  read -p "Continue? (y/n): " -n 1 choice
  echo
  if [ "$choice" == "y" ] || [ "$choice" == "Y" ]; then
    remotes=`echo "$remote_branches" | sed 's/\(.*\)\/\(.*\)/\1/g' | sort -u`
# Remove remote branches
for remote in $remotes
do
        branches=`echo "$remote_branches" | grep "$remote/" | sed 's/\(.*\)\/\(.*\)/:\2 /g' | tr -d '\n'`
        git push $remote $branches 
done

# Remove local branches
git branch -d `git branch --merged | grep -v 'master$' | grep -v "$current_branch$" | sed 's/origin\///g' | tr -d '\n'`
  else
echo "No branches removed."
  fi
fi
Alex Amiryan
  • 1,326
  • 1
  • 15
  • 30
8

If git branch -r shows a lot of remote-tracking branches that you're not interested in and you want to remove them only from local, use the following command:

git branch -r | grep -Ev 'HEAD|master|develop'  | xargs -r git branch -rd

A safer version would be to only remove the merged ones:

git branch -r --merged | grep -Ev 'HEAD|master|develop'  | xargs -r git branch -rd

This might be useful for large projects, where you don't need the feature branches of other teammates but there're lots of remote-tracking branches fetched upon the initial clone.

However, this step alone is not sufficient, because those deleted remote-tracking branches would come back upon next git fetch.

To stop fetching those remote-tracking branches you need to explicitly specify the refs to fetch in .git/config:

[remote "origin"]
  # fetch = +refs/heads/*:refs/remotes/origin/*    ### don't fetch everything
  fetch = +refs/heads/master:refs/remotes/origin/master
  fetch = +refs/heads/develop:refs/remotes/origin/develop
  fetch = +refs/heads/release/*:refs/remotes/origin/release/*

In the above example we only fetch master, develop and release branches, feel free to adapt as you need.

ryenus
  • 12,350
  • 4
  • 51
  • 56
  • 1
    Thanks, first command worked for me. You can also fetch only one branch with `git fetch origin branchname` or create an alias like `ft = "!f() { git fetch origin $(git rev-parse --abbrev-ref HEAD);}; f"` to fetch only the current branch (my personal favorite). Cheers. – GiovanyMoreno May 31 '18 at 21:17
  • Maybe OT but what does the `-r` flag means? I see `xargs` has a `-R` argument but didn't find anything about `-r`. I mean, in theory if I remember correctly how `xargs` works, even without `-r` it should work. – Aldo 'xoen' Giambelluca Jan 11 '19 at 12:17
  • I like this answer. A couple of notes (stating the obvious really). It's possible to "preview" which branches are going to be deleted by removing the last pipe and last command `| xargs git branch -d`. Also, by removing the `-r` option (`--remotes`) from `git branch -d` we only clean the local branches (which may be enough considering that by default `git branch` doesn't show remote branches anyway). – Aldo 'xoen' Giambelluca Jan 11 '19 at 12:18
  • Thanks! This was exactly what i needed. Other solutions suggest pushing pruned local branches to the remote to remove them there as well, but that doesn't help when the remote has been removed or no longer exists. This approach removed the local references to the remote branches i no longer have access to. – cautionbug Sep 16 '19 at 22:14
  • @aldo, about `xargs -r`, namely `--no-run-if-empty`, it's a GNU extension means , [here's a nice explanation](https://unix.stackexchange.com/a/521599/17823/). – ryenus Sep 17 '19 at 01:59
6

Deletion is always a challenging task and can be dangerous!!! Therefore, first execute the following command to see what will happen:

git push --all --prune --dry-run

By doing so like the above, git will provide you with a list of what would happen if the below command is executed.

Then run the following command to remove all branches from the remote repo that are not in your local repo:

git push --all --prune
Can
  • 3,914
  • 6
  • 25
  • 41
Timothy L.J. Stewart
  • 1,054
  • 13
  • 12
  • 11
    This command appears to be dangerous... It managed to delete what I wanted (and could not do with at least four of the answers above). But it also deleted four other dev-branches. Git absolutely sucks... – jww Sep 30 '16 at 05:42
  • 3
    Those branches must have not been on your local. However all is not lost. Git commit, Git reflog and then git reset --hard . You can literally to any commit(aka save) and others with this. Branch is just a label, deleting the label does not delete the save... it will forever have a checksum. Let me know if I can help – Timothy L.J. Stewart Sep 30 '16 at 11:48
  • 4
    Of course, you'll want to recover these pretty quickly since git's garbage collector will eventually remove commits not referenced by branches. – Andrew Oct 28 '16 at 20:46
  • 1
    It's good to add `-n` (dry run) to this command so you can preview changes and then if everything is fine call it again without `-n`. – mati865 Nov 11 '16 at 17:11
  • This is bad advice. ^^ – Gui Weinmann Apr 04 '17 at 17:41
  • 1
    Agree with @GuiWeinmann - This is too dangerous, especially without a specific callout to at least run this in dry-run first. – Vivek M. Chawla Oct 23 '17 at 09:24
  • 1
    Executing --dry-run first is the way to go. That you will visualize what will happen. This is the command I was looking for! Tried many different commands and GUI processes and none worked, but this one. Thanks, @TimothyL.J.Stewart – tupan Jul 27 '18 at 14:00
5

I'll have to add an answer here, because the other answers are either not covering my case or are needlessly complicated. I use github with other developers and I just want all the local branches whose remotes were (possibly merged and) deleted from a github PR to be deleted in one go from my machine. No, things like git branch -r --merged don't cover the branches that were not merged locally, or the ones that were not merged at all (abandoned) etc, so a different solution is needed.

Anyway, the first step I got it from other answers:

git fetch --prune

A dry run of git remote prune origin seemed like it would do the same thing in my case, so I went with the shortest version to keep it simple.

Now, a git branch -v should mark the branches whose remotes are deleted as [gone]. Therefore, all I need to do is:

git branch -v|grep \\[gone\\]|awk '{print $1}'|xargs -I{} git branch -D {}

As simple as that, it deletes everything I want for the above scenario.

The less common xargs syntax is so that it also works on Mac & BSD in addition to Linux. Careful, this command is not a dry run so it will force-delete all the branches marked as [gone]. Obviously, this being git nothing is gone forever, if you see branches deleted that you remember you wanted kept you can always undelete them (the above command will have listed their hash on deletion, so a simple git checkout -b <branch> <hash>.

Edit: Just add this alias to your .bashrc/.bash_profile, the two commands made into one and I updated the second to work on all shells:

alias old_branch_delete='git fetch -p && git branch -vv | awk "/: gone]/{print \$1}" | xargs git branch -D'
Ecuador
  • 766
  • 4
  • 22
2

Here is how to do it with SourceTree (v2.3.1):
1. Click Fetch
2. Check "Prune tracking branches ..."
3. Press OK
4.

enter image description here

coco
  • 4,650
  • 2
  • 23
  • 31
0
# First use prune --dry-run to filter+delete the local branches
git remote prune origin --dry-run \
  | grep origin/ \
  | sed 's,.*origin/,,g' \
  | xargs git branch -D

# Second delete the remote refs without --dry-run
git remote prune origin

Prune the same branches from local- and remote-refs(in my example from origin).