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