/[svn2cvs]/trunk/svn2cvs.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/svn2cvs.pl

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3 by dpavlin, Tue Mar 9 21:45:32 2004 UTC revision 47 by dpavlin, Fri Nov 2 12:11:31 2007 UTC
# Line 8  Line 8 
8  # http://raw.no/personal/blog  # http://raw.no/personal/blog
9  #  #
10  # 2004-03-09 Dobrica Pavlinusic <dpavlin@rot13.org>  # 2004-03-09 Dobrica Pavlinusic <dpavlin@rot13.org>
11    #
12    # documentation is after __END__
13    
14  use strict;  use strict;
15  use File::Temp qw/ tempdir /;  use File::Temp qw/ tempdir /;
16    use File::Path;
17  use Data::Dumper;  use Data::Dumper;
18  use XML::Simple;  use XML::Simple;
19    
20  # get current user home directory  # do we want to sync just part of repository?
21  my $HOME = $ENV{'HOME'} || die "can't get home directory!";  my $partial_import = 1;
22    
23    # do we want to add svk-like prefix with original revision, author and date?
24    my $decorate_commit_message = 1;
25    
26  # cvsroot directory  if ( @ARGV < 2 ) {
27  my $CVSROOT="$HOME/x/cvsroot";          print "usage: $0 SVN_URL CVSROOT CVSREPOSITORY\n";
28  # name of cvs repository to commit to          exit 1;
29  my $CVSREP="svn2cvs";  }
   
 # svnroot directory  
 my $SVNROOT="file://$HOME/private/svn/svn2cvs";  
 # name of respository  
 my $SVNREP="trunk";  
   
 # webpac example  
 #$CVSROOT="$HOME/x/cvsroot";  
 #$CVSREP="webpac";  
 #$SVNROOT="file://$HOME/private/svn/webpac/";  
 #$SVNREP="trunk";  
