--- 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<cvsfusion> - CVS Repository Fusion
+B<OSSP cvsfusion> - CVS Repository Fusion
=head1 SYNOPSIS
@@ -143,16 +272,92 @@
[B<--tmpdir=>I<dir>]
[B<--cvsroot-source=>I<dir>]
[B<--cvsroot-target=>I<dir>]
-[B<--cvs-branch=>I<tag>B<:>I<rev>]
[B<--cvs-module=>I<subdir>]
+[B<--cvs-branch=>I<tag>B<:>I<rev>]
[B<--prog-rcs=>I<file>]
+[B<--prog-co=>I<file>]
[B<--prog-diff=>I<file>]
B<cvsfusion>
-[--version]
-[--help]
+[B<--version>]
+[B<--help>]
=head1 DESCRIPTION
+B<OSSP cvsfusion> 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<Revision Control System> (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<dir>
+
+...
+
+=item B<--cvsroot-source=>I<dir>
+
+...
+
+=item B<--cvsroot-target=>I<dir>
+
+...
+
+=item B<--cvs-module=>I<subdir>
+
+...
+
+=item B<--cvs-branch=>I<tag>B<:>I<rev>
+
+...
+
+=item B<--prog-rcs=>I<file>
+
+...
+
+=item B<--prog-co=>I<file>
+
+...
+
+=item B<--prog-diff=>I<file>
+
+...
+
+=item B<--version>
+
+...
+
+=item B<--help>
+
+...
+
+=back
+
+=head1 SEE ALSO
+
+cvs(1).
+
+=head1 HISTORY
+
+B<OSSP cvsfusion> was implemented in April 2004 for use in B<OpenPKG>
+project in order to provide a more powerful contributor environment
+where the B<OpenPKG> CVS repository is regularily merged into the local
+CVS repository of the contributor.
+
+=head1 AUTHOR
+
+Ralf S. Engelschall E<lt>rse@engelschall.comE<gt>
+
=cut
|