blob: a7d215c8aa479bc6b456ec5c3792e931598ac365 [file] [log] [blame]
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001#!/usr/bin/perl -w
Tommy M. McGuire9718a002005-06-10 01:38:32 -05002
Matthias Urlichsa57a9492005-06-28 16:48:40 +02003# This tool is copyright (c) 2005, Matthias Urlichs.
4# It is released under the Gnu Public License, version 2.
5#
6# The basic idea is to aggregate CVS check-ins into related changes.
7# Fortunately, "cvsps" does that for us; all we have to do is to parse
8# its output.
9#
10# Checking out the files is done by a single long-running CVS connection
11# / server process.
12#
13# The head revision is on branch "origin" by default.
14# You can change that with the '-o' option.
15
16use strict;
17use warnings;
Philippe Bruhat (BooKbc434e82008-02-28 11:18:22 +010018use Getopt::Long;
Sven Verdoolaege79ee4562005-07-04 13:36:59 +020019use File::Spec;
Martin Langhoff7ccd9002006-06-24 23:13:08 +120020use File::Temp qw(tempfile tmpnam);
Matthias Urlichsa57a9492005-06-28 16:48:40 +020021use File::Path qw(mkpath);
22use File::Basename qw(basename dirname);
23use Time::Local;
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +020024use IO::Socket;
25use IO::Pipe;
Jeff Kinge49289d2006-05-24 09:58:28 -040026use POSIX qw(strftime dup2 ENOENT);
H. Peter Anvin0d821d42005-09-06 10:36:01 -070027use IPC::Open2;
Matthias Urlichsa57a9492005-06-28 16:48:40 +020028
29$SIG{'PIPE'}="IGNORE";
30$ENV{'TZ'}="UTC";
31
Philippe Bruhat (BooKbc434e82008-02-28 11:18:22 +010032our ($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);
Andreas Ericssonffd97f32006-01-13 00:38:59 +010033my (%conv_author_name, %conv_author_email);
Matthias Urlichsa57a9492005-06-28 16:48:40 +020034
Frank Lichtenheld7bf77642007-04-06 23:52:41 +020035sub usage(;$) {
36 my $msg = shift;
37 print(STDERR "Error: $msg\n") if $msg;
Matthias Urlichsa57a9492005-06-28 16:48:40 +020038 print STDERR <<END;
Stephan Beyer1b1dd232008-07-13 15:36:15 +020039Usage: git cvsimport # fetch/update GIT from CVS
Andreas Ericssonffd97f32006-01-13 00:38:59 +010040 [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
Frank Lichtenheldedbe4462007-04-06 23:52:39 +020041 [-p opts-for-cvsps] [-P file] [-C GIT_repository] [-z fuzz] [-i] [-k]
42 [-u] [-s subst] [-a] [-m] [-M regex] [-S regex] [-L commitlimit]
Andy Whitcroftcbc9be52007-06-04 10:01:34 +010043 [-r remote] [CVS_module]
Matthias Urlichsa57a9492005-06-28 16:48:40 +020044END
45 exit(1);
Tommy M. McGuire9718a002005-06-10 01:38:32 -050046}
47
Andreas Ericssonffd97f32006-01-13 00:38:59 +010048sub read_author_info($) {
49 my ($file) = @_;
50 my $user;
51 open my $f, '<', "$file" or die("Failed to open $file: $!\n");
52
53 while (<$f>) {
Junio C Hamano8cd16212006-01-15 03:30:30 -080054 # Expected format is this:
Andreas Ericssonffd97f32006-01-13 00:38:59 +010055 # exon=Andreas Ericsson <ae@op5.se>
Junio C Hamano8cd16212006-01-15 03:30:30 -080056 if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
Andreas Ericssonffd97f32006-01-13 00:38:59 +010057 $user = $1;
Junio C Hamano8cd16212006-01-15 03:30:30 -080058 $conv_author_name{$user} = $2;
59 $conv_author_email{$user} = $3;
Andreas Ericssonffd97f32006-01-13 00:38:59 +010060 }
Junio C Hamano8cd16212006-01-15 03:30:30 -080061 # However, we also read from CVSROOT/users format
62 # to ease migration.
63 elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
64 my $mapped;
65 ($user, $mapped) = ($1, $3);
66 if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
67 $conv_author_name{$user} = $1;
68 $conv_author_email{$user} = $2;
69 }
70 elsif ($mapped =~ /^<?(.*)>?$/) {
71 $conv_author_name{$user} = $user;
72 $conv_author_email{$user} = $1;
73 }
74 }
75 # NEEDSWORK: Maybe warn on unrecognized lines?
Andreas Ericssonffd97f32006-01-13 00:38:59 +010076 }
77 close ($f);
78}
79
80sub write_author_info($) {
81 my ($file) = @_;
82 open my $f, '>', $file or
83 die("Failed to open $file for writing: $!");
84
85 foreach (keys %conv_author_name) {
Junio C Hamano8cd16212006-01-15 03:30:30 -080086 print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>\n";
Andreas Ericssonffd97f32006-01-13 00:38:59 +010087 }
88 close ($f);
89}
90
Dan McGeecfc44a12008-01-13 22:51:10 -060091# convert getopts specs for use by git config
James Bowesed35dec2007-02-07 17:57:43 -050092sub read_repo_config {
93 # Split the string between characters, unless there is a ':'
94 # So "abc:de" becomes ["a", "b", "c:", "d", "e"]
95 my @opts = split(/ *(?!:)/, shift);
96 foreach my $o (@opts) {
97 my $key = $o;
98 $key =~ s/://g;
Dan McGeecfc44a12008-01-13 22:51:10 -060099 my $arg = 'git config';
James Bowesed35dec2007-02-07 17:57:43 -0500100 $arg .= ' --bool' if ($o !~ /:$/);
101
102 chomp(my $tmp = `$arg --get cvsimport.$key`);
103 if ($tmp && !($arg =~ /--bool/ && $tmp eq 'false')) {
104 no strict 'refs';
105 my $opt_name = "opt_" . $key;
106 if (!$$opt_name) {
107 $$opt_name = $tmp;
108 }
109 }
110 }
James Bowesed35dec2007-02-07 17:57:43 -0500111}
112
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +0100113my $opts = "haivmkuo:d:p:r:C:z:s:M:P:A:S:L:";
James Bowesed35dec2007-02-07 17:57:43 -0500114read_repo_config($opts);
Philippe Bruhat (BooKbc434e82008-02-28 11:18:22 +0100115Getopt::Long::Configure( 'no_ignore_case', 'bundling' );
116
117# turn the Getopt::Std specification in a Getopt::Long one,
118# with support for multiple -M options
119GetOptions( map { s/:/=s/; /M/ ? "$_\@" : $_ } split( /(?!:)/, $opts ) )
120 or usage();
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200121usage if $opt_h;
Linus Torvaldsd4f8b392005-06-07 15:11:28 -0700122
Jeff King67d23242007-11-30 17:22:12 -0500123if (@ARGV == 0) {
Dan McGeecfc44a12008-01-13 22:51:10 -0600124 chomp(my $module = `git config --get cvsimport.module`);
Jeff King67d23242007-11-30 17:22:12 -0500125 push(@ARGV, $module) if $? == 0;
126}
Frank Lichtenheld7bf77642007-04-06 23:52:41 +0200127@ARGV <= 1 or usage("You can't specify more than one CVS module");
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200128
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800129if ($opt_d) {
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200130 $ENV{"CVSROOT"} = $opt_d;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800131} elsif (-f 'CVS/Root') {
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200132 open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
133 $opt_d = <$f>;
134 chomp $opt_d;
135 close $f;
136 $ENV{"CVSROOT"} = $opt_d;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800137} elsif ($ENV{"CVSROOT"}) {
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200138 $opt_d = $ENV{"CVSROOT"};
139} else {
Frank Lichtenheld7bf77642007-04-06 23:52:41 +0200140 usage("CVSROOT needs to be set");
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200141}
Johannes Schindelinfbfd60d2005-08-17 11:19:20 +0200142$opt_s ||= "-";
Martin Langhoffded9f402007-01-08 19:43:39 +1300143$opt_a ||= 0;
144
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200145my $git_tree = $opt_C;
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200146$git_tree ||= ".";
147
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +0100148my $remote;
149if (defined $opt_r) {
150 $remote = 'refs/remotes/' . $opt_r;
151 $opt_o ||= "master";
152} else {
153 $opt_o ||= "origin";
154 $remote = 'refs/heads';
155}
156
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200157my $cvs_tree;
158if ($#ARGV == 0) {
159 $cvs_tree = $ARGV[0];
160} elsif (-f 'CVS/Repository') {
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700161 open my $f, '<', 'CVS/Repository' or
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200162 die 'Failed to open CVS/Repository';
163 $cvs_tree = <$f>;
164 chomp $cvs_tree;
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200165 close $f;
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200166} else {
Frank Lichtenheld7bf77642007-04-06 23:52:41 +0200167 usage("CVS module has to be specified");
Sven Verdoolaegef9714a42005-07-03 11:34:59 +0200168}
169
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200170our @mergerx = ();
171if ($opt_m) {
Philippe Bruhat (BooKfbbbc362008-02-28 11:18:21 +0100172 @mergerx = ( qr/\b(?:from|of|merge|merging|merged) ([-\w]+)/i );
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200173}
Philippe Bruhat (BooKbc434e82008-02-28 11:18:22 +0100174if (@opt_M) {
175 push (@mergerx, map { qr/$_/ } @opt_M);
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200176}
177
Martin Langhoff62119882007-01-08 14:11:23 +1300178# Remember UTC of our starting time
179# we'll want to avoid importing commits
180# that are too recent
181our $starttime = time();
182
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200183select(STDERR); $|=1; select(STDOUT);
184
185
186package CVSconn;
187# Basic CVS dialog.
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200188# We're only interested in connecting and downloading, so ...
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200189
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200190use File::Spec;
191use File::Temp qw(tempfile);
Matthias Urlichsf65ae602005-06-28 19:58:40 +0200192use POSIX qw(strftime dup2);
193
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200194sub new {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800195 my ($what,$repo,$subdir) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200196 $what=ref($what) if ref($what);
197
198 my $self = {};
199 $self->{'buffer'} = "";
200 bless($self,$what);
201
202 $repo =~ s#/+$##;
203 $self->{'fullrep'} = $repo;
204 $self->conn();
205
206 $self->{'subdir'} = $subdir;
207 $self->{'lines'} = undef;
208
209 return $self;
Linus Torvaldsd4f8b392005-06-07 15:11:28 -0700210}
211
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200212sub conn {
213 my $self = shift;
214 my $repo = $self->{'fullrep'};
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800215 if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
216 my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100217
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800218 my ($proxyhost,$proxyport);
219 if ($param && ($param =~ m/proxy=([^;]+)/)) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100220 $proxyhost = $1;
221 # Default proxyport, if not specified, is 8080.
222 $proxyport = 8080;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800223 if ($ENV{"CVS_PROXY_PORT"}) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100224 $proxyport = $ENV{"CVS_PROXY_PORT"};
225 }
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800226 if ($param =~ m/proxyport=([^;]+)/) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100227 $proxyport = $1;
228 }
229 }
Philippe Bruhat (BooK)8c372fb2008-06-10 14:32:06 +0200230 $repo ||= '/';
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100231
Gordon Hopper2e458e02007-11-08 13:15:20 -0700232 # if username is not explicit in CVSROOT, then use current user, as cvs would
233 $user=(getlogin() || $ENV{'LOGNAME'} || $ENV{'USER'} || "anonymous") unless $user;
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200234 my $rr2 = "-";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800235 unless ($port) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200236 $rr2 = ":pserver:$user\@$serv:$repo";
237 $port=2401;
238 }
239 my $rr = ":pserver:$user\@$serv:$port$repo";
Linus Torvaldsd4f8b392005-06-07 15:11:28 -0700240
Pascal Obry3fb9d582009-09-04 13:58:32 +0200241 if ($pass) {
242 $pass = $self->_scramble($pass);
243 } else {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200244 open(H,$ENV{'HOME'}."/.cvspass") and do {
245 # :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800246 while (<H>) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200247 chomp;
248 s/^\/\d+\s+//;
249 my ($w,$p) = split(/\s/,$_,2);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800250 if ($w eq $rr or $w eq $rr2) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200251 $pass = $p;
252 last;
253 }
254 }
255 };
Clemens Buchachere481b1d2009-09-17 09:21:02 +0200256 $pass = "A" unless $pass;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200257 }
Dirk Hoernerb2139db2009-08-14 08:58:31 +0200258
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100259 my ($s, $rep);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800260 if ($proxyhost) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100261
262 # Use a HTTP Proxy. Only works for HTTP proxies that
263 # don't require user authentication
264 #
265 # See: http://www.ietf.org/rfc/rfc2817.txt
266
267 $s = IO::Socket::INET->new(PeerHost => $proxyhost, PeerPort => $proxyport);
268 die "Socket to $proxyhost: $!\n" unless defined $s;
269 $s->write("CONNECT $serv:$port HTTP/1.1\r\nHost: $serv:$port\r\n\r\n")
270 or die "Write to $proxyhost: $!\n";
271 $s->flush();
272
273 $rep = <$s>;
274
275 # The answer should look like 'HTTP/1.x 2yy ....'
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800276 if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100277 die "Proxy connect: $rep\n";
278 }
279 # Skip up to the empty line of the proxy server output
280 # including the response headers.
281 while ($rep = <$s>) {
282 last if (!defined $rep ||
283 $rep eq "\n" ||
284 $rep eq "\r\n");
285 }
286 } else {
287 $s = IO::Socket::INET->new(PeerHost => $serv, PeerPort => $port);
288 die "Socket to $serv: $!\n" unless defined $s;
289 }
290
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200291 $s->write("BEGIN AUTH REQUEST\n$repo\n$user\n$pass\nEND AUTH REQUEST\n")
292 or die "Write to $serv: $!\n";
293 $s->flush();
294
Iñaki Arenaza73bcf532006-11-22 23:26:57 +0100295 $rep = <$s>;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200296
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800297 if ($rep ne "I LOVE YOU\n") {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200298 $rep="<unknown>" unless $rep;
299 die "AuthReply: $rep\n";
300 }
301 $self->{'socketo'} = $s;
302 $self->{'socketi'} = $s;
Sven Verdoolaege34155392005-07-03 13:02:06 +0200303 } else { # local or ext: Fork off our own cvs server.
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200304 my $pr = IO::Pipe->new();
305 my $pw = IO::Pipe->new();
306 my $pid = fork();
307 die "Fork: $!\n" unless defined $pid;
Sven Verdoolaege8d0ea312005-07-03 12:26:51 +0200308 my $cvs = 'cvs';
309 $cvs = $ENV{CVS_SERVER} if exists $ENV{CVS_SERVER};
Sven Verdoolaege34155392005-07-03 13:02:06 +0200310 my $rsh = 'rsh';
311 $rsh = $ENV{CVS_RSH} if exists $ENV{CVS_RSH};
312
313 my @cvs = ($cvs, 'server');
314 my ($local, $user, $host);
315 $local = $repo =~ s/:local://;
316 if (!$local) {
317 $repo =~ s/:ext://;
318 $local = !($repo =~ s/^(?:([^\@:]+)\@)?([^:]+)://);
319 ($user, $host) = ($1, $2);
320 }
321 if (!$local) {
322 if ($user) {
323 unshift @cvs, $rsh, '-l', $user, $host;
324 } else {
325 unshift @cvs, $rsh, $host;
326 }
327 }
328
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800329 unless ($pid) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200330 $pr->writer();
331 $pw->reader();
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200332 dup2($pw->fileno(),0);
333 dup2($pr->fileno(),1);
334 $pr->close();
335 $pw->close();
Sven Verdoolaege34155392005-07-03 13:02:06 +0200336 exec(@cvs);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200337 }
338 $pw->writer();
339 $pr->reader();
340 $self->{'socketo'} = $pw;
341 $self->{'socketi'} = $pr;
342 }
343 $self->{'socketo'}->write("Root $repo\n");
344
345 # Trial and error says that this probably is the minimum set
iso-8859-1?Q?David_K=E5gedalb0921332005-08-15 20:18:25 +0200346 $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200347
348 $self->{'socketo'}->write("valid-requests\n");
349 $self->{'socketo'}->flush();
350
351 chomp(my $rep=$self->readline());
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800352 if ($rep !~ s/^Valid-requests\s*//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200353 $rep="<unknown>" unless $rep;
354 die "Expected Valid-requests from server, but got: $rep\n";
355 }
356 chomp(my $res=$self->readline());
357 die "validReply: $res\n" if $res ne "ok";
358
359 $self->{'socketo'}->write("UseUnchanged\n") if $rep =~ /\bUseUnchanged\b/;
360 $self->{'repo'} = $repo;
361}
362
363sub readline {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800364 my ($self) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200365 return $self->{'socketi'}->getline();
366}
367
368sub _file {
369 # Request a file with a given revision.
370 # Trial and error says this is a good way to do it. :-/
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800371 my ($self,$fn,$rev) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200372 $self->{'socketo'}->write("Argument -N\n") or return undef;
373 $self->{'socketo'}->write("Argument -P\n") or return undef;
Martin Langhoffabe05822005-08-16 17:39:29 +1200374 # -kk: Linus' version doesn't use it - defaults to off
375 if ($opt_k) {
376 $self->{'socketo'}->write("Argument -kk\n") or return undef;
377 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200378 $self->{'socketo'}->write("Argument -r\n") or return undef;
379 $self->{'socketo'}->write("Argument $rev\n") or return undef;
380 $self->{'socketo'}->write("Argument --\n") or return undef;
381 $self->{'socketo'}->write("Argument $self->{'subdir'}/$fn\n") or return undef;
382 $self->{'socketo'}->write("Directory .\n") or return undef;
383 $self->{'socketo'}->write("$self->{'repo'}\n") or return undef;
Matthias Urlichs4f7c0ca2005-06-30 12:19:48 +0200384 # $self->{'socketo'}->write("Sticky T1.0\n") or return undef;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200385 $self->{'socketo'}->write("co\n") or return undef;
386 $self->{'socketo'}->flush() or return undef;
387 $self->{'lines'} = 0;
388 return 1;
389}
390sub _line {
391 # Read a line from the server.
392 # ... except that 'line' may be an entire file. ;-)
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800393 my ($self, $fh) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200394 die "Not in lines" unless defined $self->{'lines'};
395
396 my $line;
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200397 my $res=0;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800398 while (defined($line = $self->readline())) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200399 # M U gnupg-cvs-rep/AUTHORS
400 # Updated gnupg-cvs-rep/
401 # /daten/src/rsync/gnupg-cvs-rep/AUTHORS
402 # /AUTHORS/1.1///T1.1
403 # u=rw,g=rw,o=rw
404 # 0
405 # ok
406
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800407 if ($line =~ s/^(?:Created|Updated) //) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200408 $line = $self->readline(); # path
409 $line = $self->readline(); # Entries line
410 my $mode = $self->readline(); chomp $mode;
411 $self->{'mode'} = $mode;
412 defined (my $cnt = $self->readline())
413 or die "EOF from server after 'Changed'\n";
414 chomp $cnt;
415 die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
416 $line="";
Martin Langhoff55cad842006-05-23 20:08:58 +1200417 $res = $self->_fetchfile($fh, $cnt);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800418 } elsif ($line =~ s/^ //) {
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200419 print $fh $line;
420 $res += length($line);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800421 } elsif ($line =~ /^M\b/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200422 # output, do nothing
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800423 } elsif ($line =~ /^Mbinary\b/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200424 my $cnt;
425 die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
426 chomp $cnt;
427 die "Duh: Mbinary $cnt" if $cnt !~ /^\d+$/ or $cnt<1;
428 $line="";
Martin Langhoff55cad842006-05-23 20:08:58 +1200429 $res += $self->_fetchfile($fh, $cnt);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200430 } else {
431 chomp $line;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800432 if ($line eq "ok") {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200433 # print STDERR "S: ok (".length($res).")\n";
434 return $res;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800435 } elsif ($line =~ s/^E //) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200436 # print STDERR "S: $line\n";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800437 } elsif ($line =~ /^(Remove-entry|Removed) /i) {
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200438 $line = $self->readline(); # filename
439 $line = $self->readline(); # OK
440 chomp $line;
441 die "Unknown: $line" if $line ne "ok";
442 return -1;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200443 } else {
444 die "Unknown: $line\n";
445 }
446 }
447 }
Martin Mares39ba7d52006-02-18 21:44:20 +0100448 return undef;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200449}
450sub file {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800451 my ($self,$fn,$rev) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200452 my $res;
453
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700454 my ($fh, $name) = tempfile('gitcvs.XXXXXX',
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200455 DIR => File::Spec->tmpdir(), UNLINK => 1);
456
457 $self->_file($fn,$rev) and $res = $self->_line($fh);
458
459 if (!defined $res) {
Martin Mares39ba7d52006-02-18 21:44:20 +0100460 print STDERR "Server has gone away while fetching $fn $rev, retrying...\n";
461 truncate $fh, 0;
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200462 $self->conn();
Martin Mares39ba7d52006-02-18 21:44:20 +0100463 $self->_file($fn,$rev) or die "No file command send";
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200464 $res = $self->_line($fh);
Martin Mares39ba7d52006-02-18 21:44:20 +0100465 die "Retry failed" unless defined $res;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200466 }
Sven Verdoolaegec619ad52005-07-06 08:37:12 +0200467 close ($fh);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200468
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200469 return ($name, $res);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200470}
Martin Langhoff55cad842006-05-23 20:08:58 +1200471sub _fetchfile {
472 my ($self, $fh, $cnt) = @_;
Junio C Hamano61efa5e2006-05-23 16:30:39 -0700473 my $res = 0;
Martin Langhoff55cad842006-05-23 20:08:58 +1200474 my $bufsize = 1024 * 1024;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800475 while ($cnt) {
Martin Langhoff55cad842006-05-23 20:08:58 +1200476 if ($bufsize > $cnt) {
477 $bufsize = $cnt;
478 }
479 my $buf;
480 my $num = $self->{'socketi'}->read($buf,$bufsize);
481 die "Server: Filesize $cnt: $num: $!\n" if not defined $num or $num<=0;
482 print $fh $buf;
483 $res += $num;
484 $cnt -= $num;
485 }
486 return $res;
487}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200488
Dirk Hoernerb2139db2009-08-14 08:58:31 +0200489sub _scramble {
490 my ($self, $pass) = @_;
491 my $scrambled = "A";
492
493 return $scrambled unless $pass;
494
495 my $pass_len = length($pass);
496 my @pass_arr = split("", $pass);
497 my $i;
498
499 # from cvs/src/scramble.c
500 my @shifts = (
501 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
502 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
503 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
504 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
505 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
506 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
507 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
508 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
509 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
510 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
511 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
512 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
513 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
514 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
515 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
516 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
517 );
518
519 for ($i = 0; $i < $pass_len; $i++) {
520 $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
521 }
522
523 return $scrambled;
524}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200525
526package main;
527
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200528my $cvs = CVSconn->new($opt_d, $cvs_tree);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200529
530
531sub pdate($) {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800532 my ($d) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200533 m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
534 or die "Unparseable date: $d\n";
535 my $y=$1; $y-=1900 if $y>1900;
536 return timegm($6||0,$5,$4,$3,$2-1,$y);
537}
538
539sub pmode($) {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800540 my ($mode) = @_;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200541 my $m = 0;
542 my $mm = 0;
543 my $um = 0;
544 for my $x(split(//,$mode)) {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800545 if ($x eq ",") {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200546 $m |= $mm&$um;
547 $mm = 0;
548 $um = 0;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800549 } elsif ($x eq "u") { $um |= 0700;
550 } elsif ($x eq "g") { $um |= 0070;
551 } elsif ($x eq "o") { $um |= 0007;
552 } elsif ($x eq "r") { $mm |= 0444;
553 } elsif ($x eq "w") { $mm |= 0222;
554 } elsif ($x eq "x") { $mm |= 0111;
555 } elsif ($x eq "=") { # do nothing
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200556 } else { die "Unknown mode: $mode\n";
557 }
558 }
559 $m |= $mm&$um;
560 return $m;
561}
562
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200563sub getwd() {
564 my $pwd = `pwd`;
565 chomp $pwd;
566 return $pwd;
567}
568
Jeff Kinge73aefe2006-05-23 03:27:46 -0400569sub is_sha1 {
570 my $s = shift;
571 return $s =~ /^[a-f0-9]{40}$/;
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200572}
573
Jeff King9da0dab2007-11-28 13:56:11 -0500574sub get_headref ($) {
575 my $name = shift;
576 my $r = `git rev-parse --verify '$name' 2>/dev/null`;
577 return undef unless $? == 0;
578 chomp $r;
579 return $r;
Jeff Kinge73aefe2006-05-23 03:27:46 -0400580}
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200581
Jeff Kingf6fdbb62009-10-19 02:49:55 -0400582my $user_filename_prepend = '';
583sub munge_user_filename {
584 my $name = shift;
585 return File::Spec->file_name_is_absolute($name) ?
586 $name :
587 $user_filename_prepend . $name;
588}
589
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200590-d $git_tree
591 or mkdir($git_tree,0777)
592 or die "Could not create $git_tree: $!";
Jeff Kingf6fdbb62009-10-19 02:49:55 -0400593if ($git_tree ne '.') {
594 $user_filename_prepend = getwd() . '/';
595 chdir($git_tree);
596}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200597
598my $last_branch = "";
Matthias Urlichs46541662005-06-28 21:08:15 +0200599my $orig_branch = "";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200600my %branch_date;
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -0800601my $tip_at_start = undef;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200602
603my $git_dir = $ENV{"GIT_DIR"} || ".git";
604$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#;
605$ENV{"GIT_DIR"} = $git_dir;
Sven Verdoolaege79ee4562005-07-04 13:36:59 +0200606my $orig_git_index;
607$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
Martin Langhoff8f732642006-06-12 23:50:49 +1200608
609my %index; # holds filenames of one index per branch
Johannes Schindelin061303f2006-06-24 21:42:20 +0200610
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800611unless (-d $git_dir) {
Nicolas Pitre5c94f872007-01-12 16:01:46 -0500612 system("git-init");
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200613 die "Cannot init the GIT db at $git_tree: $?\n" if $?;
614 system("git-read-tree");
615 die "Cannot init an empty tree: $?\n" if $?;
616
617 $last_branch = $opt_o;
Matthias Urlichs46541662005-06-28 21:08:15 +0200618 $orig_branch = "";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200619} else {
Pavel Roskin8366a102005-11-16 13:27:28 -0500620 open(F, "git-symbolic-ref HEAD |") or
621 die "Cannot run git-symbolic-ref: $!\n";
622 chomp ($last_branch = <F>);
623 $last_branch = basename($last_branch);
624 close(F);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800625 unless ($last_branch) {
Matthias Urlichs46541662005-06-28 21:08:15 +0200626 warn "Cannot read the last branch name: $! -- assuming 'master'\n";
627 $last_branch = "master";
628 }
629 $orig_branch = $last_branch;
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -0800630 $tip_at_start = `git-rev-parse --verify HEAD`;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200631
632 # Get the last import timestamps
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100633 my $fmt = '($ref, $author) = (%(refname), %(author));';
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +0100634 open(H, "git-for-each-ref --perl --format='$fmt' $remote |") or
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100635 die "Cannot run git-for-each-ref: $!\n";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800636 while (defined(my $entry = <H>)) {
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100637 my ($ref, $author);
638 eval($entry) || die "cannot eval refs list: $@";
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +0100639 my ($head) = ($ref =~ m|^$remote/(.*)|);
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100640 $author =~ /^.*\s(\d+)\s[-+]\d{4}$/;
641 $branch_date{$head} = $1;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200642 }
Andy Whitcroft1f24c582006-09-20 17:37:04 +0100643 close(H);
Stephan Springl7ca055f2007-05-23 12:13:21 +0100644 if (!exists $branch_date{$opt_o}) {
645 die "Branch '$opt_o' does not exist.\n".
646 "Either use the correct '-o branch' option,\n".
647 "or import to a new repository.\n";
648 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200649}
650
651-d $git_dir
652 or die "Could not create git subdir ($git_dir).\n";
653
Andreas Ericssonffd97f32006-01-13 00:38:59 +0100654# now we read (and possibly save) author-info as well
655-f "$git_dir/cvs-authors" and
656 read_author_info("$git_dir/cvs-authors");
657if ($opt_A) {
Jeff Kingf6fdbb62009-10-19 02:49:55 -0400658 read_author_info(munge_user_filename($opt_A));
Andreas Ericssonffd97f32006-01-13 00:38:59 +0100659 write_author_info("$git_dir/cvs-authors");
660}
661
Martin Langhoff2f57c692006-06-11 20:12:20 +1200662
663#
664# run cvsps into a file unless we are getting
665# it passed as a file via $opt_P
666#
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300667my $cvspsfile;
Martin Langhoff2f57c692006-06-11 20:12:20 +1200668unless ($opt_P) {
669 print "Running cvsps...\n" if $opt_v;
670 my $pid = open(CVSPS,"-|");
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300671 my $cvspsfh;
Martin Langhoff2f57c692006-06-11 20:12:20 +1200672 die "Cannot fork: $!\n" unless defined $pid;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800673 unless ($pid) {
Martin Langhoff2f57c692006-06-11 20:12:20 +1200674 my @opt;
675 @opt = split(/,/,$opt_p) if defined $opt_p;
676 unshift @opt, '-z', $opt_z if defined $opt_z;
677 unshift @opt, '-q' unless defined $opt_v;
678 unless (defined($opt_p) && $opt_p =~ m/--no-cvs-direct/) {
679 push @opt, '--cvs-direct';
680 }
681 exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
682 die "Could not start cvsps: $!\n";
Martin Langhoffdf73e9c2005-10-11 21:57:04 -0700683 }
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300684 ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
685 DIR => File::Spec->tmpdir());
Martin Langhoff2f57c692006-06-11 20:12:20 +1200686 while (<CVSPS>) {
687 print $cvspsfh $_;
Martin Langhoff211dcac2005-11-02 13:48:47 +1300688 }
Martin Langhoff2f57c692006-06-11 20:12:20 +1200689 close CVSPS;
Jeff King3a969ef2007-12-23 22:08:19 -0500690 $? == 0 or die "git-cvsimport: fatal: cvsps reported error\n";
Martin Langhoff2f57c692006-06-11 20:12:20 +1200691 close $cvspsfh;
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300692} else {
Jeff Kingf6fdbb62009-10-19 02:49:55 -0400693 $cvspsfile = munge_user_filename($opt_P);
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200694}
695
Martin Langhoff4083c2f2007-01-08 21:08:46 +1300696open(CVS, "<$cvspsfile") or die $!;
Martin Langhoff2f57c692006-06-11 20:12:20 +1200697
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200698## cvsps output:
699#---------------------
700#PatchSet 314
701#Date: 1999/09/18 13:03:59
702#Author: wkoch
703#Branch: STABLE-BRANCH-1-0
704#Ancestor branch: HEAD
705#Tag: (none)
706#Log:
707# See ChangeLog: Sat Sep 18 13:03:28 CEST 1999 Werner Koch
708#Members:
709# README:1.57->1.57.2.1
710# VERSION:1.96->1.96.2.1
711#
712#---------------------
713
714my $state = 0;
715
Jeff Kinge73aefe2006-05-23 03:27:46 -0400716sub update_index (\@\@) {
717 my $old = shift;
718 my $new = shift;
Jeff King6a1871e2006-05-23 03:27:45 -0400719 open(my $fh, '|-', qw(git-update-index -z --index-info))
720 or die "unable to open git-update-index: $!";
721 print $fh
722 (map { "0 0000000000000000000000000000000000000000\t$_\0" }
Jeff Kinge73aefe2006-05-23 03:27:46 -0400723 @$old),
Jeff King6a1871e2006-05-23 03:27:45 -0400724 (map { '100' . sprintf('%o', $_->[0]) . " $_->[1]\t$_->[2]\0" }
Jeff Kinge73aefe2006-05-23 03:27:46 -0400725 @$new)
Jeff King6a1871e2006-05-23 03:27:45 -0400726 or die "unable to write to git-update-index: $!";
727 close $fh
728 or die "unable to write to git-update-index: $!";
729 $? and die "git-update-index reported error: $?";
Jeff Kinge73aefe2006-05-23 03:27:46 -0400730}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200731
Jeff Kinge73aefe2006-05-23 03:27:46 -0400732sub write_tree () {
733 open(my $fh, '-|', qw(git-write-tree))
734 or die "unable to open git-write-tree: $!";
735 chomp(my $tree = <$fh>);
736 is_sha1($tree)
737 or die "Cannot get tree id ($tree): $!";
738 close($fh)
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200739 or die "Error running git-write-tree: $?\n";
740 print "Tree ID $tree\n" if $opt_v;
Jeff Kinge73aefe2006-05-23 03:27:46 -0400741 return $tree;
742}
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200743
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800744my ($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
745my (@old,@new,@skipped,%ignorebranch);
Martin Langhoff71b08142006-06-11 20:12:09 +1200746
747# commits that cvsps cannot place anywhere...
748$ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
749
Jeff Kinge73aefe2006-05-23 03:27:46 -0400750sub commit {
Jeff King9da0dab2007-11-28 13:56:11 -0500751 if ($branch eq $opt_o && !$index{branch} &&
752 !get_headref("$remote/$branch")) {
Martin Langhoffc5f448b2006-06-28 22:13:23 +1200753 # looks like an initial commit
Nicolas Pitre5c94f872007-01-12 16:01:46 -0500754 # use the index primed by git-init
Michael Milligan23fcdc72007-06-05 00:06:30 -0600755 $ENV{GIT_INDEX_FILE} = "$git_dir/index";
756 $index{$branch} = "$git_dir/index";
Martin Langhoffc5f448b2006-06-28 22:13:23 +1200757 } else {
758 # use an index per branch to speed up
759 # imports of projects with many branches
760 unless ($index{$branch}) {
761 $index{$branch} = tmpnam();
762 $ENV{GIT_INDEX_FILE} = $index{$branch};
763 if ($ancestor) {
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +0100764 system("git-read-tree", "$remote/$ancestor");
Martin Langhoffc5f448b2006-06-28 22:13:23 +1200765 } else {
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +0100766 system("git-read-tree", "$remote/$branch");
Martin Langhoffc5f448b2006-06-28 22:13:23 +1200767 }
768 die "read-tree failed: $?\n" if $?;
769 }
770 }
771 $ENV{GIT_INDEX_FILE} = $index{$branch};
772
Jeff Kinge73aefe2006-05-23 03:27:46 -0400773 update_index(@old, @new);
774 @old = @new = ();
775 my $tree = write_tree();
Jeff King9da0dab2007-11-28 13:56:11 -0500776 my $parent = get_headref("$remote/$last_branch");
Jeff Kinge73aefe2006-05-23 03:27:46 -0400777 print "Parent ID " . ($parent ? $parent : "(empty)") . "\n" if $opt_v;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200778
Jeff Kinge73aefe2006-05-23 03:27:46 -0400779 my @commit_args;
780 push @commit_args, ("-p", $parent) if $parent;
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200781
Jeff Kinge73aefe2006-05-23 03:27:46 -0400782 # loose detection of merges
783 # based on the commit msg
784 foreach my $rx (@mergerx) {
785 next unless $logmsg =~ $rx && $1;
786 my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
Jeff King9da0dab2007-11-28 13:56:11 -0500787 if (my $sha1 = get_headref("$remote/$mparent")) {
Marc-Andre Lureauc36c5b82008-03-12 21:54:21 +0200788 push @commit_args, '-p', "$remote/$mparent";
Jeff Kinge73aefe2006-05-23 03:27:46 -0400789 print "Merge parent branch: $mparent\n" if $opt_v;
Martin Langhoffdb4b6582005-08-16 22:35:27 +1200790 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200791 }
Jeff Kinge73aefe2006-05-23 03:27:46 -0400792
793 my $commit_date = strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date));
Jeff King62bf0d92006-05-23 16:59:44 -0400794 $ENV{GIT_AUTHOR_NAME} = $author_name;
795 $ENV{GIT_AUTHOR_EMAIL} = $author_email;
796 $ENV{GIT_AUTHOR_DATE} = $commit_date;
797 $ENV{GIT_COMMITTER_NAME} = $author_name;
798 $ENV{GIT_COMMITTER_EMAIL} = $author_email;
799 $ENV{GIT_COMMITTER_DATE} = $commit_date;
Jeff Kinge73aefe2006-05-23 03:27:46 -0400800 my $pid = open2(my $commit_read, my $commit_write,
Jeff Kinge73aefe2006-05-23 03:27:46 -0400801 'git-commit-tree', $tree, @commit_args);
Matthias Urlichse3710462005-06-30 22:09:42 +0200802
803 # compatibility with git2cvs
804 substr($logmsg,32767) = "" if length($logmsg) > 32767;
805 $logmsg =~ s/[\s\n]+\z//;
806
Martin Langhoff5179c8a2006-01-30 19:12:41 +1300807 if (@skipped) {
808 $logmsg .= "\n\n\nSKIPPED:\n\t";
809 $logmsg .= join("\n\t", @skipped) . "\n";
Martin Langhofff396f012006-05-23 00:45:47 +1200810 @skipped = ();
Martin Langhoff5179c8a2006-01-30 19:12:41 +1300811 }
812
Jeff Kinge73aefe2006-05-23 03:27:46 -0400813 print($commit_write "$logmsg\n") && close($commit_write)
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200814 or die "Error writing to git-commit-tree: $!\n";
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200815
Jeff Kinge73aefe2006-05-23 03:27:46 -0400816 print "Committed patch $patchset ($branch $commit_date)\n" if $opt_v;
817 chomp(my $cid = <$commit_read>);
818 is_sha1($cid) or die "Cannot get commit id ($cid): $!\n";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200819 print "Commit ID $cid\n" if $opt_v;
Jeff Kinge73aefe2006-05-23 03:27:46 -0400820 close($commit_read);
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200821
822 waitpid($pid,0);
823 die "Error running git-commit-tree: $?\n" if $?;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200824
Jeff Kingb3bb5f72008-04-30 00:36:14 -0400825 system('git-update-ref', "$remote/$branch", $cid) == 0
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200826 or die "Cannot write branch $branch for update: $!\n";
827
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800828 if ($tag) {
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800829 my ($xtag) = $tag;
H. Peter Anvin0d821d42005-09-06 10:36:01 -0700830 $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
831 $xtag =~ tr/_/\./ if ( $opt_u );
Joe English34c99da2006-01-06 12:52:27 -0800832 $xtag =~ s/[\/]/$opt_s/g;
Paul Oliver509792b2008-05-23 19:29:22 +0100833 $xtag =~ s/\[//g;
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700834
Michael Smithee834cf2007-09-07 17:35:07 -0400835 system('git-tag', '-f', $xtag, $cid) == 0
H. Peter Anvin0d821d42005-09-06 10:36:01 -0700836 or die "Cannot create tag $xtag: $!\n";
H. Peter Anvin0d821d42005-09-06 10:36:01 -0700837
838 print "Created tag '$xtag' on '$branch'\n" if $opt_v;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200839 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200840};
841
Martin Langhoff06918342006-05-22 23:38:08 +1200842my $commitcount = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800843while (<CVS>) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200844 chomp;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800845 if ($state == 0 and /^-+$/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200846 $state = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800847 } elsif ($state == 0) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200848 $state = 1;
849 redo;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800850 } elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200851 $patchset = 0+$_;
852 $state=2;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800853 } elsif ($state == 2 and s/^Date:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200854 $date = pdate($_);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800855 unless ($date) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200856 print STDERR "Could not parse date: $_\n";
857 $state=0;
858 next;
859 }
860 $state=3;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800861 } elsif ($state == 3 and s/^Author:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200862 s/\s+$//;
Junio C Hamano94c23342005-09-30 01:48:57 -0700863 if (/^(.*?)\s+<(.*)>/) {
864 ($author_name, $author_email) = ($1, $2);
Andreas Ericssonffd97f32006-01-13 00:38:59 +0100865 } elsif ($conv_author_name{$_}) {
866 $author_name = $conv_author_name{$_};
867 $author_email = $conv_author_email{$_};
Junio C Hamano94c23342005-09-30 01:48:57 -0700868 } else {
869 $author_name = $author_email = $_;
870 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200871 $state = 4;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800872 } elsif ($state == 4 and s/^Branch:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200873 s/\s+$//;
Gerrit Papea0554222007-11-03 11:55:02 +0000874 tr/_/\./ if ( $opt_u );
Johannes Schindelinfbfd60d2005-08-17 11:19:20 +0200875 s/[\/]/$opt_s/g;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200876 $branch = $_;
877 $state = 5;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800878 } elsif ($state == 5 and s/^Ancestor branch:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200879 s/\s+$//;
880 $ancestor = $_;
Sven Verdoolaege0fa28242005-06-30 17:23:22 +0200881 $ancestor = $opt_o if $ancestor eq "HEAD";
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200882 $state = 6;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800883 } elsif ($state == 5) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200884 $ancestor = undef;
885 $state = 6;
886 redo;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800887 } elsif ($state == 6 and s/^Tag:\s+//) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200888 s/\s+$//;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800889 if ($_ eq "(none)") {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200890 $tag = undef;
891 } else {
892 $tag = $_;
893 }
894 $state = 7;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800895 } elsif ($state == 7 and /^Log:/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200896 $logmsg = "";
897 $state = 8;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800898 } elsif ($state == 8 and /^Members:/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200899 $branch = $opt_o if $branch eq "HEAD";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800900 if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200901 # skip
Matthias Urlichs9da07f32005-07-03 19:03:30 +0200902 print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200903 $state = 11;
904 next;
905 }
Martin Langhoffded9f402007-01-08 19:43:39 +1300906 if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
Martin Langhoff62119882007-01-08 14:11:23 +1300907 # skip if the commit is too recent
Stefan Sperling77190eb2007-12-21 16:57:26 +0100908 # given that the cvsps default fuzz is 300s, we give ourselves another
Martin Langhoff62119882007-01-08 14:11:23 +1300909 # 300s just in case -- this also prevents skipping commits
910 # due to server clock drift
911 print "skip patchset $patchset: $date too recent\n" if $opt_v;
912 $state = 11;
913 next;
914 }
Martin Langhoff71b08142006-06-11 20:12:09 +1200915 if (exists $ignorebranch{$branch}) {
916 print STDERR "Skipping $branch\n";
917 $state = 11;
918 next;
919 }
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800920 if ($ancestor) {
921 if ($ancestor eq $branch) {
Martin Langhoff71b08142006-06-11 20:12:09 +1200922 print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
923 $ancestor = $opt_o;
924 }
Jeff King0750d752007-11-28 13:56:28 -0500925 if (defined get_headref("$remote/$branch")) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200926 print STDERR "Branch $branch already exists!\n";
927 $state=11;
928 next;
929 }
Jeff King0750d752007-11-28 13:56:28 -0500930 my $id = get_headref("$remote/$ancestor");
931 if (!$id) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200932 print STDERR "Branch $ancestor does not exist!\n";
Martin Langhoff71b08142006-06-11 20:12:09 +1200933 $ignorebranch{$branch} = 1;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200934 $state=11;
935 next;
936 }
Jeff King0750d752007-11-28 13:56:28 -0500937
938 system(qw(git update-ref -m cvsimport),
939 "$remote/$branch", $id);
940 if($? != 0) {
941 print STDERR "Could not create branch $branch\n";
Martin Langhoff71b08142006-06-11 20:12:09 +1200942 $ignorebranch{$branch} = 1;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200943 $state=11;
944 next;
945 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200946 }
Sven Verdoolaege46e63ef2005-07-04 15:28:36 +0200947 $last_branch = $branch if $branch ne $last_branch;
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200948 $state = 9;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800949 } elsif ($state == 8) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200950 $logmsg .= "$_\n";
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800951 } elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200952# VERSION:1.96->1.96.2.1
Matthias Urlichs2a3e1a82005-06-28 19:49:19 +0200953 my $init = ($2 eq "INITIAL");
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200954 my $fn = $1;
Matthias Urlichsf65ae602005-06-28 19:58:40 +0200955 my $rev = $3;
956 $fn =~ s#^/+##;
Martin Langhoff5179c8a2006-01-30 19:12:41 +1300957 if ($opt_S && $fn =~ m/$opt_S/) {
958 print "SKIPPING $fn v $rev\n";
959 push(@skipped, $fn);
960 next;
961 }
962 print "Fetching $fn v $rev\n" if $opt_v;
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200963 my ($tmpname, $size) = $cvs->file($fn,$rev);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800964 if ($size == -1) {
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200965 push(@old,$fn);
966 print "Drop $fn\n" if $opt_v;
967 } else {
968 print "".($init ? "New" : "Update")." $fn: $size bytes\n" if $opt_v;
Junio C Hamanodd274782006-02-20 14:17:28 -0800969 my $pid = open(my $F, '-|');
970 die $! unless defined $pid;
971 if (!$pid) {
972 exec("git-hash-object", "-w", $tmpname)
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200973 or die "Cannot create object: $!\n";
Junio C Hamanodd274782006-02-20 14:17:28 -0800974 }
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200975 my $sha = <$F>;
976 chomp $sha;
977 close $F;
978 my $mode = pmode($cvs->{'mode'});
979 push(@new,[$mode, $sha, $fn]); # may be resurrected!
980 }
Sven Verdoolaege2eb6d822005-07-04 00:43:26 +0200981 unlink($tmpname);
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800982 } elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
Matthias Urlichsf65ae602005-06-28 19:58:40 +0200983 my $fn = $1;
984 $fn =~ s#^/+##;
985 push(@old,$fn);
Matthias Urlichs8b8840e2005-08-15 11:28:19 +0200986 print "Delete $fn\n" if $opt_v;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800987 } elsif ($state == 9 and /^\s*$/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200988 $state = 10;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800989 } elsif (($state == 9 or $state == 10) and /^-+$/) {
Linus Torvalds4adcea92006-05-22 19:28:37 -0700990 $commitcount++;
991 if ($opt_L && $commitcount > $opt_L) {
Martin Langhoff06918342006-05-22 23:38:08 +1200992 last;
993 }
Martin Langhoffc4b16f82006-05-23 00:45:39 +1200994 commit();
Linus Torvalds4adcea92006-05-22 19:28:37 -0700995 if (($commitcount & 1023) == 0) {
996 system("git repack -a -d");
997 }
Matthias Urlichsa57a9492005-06-28 16:48:40 +0200998 $state = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -0800999 } elsif ($state == 11 and /^-+$/) {
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001000 $state = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001001 } elsif (/^-+$/) { # end of unknown-line processing
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001002 $state = 1;
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001003 } elsif ($state != 11) { # ignore stuff when skipping
Jim Meyering3be39992008-08-05 16:54:42 +02001004 print STDERR "* UNKNOWN LINE * $_\n";
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001005 }
1006}
Martin Langhoffc4b16f82006-05-23 00:45:39 +12001007commit() if $branch and $state != 11;
Matthias Urlichsa57a9492005-06-28 16:48:40 +02001008
Martin Langhoff4083c2f2007-01-08 21:08:46 +13001009unless ($opt_P) {
1010 unlink($cvspsfile);
1011}
1012
Jim Meyeringefe4abd2006-11-15 21:15:44 +01001013# The heuristic of repacking every 1024 commits can leave a
1014# lot of unpacked data. If there is more than 1MB worth of
1015# not-packed objects, repack once more.
1016my $line = `git-count-objects`;
1017if ($line =~ /^(\d+) objects, (\d+) kilobytes$/) {
1018 my ($n_objects, $kb) = ($1, $2);
1019 1024 < $kb
1020 and system("git repack -a -d");
1021}
1022
Martin Langhoff8f732642006-06-12 23:50:49 +12001023foreach my $git_index (values %index) {
Michael Milligan23fcdc72007-06-05 00:06:30 -06001024 if ($git_index ne "$git_dir/index") {
Martin Langhoffc5f448b2006-06-28 22:13:23 +12001025 unlink($git_index);
1026 }
Martin Langhoff8f732642006-06-12 23:50:49 +12001027}
Sven Verdoolaege79ee4562005-07-04 13:36:59 +02001028
Sven Verdoolaege210569f2005-07-05 13:19:59 +02001029if (defined $orig_git_index) {
1030 $ENV{GIT_INDEX_FILE} = $orig_git_index;
1031} else {
1032 delete $ENV{GIT_INDEX_FILE};
1033}
1034
Matthias Urlichs46541662005-06-28 21:08:15 +02001035# Now switch back to the branch we were in before all of this happened
Junio C Hamano86d11cf2006-11-27 14:21:30 -08001036if ($orig_branch) {
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -08001037 print "DONE.\n" if $opt_v;
1038 if ($opt_i) {
1039 exit 0;
1040 }
1041 my $tip_at_end = `git-rev-parse --verify HEAD`;
1042 if ($tip_at_start ne $tip_at_end) {
Junio C Hamanocb9594e2006-03-18 02:05:02 -08001043 for ($tip_at_start, $tip_at_end) { chomp; }
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -08001044 print "Fetched into the current branch.\n" if $opt_v;
1045 system(qw(git-read-tree -u -m),
1046 $tip_at_start, $tip_at_end);
1047 die "Fast-forward update failed: $?\n" if $?;
1048 }
1049 else {
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +01001050 system(qw(git-merge cvsimport HEAD), "$remote/$opt_o");
Junio C Hamano8a5f2ea2006-03-17 14:08:39 -08001051 die "Could not merge $opt_o into the current branch.\n" if $?;
1052 }
Matthias Urlichs46541662005-06-28 21:08:15 +02001053} else {
1054 $orig_branch = "master";
1055 print "DONE; creating $orig_branch branch\n" if $opt_v;
Andy Whitcroft8b7f5fc2007-05-30 01:56:41 +01001056 system("git-update-ref", "refs/heads/master", "$remote/$opt_o")
Jeff King0750d752007-11-28 13:56:28 -05001057 unless defined get_headref('refs/heads/master');
Andy Whitcroft06baffd2007-06-04 10:01:49 +01001058 system("git-symbolic-ref", "$remote/HEAD", "$remote/$opt_o")
1059 if ($opt_r && $opt_o ne 'HEAD');
Pavel Roskin8366a102005-11-16 13:27:28 -05001060 system('git-update-ref', 'HEAD', "$orig_branch");
Sven Verdoolaegec1c774e2005-07-11 16:57:49 +02001061 unless ($opt_i) {
Gerrit Pape7051c3b2007-06-28 11:12:07 +00001062 system('git checkout -f');
Sven Verdoolaegec1c774e2005-07-11 16:57:49 +02001063 die "checkout failed: $?\n" if $?;
1064 }
Matthias Urlichs46541662005-06-28 21:08:15 +02001065}