filesynced.t: Skip tests if `~/bin/synced.sql` doesn't exist
[sunny256-utils.git] / git-dbr
blob702adc7bb6a48ab4c80cd316a96e080ed1420c1b
1 #!/usr/bin/env perl
3 #=======================================================================
4 # git-dbr
5 # File ID: fd27c966-f4ed-11e4-bb56-000df06acc56
7 # Delete local and remote branches from local and remote Git
8 # repositories.
10 # Character set: UTF-8
11 # ©opyleft 2015– Øyvind A. Holm <sunny@sunbase.org>
12 # License: GNU General Public License version 2 or later, see end of
13 # file for legal stuff.
14 #=======================================================================
16 use strict;
17 use warnings;
18 use Getopt::Long;
20 local $| = 1;
22 our %Opt = (
24 'dry-run' => 0,
25 'help' => 0,
26 'quiet' => 0,
27 'verbose' => 0,
28 'version' => 0,
32 our $progname = $0;
33 $progname =~ s/^.*\/(.*?)$/$1/;
34 our $VERSION = '0.2.1';
36 Getopt::Long::Configure('bundling');
37 GetOptions(
39 'dry-run|n' => \$Opt{'dry-run'},
40 'help|h' => \$Opt{'help'},
41 'quiet|q+' => \$Opt{'quiet'},
42 'verbose|v+' => \$Opt{'verbose'},
43 'version' => \$Opt{'version'},
45 ) || die("$progname: Option error. Use -h for help.\n");
47 $Opt{'verbose'} -= $Opt{'quiet'};
48 $Opt{'help'} && usage(0);
49 if ($Opt{'version'}) {
50 print_version();
51 exit(0);
54 my @remotes = split("\n", `git remote`);
55 my @local_branches = format_array(split("\n", `git branch`));
56 my @remote_branches = format_array(split("\n", `git branch -r`));
58 exit(main());
60 sub main {
61 # {{{
62 my $Retval = 0;
64 for my $arg (@ARGV) {
65 # Check if it's a local branch
66 if (!delete_branch($arg)) {
67 # No, try to strip trailing comma
68 my $exp = $arg;
69 $exp =~ s/,$//;
70 if (!delete_branch($exp)) {
71 # Try to remove "remotes/" prefix
72 $exp = $arg;
73 $exp =~ s/^remotes\///;
74 if (!delete_branch($exp)) {
75 msg(0, "$arg: Could not delete branch");
80 return $Retval;
81 # }}}
82 } # main()
84 sub delete_branch {
85 # Delete local or remote branch. Return 1 if ok, 0 if branch not found {{{
86 my $arg = shift;
87 my $grep_arg = $arg;
88 my $retval = 0;
89 $grep_arg = quotemeta($arg);
90 if (grep(/^$grep_arg$/, @local_branches)) {
91 # $arg is a local branch
92 mysystem("git", "branch", "-D", $arg);
93 $retval = 1;
94 } elsif (grep(/^$grep_arg$/, @remote_branches)) {
95 # $arg is a remote branch
96 if ($arg =~ /^(\S+?)\/(\S+)/) {
97 my ($remote, $branch) = ($1, $2);
98 if (grep(/^$remote$/, @remotes)) {
99 # Text before first slash is a remote name
100 mysystem("git", "push", $remote, ":$branch");
101 $retval = 1;
105 return($retval);
106 # }}}
107 } # delete_branch()
109 sub format_array {
110 # Strip initial two characters from array elements {{{
111 my @initial = @_;
112 my @retval = ();
113 for my $loop (@initial) {
114 my $curr = $loop;
115 $curr =~ s/^..(.+)$/$1/;
116 push(@retval, $curr);
118 return(sort @retval);
119 # }}}
120 } # format_array()
122 sub check_sig {
123 # {{{
124 my $retval = shift;
125 ($retval & 127) &&
126 die("\n$progname: Child process interrupted, aborting.\n");
127 return(0);
128 # }}}
129 } # check_sig()
131 sub mysystem {
132 # {{{
133 my @cmd = @_;
134 msg(0, sprintf("%s '", $Opt{'dry-run'} ? "Simulating" : "Executing") .
135 join(" ", @cmd) . "'...");
136 $? = 0;
137 !$Opt{'dry-run'} && system(@cmd) && check_sig($?);
138 return $?;
139 # }}}
140 } # mysystem()
142 sub print_version {
143 # Print program version {{{
144 print("$progname $VERSION\n");
145 return;
146 # }}}
147 } # print_version()
149 sub usage {
150 # Send the help message to stdout {{{
151 my $Retval = shift;
153 if ($Opt{'verbose'}) {
154 print("\n");
155 print_version();
157 print(<<"END");
159 Delete local and remote branches from local and remote Git repositories.
160 Also removes trailing comma to make it easier to copy+paste from %d
161 output used with "git log --format".
163 Usage: $progname [options] [remote/]branch[,] [[remote/]branch[,] [...]]
165 Options:
167 -h, --help
168 Show this help.
169 -n, --dry-run
170 Simulate what would happen, don't actually do anything.
171 -q, --quiet
172 Be more quiet. Can be repeated to increase silence.
173 -v, --verbose
174 Increase level of verbosity. Can be repeated.
175 --version
176 Print version information.
178 Examples:
180 git dbr gitlab/oldbranch oldbranch
181 Delete oldbranch from the gitlab remote and also delete it if it
182 exists locally.
183 git dbr gitlab/oldbranch, oldbranch,
184 Same as above. This is copy+paste from the %d output created by "git
185 log --format". The comma is removed internally before the branches
186 are deleted.
187 git branch -a | grep oldbranch | xargs git dbr
188 git dbr \$(git branch -a | grep oldbranch)
189 These two examples works identically, it's just a matter of
190 preference how to write it. Delete all branches locally and from all
191 known remotes where the string "oldbranch" occurs.
194 exit($Retval);
195 # }}}
196 } # usage()
198 sub msg {
199 # Print a status message to stderr based on verbosity level {{{
200 my ($verbose_level, $Txt) = @_;
202 if ($Opt{'verbose'} >= $verbose_level) {
203 print(STDERR "$progname: $Txt\n");
205 return;
206 # }}}
207 } # msg()
209 __END__
211 # This program is free software; you can redistribute it and/or modify
212 # it under the terms of the GNU General Public License as published by
213 # the Free Software Foundation; either version 2 of the License, or (at
214 # your option) any later version.
216 # This program is distributed in the hope that it will be useful, but
217 # WITHOUT ANY WARRANTY; without even the implied warranty of
218 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
219 # See the GNU General Public License for more details.
221 # You should have received a copy of the GNU General Public License
222 # along with this program.
223 # If not, see L<http://www.gnu.org/licenses/>.
225 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :