/[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 12 by dpavlin, Sat Mar 13 17:22:53 2004 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 Data::Dumper;  use Data::Dumper;
17  use XML::Simple;  use XML::Simple;
18    
19  # get current user home directory  if (@ARGV < 2) {
20  my $HOME = $ENV{'HOME'} || die "can't get home directory!";          print "usage: $0 SVN_URL CVSROOT CVSREPOSITORY\n";
21            exit 1;
22    }
23    
24    my ($SVNROOT,$CVSROOT, $CVSREP) = @ARGV;
25    
26  # cvsroot directory  if ($SVNROOT !~ m,^[\w+]+:///*\w+,) {
27  my $CVSROOT="$HOME/x/cvsroot";          print "ERROR: invalid svn root $SVNROOT\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 $TMPDIR=tempdir( "/tmp/checkoutXXXXX", CLEANUP => 1 );
32    
# Line 40  chdir($TMPDIR) || die "can't cd to $TMPD Line 35  chdir($TMPDIR) || die "can't cd to $TMPD
35  # cvs command with root  # cvs command with root
36  my $cvs="cvs -d $CVSROOT";  my $cvs="cvs -d $CVSROOT";
37    
   
38  #  #
39  # sub to do logging and system calls  # sub to do logging and system calls
40  #  #
41  sub log_system($$) {  sub log_system($$) {
42          my ($cmd,$errmsg) = @_;          my ($cmd,$errmsg) = @_;
43          print STDERR "## $cmd\n";          print STDERR "## $cmd\n";
44          system $cmd || die "$errmsg: $!";          system($cmd) == 0 || die "$errmsg: $!";
45  }  }
46    
47  #  #
# Line 59  sub commit_svnrev { Line 53  sub commit_svnrev {
53    
54          die "commit_svnrev needs revision" if (! defined($rev));          die "commit_svnrev needs revision" if (! defined($rev));
55    
56          open(SVNREV,"> $CVSREP/.svnrev") || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";          open(SVNREV,"> .svnrev") || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";
57          print SVNREV $rev;          print SVNREV $rev;
58          close(SVNREV);          close(SVNREV);
59    
60          my $path=".svnrev";          my $path=".svnrev";
61    
62          if ($add_new) {          if ($add_new) {
63                  system "$cvs add $CVSREP/$path" || die "cvs add of $path failed: $!";                  system "$cvs add $path" || die "cvs add of $path failed: $!";
64            } else {
65                    my $msg="subversion revision $rev commited to CVS";
66                    print "$msg\n";
67                    system "$cvs commit -m '$msg' $path" || die "cvs commit of $path failed: $!";
68          }          }
   
         my $msg="subversion revision $rev commited to CVS";  
         print "$msg\n";  
         system "$cvs commit -m \"$msg\" $CVSREP/$path" || die "cvs commit of $path failed: $!";  
69  }  }
70    
   
