std.h: Don't use `extern` in function prototypes
[sunny256-utils.git] / git-pa
blob9f6691d3d604f336888babcae714b8be47d0f98e
1 #!/usr/bin/env perl
3 #=======================================================================
4 # git-pa
5 # File ID: ee782996-282c-11e2-9c05-00c0a8deee11
7 # Push to all predefined Git remotes in one go.
9 # Character set: UTF-8
10 # ©opyleft 2012– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License version 2 or later, see end of
12 # file for legal stuff.
13 #=======================================================================
15 use strict;
16 use warnings;
17 use Getopt::Long;
19 local $| = 1;
21 our %Std = (
23 'regexpfile' => "$ENV{'HOME'}/.git-pa-remotes",
26 our %Opt = (
28 'all-remotes' => 0,
29 'dry-run' => 0,
30 'force' => 0,
31 'help' => 0,
32 'quiet' => 0,
33 'regexpfile' => $Std{'regexpfile'},
34 'synced-only' => 0,
35 'verbose' => 0,
36 'version' => 0,
40 our $progname = $0;
41 $progname =~ s/^.*\/(.*?)$/$1/;
42 our $VERSION = '0.6.0';
44 Getopt::Long::Configure('bundling');
45 GetOptions(
47 'all-remotes|a' => \$Opt{'all-remotes'},
48 'dry-run|n' => \$Opt{'dry-run'},
49 'force|f' => \$Opt{'force'},
50 'help|h' => \$Opt{'help'},
51 'quiet|q+' => \$Opt{'quiet'},
52 'regexpfile=s' => \$Opt{'regexpfile'},
53 'synced-only|s' => \$Opt{'synced-only'},
54 'verbose|v+' => \$Opt{'verbose'},
55 'version' => \$Opt{'version'},
57 ) || die("$progname: Option error. Use -h for help.\n");
59 $Opt{'verbose'} -= $Opt{'quiet'};
60 $Opt{'help'} && usage(0);
61 if ($Opt{'version'}) {
62 print_version();
63 exit(0);
66 exit(main());
68 sub main {
69 # {{{
70 my $Retval = 0;
72 $Opt{'dry-run'} && msg(0, "Note: This is just a simulation.");
74 if ($Opt{'synced-only'}) {
75 update_synced();
76 return 0;
79 =pod
81 For every remote
82 For every regexp in ~/.git-pa-remotes
83 List remotes | grep ^remote | grep current regexp
84 Push with args
85 If the remote is specified in git-pa.allremotes
86 Push all
87 Push tags
89 =cut
91 my @pushremotes = read_regexpfile($Opt{'regexpfile'});
93 my @cmdline_args = @ARGV;
94 msg(2, "cmdline_args = '" . join('|', @cmdline_args) . "'");
95 update_synced();
96 my $config_allremotes = `git config --get git-pa.allremotes`;
97 chomp($config_allremotes);
98 my @allremotes_elem = split(' ', $config_allremotes);
99 msg(1, 'Will use --all and --tags with the following remotes: ' .
100 join(', ', @allremotes_elem));
102 my %allremotes = ();
103 for (@allremotes_elem) {
104 $allremotes{$_} = 1;
107 for my $f (`git remote`) {
108 chomp($f);
109 my $do_push = 0;
110 for my $ch (@pushremotes) {
111 chomp($ch);
112 msg(4, "ch = '$ch'");
113 my $pipefp;
114 open($pipefp, 'git remote -v |') or
115 die("$progname: git remote -v: Cannot open pipe: $!\n");
116 while (my $curr = <$pipefp>) {
117 chomp($curr);
118 msg(5, "curr = '$curr'");
119 if ($curr =~ /^$f\s/ && $curr =~ /$ch/) {
120 msg(2, "Found ('$curr' =~ /$ch/)");
121 $do_push = 1;
124 close($pipefp);
126 $Opt{'all-remotes'} && ($do_push = 1);
127 if ($do_push) {
128 my $tags_found = 0;
130 my $pipefp;
131 open($pipefp, 'git tag |') or
132 die("$progname: git tag: Cannot open pipe: $!\n");
133 my $check_tag = <$pipefp>;
134 $tags_found = 1 if (defined($check_tag));
135 close($pipefp);
136 msg(2, "tags_found = \"$tags_found\"");
138 if ($Opt{'force'}) {
139 mysystem('git', 'push', '--force', $f, @ARGV);
140 } else {
141 mysystem('git', 'push', $f, @ARGV);
143 if (defined($allremotes{$f})) {
144 mysystem('git', 'push', '--all', $f);
145 mysystem('git', 'push', '--tags', $f) if ($tags_found);
150 return $Retval;
151 # }}}
152 } # main()
154 sub update_synced {
155 # {{{
156 my $pipefp;
157 my $local_branches = `git branch | cut -c 3-`;
159 open($pipefp, "git branch | cut -c 3-|");
160 while (my $curr = <$pipefp>) {
161 chomp($curr);
162 next if ($curr =~ /^synced\//);
163 if ($local_branches =~ /^synced\/$curr$/m) {
164 if ($Opt{'force'}) {
165 mysystem("git", "push", "-f", ".", "$curr:synced/$curr");
166 } else {
167 mysystem("git", "push", ".", "$curr:synced/$curr");
169 system("git", "branch", "-q", "-u", "synced/$curr", $curr);
172 close($pipefp);
174 return;
175 # }}}
176 } # update_synced()
178 sub check_sig {
179 # {{{
180 my $retval = shift;
181 ($retval & 127) &&
182 die("\n$progname: Child process interrupted, aborting.\n");
183 return(0);
184 # }}}
185 } # check_sig()
187 sub mysystem {
188 # {{{
189 my @args = @_;
190 print(STDERR "\n");
191 msg(0, "Executing '" . join(' ', @args) . "'");
192 $Opt{'dry-run'} || system(@args) && check_sig($?);
193 return;
194 # }}}
195 } # mysystem()
197 sub read_regexpfile {
198 # {{{
199 my $regexpfile = shift;
200 my $fp;
201 my @Retval = ();
202 open($fp, '<', $regexpfile) ||
203 die("$progname: $regexpfile: Could not read regexp file: $!\n");
204 while (my $curr = <$fp>) {
205 chomp($curr);
206 push(@Retval, $curr);
207 msg(3, "regexp: $curr");
209 return(@Retval);
210 # }}}
211 } # read_regexpfile()
213 sub print_version {
214 # Print program version {{{
215 print("$progname $VERSION\n");
216 return;
217 # }}}
218 } # print_version()
220 sub usage {
221 # Send the help message to stdout {{{
222 my $Retval = shift;
224 if ($Opt{'verbose'}) {
225 print("\n");
226 print_version();
228 print(<<"END");
230 Push to all predefined Git remotes in one go. If any synced/* branches
231 exist, push the parent branch to them to get them in sync. Also set all
232 branches with a corresponding synced/ branch up to track the synced/
233 branch. This will show up in a Git-aware prompt that new commits have
234 been pushed to the local branch, or there are commits that haven't been
235 pushed yet.
237 Usage: $progname [options] [args for git push]
239 Options:
241 -a, --all-remotes
242 Push to all local remotes.
243 -f, --force
244 Use --force when pushing. Overwrites remote branches, be careful.
245 -h, --help
246 Show this help.
247 -n, --dry-run
248 Don't really push the branches, simulate only.
249 -q, --quiet
250 Be more quiet. Can be repeated to increase silence.
251 --regexpfile X
252 Use regexpfile X instead of the default
253 '$Std{'regexpfile'}'.
254 -s, --synced-only
255 Don't push, only update the local synced/* branches.
256 -v, --verbose
257 Increase level of verbosity. Can be repeated.
258 --version
259 Print version information.
261 The 'git-pa.allremotes' configuration option can contain a
262 space-separated list of remotes which will also get pushed to using
263 '--all' and '--tags'.
266 exit($Retval);
267 # }}}
268 } # usage()
270 sub msg {
271 # Print a status message to stderr based on verbosity level {{{
272 my ($verbose_level, $Txt) = @_;
274 if ($Opt{'verbose'} >= $verbose_level) {
275 print(STDERR "$progname: $Txt\n");
277 return;
278 # }}}
279 } # msg()
281 __END__
283 # This program is free software; you can redistribute it and/or modify
284 # it under the terms of the GNU General Public License as published by
285 # the Free Software Foundation; either version 2 of the License, or (at
286 # your option) any later version.
288 # This program is distributed in the hope that it will be useful, but
289 # WITHOUT ANY WARRANTY; without even the implied warranty of
290 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
291 # See the GNU General Public License for more details.
293 # You should have received a copy of the GNU General Public License
294 # along with this program.
295 # If not, see L<http://www.gnu.org/licenses/>.
297 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :