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