166

I want to have my local and remote repositories always in sync in terms of branches.

After a Pull Request review on GitHub, I merge and remove my branch there (remote). How could I fetch this information in my local repository and get Git to remove my local version of the branch as well?

sf89
  • 4,570
  • 7
  • 20
  • 27
  • Do you want to delete your remote tracking branches, local branches, or both? You can actually write an alias (bash or git) that will take all of the deleted remote branches, and find local copies to delete too, all in one command. –  Aug 01 '13 at 00:50
  • Maybe try using the following commands to come up with something, [`git ls-remote`](https://www.kernel.org/pub/software/scm/git/docs/git-ls-remote.html) and [`git show-ref`](https://www.kernel.org/pub/software/scm/git/docs/git-show-ref.html). –  Aug 01 '13 at 02:38
  • Also, you might want to check out [`git symbolic-ref`](https://www.kernel.org/pub/software/scm/git/docs/git-symbolic-ref.html) and [`git update-ref`](https://www.kernel.org/pub/software/scm/git/docs/git-update-ref.html). –  Aug 01 '13 at 02:49
  • thanks for your help, I ended up finding the answer somewhere else. See my response. – sf89 Aug 01 '13 at 07:30
  • possible duplicate of [How can I delete all git branches which have been merged?](http://stackoverflow.com/questions/6127328/how-can-i-delete-all-git-branches-which-have-been-merged) – Andrew C Nov 07 '14 at 15:20
  • Related: [Delete a Git branch both locally and remotely](http://stackoverflow.com/q/2003505/456814). –  Oct 18 '15 at 01:31
  • 3
    Possible duplicate of [Remove branches no longer on remote](http://stackoverflow.com/questions/7726949/remove-branches-no-longer-on-remote) – amaechler Mar 01 '16 at 22:41

12 Answers12

185

The quick way

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

NB: if you're not on master, this has the potential to delete the branch. Keep reading for the "better way".

Make sure we keep master

You can ensure that master, or any other branch for that matter, doesn't get removed by greping for more. In that case you would go:

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

So if we wanted to keep master, develop and staging for instance, we would go:

git branch --merged | grep -v "\*" | grep -Ev "(\*|master|develop|staging)" | xargs -n 1 git branch -d

Make this an alias

Since it's a bit long, you might want to add an alias to your .zshrc or .bashrc. Mine is called gbpurge (for git branches purge):

alias gbpurge='git branch --merged | grep -Ev "(\*|master|develop|staging)" | xargs -n 1 git branch -d'

Then reload your .bashrc or .zshrc:

. ~/.bashrc

or

. ~/.zshrc
sf89
  • 4,570
  • 7
  • 20
  • 27
  • You can put the commands in an alias and make it a single command. However, since `branch` is a [porcelain and not a plumbing command](https://www.kernel.org/pub/software/scm/git/docs/), watch out for any UI changes in future versions of Git that may break it. –  Aug 01 '13 at 12:43
  • 1
    Perfect! Note that following the Github Workflow the local branch `master` will be deleted. – Rubens Mariuzzo Feb 24 '14 at 01:13
  • Nope pretty sure it stays there (I'm using it everyday and it doesn't seem to do that). – sf89 May 08 '14 at 02:00
  • I copy-pasted the command into my console and it deleted my local master branch – Chad Jun 06 '14 at 21:35
  • Yup if your master is not pushed to your remote then it gets deleted like any other branch. Make sure you read carefully the question before you copy and paste anything: as I said, I needed a way to get rid of deleted branches on the remote (branches that no longer existed). That's why your master got blown up buddy. – sf89 Jun 11 '14 at 19:04
  • If you want to keep your master you and just add an additional grep of `grep -v "master"` after the first one. You can follow that pattern with any other branch you might want to keep. – catalyst294 Jul 15 '15 at 19:57
  • 4
    FYI if you want to keep multiple branches you can use a single grep, like so: `grep -Ev '(\*|master|important-branch)'` – Andrew Burns Nov 12 '15 at 15:06
  • 4
    If you want to put this in your `~/.gitconfig` instead, add the following to the `[alias]` section: `gbpurge = !"git branch --merged | grep -Ev '\\*|master|develop|staging' | xargs -n 1 git branch -d"` (no need to use () in the grep expression). – dskrvk Mar 10 '16 at 16:11
  • it would be nice if you could automatically make it so you'd keep any branches whose remote equivalent haven't been deleted; that way you don't have to keep track of master|develop|staging|etc – Attila Szeremi Mar 31 '16 at 17:26
  • More bad advice on Stack Overflow... You just whacked Master with ***The quick way***. Perhaps you should have stated that the ***The quick way*** deletes master even if its not deleted on remote... – jww Jun 07 '16 at 04:13
  • BE CAREFUL if you're a newbie. Adding a git alias and using it in other repositories requires extra care. Not every git repository would have "master|develop|staging" as main branches. I personally don't recommend this method. – Alexar Jun 13 '17 at 02:11
91

I use the same flow with GitHub, and didn't find the previous answers satisfying me, as git branch --merged lists branches which were merged, but not every of them was removed remotely in my case. So, this worked for me:

git fetch --all -p; git branch -vv | grep ": gone]" | awk '{ print $1 }' | xargs -n 1 git branch -d

where:

  • git fetch --all -p: update local branches status
  • git branch -vv: list local branches status
  • grep ": gone]": filter deleted ones
  • awk '{ print $1 }': extract their names
  • xargs -n 1 git branch -d: pass the name to the delete command

Note: if you prefer, you could use -D instead of -d, which enforces the delete.

For example:

someUsr@someHost:~/repo$ git branch -a
basic-testing
integration-for-tests
* master
origin
playground-for-tests
test-services
remotes/origin/HEAD -> origin/master
remotes/origin/basic-testing
remotes/origin/master
remotes/origin/test-services

someUsr@someHost:~/repo$ git fetch --all -p; git branch -vv | grep ": gone]" | awk '{ print $1 }' | xargs -n 1 git branch -d
Fetching origin
Deleted branch integration-for-tests (was fbc609a).
Deleted branch playground-for-tests (was 584b900).

someUsr@someHost:~/repo$ git branch -a
basic-testing
* master
origin
test-services
remotes/origin/HEAD -> origin/master
remotes/origin/basic-testing
remotes/origin/master
remotes/origin/test-services

Reference:

http://git-scm.com/book/en/v2/Git-Branching-Remote-Branches

Alessio
  • 2,696
  • 13
  • 16
  • 4
    I took the liberty to ensure that I'll always do that against master, therefore: `git checkout master; git pull origin master; git fetch --all -p; git branch -vv | grep gone | awk '{ print $1 }' | xargs -n 1 git branch -d` Great script and explanation, thank you for that :) – Miguelgraz Feb 26 '15 at 13:43
  • Note that `branch -vv` shows the last commit message from the branch. If you happened to have "gone" in that message the `grep gone` would hit that branch too. So, `grep ": gone]"` is probably a little safer to use. – chawkinsuf Apr 30 '15 at 18:29
  • 1
    This is the actual answer to the question. Thank you. – Andrei Gladkyi Sep 05 '15 at 10:22
  • I'd add one more thing - make to switch to most relevant branch you have. Otherwise, you will get notices: `error: The branch 'feature/SHOP-498' is not fully merged. If you are sure you want to delete it, run 'git branch -D feature/SHOP-498'.` – profuel Nov 13 '15 at 10:35
  • @profuel I'm not so sure if I get your comment right, whereas you say "most relevant branch". That type of output is exactly the warning I want to see, as it happens for local branches which I didn't push fully - so for me for example they contain some work in progress which is so experimental that is not even worth to be pushed. So either you push it, then it will get deleted, or you delete it manually, but the warning is needed as you've some unpushed commits into it. – Alessio Nov 16 '15 at 06:02
  • How about getting rid of grep completely in favor of `git branch -vv | awk '{ if ($4 == "gone]") print $1 }'` -- one less pipe and it's slightly more exact as it only checks for `gone]` in specific column – Jakub Bochenski Aug 31 '16 at 12:37
  • 3
    Even better: `awk '$3 $4 ~ /:gone]$/ { print $1 }'` – Jakub Bochenski Aug 31 '16 at 12:46
  • 4
    Apart from needing `-D` instead of `-d` this is the perfect answer! – Cas Sep 22 '17 at 15:48
  • @Cas thanks to point that out! I will add a note about it, but given the --force option of -D, I would let the others to choose what's best for them :) – Alessio Sep 25 '17 at 06:34
  • thank you, this worked for me where the other commands using `--merged` didn't! – A.Wan Dec 08 '20 at 22:48
  • @A.Wan you're welcome, I still use this very same nowadays :) – Alessio Dec 09 '20 at 19:45
  • 1
    This worked perfectly, except for on a particular shell where my user is not given access to `xargs`. For anyone else in that situation, you can pipe to a while loop instead of `xargs`, like so: `...awk '{ print $1 }' | while read -r line; do git branch -D $line; done` – Michael Murphy Apr 16 '21 at 08:58
74

try:

git pull --prune

which deletes your local branch, if its corresponding remote branch is deleted.

Updated:

The statement above is not that correct.

In fact, running git pull --prune will only REMOVE the remote-tracking branches such like

remotes/origin/fff
remotes/origin/dev
remotes/origin/master

Then, you can run git branch -r to check the remote-tracking branches left on your machine. Suppose the left branches are:

origin/dev
origin/master

which means the branch origin/fff is deleted.

So, after running git pull --prune, just run:

git branch --merged | grep -vFf <(git branch -r | cut -d'/' -f2-)

you can find out all the local branches which:

  1. have no correspoding remote branches any more;
  2. can be removed safely.

then, <the command above> | xargs git branch -d can delete all of them.

Community
  • 1
  • 1
nicky_zs
  • 3,176
  • 1
  • 16
  • 25
  • 44
    This answer is not quite correct. The `--prune` flag will only delete remote-tracking branches, not local branches. –  Aug 01 '13 at 00:49
  • 3
    Agreed with @Cupcake here, this doesn't achieve what I'm looking for here. – sf89 Aug 01 '13 at 01:42
  • 6
    Not going to upvote, but this is what I needed after having deleted local branches and then deleting from GitHub but them still existing as remotes in my git remote -v command. – Spechal Feb 02 '14 at 21:36
  • is there really a way to delete local branch for which remote tracking branch is deleted ? – shashwat Nov 26 '14 at 19:02
  • This is exactly the answer I was looking for. The one-liner piped commands that delete branches scare me a bit. This is a nice safe option for routine cleanup. – Matt Montag Jul 02 '15 at 09:39
  • 8
    You can also do `git fetch --prune`, that's my way of choice – e_m0ney Aug 18 '15 at 07:27
  • 1
    Yet another Git error from advice found on Stack overflow... ***`git pull --prune`*** resulted in ***"You asked to pull from the remote '--prune', but did not specify a branch. Because this is not the default configured remote for your current branch, you must specify a branch on the command line."*** – jww Jun 07 '16 at 04:10
  • `git fetch origin --prune` may help in prev. comments case – PapaKai Jun 24 '16 at 07:55
23

This should work to avoid deleting the master and development branches with the accepted solution:

git branch --merged | egrep -v "^\*|master|development" | xargs -n 1 git branch -d
Alison R.
  • 3,994
  • 25
  • 32
Intrepid
  • 331
  • 2
  • 3
16

For people using powershell, this is the equivalent to the answer above:

git branch -vv | Select-String -Pattern ': gone]' | ForEach-Object{($_ -split "\s+")[1]} | %{ git branch -D $_ }
  1. Filter all the branches that are marked as gone
  2. Call git branch -D on each of the found branches
Community
  • 1
  • 1
amaechler
  • 785
  • 8
  • 17
6

None of this was working for me. You can see my other answer here: https://stackoverflow.com/a/34969726/550454

But essentially, I now have this in my ~/.gitconfig:

[alias]
  prune-branches = !git remote prune origin && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -d
Community
  • 1
  • 1
Karl Wilbur
  • 4,826
  • 2
  • 36
  • 45
5

Very simple solution: remove your local repo and clone the remote one anew. May not seem very elegant, but it is simple and you'll understand exactly what you're doing without reading man pages :-).

  • 1
    Why so many downvotes? I mean obviously not efficient, especially with larger repos, but it does what OP asked. Is there some other reason not to do this? – 3ocene Jan 04 '16 at 23:14
  • 7
    Because you'll lose all your local branches, stashes, unpushed commits... it's like fishing with dynamite. – sevenseacat Feb 13 '17 at 05:14
  • 1
    The same happens when the laptop you are working on somehow gets corrupted, lost, or stolen, so I tend not to keep nothing crucial locally. It seems better to me to just create a branch and push it, even for small features, and delete it after it is not useful anymore. –  Mar 10 '17 at 21:11
2

I just do that to remove merged local branches:

git branch -d $(git branch --merged)

and in case you want to remove inexistent trackings too:

git pull --prune
alacambra
  • 458
  • 5
  • 12
2

In the event that you've just pushed and merged your branch to master, then do the following in git bash:

git branch -d branch_name_to_delete

If you're currently in that branch it will push you back to master. At this point do a pull with

git pull
Joshua Schlichting
  • 2,361
  • 3
  • 23
  • 38
1

I've written this one-liner to list all local branches which do not have corresponding remote branch:

diff -u <(git branch|sed 's/..//') <(git branch -r|sed 's/..origin\///')|tail -n +4|sed -n "s/^-//p" -

After this done, deleting these local branches is easy with xargs:

diff -u <(git branch|sed 's/..//') <(git branch -r|sed 's/..origin\///')|tail -n +4|sed -n "s/^-//p" -|xargs -r git branch -d
ks1322
  • 29,461
  • 12
  • 91
  • 140
0

I had the same question and I could solve it with a simple command line:

git branch -d $(git branch --merged | grep -v "master")

First of all, do a git pull origin to make sure it will work fine.

The subshell first returns all branches that were merged to your current branch (make sure you have master branch checked out!!!), except for master which can be on the list if you don't use grep -v. Then, all merged branches will be simply removed from your local repo by git branch -d <branch_1> <branch_n>

gpaulini
  • 153
  • 1
  • 9
-2

The voted answer does have the potential to delete master. Consdier the below practical example.

I had two feature branches hemen_README and hemen_BASEBOX which were merged into develop, and then develop was merged into master. The feature branches hemen_README and hemen_BASEBOX were deleted remotely but were still showing up locally. Also i am not on master locally, but on develop.

In that case

    hemen@hemen-MXC061:~/data/projects/vagrant-webdev$ git branch -v -a
    * develop                      671ad6c Merged in hemen_README (pull request #1)
        hemen_BASEBOX                a535c0f added global exec paths to puppet manifest
        hemen_README                 ba87489 Updated Readme with considerable details
        master                       8980894 [behind 7] Initial Vagrantfile, works for vagrant up. Also initial .gitignore
        remotes/origin/develop       671ad6c Merged in hemen_README (pull request #1)
        remotes/origin/hemen_BASEBOX a535c0f added global exec paths to puppet manifest
        remotes/origin/hemen_README  ba87489 Updated Readme with considerable details
        remotes/origin/master        2f093ce Merged in develop (pull request #3)

So if i run the above partial command

    hemen@hemen-MXC061:~/data/projects/vagrant-webdev$ git branch --merged | grep -v "\*"
        hemen_BASEBOX
        hemen_README
        master

Notice that it shows master too, which will eventually be deleted.

In any case I was able to do it. I am sharing my session log with you on how I achieved that.

    hemen@hemen-MXC061:~/data/projects/vagrant-webdev$ git remote prune origin --dry-run
    Pruning origin
    URL: git@bitbucket.org:hemenkapadiapublic/vagrant-webdev.git
     * [would prune] origin/hemen_BASEBOX
     * [would prune] origin/hemen_README
    hemen@hemen-MXC061:~/data/projects/vagrant-webdev$ git remote prune origin 
    Pruning origin
    URL: git@bitbucket.org:hemenkapadiapublic/vagrant-webdev.git
     * [pruned] origin/hemen_BASEBOX
     * [pruned] origin/hemen_README

I just checked whay will be pruned and then pruned it. looking at branch command below we have taken care of remotes

    hemen@hemen-MXC061:~/data/projects/vagrant-webdev$ git branch -v -a
    * develop                671ad6c Merged in hemen_README (pull request #1)
        hemen_BASEBOX          a535c0f added global exec paths to puppet manifest
        hemen_README           ba87489 Updated Readme with considerable details
        master                 8980894 [behind 7] Initial Vagrantfile, works for vagrant up. Also initial .gitignore
        remotes/origin/develop 671ad6c Merged in hemen_README (pull request #1)
        remotes/origin/master  2f093ce Merged in develop (pull request #3)

Now go ahead and delete local branches

    hemen@hemen-MXC061:~/data/projects/vagrant-webdev$ git branch -d hemen_BASEBOX 
    Deleted branch hemen_BASEBOX (was a535c0f).
    hemen@hemen-MXC061:~/data/projects/vagrant-webdev$ git branch -d hemen_README
    Deleted branch hemen_README (was ba87489).

Good now the branches are as desired.

    hemen@hemen-MXC061:~/data/projects/vagrant-webdev$ git branch -v -a
    * develop                671ad6c Merged in hemen_README (pull request #1)
        master                 8980894 [behind 7] Initial Vagrantfile, works for vagrant up. Also initial .gitignore
        remotes/origin/develop 671ad6c Merged in hemen_README (pull request #1)
        remotes/origin/master  2f093ce Merged in develop (pull request #3)
  • Of course it has the potential of deleting master. Please read the question carefully. As I said there, I needed a way to clean things up on my local. That means deleting all the branches that no longer exist on remote. If master is no longer there, then it will disappear on your local machine as well. – sf89 Jun 11 '14 at 19:07