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/;
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 => "Path::Class::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' ],
38 $self->$_() for qw/_util last_change owner description/; # Ensure to build early.
42 -f $self->{path}->file('.git', 'HEAD')
43 ? $self->{path}->subdir('.git')
48 Gitalist::Git::Util->new(
49 gitdir => $self->_project_dir($self->path),
53 method _build_description {
56 $description = $self->path->file('description')->slurp;
63 my ($gecos, $name) = (getpwuid $self->path->stat->uid)[6,0];
65 return length($gecos) ? $gecos : $name;
68 method _build_last_change {
70 my $output = $self->run_cmd(
71 qw{ for-each-ref --format=%(committer)
72 --sort=-committerdate --count=1 refs/heads
74 if (my ($epoch, $tz) = $output =~ /\s(\d+)\s+([+-]\d+)$/) {
75 my $dt = DateTime->from_epoch(epoch => $epoch);
76 $dt->set_time_zone($tz);
83 my $cmdout = $self->run_cmd(qw/for-each-ref --sort=-committerdate /, '--format=%(objectname)%00%(refname)%00%(committer)', 'refs/heads');
84 my @output = $cmdout ? split(/\n/, $cmdout) : ();
86 for my $line (@output) {
87 my ($rev, $head, $commiter) = split /\0/, $line, 3;
88 $head =~ s!^refs/heads/!!;
90 push @ret, { sha1 => $rev, name => $head };
92 #FIXME: That isn't the time I'm looking for..
93 if (my ($epoch, $tz) = $line =~ /\s(\d+)\s+([+-]\d+)$/) {
94 my $dt = DateTime->from_epoch(epoch => $epoch);
95 $dt->set_time_zone($tz);
96 $ret[-1]->{last_change} = $dt;
106 Find the hash of a given head (defaults to HEAD).
110 method head_hash (Str $head?) {
111 my $output = $self->run_cmd(qw/rev-parse --verify/, $head || 'HEAD' );
112 return unless defined $output;
114 my($sha1) = $output =~ /^($SHA1RE)$/;
120 Return an array of contents for a given tree.
121 The tree is specified by sha1, and defaults to HEAD.
122 The keys for each item will be:
131 method list_tree (Str $sha1?) {
132 $sha1 ||= $self->head_hash;
134 my $output = $self->run_cmd(qw/ls-tree -z/, $sha1);
135 return unless defined $output;
138 for my $line (split /\0/, $output) {
139 my ($mode, $type, $object, $file) = split /\s+/, $line, 4;
140 push @ret, Object->new( mode => oct $mode,
150 use Gitalist::Git::Object;
151 method get_object (Str $sha1) {
152 return Gitalist::Git::Object->new(
158 # Should be in ::Object
159 method get_object_mode_string (Gitalist::Git::Object $object) {
160 return unless $object && $object->{mode};
161 return $object->{modestr};
164 method get_object_type ($object) {
165 chomp(my $output = $self->run_cmd(qw/cat-file -t/, $object));
166 return unless $output;
171 method cat_file ($object) {
172 my $type = $self->get_object_type($object);
173 die "object `$object' is not a file\n"
174 if (!defined $type || $type ne 'blob');
176 my $output = $self->run_cmd(qw/cat-file -p/, $object);
177 return unless $output;
182 method hash_by_path ($base, $path?, $type?) {
186 my $output = $self->run_cmd('ls-tree', $base, '--', $path)
188 my($line) = $output ? split(/\n/, $output) : ();
190 #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
191 $line =~ m/^([0-9]+) (.+) ($SHA1RE)\t/;
192 return defined $type && $type ne $2
203 Returns a hash containing properties of this project. The keys will
207 description (empty if .git/description is empty/unnamed)
216 description => $self->description,
217 owner => $self->owner,
218 last_change => $self->last_change,