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

Legend:
Removed from v.36  
changed lines
  Added in v.42

  ViewVC Help
Powered by ViewVC 1.1.26