Created
February 19, 2025 14:39
-
-
Save andlrc/1e9bc0e30c530f1a02a3e73139b62574 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env perl | |
| # Author: Andreas Louv <[email protected]> | |
| # Date: Sep 19 2023 | |
| use strict; | |
| use warnings; | |
| use Getopt::Long qw(:config no_ignore_case bundling); | |
| use Pod::Usage; | |
| my $FFLAG_CONFIG_KEY = "gc-branch.requireForce"; | |
| my $nflag = 0; | |
| my $fflag = qx{ git config $FFLAG_CONFIG_KEY } =~ m{ false }x; | |
| my $iflag = 0; | |
| my $qflag = 0; | |
| sub main | |
| { | |
| GetOptions( | |
| "h|help" => sub { pod2usage(1); }, | |
| "man" => sub { pod2usage(-exitval => 0, -verbose => 2); }, | |
| "n|dry-run!" => \$nflag, | |
| "f|force!" => \$fflag, | |
| "i|Interactive!" => \$iflag, | |
| "q|quiet!" => \$qflag, | |
| ) or pod2usage(2); | |
| if (!$nflag && !$fflag && !$iflag) { | |
| print STDERR "fatal: $FFLAG_CONFIG_KEY defaults to true and neither -i, -n, nor -f given; refusing to delete\n"; | |
| exit 128 | |
| } | |
| my @local_branches = get_local_branches(); | |
| for my $branch (@local_branches) { | |
| next unless $branch->{gone}; | |
| my $local_branch = $branch->{local_branch}; | |
| chomp(my $latest_commit_subject = qx{ git log -1 --format=%s $local_branch }); | |
| if ($nflag) { | |
| print "Would delete branch $local_branch: $latest_commit_subject\n" unless $qflag; | |
| next; | |
| } | |
| if ($iflag) { | |
| print "Delete branch $local_branch: $latest_commit_subject [y/N/q]: "; | |
| my $answer = <>; | |
| exit if $answer =~ m{^ (?: q | quit ) $}msxi; | |
| next unless $answer =~ m{^ (?: y | yes ) $}msxi; | |
| } | |
| my $output = qx{ git branch -D $local_branch }; | |
| print $output unless $qflag; | |
| } | |
| } | |
| sub get_local_branches | |
| { | |
| my @branches; | |
| open(my $gone_fh, "-|", "git branch -vv"); | |
| while (<$gone_fh>) { | |
| next unless m{ | |
| ^ | |
| .. # "* " indices current branch, otherwise " " | |
| (?<local_branch>\S+) # local branch | |
| \s+ | |
| (?<sha>[A-Fa-f0-9]+) # short sha | |
| \s+ | |
| \[ # "[" | |
| (?<remote_branch>\S+) # remote branch | |
| (?<gone>:\sgone)? # the text ": gone" is present the branch has been deleted on the remote | |
| \] # "]" | |
| \s+ | |
| (?<commit_subject>.*) # commit subject | |
| $ | |
| }xms; | |
| push(@branches, { | |
| local_branch => $+{local_branch}, | |
| sha => $+{sha}, | |
| remote_branch => $+{remote_branch}, | |
| gone => defined $+{gone} && $+{gone} eq ": gone", | |
| commit_subject => $+{commit_subjcet}, | |
| }) | |
| } | |
| close($gone_fh); | |
| return @branches; | |
| } | |
| main(); | |
| __END__ | |
| =head1 NAME | |
| git gc-branch Delete remote closed branches | |
| =head1 SYNOPSIS | |
| git gc-branch [-q] [-f] [-i] [-n] | |
| Options: | |
| -q, --quiet do not print names of files removed | |
| -i, --interactive enable interactive removal | |
| -n, --dry-run dry run | |
| -f, --force force | |
| =head1 OPTIONS | |
| =over 8 | |
| =item B<-f>, B<--force> | |
| If the Git configuration variable gc-branch.requireForce is not set to false, | |
| B<git> B<gc-branch> will refuse to delete branches given -f. | |
| =item B<-i>, B<--interactive> | |
| Show what would be done and remove branches interactively. | |
| =item B<-n>, B<--dry-run> | |
| Don't actually remove anything, just show what would be done. | |
| =item B<-q>, B<--quiet> | |
| Be quiet, only report errors, but not the files that are successfully removed. | |
| =back | |
| =head1 DESCRIPTION | |
| Deletes all local branches that are reported in the commit messages to be closed. | |
| =cut |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Does
(?<gone>:\sgone)? # the text ": gone" is present the branch has been deleted on the remotemean that some commit message in that branch has to contain the word
gonefor it to be deleted? If so, is that a (custom) convention ?