3 # This tool is copyright (c) 2005, Matthias Urlichs.
4 # It is released under the Gnu Public License, version 2.
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
10 # Checking out the files is done by a single long-running CVS connection
13 # The head revision is on branch "origin" by default.
14 # You can change that with the '-o' option.
21 use File::Temp qw(tempfile tmpnam);
22 use File::Path qw(mkpath);
23 use File::Basename qw(basename dirname);
27 use POSIX qw(strftime dup2 ENOENT);
30 $SIG{'PIPE'}="IGNORE";
33 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);
34 my (%conv_author_name, %conv_author_email);
38 print(STDERR "Error: $msg\n") if $msg;
40 Usage: git cvsimport # fetch/update GIT from CVS
41 [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
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]
44 [-r remote] [-R] [CVS_module]
49 sub read_author_info($) {
52 open my $f, '<', "$file" or die("Failed to open $file: $!\n");
55 # Expected format is this:
56 # exon=Andreas Ericsson <ae@op5.se>
57 if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
59 $conv_author_name{$user} = $2;
60 $conv_author_email{$user} = $3;
62 # However, we also read from CVSROOT/users format
64 elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
66 ($user, $mapped) = ($1, $3);
67 if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
68 $conv_author_name{$user} = $1;
69 $conv_author_email{$user} = $2;
71 elsif ($mapped =~ /^<?(.*)>?$/) {
72 $conv_author_name{$user} = $user;
73 $conv_author_email{$user} = $1;
76 # NEEDSWORK: Maybe warn on unrecognized lines?
81 sub write_author_info($) {
83 open my $f, '>', $file or
84 die("Failed to open $file for writing: $!");
86 foreach (keys %conv_author_name) {
87 print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>\n";
92 # convert getopts specs for use by git config
94 'A:' => 'authors-file',
95 'M:' => 'merge-regex',
97 'R' => 'track-revisions',
98 'S:' => 'ignore-paths',
101 sub read_repo_config {
102 # Split the string between characters, unless there is a ':'
103 # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
104 my @opts = split(/ *(?!:)/, shift);
105 foreach my $o (@opts) {
108 my $arg = 'git config';
109 $arg .= ' --bool' if ($o !~ /:$/);
112 if (exists $longmap{$o}) {
113 # An uppercase option like -R cannot be
114 # expressed in the configuration, as the
115 # variable names are downcased.
116 $ckey = $longmap{$o};
117 next if (! defined $ckey);
120 chomp(my $tmp = `$arg --get cvsimport.$ckey`);
121 if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
123 my $opt_name = "opt_" . $key;
131 my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:R";
132 read_repo_config($opts);
133 Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
135 # turn the Getopt::Std specification in a Getopt::Long one,
136 # with support for multiple -M options
137 GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
142 chomp(my $module = `git config --get cvsimport.module`);
143 push(@ARGV, $module) if $? == 0;
145 @ARGV <= 1 or usage("You can't specify more than one CVS module");
148 $ENV{"CVSROOT"} = $opt_d;
149 } elsif (-f 'CVS/Root') {
150 open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
154 $ENV{"CVSROOT"} = $opt_d;
155 } elsif ($ENV{"CVSROOT"}) {
156 $opt_d = $ENV{"CVSROOT"};
158 usage("CVSROOT needs to be set");
163 my $git_tree = $opt_C;
167 if (defined $opt_r) {
168 $remote = 'refs/remotes/' . $opt_r;
172 $remote = 'refs/heads';
177 $cvs_tree = $ARGV[0];
178 } elsif (-f 'CVS/Repository') {
179 open my $f, '<', 'CVS/Repository' or
180 die 'Failed to open CVS/Repository';
185 usage("CVS module has to be specified");
190 @mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
193 push (@mergerx, map { qr/$_/ } @opt_M);
196 # Remember UTC of our starting time
197 # we'll want to avoid importing commits
198 # that are too recent
199 our $starttime = time();
201 select(STDERR); $|=1; select(STDOUT);
206 # We're only interested in connecting and downloading, so ...
209 use File::Temp qw(tempfile);
210 use POSIX qw(strftime dup2);
213 my ($what,$repo,$subdir) = @_;
214 $what=ref($what) if ref($what);
217 $self->{'buffer'} = "";
221 $self->{'fullrep'} = $repo;
224 $self->{'subdir'} = $subdir;
225 $self->{'lines'} = undef;
232 my $repo = $self->{'fullrep'};
233 if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
234 my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
236 my ($proxyhost,$proxyport);
237 if ($param && ($param =~ m/proxy=([^;]+)/)) {
239 # Default proxyport, if not specified, is 8080.
241 if ($ENV{"CVS_PROXY_PORT"}) {
242 $proxyport = $ENV{"CVS_PROXY_PORT"};
244 if ($param =~ m/proxyport=([^;]+)/) {
250 # if username is not explicit in CVSROOT, then use current user, as cvs would
251 $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
254 $rr2 = ":pserver:$user\@$serv:$repo";
257 my $rr = ":pserver:$user\@$serv:$port$repo";
260 $pass = $self->_scramble($pass);
262 open(H,$ENV{'HOME'}."/.cvspass") and do {
263 # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
267 my ($w,$p) = split(/\s/,$_,2);
268 if ($w eq $rr or $w eq $rr2) {
274 $pass = "A" unless $pass;
280 # Use a HTTP Proxy. Only works for HTTP proxies that
281 # don't require user authentication
283 # See: http://www.ietf.org/rfc/rfc2817.txt
285 $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
286 die "Socket to $proxyhost: $!\n" unless defined $s;
287 $s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
288 or die "Write to $proxyhost: $!\n";
293 # The answer should look like 'HTTP/1.x 2yy ....'
294 if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
295 die "Proxy connect: $rep\n";
297 # Skip up to the empty line of the proxy server output
298 # including the response headers.
299 while ($rep = <$s>) {
300 last if (!defined $rep ||
305 $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
306 die "Socket to $serv: $!\n" unless defined $s;
309 $s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
310 or die "Write to $serv: $!\n";
315 if ($rep ne "I LOVE YOU\n") {
316 $rep="<unknown>" unless $rep;
317 die "AuthReply: $rep\n";
319 $self->{'socketo'} = $s;
320 $self->{'socketi'} = $s;
321 } else { # local or ext: Fork off our own cvs server.
322 my $pr = IO::Pipe->new();
323 my $pw = IO::Pipe->new();
325 die "Fork: $!\n" unless defined $pid;
327 $cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
329 $rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
331 my @cvs = ($cvs, 'server');
332 my ($local, $user, $host);
333 $local = $repo =~ s/:local://;
336 $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
337 ($user, $host) = ($1, $2);
341 unshift @cvs, $rsh, '-l', $user, $host;
343 unshift @cvs, $rsh, $host;
350 dup2($pw->fileno(),0);
351 dup2($pr->fileno(),1);
358 $self->{'socketo'} = $pw;
359 $self->{'socketi'} = $pr;
361 $self->{'socketo'}->write("Root $repo\n");
363 # Trial and error says that this probably is the minimum set
364 $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
366 $self->{'socketo'}->write("valid-requests\n");
367 $self->{'socketo'}->flush();
369 my $rep=$self->readline();
370 die "Failed to read from server" unless defined $rep;
372 if ($rep !~ s/^Valid-requests\s*//) {
373 $rep="<unknown>" unless $rep;
374 die "Expected Valid-requests from server, but got: $rep\n";
376 chomp(my $res=$self->readline());
377 die "validReply: $res\n" if $res ne "ok";
379 $self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
380 $self->{'repo'} = $repo;
385 return $self->{'socketi'}->getline();
389 # Request a file with a given revision.
390 # Trial and error says this is a good way to do it. :-/
391 my ($self,$fn,$rev) = @_;
392 $self->{'socketo'}->write("Argument -N\n") or return undef;
393 $self->{'socketo'}->write("Argument -P\n") or return undef;
394 # -kk: Linus' version doesn't use it - defaults to off
396 $self->{'socketo'}->write("Argument -kk\n") or return undef;
398 $self->{'socketo'}->write("Argument -r\n") or return undef;
399 $self->{'socketo'}->write("Argument $rev\n") or return undef;
400 $self->{'socketo'}->write("Argument --\n") or return undef;
401 $self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
402 $self->{'socketo'}->write("Directory .\n") or return undef;
403 $self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
404 # $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
405 $self->{'socketo'}->write("co\n") or return undef;
406 $self->{'socketo'}->flush() or return undef;
407 $self->{'lines'} = 0;
411 # Read a line from the server.
412 # ... except that 'line' may be an entire file. ;-)
413 my ($self, $fh) = @_;
414 die "Not in lines" unless defined $self->{'lines'};
418 while (defined($line = $self->readline())) {
419 # M U gnupg-cvs-rep/AUTHORS
420 # Updated gnupg-cvs-rep/
421 # /daten/src/rsync/gnupg-cvs-rep/AUTHORS
422 # /AUTHORS/1.1///T1.1
427 if ($line =~ s/^(?:Created|Updated) //) {
428 $line = $self->readline(); # path
429 $line = $self->readline(); # Entries line
430 my $mode = $self->readline(); chomp $mode;
431 $self->{'mode'} = $mode;
432 defined (my $cnt = $self->readline())
433 or die "EOF from server after 'Changed'\n";
435 die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
437 $res = $self->_fetchfile($fh, $cnt);
438 } elsif ($line =~ s/^ //) {
440 $res += length($line);
441 } elsif ($line =~ /^M\b/) {
443 } elsif ($line =~ /^Mbinary\b/) {
445 die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
447 die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
449 $res += $self->_fetchfile($fh, $cnt);
453 # print STDERR "S: ok (".length($res).")\n";
455 } elsif ($line =~ s/^E //) {
456 # print STDERR "S: $line\n";
457 } elsif ($line =~ /^(Remove-entry|Removed) /i) {
458 $line = $self->readline(); # filename
459 $line = $self->readline(); # OK
461 die "Unknown: $line" if $line ne "ok";
464 die "Unknown: $line\n";
471 my ($self,$fn,$rev) = @_;
474 my ($fh, $name) = tempfile('gitcvs.XXXXXX',
475 DIR => File::Spec->tmpdir(), UNLINK => 1);
477 $self->_file($fn,$rev) and $res = $self->_line($fh);
480 print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
483 $self->_file($fn,$rev) or die "No file command send";
484 $res = $self->_line($fh);
485 die "Retry failed" unless defined $res;
489 return ($name, $res);
492 my ($self, $fh, $cnt) = @_;
494 my $bufsize = 1024 * 1024;
496 if ($bufsize > $cnt) {
500 my $num = $self->{'socketi'}->read($buf,$bufsize);
501 die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
510 my ($self, $pass) = @_;
513 return $scrambled unless $pass;
515 my $pass_len = length($pass);
516 my @pass_arr = split("", $pass);
519 # from cvs/src/scramble.c
521 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
522 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
523 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
524 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
525 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
526 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
527 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
528 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
529 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
530 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
531 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
532 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
533 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
534 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
535 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
536 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
539 for ($i = 0; $i < $pass_len; $i++) {
540 $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
548 my $cvs = CVSconn->new($opt_d, $cvs_tree);
553 m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
554 or die "Unparseable date: $d\n";
555 my $y=$1; $y-=1900 if $y>1900;
556 return timegm($6||0,$5,$4,$3,$2-1,$y);
564 for my $x(split(//,$mode)) {
569 } elsif ($x eq "u") { $um |= 0700;
570 } elsif ($x eq "g") { $um |= 0070;
571 } elsif ($x eq "o") { $um |= 0007;
572 } elsif ($x eq "r") { $mm |= 0444;
573 } elsif ($x eq "w") { $mm |= 0222;
574 } elsif ($x eq "x") { $mm |= 0111;
575 } elsif ($x eq "=") { # do nothing
576 } else { die "Unknown mode: $mode\n";
591 return $s =~ /^[a-f0-9]{40}$/;
594 sub get_headref ($) {
596 my $r = `git rev-parse --verify '$name' 2>/dev/null`;
597 return undef unless $? == 0;
602 my $user_filename_prepend = '';
603 sub munge_user_filename {
605 return File::Spec->file_name_is_absolute($name) ?
607 $user_filename_prepend . $name;
611 or mkdir($git_tree,0777)
612 or die "Could not create $git_tree: $!";
613 if ($git_tree ne '.') {
614 $user_filename_prepend = getwd() . '/';
618 my $last_branch = "";
619 my $orig_branch = "";
621 my $tip_at_start = undef;
623 my $git_dir = $ENV{"GIT_DIR"} || ".git";
624 $git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
625 $ENV{"GIT_DIR"} = $git_dir;
627 $orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
629 my %index; # holds filenames of one index per branch
631 unless (-d $git_dir) {
632 system(qw(git init));
633 die "Cannot init the GIT db at $git_tree: $?\n" if $?;
634 system(qw(git read-tree --empty));
635 die "Cannot init an empty tree: $?\n" if $?;
637 $last_branch = $opt_o;
640 open(F, "-|", qw(git symbolic-ref HEAD)) or
641 die "Cannot run git symbolic-ref: $!\n";
642 chomp ($last_branch = <F>);
643 $last_branch = basename($last_branch);
645 unless ($last_branch) {
646 warn "Cannot read the last branch name: $! -- assuming 'master'\n";
647 $last_branch = "master";
649 $orig_branch = $last_branch;
650 $tip_at_start = `git rev-parse --verify HEAD`;
652 # Get the last import timestamps
653 my $fmt = '($ref, $author) = (%(refname), %(author));';
654 my @cmd = ('git', 'for-each-ref', '--perl', "--format=$fmt", $remote);
655 open(H, "-|", @cmd) or die "Cannot run git for-each-ref: $!\n";
656 while (defined(my $entry = <H>)) {
658 eval($entry) || die "cannot eval refs list: $@";
659 my ($head) = ($ref =~ m|^$remote/(.*)|);
660 $author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
661 $branch_date{$head} = $1;
664 if (!exists $branch_date{$opt_o}) {
665 die "Branch '$opt_o' does not exist.\n".
666 "Either use the correct '-o branch' option,\n".
667 "or import to a new repository.\n";
672 or die "Could not create git subdir ($git_dir).\n";
674 # now we read (and possibly save) author-info as well
675 -f "$git_dir/cvs-authors" and
676 read_author_info("$git_dir/cvs-authors");
678 read_author_info(munge_user_filename($opt_A));
679 write_author_info("$git_dir/cvs-authors");
682 # open .git/cvs-revisions, if requested
683 open my $revision_map, '>>', "$git_dir/cvs-revisions"
684 or die "Can't open $git_dir/cvs-revisions for appending: $!\n"
689 # run cvsps into a file unless we are getting
690 # it passed as a file via $opt_P
694 print "Running cvsps...\n" if $opt_v;
695 my $pid = open(CVSPS,"-|");
697 die "Cannot fork: $!\n" unless defined $pid;
700 @opt = split(/,/,$opt_p) if defined $opt_p;
701 unshift @opt, '-z', $opt_z if defined $opt_z;
702 unshift @opt, '-q' unless defined $opt_v;
703 unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
704 push @opt, '--cvs-direct';
706 exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
707 die "Could not start cvsps: $!\n";
709 ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
710 DIR => File::Spec->tmpdir());
715 $? == 0 or die "git cvsimport: fatal: cvsps reported error\n";
718 $cvspsfile = munge_user_filename($opt_P);
721 open(CVS, "<$cvspsfile") or die $!;
724 #---------------------
726 #Date: 1999/09/18 13:03:59
728 #Branch: STABLE-BRANCH-1-0
729 #Ancestor branch: HEAD
732 # See ChangeLog: Sat Sep 18 13:03:28 CEST 1999 Werner Koch
734 # README:1.57->1.57.2.1
735 # VERSION:1.96->1.96.2.1
737 #---------------------
741 sub update_index (\@\@) {
744 open(my $fh, '|-', qw(git update-index -z --index-info))
745 or die "unable to open git update-index: $!";
747 (map { "0 0000000000000000000000000000000000000000\t$_\0" }
749 (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
751 or die "unable to write to git update-index: $!";
753 or die "unable to write to git update-index: $!";
754 $? and die "git update-index reported error: $?";
758 open(my $fh, '-|', qw(git write-tree))
759 or die "unable to open git write-tree: $!";
760 chomp(my $tree = <$fh>);
762 or die "Cannot get tree id ($tree): $!";
764 or die "Error running git write-tree: $?\n";
765 print "Tree ID $tree\n" if $opt_v;
769 my ($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
770 my (@old,@new,@skipped,%ignorebranch,@commit_revisions);
772 # commits that cvsps cannot place anywhere...
773 $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
776 if ($branch eq $opt_o && !$index{branch} &&
777 !get_headref("$remote/$branch")) {
778 # looks like an initial commit
779 # use the index primed by git init
780 $ENV{GIT_INDEX_FILE} = "$git_dir/index";
781 $index{$branch} = "$git_dir/index";
783 # use an index per branch to speed up
784 # imports of projects with many branches
785 unless ($index{$branch}) {
786 $index{$branch} = tmpnam();
787 $ENV{GIT_INDEX_FILE} = $index{$branch};
789 system("git", "read-tree", "$remote/$ancestor");
791 system("git", "read-tree", "$remote/$branch");
793 die "read-tree failed: $?\n" if $?;
796 $ENV{GIT_INDEX_FILE} = $index{$branch};
798 update_index(@old, @new);
800 my $tree = write_tree();
801 my $parent = get_headref("$remote/$last_branch");
802 print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
805 push @commit_args, ("-p", $parent) if $parent;
807 # loose detection of merges
808 # based on the commit msg
809 foreach my $rx (@mergerx) {
810 next unless $logmsg =~ $rx && $1;
811 my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
812 if (my $sha1 = get_headref("$remote/$mparent")) {
813 push @commit_args, '-p', "$remote/$mparent";
814 print "Merge parent branch: $mparent\n" if $opt_v;
818 my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date));
819 $ENV{GIT_AUTHOR_NAME} = $author_name;
820 $ENV{GIT_AUTHOR_EMAIL} = $author_email;
821 $ENV{GIT_AUTHOR_DATE} = $commit_date;
822 $ENV{GIT_COMMITTER_NAME} = $author_name;
823 $ENV{GIT_COMMITTER_EMAIL} = $author_email;
824 $ENV{GIT_COMMITTER_DATE} = $commit_date;
825 my $pid = open2(my $commit_read, my $commit_write,
826 'git', 'commit-tree', $tree, @commit_args);
828 # compatibility with git2cvs
829 substr($logmsg,32767) = "" if length($logmsg) > 32767;
830 $logmsg =~ s/[\s\n]+\z//;
833 $logmsg .= "\n\n\nSKIPPED:\n\t";
834 $logmsg .= join("\n\t", @skipped) . "\n";
838 print($commit_write "$logmsg\n") && close($commit_write)
839 or die "Error writing to git commit-tree: $!\n";
841 print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
842 chomp(my $cid = <$commit_read>);
843 is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
844 print "Commit ID $cid\n" if $opt_v;
848 die "Error running git commit-tree: $?\n" if $?;
850 system('git' , 'update-ref', "$remote/$branch", $cid) == 0
851 or die "Cannot write branch $branch for update: $!\n";
854 print $revision_map "@$_ $cid\n" for @commit_revisions;
856 @commit_revisions = ();
860 $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
861 $xtag =~ tr/_/\./ if ( $opt_u );
862 $xtag =~ s/[\/]/$opt_s/g;
865 system('git' , 'tag', '-f', $xtag, $cid) == 0
866 or die "Cannot create tag $xtag: $!\n";
868 print "Created tag '$xtag' on '$branch'\n" if $opt_v;
875 if ($state == 0 and /^-+$/) {
877 } elsif ($state == 0) {
880 } elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
883 } elsif ($state == 2 and s/^Date:\s+//) {
886 print STDERR "Could not parse date: $_\n";
891 } elsif ($state == 3 and s/^Author:\s+//) {
893 if (/^(.*?)\s+<(.*)>/) {
894 ($author_name, $author_email) = ($1, $2);
895 } elsif ($conv_author_name{$_}) {
896 $author_name = $conv_author_name{$_};
897 $author_email = $conv_author_email{$_};
899 $author_name = $author_email = $_;
902 } elsif ($state == 4 and s/^Branch:\s+//) {
904 tr/_/\./ if ( $opt_u );
908 } elsif ($state == 5 and s/^Ancestor branch:\s+//) {
911 $ancestor = $opt_o if $ancestor eq "HEAD";
913 } elsif ($state == 5) {
917 } elsif ($state == 6 and s/^Tag:\s+//) {
919 if ($_ eq "(none)") {
925 } elsif ($state == 7 and /^Log:/) {
928 } elsif ($state == 8 and /^Members:/) {
929 $branch = $opt_o if $branch eq "HEAD";
930 if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
932 print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
936 if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
937 # skip if the commit is too recent
938 # given that the cvsps default fuzz is 300s, we give ourselves another
939 # 300s just in case -- this also prevents skipping commits
940 # due to server clock drift
941 print "skip patchset $patchset: $date too recent\n" if $opt_v;
945 if (exists $ignorebranch{$branch}) {
946 print STDERR "Skipping $branch\n";
951 if ($ancestor eq $branch) {
952 print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
955 if (defined get_headref("$remote/$branch")) {
956 print STDERR "Branch $branch already exists!\n";
960 my $id = get_headref("$remote/$ancestor");
962 print STDERR "Branch $ancestor does not exist!\n";
963 $ignorebranch{$branch} = 1;
968 system(qw(git update-ref -m cvsimport),
969 "$remote/$branch", $id);
971 print STDERR "Could not create branch $branch\n";
972 $ignorebranch{$branch} = 1;
977 $last_branch = $branch if $branch ne $last_branch;
979 } elsif ($state == 8) {
981 } elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
982 # VERSION:1.96->1.96.2.1
983 my $init = ($2 eq "INITIAL");
987 if ($opt_S && $fn =~ m/$opt_S/) {
988 print "SKIPPING $fn v $rev\n";
992 push @commit_revisions, [$fn, $rev];
993 print "Fetching $fn v $rev\n" if $opt_v;
994 my ($tmpname, $size) = $cvs->file($fn,$rev);
997 print "Drop $fn\n" if $opt_v;
999 print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
1000 my $pid = open(my $F, '-|');
1001 die $! unless defined $pid;
1003 exec("git", "hash-object", "-w", $tmpname)
1004 or die "Cannot create object: $!\n";
1009 my $mode = pmode($cvs->{'mode'});
1010 push(@new,[$mode, $sha, $fn]); # may be resurrected!
1013 } elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
1017 push @commit_revisions, [$fn, $rev];
1019 print "Delete $fn\n" if $opt_v;
1020 } elsif ($state == 9 and /^\s*$/) {
1022 } elsif (($state == 9 or $state == 10) and /^-+$/) {
1024 if ($opt_L && $commitcount > $opt_L) {
1028 if (($commitcount & 1023) == 0) {
1029 system(qw(git repack -a -d));
1032 } elsif ($state == 11 and /^-+$/) {
1034 } elsif (/^-+$/) { # end of unknown-line processing
1036 } elsif ($state != 11) { # ignore stuff when skipping
1037 print STDERR "* UNKNOWN LINE * $_\n";
1040 commit() if $branch and $state != 11;
1046 # The heuristic of repacking every 1024 commits can leave a
1047 # lot of unpacked data. If there is more than 1MB worth of
1048 # not-packed objects, repack once more.
1049 my $line = `git count-objects`;
1050 if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
1051 my ($n_objects, $kb) = ($1, $2);
1053 and system(qw(git repack -a -d));
1056 foreach my $git_index (values %index) {
1057 if ($git_index ne "$git_dir/index") {
1062 if (defined $orig_git_index) {
1063 $ENV{GIT_INDEX_FILE} = $orig_git_index;
1065 delete $ENV{GIT_INDEX_FILE};
1068 # Now switch back to the branch we were in before all of this happened
1070 print "DONE.\n" if $opt_v;
1074 my $tip_at_end = `git rev-parse --verify HEAD`;
1075 if ($tip_at_start ne $tip_at_end) {
1076 for ($tip_at_start, $tip_at_end) { chomp; }
1077 print "Fetched into the current branch.\n" if $opt_v;
1078 system(qw(git read-tree -u -m),
1079 $tip_at_start, $tip_at_end);
1080 die "Fast-forward update failed: $?\n" if $?;
1083 system(qw(git merge cvsimport HEAD), "$remote/$opt_o");
1084 die "Could not merge $opt_o into the current branch.\n" if $?;
1087 $orig_branch = "master";
1088 print "DONE; creating $orig_branch branch\n" if $opt_v;
1089 system("git", "update-ref", "refs/heads/master", "$remote/$opt_o")
1090 unless defined get_headref('refs/heads/master');
1091 system("git", "symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
1092 if ($opt_r && $opt_o ne 'HEAD');
1093 system('git', 'update-ref', 'HEAD', "$orig_branch");
1095 system(qw(git checkout -f));
1096 die "checkout failed: $?\n" if $?;