2 use lib (split(/:/, $ENV{GITPERLLIB} || "/home/eos/Eos/util/X86LINUX64/share/perl5"));
4 # This tool is copyright (c) 2005, Matthias Urlichs.
5 # It is released under the Gnu Public License, version 2.
7 # The basic idea is to aggregate CVS check-ins into related changes.
8 # Fortunately, "cvsps" does that for us; all we have to do is to parse
11 # Checking out the files is done by a single long-running CVS connection
14 # The head revision is on branch "origin" by default.
15 # You can change that with the '-o' option.
22 use File::Temp qw(tempfile tmpnam);
23 use File::Path qw(mkpath);
24 use File::Basename qw(basename dirname);
28 use POSIX qw(strftime tzset dup2 ENOENT);
30 use Git qw(get_tz_offset);
32 $SIG{'PIPE'}="IGNORE";
35 our ($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);
36 my (%conv_author_name, %conv_author_email, %conv_author_tz);
40 print(STDERR "Error: $msg\n") if $msg;
42 usage: git cvsimport # fetch/update GIT from CVS
43 [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
44 [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
45 [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
46 [-r remote] [-R] [CVS_module]
51 sub read_author_info($) {
54 open my $f, '<', "$file" or die("Failed to open $file: $!\n");
57 # Expected format is this:
58 # exon=Andreas Ericsson <ae@op5.se>
59 if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
61 $conv_author_name{$user} = $2;
62 $conv_author_email{$user} = $3;
64 # or with an optional timezone:
65 # spawn=Simon Pawn <spawn@frog-pond.org> America/Chicago
66 elsif (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*(\S+?)\s*$/) {
68 $conv_author_name{$user} = $2;
69 $conv_author_email{$user} = $3;
70 $conv_author_tz{$user} = $4;
72 # However, we also read from CVSROOT/users format
74 elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
76 ($user, $mapped) = ($1, $3);
77 if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
78 $conv_author_name{$user} = $1;
79 $conv_author_email{$user} = $2;
81 elsif ($mapped =~ /^<?(.*)>?$/) {
82 $conv_author_name{$user} = $user;
83 $conv_author_email{$user} = $1;
86 # NEEDSWORK: Maybe warn on unrecognized lines?
91 sub write_author_info($) {
93 open my $f, '>', $file or
94 die("Failed to open $file for writing: $!");
96 foreach (keys %conv_author_name) {
97 print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>";
98 print $f " $conv_author_tz{$_}" if ($conv_author_tz{$_});
104 # Versions of perl before 5.10.0 may not automatically check $TZ each
105 # time localtime is run (most platforms will do so only the first time).
106 # We can work around this by using tzset() to update the internal
107 # variable whenever we change the environment.
113 # convert getopts specs for use by git config
115 'A:' => 'authors-file',
116 'M:' => 'merge-regex',
118 'R' => 'track-revisions',
119 'S:' => 'ignore-paths',
122 sub read_repo_config {
123 # Split the string between characters, unless there is a ':'
124 # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
125 my @opts = split(/ *(?!:)/, shift);
126 foreach my $o (@opts) {
129 my $arg = 'git config';
130 $arg .= ' --bool' if ($o !~ /:$/);
133 if (exists $longmap{$o}) {
134 # An uppercase option like -R cannot be
135 # expressed in the configuration, as the
136 # variable names are downcased.
137 $ckey = $longmap{$o};
138 next if (! defined $ckey);
141 chomp(my $tmp = `$arg --get cvsimport.$ckey`);
142 if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
144 my $opt_name = "opt_" . $key;
152 my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
153 read_repo_config($opts);
154 Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
156 # turn the Getopt::Std specification in a Getopt::Long one,
157 # with support for multiple -M options
158 GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
163 chomp(my $module = `git config --get cvsimport.module`);
164 push(@ARGV, $module) if $? == 0;
166 @ARGV <= 1 or usage("You can't specify more than one CVS module");
169 $ENV{"CVSROOT"} = $opt_d;
170 } elsif (-f 'CVS/Root') {
171 open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
175 $ENV{"CVSROOT"} = $opt_d;
176 } elsif ($ENV{"CVSROOT"}) {
177 $opt_d = $ENV{"CVSROOT"};
179 usage("CVSROOT needs to be set");
184 my $git_tree = $opt_C;
188 if (defined $opt_r) {
189 $remote = 'refs/remotes/' . $opt_r;
193 $remote = 'refs/heads';
198 $cvs_tree = $ARGV[0];
199 } elsif (-f 'CVS/Repository') {
200 open my $f, '<', 'CVS/Repository' or
201 die 'Failed to open CVS/Repository';
206 usage("CVS module has to be specified");
211 @mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
214 push (@mergerx, map { qr/$_/ } @opt_M);
217 # Remember UTC of our starting time
218 # we'll want to avoid importing commits
219 # that are too recent
220 our $starttime = time();
222 select(STDERR); $|=1; select(STDOUT);
227 # We're only interested in connecting and downloading, so ...
230 use File::Temp qw(tempfile);
231 use POSIX qw(strftime dup2);
234 my ($what,$repo,$subdir) = @_;
235 $what=ref($what) if ref($what);
238 $self->{'buffer'} = "";
242 $self->{'fullrep'} = $repo;
245 $self->{'subdir'} = $subdir;
246 $self->{'lines'} = undef;
251 sub find_password_entry {
252 my ($cvspass, @cvsroot) = @_;
253 my ($file, $delim) = @$cvspass;
257 if (open(my $fh, $file)) {
258 # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
263 my ($w, $p) = split($delim,$_,2);
264 for my $cvsroot (@cvsroot) {
265 if ($w eq $cvsroot) {
278 my $repo = $self->{'fullrep'};
279 if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
280 my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
282 my ($proxyhost,$proxyport);
283 if ($param && ($param =~ m/proxy=([^;]+)/)) {
285 # Default proxyport, if not specified, is 8080.
287 if ($ENV{"CVS_PROXY_PORT"}) {
288 $proxyport = $ENV{"CVS_PROXY_PORT"};
290 if ($param =~ m/proxyport=([^;]+)/) {
296 # if username is not explicit in CVSROOT, then use current user, as cvs would
297 $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
300 $rr2 = ":pserver:$user\@$serv:$repo";
303 my $rr = ":pserver:$user\@$serv:$port$repo";
306 $pass = $self->_scramble($pass);
308 my @cvspass = ([$ENV{'HOME'}."/.cvspass", qr/\s/],
309 [$ENV{'HOME'}."/.cvs/cvspass", qr/=/]);
311 foreach my $cvspass (@cvspass) {
312 my $p = find_password_entry($cvspass, $rr, $rr2);
314 push @loc, $cvspass->[0];
320 die("Multiple cvs password files have ".
321 "entries for CVSROOT $opt_d: @loc");
330 # Use a HTTP Proxy. Only works for HTTP proxies that
331 # don't require user authentication
333 # See: http://www.ietf.org/rfc/rfc2817.txt
335 $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
336 die "Socket to $proxyhost: $!\n" unless defined $s;
337 $s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
338 or die "Write to $proxyhost: $!\n";
343 # The answer should look like 'HTTP/1.x 2yy ....'
344 if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
345 die "Proxy connect: $rep\n";
347 # Skip up to the empty line of the proxy server output
348 # including the response headers.
349 while ($rep = <$s>) {
350 last if (!defined $rep ||
355 $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
356 die "Socket to $serv: $!\n" unless defined $s;
359 $s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
360 or die "Write to $serv: $!\n";
365 if ($rep ne "I LOVE YOU\n") {
366 $rep="<unknown>" unless $rep;
367 die "AuthReply: $rep\n";
369 $self->{'socketo'} = $s;
370 $self->{'socketi'} = $s;
371 } else { # local or ext: Fork off our own cvs server.
372 my $pr = IO::Pipe->new();
373 my $pw = IO::Pipe->new();
375 die "Fork: $!\n" unless defined $pid;
377 $cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
379 $rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
381 my @cvs = ($cvs, 'server');
382 my ($local, $user, $host);
383 $local = $repo =~ s/:local://;
386 $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
387 ($user, $host) = ($1, $2);
391 unshift @cvs, $rsh, '-l', $user, $host;
393 unshift @cvs, $rsh, $host;
400 dup2($pw->fileno(),0);
401 dup2($pr->fileno(),1);
408 $self->{'socketo'} = $pw;
409 $self->{'socketi'} = $pr;
411 $self->{'socketo'}->write("Root $repo\n");
413 # Trial and error says that this probably is the minimum set
414 $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
416 $self->{'socketo'}->write("valid-requests\n");
417 $self->{'socketo'}->flush();
419 my $rep=$self->readline();
420 die "Failed to read from server" unless defined $rep;
422 if ($rep !~ s/^Valid-requests\s*//) {
423 $rep="<unknown>" unless $rep;
424 die "Expected Valid-requests from server, but got: $rep\n";
426 chomp(my $res=$self->readline());
427 die "validReply: $res\n" if $res ne "ok";
429 $self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
430 $self->{'repo'} = $repo;
435 return $self->{'socketi'}->getline();
439 # Request a file with a given revision.
440 # Trial and error says this is a good way to do it. :-/
441 my ($self,$fn,$rev) = @_;
442 $self->{'socketo'}->write("Argument -N\n") or return undef;
443 $self->{'socketo'}->write("Argument -P\n") or return undef;
444 # -kk: Linus' version doesn't use it - defaults to off
446 $self->{'socketo'}->write("Argument -kk\n") or return undef;
448 $self->{'socketo'}->write("Argument -r\n") or return undef;
449 $self->{'socketo'}->write("Argument $rev\n") or return undef;
450 $self->{'socketo'}->write("Argument --\n") or return undef;
451 $self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
452 $self->{'socketo'}->write("Directory .\n") or return undef;
453 $self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
454 # $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
455 $self->{'socketo'}->write("co\n") or return undef;
456 $self->{'socketo'}->flush() or return undef;
457 $self->{'lines'} = 0;
461 # Read a line from the server.
462 # ... except that 'line' may be an entire file. ;-)
463 my ($self, $fh) = @_;
464 die "Not in lines" unless defined $self->{'lines'};
468 while (defined($line = $self->readline())) {
469 # M U gnupg-cvs-rep/AUTHORS
470 # Updated gnupg-cvs-rep/
471 # /daten/src/rsync/gnupg-cvs-rep/AUTHORS
472 # /AUTHORS/1.1///T1.1
477 if ($line =~ s/^(?:Created|Updated) //) {
478 $line = $self->readline(); # path
479 $line = $self->readline(); # Entries line
480 my $mode = $self->readline(); chomp $mode;
481 $self->{'mode'} = $mode;
482 defined (my $cnt = $self->readline())
483 or die "EOF from server after 'Changed'\n";
485 die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
487 $res = $self->_fetchfile($fh, $cnt);
488 } elsif ($line =~ s/^ //) {
490 $res += length($line);
491 } elsif ($line =~ /^M\b/) {
493 } elsif ($line =~ /^Mbinary\b/) {
495 die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
497 die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
499 $res += $self->_fetchfile($fh, $cnt);
503 # print STDERR "S: ok (".length($res).")\n";
505 } elsif ($line =~ s/^E //) {
506 # print STDERR "S: $line\n";
507 } elsif ($line =~ /^(Remove-entry|Removed) /i) {
508 $line = $self->readline(); # filename
509 $line = $self->readline(); # OK
511 die "Unknown: $line" if $line ne "ok";
514 die "Unknown: $line\n";
521 my ($self,$fn,$rev) = @_;
524 my ($fh, $name) = tempfile('gitcvs.XXXXXX',
525 DIR => File::Spec->tmpdir(), UNLINK => 1);
527 $self->_file($fn,$rev) and $res = $self->_line($fh);
530 print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
533 $self->_file($fn,$rev) or die "No file command send";
534 $res = $self->_line($fh);
535 die "Retry failed" unless defined $res;
539 return ($name, $res);
542 my ($self, $fh, $cnt) = @_;
544 my $bufsize = 1024 * 1024;
546 if ($bufsize > $cnt) {
550 my $num = $self->{'socketi'}->read($buf,$bufsize);
551 die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
560 my ($self, $pass) = @_;
563 return $scrambled unless $pass;
565 my $pass_len = length($pass);
566 my @pass_arr = split("", $pass);
569 # from cvs/src/scramble.c
571 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
572 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
573 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
574 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
575 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
576 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
577 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
578 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
579 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
580 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
581 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
582 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
583 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
584 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
585 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
586 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
589 for ($i = 0; $i < $pass_len; $i++) {
590 $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
598 my $cvs = CVSconn->new($opt_d, $cvs_tree);
603 m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
604 or die "Unparseable date: $d\n";
605 my $y=$1; $y-=1900 if $y>1900;
606 return timegm($6||0,$5,$4,$3,$2-1,$y);
614 for my $x(split(//,$mode)) {
619 } elsif ($x eq "u") { $um |= 0700;
620 } elsif ($x eq "g") { $um |= 0070;
621 } elsif ($x eq "o") { $um |= 0007;
622 } elsif ($x eq "r") { $mm |= 0444;
623 } elsif ($x eq "w") { $mm |= 0222;
624 } elsif ($x eq "x") { $mm |= 0111;
625 } elsif ($x eq "=") { # do nothing
626 } else { die "Unknown mode: $mode\n";
641 return $s =~ /^[a-f0-9]{40}$/;
644 sub get_headref ($) {
646 my $r = `git rev-parse --verify '$name' 2>/dev/null`;
647 return undef unless $? == 0;
652 my $user_filename_prepend = '';
653 sub munge_user_filename {
655 return File::Spec->file_name_is_absolute($name) ?
657 $user_filename_prepend . $name;
661 or mkdir($git_tree,0777)
662 or die "Could not create $git_tree: $!";
663 if ($git_tree ne '.') {
664 $user_filename_prepend = getwd() . '/';
668 my $last_branch = "";
669 my $orig_branch = "";
671 my $tip_at_start = undef;
673 my $git_dir = $ENV{"GIT_DIR"} || ".git";
674 $git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
675 $ENV{"GIT_DIR"} = $git_dir;
677 $orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
679 my %index; # holds filenames of one index per branch
681 unless (-d $git_dir) {
682 system(qw(git init));
683 die "Cannot init the GIT db at $git_tree: $?\n" if $?;
684 system(qw(git read-tree --empty));
685 die "Cannot init an empty tree: $?\n" if $?;
687 $last_branch = $opt_o;
690 open(F, "-|", qw(git symbolic-ref HEAD)) or
691 die "Cannot run git symbolic-ref: $!\n";
692 chomp ($last_branch = <F>);
693 $last_branch = basename($last_branch);
695 unless ($last_branch) {
696 warn "Cannot read the last branch name: $! -- assuming 'master'\n";
697 $last_branch = "master";
699 $orig_branch = $last_branch;
700 $tip_at_start = `git rev-parse --verify HEAD`;
702 # Get the last import timestamps
703 my $fmt = '($ref, $author) = (%(refname), %(author));';
704 my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
705 open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
706 while (defined(my $entry = <H>)) {
708 eval($entry) || die "cannot eval refs list: $@";
709 my ($head) = ($ref =~ m|^$remote/(.*)|);
710 $author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
711 $branch_date{$head} = $1;
714 if (!exists $branch_date{$opt_o}) {
715 die "Branch '$opt_o' does not exist.\n".
716 "Either use the correct '-o branch' option,\n".
717 "or import to a new repository.\n";
722 or die "Could not create git subdir ($git_dir).\n";
724 # now we read (and possibly save) author-info as well
725 -f "$git_dir/cvs-authors" and
726 read_author_info("$git_dir/cvs-authors");
728 read_author_info(munge_user_filename($opt_A));
729 write_author_info("$git_dir/cvs-authors");
732 # open .git/cvs-revisions, if requested
733 open my $revision_map, '>>', "$git_dir/cvs-revisions"
734 or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
739 # run cvsps into a file unless we are getting
740 # it passed as a file via $opt_P
744 print "Running cvsps...\n" if $opt_v;
745 my $pid = open(CVSPS,"-|");
747 die "Cannot fork: $!\n" unless defined $pid;
750 @opt = split(/,/,$opt_p) if defined $opt_p;
751 unshift @opt, '-z', $opt_z if defined $opt_z;
752 unshift @opt, '-q' unless defined $opt_v;
753 unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
754 push @opt, '--cvs-direct';
756 exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
757 die "Could not start cvsps: $!\n";
759 ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
760 DIR => File::Spec->tmpdir());
765 $? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
768 $cvspsfile = munge_user_filename($opt_P);
771 open(CVS, "<$cvspsfile") or die $!;
774 #---------------------
776 #Date: 1999/09/18 13:03:59
778 #Branch: STABLE-BRANCH-1-0
779 #Ancestor branch: HEAD
782 # See ChangeLog: Sat Sep 18 13:03:28 CEST 1999 Werner Koch
784 # README:1.57->1.57.2.1
785 # VERSION:1.96->1.96.2.1
787 #---------------------
791 sub update_index (\@\@) {
794 open(my $fh, '|-', qw(git update-index -z --index-info))
795 or die "unable to open git update-index: $!";
797 (map { "0 0000000000000000000000000000000000000000\t$_\0" }
799 (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
801 or die "unable to write to git update-index: $!";
803 or die "unable to write to git update-index: $!";
804 $? and die "git update-index reported error: $?";
808 open(my $fh, '-|', qw(git write-tree))
809 or die "unable to open git write-tree: $!";
810 chomp(my $tree = <$fh>);
812 or die "Cannot get tree id ($tree): $!";
814 or die "Error running git write-tree: $?\n";
815 print "Tree ID $tree\n" if $opt_v;
819 my ($patchset,$date,$author_name,$author_email,$author_tz,$branch,$ancestor,$tag,$logmsg);
820 my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
822 # commits that cvsps cannot place anywhere...
823 $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
826 if ($branch eq $opt_o && !$index{branch} &&
827 !get_headref("$remote/$branch")) {
828 # looks like an initial commit
829 # use the index primed by git init
830 $ENV{GIT_INDEX_FILE} = "$git_dir/index";
831 $index{$branch} = "$git_dir/index";
833 # use an index per branch to speed up
834 # imports of projects with many branches
835 unless ($index{$branch}) {
836 $index{$branch} = tmpnam();
837 $ENV{GIT_INDEX_FILE} = $index{$branch};
839 system("git", "read-tree", "$remote/$ancestor");
841 system("git", "read-tree", "$remote/$branch");
843 die "read-tree failed: $?\n" if $?;
846 $ENV{GIT_INDEX_FILE} = $index{$branch};
848 update_index(@old, @new);
850 my $tree = write_tree();
851 my $parent = get_headref("$remote/$last_branch");
852 print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
855 push @commit_args, ("-p", $parent) if $parent;
857 # loose detection of merges
858 # based on the commit msg
859 foreach my $rx (@mergerx) {
860 next unless $logmsg =~ $rx && $1;
861 my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
862 if (my $sha1 = get_headref("$remote/$mparent")) {
863 push @commit_args, '-p', "$remote/$mparent";
864 print "Merge parent branch: $mparent\n" if $opt_v;
868 set_timezone($author_tz);
869 # $date is in the seconds since epoch format
870 my $tz_offset = get_tz_offset($date);
871 my $commit_date = "$date $tz_offset";
873 $ENV{GIT_AUTHOR_NAME} = $author_name;
874 $ENV{GIT_AUTHOR_EMAIL} = $author_email;
875 $ENV{GIT_AUTHOR_DATE} = $commit_date;
876 $ENV{GIT_COMMITTER_NAME} = $author_name;
877 $ENV{GIT_COMMITTER_EMAIL} = $author_email;
878 $ENV{GIT_COMMITTER_DATE} = $commit_date;
879 my $pid = open2(my $commit_read, my $commit_write,
880 'git', 'commit-tree', $tree, @commit_args);
882 # compatibility with git2cvs
883 substr($logmsg,32767) = "" if length($logmsg) > 32767;
884 $logmsg =~ s/[\s\n]+\z//;
887 $logmsg .= "\n\n\nSKIPPED:\n\t";
888 $logmsg .= join("\n\t", @skipped) . "\n";
892 print($commit_write "$logmsg\n") && close($commit_write)
893 or die "Error writing to git commit-tree: $!\n";
895 print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
896 chomp(my $cid = <$commit_read>);
897 is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
898 print "Commit ID $cid\n" if $opt_v;
902 die "Error running git commit-tree: $?\n" if $?;
904 system('git' , 'update-ref', "$remote/$branch", $cid) == 0
905 or die "Cannot write branch $branch for update: $!\n";
908 print $revision_map "@$_ $cid\n" for @commit_revisions;
910 @commit_revisions = ();
914 $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
915 $xtag =~ tr/_/\./ if ( $opt_u );
916 $xtag =~ s/[\/]/$opt_s/g;
918 # See refs.c for these rules.
919 # Tag cannot contain bad chars. (See bad_ref_char in refs.c.)
920 $xtag =~ s/[ ~\^:\\\*\?\[]//g;
921 # Other bad strings for tags:
922 # (See check_refname_component in refs.c.)
924 (?: \.\. # Tag cannot contain '..'.
925 | \@\{ # Tag cannot contain '@{'.
926 | ^ - # Tag cannot begin with '-'.
927 | \.lock $ # Tag cannot end with '.lock'.
928 | ^ \. # Tag cannot begin...
929 | \. $ # ...or end with '.'
931 # Tag cannot be empty.
933 warn("warning: ignoring tag '$tag'",
934 " with invalid tagname\n");
938 if (system('git' , 'tag', '-f', $xtag, $cid) != 0) {
939 # We did our best to sanitize the tag, but still failed
940 # for whatever reason. Bail out, and give the user
941 # enough information to understand if/how we should
942 # improve the translation in the future.
944 print "Translated '$tag' tag to '$xtag'\n";
946 die "Cannot create tag $xtag: $!\n";
949 print "Created tag '$xtag' on '$branch'\n" if $opt_v;
956 if ($state == 0 and /^-+$/) {
958 } elsif ($state == 0) {
961 } elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
964 } elsif ($state == 2 and s/^Date:\s+//) {
967 print STDERR "Could not parse date: $_\n";
972 } elsif ($state == 3 and s/^Author:\s+//) {
975 if (/^(.*?)\s+<(.*)>/) {
976 ($author_name, $author_email) = ($1, $2);
977 } elsif ($conv_author_name{$_}) {
978 $author_name = $conv_author_name{$_};
979 $author_email = $conv_author_email{$_};
980 $author_tz = $conv_author_tz{$_} if ($conv_author_tz{$_});
982 $author_name = $author_email = $_;
985 } elsif ($state == 4 and s/^Branch:\s+//) {
987 tr/_/\./ if ( $opt_u );
991 } elsif ($state == 5 and s/^Ancestor branch:\s+//) {
994 $ancestor = $opt_o if $ancestor eq "HEAD";
996 } elsif ($state == 5) {
1000 } elsif ($state == 6 and s/^Tag:\s+//) {
1002 if ($_ eq "(none)") {
1008 } elsif ($state == 7 and /^Log:/) {
1011 } elsif ($state == 8 and /^Members:/) {
1012 $branch = $opt_o if $branch eq "HEAD";
1013 if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
1015 print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
1019 if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
1020 # skip if the commit is too recent
1021 # given that the cvsps default fuzz is 300s, we give ourselves another
1022 # 300s just in case -- this also prevents skipping commits
1023 # due to server clock drift
1024 print "skip patchset $patchset: $date too recent\n" if $opt_v;
1028 if (exists $ignorebranch{$branch}) {
1029 print STDERR "Skipping $branch\n";
1034 if ($ancestor eq $branch) {
1035 print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
1038 if (defined get_headref("$remote/$branch")) {
1039 print STDERR "Branch $branch already exists!\n";
1043 my $id = get_headref("$remote/$ancestor");
1045 print STDERR "Branch $ancestor does not exist!\n";
1046 $ignorebranch{$branch} = 1;
1051 system(qw(git update-ref -m cvsimport),
1052 "$remote/$branch", $id);
1054 print STDERR "Could not create branch $branch\n";
1055 $ignorebranch{$branch} = 1;
1060 $last_branch = $branch if $branch ne $last_branch;
1062 } elsif ($state == 8) {
1064 } elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
1065 # VERSION:1.96->1.96.2.1
1066 my $init = ($2 eq "INITIAL");
1070 if ($opt_S && $fn =~ m/$opt_S/) {
1071 print "SKIPPING $fn v $rev\n";
1072 push(@skipped, $fn);
1075 push @commit_revisions, [$fn, $rev];
1076 print "Fetching $fn v $rev\n" if $opt_v;
1077 my ($tmpname, $size) = $cvs->file($fn,$rev);
1080 print "Drop $fn\n" if $opt_v;
1082 print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
1083 my $pid = open(my $F, '-|');
1084 die $! unless defined $pid;
1086 exec("git", "hash-object", "-w", $tmpname)
1087 or die "Cannot create object: $!\n";
1092 my $mode = pmode($cvs->{'mode'});
1093 push(@new,[$mode, $sha, $fn]); # may be resurrected!
1096 } elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
1100 push @commit_revisions, [$fn, $rev];
1102 print "Delete $fn\n" if $opt_v;
1103 } elsif ($state == 9 and /^\s*$/) {
1105 } elsif (($state == 9 or $state == 10) and /^-+$/) {
1107 if ($opt_L && $commitcount > $opt_L) {
1111 if (($commitcount & 1023) == 0) {
1112 system(qw(git repack -a -d));
1115 } elsif ($state == 11 and /^-+$/) {
1117 } elsif (/^-+$/) { # end of unknown-line processing
1119 } elsif ($state != 11) { # ignore stuff when skipping
1120 print STDERR "* UNKNOWN LINE * $_\n";
1123 commit() if $branch and $state != 11;
1129 # The heuristic of repacking every 1024 commits can leave a
1130 # lot of unpacked data. If there is more than 1MB worth of
1131 # not-packed objects, repack once more.
1132 my $line = `git count-objects`;
1133 if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
1134 my ($n_objects, $kb) = ($1, $2);
1136 and system(qw(git repack -a -d));
1139 foreach my $git_index (values %index) {
1140 if ($git_index ne "$git_dir/index") {
1145 if (defined $orig_git_index) {
1146 $ENV{GIT_INDEX_FILE} = $orig_git_index;
1148 delete $ENV{GIT_INDEX_FILE};
1151 # Now switch back to the branch we were in before all of this happened
1153 print "DONE.\n" if $opt_v;
1157 my $tip_at_end = `git rev-parse --verify HEAD`;
1158 if ($tip_at_start ne $tip_at_end) {
1159 for ($tip_at_start, $tip_at_end) { chomp; }
1160 print "Fetched into the current branch.\n" if $opt_v;
1161 system(qw(git read-tree -u -m),
1162 $tip_at_start, $tip_at_end);
1163 die "Fast-forward update failed: $?\n" if $?;
1166 system(qw(git merge -m cvsimport), "$remote/$opt_o");
1167 die "Could not merge $opt_o into the current branch.\n" if $?;
1170 $orig_branch = "master";
1171 print "DONE; creating $orig_branch branch\n" if $opt_v;
1172 system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
1173 unless defined get_headref('refs/heads/master');
1174 system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
1175 if ($opt_r && $opt_o ne 'HEAD');
1176 system('git', 'update-ref', 'HEAD', "$orig_branch");
1178 system(qw(git checkout -f));
1179 die "checkout failed: $?\n" if $?;