3 class Gitalist::Git::Project {
4 # FIXME, use Types::Path::Class and coerce
5 use MooseX::Types::Common::String qw/NonEmptySimpleStr/;
6 use MooseX::Types::Moose qw/Str Maybe Bool/;
8 use MooseX::Types::Path::Class qw/Dir/;
9 use Gitalist::Git::Util;
10 use aliased 'Gitalist::Git::Object';
12 our $SHA1RE = qr/[0-9a-fA-F]{40}/;
14 has name => ( isa => NonEmptySimpleStr,
15 is => 'ro', required => 1 );
16 has path => ( isa => Dir,
17 is => 'ro', required => 1);
19 has description => ( isa => Str,
23 has owner => ( isa => NonEmptySimpleStr,
27 has last_change => ( isa => Maybe['DateTime'],
31 has _util => ( isa => 'Gitalist::Git::Util',
34 handles => [ 'run_cmd' ],
37 has project_dir => ( isa => Dir,
44 : $self->path->subdir('.git')
53 -f $self->path->file('.git', 'HEAD')
55 : -f $self->path->file('HEAD')
57 : confess("Cannot find " . $self->path . "/.git/HEAD or "
58 . $self->path . "/HEAD");
63 $self->$_() for qw/_util last_change owner description/; # Ensure to build early.
67 -f $self->{path}->file('.git', 'HEAD')
68 ? $self->{path}->subdir('.git')
73 Gitalist::Git::Util->new(
78 method _build_description {
81 $description = $self->project_dir->file('description')->slurp;
88 my ($gecos, $name) = (getpwuid $self->project_dir->stat->uid)[6,0];
90 return length($gecos) ? $gecos : $name;
93 method _build_last_change {
95 my $output = $self->run_cmd(
96 qw{ for-each-ref --format=%(committer)
97 --sort=-committerdate --count=1 refs/heads
99 if (my ($epoch, $tz) = $output =~ /\s(\d+)\s+([+-]\d+)$/) {
100 my $dt = DateTime->from_epoch(epoch => $epoch);
101 $dt->set_time_zone($tz);
108 my $cmdout = $self->run_cmd(qw/for-each-ref --sort=-committerdate /, '--format=%(objectname)%00%(refname)%00%(committer)', 'refs/heads');
109 my @output = $cmdout ? split(/\n/, $cmdout) : ();
111 for my $line (@output) {
112 my ($rev, $head, $commiter) = split /\0/, $line, 3;
113 $head =~ s!^refs/heads/!!;
115 push @ret, { sha1 => $rev, name => $head };
117 #FIXME: That isn't the time I'm looking for..
118 if (my ($epoch, $tz) = $line =~ /\s(\d+)\s+([+-]\d+)$/) {
119 my $dt = DateTime->from_epoch(epoch => $epoch);
120 $dt->set_time_zone($tz);
121 $ret[-1]->{last_change} = $dt;
131 Find the hash of a given head (defaults to HEAD).
135 method head_hash (Str $head?) {
136 my $output = $self->run_cmd(qw/rev-parse --verify/, $head || 'HEAD' );
137 return unless defined $output;
139 my($sha1) = $output =~ /^($SHA1RE)$/;
145 Return an array of contents for a given tree.
146 The tree is specified by sha1, and defaults to HEAD.
147 The keys for each item will be:
156 method list_tree (Str $sha1?) {
157 $sha1 ||= $self->head_hash;
159 my $output = $self->run_cmd(qw/ls-tree -z/, $sha1);
160 return unless defined $output;
163 for my $line (split /\0/, $output) {
164 my ($mode, $type, $object, $file) = split /\s+/, $line, 4;
165 push @ret, Object->new( mode => oct $mode,
175 use Gitalist::Git::Object;
176 method get_object (Str $sha1) {
177 return Gitalist::Git::Object->new(
183 # Should be in ::Object
184 method get_object_mode_string (Gitalist::Git::Object $object) {
185 return unless $object && $object->{mode};
186 return $object->{modestr};
189 method get_object_type ($object) {
190 chomp(my $output = $self->run_cmd(qw/cat-file -t/, $object));
191 return unless $output;
196 method cat_file ($object) {
197 my $type = $self->get_object_type($object);
198 die "object `$object' is not a file\n"
199 if (!defined $type || $type ne 'blob');
201 my $output = $self->run_cmd(qw/cat-file -p/, $object);
202 return unless $output;
207 method hash_by_path ($base, $path?, $type?) {
211 my $output = $self->run_cmd('ls-tree', $base, '--', $path)
213 my($line) = $output ? split(/\n/, $output) : ();
215 #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
216 $line =~ m/^([0-9]+) (.+) ($SHA1RE)\t/;
217 return defined $type && $type ne $2
228 Returns a hash containing properties of this project. The keys will
232 description (empty if .git/description is empty/unnamed)
241 description => $self->description,
242 owner => $self->owner,
243 last_change => $self->last_change,