{"thread":{"id":"7291","subject":"[wishlist] git branch -d -r remotename","startedAt":"2007-03-18T09:36:04Z","lastAt":"2007-03-19T23:37:01Z","messageCount":8,"participants":["Sam Vilain","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"37362","messageId":"45FD0804.6010401@vilain.net","threadId":"7291","inReplyTo":null,"subject":"[wishlist] git branch -d -r remotename","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2007-03-18T09:36:04Z","receivedAt":"2007-03-18T09:36:04Z","isPatch":false,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"I'm finding myself wanting to delete all local branch heads that are\nalready present on a remote.\n\nYou wouldn't want it to be too eager about it, and certainly you\nwouldn't want to use it against any server that isn't considered\ncompletely \"upstream\" of the repository you're doing it on, otherwise if\nboth ends run it and then \"git remote prune\" the refs will go away.\n\nI think either \"git remote prune\" or \"git branch -d\" would be a good\nplace for that.\n\nThoughts?\n\nSam.\n"},{"id":"37368","messageId":"20070318110507.5701413A382@magnus.utsl.gen.nz","threadId":"7291","inReplyTo":"45FD0804.6010401@vilain.net","subject":"Re: [wishlist] git branch -d -r remotename","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2007-03-18T11:01:33Z","receivedAt":"2007-03-18T11:01:33Z","isPatch":false,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"> I'm finding myself wanting to delete all local branch heads that are\n> already present on a remote.\n\nSomething like this.\n\nSubject: [PATCH] git-remote: implement prune -c\n\nIt would be nice to prune local refs which are irrelevant; add an\noption to git-remote prune, with documentation.\n\nSigned-off-by: Sam Vilain <sam@vilain.net>\n---\n Documentation/git-remote.txt |    4 ++++\n git-remote.perl              |   33 +++++++++++++++++++++++++++++++--\n 2 files changed, 35 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex ab04b86..7381b08 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -63,6 +63,10 @@ referenced by <name>, but are still locally available in\n With `-n` option, the remote heads are not confirmed first with `git\n ls-remote <name>`; cached information is used instead.  Use with\n caution.\n++\n+With `-c` option, all *local* branches that can be found via this\n+remote are removed.  Useful for cleaning up old style remote branches,\n+or temporary branches.\n \n 'fetch'::\n \ndiff --git a/git-remote.perl b/git-remote.perl\nindex 20f9f54..e74bce2 100755\n--- a/git-remote.perl\n+++ b/git-remote.perl\n@@ -210,7 +210,7 @@ sub show_mapping {\n }\n \n sub prune_remote {\n-\tmy ($name, $ls_remote) = @_;\n+\tmy ($name, $ls_remote, $clean_local) = @_;\n \tif (!exists $remote->{$name}) {\n \t\tprint STDERR \"No such remote $name\\n\";\n \t\treturn;\n@@ -224,6 +224,32 @@ sub prune_remote {\n \t\tmy @v = $git->command(qw(rev-parse --verify), \"$prefix/$to_prune\");\n \t\t$git->command(qw(update-ref -d), \"$prefix/$to_prune\", $v[0]);\n \t}\n+\tif ( $clean_local ) {\n+\t\tmy @refs = map { (split \" \")[0] }\n+\t\t\t$git->command(qw(for-each-ref), $prefix);\n+\t\tprint \"Saw \".@refs.\" remote refs\\n\";\n+\t\tmy %r = map { ($_ => undef) }\n+\t\t\t$git->command(\"rev-list\", @refs);\n+\t\tprint \"Saw \".(keys %r).\" revs\\n\";\n+\t\tmy %l = map { (split \" \")[2,0] }\n+\t\t\t$git->command(qw(for-each-ref refs/heads));\n+\t\tprint \"Saw \".(keys %l).\" local heads\\n\";\n+\n+\t\t# don't delete the current branch, but there must be a\n+\t\t# better way to find it out\n+\t\tchomp(my ($checked_out) = map { /^\\* (.*)/ }\n+\t\t\t$git->command(\"branch\"));\n+\t\t$checked_out = \"refs/heads/$checked_out\"\n+\t\t    if $checked_out;\n+\n+\t\twhile ( my ($ref, $rev) = each %l ) {\n+\t\t\tnext if $checked_out and $ref eq $checked_out;\n+\t\t\tif ( exists $r{$rev} ) {\n+\t\t\t\tprint \"$ref is obsolete\\n\";\n+\t\t\t\t$git->command(qw(update-ref -d), $ref, $rev);\n+\t\t\t}\n+\t\t}\n+\t}\n }\n \n sub show_remote {\n@@ -344,6 +370,9 @@ elsif ($ARGV[0] eq 'prune') {\n \t\tif ($ARGV[$i] eq '-n') {\n \t\t\t$ls_remote = 0;\n \t\t}\n+\t\telsif ($ARGV[$i] eq '-c') {\n+\t\t\t$clean_local = 1;\n+\t\t}\n \t\telse {\n \t\t\tlast;\n \t\t}\n@@ -353,7 +382,7 @@ elsif ($ARGV[0] eq 'prune') {\n \t\texit(1);\n \t}\n \tfor (; $i < @ARGV; $i++) {\n-\t\tprune_remote($ARGV[$i], $ls_remote);\n+\t\tprune_remote($ARGV[$i], $ls_remote, $clean_local);\n \t}\n }\n elsif ($ARGV[0] eq 'add') {\n-- \n1.5.0.4.210.gf8a7c-dirty\n"},{"id":"37370","messageId":"20070318113210.5843E13A382@magnus.utsl.gen.nz","threadId":"7291","inReplyTo":"20070318110507.5701413A382@magnus.utsl.gen.nz","subject":"Re: [wishlist] git branch -d -r remotename","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2007-03-18T11:01:33Z","receivedAt":"2007-03-18T11:01:33Z","isPatch":false,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"One time again, this time with the call to porcelain command\n`git-branch` replaced with the plumbing command `symbolic-ref HEAD`\n\nI also changed the output to be a little less \"Got here\\n\" style.\n\nSubject: [PATCH] git-remote: implement prune -c\n\nIt would be nice to prune local refs which are irrelevant; add an\noption to git-remote prune, with documentation.\n\nSigned-off-by: Sam Vilain <sam@vilain.net>\n---\n Documentation/git-remote.txt |    4 ++++\n git-remote.perl              |   29 +++++++++++++++++++++++++++--\n 2 files changed, 31 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex ab04b86..7381b08 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -63,6 +63,10 @@ referenced by <name>, but are still locally available in\n With `-n` option, the remote heads are not confirmed first with `git\n ls-remote <name>`; cached information is used instead.  Use with\n caution.\n++\n+With `-c` option, all *local* branches that can be found via this\n+remote are removed.  Useful for cleaning up old style remote branches,\n+or temporary branches.\n \n 'fetch'::\n \ndiff --git a/git-remote.perl b/git-remote.perl\nindex 20f9f54..0ed8a77 100755\n--- a/git-remote.perl\n+++ b/git-remote.perl\n@@ -210,7 +210,7 @@ sub show_mapping {\n }\n \n sub prune_remote {\n-\tmy ($name, $ls_remote) = @_;\n+\tmy ($name, $ls_remote, $clean_local) = @_;\n \tif (!exists $remote->{$name}) {\n \t\tprint STDERR \"No such remote $name\\n\";\n \t\treturn;\n@@ -224,6 +224,28 @@ sub prune_remote {\n \t\tmy @v = $git->command(qw(rev-parse --verify), \"$prefix/$to_prune\");\n \t\t$git->command(qw(update-ref -d), \"$prefix/$to_prune\", $v[0]);\n \t}\n+\tif ( $clean_local ) {\n+\t\tmy @refs = map { (split \" \")[0] }\n+\t\t\t$git->command(qw(for-each-ref), $prefix);\n+\t\tprint \"fetching revs for \".@refs.\" remote refs\\n\";\n+\t\tmy %r = map { ($_ => undef) }\n+\t\t\t$git->command(\"rev-list\", @refs);\n+\t\tprint \"fetched \".(keys %r).\" revs\\n\";\n+\t\tmy %l = map { (split \" \")[2,0] }\n+\t\t\t$git->command(qw(for-each-ref refs/heads));\n+\t\tprint \"checking \".(keys %l).\" local heads\\n\";\n+\n+\t\t# don't delete the current branch\n+\t\tmy ($checked_out) = $git->command(qw(symbolic-ref HEAD));\n+\n+\t\twhile ( my ($ref, $rev) = each %l ) {\n+\t\t\tnext if $checked_out and $ref eq $checked_out;\n+\t\t\tif ( exists $r{$rev} ) {\n+\t\t\t\tprint \"$ref is obsolete\\n\";\n+\t\t\t\t$git->command(qw(update-ref -d), $ref, $rev);\n+\t\t\t}\n+\t\t}\n+\t}\n }\n \n sub show_remote {\n@@ -344,6 +366,9 @@ elsif ($ARGV[0] eq 'prune') {\n \t\tif ($ARGV[$i] eq '-n') {\n \t\t\t$ls_remote = 0;\n \t\t}\n+\t\telsif ($ARGV[$i] eq '-c') {\n+\t\t\t$clean_local = 1;\n+\t\t}\n \t\telse {\n \t\t\tlast;\n \t\t}\n@@ -353,7 +378,7 @@ elsif ($ARGV[0] eq 'prune') {\n \t\texit(1);\n \t}\n \tfor (; $i < @ARGV; $i++) {\n-\t\tprune_remote($ARGV[$i], $ls_remote);\n+\t\tprune_remote($ARGV[$i], $ls_remote, $clean_local);\n \t}\n }\n elsif ($ARGV[0] eq 'add') {\n-- \n1.5.0.4.210.gf8a7c-dirty\n"},{"id":"37390","messageId":"7vvegyl4nt.fsf@assigned-by-dhcp.cox.net","threadId":"7291","inReplyTo":"20070318113210.5843E13A382@magnus.utsl.gen.nz","subject":"Re: [wishlist] git branch -d -r remotename","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-03-18T19:42:30Z","receivedAt":"2007-03-18T19:42:30Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Sam Vilain <sam@vilain.net> writes:\n\n> One time again, this time with the call to porcelain command\n> `git-branch` replaced with the plumbing command `symbolic-ref HEAD`\n>\n> I also changed the output to be a little less \"Got here\\n\" style.\n>\n> Subject: [PATCH] git-remote: implement prune -c\n>\n> It would be nice to prune local refs which are irrelevant; add an\n> option to git-remote prune, with documentation.\n\nI do not understand what workflow you are assuming, so your use\nof the word \"irrelevant\" does not mean much to me.  I suspect\nother readers of the patch and documentation wouldn't find it\nclear in what situation this option is useful.\n\nPerhaps you are thinking about this scenario?  I am only\nguessing because you are not clear enough:\n\n\t$ git clone\n        ... time passes ...\n        $ git checkout -b next origin/next\n        ... build, install, have fun ...\n\t$ git checkout master\n        ... time passes ...\n        $ git branch\n        ... notice that you do not hack on your copy of 'next'\n        ... and want to remove it\n\t$ git remote prune -c\n\nIn any case, are you checking irrelevancy?  What if your foo branch has\nmore changes to be sent upstream?  Even when the remote has a\nbit older version doesn't your code remove yours?  For example,\nif you did this, instead of the above, what happens?\n\n\t$ git clone\n        ... time passes ...\n        $ git checkout -b next origin/next\n        ... build, install, have fun ...\n\t... find an opportunity to improve ...\n        $ edit\n        $ git commit ;# on your 'next'.\n        ... build, install, test ...\n\t$ git checkout master\n        ... time passes ...\n        $ git branch\n        ... notice that you do not hack on your copy of 'next' anymore,\n        ... and want to remove it\n\t$ git remote prune -c\n\nIf the above is the usage scenario you are trying to help, then\nwouldn't it be helpful if you could also help removing 'my-next'\nin this slightly altered example?\n\n\t$ git clone\n        ... time passes ...\n        $ git checkout -b my-next origin/next\n        ... build, install, have fun ...\n\t$ git checkout master\n        ... time passes ...\n        $ git branch\n        ... notice that you do not hack on your copy of 'next'\n        ... which is 'my-next', and want to remove it\n\t$ git remote prune -c\n"},{"id":"37405","messageId":"45FDB322.10904@vilain.net","threadId":"7291","inReplyTo":"7vvegyl4nt.fsf@assigned-by-dhcp.cox.net","subject":"Re: [wishlist] git branch -d -r remotename","fromName":"Sam Vilain","fromEmail":"sam@vilain.net","sentAt":"2007-03-18T21:46:10Z","receivedAt":"2007-03-18T21:46:10Z","isPatch":false,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"Junio C Hamano wrote:\n>> Subject: [PATCH] git-remote: implement prune -c\n>>\n>> It would be nice to prune local refs which are irrelevant; add an\n>> option to git-remote prune, with documentation.\n>>     \n>\n> I do not understand what workflow you are assuming, so your use\n> of the word \"irrelevant\" does not mean much to me.  I suspect\n> other readers of the patch and documentation wouldn't find it\n> clear in what situation this option is useful.\n>   \n\nBad choice of words. What I mean is to delete all local refs which are\nalready reachable by a remote ref on the given remote.\n\n> Perhaps you are thinking about this scenario?  I am only\n> guessing because you are not clear enough:\n>\n> \t$ git clone\n>         ... time passes ...\n>         $ git checkout -b next origin/next\n>         ... build, install, have fun ...\n> \t$ git checkout master\n>         ... time passes ...\n>         $ git branch\n>         ... notice that you do not hack on your copy of 'next'\n>         ... and want to remove it\n> \t$ git remote prune -c\n>   \n\nYes, that's it. Or clean up the references you already pushed because\nthey are no longer of interest.\n\n> In any case, are you checking irrelevancy?  What if your foo branch has\n> more changes to be sent upstream?  Even when the remote has a\n> bit older version doesn't your code remove yours?  For example,\n> if you did this, instead of the above, what happens?\n>\n> \t$ git clone\n>         ... time passes ...\n>         $ git checkout -b next origin/next\n>         ... build, install, have fun ...\n> \t... find an opportunity to improve ...\n>         $ edit\n>         $ git commit ;# on your 'next'.\n>         ... build, install, test ...\n> \t$ git checkout master\n>         ... time passes ...\n>         $ git branch\n>         ... notice that you do not hack on your copy of 'next' anymore,\n>         ... and want to remove it\n> \t$ git remote prune -c\n>   \n\nIt doesn't do that because the head doesn't match any revision that was\ngiven to us by `rev-list refs/remotes/foo/*`\n\n> If the above is the usage scenario you are trying to help, then\n> wouldn't it be helpful if you could also help removing 'my-next'\n> in this slightly altered example?\n>\n> \t$ git clone\n>         ... time passes ...\n>         $ git checkout -b my-next origin/next\n>         ... build, install, have fun ...\n> \t$ git checkout master\n>         ... time passes ...\n>         $ git branch\n>         ... notice that you do not hack on your copy of 'next'\n>         ... which is 'my-next', and want to remove it\n> \t$ git remote prune -c\n\nYes, the idea was to \"sweep\" all branches that were just local branches\nof a remote and never worked on. This is most useful right now for\npeople switching from Cogito or old-style remotes, who have a lot of\nbranches that are remote tracking branches. Using this, they can just\nset up a new remote, fetch and prune -c and be left in a tidy state.\n\nSam.\n"},{"id":"37472","messageId":"7v8xdtkb7p.fsf@assigned-by-dhcp.cox.net","threadId":"7291","inReplyTo":"45FDB322.10904@vilain.net","subject":"Re: [wishlist] git branch -d -r remotename","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-03-19T06:18:34Z","receivedAt":"2007-03-19T06:18:34Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Sam Vilain <sam@vilain.net> writes:\n\n> It doesn't do that because the head doesn't match any revision that was\n> given to us by `rev-list refs/remotes/foo/*`\n\nGaah.  That goes all the way down to the root commit.\n\n+\t\tprint \"fetching revs for \".@refs.\" remote refs\\n\";\n\nIs this meant to be final message to the end-user, or debug?\n\n+\t\tmy %r = map { ($_ => undef) }\n+\t\t\t$git->command(\"rev-list\", @refs);\n\nThis traverses all the way down to the root commit, doesn't it?\n\nThat is probably good enough for a toy repository for testing,\nbut is impractical in real repositories, I am afraid.  See below\nfor the standard ways to check ancestry.\n\n+\t\t# don't delete the current branch\n+\t\tmy ($checked_out) = $git->command(qw(symbolic-ref HEAD));\n\nWhen the HEAD is detached, does the error message go directly to\nthe end user?\n\n+\t\twhile ( my ($ref, $rev) = each %l ) {\n+\t\t\tnext if $checked_out and $ref eq $checked_out;\n+\t\t\tif ( exists $r{$rev} ) {\n+\t\t\t\tprint \"$ref is obsolete\\n\";\n+\t\t\t\t$git->command(qw(update-ref -d), $ref, $rev);\n+\t\t\t}\n+\t\t}\n \nThe standard way to check if commit A is included in (i.e. is an\nancestor of) commit B, without traversing the ancestry chain of\nB all the way down to the root commit, is to run:\n\n\tgit merge-base --all A B\n\nand see if A appears in its output (if so, then A is an ancestor\nof B, otherwise it is not).  This is a pair-wise check, and for\nyour purpose the check would become N*M operation (Yuck).\n\nThe same check can be done in parallel with:\n\n\tgit show-branch --independent A B C D...\n\nwhose output would include A if the commit is not included in\nany of the other commits B C D...  This parallel traversal has a\nlimit --- you can only check 25 branches at a time.\n\n>> If the above is the usage scenario you are trying to help, then\n>> wouldn't it be helpful if you could also help removing 'my-next'\n>> in this slightly altered example?\n>>\n>> \t$ git clone\n>>         ... time passes ...\n>>         $ git checkout -b my-next origin/next\n>>         ... build, install, have fun ...\n>> \t$ git checkout master\n>>         ... time passes ...\n>>         $ git branch\n>>         ... notice that you do not hack on your copy of 'next'\n>>         ... which is 'my-next', and want to remove it\n>> \t$ git remote prune -c\n>\n> Yes, the idea was to \"sweep\" all branches that were just local branches\n> of a remote and never worked on. This is most useful right now for\n> people switching from Cogito or old-style remotes, who have a lot of\n> branches that are remote tracking branches. Using this, they can just\n> set up a new remote, fetch and prune -c and be left in a tidy state.\n\nYou make it sound like this is just a one-shot conversion issue,\nin which case I really doubt we would want that.  But it appears\nto be a useful feature in general, provided if the assumed use\ncase is described clearly so that new users know when to use it.\nIn the form that was given to me, I think the documentation\nleaves the user in a \"Huh?\" state.\n"},{"id":"37474","messageId":"7v4pohka7g.fsf@assigned-by-dhcp.cox.net","threadId":"7291","inReplyTo":"7v8xdtkb7p.fsf@assigned-by-dhcp.cox.net","subject":"Re: [wishlist] git branch -d -r remotename","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-03-19T06:40:19Z","receivedAt":"2007-03-19T06:40:19Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n> The standard way to check if commit A is included in (i.e. is an\n> ancestor of) commit B, without traversing the ancestry chain of\n> B all the way down to the root commit, is to run:\n>\n> \tgit merge-base --all A B\n>\n> and see if A appears in its output (if so, then A is an ancestor\n> of B, otherwise it is not).  This is a pair-wise check, and for\n> your purpose the check would become N*M operation (Yuck).\n>\n> The same check can be done in parallel with:\n>\n> \tgit show-branch --independent A B C D...\n>\n> whose output would include A if the commit is not included in\n> any of the other commits B C D...  This parallel traversal has a\n> limit --- you can only check 25 branches at a time.\n\nWell, I was silly.  If you want to see if A is an ancestor of\nany of B C D..., the standard and most efficient way to do so is\nwith rev-list.\n\n\tgit rev-list A --not B C D...\n\nwill show _nothing_ only when A is an ancestor of one (or more)\nof B C D..., so you invoke it and upon getting the first line of\noutput you declare A cannot be removed without reading the\nremainder of the output.\n\nshow-branch --independent is an overkill for your purpose as it\ndoes not treat A any more special from others (iow, it checks if\nB is contained in A C D..., C is contained in A B D... all in\nparallel), and you are not interested in finding out how remote\nrefs are related with each other.\n"},{"id":"37562","messageId":"20070319234722.CF1FF13A382@magnus.utsl.gen.nz","threadId":"7291","inReplyTo":"7v4pohka7g.fsf@assigned-by-dhcp.cox.net","subject":"(unknown)","fromName":"Sam Vilain","fromEmail":"sam.vilain@catalyst.net.nz","sentAt":"2007-03-19T23:37:01Z","receivedAt":"2007-03-19T23:37:01Z","isPatch":false,"sender":{"key":"sam@vilain.net","avatar":"https://gravatar.com/avatar/8fc840ca854dbf6f7065b4335e3b934951c1dca3b11db688e95e471901f8f4a8?d=mp&s=160"},"body":"Junio C Hamano wrote:\n> If you want to see if A is an ancestor of\n> any of B C D..., the standard and most efficient way to do so is\n> with rev-list.\n>\n> \tgit rev-list A --not B C D...\n>\n> will show _nothing_ only when A is an ancestor of one (or more)\n> of B C D..., so you invoke it and upon getting the first line of\n> output you declare A cannot be removed without reading the\n> remainder of the output.\n\nSure.  I figured the commands I put would only use about 30MB for a\n300,000 commit repository, so it wouldn't be too bad.  But I didn't\nthink of that.\n\nHere we go, also based upon 'next' rather than the result of my\nprevious patches of the day.\n\nSubject: [PATCH] git-remote: implement prune -c\n\nIt would be nice to be able to prune local refs which just point to\nsome place in the remote; add an option to git-remote prune, with\ndocumentation.\n\nSigned-off-by: Sam Vilain <sam.vilain@catalyst.net.nz>\n---\n Documentation/git-remote.txt |    5 +++++\n git-remote.perl              |   25 +++++++++++++++++++++++--\n 2 files changed, 28 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex f96b304..6a28e6c 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -54,6 +54,11 @@ Gives some information about the remote <name>.\n Deletes all stale tracking branches under <name>.\n These stale branches have already been removed from the remote repository\n referenced by <name>, but are still locally available in \"remotes/<name>\".\n++\n+With `-c` option, all *local* branches that exist in the history of\n+any of the mirrored heads from the remote are removed.  This is useful\n+for discarding temporary local branches that you did not make any\n+commits to.\n \n \n DISCUSSION\ndiff --git a/git-remote.perl b/git-remote.perl\nindex 670bafb..84ac534 100755\n--- a/git-remote.perl\n+++ b/git-remote.perl\n@@ -210,7 +210,7 @@ sub show_mapping {\n }\n \n sub prune_remote {\n-\tmy ($name, $ls_remote) = @_;\n+\tmy ($name, $ls_remote, $clean_local) = @_;\n \tif (!exists $remote->{$name}) {\n \t\tprint STDERR \"No such remote $name\\n\";\n \t\treturn;\n@@ -224,6 +224,24 @@ sub prune_remote {\n \t\tmy @v = $git->command(qw(rev-parse --verify), \"$prefix/$to_prune\");\n \t\t$git->command(qw(update-ref -d), \"$prefix/$to_prune\", $v[0]);\n \t}\n+\tif ( $clean_local ) {\n+\t\tmy @remote = map { (split \" \")[0] }\n+\t\t\t$git->command(qw(for-each-ref), $prefix);\n+\t\tmy %local = map { (split \" \")[2,0] }\n+\t\t\t$git->command(qw(for-each-ref refs/heads));\n+\t\tmy %not_remote = map { ($_ => undef) }\n+\t\t\t$git->command(\"rev-list\", keys %local, \"--not\", @remote);\n+\t\t# don't delete the current branch\n+\t\tmy ($checked_out) = $git->command(qw(symbolic-ref HEAD));\n+\n+\t\twhile ( my ($ref, $rev) = each %local ) {\n+\t\t\tnext if $checked_out and $ref eq $checked_out;\n+\t\t\tif ( not exists $not_remote{$rev} ) {\n+\t\t\t\tprint \"$ref is obsolete\\n\";\n+\t\t\t\t$git->command(qw(update-ref -d), $ref, $rev);\n+\t\t\t}\n+\t\t}\n+\t}\n }\n \n sub show_remote {\n@@ -310,6 +328,9 @@ elsif ($ARGV[0] eq 'prune') {\n \t\tif ($ARGV[$i] eq '-n') {\n \t\t\t$ls_remote = 0;\n \t\t}\n+\t\telsif ($ARGV[$i] eq '-c') {\n+\t\t\t$clean_local = 1;\n+\t\t}\n \t\telse {\n \t\t\tlast;\n \t\t}\n@@ -319,7 +340,7 @@ elsif ($ARGV[0] eq 'prune') {\n \t\texit(1);\n \t}\n \tfor (; $i < @ARGV; $i++) {\n-\t\tprune_remote($ARGV[$i], $ls_remote);\n+\t\tprune_remote($ARGV[$i], $ls_remote, $clean_local);\n \t}\n }\n elsif ($ARGV[0] eq 'add') {\n-- \n1.5.0.2.214.gb318\n"}]}