71  # ok, now do the checkout  # ok, now do the checkout
72    
73  log_system("$cvs -q checkout $CVSREP","cvs checkout failed");  log_system("$cvs -q checkout $CVSREP", "cvs checkout failed");
74    
75    chdir($CVSREP) || die "can't cd to $TMPDIR/$CVSREP: $!";
76    
77    
78  my $rev;  my $rev;
79    
80  # check if svnrev exists  # check if svnrev exists
81  if (! -e "$CVSREP/.svnrev") {  if (! -e ".svnrev") {
82          print <<_USAGE_;          print <<_USAGE_;
83    
84  Your CVS repository doesn't have .svnrev file!  Your CVS repository doesn't have .svnrev file!
85    
86  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
87  that only newer changes will be commited.  that only newer changes will be commited.
88    
89  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 92  corresponds to current version of CVS re
92  been checkouted.  been checkouted.
93    
94  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
95  last SubVersion revision. If this is initial run of conversion of  last Subversion revision. If this is initial run of conversion of
96  SubVersion repository to CVS, correct revision is 0.  Subversion repository to CVS, correct revision is 0.
97    
98  _USAGE_  _USAGE_
99    
# Line 112  _USAGE_ Line 108  _USAGE_
108                  commit_svnrev($rev,1);  # create new                  commit_svnrev($rev,1);  # create new
109          }          }
110  } else {  } else {
111          open(SVNREV,"$CVSREP/.svnrev") || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";          open(SVNREV,".svnrev") || die "can't open $TMPDIR/$CVSREP/.svnrev: $!";
112          my $rev = <SVNREV>;          $rev = <SVNREV>;
113          chomp($rev);          chomp($rev);
114          close(SVNREV);          close(SVNREV);
115  }  }
# Line 129  $rev++; Line 125  $rev++;
125  # case much about accuracy and completnes of logs there, this might  # case much about accuracy and completnes of logs there, this might
126  # be good. YMMV  # be good. YMMV
127  #  #
128  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 |") || die "svn log for repository $SVNROOT failed: $!";
129  my $log;  my $log;
130  while(<LOG>) {  while(<LOG>) {
131          $log .= $_;          $log .= $_;
# Line 139  close(LOG); Line 135  close(LOG);
135  my $xml = XMLin($log, ForceArray => [ 'logentry', 'path' ]);  my $xml = XMLin($log, ForceArray => [ 'logentry', 'path' ]);
136    
137    
138  =begin log_example  #=begin log_example
139    #
140  ------------------------------------------------------------------------  #------------------------------------------------------------------------
141  r256 | dpavlin | 2004-03-09 13:18:17 +0100 (Tue, 09 Mar 2004) | 2 lines  #r256 | dpavlin | 2004-03-09 13:18:17 +0100 (Tue, 09 Mar 2004) | 2 lines
142    #
143  ported r254 from hidra branch  #ported r254 from hidra branch
144    #
145  =cut  #=cut
146    
147  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";
148    
149  if (! $xml->{'logentry'}) {  if (! $xml->{'logentry'}) {
150          print "no newer log entries in SubVersion repostory. CVS is current\n";          print "no newer log entries in Subversion repostory. CVS is current\n";
151          exit 0;          exit 0;
152  }  }
153    
 print Dumper($xml);  
   
154  foreach my $e (@{$xml->{'logentry'}}) {  foreach my $e (@{$xml->{'logentry'}}) {
155          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 (".$e->{'revision'}.")" if ($rev > $e->{'revision'});
156          $rev = $e->{'revision'};          $rev = $e->{'revision'};
157          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", "svn export of revision $rev failed");
158    
159            # deduce name of svn directory
160            my $SVNREP = "";
161            my $tmpsvn = $SVNROOT || die "BUG: SVNROOT empty!";
162            my $tmppath = $e->{'paths'}->{'path'}->[0]->{'content'} || die "BUG: tmppath empty!";
163            do {
164    print "## tmppath: $tmppath tmpsvn: $tmpsvn SVNREP: $SVNREP\n";
165                    if ($tmpsvn =~ s,(/\w+/*)$,,) {
166                            $SVNREP .= $1;
167                    } else {
168                            die "ERROR: can't deduce svn dir from $SVNROOT.\nUsing root of snv repository for current version instead of /trunk/ is not supported.\n";
169                    }
170            } until ($tmppath =~ m/^$SVNREP/);
171    
172            print "NOTICE: using $SVNREP as directory for svn\n";
173    
174          printf($fmt, $e->{'revision'}, $e->{'author'}, $e->{'date'}, $e->{'msg'});          printf($fmt, $e->{'revision'}, $e->{'author'}, $e->{'date'}, $e->{'msg'});
175          foreach my $p (@{$e->{'paths'}->{'path'}}) {          foreach my $p (@{$e->{'paths'}->{'path'}}) {
# Line 170  foreach my $e (@{$xml->{'logentry'}}) { Line 179  foreach my $e (@{$xml->{'logentry'}}) {
179    
180                  # prepare path and message                  # prepare path and message
181                  my $file = $path;                  my $file = $path;
182                  $path =~ s,^/$SVNREP/*,, || die "BUG: can't strip SVNREP from path";                  $path =~ s,^$SVNREP/*,, || die "BUG: can't strip SVNREP from path";
183    
184                    if (! $path) {
185                            print "NOTICE: skipped this operation. Probably trunk creation\n";
186                            next;
187                    }
188    
189                  my $msg = $e->{'msg'};                  my $msg = $e->{'msg'};
190                  $msg =~ s/"/\\"/g;      # quote "                  $msg =~ s/'/\\'/g;      # quote "
191    
192                  if ($action =~ /M/) {                  if ($action =~ /M/) {
193                          print "svn2cvs: modify $path -- nop\n";                          print "svn2cvs: modify $path -- nop\n";
194                  } elsif ($action =~ /A/) {                  } elsif ($action =~ /A/) {
195                          log_system("$cvs add -m \"$msg\" $CVSREP/$path", "cvs add of $path failed");                          if (-d $path) {
196                                    chdir($path) || die "can't cd into dir $path for import: $!";
197                                    log_system("$cvs import -d -m '$msg' $CVSREP/$path svn r$rev", "cvs import of $path failed");
198                                    chdir("$TMPDIR") || die "can't cd to $TMPDIR/$CVSREP: $!";
199                                    log_system("$cvs checkout $CVSREP/$path", "cvs checkout of imported dir $path failed");
200                                    chdir("$TMPDIR/$CVSREP") || die "can't cd back to $TMPDIR/$CVSREP: $!";
201                            } else {
202                                    log_system("$cvs add -m '$msg' $path", "cvs add of $path failed");
203                            }
204                  } elsif ($action =~ /D/) {                  } elsif ($action =~ /D/) {
205                          log_system("$cvs delete -m \"$msg\" $CVSREP/$path", "cvs delete of $path failed");                          log_system("$cvs delete -m '$msg' $path", "cvs delete of $path failed");
206                  } else {                  } else {
207                          print "WARNING: action $action not implemented on $path. Bug or missing feature of $0\n";                          print "WARNING: action $action not implemented on $path. Bug or missing feature of $0\n";
208                  }                  }
209    
210                  # now commit changes                  # now commit changes
211                  log_system("$cvs commit -m \"$msg\" $CVSREP/$path", "cvs commit of $path failed");                  log_system("$cvs commit -m '$msg' $path", "cvs commit of $path failed");
212    
213          }          }
214    
# Line 194  foreach my $e (@{$xml->{'logentry'}}) { Line 217  foreach my $e (@{$xml->{'logentry'}}) {
217    
218  __END__  __END__
219    
220  svn export --force "$SVNROOT/$SVNREP" "$CVSREP"  =pod
221    
222  cd dotfiles  =head1 NAME
223    
224  for file in $(find -type f -not -path \*CVS\*); do  svn2cvs - save subversion commits to (read-only) cvs repository
225          FILE=$(basename $file)  
226          DIR=$(dirname $file)  =head1 SYNOPSIS
227          if ! grep -q "^/$FILE/" $DIR/CVS/Entries ; then  
228                  cvs add $file    ./svn2cvs.pl SVN_URL CVSROOT CVSREPOSITORY
229          fi  
230  done  Usage example (used to self-host this script):
231    
232  #cvs commit -m "Automatic commit from SVN"    ./svn2cvs.pl file:///home/dpavlin/private/svn/svn2cvs/trunk/ \
233                   :pserver:dpavlin@cvs.tigris.org:/cvs svn2cvs/src
234  #rm -rf $TMPDIR  
235    =head1 DESCRIPTION
236    
237    This script will allow you to commit changes made to Subversion repository also to (read-only) CVS repository manually or from Subversion's C<post-commit> hook.
238    
239    It's using F<.svnrev> file (which will be created on first run) in
240    B<CVSROOT/CVSREPOSITORY> to store last Subversion revision which was
241    committed into CVS.
242    
243    One run will do following things:
244    
245    =over 4
246    
247    =item *
248    checkout B<CVSREPOSITORY> from B<CVSROOT> to temporary directory
249    
250    =item *
251    check if F<.svnrev> file exists and create it if it doesn't
252    
253    =item *
254    loop through all revisions from current in B<CVSROOT/CVSREPOSITORY> (using
255    F<.svnrev>) up to B<HEAD> (current one)
256    
257    =over 5
258    
259    =item *
260    checkout next Subversion revision from B<SVN_URL> over CVS checkout
261    temporary directory
262    
263    =item *
264    make modification (add and/or delete) done in that revision
265    
266    =item *
267    commit modification (added, deleted or modified files/dirs) while
268    preserving original message from CVS
269    
270    =item *
271    update F<.svnrev> to match current revision
272    
273    =back
274    
275    =item *
276    cleanup temporary directory
277    
278    =back
279    
280    If checkout fails for some reason (e.g. flaky ssh connection), you will
281    still have valid CVS repository, so all you have to do is run B<svn2cvs.pl>
282    again.
283    
284    =head1 WARNINGS
285    
286    "Cheap" copy operations in Subversion are not at all cheap in CVS. They will
287    create multiple copies of files in CVS repository!
288    
289    =head1 RELATED PROJECTS
290    
291    B<Subversion> L<http://subversion.tigris.org/> version control system that is a
292    compelling replacement for CVS in the open source community.
293    
294    B<cvs2svn> L<http://cvs2svn.tigris.org/> converts a CVS repository to a
295    Subversion repository. It is designed for one-time conversions, not for
296    repeated synchronizations between CVS and Subversion.
297    
298    =head1 AUTHOR
299    
300    Dobrica Pavlinusic <dpavlin@rot13.org>
301    
302    L<https://www.rot13.org/~dpavlin/>
303    
304    =head1 LICENSE
305    
306    This product is licensed under GNU Public License (GPL) v2 or later.
307    
308    =cut
309    
 echo "cvs left in $TMPDIR"  

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

  ViewVC Help
Powered by ViewVC 1.1.26