blob: 9e03eee4586ca3b7476b56f66e9dcf6ffe3088cf [file] [log] [blame]
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001#!/usr/bin/perl -w
Tommy M. McGuire9718a002005-06-10 01:38:32 -05002
Matthias Urlichsa57a9492005-06-28 16:48:40 +02003# This tool is copyright (c) 2005, Matthias Urlichs.
4# It is released under the Gnu Public License, version 2.
5#
6# The basic idea is to aggregate CVS check-ins into related changes.
7# Fortunately, "cvsps" does that for us; all we have to do is to parse
8# its output.
9#
10# Checking out the files is done by a single long-running CVS connection
11# / server process.
12#
13# The head revision is on branch "origin" by default.
14# You can change that with the '-o' option.
15
16use strict;
17use warnings;
Philippe Bruhat (BooKbc434e82008-02-28 11:18:22 +010018use Getopt::Long;
Sven Verdoolaege79ee4562005-07-04 13:36:59 +020019use File::Spec;
Martin Langhoff7ccd9002006-06-24 23:13:08 +120020use File::Temp qw(tempfile tmpnam);
Matthias Urlichsa57a9492005-06-28 16:48:40 +020021use File::Path qw(mkpath);
22use File::Basename qw(basename dirname);
23use Time::Local;
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +020024use IO::Socket;
25use IO::Pipe;
Jeff Kinge49289d2006-05-24 09:58:28 -040026use POSIX qw(strftime dup2 ENOENT);
H. Peter Anvin0d821d42005-09-06 10:36:01 -070027use IPC::Open2;
Matthias Urlichsa57a9492005-06-28 16:48:40 +020028
29$SIG{'PIPE'}="IGNORE";
30$ENV{'TZ'}="UTC";
31
Aaron Crane0455ec02010-02-06 18:26:24 +000032our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,@opt_M,$opt_A,$opt_S,$opt_L, $opt_a, $opt_r, $opt_R);
Andreas Ericssonffd97f32006-01-13 00:38:59 +010033my (%conv_author_name, %conv_author_email);
Matthias Urlichsa57a9492005-06-28 16:48:40 +020034
Frank Lichtenheld7bf77642007-04-06 23:52:41 +020035sub usage(;$) {
36 my $msg = shift;
37 print(STDERR "Error: $msg\n") if $msg;
Matthias Urlichsa57a9492005-06-28 16:48:40 +020038 print STDERR <<END;
Stephan Beyer1b1dd232008-07-13 15:36:15 +020039Usage: git cvsimport # fetch/update GIT from CVS
Andreas Ericssonffd97f32006-01-13 00:38:59 +010040 [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
Frank Lichtenheldedbe4462007-04-06 23:52:39 +020041 [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
42 [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
Aaron Crane0455ec02010-02-06 18:26:24 +000043 [-r remote] [-R] [CVS_module]
Matthias Urlichsa57a9492005-06-28 16:48:40 +020044END
45 exit(1);
Tommy M. McGuire9718a002005-06-10 01:38:32 -050046}
47
Andreas Ericssonffd97f32006-01-13 00:38:59 +010048sub read_author_info($) {
49 my ($file) = @_;
50 my $user;
51 open my $f, '<', "$file" or die("Failed to open $file: $!\n");
52
53 while (<$f>) {
Junio C Hamano8cd16212006-01-15 03:30:30 -080054 # Expected format is this:
Andreas Ericssonffd97f32006-01-13 00:38:59 +010055 # exon=Andreas Ericsson <ae@op5.se>
Junio C Hamano8cd16212006-01-15 03:30:30 -080056 if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
Andreas Ericssonffd97f32006-01-13 00:38:59 +010057 $user = $1;
Junio C Hamano8cd16212006-01-15 03:30:30 -080058 $conv_author_name{$user} = $2;
59 $conv_author_email{$user} = $3;
Andreas Ericssonffd97f32006-01-13 00:38:59 +010060 }
Junio C Hamano8cd16212006-01-15 03:30:30 -080061 # However, we also read from CVSROOT/users format
62 # to ease migration.
63 elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
64 my $mapped;
65 ($user, $mapped) = ($1, $3);
66 if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
67 $conv_author_name{$user} = $1;
68 $conv_author_email{$user} = $2;
69 }
70 elsif ($mapped =~ /^<?(.*)>?$/) {
71 $conv_author_name{$user} = $user;
72 $conv_author_email{$user} = $1;
73 }
74 }
75 # NEEDSWORK: Maybe warn on unrecognized lines?
Andreas Ericssonffd97f32006-01-13 00:38:59 +010076 }
77 close ($f);
78}
79
80sub write_author_info($) {
81 my ($file) = @_;
82 open my $f, '>', $file or
83 die("Failed to open $file for writing: $!");
84
85 foreach (keys %conv_author_name) {
Junio C Hamano8cd16212006-01-15 03:30:30 -080086 print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>\n";
Andreas Ericssonffd97f32006-01-13 00:38:59 +010087 }
88 close ($f);
89}
90
Dan McGeecfc44a12008-01-13 22:51:10 -060091# convert getopts specs for use by git config
James Bowesed35dec2007-02-07 17:57:43 -050092sub read_repo_config {
93 # Split the string between characters, unless there is a ':'
94 # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
95 my @opts = split(/ *(?!:)/, shift);
96 foreach my $o (@opts) {
97 my $key = $o;
98 $key =~ s/://g;
Dan McGeecfc44a12008-01-13 22:51:10 -060099 my $arg = 'git config';
James Bowesed35dec2007-02-07 17:57:43 -0500100 $arg .= ' --bool' if ($o !~ /:$/);
101
102 chomp(my $tmp = `$arg --get cvsimport.$key`);
103 if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
104 no strict 'refs';
105 my $opt_name = "opt_" . $key;
106 if (!$$opt_name) {
107 $$opt_name = $tmp;
108 }
109 }
110 }
James Bowesed35dec2007-02-07 17:57:43 -0500111}
112
Aaron Crane0455ec02010-02-06 18:26:24 +0000113my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
James Bowesed35dec2007-02-07 17:57:43 -0500114read_repo_config($opts);
Philippe Bruhat (BooKbc434e82008-02-28 11:18:22 +0100115Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
116
117# turn the Getopt::Std specification in a Getopt::Long one,
118# with support for multiple -M options
119GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
120 or usage();
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200121usage if $opt_h;
Linus Torvaldsd4f8b392005-06-07 15:11:28 -0700122
Jeff King67d23242007-11-30 17:22:12 -0500123if (@ARGV == 0) {
Dan McGeecfc44a12008-01-13 22:51:10 -0600124 chomp(my $module = `git config --get cvsimport.module`);
Jeff King67d23242007-11-30 17:22:12 -0500125 push(@ARGV, $module) if $? == 0;
126}
Frank Lichtenheld7bf77642007-04-06 23:52:41 +0200127@ARGV <= 1 or usage("You can't specify more than one CVS module");
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200128
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800129if ($opt_d) {
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200130 $ENV{"CVSROOT"} = $opt_d;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800131} elsif (-f 'CVS/Root') {
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200132 open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
133 $opt_d = <$f>;
134 chomp $opt_d;
135 close $f;
136 $ENV{"CVSROOT"} = $opt_d;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800137} elsif ($ENV{"CVSROOT"}) {
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200138 $opt_d = $ENV{"CVSROOT"};
139} else {
Frank Lichtenheld7bf77642007-04-06 23:52:41 +0200140 usage("CVSROOT needs to be set");
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200141}
Johannes Schindelinfbfd60d2005-08-17 11:19:20 +0200142$opt_s ||= "-";
Martin Langhoffded9f402007-01-08 19:43:39 +1300143$opt_a ||= 0;
144
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200145my $git_tree = $opt_C;
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200146$git_tree ||= ".";
147
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +0100148my $remote;
149if (defined $opt_r) {
150 $remote = 'refs/remotes/' . $opt_r;
151 $opt_o ||= "master";
152} else {
153 $opt_o ||= "origin";
154 $remote = 'refs/heads';
155}
156
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200157my $cvs_tree;
158if ($#ARGV == 0) {
159 $cvs_tree = $ARGV[0];
160} elsif (-f 'CVS/Repository') {
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700161 open my $f, '<', 'CVS/Repository' or
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200162 die 'Failed to open CVS/Repository';
163 $cvs_tree = <$f>;
164 chomp $cvs_tree;
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200165 close $f;
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200166} else {
Frank Lichtenheld7bf77642007-04-06 23:52:41 +0200167 usage("CVS module has to be specified");
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200168}
169
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200170our @mergerx = ();
171if ($opt_m) {
Philippe Bruhat (BooKfbbbc362008-02-28 11:18:21 +0100172 @mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200173}
Philippe Bruhat (BooKbc434e82008-02-28 11:18:22 +0100174if (@opt_M) {
175 push (@mergerx, map { qr/$_/ } @opt_M);
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200176}
177
Martin Langhoff62119882007-01-08 14:11:23 +1300178# Remember UTC of our starting time
179# we'll want to avoid importing commits
180# that are too recent
181our $starttime = time();
182
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200183select(STDERR); $|=1; select(STDOUT);
184
185
186package CVSconn;
187# Basic CVS dialog.
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200188# We're only interested in connecting and downloading, so ...
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200189
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200190use File::Spec;
191use File::Temp qw(tempfile);
Matthias Urlichsf65ae602005-06-28 19:58:40 +0200192use POSIX qw(strftime dup2);
193
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200194sub new {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800195 my ($what,$repo,$subdir) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200196 $what=ref($what) if ref($what);
197
198 my $self = {};
199 $self->{'buffer'} = "";
200 bless($self,$what);
201
202 $repo =~ s#/+$##;
203 $self->{'fullrep'} = $repo;
204 $self->conn();
205
206 $self->{'subdir'} = $subdir;
207 $self->{'lines'} = undef;
208
209 return $self;
Linus Torvaldsd4f8b392005-06-07 15:11:28 -0700210}
211
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200212sub conn {
213 my $self = shift;
214 my $repo = $self->{'fullrep'};
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800215 if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
216 my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100217
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800218 my ($proxyhost,$proxyport);
219 if ($param && ($param =~ m/proxy=([^;]+)/)) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100220 $proxyhost = $1;
221 # Default proxyport, if not specified, is 8080.
222 $proxyport = 8080;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800223 if ($ENV{"CVS_PROXY_PORT"}) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100224 $proxyport = $ENV{"CVS_PROXY_PORT"};
225 }
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800226 if ($param =~ m/proxyport=([^;]+)/) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100227 $proxyport = $1;
228 }
229 }
Philippe Bruhat (BooK)8c372fb2008-06-10 14:32:06 +0200230 $repo ||= '/';
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100231
Gordon Hopper2e458e02007-11-08 13:15:20 -0700232 # if username is not explicit in CVSROOT, then use current user, as cvs would
233 $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200234 my $rr2 = "-";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800235 unless ($port) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200236 $rr2 = ":pserver:$user\@$serv:$repo";
237 $port=2401;
238 }
239 my $rr = ":pserver:$user\@$serv:$port$repo";
Linus Torvaldsd4f8b392005-06-07 15:11:28 -0700240
Pascal Obry3fb9d582009-09-04 13:58:32 +0200241 if ($pass) {
242 $pass = $self->_scramble($pass);
243 } else {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200244 open(H,$ENV{'HOME'}."/.cvspass") and do {
245 # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800246 while (<H>) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200247 chomp;
248 s/^\/\d+\s+//;
249 my ($w,$p) = split(/\s/,$_,2);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800250 if ($w eq $rr or $w eq $rr2) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200251 $pass = $p;
252 last;
253 }
254 }
255 };
Clemens Buchachere481b1d2009-09-17 09:21:02 +0200256 $pass = "A" unless $pass;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200257 }
Dirk Hoernerb2139db2009-08-14 08:58:31 +0200258
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100259 my ($s, $rep);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800260 if ($proxyhost) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100261
262 # Use a HTTP Proxy. Only works for HTTP proxies that
263 # don't require user authentication
264 #
265 # See: http://www.ietf.org/rfc/rfc2817.txt
266
267 $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
268 die "Socket to $proxyhost: $!\n" unless defined $s;
269 $s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
270 or die "Write to $proxyhost: $!\n";
271 $s->flush();
272
273 $rep = <$s>;
274
275 # The answer should look like 'HTTP/1.x 2yy ....'
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800276 if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100277 die "Proxy connect: $rep\n";
278 }
279 # Skip up to the empty line of the proxy server output
280 # including the response headers.
281 while ($rep = <$s>) {
282 last if (!defined $rep ||
283 $rep eq "\n" ||
284 $rep eq "\r\n");
285 }
286 } else {
287 $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
288 die "Socket to $serv: $!\n" unless defined $s;
289 }
290
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200291 $s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
292 or die "Write to $serv: $!\n";
293 $s->flush();
294
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100295 $rep = <$s>;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200296
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800297 if ($rep ne "I LOVE YOU\n") {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200298 $rep="<unknown>" unless $rep;
299 die "AuthReply: $rep\n";
300 }
301 $self->{'socketo'} = $s;
302 $self->{'socketi'} = $s;
Sven Verdoolaege34155392005-07-03 13:02:06 +0200303 } else { # local or ext: Fork off our own cvs server.
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200304 my $pr = IO::Pipe->new();
305 my $pw = IO::Pipe->new();
306 my $pid = fork();
307 die "Fork: $!\n" unless defined $pid;
Sven Verdoolaege8d0ea312005-07-03 12:26:51 +0200308 my $cvs = 'cvs';
309 $cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
Sven Verdoolaege34155392005-07-03 13:02:06 +0200310 my $rsh = 'rsh';
311 $rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
312
313 my @cvs = ($cvs, 'server');
314 my ($local, $user, $host);
315 $local = $repo =~ s/:local://;
316 if (!$local) {
317 $repo =~ s/:ext://;
318 $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
319 ($user, $host) = ($1, $2);
320 }
321 if (!$local) {
322 if ($user) {
323 unshift @cvs, $rsh, '-l', $user, $host;
324 } else {
325 unshift @cvs, $rsh, $host;
326 }
327 }
328
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800329 unless ($pid) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200330 $pr->writer();
331 $pw->reader();
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200332 dup2($pw->fileno(),0);
333 dup2($pr->fileno(),1);
334 $pr->close();
335 $pw->close();
Sven Verdoolaege34155392005-07-03 13:02:06 +0200336 exec(@cvs);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200337 }
338 $pw->writer();
339 $pr->reader();
340 $self->{'socketo'} = $pw;
341 $self->{'socketi'} = $pr;
342 }
343 $self->{'socketo'}->write("Root $repo\n");
344
345 # Trial and error says that this probably is the minimum set
iso-8859-1?Q?David_K=E5gedalb0921332005-08-15 20:18:25 +0200346 $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200347
348 $self->{'socketo'}->write("valid-requests\n");
349 $self->{'socketo'}->flush();
350
351 chomp(my $rep=$self->readline());
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800352 if ($rep !~ s/^Valid-requests\s*//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200353 $rep="<unknown>" unless $rep;
354 die "Expected Valid-requests from server, but got: $rep\n";
355 }
356 chomp(my $res=$self->readline());
357 die "validReply: $res\n" if $res ne "ok";
358
359 $self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
360 $self->{'repo'} = $repo;
361}
362
363sub readline {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800364 my ($self) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200365 return $self->{'socketi'}->getline();
366}
367
368sub _file {
369 # Request a file with a given revision.
370 # Trial and error says this is a good way to do it. :-/
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800371 my ($self,$fn,$rev) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200372 $self->{'socketo'}->write("Argument -N\n") or return undef;
373 $self->{'socketo'}->write("Argument -P\n") or return undef;
Martin Langhoffabe05822005-08-16 17:39:29 +1200374 # -kk: Linus' version doesn't use it - defaults to off
375 if ($opt_k) {
376 $self->{'socketo'}->write("Argument -kk\n") or return undef;
377 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200378 $self->{'socketo'}->write("Argument -r\n") or return undef;
379 $self->{'socketo'}->write("Argument $rev\n") or return undef;
380 $self->{'socketo'}->write("Argument --\n") or return undef;
381 $self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
382 $self->{'socketo'}->write("Directory .\n") or return undef;
383 $self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
Matthias Urlichs4f7c0ca2005-06-30 12:19:48 +0200384 # $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200385 $self->{'socketo'}->write("co\n") or return undef;
386 $self->{'socketo'}->flush() or return undef;
387 $self->{'lines'} = 0;
388 return 1;
389}
390sub _line {
391 # Read a line from the server.
392 # ... except that 'line' may be an entire file. ;-)
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800393 my ($self, $fh) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200394 die "Not in lines" unless defined $self->{'lines'};
395
396 my $line;
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200397 my $res=0;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800398 while (defined($line = $self->readline())) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200399 # M U gnupg-cvs-rep/AUTHORS
400 # Updated gnupg-cvs-rep/
401 # /daten/src/rsync/gnupg-cvs-rep/AUTHORS
402 # /AUTHORS/1.1///T1.1
403 # u=rw,g=rw,o=rw
404 # 0
405 # ok
406
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800407 if ($line =~ s/^(?:Created|Updated) //) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200408 $line = $self->readline(); # path
409 $line = $self->readline(); # Entries line
410 my $mode = $self->readline(); chomp $mode;
411 $self->{'mode'} = $mode;
412 defined (my $cnt = $self->readline())
413 or die "EOF from server after 'Changed'\n";
414 chomp $cnt;
415 die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
416 $line="";
Martin Langhoff55cad842006-05-23 20:08:58 +1200417 $res = $self->_fetchfile($fh, $cnt);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800418 } elsif ($line =~ s/^ //) {
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200419 print $fh $line;
420 $res += length($line);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800421 } elsif ($line =~ /^M\b/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200422 # output, do nothing
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800423 } elsif ($line =~ /^Mbinary\b/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200424 my $cnt;
425 die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
426 chomp $cnt;
427 die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
428 $line="";
Martin Langhoff55cad842006-05-23 20:08:58 +1200429 $res += $self->_fetchfile($fh, $cnt);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200430 } else {
431 chomp $line;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800432 if ($line eq "ok") {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200433 # print STDERR "S: ok (".length($res).")\n";
434 return $res;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800435 } elsif ($line =~ s/^E //) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200436 # print STDERR "S: $line\n";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800437 } elsif ($line =~ /^(Remove-entry|Removed) /i) {
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200438 $line = $self->readline(); # filename
439 $line = $self->readline(); # OK
440 chomp $line;
441 die "Unknown: $line" if $line ne "ok";
442 return -1;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200443 } else {
444 die "Unknown: $line\n";
445 }
446 }
447 }
Martin Mares39ba7d52006-02-18 21:44:20 +0100448 return undef;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200449}
450sub file {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800451 my ($self,$fn,$rev) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200452 my $res;
453
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700454 my ($fh, $name) = tempfile('gitcvs.XXXXXX',
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200455 DIR => File::Spec->tmpdir(), UNLINK => 1);
456
457 $self->_file($fn,$rev) and $res = $self->_line($fh);
458
459 if (!defined $res) {
Martin Mares39ba7d52006-02-18 21:44:20 +0100460 print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
461 truncate $fh, 0;
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200462 $self->conn();
Martin Mares39ba7d52006-02-18 21:44:20 +0100463 $self->_file($fn,$rev) or die "No file command send";
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200464 $res = $self->_line($fh);
Martin Mares39ba7d52006-02-18 21:44:20 +0100465 die "Retry failed" unless defined $res;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200466 }
Sven Verdoolaegec619ad52005-07-06 08:37:12 +0200467 close ($fh);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200468
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200469 return ($name, $res);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200470}
Martin Langhoff55cad842006-05-23 20:08:58 +1200471sub _fetchfile {
472 my ($self, $fh, $cnt) = @_;
Junio C Hamano61efa5e2006-05-23 16:30:39 -0700473 my $res = 0;
Martin Langhoff55cad842006-05-23 20:08:58 +1200474 my $bufsize = 1024 * 1024;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800475 while ($cnt) {
Martin Langhoff55cad842006-05-23 20:08:58 +1200476 if ($bufsize > $cnt) {
477 $bufsize = $cnt;
478 }
479 my $buf;
480 my $num = $self->{'socketi'}->read($buf,$bufsize);
481 die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
482 print $fh $buf;
483 $res += $num;
484 $cnt -= $num;
485 }
486 return $res;
487}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200488
Dirk Hoernerb2139db2009-08-14 08:58:31 +0200489sub _scramble {
490 my ($self, $pass) = @_;
491 my $scrambled = "A";
492
493 return $scrambled unless $pass;
494
495 my $pass_len = length($pass);
496 my @pass_arr = split("", $pass);
497 my $i;
498
499 # from cvs/src/scramble.c
500 my @shifts = (
501 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
502 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
503 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
504 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
505 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
506 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
507 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
508 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
509 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
510 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
511 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
512 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
513 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
514 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
515 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
516 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
517 );
518
519 for ($i = 0; $i < $pass_len; $i++) {
520 $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
521 }
522
523 return $scrambled;
524}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200525
526package main;
527
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200528my $cvs = CVSconn->new($opt_d, $cvs_tree);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200529
530
531sub pdate($) {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800532 my ($d) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200533 m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
534 or die "Unparseable date: $d\n";
535 my $y=$1; $y-=1900 if $y>1900;
536 return timegm($6||0,$5,$4,$3,$2-1,$y);
537}
538
539sub pmode($) {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800540 my ($mode) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200541 my $m = 0;
542 my $mm = 0;
543 my $um = 0;
544 for my $x(split(//,$mode)) {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800545 if ($x eq ",") {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200546 $m |= $mm&$um;
547 $mm = 0;
548 $um = 0;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800549 } elsif ($x eq "u") { $um |= 0700;
550 } elsif ($x eq "g") { $um |= 0070;
551 } elsif ($x eq "o") { $um |= 0007;
552 } elsif ($x eq "r") { $mm |= 0444;
553 } elsif ($x eq "w") { $mm |= 0222;
554 } elsif ($x eq "x") { $mm |= 0111;
555 } elsif ($x eq "=") { # do nothing
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200556 } else { die "Unknown mode: $mode\n";
557 }
558 }
559 $m |= $mm&$um;
560 return $m;
561}
562
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200563sub getwd() {
564 my $pwd = `pwd`;
565 chomp $pwd;
566 return $pwd;
567}
568
Jeff Kinge73aefe2006-05-23 03:27:46 -0400569sub is_sha1 {
570 my $s = shift;
571 return $s =~ /^[a-f0-9]{40}$/;
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200572}
573
Jeff King9da0dab2007-11-28 13:56:11 -0500574sub get_headref ($) {
575 my $name = shift;
576 my $r = `git rev-parse --verify '$name' 2>/dev/null`;
577 return undef unless $? == 0;
578 chomp $r;
579 return $r;
Jeff Kinge73aefe2006-05-23 03:27:46 -0400580}
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200581
Jeff Kingf6fdbb62009-10-19 02:49:55 -0400582my $user_filename_prepend = '';
583sub munge_user_filename {
584 my $name = shift;
585 return File::Spec->file_name_is_absolute($name) ?
586 $name :
587 $user_filename_prepend . $name;
588}
589
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200590-d $git_tree
591 or mkdir($git_tree,0777)
592 or die "Could not create $git_tree: $!";
Jeff Kingf6fdbb62009-10-19 02:49:55 -0400593if ($git_tree ne '.') {
594 $user_filename_prepend = getwd() . '/';
595 chdir($git_tree);
596}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200597
598my $last_branch = "";
Matthias Urlichs46541662005-06-28 21:08:15 +0200599my $orig_branch = "";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200600my %branch_date;
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -0800601my $tip_at_start = undef;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200602
603my $git_dir = $ENV{"GIT_DIR"} || ".git";
604$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
605$ENV{"GIT_DIR"} = $git_dir;
Sven Verdoolaege79ee4562005-07-04 13:36:59 +0200606my $orig_git_index;
607$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
Martin Langhoff8f732642006-06-12 23:50:49 +1200608
609my %index; # holds filenames of one index per branch
Johannes Schindelin061303f2006-06-24 21:42:20 +0200610
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800611unless (-d $git_dir) {
Ben Walton91fe7322010-01-19 14:03:10 -0500612 system(qw(git init));
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200613 die "Cannot init the GIT db at $git_tree: $?\n" if $?;
Ben Walton91fe7322010-01-19 14:03:10 -0500614 system(qw(git read-tree));
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200615 die "Cannot init an empty tree: $?\n" if $?;
616
617 $last_branch = $opt_o;
Matthias Urlichs46541662005-06-28 21:08:15 +0200618 $orig_branch = "";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200619} else {
Ben Waltona12477d2010-01-19 14:03:09 -0500620 open(F, "-|", qw(git symbolic-ref HEAD)) or
Ben Walton640d9d02010-01-19 14:03:08 -0500621 die "Cannot run git symbolic-ref: $!\n";
Pavel Roskin8366a102005-11-16 13:27:28 -0500622 chomp ($last_branch = <F>);
623 $last_branch = basename($last_branch);
624 close(F);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800625 unless ($last_branch) {
Matthias Urlichs46541662005-06-28 21:08:15 +0200626 warn "Cannot read the last branch name: $! -- assuming 'master'\n";
627 $last_branch = "master";
628 }
629 $orig_branch = $last_branch;
Ben Walton640d9d02010-01-19 14:03:08 -0500630 $tip_at_start = `git rev-parse --verify HEAD`;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200631
632 # Get the last import timestamps
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100633 my $fmt = '($ref, $author) = (%(refname), %(author));';
Ben Waltona12477d2010-01-19 14:03:09 -0500634 my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
635 open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800636 while (defined(my $entry = <H>)) {
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100637 my ($ref, $author);
638 eval($entry) || die "cannot eval refs list: $@";
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +0100639 my ($head) = ($ref =~ m|^$remote/(.*)|);
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100640 $author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
641 $branch_date{$head} = $1;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200642 }
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100643 close(H);
Stephan Springl7ca055f2007-05-23 12:13:21 +0100644 if (!exists $branch_date{$opt_o}) {
645 die "Branch '$opt_o' does not exist.\n".
646 "Either use the correct '-o branch' option,\n".
647 "or import to a new repository.\n";
648 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200649}
650
651-d $git_dir
652 or die "Could not create git subdir ($git_dir).\n";
653
Andreas Ericssonffd97f32006-01-13 00:38:59 +0100654# now we read (and possibly save) author-info as well
655-f "$git_dir/cvs-authors" and
656 read_author_info("$git_dir/cvs-authors");
657if ($opt_A) {
Jeff Kingf6fdbb62009-10-19 02:49:55 -0400658 read_author_info(munge_user_filename($opt_A));
Andreas Ericssonffd97f32006-01-13 00:38:59 +0100659 write_author_info("$git_dir/cvs-authors");
660}
661
Aaron Crane0455ec02010-02-06 18:26:24 +0000662# open .git/cvs-revisions, if requested
663open my $revision_map, '>>', "$git_dir/cvs-revisions"
664 or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
665 if defined $opt_R;
666
Martin Langhoff2f57c692006-06-11 20:12:20 +1200667
668#
669# run cvsps into a file unless we are getting
670# it passed as a file via $opt_P
671#
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300672my $cvspsfile;
Martin Langhoff2f57c692006-06-11 20:12:20 +1200673unless ($opt_P) {
674 print "Running cvsps...\n" if $opt_v;
675 my $pid = open(CVSPS,"-|");
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300676 my $cvspsfh;
Martin Langhoff2f57c692006-06-11 20:12:20 +1200677 die "Cannot fork: $!\n" unless defined $pid;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800678 unless ($pid) {
Martin Langhoff2f57c692006-06-11 20:12:20 +1200679 my @opt;
680 @opt = split(/,/,$opt_p) if defined $opt_p;
681 unshift @opt, '-z', $opt_z if defined $opt_z;
682 unshift @opt, '-q' unless defined $opt_v;
683 unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
684 push @opt, '--cvs-direct';
685 }
686 exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
687 die "Could not start cvsps: $!\n";
Martin Langhoffdf73e9c2005-10-11 21:57:04 -0700688 }
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300689 ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
690 DIR => File::Spec->tmpdir());
Martin Langhoff2f57c692006-06-11 20:12:20 +1200691 while (<CVSPS>) {
692 print $cvspsfh $_;
Martin Langhoff211dcac2005-11-02 13:48:47 +1300693 }
Martin Langhoff2f57c692006-06-11 20:12:20 +1200694 close CVSPS;
Ben Walton640d9d02010-01-19 14:03:08 -0500695 $? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
Martin Langhoff2f57c692006-06-11 20:12:20 +1200696 close $cvspsfh;
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300697} else {
Jeff Kingf6fdbb62009-10-19 02:49:55 -0400698 $cvspsfile = munge_user_filename($opt_P);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200699}
700
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300701open(CVS, "<$cvspsfile") or die $!;
Martin Langhoff2f57c692006-06-11 20:12:20 +1200702
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200703## cvsps output:
704#---------------------
705#PatchSet 314
706#Date: 1999/09/18 13:03:59
707#Author: wkoch
708#Branch: STABLE-BRANCH-1-0
709#Ancestor branch: HEAD
710#Tag: (none)
711#Log:
712# See ChangeLog: Sat Sep 18 13:03:28 CEST 1999 Werner Koch
713#Members:
714# README:1.57->1.57.2.1
715# VERSION:1.96->1.96.2.1
716#
717#---------------------
718
719my $state = 0;
720
Jeff Kinge73aefe2006-05-23 03:27:46 -0400721sub update_index (\@\@) {
722 my $old = shift;
723 my $new = shift;
Ben Walton640d9d02010-01-19 14:03:08 -0500724 open(my $fh, '|-', qw(git update-index -z --index-info))
725 or die "unable to open git update-index: $!";
Jeff King6a1871e2006-05-23 03:27:45 -0400726 print $fh
727 (map { "0 0000000000000000000000000000000000000000\t$_\0" }
Jeff Kinge73aefe2006-05-23 03:27:46 -0400728 @$old),
Jeff King6a1871e2006-05-23 03:27:45 -0400729 (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
Jeff Kinge73aefe2006-05-23 03:27:46 -0400730 @$new)
Ben Walton640d9d02010-01-19 14:03:08 -0500731 or die "unable to write to git update-index: $!";
Jeff King6a1871e2006-05-23 03:27:45 -0400732 close $fh
Ben Walton640d9d02010-01-19 14:03:08 -0500733 or die "unable to write to git update-index: $!";
734 $? and die "git update-index reported error: $?";
Jeff Kinge73aefe2006-05-23 03:27:46 -0400735}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200736
Jeff Kinge73aefe2006-05-23 03:27:46 -0400737sub write_tree () {
Ben Waltona12477d2010-01-19 14:03:09 -0500738 open(my $fh, '-|', qw(git write-tree))
Ben Walton640d9d02010-01-19 14:03:08 -0500739 or die "unable to open git write-tree: $!";
Jeff Kinge73aefe2006-05-23 03:27:46 -0400740 chomp(my $tree = <$fh>);
741 is_sha1($tree)
742 or die "Cannot get tree id ($tree): $!";
743 close($fh)
Ben Walton640d9d02010-01-19 14:03:08 -0500744 or die "Error running git write-tree: $?\n";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200745 print "Tree ID $tree\n" if $opt_v;
Jeff Kinge73aefe2006-05-23 03:27:46 -0400746 return $tree;
747}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200748
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800749my ($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
Aaron Crane0455ec02010-02-06 18:26:24 +0000750my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
Martin Langhoff71b08142006-06-11 20:12:09 +1200751
752# commits that cvsps cannot place anywhere...
753$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
754
Jeff Kinge73aefe2006-05-23 03:27:46 -0400755sub commit {
Jeff King9da0dab2007-11-28 13:56:11 -0500756 if ($branch eq $opt_o && !$index{branch} &&
757 !get_headref("$remote/$branch")) {
Martin Langhoffc5f448b2006-06-28 22:13:23 +1200758 # looks like an initial commit
Ben Walton640d9d02010-01-19 14:03:08 -0500759 # use the index primed by git init
Michael Milligan23fcdc72007-06-05 00:06:30 -0600760 $ENV{GIT_INDEX_FILE} = "$git_dir/index";
761 $index{$branch} = "$git_dir/index";
Martin Langhoffc5f448b2006-06-28 22:13:23 +1200762 } else {
763 # use an index per branch to speed up
764 # imports of projects with many branches
765 unless ($index{$branch}) {
766 $index{$branch} = tmpnam();
767 $ENV{GIT_INDEX_FILE} = $index{$branch};
768 if ($ancestor) {
Ben Walton640d9d02010-01-19 14:03:08 -0500769 system("git", "read-tree", "$remote/$ancestor");
Martin Langhoffc5f448b2006-06-28 22:13:23 +1200770 } else {
Ben Walton640d9d02010-01-19 14:03:08 -0500771 system("git", "read-tree", "$remote/$branch");
Martin Langhoffc5f448b2006-06-28 22:13:23 +1200772 }
773 die "read-tree failed: $?\n" if $?;
774 }
775 }
776 $ENV{GIT_INDEX_FILE} = $index{$branch};
777
Jeff Kinge73aefe2006-05-23 03:27:46 -0400778 update_index(@old, @new);
779 @old = @new = ();
780 my $tree = write_tree();
Jeff King9da0dab2007-11-28 13:56:11 -0500781 my $parent = get_headref("$remote/$last_branch");
Jeff Kinge73aefe2006-05-23 03:27:46 -0400782 print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200783
Jeff Kinge73aefe2006-05-23 03:27:46 -0400784 my @commit_args;
785 push @commit_args, ("-p", $parent) if $parent;
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200786
Jeff Kinge73aefe2006-05-23 03:27:46 -0400787 # loose detection of merges
788 # based on the commit msg
789 foreach my $rx (@mergerx) {
790 next unless $logmsg =~ $rx && $1;
791 my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
Jeff King9da0dab2007-11-28 13:56:11 -0500792 if (my $sha1 = get_headref("$remote/$mparent")) {
Marc-Andre Lureauc36c5b82008-03-12 21:54:21 +0200793 push @commit_args, '-p', "$remote/$mparent";
Jeff Kinge73aefe2006-05-23 03:27:46 -0400794 print "Merge parent branch: $mparent\n" if $opt_v;
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200795 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200796 }
Jeff Kinge73aefe2006-05-23 03:27:46 -0400797
798 my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date));
Jeff King62bf0d92006-05-23 16:59:44 -0400799 $ENV{GIT_AUTHOR_NAME} = $author_name;
800 $ENV{GIT_AUTHOR_EMAIL} = $author_email;
801 $ENV{GIT_AUTHOR_DATE} = $commit_date;
802 $ENV{GIT_COMMITTER_NAME} = $author_name;
803 $ENV{GIT_COMMITTER_EMAIL} = $author_email;
804 $ENV{GIT_COMMITTER_DATE} = $commit_date;
Jeff Kinge73aefe2006-05-23 03:27:46 -0400805 my $pid = open2(my $commit_read, my $commit_write,
Ben Walton640d9d02010-01-19 14:03:08 -0500806 'git', 'commit-tree', $tree, @commit_args);
Matthias Urlichse3710462005-06-30 22:09:42 +0200807
808 # compatibility with git2cvs
809 substr($logmsg,32767) = "" if length($logmsg) > 32767;
810 $logmsg =~ s/[\s\n]+\z//;
811
Martin Langhoff5179c8a2006-01-30 19:12:41 +1300812 if (@skipped) {
813 $logmsg .= "\n\n\nSKIPPED:\n\t";
814 $logmsg .= join("\n\t", @skipped) . "\n";
Martin Langhofff396f012006-05-23 00:45:47 +1200815 @skipped = ();
Martin Langhoff5179c8a2006-01-30 19:12:41 +1300816 }
817
Jeff Kinge73aefe2006-05-23 03:27:46 -0400818 print($commit_write "$logmsg\n") && close($commit_write)
Ben Walton640d9d02010-01-19 14:03:08 -0500819 or die "Error writing to git commit-tree: $!\n";
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200820
Jeff Kinge73aefe2006-05-23 03:27:46 -0400821 print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
822 chomp(my $cid = <$commit_read>);
823 is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200824 print "Commit ID $cid\n" if $opt_v;
Jeff Kinge73aefe2006-05-23 03:27:46 -0400825 close($commit_read);
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200826
827 waitpid($pid,0);
Ben Walton640d9d02010-01-19 14:03:08 -0500828 die "Error running git commit-tree: $?\n" if $?;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200829
Ben Walton640d9d02010-01-19 14:03:08 -0500830 system('git' , 'update-ref', "$remote/$branch", $cid) == 0
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200831 or die "Cannot write branch $branch for update: $!\n";
832
Aaron Crane0455ec02010-02-06 18:26:24 +0000833 if ($revision_map) {
834 print $revision_map "@$_ $cid\n" for @commit_revisions;
835 }
836 @commit_revisions = ();
837
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800838 if ($tag) {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800839 my ($xtag) = $tag;
H. Peter Anvin0d821d42005-09-06 10:36:01 -0700840 $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
841 $xtag =~ tr/_/\./ if ( $opt_u );
Joe English34c99da2006-01-06 12:52:27 -0800842 $xtag =~ s/[\/]/$opt_s/g;
Paul Oliver509792b2008-05-23 19:29:22 +0100843 $xtag =~ s/\[//g;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700844
Ben Walton640d9d02010-01-19 14:03:08 -0500845 system('git' , 'tag', '-f', $xtag, $cid) == 0
H. Peter Anvin0d821d42005-09-06 10:36:01 -0700846 or die "Cannot create tag $xtag: $!\n";
H. Peter Anvin0d821d42005-09-06 10:36:01 -0700847
848 print "Created tag '$xtag' on '$branch'\n" if $opt_v;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200849 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200850};
851
Martin Langhoff06918342006-05-22 23:38:08 +1200852my $commitcount = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800853while (<CVS>) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200854 chomp;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800855 if ($state == 0 and /^-+$/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200856 $state = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800857 } elsif ($state == 0) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200858 $state = 1;
859 redo;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800860 } elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200861 $patchset = 0+$_;
862 $state=2;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800863 } elsif ($state == 2 and s/^Date:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200864 $date = pdate($_);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800865 unless ($date) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200866 print STDERR "Could not parse date: $_\n";
867 $state=0;
868 next;
869 }
870 $state=3;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800871 } elsif ($state == 3 and s/^Author:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200872 s/\s+$//;
Junio C Hamano94c23342005-09-30 01:48:57 -0700873 if (/^(.*?)\s+<(.*)>/) {
874 ($author_name, $author_email) = ($1, $2);
Andreas Ericssonffd97f32006-01-13 00:38:59 +0100875 } elsif ($conv_author_name{$_}) {
876 $author_name = $conv_author_name{$_};
877 $author_email = $conv_author_email{$_};
Junio C Hamano94c23342005-09-30 01:48:57 -0700878 } else {
879 $author_name = $author_email = $_;
880 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200881 $state = 4;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800882 } elsif ($state == 4 and s/^Branch:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200883 s/\s+$//;
Gerrit Papea0554222007-11-03 11:55:02 +0000884 tr/_/\./ if ( $opt_u );
Johannes Schindelinfbfd60d2005-08-17 11:19:20 +0200885 s/[\/]/$opt_s/g;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200886 $branch = $_;
887 $state = 5;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800888 } elsif ($state == 5 and s/^Ancestor branch:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200889 s/\s+$//;
890 $ancestor = $_;
Sven Verdoolaege0fa28242005-06-30 17:23:22 +0200891 $ancestor = $opt_o if $ancestor eq "HEAD";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200892 $state = 6;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800893 } elsif ($state == 5) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200894 $ancestor = undef;
895 $state = 6;
896 redo;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800897 } elsif ($state == 6 and s/^Tag:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200898 s/\s+$//;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800899 if ($_ eq "(none)") {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200900 $tag = undef;
901 } else {
902 $tag = $_;
903 }
904 $state = 7;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800905 } elsif ($state == 7 and /^Log:/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200906 $logmsg = "";
907 $state = 8;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800908 } elsif ($state == 8 and /^Members:/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200909 $branch = $opt_o if $branch eq "HEAD";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800910 if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200911 # skip
Matthias Urlichs9da07f32005-07-03 19:03:30 +0200912 print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200913 $state = 11;
914 next;
915 }
Martin Langhoffded9f402007-01-08 19:43:39 +1300916 if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
Martin Langhoff62119882007-01-08 14:11:23 +1300917 # skip if the commit is too recent
Stefan Sperling77190eb2007-12-21 16:57:26 +0100918 # given that the cvsps default fuzz is 300s, we give ourselves another
Martin Langhoff62119882007-01-08 14:11:23 +1300919 # 300s just in case -- this also prevents skipping commits
920 # due to server clock drift
921 print "skip patchset $patchset: $date too recent\n" if $opt_v;
922 $state = 11;
923 next;
924 }
Martin Langhoff71b08142006-06-11 20:12:09 +1200925 if (exists $ignorebranch{$branch}) {
926 print STDERR "Skipping $branch\n";
927 $state = 11;
928 next;
929 }
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800930 if ($ancestor) {
931 if ($ancestor eq $branch) {
Martin Langhoff71b08142006-06-11 20:12:09 +1200932 print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
933 $ancestor = $opt_o;
934 }
Jeff King0750d752007-11-28 13:56:28 -0500935 if (defined get_headref("$remote/$branch")) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200936 print STDERR "Branch $branch already exists!\n";
937 $state=11;
938 next;
939 }
Jeff King0750d752007-11-28 13:56:28 -0500940 my $id = get_headref("$remote/$ancestor");
941 if (!$id) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200942 print STDERR "Branch $ancestor does not exist!\n";
Martin Langhoff71b08142006-06-11 20:12:09 +1200943 $ignorebranch{$branch} = 1;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200944 $state=11;
945 next;
946 }
Jeff King0750d752007-11-28 13:56:28 -0500947
948 system(qw(git update-ref -m cvsimport),
949 "$remote/$branch", $id);
950 if($? != 0) {
951 print STDERR "Could not create branch $branch\n";
Martin Langhoff71b08142006-06-11 20:12:09 +1200952 $ignorebranch{$branch} = 1;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200953 $state=11;
954 next;
955 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200956 }
Sven Verdoolaege46e63ef2005-07-04 15:28:36 +0200957 $last_branch = $branch if $branch ne $last_branch;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200958 $state = 9;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800959 } elsif ($state == 8) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200960 $logmsg .= "$_\n";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800961 } elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200962# VERSION:1.96->1.96.2.1
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200963 my $init = ($2 eq "INITIAL");
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200964 my $fn = $1;
Matthias Urlichsf65ae602005-06-28 19:58:40 +0200965 my $rev = $3;
966 $fn =~ s#^/+##;
Martin Langhoff5179c8a2006-01-30 19:12:41 +1300967 if ($opt_S && $fn =~ m/$opt_S/) {
968 print "SKIPPING $fn v $rev\n";
969 push(@skipped, $fn);
970 next;
971 }
Aaron Crane0455ec02010-02-06 18:26:24 +0000972 push @commit_revisions, [$fn, $rev];
Martin Langhoff5179c8a2006-01-30 19:12:41 +1300973 print "Fetching $fn v $rev\n" if $opt_v;
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200974 my ($tmpname, $size) = $cvs->file($fn,$rev);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800975 if ($size == -1) {
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200976 push(@old,$fn);
977 print "Drop $fn\n" if $opt_v;
978 } else {
979 print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
Junio C Hamanodd274782006-02-20 14:17:28 -0800980 my $pid = open(my $F, '-|');
981 die $! unless defined $pid;
982 if (!$pid) {
Ben Walton640d9d02010-01-19 14:03:08 -0500983 exec("git", "hash-object", "-w", $tmpname)
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200984 or die "Cannot create object: $!\n";
Junio C Hamanodd274782006-02-20 14:17:28 -0800985 }
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200986 my $sha = <$F>;
987 chomp $sha;
988 close $F;
989 my $mode = pmode($cvs->{'mode'});
990 push(@new,[$mode, $sha, $fn]); # may be resurrected!
991 }
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200992 unlink($tmpname);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800993 } elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
Matthias Urlichsf65ae602005-06-28 19:58:40 +0200994 my $fn = $1;
Aaron Crane0455ec02010-02-06 18:26:24 +0000995 my $rev = $2;
Matthias Urlichsf65ae602005-06-28 19:58:40 +0200996 $fn =~ s#^/+##;
Aaron Crane0455ec02010-02-06 18:26:24 +0000997 push @commit_revisions, [$fn, $rev];
Matthias Urlichsf65ae602005-06-28 19:58:40 +0200998 push(@old,$fn);
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200999 print "Delete $fn\n" if $opt_v;
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001000 } elsif ($state == 9 and /^\s*$/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001001 $state = 10;
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001002 } elsif (($state == 9 or $state == 10) and /^-+$/) {
Linus Torvalds4adcea92006-05-22 19:28:37 -07001003 $commitcount++;
1004 if ($opt_L && $commitcount > $opt_L) {
Martin Langhoff06918342006-05-22 23:38:08 +12001005 last;
1006 }
Martin Langhoffc4b16f82006-05-23 00:45:39 +12001007 commit();
Linus Torvalds4adcea92006-05-22 19:28:37 -07001008 if (($commitcount & 1023) == 0) {
Ben Walton91fe7322010-01-19 14:03:10 -05001009 system(qw(git repack -a -d));
Linus Torvalds4adcea92006-05-22 19:28:37 -07001010 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001011 $state = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001012 } elsif ($state == 11 and /^-+$/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001013 $state = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001014 } elsif (/^-+$/) { # end of unknown-line processing
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001015 $state = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001016 } elsif ($state != 11) { # ignore stuff when skipping
Jim Meyering3be39992008-08-05 16:54:42 +02001017 print STDERR "* UNKNOWN LINE * $_\n";
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001018 }
1019}
Martin Langhoffc4b16f82006-05-23 00:45:39 +12001020commit() if $branch and $state != 11;
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001021
Martin Langhoff4083c2f2007-01-08 21:08:46 +13001022unless ($opt_P) {
1023 unlink($cvspsfile);
1024}
1025
Jim Meyeringefe4abd2006-11-15 21:15:44 +01001026# The heuristic of repacking every 1024 commits can leave a
1027# lot of unpacked data. If there is more than 1MB worth of
1028# not-packed objects, repack once more.
Ben Walton640d9d02010-01-19 14:03:08 -05001029my $line = `git count-objects`;
Jim Meyeringefe4abd2006-11-15 21:15:44 +01001030if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
1031 my ($n_objects, $kb) = ($1, $2);
1032 1024 < $kb
Ben Walton91fe7322010-01-19 14:03:10 -05001033 and system(qw(git repack -a -d));
Jim Meyeringefe4abd2006-11-15 21:15:44 +01001034}
1035
Martin Langhoff8f732642006-06-12 23:50:49 +12001036foreach my $git_index (values %index) {
Michael Milligan23fcdc72007-06-05 00:06:30 -06001037 if ($git_index ne "$git_dir/index") {
Martin Langhoffc5f448b2006-06-28 22:13:23 +12001038 unlink($git_index);
1039 }
Martin Langhoff8f732642006-06-12 23:50:49 +12001040}
Sven Verdoolaege79ee4562005-07-04 13:36:59 +02001041
Sven Verdoolaege210569f2005-07-05 13:19:59 +02001042if (defined $orig_git_index) {
1043 $ENV{GIT_INDEX_FILE} = $orig_git_index;
1044} else {
1045 delete $ENV{GIT_INDEX_FILE};
1046}
1047
Matthias Urlichs46541662005-06-28 21:08:15 +02001048# Now switch back to the branch we were in before all of this happened
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001049if ($orig_branch) {
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -08001050 print "DONE.\n" if $opt_v;
1051 if ($opt_i) {
1052 exit 0;
1053 }
Ben Walton640d9d02010-01-19 14:03:08 -05001054 my $tip_at_end = `git rev-parse --verify HEAD`;
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -08001055 if ($tip_at_start ne $tip_at_end) {
Junio C Hamanocb9594e2006-03-18 02:05:02 -08001056 for ($tip_at_start, $tip_at_end) { chomp; }
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -08001057 print "Fetched into the current branch.\n" if $opt_v;
Ben Walton640d9d02010-01-19 14:03:08 -05001058 system(qw(git read-tree -u -m),
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -08001059 $tip_at_start, $tip_at_end);
1060 die "Fast-forward update failed: $?\n" if $?;
1061 }
1062 else {
Ben Walton640d9d02010-01-19 14:03:08 -05001063 system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -08001064 die "Could not merge $opt_o into the current branch.\n" if $?;
1065 }
Matthias Urlichs46541662005-06-28 21:08:15 +02001066} else {
1067 $orig_branch = "master";
1068 print "DONE; creating $orig_branch branch\n" if $opt_v;
Ben Walton640d9d02010-01-19 14:03:08 -05001069 system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
Jeff King0750d752007-11-28 13:56:28 -05001070 unless defined get_headref('refs/heads/master');
Ben Walton640d9d02010-01-19 14:03:08 -05001071 system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
Andy Whitcroft06baffd2007-06-04 10:01:49 +01001072 if ($opt_r && $opt_o ne 'HEAD');
Ben Walton640d9d02010-01-19 14:03:08 -05001073 system('git', 'update-ref', 'HEAD', "$orig_branch");
Sven Verdoolaegec1c774e2005-07-11 16:57:49 +02001074 unless ($opt_i) {
Ben Walton91fe7322010-01-19 14:03:10 -05001075 system(qw(git checkout -f));
Sven Verdoolaegec1c774e2005-07-11 16:57:49 +02001076 die "checkout failed: $?\n" if $?;
1077 }
Matthias Urlichs46541662005-06-28 21:08:15 +02001078}