package VCfs::Subversion; use strict; use warnings; use base qw(VCfs); use Carp; use File::Basename qw( dirname ); sub new { my $class = shift; my $self = $class->_init(@_); $self->{vcs} = "svn"; $self->{vcs_cmd} = "svn"; return $self; } sub is_svn { 1 } sub _detect { my $class = shift; my $dir = shift; return -d "$dir/.svn" ? 1 : 0; } sub get_log { my $self = shift; my ($target, %opts) = @_; my @args = $opts{args} ? @{$opts{args}} : (); unshift @args, "-x" if $self->is_svk; my $out = $self->_do_run('log', @args, $target)->{out}; return split /\n/, $out; } sub get_log_times { my $self = shift; # XXX maybe want this regex for other things? - get_summary_lines ? my @l = grep(/^r\d+:?.*\|\s/, $self->get_log(@_) ); my @times; foreach my $s (@l) { # XXX also, usable in other areas if($self->is_svk) { $s =~ s/^(r\d+):\s*/$1 | /; } my ($r, $u, $d, $else) = split(/\s\|\s/, $s, 4); $else ||= ''; #warn "split into ", join("#", $r, $u, $d, $else), "\n"; push(@times, $d); } return(@times); } sub get_info { my $self = shift; my $out = $self->_do_run('info')->{out}; my %info; foreach my $line (split(/\n/, $out)) { my ($key, $val) = split(/ *: */, $line, 2); $key = lc($key); $key =~ s/ +/_/g; $key =~ s/__+/_/g; $key =~ s/[^a-z0-9_]+//g; exists($info{$key}) and die "oops $key twice in $out"; $info{$key} = $val; } return(%info); } sub taglist { my $self = shift; return(map({s#/$##;$_} $self->list($self->tag_dir))); } sub tag_dir { my $self = shift; my %info = $self->get_info; my $url = $info{url}; my $tagdir = $url; $tagdir =~ s/trunk$/tags\// or die "eek, $url not trunk?"; return($tagdir); } sub taggit { my $self = shift; my ($name, %opts) = @_; ($name =~ m#/#) and die "improper tagname $name"; my %info = $self->get_info; my $url = $info{url}; die "I can't taggit() on type ", $self->vcs_command, " yet" unless($url); # TODO svk support # TODO config-file and/or propval layout? my $trunk = $url; # could also be a branch I guess my $tagdir = $url; $tagdir =~ s{(?:trunk|branches/[^/]+)/?$}{tags/} or croak("eek, $url not trunk|branches?"); my $tagdest = $tagdir . $name; # Bah! svn doesn't prevent copying into an existing tag directory (at # least not in any form that I can see.) #warn $self->list($tagdir); my @has = grep(/^\Q$name\E\/$/, $self->list($tagdir)); @has and die "tag '$name' already exists in $tagdir"; my $message = $opts{message}; $message = "tagging $name" unless(defined($message)); $self->_do_run('copy', $trunk, $tagdest, '--message', $message); } sub add { my $self = shift; my @files = @_; my $r = $self->_do_run('add', @files); $r->{err} and warn "eek! $r->{err} ($r->{status})"; $r->{ret} or warn "eek"; # XXX or should parse output and return number of added files? return($r->{ret}); } sub remove { my $self = shift; my @files = @_; my $r = $self->_do_run('remove', @files); $r->{err} and warn "eek! $r->{err} ($r->{status})"; $r->{ret} or warn "eek"; # XXX or should parse output and return number of added files? return $r->{ret}; } sub commit { my $self = shift; my ($message, @files) = @_; @files or die; my $r = $self->_do_run('commit', '-m', $message, @files); $r->{err} and warn "eek! $r->{err} ($r->{status})"; $r->{ret} or warn "eek"; # XXX or should return what? return($r->{ret}); } sub update { my $self = shift; return $self->_do_run('update'); } sub list { my $self = shift; my ($path) = @_; $path or die; # XXX ? my $out = $self->_do_run('list', $path)->{out}; return(split(/\n/, $out)); } sub revert { my $self = shift; my (@files) = @_; @files or die "need files"; my @command = "revert"; my $out = $self->_do_run(@command, @files)->{out}; # TODO read the qr/Reverted '([^']+)'/ lines? warn $out; } sub status { my $self = shift; my @files = @_; my $r = $self->_do_run('status', @files); $r->{err} and warn "eek! $r->{err} ($r->{status})"; $r->{ret} or warn "eek"; $r->{out} or return(); return map { reverse(split(/\s+/, $_, 2)) } split /\n/, $r->{out}; } sub propget { my $self = shift; my ($prop, $file) = @_; my $out = $self->_do_run('propget', $prop, $file)->{out}; defined($out) or croak("nothing there"); die "this is unfinished"; } sub propset { my $self = shift; my ($prop, $val, @files) = @_; if(ref($val)) { UNIVERSAL::isa($val, 'ARRAY') or die; $val = join("\n", @$val); } my $r = $self->_do_run('propset', $prop, $val, @files); $r->{err} and warn "eek! $r->{err} ($r->{status})"; $r->{ret} or warn "eek"; return($r->{ret}); } # vi:sw=2:ts=2:et:sta 1;