]> git.vpit.fr Git - perl/modules/rgit.git/blob - bin/rgit
This is 0.06
[perl/modules/rgit.git] / bin / rgit
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 use Carp qw/croak/;
7 use Config qw/%Config/;
8 use Cwd qw/cwd/;
9 use File::Spec::Functions qw/catfile path/;
10
11 use App::Rgit::Utils qw/:codes :levels/;
12 use App::Rgit;
13
14 our $VERSION;
15 BEGIN {
16  $VERSION = '0.06';
17 }
18
19 my %opts;
20 my $cmd;
21
22 BEGIN {
23  @ARGV = grep {
24   defined $cmd ? $_
25                : ( /^-([DIKV]+)$/ ? do { $opts{$_} = 1 for split //, $1; () }
26                                   : do { $cmd = $_ unless /^-/; $_ } )
27  } @ARGV;
28  $cmd = ' ' unless defined $cmd;
29 }
30
31 my $shell;
32
33 BEGIN {
34  if (-t && $opts{I}) {
35   if (eval "require Term::ReadKey; 1") {
36    Term::ReadKey->import;
37    *policy = \&policy_interactive;
38    for (grep defined, $ENV{SHELL}, '/bin/sh') {
39     if (-x $_) {
40      $shell = $_;
41      last;
42     }
43    }
44   } else {
45    warn "You have to install Term::ReadKey to use the interactive mode.\n";
46   }
47  }
48  *policy = $opts{K} ? \&policy_keep
49                     : \&policy_default
50            unless defined *policy{CODE};
51 }
52
53 setpgrp 0, 0 if $Config{d_setpgrp};
54
55 my $git = $ENV{GIT_EXEC_PATH};
56 unless (defined $git) {
57  for (path) {
58   my $g = catfile $_, 'git';
59   if (-x $g) {
60    $git = $g;
61    last;
62   }
63  }
64 }
65 croak "Couldn't find any valid git executable" unless defined $git;
66
67 my $root = $ENV{GIT_DIR};
68 $root = cwd unless defined $root;
69
70 my $ar = App::Rgit->new(
71  git    => $git,
72  root   => $root,
73  cmd    => $cmd,
74  args   => \@ARGV,
75  policy => \&policy,
76  debug  => $opts{D} ? INFO : WARN,
77 );
78
79 print STDOUT "rgit $VERSION\n" if $opts{V};
80
81 exit $ar->run;
82
83 sub policy_default {
84  my ($cmd, $conf, $repo, $status, $signal) = @_;
85  return NEXT unless $status;
86  return LAST;
87 }
88
89 sub policy_keep { NEXT }
90
91 sub policy_interactive {
92  my ($cmd, $conf, $repo, $status, $signal) = @_;
93  return NEXT unless $status;
94  my %codes = (
95   'a' => [ LAST,        'aborting' ],
96   'i' => [ NEXT,        'ignoring' ],
97   'I' => [ NEXT | SAVE, 'ignoring all' ],
98   'r' => [ REDO,        'retrying' ],
99  );
100  my $int = { GetControlChars() }->{INTERRUPT};
101  while (1) {
102   $conf->warn("[a]bort, [i]gnore, [I]gnore all, [r]etry, open [s]hell ?");
103   ReadMode(4);
104   my $key = ReadKey(0);
105   ReadMode(1);
106   print STDERR "\n";
107   next unless defined $key;
108   if ($key eq $int) {
109    $conf->warn("Interrupted, aborting\n");
110    return LAST;
111   } elsif ($key eq 's') {
112    if (defined $shell) {
113     $conf->info('Opening shell in ', $repo->work, "\n");
114     my $cwd = cwd;
115     $repo->chdir;
116     system { $shell } $shell;
117     chdir $cwd;
118    } else {
119     $conf->err("Couldn't find any shell\n");
120    }
121   } elsif (exists $codes{$key}) {
122    my $code = $codes{$key};
123    $conf->info('Okay, ', $code->[1], "\n");
124    return $code->[0];
125   }
126  }
127 }
128
129 __END__
130
131 =head1 NAME
132
133 rgit - Recursively execute a command on all the git repositories in a directory tree.
134
135 =head1 VERSION
136
137 Version 0.06
138
139 =head1 SYNOPSIS
140
141     rgit [-K|-I|-D|-V] [GIT_OPTIONS] COMMAND [COMMAND_ARGS]
142
143 =head1 DESCRIPTION
144
145 This utility recursively searches in a root directory (which may be the current working directory or - if it has been set - the directory given by the C<GIT_DIR> environment variable) for all git repositories, sort this list by the repository path, C<chdir> into each of them, and executes the specified git command.
146 Moreover, those formats are substituted in the arguments before running the command :
147
148 =over 4
149
150 =item *
151
152 C<%n> with the current repository name.
153
154 =item *
155
156 C<%g> with the relative path (based from the root directory) to the current repository.
157
158 =item *
159
160 C<%G> with the absolute path to the current repository.
161
162 =item *
163
164 C<%w> with the relative path (based from the root directory) to the current repository's working directory.
165
166 =item *
167
168 C<%W> with the absolute path to the current repository's working directory.
169
170 =item *
171
172 C<%b> with a "bareified" relative path, i.e. C<%g> if this is a bare repository, and C<%w.git> otherwise.
173
174 =item *
175
176 C<%B> with an absolute version of the "bareified" path.
177
178 =item *
179
180 C<%R> with the absolute path to the root directory.
181
182 =item *
183
184 C<%%> with a bare C<%>.
185
186 =back
187
188 There are actually a few commands that are only executed once in the root directory : C<daemon>, C<gui>, C<help>, C<init> and C<version>.
189 For any of those, no format substitution is done.
190
191 You can specify which C<git> executable to use with the C<GIT_EXEC_PATH> environment variable.
192
193 =head1 COMMAND LINE SWITCHES
194
195 C<rgit> takes its options as the capital switches that comes before the git command.
196 It's possible to bundle them together.
197 They are removed from the argument list before calling C<git>.
198
199 =over 4
200
201 =item *
202
203 C<-K>
204
205 Keep processing on error.
206 The default policy is to stop whenever an error occured.
207
208 =item *
209
210 C<-I>
211
212 Enables interactive mode when the standard input is a tty.
213 Requires L<Term::ReadKey> to be installed.
214 This lets you choose interactively what to do when one of the commands returns a non-zero status.
215
216 =item *
217
218 C<-D>
219
220 Outputs diagnostics.
221
222 =item *
223
224 C<-V>
225
226 Outputs the version.
227
228 =back
229
230 =head1 EXAMPLES
231
232 Execute C<git gc> on all the repositories below the current directory :
233
234     rgit gc
235
236 Tag all the repositories with their name :
237
238     rgit tag %n
239
240 Add a remote to all repositories in "/foo/bar" to their bare counterpart in C<qux> on F<host> :
241
242     GIT_DIR="/foo/bar" rgit remote add host git://host/qux/%b
243
244 =head1 DEPENDENCIES
245
246 The core modules L<Carp>, L<Config>, L<Cwd>, L<Exporter>, L<File::Find>, L<File::Spec::Functions> and L<POSIX>.
247
248 L<Object::Tiny>.
249
250 =head1 AUTHOR
251
252 Vincent Pit, C<< <perl at profvince.com> >>, L<http://profvince.com>.
253    
254 You can contact me by mail or on C<irc.perl.org> (vincent).
255
256 =head1 BUGS
257
258 Please report any bugs or feature requests to C<bug-rgit at rt.cpan.org>, or through the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=rgit>.  I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
259
260 =head1 SUPPORT
261
262 You can find documentation for this module with the perldoc command.
263
264     perldoc rgit
265
266 Tests code coverage report is available at L<http://www.profvince.com/perl/cover/rgit>.
267
268 =head1 COPYRIGHT & LICENSE
269
270 Copyright 2008-2009 Vincent Pit, all rights reserved.
271
272 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
273
274 =cut