X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FGitalist%2FGit%2FRepository.pm;h=cb16eb2a561846723b53ce8b183fcc58ada5fe57;hb=6732f736698eece7a8a9baeb5c535a16aff7dd1e;hp=f82a02667e506a59de057a5f27e74f58ccf55771;hpb=44a9ed75e5d936e96ce63cfb13c943ab3f59dc52;p=catagits%2FGitalist.git diff --git a/lib/Gitalist/Git/Repository.pm b/lib/Gitalist/Git/Repository.pm index f82a026..cb16eb2 100644 --- a/lib/Gitalist/Git/Repository.pm +++ b/lib/Gitalist/Git/Repository.pm @@ -1,32 +1,43 @@ use MooseX::Declare; -class Gitalist::Git::Repository with Gitalist::Git::HasUtils { - # FIXME, use Types::Path::Class and coerce +class Gitalist::Git::Repository with (Gitalist::Git::HasUtils, Gitalist::Git::Serializable) { + use MooseX::Storage::Meta::Attribute::Trait::DoNotSerialize; + 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 Gitalist::Git::Types qw/SHA1/; + use MooseX::Types::Moose qw/Str Maybe Bool HashRef ArrayRef/; + use Gitalist::Git::Types qw/SHA1 Dir/; + use MooseX::Types::DateTime qw/ DateTime /; + use Moose::Autobox; + use aliased 'DateTime' => 'DT'; use List::MoreUtils qw/any zip/; - use DateTime; - use Encode qw/decode/; - use I18N::Langinfo qw/langinfo CODESET/; + use Encode qw/decode/; + + use if $^O ne 'MSWin32' => 'I18N::Langinfo', qw/langinfo CODESET/; + use Gitalist::Git::Object::Blob; use Gitalist::Git::Object::Tree; use Gitalist::Git::Object::Commit; use Gitalist::Git::Object::Tag; + use Gitalist::Git::Head; + use Gitalist::Git::Tag; our $SHA1RE = qr/[0-9a-fA-F]{40}/; - around BUILDARGS (ClassName $class: Dir $dir) { + around BUILDARGS (ClassName $class: Dir $dir, Str $override_name = '') { # Allows us to be called as Repository->new($dir) # Last path component becomes $self->name # Full path to git objects becomes $self->path my $name = $dir->dir_list(-1); - $dir = $dir->subdir('.git') if (-f $dir->file('.git', 'HEAD')); + if(-f $dir->file('.git', 'HEAD')) { # Non-bare repo above .git + $dir = $dir->subdir('.git'); + $name = $dir->dir_list(-2, 1); # .../name/.git + } elsif('.git' eq $dir->dir_list(-1)) { # Non-bare repo in .git + $name = $dir->dir_list(-2); + } confess("Can't find a git repository at " . $dir) - unless ( -f $dir->file('HEAD') ); - return $class->$orig(name => $name, + unless -f $dir->file('HEAD'); + return $class->$orig(name => $override_name || $name, path => $dir); } @@ -34,7 +45,8 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { is => 'ro', required => 1 ); has path => ( isa => Dir, - is => 'ro', required => 1); + is => 'ro', required => 1, + traits => ['DoNotSerialize'] ); has description => ( isa => Str, is => 'ro', @@ -46,7 +58,7 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { lazy_build => 1, ); - has last_change => ( isa => Maybe['DateTime'], + has last_change => ( isa => Maybe[DateTime], is => 'ro', lazy_build => 1, ); @@ -55,14 +67,14 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { is => 'ro', lazy => 1, default => sub { - -d $_[0]->path->parent->subdir->($_[0]->name) + -d $_[0]->path->parent->subdir($_[0]->name) ? 1 : 0 }, ); - has heads => ( isa => ArrayRef[HashRef], + has heads => ( isa => ArrayRef['Gitalist::Git::Head'], is => 'ro', lazy_build => 1); - has tags => ( isa => ArrayRef[HashRef], + has tags => ( isa => ArrayRef['Gitalist::Git::Tag'], is => 'ro', lazy_build => 1); has references => ( isa => HashRef[ArrayRef[Str]], @@ -70,10 +82,11 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { lazy_build => 1 ); method BUILD { - $self->$_() for qw/last_change owner description/; # Ensure to build early. + $self->$_() for qw/last_change owner description /; # Ensure to build early. } ## Public methods + method head_hash (Str $head?) { my $output = $self->run_cmd(qw/rev-parse --verify/, $head || 'HEAD' ); confess("No such head: " . $head) unless defined $output; @@ -82,12 +95,6 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { return $sha1; } - method list_tree (SHA1 $sha1?) { - $sha1 ||= $self->head_hash; - my $object = $self->get_object($sha1); - return @{$object->tree}; - } - method get_object (NonEmptySimpleStr $sha1) { unless (is_SHA1($sha1)) { $sha1 = $self->head_hash($sha1); @@ -96,26 +103,12 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { chomp($type); my $class = 'Gitalist::Git::Object::' . ucfirst($type); $class->new( - project => $self, + repository => $self, sha1 => $sha1, type => $type, ); } - method hash_by_path ($base, $path = '', $type?) { - $path =~ s{/+$}(); - # FIXME should this really just take the first result? - my @paths = $self->run_cmd('ls-tree', $base, '--', $path) - or return; - my $line = $paths[0]; - - #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c' - $line =~ m/^([0-9]+) (.+) ($SHA1RE)\t/; - return defined $type && $type ne $2 - ? () - : $3; - } - method list_revs ( NonEmptySimpleStr :$sha1!, Int :$count?, Int :$skip?, @@ -125,7 +118,7 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { if !$sha1 || $sha1 !~ $SHA1RE; my @search_opts; - if ($search) { + if ($search and exists $search->{text}) { $search->{type} = 'grep' if $search->{type} eq 'commit'; @search_opts = ( @@ -173,16 +166,6 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { # TODO - support compressed archives } - method diff ( Gitalist::Git::Object :$commit!, - Bool :$patch?, - Maybe[NonEmptySimpleStr] :$parent?, - NonEmptySimpleStr :$file? - ) { - return $commit->diff( patch => $patch, - parent => $parent, - file => $file); - } - method reflog (@logargs) { my @entries = $self->run_cmd(qw(log -g), @logargs) @@ -227,7 +210,7 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { ## BUILDERS method _build_util { Gitalist::Git::Util->new( - project => $self, + repository => $self, ); } @@ -235,13 +218,18 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { my $description = ""; eval { $description = $self->path->file('description')->slurp; + utf8::decode($description); chomp $description; }; + $description = "Unnamed repository, edit the .git/description file to set a description" + if $description eq "Unnamed repository; edit this file 'description' to name the repository."; return $description; } method _build_owner { - my ($gecos, $name) = map { decode(langinfo(CODESET), $_) } (getpwuid $self->path->stat->uid)[6,0]; + return 'system' if $^O =~ 'MSWin32'; + + my ($gecos, $name) = map { decode(langinfo(CODESET()), $_) } (getpwuid $self->path->stat->uid)[6,0]; $gecos =~ s/,+$//; return length($gecos) ? $gecos : $name; } @@ -253,7 +241,7 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { --sort=-committerdate --count=1 refs/heads }); if (my ($epoch, $tz) = $output =~ /\s(\d+)\s+([+-]\d+)$/) { - my $dt = DateTime->from_epoch(epoch => $epoch); + my $dt = DT->from_epoch(epoch => $epoch); $dt->set_time_zone($tz); $last_change = $dt; } @@ -264,19 +252,8 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { my @revlines = $self->run_cmd_list(qw/for-each-ref --sort=-committerdate /, '--format=%(objectname)%00%(refname)%00%(committer)', 'refs/heads'); my @ret; for my $line (@revlines) { - my ($rev, $head, $commiter) = split /\0/, $line, 3; - $head =~ s!^refs/heads/!!; - - push @ret, { sha1 => $rev, name => $head }; - - #FIXME: That isn't the time I'm looking for.. - if (my ($epoch, $tz) = $line =~ /\s(\d+)\s+([+-]\d+)$/) { - my $dt = DateTime->from_epoch(epoch => $epoch); - $dt->set_time_zone($tz); - $ret[-1]->{last_change} = $dt; - } + push @ret, Gitalist::Git::Head->new($line); } - return \@ret; } @@ -286,24 +263,10 @@ class Gitalist::Git::Repository with Gitalist::Git::HasUtils { '--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; + return [ + map Gitalist::Git::Tag->new($_), + grep Gitalist::Git::Tag::is_valid_tag($_), @revlines + ]; } method _build_references { @@ -339,22 +302,22 @@ Gitalist::Git::Repository - Model of a git repository =head1 SYNOPSIS my $gitrepo = dir('/repo/base/Gitalist'); - my $project = Gitalist::Git::Repository->new($gitrepo); - $project->name; # 'Gitalist' - $project->path; # '/repo/base/Gitalist/.git' - $project->description; # 'Unnamed repository.' + my $repository = Gitalist::Git::Repository->new($gitrepo); + $repository->name; # 'Gitalist' + $repository->path; # '/repo/base/Gitalist/.git' + $repository->description; # 'Unnamed repository.' =head1 DESCRIPTION This class models a git repository, referred to in Gitalist -as a "Project". +as a "Repository". =head1 ATTRIBUTES =head2 name -The name of the Project. If unspecified, this will be derived from the path to the git repository. +The name of the Repository. If unspecified, this will be derived from the path to the git repository. =head2 path @@ -393,20 +356,10 @@ Hashref of ArrayRefs for each reference. Return the sha1 for HEAD, or any specified head. -=head2 list_tree ($sha1?) - -Return an array of contents for a given tree. -The tree is specified by sha1, and defaults to HEAD. -Each item is a L. - =head2 get_object ($sha1) Return an appropriate subclass of L for the given sha1. -=head2 hash_by_path ($sha1, $path, $type?) - -Returns the sha1 for a given path, optionally limited by type. - =head2 list_revs ($sha1, $count?, $skip?, \%search?, $file?) Returns a list of revs for the given head ($sha1).