ceea61b1988011ac3437c1b44e27fdf07f51db18
[catagits/Gitalist.git] / lib / Gitalist / Git / Project.pm
1 use MooseX::Declare;
2
3 class Gitalist::Git::Project {
4     # FIXME, use Types::Path::Class and coerce
5     use MooseX::Types::Common::String qw/NonEmptySimpleStr/;
6     use DateTime;
7     use Path::Class;
8     use Gitalist::Git::Util;
9     use aliased 'Gitalist::Git::Object';
10
11     our $SHA1RE = qr/[0-9a-fA-F]{40}/;
12     
13     has name => ( isa => NonEmptySimpleStr,
14                   is => 'ro' );
15     has path => ( isa => "Path::Class::Dir",
16                   is => 'ro');
17
18     has description => ( isa => NonEmptySimpleStr,
19                          is => 'ro',
20                          lazy_build => 1,
21                      );
22     has owner => ( isa => NonEmptySimpleStr,
23                    is => 'ro',
24                    lazy_build => 1,
25                );
26     has last_change => ( isa => 'DateTime',
27                          is => 'ro',
28                          lazy_build => 1,
29                      );
30     has _util => ( isa => 'Gitalist::Git::Util',
31                    is => 'ro',
32                    lazy_build => 1,
33                    handles => [ 'run_cmd' ],
34                );
35
36     method _build__util {
37         my $util = Gitalist::Git::Util->new(
38             gitdir => $self->path,
39         );
40         return $util;
41     }
42     
43     method _build_description {
44         my $description = $self->path->file('description')->slurp;
45         chomp $description;
46         return $description;
47     }
48
49     method _build_owner {
50         my $owner = (getpwuid $self->path->stat->uid)[6];
51         $owner =~ s/,+$//;
52         return $owner;
53     }
54     
55     method _build_last_change {
56         my $last_change;
57         my $output = $self->run_cmd(
58             qw{ for-each-ref --format=%(committer)
59                 --sort=-committerdate --count=1 refs/heads
60           });
61         if (my ($epoch, $tz) = $output =~ /\s(\d+)\s+([+-]\d+)$/) {
62             my $dt = DateTime->from_epoch(epoch => $epoch);
63             $dt->set_time_zone($tz);
64             $last_change = $dt;
65         }
66         return $last_change;
67     }
68
69 =head2 head_hash
70
71 Find the hash of a given head (defaults to HEAD).
72
73 =cut
74
75     method head_hash (Str $head?) {
76         my $output = $self->run_cmd(qw/rev-parse --verify/, $head || 'HEAD' );
77         return unless defined $output;
78
79         my($sha1) = $output =~ /^($SHA1RE)$/;
80         return $sha1;
81     }
82
83 =head2 list_tree
84
85 Return an array of contents for a given tree.
86 The tree is specified by sha1, and defaults to HEAD.
87 The keys for each item will be:
88
89         mode
90         type
91         object
92         file
93
94 =cut
95
96     method list_tree (Str $sha1?) {
97         $sha1 ||= $self->head_hash;
98
99         my $output = $self->run_cmd(qw/ls-tree -z/, $sha1);
100         return unless defined $output;
101
102         my @ret;
103         for my $line (split /\0/, $output) {
104             my ($mode, $type, $object, $file) = split /\s+/, $line, 4;
105             push @ret, Object->new( mode => oct $mode,
106                                     type => $type,
107                                     sha1 => $object,
108                                     file => $file,
109                                   );
110         }
111         return @ret;
112     }
113
114
115     method project_dir {
116         my $dir = $self->path->stringify;
117         $dir .= '/.git'
118             if -f dir($dir)->file('.git/HEAD');
119         return $dir;
120     }
121
122     # Compatibility
123
124 =head2 info
125
126 Returns a hash containing properties of this project. The keys will
127 be:
128
129         name
130         description (empty if .git/description is empty/unnamed)
131         owner
132         last_change
133
134 =cut
135
136     method info {
137         return {
138             name => $self->name,
139             description => $self->description,
140             owner => $self->owner,
141             last_change => $self->last_change,
142         };
143     };
144     
145 } # end class