diff options
Diffstat (limited to 'lib/LXRng/Repo')
-rw-r--r-- | lib/LXRng/Repo/Directory.pm | 17 | ||||
-rw-r--r-- | lib/LXRng/Repo/File.pm | 17 | ||||
-rw-r--r-- | lib/LXRng/Repo/Git.pm | 111 | ||||
-rw-r--r-- | lib/LXRng/Repo/Git/Directory.pm | 56 | ||||
-rw-r--r-- | lib/LXRng/Repo/Git/File.pm | 80 | ||||
-rw-r--r-- | lib/LXRng/Repo/Git/Iterator.pm | 33 | ||||
-rw-r--r-- | lib/LXRng/Repo/Git/TarFile.pm | 98 | ||||
-rw-r--r-- | lib/LXRng/Repo/Plain.pm | 38 | ||||
-rw-r--r-- | lib/LXRng/Repo/Plain/Directory.pm | 45 | ||||
-rw-r--r-- | lib/LXRng/Repo/Plain/File.pm | 51 | ||||
-rw-r--r-- | lib/LXRng/Repo/Plain/Iterator.pm | 29 | ||||
-rw-r--r-- | lib/LXRng/Repo/TmpFile.pm | 30 |
12 files changed, 605 insertions, 0 deletions
diff --git a/lib/LXRng/Repo/Directory.pm b/lib/LXRng/Repo/Directory.pm new file mode 100644 index 0000000..ad892b2 --- /dev/null +++ b/lib/LXRng/Repo/Directory.pm @@ -0,0 +1,17 @@ +package LXRng::Repo::Directory; + +use strict; + +sub name { + my ($self) = @_; + + return $$self{'name'}; +} + +sub node { + my ($self) = @_; + + $self->name =~ m,([^/]+)/?$, and return $1; +} + +1; diff --git a/lib/LXRng/Repo/File.pm b/lib/LXRng/Repo/File.pm new file mode 100644 index 0000000..7df5a04 --- /dev/null +++ b/lib/LXRng/Repo/File.pm @@ -0,0 +1,17 @@ +package LXRng::Repo::File; + +use strict; + +sub name { + my ($self) = @_; + + return $$self{'name'}; +} + +sub node { + my ($self) = @_; + + $self->name =~ m,([^/]+)$, and return $1; +} + +1; diff --git a/lib/LXRng/Repo/Git.pm b/lib/LXRng/Repo/Git.pm new file mode 100644 index 0000000..2d6ea33 --- /dev/null +++ b/lib/LXRng/Repo/Git.pm @@ -0,0 +1,111 @@ +package LXRng::Repo::Git; + +use strict; +use Memoize; +use LXRng::Cached; +use LXRng::Repo::Git::Iterator; +use LXRng::Repo::Git::File; +use LXRng::Repo::Git::Directory; + +sub _git_cmd { + my ($self, $cmd, @args) = @_; + + my $git; + my $pid = open($git, "-|"); + die $! unless defined $pid; + if ($pid == 0) { + $ENV{'GIT_DIR'} = $$self{'root'}; + exec('git', $cmd, @args); + warn $!; + kill(9, $$); + } + return $git; +} + +sub new { + my ($class, $root, %args) = @_; + + memoize('_release_timestamp'); + + return bless({root => $root, %args}, $class); +} + +sub _release_timestamp { + my ($self, $release) = @_; + + my $cinfo = $self->_git_cmd('cat-file', 'commit', $release); + + my $time; + while (<$cinfo>) { + $time = $1 if /^author .*? (\d+(?: [-+]\d+|))$/ ; + $time ||= $1 if /^committer .*? (\d+(?: [-+]\d+|))$/ ; + } + + return $time; +} + +sub _use_author_timestamp { + my ($self) = @_; + + return $$self{'author_timestamp'}; +} + +sub _sort_key { + my ($v) = @_; + + $v =~ s/(\d+)/sprintf("%05d", $1)/ge; + return $v; +} + +sub allversions { + my ($self) = @_; + + cached { + my @tags; + my $tags = $self->_git_cmd('tag', '-l'); + while (<$tags>) { + chomp; + next if $$self{'release_re'} and $_ !~ $$self{'release_re'}; + push(@tags, $_); + } + + return (sort {_sort_key($b) cmp _sort_key($a) } @tags); + }; +} + +sub node { + my ($self, $path, $release) = @_; + + $path =~ s,^/+,,; + $path =~ s,/+$,,; + + if ($path eq '') { + open(my $tag, '<', $$self{'root'}.'/refs/tags/'.$release) + or return undef; + my $ref = <$tag>; + close($tag); + chomp($ref); + return LXRng::Repo::Git::Directory->new($self, '', $ref); + } + + my $git = $self->_git_cmd('ls-tree', $release, $path); + my ($mode, $type, $ref, $gitpath) = split(" ", <$git>); + + if ($type eq 'tree') { + return LXRng::Repo::Git::Directory->new($self, $path, $ref, $release); + } + elsif ($type eq 'blob') { + return LXRng::Repo::Git::File->new($self, $path, $ref, $release); + } + else { + return undef; + } +} + +sub iterator { + my ($self, $release) = @_; + + return LXRng::Repo::Git::Iterator->new($self, $release); +} + +1; diff --git a/lib/LXRng/Repo/Git/Directory.pm b/lib/LXRng/Repo/Git/Directory.pm new file mode 100644 index 0000000..592e608 --- /dev/null +++ b/lib/LXRng/Repo/Git/Directory.pm @@ -0,0 +1,56 @@ +package LXRng::Repo::Git::Directory; + +use strict; + +use base qw(LXRng::Repo::Directory); + +sub new { + my ($class, $repo, $name, $ref, $rel) = @_; + + $name =~ s,/*$,/,; + return bless({repo => $repo, name => $name, ref => $ref, rel => $rel}, + $class); +} + +sub time { + my ($self) = @_; + + return 0; +# return $$self{'stat'}[9]; +} + +sub size { + my ($self) = @_; + + return ''; +} + +sub contents { + my ($self) = @_; + + my $git = $$self{'repo'}->_git_cmd('ls-tree', $$self{'ref'}); + + my $prefix = $$self{'name'}; + $prefix =~ s,^/+,,; + my (@dirs, @files); + while (<$git>) { + chomp; + my ($mode, $type, $ref, $node) = split(" ", $_); + if ($type eq 'tree') { + push(@dirs, LXRng::Repo::Git::Directory->new($$self{'repo'}, + $prefix.$node, + $ref, + $$self{'rel'})); + } + elsif ($type eq 'blob') { + push(@files, LXRng::Repo::Git::File->new($$self{'repo'}, + $prefix.$node, + $ref, + $$self{'rel'})); + } + } + + return (@dirs, @files); +} + +1; diff --git a/lib/LXRng/Repo/Git/File.pm b/lib/LXRng/Repo/Git/File.pm new file mode 100644 index 0000000..b0bb9a3 --- /dev/null +++ b/lib/LXRng/Repo/Git/File.pm @@ -0,0 +1,80 @@ +package LXRng::Repo::Git::File; + +use strict; + +use base qw(LXRng::Repo::File); +use LXRng::Repo::TmpFile; +use File::Temp qw(tempdir); + +sub new { + my ($class, $repo, $name, $ref, $rel) = @_; + + return bless({repo => $repo, name => $name, ref => $ref, rel => $rel}, + $class); +} + +sub time { + my ($self) = @_; + + if ($$self{'repo'}->_use_author_timestamp) { + # This is painfully slow. It is only performed index-time, + # but that might stil be bad enough that you would want to + # just use the release-timestamp insted. + my $cinfo = $$self{'repo'}->_git_cmd('log', '--pretty=raw', + '--max-count=1', '--all', + '..'.$$self{'rel'}, + '--', $self->name); + + my $time; + while (<$cinfo>) { + $time = $1 if /^author .*? (\d+(?: [-+]\d+|))$/ ; + $time ||= $1 if /^committer .*? (\d+(?: [-+]\d+|))$/ ; + } + + return $time if $time; + } + + return $$self{'repo'}->_release_timestamp($$self{'rel'}); +} + +sub size { + my ($self) = @_; + + my $git = $$self{'repo'}->_git_cmd('cat-file', '-s', $$self{'ref'}); + my $size = <$git>; + close($git); + chomp($size); + return $size; +} + +sub handle { + my ($self) = @_; + + return $$self{'repo'}->_git_cmd('cat-file', 'blob', $$self{'ref'}); +} + +sub revision { + my ($self) = @_; + + return $$self{'ref'}; +} + +sub phys_path { + my ($self) = @_; + + my $tmpdir = tempdir() or die($!); + open(my $phys, ">", $tmpdir.'/'.$self->node) or die($!); + + my $handle = $self->handle(); + my $buf = ''; + while (sysread($handle, $buf, 64*1024) > 0) { + print($phys $buf) or die($!); + } + close($handle); + close($phys) or die($!); + + return LXRng::Repo::TmpFile->new(dir => $tmpdir, + node => $self->node); +} + +1; diff --git a/lib/LXRng/Repo/Git/Iterator.pm b/lib/LXRng/Repo/Git/Iterator.pm new file mode 100644 index 0000000..978e584 --- /dev/null +++ b/lib/LXRng/Repo/Git/Iterator.pm @@ -0,0 +1,33 @@ +package LXRng::Repo::Git::Iterator; + +use strict; +use LXRng::Repo::Git::File; + +sub new { + my ($class, $repo, $release) = @_; + + my @refs; + my $git = $repo->_git_cmd('ls-tree', '-r', $release); + while (<$git>) { + if (/\S+\s+blob\s+(\S+)\s+(\S+)/) { + push(@refs, [$2, $1]); + } + } + close($git); + + return bless({refs => \@refs, repo => $repo, rel => $release}, $class); +} + +sub next { + my ($self) = @_; + + return undef unless @{$$self{'refs'}} > 0; + my $file = shift(@{$$self{'refs'}}); + + return LXRng::Repo::Git::File->new($$self{'repo'}, + $$file[0], + $$file[1], + $$self{'rel'}); +} + +1; diff --git a/lib/LXRng/Repo/Git/TarFile.pm b/lib/LXRng/Repo/Git/TarFile.pm new file mode 100644 index 0000000..33af87d --- /dev/null +++ b/lib/LXRng/Repo/Git/TarFile.pm @@ -0,0 +1,98 @@ +package LXRng::Repo::Git::TarFile; + +use strict; +use File::Temp qw(tempdir); +use Fcntl qw(F_GETFD F_SETFD FD_CLOEXEC); + +use base qw(LXRng::Repo::File); + +sub new { + my ($class, $tar, $ref) = @_; + + return bless({tar => $tar, ref => $ref}, $class); +} + +sub name { + my ($self) = @_; + + return $$self{'tar'}->name(); +} + +sub node { + my ($self) = @_; + + $self->name =~ m,.*/([^/]+), and return $1; +} + +sub time { + my ($self) = @_; + + return $$self{'tar'}->mtime(); +} + +sub size { + my ($self) = @_; + + return $$self{'tar'}->size; +} + +sub phys_path { + my ($self) = @_; + + my $tmpdir = tempdir() or die($!); + open(my $phys, ">", $tmpdir.'/'.$self->node); + + my $data = $$self{'tar'}->get_content_by_ref(); + my $len = $$self{'tar'}->size(); + my $pos = 0; + while ($pos < $len) { + print($phys substr($$data, $pos, 64*1024)); + $pos += 64*1024; + } + close($phys); + + return LXRng::Repo::Git::TarFile::Virtual->new(dir => $tmpdir, + node => $self->node); +} + +sub handle { + my ($self) = @_; + + my $data = $$self{'tar'}->get_content_by_ref(); + open(my $fh, "<", $data); + + return $fh; +} + +sub revision { + my ($self) = @_; + + $$self{'ref'} ||= $self->time.'.'.$self->size; + return $$self{'ref'}; +} + +package LXRng::Repo::Git::TarFile::Virtual; + +use strict; +use overload '""' => \&filename; + +sub new { + my ($class, %args) = @_; + + return bless(\%args, $class); +} + +sub filename { + my ($self) = @_; + + return $$self{'dir'}.'/'.$$self{'node'}; +} + +sub DESTROY { + my ($self) = @_; + unlink($$self{'dir'}.'/'.$$self{'node'}); + rmdir($$self{'dir'}); +# kill(9, $$self{'pid'}); +} + +1; diff --git a/lib/LXRng/Repo/Plain.pm b/lib/LXRng/Repo/Plain.pm new file mode 100644 index 0000000..c30835e --- /dev/null +++ b/lib/LXRng/Repo/Plain.pm @@ -0,0 +1,38 @@ +package LXRng::Repo::Plain; + +use strict; +use LXRng::Repo::Plain::Iterator; +use LXRng::Repo::Plain::File; +use LXRng::Repo::Plain::Directory; + +sub new { + my ($class, $root) = @_; + + return bless({root => $root}, $class); +} + +sub allversions { + my ($self) = @_; + + my @ver = (sort + grep { $_ ne "." and $_ ne ".." } + map { substr($_, length($$self{'root'})) =~ /([^\/]*)/; $1 } + glob($$self{'root'}."*/")); + + return @ver; +} + +sub node { + my ($self, $path, $release) = @_; + + my $realpath = join('/', $$self{'root'}, $release, $path); + return LXRng::Repo::Plain::File->new($path, $realpath); +} + +sub iterator { + my ($self, $release) = @_; + + return LXRng::Repo::Plain::Iterator->new($self->node('', $release)); +} + +1; diff --git a/lib/LXRng/Repo/Plain/Directory.pm b/lib/LXRng/Repo/Plain/Directory.pm new file mode 100644 index 0000000..8d7e701 --- /dev/null +++ b/lib/LXRng/Repo/Plain/Directory.pm @@ -0,0 +1,45 @@ +package LXRng::Repo::Plain::Directory; + +use strict; + +use base qw(LXRng::Repo::Directory); + +sub new { + my ($class, $name, $path, $stat) = @_; + + $name =~ s,(.)/*$,$1/,; + $path =~ s,/*$,/,; + return bless({name => $name, path => $path, stat => $stat}, $class); +} + +sub time { + my ($self) = @_; + + return $$self{'stat'}[9]; +} + +sub size { + my ($self) = @_; + + return ''; +} + +sub contents { + my ($self) = @_; + + my (@dirs, @files); + my ($dir, $node); + opendir($dir, $$self{'path'}) or die("Can't open ".$$self{'path'}.": $!"); + while (defined($node = readdir($dir))) { + next if $node =~ /^\.|~$|\.orig$/; + next if $node eq 'CVS'; + + push(@files, LXRng::Repo::Plain::File->new($$self{'name'}.$node, + $$self{'path'}.$node)); + } + closedir($dir); + + return sort { ref($a) cmp ref($b) || $$a{'name'} cmp $$b{'name'} } @files; +} + +1; diff --git a/lib/LXRng/Repo/Plain/File.pm b/lib/LXRng/Repo/Plain/File.pm new file mode 100644 index 0000000..cf2d6d5 --- /dev/null +++ b/lib/LXRng/Repo/Plain/File.pm @@ -0,0 +1,51 @@ +package LXRng::Repo::Plain::File; + +use strict; + +use base qw(LXRng::Repo::File); +use Fcntl; + +sub new { + my ($class, $name, $path) = @_; + + my @stat = stat($path); + + return undef unless @stat; + + return LXRng::Repo::Plain::Directory->new($name, $path, \@stat) if -d _; + + return bless({name => $name, path => $path, stat => \@stat}, $class); +} + +sub time { + my ($self) = @_; + + return $$self{'stat'}[9]; +} + +sub size { + my ($self) = @_; + + return $$self{'stat'}[7]; +} + +sub phys_path { + my ($self) = @_; + + return $$self{'path'}; +} + +sub revision { + my ($self) = @_; + + return $self->time.'.'.$self->size; +} + +sub handle { + my ($self) = @_; + + sysopen(my $handle, $self->phys_path, O_RDONLY) or die($!); + return $handle; +} + +1; diff --git a/lib/LXRng/Repo/Plain/Iterator.pm b/lib/LXRng/Repo/Plain/Iterator.pm new file mode 100644 index 0000000..b086860 --- /dev/null +++ b/lib/LXRng/Repo/Plain/Iterator.pm @@ -0,0 +1,29 @@ +package LXRng::Repo::Plain::Iterator; + +use strict; +use LXRng::Repo::Plain; + +sub new { + my ($class, $dir) = @_; + + return bless({dir => $dir, stack => [], nodes => [$dir->contents]}, $class); +} + +sub next { + my ($self) = @_; + + while (@{$$self{'nodes'}} == 0) { + return undef unless @{$$self{'stack'}}; + $$self{'nodes'} = pop(@{$$self{'stack'}}); + } + + my $node = shift(@{$$self{'nodes'}}); + if ($node->isa('LXRng::Repo::Directory')) { + push(@{$$self{'stack'}}, $$self{'nodes'}); + $$self{'nodes'} = [$node->contents]; + return $self->next; + } + return $node; +} + +1; diff --git a/lib/LXRng/Repo/TmpFile.pm b/lib/LXRng/Repo/TmpFile.pm new file mode 100644 index 0000000..bc9024a --- /dev/null +++ b/lib/LXRng/Repo/TmpFile.pm @@ -0,0 +1,30 @@ +package LXRng::Repo::TmpFile; + +# This package is used to hold on to a reference to a physical copy of +# a file normally only present inside a repo of some sort. When it +# leaves scopy, the destructor will remove it. (The object acts as +# string containing the path of the physical manifestation of the +# file.) + +use strict; +use overload '""' => \&filename; + +sub new { + my ($class, %args) = @_; + + return bless(\%args, $class); +} + +sub filename { + my ($self) = @_; + + return $$self{'dir'}.'/'.$$self{'node'}; +} + +sub DESTROY { + my ($self) = @_; + unlink($$self{'dir'}.'/'.$$self{'node'}); + rmdir($$self{'dir'}); +} + +1; |