Document how various VCSs handle keywords, EOLs, and file permissions.
[cvs2svn.git] / contrib / cvsVsvn.pl
blob7c0c2376308e223ac7b07c97334caf681dc5b684
1 #!/usr/bin/perl -w
4 # (C) 2005 The Measurement Factory http://www.measurement-factory.com/
5 # This software is distributed under Apache License, version 2.0.
8 # The cvsVsvn.pl script compares CVS and Subversion projects. The
9 # user is notified if project files maintained by CVS differ from
10 # those maintained by Subversion:
12 # $ CVSROOT=/my/cvsrepo ./cvsVsvn project1 svn+ssh://host/path/projects/p1
13 # collecting CVS tags...
14 # found 34 CVS tags
15 # comparing tagged snapshots...
16 # HEAD snapshots appear to be the same
17 # ...
18 # CVS and SVN repositories differ because cvs_foo and svn_tags_foo
19 # export directories differ in cvsVsvn.tmp
21 # The comparison is done for every CVS tag and branch (including
22 # HEAD), by exporting corresponding CVS and Subversion snapshots and
23 # running 'diff' against the two resulting directories. One can edit
24 # the script or use the environment variable DIFF_OPTIONS to alter
25 # 'diff' behavior (e.g., ignore differences in some files).
27 # Commit logs are not compared, unfortunately. This script is also
28 # confused by files that differ due to different keyword expansion by
29 # CVS and SVN.
31 use strict;
33 # cvsVsvn exports a user-specified module from CVS and Subversion
34 # repositories and compares the two exported directories using the
35 # 'diff' tool. The procedure is performed for all CVS tags (including
36 # HEAD and branches).
38 die(&usage()) unless @ARGV == 2;
39 my ($CvsModule, $SvnModule) = @ARGV;
41 my $TmpDir = 'cvsVsvn.tmp'; # directory to store temporary files
43 my @Tags = &collectTags();
45 print(STDERR "comparing tagged snapshots...\n");
46 foreach my $tagPair (@Tags) {
47 &compareTags($tagPair->{cvs}, $tagPair->{svn});
50 print(STDERR "CVS and Subversion repositories appear to be the same\n");
51 exit(0);
53 sub collectTags {
54 print(STDERR "collecting CVS tags...\n");
56 my @tags = (
58 cvs => 'HEAD',
59 svn => 'trunk'
63 # get CVS log headers with symbolic tags
64 my %names = ();
65 my $inNames;
66 my $cmd = sprintf('cvs rlog -h %s', $CvsModule);
67 open(IF, "$cmd|") or die("cannot execute $cmd: $!, stopped");
68 while (<IF>) {
69 if ($inNames) {
70 my ($name, $version) = /\s+(\S+):\s*(\d\S*)/;
71 if ($inNames = defined $version) {
72 my @nums = split(/\./, $version);
73 my $isBranch =
74 (2*int(@nums/2) != @nums) ||
75 (@nums > 2 && $nums[$#nums-1] == 0);
76 my $status = $isBranch ? 'branches' : 'tags';
77 my $oldStatus = $names{$name};
78 next if $oldStatus && $oldStatus eq $status;
79 die("change in $name tag status, stopped") if $oldStatus;
80 $names{$name} = $status;
82 } else {
83 $inNames = /^symbolic names:/;
86 close(IF);
88 while (my ($name, $status) = each %names) {
89 my $tagPair = {
90 cvs => $name,
91 svn => sprintf('%s/%s', $status, $name)
93 push (@tags, $tagPair);
96 printf(STDERR "found %d CVS tags\n", scalar @tags);
97 return @tags;
100 sub compareTags {
101 my ($cvsTag, $svnTag) = @_;
103 &prepDirs();
105 &cvsExport($cvsTag);
106 &svnExport($svnTag);
108 &diffDir($cvsTag, $svnTag);
110 # identical directories, clean up
111 &cleanDirs();
114 sub diffDir {
115 my ($cvsTag, $svnTag) = @_;
116 my $cvsDir = &cvsDir($cvsTag);
117 my $svnDir = &svnDir($svnTag);
119 my $same = systemf('diff --brief -b -B -r "%s" "%s"',
120 $cvsDir, $svnDir) == 0;
121 die("CVS and SVN repositories differ because ".
122 "$cvsDir and $svnDir export directories differ in $TmpDir; stopped")
123 unless $same;
125 print(STDERR "$cvsTag snapshots appear to be the same\n");
126 return 0;
129 sub makeDir {
130 my $dir = shift;
131 &systemf('mkdir %s', $dir) == 0 or die("cannot create $dir: $!, stopped");
134 sub prepDirs {
135 &makeDir($TmpDir);
136 chdir($TmpDir) or die($!);
139 sub cleanDirs {
140 chdir('..') or die($!);
141 &systemf('rm -irf %s', $TmpDir) == 0
142 or die("cannot delete $TmpDir: $!, stopped");
145 sub cvsExport {
146 my ($cvsTag) = @_;
148 my $dir = &cvsDir($cvsTag);
149 &makeDir($dir);
150 &systemf('cvs -Q export -r %s -d %s %s', $cvsTag, $dir, $CvsModule) == 0
151 or die("cannot export $cvsTag of CVS module '$CvsModule', stopped");
154 sub svnExport {
155 my ($svnTag) = @_;
157 my $dir = &svnDir($svnTag);
158 my $cvsOk =
159 &systemf('svn list %s/%s > /dev/null', $SvnModule, $svnTag) == 0
160 && &systemf('svn -q export %s/%s %s', $SvnModule, $svnTag, $dir) == 0;
161 die("cannot export $svnTag of svn module '$SvnModule', stopped")
162 unless $cvsOk && -d $dir;
165 sub tag2dir {
166 my ($category, $tag) = @_;
168 my $dir = sprintf('%s_%s', $category, $tag);
169 # remove dangerous chars
170 $dir =~ s/[^A-z0-9_\.\-]+/_/g;
171 return $dir;
174 sub cvsDir {
175 return &tag2dir('cvs', @_);
178 sub svnDir {
179 return &tag2dir('svn', @_);
182 sub systemf {
183 my ($fmt, @params) = @_;
185 my $cmd = sprintf($fmt, (@params));
186 #print(STDERR "$cmd\n");
187 return system($cmd);
190 sub usage {
191 return "usage: $0 <CVS module name> <Subversion URL>\n";