Added the /tags action.
[catagits/Gitalist.git] / lib / Gitalist / Git / Project.pm
index efcfbd6..981e824 100644 (file)
@@ -24,9 +24,13 @@ class Gitalist::Git::Project with Gitalist::Git::HasUtils {
     use MooseX::Types::Common::String qw/NonEmptySimpleStr/;
     use MooseX::Types::Path::Class qw/Dir/;
     use MooseX::Types::Moose qw/Str Maybe Bool HashRef ArrayRef/;
+    use Moose::Autobox;
     use List::MoreUtils qw/any zip/;
     use DateTime;
-    use aliased 'Gitalist::Git::Object';
+    use Gitalist::Git::Object::Blob;
+    use Gitalist::Git::Object::Tree;
+    use Gitalist::Git::Object::Commit;
+    use Gitalist::Git::Object::Tag;
 
     our $SHA1RE = qr/[0-9a-fA-F]{40}/;
 
@@ -113,6 +117,14 @@ ArrayRef of hashes containing the name and sha1 of all heads.
     has heads => ( isa => ArrayRef[HashRef],
                    is => 'ro',
                    lazy_build => 1);
+=head2 tags
+
+ArrayRef of hashes containing the name and sha1 of all tags.
+
+=cut
+    has tags => ( isa => ArrayRef[HashRef],
+                   is => 'ro',
+                   lazy_build => 1);
 
 =head2 references
 
@@ -151,35 +163,26 @@ Each item is a L<Gitalist::Git::Object>.
 =cut
     method list_tree (Str $sha1?) {
         $sha1 ||= $self->head_hash;
-
-        my $output = $self->run_cmd(qw/ls-tree -z/, $sha1);
-        return unless defined $output;
-
-        my @ret;
-        for my $line (split /\0/, $output) {
-            my ($mode, $type, $object, $file) = split /\s+/, $line, 4;
-            push @ret, Object->new( mode => oct $mode,
-                                    type => $type,
-                                    sha1 => $object,
-                                    file => $file,
-                                    project => $self,
-                                  );
-        }
-        return @ret;
+        my $object = $self->get_object($sha1);
+        return @{$object->tree};
     }
 
 =head2 get_object ($sha1)
 
-Return a L<Gitalist::Git::Object> for the given sha1.
+Return an appropriate subclass of L<Gitalist::Git::Object> for the given sha1.
 
 =cut
     method get_object (NonEmptySimpleStr $sha1) {
         unless ( $self->_is_valid_rev($sha1) ) {
             $sha1 = $self->head_hash($sha1);
         }
-        return Object->new(
+        my $type = $self->run_cmd('cat-file', '-t', $sha1);
+        chomp($type);
+        my $class = 'Gitalist::Git::Object::' . ucfirst($type);
+        $class->new(
             project => $self,
             sha1 => $sha1,
+            type => $type,
         );
     }
 
@@ -244,6 +247,34 @@ Returns a list of revs for the given head ($sha1).
         return @revs;
     }
 
+=head2 snapshot($sha1, $format)
+
+Generate an archived snapshot of the repository.
+$sha1 should be a commit or tree.
+Returns a filehandle to read from.
+
+=cut
+
+method snapshot (NonEmptySimpleStr :$sha1,
+                 NonEmptySimpleStr :$format
+               ) {
+    # TODO - only valid formats are 'tar' and 'zip'
+    my $formats = { tgz => 'tar', zip => 'zip' };
+    unless ($formats->exists($format)) {
+        die("No such format: $format");
+    }
+    $format = $formats->{$format};
+    my $name = $self->name;
+    $name =~ s,([^/])/*\.git$,$1,;
+    my $filename = $name;
+    $filename .= "-$sha1.$format";
+    $name =~ s/\047/\047\\\047\047/g;
+
+    my @cmd = ('archive', "--format=$format", "--prefix=$name/", $sha1);
+    return ($filename, $self->run_cmd_fh(@cmd));
+    # TODO - support compressed archives
+}
+
 =head2 diff($commit, $patch?, $parent?, $file?)
 
 Generate a diff from a given L<Gitalist::Git::Object>.
@@ -316,7 +347,12 @@ FIXME Should this return objects?
     }
 
     method _build_description {
-        return $self->gpp->description;
+        my $description = "";
+        eval {
+            $description = $self->path->file('description')->slurp;
+            chomp $description;
+        };
+        return $description;
     }
 
     method _build_owner {
@@ -359,6 +395,32 @@ FIXME Should this return objects?
         return \@ret;
     }
 
+    method _build_tags {
+        my @revlines = $self->run_cmd_list('for-each-ref',
+          '--sort=-creatordate',
+          '--format=%(objectname) %(objecttype) %(refname) %(*objectname) %(*objecttype) %(subject)%00%(creator)',
+         'refs/tags'
+        );
+        my @ret;
+        for my $line (@revlines) {
+            my($refinfo, $creatorinfo) = split /\0/, $line;
+           my($rev, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6);
+            my($creator, $epoch, $tz) = ($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/);
+            $name =~ s!^refs/tags/!!;
+
+            push @ret, { sha1 => $rev, name => $name };
+
+            #FIXME: That isn't the time I'm looking for..
+            if($epoch and $tz) {
+                my $dt = DateTime->from_epoch(epoch => $epoch);
+                $dt->set_time_zone($tz);
+                $ret[-1]->{last_change} = $dt;
+            }
+        }
+
+        return \@ret;
+    }
+
     method _build_references {
        # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11
        # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}