OSSP CVS Repository

ossp - ossp-pkg/cvsfusion/cvsfusion.pl
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/cvsfusion/cvsfusion.pl
#!/usr/opkg/bin/perl
##
##  OSSP cvsfusion - CVS Repository Fusion
##  Copyright (c) 2004 Ralf S. Engelschall <rse@engelschall.com>
##  Copyright (c) 2004 The OSSP Project <http://www.ossp.org/>
##  Copyright (c) 2004 Cable & Wireless <http://www.cw.com/>
##
##  This file is part of OSSP cvsfusion, a CVS repository fusion
##  utility which can be found at http://www.ossp.org/pkg/tool/cvsfusion/.
##
##  Permission to use, copy, modify, and distribute this software for
##  any purpose with or without fee is hereby granted, provided that
##  the above copyright notice and this permission notice appear in all
##  copies.
##
##  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
##  SUCH DAMAGE.
##
##  cvsfusion.pl: main program
##

require 5;
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 = {
    'name' => "cvsfusion",
    'vers' => "0.0.1",
    'date' => "20-Apr-2004"
};

#   program parameters (defaults)
my $opt = {
    'version'        => 0,
    'verbose'        => 0,
    'help'           => 0,
    'tmpdir'         => '',
    'cvsroot-source' => '',
    'cvsroot-target' => '',
    'cvs-module'     => [],
    'cvs-branch'     => '',
    'prog-rcs'       => "rcs",
    'prog-co'        => "co",
    'prog-diff'      => "diff"
};

#   exception handling support
$SIG{__DIE__} = sub {
    my ($err) = @_;
    $err =~ s|\s+at\s+.*||s if (not $opt->{'verbose'});
    print STDERR $prog->{'name'} . ":ERROR: $err ". ($! ? "($!)" : "") . "\n";
    exit(1);
};

#   command line parsing
Getopt::Long::Configure("bundling");
my $result = GetOptions(
    'V|version'           => \$opt->{'version'},
    'v|verbose'           => \$opt->{'verbose'},
    'h|help'              => \$opt->{'help'},
    't|tmpdir=s'          => \$opt->{'tmpdir'},
    'f|cvsroot-source=s'  => \$opt->{'cvsroot-source'},
    'l|cvsroot-target=s'  => \$opt->{'cvsroot-target'},
    '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'}) {
    print "Usage: ".$prog->{'name'}." [options]\n" .
          "Available options:\n" .
          " -V,--version             print program version\n" .
          " -v,--verbose             enable verbose run-time mode\n" .
          " -h,--help                print out this usage page\n" .
          " -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" .
          " -m,--cvs-module=SUBDIR   selects the CVS repository module(s)\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'}) {
    print "OSSP ".$prog->{'name'}." ".$prog->{'vers'}." (".$prog->{'date'}.")\n";
    exit(0);
}

#   verbose message printing
sub msg_verbose {
    my ($msg) = @_;
    print STDERR "$msg\n" if ($opt->{'verbose'});
}

#   warning message printing
sub msg_warning {
    my ($msg) = @_;
    print STDERR $prog->{'name'}.":WARNING: $msg\n";
}

#   error message printing
sub msg_error { 
    my ($msg) = @_;
    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);

#   determine branch tag and revision
my ($branch_tag, $branch_rev) = ($opt->{'cvs-branch'} =~ m/^(.+):(.+)$/);

#   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 @revs = $rcs->lookup();
    printf("    (%d revisions)\n", scalar(@revs));

    #   move all source revisions onto target branch
    foreach my $rev (@revs) {
        my $num = $rev->revision();
        $rev->revision($branch_rev.".".$num);
    }

    #   transform source trunk into regular branch
    #   FIXME

    #   merge all source revisions into target
    #   FIXME

    #   attach merged revisions onto source revision 1.1
    #   (and create a dead revision 1.1 if none exists)
    #   FIXME

    #   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;
#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<OSSP cvsfusion> - CVS Repository Fusion

=head1 SYNOPSIS

B<cvsfusion>
[B<--verbose>]
[B<--tmpdir=>I<dir>]
[B<--cvsroot-source=>I<dir>]
[B<--cvsroot-target=>I<dir>]
[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>
[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


CVSTrac 2.0.1