]> git.ipfire.org Git - thirdparty/git.git/blame - git-cvsimport.perl
perl/Git.pm: fix get_tz_offset to properly handle DST boundary cases
[thirdparty/git.git] / git-cvsimport.perl
CommitLineData
3328aced 1#!/usr/bin/perl
9718a00b 2
a57a9493
MU
3# 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
d48b2841 16use 5.008;
a57a9493
MU
17use strict;
18use warnings;
bc434e82 19use Getopt::Long;
79ee456c 20use File::Spec;
7ccd9009 21use File::Temp qw(tempfile tmpnam);
a57a9493
MU
22use File::Path qw(mkpath);
23use File::Basename qw(basename dirname);
24use Time::Local;
2a3e1a85
MU
25use IO::Socket;
26use IO::Pipe;
c2b3af05 27use POSIX qw(strftime tzset dup2 ENOENT);
0d821d4d 28use IPC::Open2;
a57a9493
MU
29
30$SIG{'PIPE'}="IGNORE";
c2b3af05 31set_timezone('UTC');
a57a9493 32
0455ec03 33our ($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);
fb2c9841 34my (%conv_author_name, %conv_author_email, %conv_author_tz);
a57a9493 35
7bf77644
FL
36sub usage(;$) {
37 my $msg = shift;
38 print(STDERR "Error: $msg\n") if $msg;
a57a9493 39 print STDERR <<END;
1b1dd23f 40Usage: git cvsimport # fetch/update GIT from CVS
ffd97f3a 41 [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
edbe4466
FL
42 [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
43 [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
0455ec03 44 [-r remote] [-R] [CVS_module]
a57a9493
MU
45END
46 exit(1);
47}
48
ffd97f3a
AE
49sub read_author_info($) {
50 my ($file) = @_;
51 my $user;
52 open my $f, '<', "$file" or die("Failed to open $file: $!\n");
53
54 while (<$f>) {
8cd16211 55 # Expected format is this:
ffd97f3a 56 # exon=Andreas Ericsson <ae@op5.se>
8cd16211 57 if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
ffd97f3a 58 $user = $1;
8cd16211
JH
59 $conv_author_name{$user} = $2;
60 $conv_author_email{$user} = $3;
ffd97f3a 61 }
fb2c9841
CR
62 # or with an optional timezone:
63 # spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago
64 elsif (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*(\S+?)\s*$/) {
65 $user = $1;
66 $conv_author_name{$user} = $2;
67 $conv_author_email{$user} = $3;
68 $conv_author_tz{$user} = $4;
69 }
8cd16211
JH
70 # However, we also read from CVSROOT/users format
71 # to ease migration.
72 elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
73 my $mapped;
74 ($user, $mapped) = ($1, $3);
75 if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
76 $conv_author_name{$user} = $1;
77 $conv_author_email{$user} = $2;
78 }
79 elsif ($mapped =~ /^<?(.*)>?$/) {
80 $conv_author_name{$user} = $user;
81 $conv_author_email{$user} = $1;
82 }
83 }
84 # NEEDSWORK: Maybe warn on unrecognized lines?
ffd97f3a
AE
85 }
86 close ($f);
87}
88
89sub write_author_info($) {
90 my ($file) = @_;
91 open my $f, '>', $file or
92 die("Failed to open $file for writing: $!");
93
94 foreach (keys %conv_author_name) {
fb2c9841
CR
95 print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>";
96 print $f " $conv_author_tz{$_}" if ($conv_author_tz{$_});
97 print $f "\n";
ffd97f3a
AE
98 }
99 close ($f);
100}
101
c2b3af05
JK
102# Versions of perl before 5.10.0 may not automatically check $TZ each
103# time localtime is run (most platforms will do so only the first time).
104# We can work around this by using tzset() to update the internal
105# variable whenever we change the environment.
106sub set_timezone {
107 $ENV{TZ} = shift;
108 tzset();
109}
110
cfc44a12 111# convert getopts specs for use by git config
60d5985d
MG
112my %longmap = (
113 'A:' => 'authors-file',
114 'M:' => 'merge-regex',
115 'P:' => undef,
116 'R' => 'track-revisions',
117 'S:' => 'ignore-paths',
118);
119
ed35dece 120sub read_repo_config {
549ad6d2
MG
121 # Split the string between characters, unless there is a ':'
122 # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
ed35dece
JB
123 my @opts = split(/ *(?!:)/, shift);
124 foreach my $o (@opts) {
125 my $key = $o;
126 $key =~ s/://g;
cfc44a12 127 my $arg = 'git config';
ed35dece 128 $arg .= ' --bool' if ($o !~ /:$/);
60d5985d 129 my $ckey = $key;
ed35dece 130
60d5985d
MG
131 if (exists $longmap{$o}) {
132 # An uppercase option like -R cannot be
133 # expressed in the configuration, as the
134 # variable names are downcased.
135 $ckey = $longmap{$o};
136 next if (! defined $ckey);
137 $ckey =~ s/-//g;
138 }
139 chomp(my $tmp = `$arg --get cvsimport.$ckey`);
ed35dece 140 if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
549ad6d2
MG
141 no strict 'refs';
142 my $opt_name = "opt_" . $key;
143 if (!$$opt_name) {
144 $$opt_name = $tmp;
145 }
ed35dece
JB
146 }
147 }
ed35dece
JB
148}
149
0455ec03 150my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
ed35dece 151read_repo_config($opts);
bc434e82
PB
152Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
153
154# turn the Getopt::Std specification in a Getopt::Long one,
155# with support for multiple -M options
156GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
157 or usage();
a57a9493
MU
158usage if $opt_h;
159
67d23242 160if (@ARGV == 0) {
cfc44a12 161 chomp(my $module = `git config --get cvsimport.module`);
67d23242
JK
162 push(@ARGV, $module) if $? == 0;
163}
7bf77644 164@ARGV <= 1 or usage("You can't specify more than one CVS module");
a57a9493 165
86d11cf2 166if ($opt_d) {
2a3e1a85 167 $ENV{"CVSROOT"} = $opt_d;
86d11cf2 168} elsif (-f 'CVS/Root') {
f9714a4a
SV
169 open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
170 $opt_d = <$f>;
171 chomp $opt_d;
172 close $f;
173 $ENV{"CVSROOT"} = $opt_d;
86d11cf2 174} elsif ($ENV{"CVSROOT"}) {
2a3e1a85
MU
175 $opt_d = $ENV{"CVSROOT"};
176} else {
7bf77644 177 usage("CVSROOT needs to be set");
2a3e1a85 178}
fbfd60d6 179$opt_s ||= "-";
ded9f400
ML
180$opt_a ||= 0;
181
f9714a4a 182my $git_tree = $opt_C;
2a3e1a85
MU
183$git_tree ||= ".";
184
8b7f5fc1
AW
185my $remote;
186if (defined $opt_r) {
187 $remote = 'refs/remotes/' . $opt_r;
188 $opt_o ||= "master";
189} else {
190 $opt_o ||= "origin";
191 $remote = 'refs/heads';
192}
193
f9714a4a
SV
194my $cvs_tree;
195if ($#ARGV == 0) {
196 $cvs_tree = $ARGV[0];
197} elsif (-f 'CVS/Repository') {
a6080a0a 198 open my $f, '<', 'CVS/Repository' or
f9714a4a
SV
199 die 'Failed to open CVS/Repository';
200 $cvs_tree = <$f>;
201 chomp $cvs_tree;
db4b6582 202 close $f;
f9714a4a 203} else {
7bf77644 204 usage("CVS module has to be specified");
f9714a4a
SV
205}
206
db4b6582
ML
207our @mergerx = ();
208if ($opt_m) {
fbbbc362 209 @mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
db4b6582 210}
bc434e82
PB
211if (@opt_M) {
212 push (@mergerx, map { qr/$_/ } @opt_M);
db4b6582
ML
213}
214
6211988f
ML
215# Remember UTC of our starting time
216# we'll want to avoid importing commits
217# that are too recent
218our $starttime = time();
219
a57a9493
MU
220select(STDERR); $|=1; select(STDOUT);
221
222
223package CVSconn;
224# Basic CVS dialog.
2a3e1a85 225# We're only interested in connecting and downloading, so ...
a57a9493 226
2eb6d82e
SV
227use File::Spec;
228use File::Temp qw(tempfile);
f65ae603
MU
229use POSIX qw(strftime dup2);
230
a57a9493 231sub new {
86d11cf2 232 my ($what,$repo,$subdir) = @_;
a57a9493
MU
233 $what=ref($what) if ref($what);
234
235 my $self = {};
236 $self->{'buffer'} = "";
237 bless($self,$what);
238
239 $repo =~ s#/+$##;
240 $self->{'fullrep'} = $repo;
241 $self->conn();
242
243 $self->{'subdir'} = $subdir;
244 $self->{'lines'} = undef;
245
246 return $self;
247}
248
58fdef0c
GR
249sub find_password_entry {
250 my ($cvspass, @cvsroot) = @_;
251 my ($file, $delim) = @$cvspass;
252 my $pass;
253 local ($_);
254
255 if (open(my $fh, $file)) {
256 # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
257 CVSPASSFILE:
258 while (<$fh>) {
259 chomp;
260 s/^\/\d+\s+//;
261 my ($w, $p) = split($delim,$_,2);
262 for my $cvsroot (@cvsroot) {
263 if ($w eq $cvsroot) {
264 $pass = $p;
265 last CVSPASSFILE;
266 }
267 }
268 }
269 close($fh);
270 }
271 return $pass;
272}
273
a57a9493
MU
274sub conn {
275 my $self = shift;
276 my $repo = $self->{'fullrep'};
86d11cf2
JH
277 if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
278 my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
73bcf533 279
86d11cf2
JH
280 my ($proxyhost,$proxyport);
281 if ($param && ($param =~ m/proxy=([^;]+)/)) {
73bcf533
IA
282 $proxyhost = $1;
283 # Default proxyport, if not specified, is 8080.
284 $proxyport = 8080;
86d11cf2 285 if ($ENV{"CVS_PROXY_PORT"}) {
73bcf533
IA
286 $proxyport = $ENV{"CVS_PROXY_PORT"};
287 }
86d11cf2 288 if ($param =~ m/proxyport=([^;]+)/) {
73bcf533
IA
289 $proxyport = $1;
290 }
291 }
8c372fb0 292 $repo ||= '/';
73bcf533 293
2e458e05
GH
294 # if username is not explicit in CVSROOT, then use current user, as cvs would
295 $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
2a3e1a85 296 my $rr2 = "-";
86d11cf2 297 unless ($port) {
a57a9493
MU
298 $rr2 = ":pserver:$user\@$serv:$repo";
299 $port=2401;
300 }
301 my $rr = ":pserver:$user\@$serv:$port$repo";
302
3fb9d582
PO
303 if ($pass) {
304 $pass = $self->_scramble($pass);
305 } else {
58fdef0c
GR
306 my @cvspass = ([$ENV{'HOME'}."/.cvspass", qr/\s/],
307 [$ENV{'HOME'}."/.cvs/cvspass", qr/=/]);
308 my @loc = ();
309 foreach my $cvspass (@cvspass) {
310 my $p = find_password_entry($cvspass, $rr, $rr2);
311 if ($p) {
312 push @loc, $cvspass->[0];
313 $pass = $p;
a57a9493 314 }
58fdef0c
GR
315 }
316
317 if (1 < @loc) {
318 die("Multiple cvs password files have ".
319 "entries for CVSROOT $opt_d: @loc");
320 } elsif (!$pass) {
321 $pass = "A";
322 }
a57a9493 323 }
b2139dbd 324
73bcf533 325 my ($s, $rep);
86d11cf2 326 if ($proxyhost) {
73bcf533
IA
327
328 # Use a HTTP Proxy. Only works for HTTP proxies that
329 # don't require user authentication
330 #
331 # See: http://www.ietf.org/rfc/rfc2817.txt
332
333 $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
334 die "Socket to $proxyhost: $!\n" unless defined $s;
335 $s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
336 or die "Write to $proxyhost: $!\n";
337 $s->flush();
338
339 $rep = <$s>;
340
341 # The answer should look like 'HTTP/1.x 2yy ....'
86d11cf2 342 if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
73bcf533
IA
343 die "Proxy connect: $rep\n";
344 }
345 # Skip up to the empty line of the proxy server output
346 # including the response headers.
347 while ($rep = <$s>) {
348 last if (!defined $rep ||
349 $rep eq "\n" ||
350 $rep eq "\r\n");
351 }
352 } else {
353 $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
354 die "Socket to $serv: $!\n" unless defined $s;
355 }
356
a57a9493
MU
357 $s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
358 or die "Write to $serv: $!\n";
359 $s->flush();
360
73bcf533 361 $rep = <$s>;
a57a9493 362
86d11cf2 363 if ($rep ne "I LOVE YOU\n") {
a57a9493
MU
364 $rep="<unknown>" unless $rep;
365 die "AuthReply: $rep\n";
366 }
367 $self->{'socketo'} = $s;
368 $self->{'socketi'} = $s;
34155390 369 } else { # local or ext: Fork off our own cvs server.
a57a9493
MU
370 my $pr = IO::Pipe->new();
371 my $pw = IO::Pipe->new();
372 my $pid = fork();
373 die "Fork: $!\n" unless defined $pid;
8d0ea311
SV
374 my $cvs = 'cvs';
375 $cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
34155390
SV
376 my $rsh = 'rsh';
377 $rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
378
379 my @cvs = ($cvs, 'server');
380 my ($local, $user, $host);
381 $local = $repo =~ s/:local://;
382 if (!$local) {
383 $repo =~ s/:ext://;
384 $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
385 ($user, $host) = ($1, $2);
386 }
387 if (!$local) {
388 if ($user) {
389 unshift @cvs, $rsh, '-l', $user, $host;
390 } else {
391 unshift @cvs, $rsh, $host;
392 }
393 }
394
86d11cf2 395 unless ($pid) {
a57a9493
MU
396 $pr->writer();
397 $pw->reader();
a57a9493
MU
398 dup2($pw->fileno(),0);
399 dup2($pr->fileno(),1);
400 $pr->close();
401 $pw->close();
34155390 402 exec(@cvs);
a57a9493
MU
403 }
404 $pw->writer();
405 $pr->reader();
406 $self->{'socketo'} = $pw;
407 $self->{'socketi'} = $pr;
408 }
409 $self->{'socketo'}->write("Root $repo\n");
410
411 # Trial and error says that this probably is the minimum set
b0921331 412 $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
a57a9493
MU
413
414 $self->{'socketo'}->write("valid-requests\n");
415 $self->{'socketo'}->flush();
416
e7cad3cc
FK
417 my $rep=$self->readline();
418 die "Failed to read from server" unless defined $rep;
419 chomp($rep);
86d11cf2 420 if ($rep !~ s/^Valid-requests\s*//) {
a57a9493
MU
421 $rep="<unknown>" unless $rep;
422 die "Expected Valid-requests from server, but got: $rep\n";
423 }
424 chomp(my $res=$self->readline());
425 die "validReply: $res\n" if $res ne "ok";
426
427 $self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
428 $self->{'repo'} = $repo;
429}
430
431sub readline {
86d11cf2 432 my ($self) = @_;
a57a9493
MU
433 return $self->{'socketi'}->getline();
434}
435
436sub _file {
437 # Request a file with a given revision.
438 # Trial and error says this is a good way to do it. :-/
86d11cf2 439 my ($self,$fn,$rev) = @_;
a57a9493
MU
440 $self->{'socketo'}->write("Argument -N\n") or return undef;
441 $self->{'socketo'}->write("Argument -P\n") or return undef;
abe05822
ML
442 # -kk: Linus' version doesn't use it - defaults to off
443 if ($opt_k) {
444 $self->{'socketo'}->write("Argument -kk\n") or return undef;
445 }
a57a9493
MU
446 $self->{'socketo'}->write("Argument -r\n") or return undef;
447 $self->{'socketo'}->write("Argument $rev\n") or return undef;
448 $self->{'socketo'}->write("Argument --\n") or return undef;
449 $self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
450 $self->{'socketo'}->write("Directory .\n") or return undef;
451 $self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
4f7c0caa 452 # $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
a57a9493
MU
453 $self->{'socketo'}->write("co\n") or return undef;
454 $self->{'socketo'}->flush() or return undef;
455 $self->{'lines'} = 0;
456 return 1;
457}
458sub _line {
459 # Read a line from the server.
460 # ... except that 'line' may be an entire file. ;-)
86d11cf2 461 my ($self, $fh) = @_;
a57a9493
MU
462 die "Not in lines" unless defined $self->{'lines'};
463
464 my $line;
2eb6d82e 465 my $res=0;
86d11cf2 466 while (defined($line = $self->readline())) {
a57a9493
MU
467 # M U gnupg-cvs-rep/AUTHORS
468 # Updated gnupg-cvs-rep/
469 # /daten/src/rsync/gnupg-cvs-rep/AUTHORS
470 # /AUTHORS/1.1///T1.1
471 # u=rw,g=rw,o=rw
472 # 0
473 # ok
474
86d11cf2 475 if ($line =~ s/^(?:Created|Updated) //) {
a57a9493
MU
476 $line = $self->readline(); # path
477 $line = $self->readline(); # Entries line
478 my $mode = $self->readline(); chomp $mode;
479 $self->{'mode'} = $mode;
480 defined (my $cnt = $self->readline())
481 or die "EOF from server after 'Changed'\n";
482 chomp $cnt;
483 die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
484 $line="";
55cad842 485 $res = $self->_fetchfile($fh, $cnt);
86d11cf2 486 } elsif ($line =~ s/^ //) {
2eb6d82e
SV
487 print $fh $line;
488 $res += length($line);
86d11cf2 489 } elsif ($line =~ /^M\b/) {
a57a9493 490 # output, do nothing
86d11cf2 491 } elsif ($line =~ /^Mbinary\b/) {
a57a9493
MU
492 my $cnt;
493 die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
494 chomp $cnt;
495 die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
496 $line="";
55cad842 497 $res += $self->_fetchfile($fh, $cnt);
a57a9493
MU
498 } else {
499 chomp $line;
86d11cf2 500 if ($line eq "ok") {
a57a9493
MU
501 # print STDERR "S: ok (".length($res).")\n";
502 return $res;
86d11cf2 503 } elsif ($line =~ s/^E //) {
a57a9493 504 # print STDERR "S: $line\n";
86d11cf2 505 } elsif ($line =~ /^(Remove-entry|Removed) /i) {
8b8840e0
MU
506 $line = $self->readline(); # filename
507 $line = $self->readline(); # OK
508 chomp $line;
509 die "Unknown: $line" if $line ne "ok";
510 return -1;
a57a9493
MU
511 } else {
512 die "Unknown: $line\n";
513 }
514 }
515 }
39ba7d54 516 return undef;
a57a9493
MU
517}
518sub file {
86d11cf2 519 my ($self,$fn,$rev) = @_;
a57a9493
MU
520 my $res;
521
a6080a0a 522 my ($fh, $name) = tempfile('gitcvs.XXXXXX',
2eb6d82e
SV
523 DIR => File::Spec->tmpdir(), UNLINK => 1);
524
525 $self->_file($fn,$rev) and $res = $self->_line($fh);
526
527 if (!defined $res) {
39ba7d54
MM
528 print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
529 truncate $fh, 0;
2eb6d82e 530 $self->conn();
39ba7d54 531 $self->_file($fn,$rev) or die "No file command send";
2eb6d82e 532 $res = $self->_line($fh);
39ba7d54 533 die "Retry failed" unless defined $res;
a57a9493 534 }
c619ad51 535 close ($fh);
a57a9493 536
2eb6d82e 537 return ($name, $res);
a57a9493 538}
55cad842
ML
539sub _fetchfile {
540 my ($self, $fh, $cnt) = @_;
61efa5e3 541 my $res = 0;
55cad842 542 my $bufsize = 1024 * 1024;
86d11cf2 543 while ($cnt) {
55cad842
ML
544 if ($bufsize > $cnt) {
545 $bufsize = $cnt;
546 }
547 my $buf;
548 my $num = $self->{'socketi'}->read($buf,$bufsize);
549 die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
550 print $fh $buf;
551 $res += $num;
552 $cnt -= $num;
553 }
554 return $res;
555}
a57a9493 556
b2139dbd
DH
557sub _scramble {
558 my ($self, $pass) = @_;
559 my $scrambled = "A";
560
561 return $scrambled unless $pass;
562
563 my $pass_len = length($pass);
564 my @pass_arr = split("", $pass);
565 my $i;
566
567 # from cvs/src/scramble.c
568 my @shifts = (
569 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
570 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
571 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
572 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
573 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
574 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
575 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
576 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
577 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
578 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
579 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
580 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
581 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
582 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
583 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
584 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
585 );
586
587 for ($i = 0; $i < $pass_len; $i++) {
588 $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
589 }
590
591 return $scrambled;
592}
a57a9493
MU
593
594package main;
595
2a3e1a85 596my $cvs = CVSconn->new($opt_d, $cvs_tree);
a57a9493
MU
597
598
599sub pdate($) {
86d11cf2 600 my ($d) = @_;
a57a9493
MU
601 m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
602 or die "Unparseable date: $d\n";
603 my $y=$1; $y-=1900 if $y>1900;
604 return timegm($6||0,$5,$4,$3,$2-1,$y);
9718a00b
TM
605}
606
a57a9493 607sub pmode($) {
86d11cf2 608 my ($mode) = @_;
a57a9493
MU
609 my $m = 0;
610 my $mm = 0;
611 my $um = 0;
612 for my $x(split(//,$mode)) {
86d11cf2 613 if ($x eq ",") {
a57a9493
MU
614 $m |= $mm&$um;
615 $mm = 0;
616 $um = 0;
86d11cf2
JH
617 } elsif ($x eq "u") { $um |= 0700;
618 } elsif ($x eq "g") { $um |= 0070;
619 } elsif ($x eq "o") { $um |= 0007;
620 } elsif ($x eq "r") { $mm |= 0444;
621 } elsif ($x eq "w") { $mm |= 0222;
622 } elsif ($x eq "x") { $mm |= 0111;
623 } elsif ($x eq "=") { # do nothing
a57a9493
MU
624 } else { die "Unknown mode: $mode\n";
625 }
626 }
627 $m |= $mm&$um;
628 return $m;
629}
d4f8b390 630
a57a9493
MU
631sub getwd() {
632 my $pwd = `pwd`;
633 chomp $pwd;
634 return $pwd;
d4f8b390
LT
635}
636
e73aefe4
JK
637sub is_sha1 {
638 my $s = shift;
639 return $s =~ /^[a-f0-9]{40}$/;
640}
db4b6582 641
9da0dabc
JK
642sub get_headref ($) {
643 my $name = shift;
644 my $r = `git rev-parse --verify '$name' 2>/dev/null`;
645 return undef unless $? == 0;
646 chomp $r;
647 return $r;
db4b6582
ML
648}
649
f6fdbb68
JK
650my $user_filename_prepend = '';
651sub munge_user_filename {
652 my $name = shift;
653 return File::Spec->file_name_is_absolute($name) ?
654 $name :
655 $user_filename_prepend . $name;
656}
657
a57a9493
MU
658-d $git_tree
659 or mkdir($git_tree,0777)
660 or die "Could not create $git_tree: $!";
f6fdbb68
JK
661if ($git_tree ne '.') {
662 $user_filename_prepend = getwd() . '/';
663 chdir($git_tree);
664}
d4f8b390 665
a57a9493 666my $last_branch = "";
46541669 667my $orig_branch = "";
a57a9493 668my %branch_date;
8a5f2eac 669my $tip_at_start = undef;
a57a9493
MU
670
671my $git_dir = $ENV{"GIT_DIR"} || ".git";
672$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
673$ENV{"GIT_DIR"} = $git_dir;
79ee456c
SV
674my $orig_git_index;
675$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
8f732649
ML
676
677my %index; # holds filenames of one index per branch
061303f0 678
86d11cf2 679unless (-d $git_dir) {
91fe7324 680 system(qw(git init));
a57a9493 681 die "Cannot init the GIT db at $git_tree: $?\n" if $?;
1bb28d87 682 system(qw(git read-tree --empty));
a57a9493
MU
683 die "Cannot init an empty tree: $?\n" if $?;
684
685 $last_branch = $opt_o;
46541669 686 $orig_branch = "";
a57a9493 687} else {
a12477db 688 open(F, "-|", qw(git symbolic-ref HEAD)) or
640d9d08 689 die "Cannot run git symbolic-ref: $!\n";
8366a10a
PR
690 chomp ($last_branch = <F>);
691 $last_branch = basename($last_branch);
692 close(F);
86d11cf2 693 unless ($last_branch) {
46541669
MU
694 warn "Cannot read the last branch name: $! -- assuming 'master'\n";
695 $last_branch = "master";
696 }
697 $orig_branch = $last_branch;
640d9d08 698 $tip_at_start = `git rev-parse --verify HEAD`;
a57a9493
MU
699
700 # Get the last import timestamps
1f24c587 701 my $fmt = '($ref, $author) = (%(refname), %(author));';
a12477db
BW
702 my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
703 open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
86d11cf2 704 while (defined(my $entry = <H>)) {
1f24c587
AW
705 my ($ref, $author);
706 eval($entry) || die "cannot eval refs list: $@";
8b7f5fc1 707 my ($head) = ($ref =~ m|^$remote/(.*)|);
1f24c587
AW
708 $author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
709 $branch_date{$head} = $1;
a57a9493 710 }
1f24c587 711 close(H);
7ca055f7
SS
712 if (!exists $branch_date{$opt_o}) {
713 die "Branch '$opt_o' does not exist.\n".
714 "Either use the correct '-o branch' option,\n".
715 "or import to a new repository.\n";
716 }
a57a9493
MU
717}
718
719-d $git_dir
720 or die "Could not create git subdir ($git_dir).\n";
721
ffd97f3a
AE
722# now we read (and possibly save) author-info as well
723-f "$git_dir/cvs-authors" and
724 read_author_info("$git_dir/cvs-authors");
725if ($opt_A) {
f6fdbb68 726 read_author_info(munge_user_filename($opt_A));
ffd97f3a
AE
727 write_author_info("$git_dir/cvs-authors");
728}
729
0455ec03
AC
730# open .git/cvs-revisions, if requested
731open my $revision_map, '>>', "$git_dir/cvs-revisions"
732 or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
733 if defined $opt_R;
734
2f57c697
ML
735
736#
737# run cvsps into a file unless we are getting
738# it passed as a file via $opt_P
739#
4083c2fc 740my $cvspsfile;
2f57c697
ML
741unless ($opt_P) {
742 print "Running cvsps...\n" if $opt_v;
743 my $pid = open(CVSPS,"-|");
4083c2fc 744 my $cvspsfh;
2f57c697 745 die "Cannot fork: $!\n" unless defined $pid;
86d11cf2 746 unless ($pid) {
2f57c697
ML
747 my @opt;
748 @opt = split(/,/,$opt_p) if defined $opt_p;
749 unshift @opt, '-z', $opt_z if defined $opt_z;
750 unshift @opt, '-q' unless defined $opt_v;
751 unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
752 push @opt, '--cvs-direct';
753 }
754 exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
755 die "Could not start cvsps: $!\n";
df73e9c6 756 }
4083c2fc
ML
757 ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
758 DIR => File::Spec->tmpdir());
2f57c697
ML
759 while (<CVSPS>) {
760 print $cvspsfh $_;
211dcac6 761 }
2f57c697 762 close CVSPS;
640d9d08 763 $? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
2f57c697 764 close $cvspsfh;
4083c2fc 765} else {
f6fdbb68 766 $cvspsfile = munge_user_filename($opt_P);
a57a9493
MU
767}
768
4083c2fc 769open(CVS, "<$cvspsfile") or die $!;
2f57c697 770
a57a9493
MU
771## cvsps output:
772#---------------------
773#PatchSet 314
774#Date: 1999/09/18 13:03:59
775#Author: wkoch
776#Branch: STABLE-BRANCH-1-0
777#Ancestor branch: HEAD
778#Tag: (none)
779#Log:
780# See ChangeLog: Sat Sep 18 13:03:28 CEST 1999 Werner Koch
781#Members:
782# README:1.57->1.57.2.1
783# VERSION:1.96->1.96.2.1
784#
785#---------------------
786
787my $state = 0;
788
e73aefe4
JK
789sub update_index (\@\@) {
790 my $old = shift;
791 my $new = shift;
640d9d08
BW
792 open(my $fh, '|-', qw(git update-index -z --index-info))
793 or die "unable to open git update-index: $!";
6a1871e1
JK
794 print $fh
795 (map { "0 0000000000000000000000000000000000000000\t$_\0" }
e73aefe4 796 @$old),
6a1871e1 797 (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
e73aefe4 798 @$new)
640d9d08 799 or die "unable to write to git update-index: $!";
6a1871e1 800 close $fh
640d9d08
BW
801 or die "unable to write to git update-index: $!";
802 $? and die "git update-index reported error: $?";
e73aefe4 803}
a57a9493 804
e73aefe4 805sub write_tree () {
a12477db 806 open(my $fh, '-|', qw(git write-tree))
640d9d08 807 or die "unable to open git write-tree: $!";
e73aefe4
JK
808 chomp(my $tree = <$fh>);
809 is_sha1($tree)
810 or die "Cannot get tree id ($tree): $!";
811 close($fh)
640d9d08 812 or die "Error running git write-tree: $?\n";
a57a9493 813 print "Tree ID $tree\n" if $opt_v;
e73aefe4
JK
814 return $tree;
815}
a57a9493 816
fb2c9841 817my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);
0455ec03 818my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
71b08148
ML
819
820# commits that cvsps cannot place anywhere...
821$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
822
e73aefe4 823sub commit {
9da0dabc
JK
824 if ($branch eq $opt_o && !$index{branch} &&
825 !get_headref("$remote/$branch")) {
c5f448b0 826 # looks like an initial commit
640d9d08 827 # use the index primed by git init
23fcdc79
MM
828 $ENV{GIT_INDEX_FILE} = "$git_dir/index";
829 $index{$branch} = "$git_dir/index";
c5f448b0
ML
830 } else {
831 # use an index per branch to speed up
832 # imports of projects with many branches
833 unless ($index{$branch}) {
834 $index{$branch} = tmpnam();
835 $ENV{GIT_INDEX_FILE} = $index{$branch};
836 if ($ancestor) {
640d9d08 837 system("git", "read-tree", "$remote/$ancestor");
c5f448b0 838 } else {
640d9d08 839 system("git", "read-tree", "$remote/$branch");
c5f448b0
ML
840 }
841 die "read-tree failed: $?\n" if $?;
842 }
843 }
844 $ENV{GIT_INDEX_FILE} = $index{$branch};
845
e73aefe4
JK
846 update_index(@old, @new);
847 @old = @new = ();
848 my $tree = write_tree();
9da0dabc 849 my $parent = get_headref("$remote/$last_branch");
e73aefe4
JK
850 print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
851
852 my @commit_args;
853 push @commit_args, ("-p", $parent) if $parent;
854
855 # loose detection of merges
856 # based on the commit msg
857 foreach my $rx (@mergerx) {
858 next unless $logmsg =~ $rx && $1;
859 my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
9da0dabc 860 if (my $sha1 = get_headref("$remote/$mparent")) {
c36c5b84 861 push @commit_args, '-p', "$remote/$mparent";
e73aefe4 862 print "Merge parent branch: $mparent\n" if $opt_v;
db4b6582 863 }
a57a9493 864 }
e73aefe4 865
c2b3af05 866 set_timezone($author_tz);
fb2c9841 867 my $commit_date = strftime("%s %z", localtime($date));
c2b3af05 868 set_timezone('UTC');
62bf0d96
JK
869 $ENV{GIT_AUTHOR_NAME} = $author_name;
870 $ENV{GIT_AUTHOR_EMAIL} = $author_email;
871 $ENV{GIT_AUTHOR_DATE} = $commit_date;
872 $ENV{GIT_COMMITTER_NAME} = $author_name;
873 $ENV{GIT_COMMITTER_EMAIL} = $author_email;
874 $ENV{GIT_COMMITTER_DATE} = $commit_date;
e73aefe4 875 my $pid = open2(my $commit_read, my $commit_write,
640d9d08 876 'git', 'commit-tree', $tree, @commit_args);
e371046b
MU
877
878 # compatibility with git2cvs
879 substr($logmsg,32767) = "" if length($logmsg) > 32767;
880 $logmsg =~ s/[\s\n]+\z//;
881
5179c8a5
ML
882 if (@skipped) {
883 $logmsg .= "\n\n\nSKIPPED:\n\t";
884 $logmsg .= join("\n\t", @skipped) . "\n";
f396f01f 885 @skipped = ();
5179c8a5
ML
886 }
887
e73aefe4 888 print($commit_write "$logmsg\n") && close($commit_write)
640d9d08 889 or die "Error writing to git commit-tree: $!\n";
2a3e1a85 890
e73aefe4
JK
891 print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
892 chomp(my $cid = <$commit_read>);
893 is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
a57a9493 894 print "Commit ID $cid\n" if $opt_v;
e73aefe4 895 close($commit_read);
2a3e1a85
MU
896
897 waitpid($pid,0);
640d9d08 898 die "Error running git commit-tree: $?\n" if $?;
a57a9493 899
640d9d08 900 system('git' , 'update-ref', "$remote/$branch", $cid) == 0
a57a9493
MU
901 or die "Cannot write branch $branch for update: $!\n";
902
0455ec03
AC
903 if ($revision_map) {
904 print $revision_map "@$_ $cid\n" for @commit_revisions;
905 }
906 @commit_revisions = ();
907
86d11cf2 908 if ($tag) {
86d11cf2 909 my ($xtag) = $tag;
0d821d4d
PA
910 $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
911 $xtag =~ tr/_/\./ if ( $opt_u );
34c99da2 912 $xtag =~ s/[\/]/$opt_s/g;
a6080a0a 913
70b67b07
KD
914 # See refs.c for these rules.
915 # Tag cannot contain bad chars. (See bad_ref_char in refs.c.)
916 $xtag =~ s/[ ~\^:\\\*\?\[]//g;
917 # Other bad strings for tags:
918 # (See check_refname_component in refs.c.)
919 1 while $xtag =~ s/
920 (?: \.\. # Tag cannot contain '..'.
921 | \@{ # Tag cannot contain '@{'.
922 | ^ - # Tag cannot begin with '-'.
923 | \.lock $ # Tag cannot end with '.lock'.
924 | ^ \. # Tag cannot begin...
925 | \. $ # ...or end with '.'
926 )//xg;
927 # Tag cannot be empty.
928 if ($xtag eq '') {
929 warn("warning: ignoring tag '$tag'",
930 " with invalid tagname\n");
931 return;
932 }
933
934 if (system('git' , 'tag', '-f', $xtag, $cid) != 0) {
935 # We did our best to sanitize the tag, but still failed
936 # for whatever reason. Bail out, and give the user
937 # enough information to understand if/how we should
938 # improve the translation in the future.
939 if ($tag ne $xtag) {
940 print "Translated '$tag' tag to '$xtag'\n";
941 }
942 die "Cannot create tag $xtag: $!\n";
943 }
0d821d4d
PA
944
945 print "Created tag '$xtag' on '$branch'\n" if $opt_v;
a57a9493 946 }
a57a9493
MU
947};
948
06918348 949my $commitcount = 1;
86d11cf2 950while (<CVS>) {
a57a9493 951 chomp;
86d11cf2 952 if ($state == 0 and /^-+$/) {
a57a9493 953 $state = 1;
86d11cf2 954 } elsif ($state == 0) {
a57a9493
MU
955 $state = 1;
956 redo;
86d11cf2 957 } elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
a57a9493
MU
958 $patchset = 0+$_;
959 $state=2;
86d11cf2 960 } elsif ($state == 2 and s/^Date:\s+//) {
a57a9493 961 $date = pdate($_);
86d11cf2 962 unless ($date) {
a57a9493
MU
963 print STDERR "Could not parse date: $_\n";
964 $state=0;
965 next;
966 }
967 $state=3;
86d11cf2 968 } elsif ($state == 3 and s/^Author:\s+//) {
fb2c9841 969 $author_tz = "UTC";
a57a9493 970 s/\s+$//;
94c23343
JH
971 if (/^(.*?)\s+<(.*)>/) {
972 ($author_name, $author_email) = ($1, $2);
ffd97f3a
AE
973 } elsif ($conv_author_name{$_}) {
974 $author_name = $conv_author_name{$_};
975 $author_email = $conv_author_email{$_};
fb2c9841 976 $author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});
94c23343
JH
977 } else {
978 $author_name = $author_email = $_;
979 }
a57a9493 980 $state = 4;
86d11cf2 981 } elsif ($state == 4 and s/^Branch:\s+//) {
a57a9493 982 s/\s+$//;
a0554224 983 tr/_/\./ if ( $opt_u );
fbfd60d6 984 s/[\/]/$opt_s/g;
a57a9493
MU
985 $branch = $_;
986 $state = 5;
86d11cf2 987 } elsif ($state == 5 and s/^Ancestor branch:\s+//) {
a57a9493
MU
988 s/\s+$//;
989 $ancestor = $_;
0fa2824f 990 $ancestor = $opt_o if $ancestor eq "HEAD";
a57a9493 991 $state = 6;
86d11cf2 992 } elsif ($state == 5) {
a57a9493
MU
993 $ancestor = undef;
994 $state = 6;
995 redo;
86d11cf2 996 } elsif ($state == 6 and s/^Tag:\s+//) {
a57a9493 997 s/\s+$//;
86d11cf2 998 if ($_ eq "(none)") {
a57a9493
MU
999 $tag = undef;
1000 } else {
1001 $tag = $_;
1002 }
1003 $state = 7;
86d11cf2 1004 } elsif ($state == 7 and /^Log:/) {
a57a9493
MU
1005 $logmsg = "";
1006 $state = 8;
86d11cf2 1007 } elsif ($state == 8 and /^Members:/) {
a57a9493 1008 $branch = $opt_o if $branch eq "HEAD";
86d11cf2 1009 if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
a57a9493 1010 # skip
9da07f34 1011 print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
a57a9493
MU
1012 $state = 11;
1013 next;
1014 }
ded9f400 1015 if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
6211988f 1016 # skip if the commit is too recent
77190eb9 1017 # given that the cvsps default fuzz is 300s, we give ourselves another
6211988f
ML
1018 # 300s just in case -- this also prevents skipping commits
1019 # due to server clock drift
1020 print "skip patchset $patchset: $date too recent\n" if $opt_v;
1021 $state = 11;
1022 next;
1023 }
71b08148
ML
1024 if (exists $ignorebranch{$branch}) {
1025 print STDERR "Skipping $branch\n";
1026 $state = 11;
1027 next;
1028 }
86d11cf2
JH
1029 if ($ancestor) {
1030 if ($ancestor eq $branch) {
71b08148
ML
1031 print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
1032 $ancestor = $opt_o;
1033 }
0750d751 1034 if (defined get_headref("$remote/$branch")) {
a57a9493
MU
1035 print STDERR "Branch $branch already exists!\n";
1036 $state=11;
1037 next;
1038 }
0750d751
JK
1039 my $id = get_headref("$remote/$ancestor");
1040 if (!$id) {
a57a9493 1041 print STDERR "Branch $ancestor does not exist!\n";
71b08148 1042 $ignorebranch{$branch} = 1;
a57a9493
MU
1043 $state=11;
1044 next;
1045 }
0750d751
JK
1046
1047 system(qw(git update-ref -m cvsimport),
1048 "$remote/$branch", $id);
1049 if($? != 0) {
1050 print STDERR "Could not create branch $branch\n";
71b08148 1051 $ignorebranch{$branch} = 1;
a57a9493
MU
1052 $state=11;
1053 next;
1054 }
a57a9493 1055 }
46e63efc 1056 $last_branch = $branch if $branch ne $last_branch;
a57a9493 1057 $state = 9;
86d11cf2 1058 } elsif ($state == 8) {
a57a9493 1059 $logmsg .= "$_\n";
86d11cf2 1060 } elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
a57a9493 1061# VERSION:1.96->1.96.2.1
2a3e1a85 1062 my $init = ($2 eq "INITIAL");
a57a9493 1063 my $fn = $1;
f65ae603
MU
1064 my $rev = $3;
1065 $fn =~ s#^/+##;
5179c8a5
ML
1066 if ($opt_S && $fn =~ m/$opt_S/) {
1067 print "SKIPPING $fn v $rev\n";
1068 push(@skipped, $fn);
1069 next;
1070 }
0455ec03 1071 push @commit_revisions, [$fn, $rev];
5179c8a5 1072 print "Fetching $fn v $rev\n" if $opt_v;
2eb6d82e 1073 my ($tmpname, $size) = $cvs->file($fn,$rev);
86d11cf2 1074 if ($size == -1) {
8b8840e0
MU
1075 push(@old,$fn);
1076 print "Drop $fn\n" if $opt_v;
1077 } else {
1078 print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
dd27478f
JH
1079 my $pid = open(my $F, '-|');
1080 die $! unless defined $pid;
1081 if (!$pid) {
640d9d08 1082 exec("git", "hash-object", "-w", $tmpname)
8b8840e0 1083 or die "Cannot create object: $!\n";
dd27478f 1084 }
8b8840e0
MU
1085 my $sha = <$F>;
1086 chomp $sha;
1087 close $F;
1088 my $mode = pmode($cvs->{'mode'});
1089 push(@new,[$mode, $sha, $fn]); # may be resurrected!
1090 }
2eb6d82e 1091 unlink($tmpname);
86d11cf2 1092 } elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
f65ae603 1093 my $fn = $1;
0455ec03 1094 my $rev = $2;
f65ae603 1095 $fn =~ s#^/+##;
0455ec03 1096 push @commit_revisions, [$fn, $rev];
f65ae603 1097 push(@old,$fn);
8b8840e0 1098 print "Delete $fn\n" if $opt_v;
86d11cf2 1099 } elsif ($state == 9 and /^\s*$/) {
a57a9493 1100 $state = 10;
86d11cf2 1101 } elsif (($state == 9 or $state == 10) and /^-+$/) {
4adcea99
LT
1102 $commitcount++;
1103 if ($opt_L && $commitcount > $opt_L) {
06918348
ML
1104 last;
1105 }
c4b16f8d 1106 commit();
4adcea99 1107 if (($commitcount & 1023) == 0) {
91fe7324 1108 system(qw(git repack -a -d));
4adcea99 1109 }
a57a9493 1110 $state = 1;
86d11cf2 1111 } elsif ($state == 11 and /^-+$/) {
a57a9493 1112 $state = 1;
86d11cf2 1113 } elsif (/^-+$/) { # end of unknown-line processing
a57a9493 1114 $state = 1;
86d11cf2 1115 } elsif ($state != 11) { # ignore stuff when skipping
3be39998 1116 print STDERR "* UNKNOWN LINE * $_\n";
a57a9493
MU
1117 }
1118}
c4b16f8d 1119commit() if $branch and $state != 11;
d4f8b390 1120
4083c2fc
ML
1121unless ($opt_P) {
1122 unlink($cvspsfile);
1123}
1124
efe4abd1
JM
1125# The heuristic of repacking every 1024 commits can leave a
1126# lot of unpacked data. If there is more than 1MB worth of
1127# not-packed objects, repack once more.
640d9d08 1128my $line = `git count-objects`;
efe4abd1
JM
1129if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
1130 my ($n_objects, $kb) = ($1, $2);
1131 1024 < $kb
91fe7324 1132 and system(qw(git repack -a -d));
efe4abd1
JM
1133}
1134
8f732649 1135foreach my $git_index (values %index) {
23fcdc79 1136 if ($git_index ne "$git_dir/index") {
c5f448b0
ML
1137 unlink($git_index);
1138 }
8f732649 1139}
79ee456c 1140
210569f9
SV
1141if (defined $orig_git_index) {
1142 $ENV{GIT_INDEX_FILE} = $orig_git_index;
1143} else {
1144 delete $ENV{GIT_INDEX_FILE};
1145}
1146
46541669 1147# Now switch back to the branch we were in before all of this happened
86d11cf2 1148if ($orig_branch) {
8a5f2eac
JH
1149 print "DONE.\n" if $opt_v;
1150 if ($opt_i) {
1151 exit 0;
1152 }
640d9d08 1153 my $tip_at_end = `git rev-parse --verify HEAD`;
8a5f2eac 1154 if ($tip_at_start ne $tip_at_end) {
cb9594e2 1155 for ($tip_at_start, $tip_at_end) { chomp; }
8a5f2eac 1156 print "Fetched into the current branch.\n" if $opt_v;
640d9d08 1157 system(qw(git read-tree -u -m),
8a5f2eac
JH
1158 $tip_at_start, $tip_at_end);
1159 die "Fast-forward update failed: $?\n" if $?;
1160 }
1161 else {
640d9d08 1162 system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
8a5f2eac
JH
1163 die "Could not merge $opt_o into the current branch.\n" if $?;
1164 }
46541669
MU
1165} else {
1166 $orig_branch = "master";
1167 print "DONE; creating $orig_branch branch\n" if $opt_v;
640d9d08 1168 system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
0750d751 1169 unless defined get_headref('refs/heads/master');
640d9d08 1170 system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
06baffd3 1171 if ($opt_r && $opt_o ne 'HEAD');
640d9d08 1172 system('git', 'update-ref', 'HEAD', "$orig_branch");
c1c774e7 1173 unless ($opt_i) {
91fe7324 1174 system(qw(git checkout -f));
c1c774e7
SV
1175 die "checkout failed: $?\n" if $?;
1176 }
46541669 1177}