/[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 41 by dpavlin, Sat Sep 22 13:52:30 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 {          }
87                  my $msg="subversion revision $rev commited to CVS";          else {
88                    my $msg = "subversion revision $rev commited to CVS";
89                  print "$msg\n";                  print "$msg\n";
90                  system "$cvs commit -m '$msg' '$path'" || die "cvs commit of $path failed: $!";                  system "$cvs commit -m '$msg' '$path'"
91                            || die "cvs commit of $path failed: $!";
92          }          }
93  }  }
94    
95  sub add_dir($$) {  sub add_dir($$) {
96          my ($path,$msg) = @_;          my ( $path, $msg ) = @_;
97          print "# add_dir($path)\n";          print "# add_dir($path)\n";
98          die "add_dir($path) is not directory" unless (-d $path);          die "add_dir($path) is not directory" unless ( -d $path );
99    
100          my $curr_dir;          my $curr_dir;
101    
102          foreach my $d (split(m#/#, $path)) {          foreach my $d ( split( m#/#, $path ) ) {
103                  $curr_dir .= ( $curr_dir ? '/' : '') . $d;                  $curr_dir .= ( $curr_dir ? '/' : '' ) . $d;
104    
105                  next if in_entries($curr_dir);                  next if in_entries($curr_dir);
106                  next if (-e "$curr_dir/CVS");                  next if ( -e "$curr_dir/CVS" );
107    
108                  log_system("$cvs add '$curr_dir'", "cvs add of $curr_dir failed");                  log_system( "$cvs add '$curr_dir'", "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 {  }
150    else {
151    
152          # import into existing module directory in CVS          # import into existing module directory in CVS
153    
154          cd_rep;          cd_rep;
155    
156          # check if svnrev exists          # check if svnrev exists
157          if (! -e ".svnrev") {          if ( !-e ".svnrev" ) {
158                  print <<_USAGE_;                  print <<_USAGE_;
159    
160  Your CVS repository doesn't have .svnrev file!  Your CVS repository doesn't have .svnrev file!
# Line 169  _USAGE_ Line 176  _USAGE_
176                  print "svn revision corresponding to CVS [abort]: ";                  print "svn revision corresponding to CVS [abort]: ";
177                  my $in = <STDIN>;                  my $in = <STDIN>;
178                  chomp($in);                  chomp($in);
179                  if ($in !~ /^\d+$/) {                  if ( $in !~ /^\d+$/ ) {
180                          print "Aborting: revision not a number\n";                          print "Aborting: revision not a number\n";
181                          exit 1;                          exit 1;
182                  } else {                  }
183                    else {
184                          $rev = $in;                          $rev = $in;
185                          commit_svnrev($rev,1);  # create new                          commit_svnrev( $rev, 1 );    # create new
186                  }                  }
187          } else {          }
188                  open(SVNREV,".svnrev") || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";          else {
189                    open( SVNREV, ".svnrev" )
190                            || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";
191                  $rev = <SVNREV>;                  $rev = <SVNREV>;
192                  chomp($rev);                  chomp($rev);
193                  close(SVNREV);                  close(SVNREV);
# Line 187  _USAGE_ Line 197  _USAGE_
197          $rev++;          $rev++;
198  }  }
199    
   
200  #  #
201  # 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
202  # 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 204  _USAGE_
204  # case much about accuracy and completnes of logs there, this might  # case much about accuracy and completnes of logs there, this might
205  # be good. YMMV  # be good. YMMV
206  #  #
207  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 |" )
208            || die "svn log for repository $SVNROOT failed: $!";
209  my $log;  my $log;
210  while(<LOG>) {  while (<LOG>) {
211          $log .= $_;          $log .= $_;
212  }  }
213  close(LOG);  close(LOG);
214    
   
215  my $xml;  my $xml;
216  eval {  eval { $xml = XMLin( $log, ForceArray => [ 'logentry', 'path' ] ); };
         $xml = XMLin($log, ForceArray => [ 'logentry', 'path' ]);  
 };  
   
217    
218  #=begin log_example  #=begin log_example
219  #  #
# Line 220  eval { Line 226  eval {
226    
227  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";
228    
229  if (! $xml->{'logentry'}) {  if ( !$xml->{'logentry'} ) {
230          print "no newer log entries in Subversion repostory. CVS is current\n";          print "no newer log entries in Subversion repostory. CVS is current\n";
231          exit 0;          exit 0;
232  }  }
# Line 230  sub entries($) { Line 236  sub entries($) {
236          my $dir = shift;          my $dir = shift;
237          die "entries expects directory argument!" unless -d $dir;          die "entries expects directory argument!" unless -d $dir;
238          my @entries;          my @entries;
239          open(my $fh, "./$dir/CVS/Entries") || return 0;          open( my $fh, "./$dir/CVS/Entries" ) || return 0;
240          while(<$fh>) {          while (<$fh>) {
241                  if ( m{^D/([^/]+)}, ) {                  if ( m{^D/([^/]+)}, ) {
242                          my $sub_dir = $1;                          my $sub_dir = $1;
243                          warn "#### entries recurse into: $dir/$sub_dir";                          warn "#### entries recurse into: $dir/$sub_dir";
244                          push @entries, map { "$sub_dir/$_" } entries("$dir/$sub_dir");                          push @entries, map {"$sub_dir/$_"} entries("$dir/$sub_dir");
245                          push @entries, $sub_dir;                          push @entries, $sub_dir;
246                  } elsif ( m{^/([^/]+)/} ) {                  }
247                    elsif (m{^/([^/]+)/}) {
248                          push @entries, $1;                          push @entries, $1;
249                  } elsif ( ! m{^D$} ) {                  }
250                    elsif ( !m{^D$} ) {
251                          die "can't decode entries line: $_";                          die "can't decode entries line: $_";
252                  }                  }
253          }          }
254          close($fh);          close($fh);
255          warn "#### entries($dir) => ",join("|",@entries);          warn "#### entries($dir) => ", join( "|", @entries );
256          return @entries;          return @entries;
257  }  }
258    
259  # check if file exists in CVS/Entries  # check if file exists in CVS/Entries
260  sub in_entries($) {  sub in_entries($) {
261          my $path = shift;          my $path = shift;
262          if ($path =~ m,^(.*?/*)([^/]+)$,) {          if ( $path =~ m,^(.*?/*)([^/]+)$, ) {
263                  my ($dir,$file) = ($1,$2);                  my ( $dir, $file ) = ( $1, $2 );
264                  if ($dir !~ m,/$, && $dir ne "") {                  if ( $dir !~ m,/$, && $dir ne "" ) {
265                          $dir .= "/";                          $dir .= "/";
266                  }                  }
267    
268                  open(my $fh, "./$dir/CVS/Entries") || return 0; #die "no entries file: $dir/CVS/Entries";                  open( my $fh, "./$dir/CVS/Entries" )
269                  while(<$fh>) {                          || return 0;    #die "no entries file: $dir/CVS/Entries";
270                    while (<$fh>) {
271                          return 1 if (m{^/$file/});                          return 1 if (m{^/$file/});
272                  }                  }
273                  close($fh);                  close($fh);
274                  return 0;                  return 0;
275          } else {          }
276            else {
277                  die "can't split '$path' to dir and file!";                  die "can't split '$path' to dir and file!";
278          }          }
279  }  }
# Line 271  sub in_entries($) { Line 281  sub in_entries($) {
281  cd_tmp;  cd_tmp;
282  cd_rep;  cd_rep;
283    
284  foreach my $e (@{$xml->{'logentry'}}) {  foreach my $e ( @{ $xml->{'logentry'} } ) {
285          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 ("
286                    . $e->{'revision'} . ")"
287                    if ( $rev > $e->{'revision'} );
288          $rev = $e->{'revision'};          $rev = $e->{'revision'};
289          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",
290                    "svn export of revision $rev failed" );
291    
292          # deduce name of svn directory          # deduce name of svn directory
293          my $SVNREP = "";          my $SVNREP  = "";
294          my $tmpsvn = $SVNROOT || die "BUG: SVNROOT empty!";          my $tmpsvn  = $SVNROOT || die "BUG: SVNROOT empty!";
295          my $tmppath = $e->{'paths'}->{'path'}->[0]->{'content'} || die "BUG: tmppath empty!";          my $tmppath = $e->{'paths'}->{'path'}->[0]->{'content'}
296                    || die "BUG: tmppath empty!";
297          do {          do {
298                  if ($tmpsvn =~ s#(/[^/]+)/*$##) {       # vim fix                  if ( $tmpsvn =~ s#(/[^/]+)/*$## ) {    # vim fix
299                          $SVNREP = $1 . $SVNREP;                          $SVNREP = $1 . $SVNREP;
300                  } elsif ($e->{'paths'}->{'path'}->[0]->{'copyfrom-path'}) {                  }
301                          print "NOTICE: copyfrom outside synced repository ignored - skipping\n";                  elsif ( $e->{'paths'}->{'path'}->[0]->{'copyfrom-path'} ) {
302                            print
303                                    "NOTICE: copyfrom outside synced repository ignored - skipping\n";
304                          next;                          next;
305                  } else {                  }
306                    else {
307                          print "NOTICE: can't deduce svn dir from $SVNROOT - skipping\n";                          print "NOTICE: can't deduce svn dir from $SVNROOT - skipping\n";
308                          next;                          next;
309                  }                  }
310          } until ($tmppath =~ m/^$SVNREP/);          } until ( $tmppath =~ m/^$SVNREP/ );
311    
312          print "NOTICE: using $SVNREP as directory for svn\n";          print "NOTICE: using $SVNREP as directory for svn\n";
313    
314          printf($fmt, $e->{'revision'}, $e->{'author'}, $e->{'date'}, $e->{'msg'});          printf( $fmt,
315                    $e->{'revision'}, $e->{'author'}, $e->{'date'}, $e->{'msg'} );
316          my @commit;          my @commit;
317    
318          my $msg = $e->{'msg'};          my $msg = $e->{'msg'};
319          $msg =~ s/'/'\\''/g;    # quote "          $msg =~ s/'/'\\''/g;    # quote "
320    
321          $msg = 'r' . $rev . ' ' . $e->{author} . ' | ' . $e->{date} . "\n" . $msg if $decorate_commit_message;          $msg = 'r' . $rev . ' ' . $e->{author} . ' | ' . $e->{date} . "\n" . $msg
322                    if $decorate_commit_message;
323    
324          sub cvs_commit {          sub cvs_commit {
325                  my $msg = shift || die "no msg?";                  my $msg = shift || die "no msg?";
326                  if ( ! @_ ) {                  if ( !@_ ) {
327                          warn "commit ignored, no files\n";                          warn "commit ignored, no files\n";
328                          return;                          return;
329                  }                  }
330                  log_system("$cvs commit -m '$msg' '".join("' '",@_)."'", "cvs commit of ".join(",",@_)." failed");                  log_system(
331                            "$cvs commit -m '$msg' '" . join( "' '", @_ ) . "'",
332                            "cvs commit of " . join( ",",            @_ ) . " failed"
333                    );
334          }          }
335    
336          foreach my $p (@{$e->{'paths'}->{'path'}}) {          foreach my $p ( @{ $e->{'paths'}->{'path'} } ) {
337                  my ($action,$path) = ($p->{'action'},$p->{'content'});                  my ( $action, $path ) = ( $p->{'action'}, $p->{'content'} );
338    
339                  next if ($path =~ m#/\.svnrev$#);                  next if ( $path =~ m#/\.svnrev$# );
340    
341                  print "svn2cvs: $action $path\n";                  print "svn2cvs: $action $path\n";
342    
343                  # prepare path and message                  # prepare path and message
344                  my $file = $path;                  my $file = $path;
345                  if ( $path !~ s#^\Q$SVNREP\E/*## ) {                  if ( $path !~ s#^\Q$SVNREP\E/*## ) {
346                          print "NOTICE: skipping '$path' which isn't under repository root '$SVNREP'\n";                          print
347                                    "NOTICE: skipping '$path' which isn't under repository root '$SVNREP'\n";
348                          die unless $partial_import;                          die unless $partial_import;
349                          next;                          next;
350                  }                  }
351    
352                  if (! $path) {                  if ( !$path ) {
353                          print "NOTICE: skipped this operation. Probably trunk creation\n";                          print "NOTICE: skipped this operation. Probably trunk creation\n";
354                          next;                          next;
355                  }                  }
356    
357                  my $msg = $e->{'msg'};                  my $msg = $e->{'msg'};
358                  $msg =~ s/'/'\\''/g;    # quote "                  $msg =~ s/'/'\\''/g;    # quote "
359    
360                  sub add_path {                  sub add_path {
361                          my $path = shift || die "no path?";                          my $path = shift || die "no path?";
362            
363                          if (-d $path) {                          if ( -d $path ) {
364                                  add_dir($path, $msg);                                  add_dir( $path, $msg );
365                          } elsif ($path =~ m,^(.+)/[^/]+$, && ! -e "$1/CVS/Root") {                          }
366                            elsif ( $path =~ m,^(.+)/[^/]+$, && !-e "$1/CVS/Root" ) {
367                                  my $dir = $1;                                  my $dir = $1;
368                                  in_entries($dir) || add_dir($dir, $msg);                                  in_entries($dir) || add_dir( $dir, $msg );
369                                  in_entries($path) || log_system("$cvs add '$path'", "cvs add of $path failed");                                  in_entries($path) || log_system( "$cvs add '$path'",
370                          } else {                                          "cvs add of $path failed" );
371                                  in_entries($path) || log_system("$cvs add '$path'", "cvs add of $path failed");                          }
372                            else {
373                                    in_entries($path) || log_system( "$cvs add '$path'",
374                                            "cvs add of $path failed" );
375                          }                          }
376                  }                  }
377    
378                  if ($action =~ /M/) {                  if ( $action =~ /M/ ) {
379                          if ( in_entries( $path ) ) {                          if ( in_entries($path) ) {
380                                  print "svn2cvs: modify $path -- nop\n";                                  print "svn2cvs: modify $path -- nop\n";
381                          } else {                          }
382                            else {
383                                  print "WARNING: modify $path which isn't in CVS, adding...\n";                                  print "WARNING: modify $path which isn't in CVS, adding...\n";
384                                  add_path($path);                                  add_path($path);
385                          }                          }
386                  } elsif ($action =~ /A/) {                  }
387                    elsif ( $action =~ /A/ ) {
388                          add_path($path);                          add_path($path);
389                  } elsif ($action =~ /D/) {                  }
390                          if (-e $path) {                  elsif ( $action =~ /D/ ) {
391                            if ( -e $path ) {
392                                  if ( -d $path ) {                                  if ( -d $path ) {
393                                          warn "#### remove directory: $path";                                          warn "#### remove directory: $path";
394                                          my @sub_commit;                                          my @sub_commit;
395                                          foreach my $f ( entries($path) ) {                                          foreach my $f ( entries($path) ) {
396                                                  $f = "$path/$f";                                                  $f = "$path/$f";
397                                          if ( -f $f ) {                                                  if ( -f $f ) {
398                                                          unlink($f) || die "can't delete file $f: $!";                                                          unlink($f) || die "can't delete file $f: $!";
399  #                                               } else {  
400  #                                                       rmtree($f) || die "can't delete dir $f: $!";                                                    #                                             } else {
401                                                      #                                                     rmtree($f) || die "can't delete dir $f: $!";
402                                                  }                                                  }
403                                                  log_system("$cvs delete '$f'", "cvs delete of file $f failed");                                                  log_system( "$cvs delete '$f'",
404                                                            "cvs delete of file $f failed" );
405                                                  push @sub_commit, $f;                                                  push @sub_commit, $f;
406                                          }                                          }
407                                          log_system("$cvs delete '$path'", "cvs delete of file $path failed");                                          log_system( "$cvs delete '$path'",
408                                          cvs_commit($msg, @sub_commit, $path);                                                  "cvs delete of file $path failed" );
409                                          log_system("$cvs update -dP '$path'", "cvs update -dP $path failed");                                          cvs_commit( $msg, @sub_commit, $path );
410                                            log_system(
411                                                    "$cvs update -dP '$path'",
412                                                    "cvs update -dP $path failed"
413                                            );
414                                          undef $path;                                          undef $path;
415                                  } else {                                  }
416                                    else {
417                                          warn "#### remove file: $path";                                          warn "#### remove file: $path";
418                                          unlink($path) || die "can't delete $path: $!";                                          unlink($path) || die "can't delete $path: $!";
419                                          log_system("$cvs delete '$path'", "cvs delete of dir $path failed");                                          log_system( "$cvs delete '$path'",
420                                                    "cvs delete of dir $path failed" );
421                                  }                                  }
422                          } else {                          }
423                            else {
424                                  print "WARNING: $path is not present, skipping...\n";                                  print "WARNING: $path is not present, skipping...\n";
425                                  undef $path;                                  undef $path;
426                          }                          }
427                  } else {                  }
428                          print "WARNING: action $action not implemented on $path. Bug or missing feature of $0\n";                  else {
429                            print
430                                    "WARNING: action $action not implemented on $path. Bug or missing feature of $0\n";
431                  }                  }
432    
433                  # save commits for later                  # save commits for later
# Line 395  foreach my $e (@{$xml->{'logentry'}}) { Line 436  foreach my $e (@{$xml->{'logentry'}}) {
436          }          }
437    
438          # now commit changes          # now commit changes
439          cvs_commit($msg, @commit);          cvs_commit( $msg, @commit );
440    
441          commit_svnrev($rev);          commit_svnrev($rev);
442  }  }

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

  ViewVC Help
Powered by ViewVC 1.1.26