30    
31  my $TMPDIR=tempdir( "/tmp/checkoutXXXXX", CLEANUP => 1 );  my ( $SVNROOT, $CVSROOT, $CVSREP ) = @ARGV;
32    
33    if ( $SVNROOT !~ m,^[\w+]+:///*\w+, ) {
34            print "ERROR: invalid svn root $SVNROOT\n";
35            exit 1;
36    }
37    
38  chdir($TMPDIR) || die "can't cd to $TMPDIR: $!";  # Ensure File::Temp::END can clean up:
39    $SIG{__DIE__} = sub { chdir("/tmp"); die @_ };
40    
41    my $TMPDIR = tempdir( "/tmp/checkoutXXXXX", CLEANUP => 1 );
42    
43    sub cd_tmp {
44            chdir($TMPDIR) || die "can't cd to $TMPDIR: $!";
45    }
46    
47    sub cd_rep {
48            chdir("$TMPDIR/$CVSREP") || die "can't cd to $TMPDIR/$CVSREP: $!";
49    }
50    
51    print "## using TMPDIR $TMPDIR\n";
52    
53  # cvs command with root  # cvs command with root
54  my $cvs="cvs -d $CVSROOT";  my $cvs = "cvs -f -d $CVSROOT";
55    
56    # current revision in CVS
57    my $rev;
58    
59  #  #
60  # sub to do logging and system calls  # sub to do logging and system calls
61  #  #
62  sub log_system($$) {  sub log_system($$) {
63          my ($cmd,$errmsg) = @_;          my ( $cmd, $errmsg ) = @_;
64          print STDERR "## $cmd\n";          print STDERR "## $cmd\n";
65          system $cmd || die "$errmsg: $!";          system($cmd) == 0 || die "$errmsg: $!";
66  }  }
67    
68  #  #
69  # sub to commit .svn rev file later  # sub to commit .svn rev file later
70  #  #
71  sub commit_svnrev {  sub commit_svnrev {
72          my $rev = shift @_;          my $rev     = shift @_;
73          my $add_new = shift @_;          my $add_new = shift @_;
74    
75          die "commit_svnrev needs revision" if (! defined($rev));          die "commit_svnrev needs revision" if ( !defined($rev) );
76    
77          open(SVNREV,"> $CVSREP/.svnrev") || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";          open( SVNREV, "> .svnrev" )
78                    || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";
79          print SVNREV $rev;          print SVNREV $rev;
80          close(SVNREV);          close(SVNREV);
81    
82          my $path=".svnrev";          my $path = ".svnrev";
83    
84          if ($add_new) {          if ($add_new) {
85                  system "$cvs add $CVSREP/$path" || die "cvs add of $path failed: $!";                  system "$cvs add '$path'" || die "cvs add of $path failed: $!";
86            } else {
87                    my $msg = "subversion revision $rev commited to CVS";
88                    print "$msg\n";
89                    system "$cvs commit -m '$msg' '$path'"
90                            || die "cvs commit of $path failed: $!";
91          }          }
   
         my $msg="subversion revision $rev commited to CVS";  
         print "$msg\n";  
         system "$cvs commit -m \"$msg\" $CVSREP/$path" || die "cvs commit of $path failed: $!";  
92  }  }
93    
94    sub add_dir($$) {
95            my ( $path, $msg ) = @_;
96            print "# add_dir($path)\n";
97            die "add_dir($path) is not directory" unless ( -d $path );
98    
99            my $curr_dir;
100    
101            foreach my $d ( split( m#/#, $path ) ) {
102                    $curr_dir .= ( $curr_dir ? '/' : '' ) . $d;
103    
104                    next if in_entries($curr_dir);
105                    next if ( -e "$curr_dir/CVS" );
106    
107                    log_system( "touch '$curr_dir/.keepme'", "creation of .keepme file (to keep $curr_dir alive in CVS) failed" );
108                    log_system( "$cvs add '$curr_dir' '$curr_dir/.keepme'", "cvs add of $curr_dir failed" );
109            }
110    }
111    
112  # ok, now do the checkout  # ok, now do the checkout
113    eval {
114            cd_tmp;
115            log_system( "$cvs -q checkout $CVSREP", "cvs checkout failed" );
116    };
117    
118  log_system("$cvs -q checkout $CVSREP","cvs checkout failed");  if ($@) {
119            print <<_NEW_REP_;
120    
121  my $rev;  There is no CVS repository '$CVSREP' in your CVS. I will assume that
122    this is import of new module in your CVS and start from revision 0.
123    
124    Press enter to continue importing new CVS repository or CTRL+C to abort.
125    
126    _NEW_REP_
127    
128            print "start import of new module [yes]: ";
129            my $in = <STDIN>;
130            cd_tmp;
131            mkdir($CVSREP) || die "can't create $CVSREP: $!";
132            cd_rep;
133    
134            open( SVNREV, "> .svnrev" ) || die "can't open $CVSREP/.svnrev: $!";
135            print SVNREV "0";
136            close(SVNREV);
137    
138            $rev = 0;
139    
140            # create new module
141            cd_rep;
142            log_system( "$cvs import -d -m 'new CVS module' $CVSREP svn r$rev",
143                    "import of new repository" );
144            cd_tmp;
145            rmtree($CVSREP) || die "can't remove $CVSREP";
146            log_system( "$cvs -q checkout $CVSREP", "cvs checkout failed" );
147            cd_rep;
148    
149    } else {
150    
151            # import into existing module directory in CVS
152    
153            cd_rep;
154    
155  # check if svnrev exists          # check if svnrev exists
156  if (! -e "$CVSREP/.svnrev") {          if ( !-e ".svnrev" ) {
157          print <<_USAGE_;                  print <<_USAGE_;
158    
159  Your CVS repository doesn't have .svnrev file!  Your CVS repository doesn't have .svnrev file!
160    
161  This file is used to keep CVS repository and SubVersion in sync, so  This file is used to keep CVS repository and Subversion in sync, so
162  that only newer changes will be commited.  that only newer changes will be commited.
163    
164  It's quote possible that this is first svn2cvs run for this repository.  It's quote possible that this is first svn2cvs run for this repository.
# Line 96  corresponds to current version of CVS re Line 167  corresponds to current version of CVS re
167  been checkouted.  been checkouted.
168    
169  If you migrated your cvs repository to svn using cvs2svn, this will be  If you migrated your cvs repository to svn using cvs2svn, this will be
170  last SubVersion revision. If this is initial run of conversion of  last Subversion revision. If this is initial run of conversion of
171  SubVersion repository to CVS, correct revision is 0.  Subversion repository to CVS, correct revision is 0.
172    
173  _USAGE_  _USAGE_
174    
175          print "svn revision corresponding to CVS [abort]: ";                  print "svn revision corresponding to CVS [abort]: ";
176          my $in = <STDIN>;                  my $in = <STDIN>;
177          chomp($in);                  chomp($in);
178          if ($in !~ /^\d+$/) {                  if ( $in !~ /^\d+$/ ) {
179                  print "Aborting: revision not a number\n";                          print "Aborting: revision not a number\n";
180                  exit 1;                          exit 1;
181                    } else {
182                            $rev = $in;
183                            commit_svnrev( $rev, 1 );    # create new
184                    }
185          } else {          } else {
186                  $rev = $in;                  open( SVNREV, ".svnrev" )
187                  commit_svnrev($rev,1);  # create new                          || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";
188                    $rev = <SVNREV>;
189                    chomp($rev);
190                    close(SVNREV);
191          }          }
 } else {  
         open(SVNREV,"$CVSREP/.svnrev") || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";  
         my $rev = <SVNREV>;  
         chomp($rev);  
         close(SVNREV);  
 }  
   
 print "Starting after revision $rev\n";  
 $rev++;  
192    
193            print "Starting after revision $rev\n";
194            $rev++;
195    }
196    
197  #  #
198  # FIXME!! HEAD should really be next verison and loop because this way we  # FIXME!! HEAD should really be next verison and loop because this way we
# Line 129  $rev++; Line 201  $rev++;
201  # case much about accuracy and completnes of logs there, this might  # case much about accuracy and completnes of logs there, this might
202  # be good. YMMV  # be good. YMMV
203  #  #
204  open(LOG, "svn log -r $rev:HEAD -v --xml $SVNROOT/$SVNREP |") || die "svn log for repository $SVNROOT/$SVNREP failed: $!";  open( LOG, "svn log -r $rev:HEAD -v --xml $SVNROOT |" )
205            || die "svn log for repository $SVNROOT failed: $!";
206  my $log;  my $log;
207  while(<LOG>) {  while (<LOG>) {
208          $log .= $_;          $log .= $_;
209  }  }
210  close(LOG);  close(LOG);
211    
212  my $xml = XMLin($log, ForceArray => [ 'logentry', 'path' ]);  my $xml;
213    eval { $xml = XMLin( $log, ForceArray => [ 'logentry', 'path' ] ); };
214    
215    #=begin log_example
216    #
217    #------------------------------------------------------------------------
218    #r256 | dpavlin | 2004-03-09 13:18:17 +0100 (Tue, 09 Mar 2004) | 2 lines
219    #
220    #ported r254 from hidra branch
221    #
222    #=cut
223    
224  =begin log_example  my $fmt = "\n" . "-" x 79 . "\nr%5s| %8s | %s\n\n%s\n";
   
 ------------------------------------------------------------------------  
 r256 | dpavlin | 2004-03-09 13:18:17 +0100 (Tue, 09 Mar 2004) | 2 lines  
225    
226  ported r254 from hidra branch  if ( !$xml->{'logentry'} ) {
227            print "no newer log entries in Subversion repostory. CVS is current\n";
228            exit 0;
229    }
230    
231  =cut  # return all files in CVS/Entries
232    sub entries($) {
233            my $dir = shift;
234            die "entries expects directory argument!" unless -d $dir;
235            my @entries;
236            open( my $fh, "./$dir/CVS/Entries" ) || return 0;
237            while (<$fh>) {
238                    if ( m{^D/([^/]+)}, ) {
239                            my $sub_dir = $1;
240                            warn "#### entries recurse into: $dir/$sub_dir";
241                            push @entries, map {"$sub_dir/$_"} entries("$dir/$sub_dir");
242                            push @entries, $sub_dir;
243                    } elsif (m{^/([^/]+)/}) {
244                            push @entries, $1;
245                    } elsif ( !m{^D$} ) {
246                            die "can't decode entries line: $_";
247                    }
248            }
249            close($fh);
250            warn "#### entries($dir) => ", join( "|", @entries );
251            return @entries;
252    }
253    
254  my $fmt = "\n" . "-" x 79 . "\nr%5s| %8s | %s\n\n%s\n";  # check if file exists in CVS/Entries
255    sub in_entries($) {
256            my $path = shift;
257            if ( $path =~ m,^(.*?/*)([^/]+)$, ) {
258                    my ( $dir, $file ) = ( $1, $2 );
259                    if ( $dir !~ m,/$, && $dir ne "" ) {
260                            $dir .= "/";
261                    }
262    
263  if (! $xml->{'logentry'}) {                  open( my $fh, "./$dir/CVS/Entries" )
264          print "no newer log entries in SubVersion repostory. CVS is current\n";                          || return 0;    #die "no entries file: $dir/CVS/Entries";
265          exit 0;                  while (<$fh>) {
266                            return 1 if (m{^D?/$file/});
267                    }
268                    close($fh);
269                    return 0;
270            } else {
271                    die "can't split '$path' to dir and file!";
272            }
273  }  }
274    
275  print Dumper($xml);  cd_tmp;
276    cd_rep;
277    
278  foreach my $e (@{$xml->{'logentry'}}) {  foreach my $e ( @{ $xml->{'logentry'} } ) {
279          die "BUG: revision from .svnrev ($rev) greater than from subversion (".$e->{'revision'}.")" if ($rev > $e->{'revision'});          die "BUG: revision from .svnrev ($rev) greater than from subversion ("
280                    . $e->{'revision'} . ")"
281                    if ( $rev > $e->{'revision'} );
282          $rev = $e->{'revision'};          $rev = $e->{'revision'};
283          log_system("svn export --force -q -r $rev $SVNROOT/$SVNREP $CVSREP", "svn export of revision $rev failed");          log_system( "svn export --force -q -r $rev $SVNROOT $TMPDIR/$CVSREP",
284                    "svn export of revision $rev failed" );
285    
286            # deduce name of svn directory
287            my $SVNREP  = "";
288            my $tmpsvn  = $SVNROOT || die "BUG: SVNROOT empty!";
289            my $tmppath = $e->{'paths'}->{'path'}->[0]->{'content'}
290                    || die "BUG: tmppath empty!";
291            do {
292                    if ( $tmpsvn =~ s#(/[^/]+)/*$## ) {    # vim fix
293                            $SVNREP = $1 . $SVNREP;
294                    } elsif ( $e->{'paths'}->{'path'}->[0]->{'copyfrom-path'} ) {
295                            print
296                                    "NOTICE: copyfrom outside synced repository ignored - skipping\n";
297                            next;
298                    } else {
299                            print "NOTICE: can't deduce svn dir from $SVNROOT - skipping\n";
300                            next;
301                    }
302            } until ( $tmppath =~ m/^$SVNREP/ );
303    
304            print "NOTICE: using $SVNREP as directory for svn\n";
305    
306            printf( $fmt,
307                    $e->{'revision'}, $e->{'author'}, $e->{'date'}, $e->{'msg'} );
308            my @commit;
309    
310            my $msg = $e->{'msg'};
311            $msg =~ s/'/'\\''/g;    # quote "
312    
313            $msg = 'r' . $rev . ' ' . $e->{author} . ' | ' . $e->{date} . "\n" . $msg
314                    if $decorate_commit_message;
315    
316          printf($fmt, $e->{'revision'}, $e->{'author'}, $e->{'date'}, $e->{'msg'});          sub cvs_commit {
317          foreach my $p (@{$e->{'paths'}->{'path'}}) {                  my $msg = shift || die "no msg?";
318                  my ($action,$path) = ($p->{'action'},$p->{'content'});                  if ( !@_ ) {
319                            warn "commit ignored, no files\n";
320                            return;
321                    }
322                    log_system(
323                            "$cvs commit -m '$msg' '" . join( "' '", @_ ) . "'",
324                            "cvs commit of " . join( ",",            @_ ) . " failed"
325                    );
326            }
327    
328            foreach my $p ( @{ $e->{'paths'}->{'path'} } ) {
329                    my ( $action, $path ) = ( $p->{'action'}, $p->{'content'} );
330    
331                    next if ( $path =~ m#/\.svnrev$# );
332    
333                  print "svn2cvs: $action $path\n";                  print "svn2cvs: $action $path\n";
334    
335                  # prepare path and message                  # prepare path and message
336                  my $file = $path;                  my $file = $path;
337                  $path =~ s,^/$SVNREP/*,, || die "BUG: can't strip SVNREP from path";                  if ( $path !~ s#^\Q$SVNREP\E/*## ) {
338                            print
339                                    "NOTICE: skipping '$path' which isn't under repository root '$SVNREP'\n";
340                            die unless $partial_import;
341                            next;
342                    }
343    
344                    if ( !$path ) {
345                            print "NOTICE: skipped this operation. Probably trunk creation\n";
346                            next;
347                    }
348    
349                  my $msg = $e->{'msg'};                  my $msg = $e->{'msg'};
350                  $msg =~ s/"/\\"/g;      # quote "                  $msg =~ s/'/'\\''/g;    # quote "
351    
352                    sub add_path {
353                            my $path = shift || die "no path?";
354    
355                  if ($action =~ /M/) {                          if ( -d $path ) {
356                          print "svn2cvs: modify $path -- nop\n";                                  add_dir( $path, $msg );
357                  } elsif ($action =~ /A/) {                          } elsif ( $path =~ m,^(.+)/[^/]+$, && !-e "$1/CVS/Root" ) {
358                          log_system("$cvs add -m \"$msg\" $CVSREP/$path", "cvs add of $path failed");                                  my $dir = $1;
359                  } elsif ($action =~ /D/) {                                  in_entries($dir) || add_dir( $dir, $msg );
360                          log_system("$cvs delete -m \"$msg\" $CVSREP/$path", "cvs delete of $path failed");                                  in_entries($path) || log_system( "$cvs add '$path'",
361                                            "cvs add of $path failed" );
362                            } else {
363                                    in_entries($path) || log_system( "$cvs add '$path'",
364                                            "cvs add of $path failed" );
365                            }
366                    }
367    
368                    if ( $action =~ /M/ ) {
369                            if ( in_entries($path) ) {
370                                    print "svn2cvs: modify $path -- nop\n";
371                            } else {
372                                    print "WARNING: modify $path which isn't in CVS, adding...\n";
373                                    add_path($path);
374                            }
375                    } elsif ( $action =~ /A/ ) {
376                            add_path($path);
377                    } elsif ( $action =~ /D/ ) {
378                            if ( -e $path ) {
379                                    if ( ! in_entries( $path ) ) {
380                                            print "WARNING: $path is not present in CVS, skipping...\n";
381                                            undef $path;
382                                    } elsif ( -d $path ) {
383                                            warn "#### remove directory: $path";
384                                            foreach my $f ( entries($path) ) {
385                                                    $f = "$path/$f";
386                                                    if ( -f $f ) {
387                                                            unlink($f) || die "can't delete file $f: $!";
388    
389                                                      #                                             } else {
390                                                      #                                                     rmtree($f) || die "can't delete dir $f: $!";
391                                                    }
392                                                    log_system( "$cvs delete '$f'",
393                                                            "cvs delete of file $f failed" );
394                                                    cvs_commit( $msg, $f );
395                                            }
396                                            log_system( "$cvs delete '$path'",
397                                                    "cvs delete of file $path failed" );
398                                            cvs_commit( $msg, $path );
399                                            log_system( "$cvs update -dP .",
400                                                    "cvs update -dP . failed" );
401                                            undef $path;
402                                    } else {
403                                            warn "#### remove file: $path";
404                                            unlink($path) || die "can't delete $path: $!";
405                                            log_system( "$cvs delete '$path'",
406                                                    "cvs delete of dir $path failed" );
407                                            cvs_commit( $msg, $path );
408                                            undef $path;
409                                    }
410                            } else {
411                                    print "WARNING: $path is not present, skipping...\n";
412                                    undef $path;
413                            }
414                  } else {                  } else {
415                          print "WARNING: action $action not implemented on $path. Bug or missing feature of $0\n";                          print
416                                    "WARNING: action $action not implemented on $path. Bug or missing feature of $0\n";
417                  }                  }
418    
419                  # now commit changes                  # save commits for later
420                  log_system("$cvs commit -m \"$msg\" $CVSREP/$path", "cvs commit of $path failed");                  push @commit, $path if ($path);
421    
422          }          }
423    
424            # now commit changes
425            cvs_commit( $msg, @commit );
426    
427          commit_svnrev($rev);          commit_svnrev($rev);
428  }  }
429    
430    # cd out of $CVSREP before File::Temp::END is called
431    chdir("/tmp") || die "can't cd to /tmp: $!";
432    
433  __END__  __END__
434    
435  svn export --force "$SVNROOT/$SVNREP" "$CVSREP"  =pod
436    
437  cd dotfiles  =head1 NAME
438    
439  for file in $(find -type f -not -path \*CVS\*); do  svn2cvs - save subversion commits to (read-only) cvs repository
440          FILE=$(basename $file)  
441          DIR=$(dirname $file)  =head1 SYNOPSIS
442          if ! grep -q "^/$FILE/" $DIR/CVS/Entries ; then  
443                  cvs add $file    ./svn2cvs.pl SVN_URL CVSROOT CVSREPOSITORY
444          fi  
445  done  Usage example (used to self-host this script):
446    
447  #cvs commit -m "Automatic commit from SVN"    ./svn2cvs.pl file:///home/dpavlin/private/svn/svn2cvs/trunk/ \
448                   :pserver:dpavlin@cvs.tigris.org:/cvs svn2cvs/src
449  #rm -rf $TMPDIR  
450    =head1 DESCRIPTION
451    
452    This script will allows you to commit changes made to Subversion repository to
453    (read-only) CVS repository manually or from Subversion's C<post-commit> hook.
454    
455    It's using F<.svnrev> file (which will be created on first run) in
456    B<CVSROOT/CVSREPOSITORY> to store last Subversion revision which was
457    committed into CVS.
458    
459    One run will do following things:
460    
461    =over 4
462    
463    =item *
464    checkout B<CVSREPOSITORY> from B<CVSROOT> to temporary directory
465    
466    =item *
467    check if F<.svnrev> file exists and create it if it doesn't
468    
469    =item *
470    loop through all revisions from current in B<CVSROOT/CVSREPOSITORY> (using
471    F<.svnrev>) up to B<HEAD> (current one)
472    
473    =over 5
474    
475    =item *
476    checkout next Subversion revision from B<SVN_URL> over CVS checkout
477    temporary directory
478    
479    =item *
480    make modification (add and/or delete) done in that revision
481    
482    =item *
483    commit modification (added, deleted or modified files/dirs) while
484    preserving original message from CVS
485    
486    =item *
487    update F<.svnrev> to match current revision
488    
489    =back
490    
491    =item *
492    cleanup temporary directory
493    
494    =back
495    
496    If checkout fails for some reason (e.g. flaky ssh connection), you will
497    still have valid CVS repository, so all you have to do is run B<svn2cvs.pl>
498    again.
499    
500    =head1 WARNINGS
501    
502    "Cheap" copy operations in Subversion are not at all cheap in CVS. They will
503    create multiple copies of files in CVS repository!
504    
505    This script assume that you want to sync your C<trunk> (or any other
506    directory for that matter) directory with CVS, not root of your subversion.
507    This might be considered bug, but since common practise is to have
508    directories C<trunk> and C<branches> in svn and source code in them, it's
509    not serious limitation.
510    
511    =head1 RELATED PROJECTS
512    
513    B<Subversion> L<http://subversion.tigris.org/> version control system that is a
514    compelling replacement for CVS in the open source community.
515    
516    B<cvs2svn> L<http://cvs2svn.tigris.org/> converts a CVS repository to a
517    Subversion repository. It is designed for one-time conversions, not for
518    repeated synchronizations between CVS and Subversion.
519    
520    =head1 CHANGES
521    
522    Versions of this utility are actually Subversion repository revisions,
523    so they might not be in sequence.
524    
525    =over 3
526    
527    =item r10
528    
529    First release available to public
530    
531    =item r15
532    
533    Addition of comprehensive documentation, fixes for quoting in commit
534    messages, and support for skipping changes which are not under current
535    Subversion checkout root (e.g. branches).
536    
537    =item r18
538    
539    Support for importing your svn into empty CVS repository (it will first
540    create module and than dump all revisions).
541    Group commit operations to save round-trips to CVS server.
542    Documentation improvements and other small fixes.
543    
544    =item r20
545    
546    Fixed path deduction (overlap between Subversion reporistory and CVS checkout).
547    
548    =item r21
549    
550    Use C<update -d> instead of checkout after import.
551    Added fixes by Paul Egan <paulegan@mail.com> for XMLin and fixing working
552    directory.
553    
554    =item r22
555    
556    Rewritten import from revision 0 to empty repository, better importing
557    of deep directory structures, initial support for recovery from partial
558    commit.
559    
560    =back
561    
562    =head1 AUTHOR
563    
564    Dobrica Pavlinusic <dpavlin@rot13.org>
565    
566    L<https://www.rot13.org/~dpavlin/>
567    
568    =head1 LICENSE
569    
570    This product is licensed under GNU Public License (GPL) v2 or later.
571    
572    =cut
573    
 echo "cvs left in $TMPDIR"  

Legend:
Removed from v.3  
changed lines
  Added in v.47

  ViewVC Help
Powered by ViewVC 1.1.26