]> git.vpit.fr Git - perl/modules/rgit.git/blob - lib/App/Rgit/Repository.pm
c340942a820487e1df2650270a1255b423e45b43
[perl/modules/rgit.git] / lib / App / Rgit / Repository.pm
1 package App::Rgit::Repository;
2
3 use strict;
4 use warnings;
5
6 use Cwd        (); # cwd
7 use File::Spec (); # canonpath, catdir, splitdir, abs2rel
8 use POSIX      (); # WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG SIGINT SIGQUIT
9
10 use App::Rgit::Utils (); # abs_path
11
12 my ($WIFEXITED, $WEXITSTATUS, $WIFSIGNALED, $WTERMSIG);
13
14 BEGIN {
15  $WIFEXITED   = eval { POSIX::WIFEXITED(0);   1 } ? \&POSIX::WIFEXITED
16                                                   : sub { 1 };
17  $WEXITSTATUS = eval { POSIX::WEXITSTATUS(0); 1 } ? \&POSIX::WEXITSTATUS
18                                                   : sub { shift() >> 8 };
19  $WIFSIGNALED = eval { POSIX::WIFSIGNALED(0); 1 } ? \&POSIX::WIFSIGNALED
20                                                   : sub { shift() & 127 };
21  $WTERMSIG    = eval { POSIX::WTERMSIG(0);    1 } ? \&POSIX::WTERMSIG
22                                                   : sub { shift() & 127 };
23 }
24
25 =head1 NAME
26
27 App::Rgit::Repository - Class representing a Git repository.
28
29 =head1 VERSION
30
31 Version 0.07
32
33 =cut
34
35 our $VERSION = '0.07';
36
37 =head1 DESCRIPTION
38
39 Class representing a Git repository.
40
41 This is an internal class to L<rgit>.
42
43 =head1 METHODS
44
45 =head2 C<< new dir => $dir [, fake => 1 ] >>
46
47 Creates a new repository starting from C<$dir>.
48 If the C<fake> option is passed, C<$dir> isn't checked to be a valid C<git> repository.
49
50 =cut
51
52 sub new {
53  my $class = shift;
54  $class = ref $class || $class;
55
56  my %args = @_;
57
58  my $dir = $args{dir};
59  if (defined $dir) {
60   $dir = App::Rgit::Utils::abs_path($dir);
61  } else {
62   $dir = Cwd::cwd;
63  }
64  $dir = File::Spec->canonpath($dir);
65
66  my ($repo, $bare, $name, $work);
67  if ($args{fake}) {
68   $repo = $work = $dir;
69  } else {
70   return unless -d $dir
71             and -d "$dir/refs"
72             and -d "$dir/objects"
73             and -e "$dir/HEAD";
74
75   my @chunks = File::Spec->splitdir($dir);
76   my $last   = pop @chunks;
77   return unless defined $last;
78
79   if (@chunks and $last eq '.git') {
80    $bare = 0;
81    $name = $chunks[-1];
82    $work = File::Spec->catdir(@chunks);
83   } elsif ($last =~ /(.+)\.git$/) {
84    $bare = 1;
85    $name = $1;
86    $work = File::Spec->catdir(@chunks, $last);
87   } else {
88    return;
89   }
90
91   $repo = $dir;
92  }
93
94  bless {
95   fake => !!$args{fake},
96   repo => $repo,
97   bare => $bare,
98   name => $name,
99   work => $work,
100  }, $class;
101 }
102
103 =head2 C<chdir>
104
105 C<chdir> into the repository's directory.
106
107 =cut
108
109 sub chdir {
110  my $self = shift;
111  my $dir = $self->work;
112  chdir $dir or do {
113   warn "Couldn't chdir into $dir: $!";
114   return;
115  };
116  return 1;
117 }
118
119 =head2 C<run $conf, @args>
120
121 Runs C<git @args> on the repository for the L<App::Rgit::Config> configuration C<$conf>.
122 When the repository isn't fake, the format substitutions applies to C<@args> elements.
123 Returns the exit code.
124
125 =cut
126
127 my $abs2rel = sub {
128  my $a = File::Spec->abs2rel(@_);
129  $a = $_[0] unless defined $a;
130  $a;
131 };
132
133 my %escapes = (
134  '%' => sub { '%' },
135  'n' => sub { shift->name },
136  'g' => sub { $abs2rel->(shift->repo, shift->root) },
137  'G' => sub { shift->repo },
138  'w' => sub { $abs2rel->(shift->work, shift->root) },
139  'W' => sub { shift->work },
140  'b' => sub {
141   my ($self, $conf) = @_;
142   $abs2rel->(
143    $self->bare ? $self->repo : $self->work . '.git',
144    $conf->root
145   );
146  },
147  'B' => sub { $_[0]->bare ? $_[0]->repo : $_[0]->work . '.git' },
148  'R' => sub { $_[1]->root },
149 );
150 my $e = quotemeta join '', keys %escapes;
151 $e = "[$e]";
152
153 sub run {
154  my $self = shift;
155  my $conf = shift;
156  return unless $conf->isa('App::Rgit::Config');
157
158  my @args = @_;
159
160  unless ($self->fake) {
161   s/%($e)/$escapes{$1}->($self, $conf)/eg for @args;
162  }
163
164  unshift @args, $conf->git;
165  $conf->info('Executing "', join(' ', @args), '" into ', $self->work, "\n");
166
167  {
168   local $ENV{GIT_DIR} = $self->repo if exists $ENV{GIT_DIR};
169   local $ENV{GIT_EXEC_PATH} = $conf->git if exists $ENV{GIT_EXEC_PATH};
170   system { $args[0] } @args;
171  }
172
173  if ($? == -1) {
174   $conf->crit("Failed to execute git: $!\n");
175   return;
176  }
177
178  my $ret;
179  $ret = $WEXITSTATUS->($?) if $WIFEXITED->($?);
180  my $sig;
181  if ($WIFSIGNALED->($?)) {
182   $sig = $WTERMSIG->($?);
183   $conf->warn("git died with signal $sig\n");
184   if ($sig == POSIX::SIGINT() || $sig == POSIX::SIGQUIT()) {
185    $conf->err("Aborting\n");
186    exit $sig;
187   }
188  } elsif ($ret) {
189   $conf->info("git returned $ret\n");
190  }
191
192  return wantarray ? ($ret, $sig) : $ret;
193 }
194
195 =head2 C<fake>
196
197 =head2 C<repo>
198
199 =head2 C<bare>
200
201 =head2 C<name>
202
203 =head2 C<work>
204
205 Read-only accessors.
206
207 =cut
208
209 BEGIN {
210  eval "sub $_ { \$_[0]->{$_} }" for qw/fake repo bare name work/;
211 }
212
213 =head1 SEE ALSO
214
215 L<rgit>.
216
217 =head1 AUTHOR
218
219 Vincent Pit, C<< <perl at profvince.com> >>, L<http://profvince.com>.
220
221 You can contact me by mail or on C<irc.perl.org> (vincent).
222
223 =head1 BUGS
224
225 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>.
226 I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
227
228 =head1 SUPPORT
229
230 You can find documentation for this module with the perldoc command.
231
232     perldoc App::Rgit::Repository
233
234 =head1 COPYRIGHT & LICENSE
235
236 Copyright 2008,2009,2010 Vincent Pit, all rights reserved.
237
238 This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
239
240 =cut
241
242 1; # End of App::Rgit::Repository