/[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 40 by dpavlin, Fri Sep 7 18:43:51 2007 UTC revision 47 by dpavlin, Fri Nov 2 12:11:31 2007 UTC
# Line 23  my $partial_import = 1; Line 23  my $partial_import = 1;
23  # do we want to add svk-like prefix with original revision, author and date?  # do we want to add svk-like prefix with original revision, author and date?
24  my $decorate_commit_message = 1;  my $decorate_commit_message = 1;
25    
26  if (@ARGV < 2) {  if ( @ARGV < 2 ) {
27          print "usage: $0 SVN_URL CVSROOT CVSREPOSITORY\n";          print "usage: $0 SVN_URL CVSROOT CVSREPOSITORY\n";
28          exit 1;          exit 1;
29  }  }
30    
31  my ($SVNROOT,$CVSROOT, $CVSREP) = @ARGV;  my ( $SVNROOT, $CVSROOT, $CVSREP ) = @ARGV;
32    
33  if ($SVNROOT !~ m,^[\w+]+:///*\w+,) {  if ( $SVNROOT !~ m,^[\w+]+:///*\w+, ) {
34          print "ERROR: invalid svn root $SVNROOT\n";          print "ERROR: invalid svn root $SVNROOT\n";
35          exit 1;          exit 1;
36  }  }
# Line 38  if ($SVNROOT !~ m,^[\w+]+:///*\w+,) { Line 38  if ($SVNROOT !~ m,^[\w+]+:///*\w+,) {
38  # Ensure File::Temp::END can clean up:  # Ensure File::Temp::END can clean up:
39  $SIG{__DIE__} = sub { chdir("/tmp"); die @_ };  $SIG{__DIE__} = sub { chdir("/tmp"); die @_ };
40    
41  my $TMPDIR=tempdir( "/tmp/checkoutXXXXX", CLEANUP => 1 );  my $TMPDIR = tempdir( "/tmp/checkoutXXXXX", CLEANUP => 1 );
42    
43  sub cd_tmp {  sub cd_tmp {
44          chdir($TMPDIR) || die "can't cd to $TMPDIR: $!";          chdir($TMPDIR) || die "can't cd to $TMPDIR: $!";
# Line 51  sub cd_rep { Line 51  sub cd_rep {
51  print "## using TMPDIR $TMPDIR\n";  print "## using TMPDIR $TMPDIR\n";
52    
53  # cvs command with root  # cvs command with root
54  my $cvs="cvs -f -d $CVSROOT";  my $cvs = "cvs -f -d $CVSROOT";
55    
56  # current revision in CVS  # current revision in CVS
57  my $rev;  my $rev;
# Line 60  my $rev; Line 60  my $rev;
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) == 0 || die "$errmsg: $!";          system($cmd) == 0 || die "$errmsg: $!";
66  }  }
# Line 69  sub log_system($$) { Line 69  sub log_system($$) {
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,"> .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 '$path'" || die "cvs add of $path failed: $!";                  system "$cvs add '$path'" || die "cvs add of $path failed: $!";
86          } else {          } else {
87                  my $msg="subversion revision $rev commited to CVS";                  my $msg = "subversion revision $rev commited to CVS";
88                  print "$msg\n";                  print "$msg\n";
89                  system "$cvs commit -m '$msg' '$path'" || die "cvs commit of $path failed: $!";                  system "$cvs commit -m '$msg' '$path'"
90                            || die "cvs commit of $path failed: $!";
91          }          }
92  }  }
93    
94  sub add_dir($$) {  sub add_dir($$) {
95          my ($path,$msg) = @_;          my ( $path, $msg ) = @_;
96          print "# add_dir($path)\n";          print "# add_dir($path)\n";
97          die "add_dir($path) is not directory" unless (-d $path);          die "add_dir($path) is not directory" unless ( -d $path );
98    
99          my $curr_dir;          my $curr_dir;
100    
101          foreach my $d (split(m#/#, $path)) {          foreach my $d ( split( m#/#, $path ) ) {
102                  $curr_dir .= ( $curr_dir ? '/' : '') . $d;                  $curr_dir .= ( $curr_dir ? '/' : '' ) . $d;
103    
104                  next if in_entries($curr_dir);                  next if in_entries($curr_dir);
105                  next if (-e "$curr_dir/CVS");                  next if ( -e "$curr_dir/CVS" );
106    
107                  log_system("$cvs add '$curr_dir'", "cvs add of $curr_dir failed");                  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 {  eval {
114          cd_tmp;          cd_tmp;
115          log_system("$cvs -q checkout $CVSREP", "cvs checkout failed");          log_system( "$cvs -q checkout $CVSREP", "cvs checkout failed" );
116  };  };
117    
118  if ($@) {  if ($@) {
# Line 128  _NEW_REP_ Line 131  _NEW_REP_
131          mkdir($CVSREP) || die "can't create $CVSREP: $!";          mkdir($CVSREP) || die "can't create $CVSREP: $!";
132          cd_rep;          cd_rep;
133    
134          open(SVNREV,"> .svnrev") || die "can't open $CVSREP/.svnrev: $!";          open( SVNREV, "> .svnrev" ) || die "can't open $CVSREP/.svnrev: $!";
135          print SVNREV "0";          print SVNREV "0";
136          close(SVNREV);          close(SVNREV);
137    
# Line 136  _NEW_REP_ Line 139  _NEW_REP_
139    
140          # create new module          # create new module
141          cd_rep;          cd_rep;
142          log_system("$cvs import -d -m 'new CVS module' $CVSREP svn r$rev", "import of new repository");          log_system( "$cvs import -d -m 'new CVS module' $CVSREP svn r$rev",
143                    "import of new repository" );
144          cd_tmp;          cd_tmp;
145          rmtree($CVSREP) || die "can't remove $CVSREP";          rmtree($CVSREP) || die "can't remove $CVSREP";
146          log_system("$cvs -q checkout $CVSREP", "cvs checkout failed");          log_system( "$cvs -q checkout $CVSREP", "cvs checkout failed" );
147          cd_rep;          cd_rep;
148    
149  } else {  } else {
150    
151          # import into existing module directory in CVS          # import into existing module directory in CVS
152    
153          cd_rep;          cd_rep;
154    
155          # check if svnrev exists          # check if svnrev exists
156          if (! -e ".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!
# Line 169  _USAGE_ Line 175  _USAGE_
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 {                  } else {
182                          $rev = $in;                          $rev = $in;
183                          commit_svnrev($rev,1);  # create new                          commit_svnrev( $rev, 1 );    # create new
184                  }                  }
185          } else {          } else {
186                  open(SVNREV,".svnrev") || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";                  open( SVNREV, ".svnrev" )
187                            || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";
188                  $rev = <SVNREV>;                  $rev = <SVNREV>;
189                  chomp($rev);                  chomp($rev);
190                  close(SVNREV);                  close(SVNREV);
# Line 187  _USAGE_ Line 194  _USAGE_
194          $rev++;          $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
199  # loose multiple edits of same file and corresponding messages. On the  # loose multiple edits of same file and corresponding messages. On the
# Line 195  _USAGE_ Line 201  _USAGE_
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 |") || die "svn log for repository $SVNROOT 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;  my $xml;
213  eval {  eval { $xml = XMLin( $log, ForceArray => [ 'logentry', 'path' ] ); };
         $xml = XMLin($log, ForceArray => [ 'logentry', 'path' ]);  
 };  
   
214    
215  #=begin log_example  #=begin log_example
216  #  #
# Line 220  eval { Line 223  eval {
223    
224  my $fmt = "\n" . "-" x 79 . "\nr%5s| %8s | %s\n\n%s\n";  my $fmt = "\n" . "-" x 79 . "\nr%5s| %8s | %s\n\n%s\n";
225    
226  if (! $xml->{'logentry'}) {  if ( !$xml->{'logentry'} ) {
227          print "no newer log entries in Subversion repostory. CVS is current\n";          print "no newer log entries in Subversion repostory. CVS is current\n";
228          exit 0;          exit 0;
229  }  }
# Line 230  sub entries($) { Line 233  sub entries($) {
233          my $dir = shift;          my $dir = shift;
234          die "entries expects directory argument!" unless -d $dir;          die "entries expects directory argument!" unless -d $dir;
235          my @entries;          my @entries;
236          open(my $fh, "./$dir/CVS/Entries") || return 0;          open( my $fh, "./$dir/CVS/Entries" ) || return 0;
237          while(<$fh>) {          while (<$fh>) {
238                  if ( m{^D/([^/]+)}, ) {                  if ( m{^D/([^/]+)}, ) {
239                          my $sub_dir = $1;                          my $sub_dir = $1;
240                          warn "#### entries recurse into: $dir/$sub_dir";                          warn "#### entries recurse into: $dir/$sub_dir";
241                          push @entries, map { "$sub_dir/$_" } entries("$dir/$sub_dir");                          push @entries, map {"$sub_dir/$_"} entries("$dir/$sub_dir");
242                          push @entries, $sub_dir;                          push @entries, $sub_dir;
243                  } elsif ( m{^/([^/]+)/} ) {                  } elsif (m{^/([^/]+)/}) {
244                          push @entries, $1;                          push @entries, $1;
245                  } elsif ( ! m{^D$} ) {                  } elsif ( !m{^D$} ) {
246                          die "can't decode entries line: $_";                          die "can't decode entries line: $_";
247                  }                  }
248          }          }
249          close($fh);          close($fh);
250          warn "#### entries($dir) => ",join("|",@entries);          warn "#### entries($dir) => ", join( "|", @entries );
251          return @entries;          return @entries;
252  }  }
253    
254  # check if file exists in CVS/Entries  # check if file exists in CVS/Entries
255  sub in_entries($) {  sub in_entries($) {
256          my $path = shift;          my $path = shift;
257          if ($path =~ m,^(.*?/*)([^/]+)$,) {          if ( $path =~ m,^(.*?/*)([^/]+)$, ) {
258                  my ($dir,$file) = ($1,$2);                  my ( $dir, $file ) = ( $1, $2 );
259                  if ($dir !~ m,/$, && $dir ne "") {                  if ( $dir !~ m,/$, && $dir ne "" ) {
260                          $dir .= "/";                          $dir .= "/";
261                  }                  }
262    
263                  open(my $fh, "./$dir/CVS/Entries") || return 0; #die "no entries file: $dir/CVS/Entries";                  open( my $fh, "./$dir/CVS/Entries" )
264                  while(<$fh>) {                          || return 0;    #die "no entries file: $dir/CVS/Entries";
265                          return 1 if (m{^/$file/});                  while (<$fh>) {
266                            return 1 if (m{^D?/$file/});
267                  }                  }
268                  close($fh);                  close($fh);
269                  return 0;                  return 0;
# Line 271  sub in_entries($) { Line 275  sub in_entries($) {
275  cd_tmp;  cd_tmp;
276  cd_rep;  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 $TMPDIR/$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          # deduce name of svn directory
287          my $SVNREP = "";          my $SVNREP  = "";
288          my $tmpsvn = $SVNROOT || die "BUG: SVNROOT empty!";          my $tmpsvn  = $SVNROOT || die "BUG: SVNROOT empty!";
289          my $tmppath = $e->{'paths'}->{'path'}->[0]->{'content'} || die "BUG: tmppath empty!";          my $tmppath = $e->{'paths'}->{'path'}->[0]->{'content'}
290                    || die "BUG: tmppath empty!";
291          do {          do {
292                  if ($tmpsvn =~ s#(/[^/]+)/*$##) {       # vim fix                  if ( $tmpsvn =~ s#(/[^/]+)/*$## ) {    # vim fix
293                          $SVNREP = $1 . $SVNREP;                          $SVNREP = $1 . $SVNREP;
294                  } elsif ($e->{'paths'}->{'path'}->[0]->{'copyfrom-path'}) {                  } elsif ( $e->{'paths'}->{'path'}->[0]->{'copyfrom-path'} ) {
295                          print "NOTICE: copyfrom outside synced repository ignored - skipping\n";                          print
296                                    "NOTICE: copyfrom outside synced repository ignored - skipping\n";
297                          next;                          next;
298                  } else {                  } else {
299                          print "NOTICE: can't deduce svn dir from $SVNROOT - skipping\n";                          print "NOTICE: can't deduce svn dir from $SVNROOT - skipping\n";
300                          next;                          next;
301                  }                  }
302          } until ($tmppath =~ m/^$SVNREP/);          } until ( $tmppath =~ m/^$SVNREP/ );
303    
304          print "NOTICE: using $SVNREP as directory for svn\n";          print "NOTICE: using $SVNREP as directory for svn\n";
305    
306          printf($fmt, $e->{'revision'}, $e->{'author'}, $e->{'date'}, $e->{'msg'});          printf( $fmt,
307                    $e->{'revision'}, $e->{'author'}, $e->{'date'}, $e->{'msg'} );
308          my @commit;          my @commit;
309    
310          my $msg = $e->{'msg'};          my $msg = $e->{'msg'};
311          $msg =~ s/'/'\\''/g;    # quote "          $msg =~ s/'/'\\''/g;    # quote "
312    
313          $msg = 'r' . $rev . ' ' . $e->{author} . ' | ' . $e->{date} . "\n" . $msg if $decorate_commit_message;          $msg = 'r' . $rev . ' ' . $e->{author} . ' | ' . $e->{date} . "\n" . $msg
314                    if $decorate_commit_message;
315    
316          sub cvs_commit {          sub cvs_commit {
317                  my $msg = shift || die "no msg?";                  my $msg = shift || die "no msg?";
318                  if ( ! @_ ) {                  if ( !@_ ) {
319                          warn "commit ignored, no files\n";                          warn "commit ignored, no files\n";
320                          return;                          return;
321                  }                  }
322                  log_system("$cvs commit -m '$msg' '".join("' '",@_)."'", "cvs commit of ".join(",",@_)." failed");                  log_system(
323                            "$cvs commit -m '$msg' '" . join( "' '", @_ ) . "'",
324                            "cvs commit of " . join( ",",            @_ ) . " failed"
325                    );
326          }          }
327    
328          foreach my $p (@{$e->{'paths'}->{'path'}}) {          foreach my $p ( @{ $e->{'paths'}->{'path'} } ) {
329                  my ($action,$path) = ($p->{'action'},$p->{'content'});                  my ( $action, $path ) = ( $p->{'action'}, $p->{'content'} );
330    
331                  next if ($path =~ m#/\.svnrev$#);                  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                  if ( $path !~ s#^\Q$SVNREP\E/*## ) {                  if ( $path !~ s#^\Q$SVNREP\E/*## ) {
338                          print "NOTICE: skipping '$path' which isn't under repository root '$SVNREP'\n";                          print
339                                    "NOTICE: skipping '$path' which isn't under repository root '$SVNREP'\n";
340                          die unless $partial_import;                          die unless $partial_import;
341                          next;                          next;
342                  }                  }
343    
344                  if (! $path) {                  if ( !$path ) {
345                          print "NOTICE: skipped this operation. Probably trunk creation\n";                          print "NOTICE: skipped this operation. Probably trunk creation\n";
346                          next;                          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 {                  sub add_path {
353                          my $path = shift || die "no path?";                          my $path = shift || die "no path?";
354            
355                          if (-d $path) {                          if ( -d $path ) {
356                                  add_dir($path, $msg);                                  add_dir( $path, $msg );
357                          } elsif ($path =~ m,^(.+)/[^/]+$, && ! -e "$1/CVS/Root") {                          } elsif ( $path =~ m,^(.+)/[^/]+$, && !-e "$1/CVS/Root" ) {
358                                  my $dir = $1;                                  my $dir = $1;
359                                  in_entries($dir) || add_dir($dir, $msg);                                  in_entries($dir) || add_dir( $dir, $msg );
360                                  in_entries($path) || log_system("$cvs add '$path'", "cvs add of $path failed");                                  in_entries($path) || log_system( "$cvs add '$path'",
361                                            "cvs add of $path failed" );
362                          } else {                          } else {
363                                  in_entries($path) || log_system("$cvs add '$path'", "cvs add of $path failed");                                  in_entries($path) || log_system( "$cvs add '$path'",
364                                            "cvs add of $path failed" );
365                          }                          }
366                  }                  }
367    
368                  if ($action =~ /M/) {                  if ( $action =~ /M/ ) {
369                          if ( in_entries( $path ) ) {                          if ( in_entries($path) ) {
370                                  print "svn2cvs: modify $path -- nop\n";                                  print "svn2cvs: modify $path -- nop\n";
371                          } else {                          } else {
372                                  print "WARNING: modify $path which isn't in CVS, adding...\n";                                  print "WARNING: modify $path which isn't in CVS, adding...\n";
373                                  add_path($path);                                  add_path($path);
374                          }                          }
375                  } elsif ($action =~ /A/) {                  } elsif ( $action =~ /A/ ) {
376                          add_path($path);                          add_path($path);
377                  } elsif ($action =~ /D/) {                  } elsif ( $action =~ /D/ ) {
378                          if (-e $path) {                          if ( -e $path ) {
379                                  if ( -d $path ) {                                  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";                                          warn "#### remove directory: $path";
                                         my @sub_commit;  
384                                          foreach my $f ( entries($path) ) {                                          foreach my $f ( entries($path) ) {
385                                                  $f = "$path/$f";                                                  $f = "$path/$f";
386                                          if ( -f $f ) {                                                  if ( -f $f ) {
387                                                          unlink($f) || die "can't delete file $f: $!";                                                          unlink($f) || die "can't delete file $f: $!";
388  #                                               } else {  
389  #                                                       rmtree($f) || die "can't delete dir $f: $!";                                                    #                                             } else {
390                                                      #                                                     rmtree($f) || die "can't delete dir $f: $!";
391                                                  }                                                  }
392                                                  log_system("$cvs delete '$f'", "cvs delete of file $f failed");                                                  log_system( "$cvs delete '$f'",
393                                                  push @sub_commit, $f;                                                          "cvs delete of file $f failed" );
394                                                    cvs_commit( $msg, $f );
395                                          }                                          }
396                                          log_system("$cvs delete '$path'", "cvs delete of file $path failed");                                          log_system( "$cvs delete '$path'",
397                                          cvs_commit($msg, @sub_commit, $path);                                                  "cvs delete of file $path failed" );
398                                          log_system("$cvs update -dP '$path'", "cvs update -dP $path failed");                                          cvs_commit( $msg, $path );
399                                            log_system( "$cvs update -dP .",
400                                                    "cvs update -dP . failed" );
401                                          undef $path;                                          undef $path;
402                                  } else {                                  } else {
403                                          warn "#### remove file: $path";                                          warn "#### remove file: $path";
404                                          unlink($path) || die "can't delete $path: $!";                                          unlink($path) || die "can't delete $path: $!";
405                                          log_system("$cvs delete '$path'", "cvs delete of dir $path failed");                                          log_system( "$cvs delete '$path'",
406                                                    "cvs delete of dir $path failed" );
407                                            cvs_commit( $msg, $path );
408                                            undef $path;
409                                  }                                  }
410                          } else {                          } else {
411                                  print "WARNING: $path is not present, skipping...\n";                                  print "WARNING: $path is not present, skipping...\n";
412                                  undef $path;                                  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                  # save commits for later                  # save commits for later
# Line 395  foreach my $e (@{$xml->{'logentry'}}) { Line 422  foreach my $e (@{$xml->{'logentry'}}) {
422          }          }
423    
424          # now commit changes          # now commit changes
425          cvs_commit($msg, @commit);          cvs_commit( $msg, @commit );
426    
427          commit_svnrev($rev);          commit_svnrev($rev);
428  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.26