Index: ossp-pkg/cvsfusion/00BRAINSTORM RCS File: /v/ossp/cvs/ossp-pkg/cvsfusion/00BRAINSTORM,v rcsdiff -q -kk '-r1.1' '-r1.2' -u '/v/ossp/cvs/ossp-pkg/cvsfusion/00BRAINSTORM,v' 2>/dev/null --- 00BRAINSTORM 2004/04/22 06:56:28 1.1 +++ 00BRAINSTORM 2004/04/24 18:11:57 1.2 @@ -47,3 +47,26 @@ } } +# apply a translation function onto all RCS revisions +sub revapply ($$) { + my ($self, $sub) = @_; + foreach my $num (keys(%{$self->{'rcs'}->{'rev'}})) { + $self->{'rcs'}->{'rev'}->{$num} = + &$sub($self->{'rcs'}->{'rev'}->{$num}); + } + return; +} + +# get and/or set paths to external tools +sub tool ($;$) { + my ($self, $tool, $path) = @_; + my $old = $self->{'tool'}->{$tool}; + if (not defined($old)) { + croak "tool \"$tool\" not known"; + } + if (defined($path)) { + $self->{'tool'}->{$tool} = $path; + } + return $old; +} + Index: ossp-pkg/cvsfusion/cvsfusion.pl RCS File: /v/ossp/cvs/ossp-pkg/cvsfusion/cvsfusion.pl,v rcsdiff -q -kk '-r1.2' '-r1.3' -u '/v/ossp/cvs/ossp-pkg/cvsfusion/cvsfusion.pl,v' 2>/dev/null --- cvsfusion.pl 2004/04/23 09:23:38 1.2 +++ cvsfusion.pl 2004/04/24 18:11:57 1.3 @@ -30,12 +30,25 @@ ## require 5; -use strict; -use warnings; use lib "."; use Getopt::Long; use IO::File; +use File::Temp qw(tempfile tempdir); +use File::Find; +use File::Path; +use File::Copy; +use File::Basename; +use Cwd; use RCS; +use strict; +use warnings; +no warnings 'File::Find'; + +## _________________________________________________________________________ +## +## Prolog +## _________________________________________________________________________ +## # program information my $prog = { @@ -49,12 +62,13 @@ 'version' => 0, 'verbose' => 0, 'help' => 0, - 'tmpdir' => ($ENV{TMPDIR} || "/tmp") . "/" . $prog->{'name'}, + 'tmpdir' => '', 'cvsroot-source' => '', 'cvsroot-target' => '', - 'cvs-branch' => '', 'cvs-module' => [], + 'cvs-branch' => '', 'prog-rcs' => "rcs", + 'prog-co' => "co", 'prog-diff' => "diff" }; @@ -75,12 +89,13 @@ 't|tmpdir=s' => \$opt->{'tmpdir'}, 'f|cvsroot-source=s' => \$opt->{'cvsroot-source'}, 'l|cvsroot-target=s' => \$opt->{'cvsroot-target'}, - 'b|cvs-branch=s' => \$opt->{'cvs-branch'}, 'm|cvs-module=s@' => $opt->{'cvs-module'}, + 'b|cvs-branch=s' => \$opt->{'cvs-branch'}, 'R|prog-rcs=s' => \$opt->{'prog-rcs'}, + 'C|prog-co=s' => \$opt->{'prog-co'}, 'D|prog-diff=s' => \$opt->{'prog-diff'}, ) || die "option parsing failed"; -if ($opt->{-help}) { +if ($opt->{'help'}) { print "Usage: ".$prog->{'name'}." [options]\n" . "Available options:\n" . " -V,--version print program version\n" . @@ -89,13 +104,14 @@ " -t,--tmpdir=DIR filesystem path to temporary directory\n" . " -f,--cvsroot-source=DIR filesystem path to source CVS repository\n" . " -l,--cvsroot-target=DIR filesystem path to target CVS repository\n" . - " -b,--cvs-branch=TAG:REV selects the CVS branch tag and revision to use\n" . " -m,--cvs-module=SUBDIR selects the CVS repository module(s)\n" . - " -R,--prog-rcs=FILE filesystem path to rcs(1) program\n" . - " -D,--prog-diff=FILE filesystem path to diff(1) program\n"; + " -b,--cvs-branch=TAG:REV selects the CVS branch tag/revision to use\n" . + " -R,--prog-rcs=FILE filesystem path to GNU RCS' rcs(1) program\n" . + " -C,--prog-co=FILE filesystem path to GNU RCS' co(1) program\n" . + " -D,--prog-diff=FILE filesystem path to GNU DiffUtils' diff(1) program\n"; exit(0); } -if ($opt->{-version}) { +if ($opt->{'version'}) { print "OSSP ".$prog->{'name'}." ".$prog->{'vers'}." (".$prog->{'date'}.")\n"; exit(0); } @@ -118,23 +134,136 @@ print STDERR $prog->{'name'}.":ERROR: $msg\n"; } +## _________________________________________________________________________ +## +## Main Procedure +## _________________________________________________________________________ +## + +# sanity check parameters +my $error = 0; +if ($opt->{'cvsroot-source'} eq '') { + &msg_error("no source CVSROOT specified (use option --cvsroot-source=DIR)"); + $error++; +} +elsif (not -d $opt->{'cvsroot-source'}) { + &msg_error("source CVSROOT not a valid directory: ".$opt->{'cvsroot-source'}); + $error++; +} +elsif (not -d $opt->{'cvsroot-source'}."/CVSROOT") { + &msg_error("source CVSROOT not a valid CVS repository: ".$opt->{'cvsroot-source'}); + $error++; +} +if ($opt->{'cvsroot-target'} eq '') { + &msg_error("no target CVSROOT specified (use option --cvsroot-target=DIR)"); + $error++; +} +elsif (not -d $opt->{'cvsroot-target'}) { + &msg_error("target CVSROOT not a valid directory: ".$opt->{'cvsroot-target'}); + $error++; +} +elsif (not -d $opt->{'cvsroot-target'}."/CVSROOT") { + &msg_error("target CVSROOT not a valid CVS repository: ".$opt->{'cvsroot-target'}); + $error++; +} +if (@{$opt->{'cvs-module'}} == 0) { + &msg_error("no source CVS module(s) specified (use option --cvs-module=SUBDIR)"); + $error++; +} +else { + my $modules = []; + foreach my $module (@{$opt->{'cvs-module'}}) { + foreach my $m (split(/,/, $module)) { + push(@{$modules}, $m); + } + } + $opt->{'cvs-module'} = $modules; + foreach my $module (@{$opt->{'cvs-module'}}) { + if (not -d $opt->{'cvsroot-source'}."/".$module) { + &msg_error("invalid source CVS module: $module"); + $error++; + } + } +} +if ($opt->{'cvs-branch'} eq '') { + &msg_error("no target CVS branch tag/revision specified (use option --cvs-branch=TAG:REV)"); + $error++; +} +elsif ($opt->{'cvs-branch'} !~ m/^[A-Z][A-Z0-9_]+:\d+(\.\d+\.\d+)+$/) { + &msg_error("invalid target CVS branch tag/revision: ". $opt->{'cvs-branch'}); + $error++; +} +exit(1) if ($error > 0); + +# determine temporary directory +if ($opt->{'tmpdir'} eq '') { + $opt->{'tmpdir'} = tempdir($prog->{'name'}.".XXXXXXXXXX", TMPDIR => 1, CLEANUP => 1); +} +#my ($fh, $filename) = tempfile(DIR => $opt->{'tmpdir'}, SUFFIX => ',v'); + +# determine information about RCS files in source CVS repository +my $cvs = {}; +$cvs->{'source'} = []; +my $cwd = getcwd(); +chdir($opt->{'cvsroot-source'}); +sub find_cb { + push(@{$cvs->{'source'}}, $File::Find::name) if (-f $_); +} +find(\&find_cb, @{$opt->{'cvs-module'}}); +chdir($cwd); + +# iterate over all RCS files in source CVS repository +foreach my $source (sort @{$cvs->{'source'}}) { + print " $source\n"; + + # load source file + my $rcs = new RCS; + $rcs->load($opt->{'cvsroot-source'} . "/". $source); + my @rev = $rcs->lookup(); + printf(" (%d revisions)\n", scalar(@rev)); + + # save target file + my $dirname = dirname($opt->{'cvsroot-target'} . "/". $source); + mkpath($dirname) if (not -d $dirname); + $rcs->save($opt->{'cvsroot-target'} . "/". $source); + $rcs->destroy; +} + +## _________________________________________________________________________ +## +## Utility Functions +## _________________________________________________________________________ +## + ## TEST -my $rcs = new RCS; -$rcs->tool("rcs", "/usr/bin/rcs"); -$rcs->tool("co", "/usr/bin/co"); -$rcs->tool("diff", "/usr/opkg/bin/diff"); -$rcs->load("bash.spec,v"); -$rcs->save("bash.spec,v.new"); -undef $rcs; +#my $rcs = new RCS; +#my $file = "sample/openpkg-bash.spec,v"; +#$rcs->load("$file"); +##$rcs->revapply(sub { my ($rev) = @_; $rev =~ s/^1\./1.42.1./; return $rev; }); +#$rcs->save("$file.new"); +##exit(0); +#foreach my $file (glob("sample/*,v")) { + #print "loading $file\n"; + #$rcs->load("$file"); + #print "saving $file\n"; + #$rcs->save("$file.new"); +#} +#undef $rcs; __END__ +## _________________________________________________________________________ +## +## Manual Page +## _________________________________________________________________________ +## + =pod =head1 NAME -B - CVS Repository Fusion +B - CVS Repository Fusion =head1 SYNOPSIS @@ -143,16 +272,92 @@ [B<--tmpdir=>I] [B<--cvsroot-source=>I] [B<--cvsroot-target=>I] -[B<--cvs-branch=>IB<:>I] [B<--cvs-module=>I] +[B<--cvs-branch=>IB<:>I] [B<--prog-rcs=>I] +[B<--prog-co=>I] [B<--prog-diff=>I] B -[--version] -[--help] +[B<--version>] +[B<--help>] =head1 DESCRIPTION +B is a tool for merging two Concurrent Versions Systems +(CVS) repositories by attaching the trunk (and all its branches) of the +source repository as a regular branch onto the target repository. It +achieves this by directly operating on the level of the CVS underlying +I (RCS) files. The intention is to have the +(usually foreign vendor) source repository available as a whole in +the (usually own local) target repository for convenient comparisons, +merges, etc. It is considered a higher-level form of the CVS vendor +branch functionality. + +=head1 OPTIONS + +=over 4 + +=item B<--verbose> + +... + +=item B<--tmpdir=>I + +... + +=item B<--cvsroot-source=>I + +... + +=item B<--cvsroot-target=>I + +... + +=item B<--cvs-module=>I + +... + +=item B<--cvs-branch=>IB<:>I + +... + +=item B<--prog-rcs=>I + +... + +=item B<--prog-co=>I + +... + +=item B<--prog-diff=>I + +... + +=item B<--version> + +... + +=item B<--help> + +... + +=back + +=head1 SEE ALSO + +cvs(1). + +=head1 HISTORY + +B was implemented in April 2004 for use in B +project in order to provide a more powerful contributor environment +where the B CVS repository is regularily merged into the local +CVS repository of the contributor. + +=head1 AUTHOR + +Ralf S. Engelschall Erse@engelschall.comE + =cut