Re: Yet another Perforce importer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Updated. A very bad bug fixed (the old script cut the imported
history from the previous history on first import).
And I'm sorry for txt (and for .bat).
@rem = 'NT: CMD.EXE vim: syntax=perl noet sw=4
@perl -x -s %0 -- %*
@exit
@rem ';
#!perl -w
#line 7

local $VERBOSE = 0;
local $DRYRUN = 0;
local $AUTO_COMMIT = 0;
local $JUST_COMMIT = 0;
local $P4CLIENT = undef;
local @EDIT_COMMIT = 0;
local @FULL_IMPORT = 0;
local @DESC = ();
local $SPEC = undef;
local @P4ARGS = ();
local $P4HAVE_FILE = undef;
local %P4USERS = ();
local $FULL_DESC = 1;
push(@P4ARGS, '-P', $ENV{P4PASSWD})
    if defined($ENV{P4PASSWD}) and length($ENV{P4PASSWD});
use Cwd;
local $start_dir = cwd();

sub read_args {
    my ($in_client, $in_cl, $in_fi, $in_p4) = (0,0,0,0);
    foreach my $f ( @_ ) {
	if ($in_client) { $in_client = 0; $P4CLIENT = $f; next }
	if ($in_cl) { $in_cl=0; push(@DESC,"c$f"); next }
	if ($in_fi) { $in_fi=0; push(@DESC,"f$f"); next }
	if ($in_p4) { $in_p4=0; push(@DESC,"4$f"); next }
	$DRYRUN=1, next if $f eq '-n' or $f eq '--dry-run';
	$AUTO_COMMIT=1, next if $f eq '-y' or $f eq '--yes';
	$JUST_COMMIT=1, next if $f eq '--just-commit';
	$EDIT_COMMIT=1, next if ($f eq '-e') or ($f eq '--edit');
	$FULL_IMPORT=1, next if $f eq '--full';
	$FULL_DESC++, next if $f eq '--p4-desc';
	$VERBOSE++, next if $f eq '-v' or $f eq '--verbose';
	$in_client = 1, next if $f eq '--client';
	$in_cl = 1, next if $f eq '-C';
	$in_fi = 1, next if $f eq '-F';
	$in_p4 = 1, next if ($f eq '--ptr') or ($f eq '--p4');
	if ($f eq '--help' or $f eq '-h') {
	    print <<EOF;
$0 <specification> [-n|--dry-run] [-y|--yes] [--client <client-name>] \
[-e|--edit] [--just-commit] [--full] [-v|--verbose] [-C <change-number>] \
[-F <filename>] [--ptr|--p4 <p4-path-and/or-revision>] [--p4-desc]

Perforce client state importer. Creates a git commit on the current
branch from a state the given p4 client and working directory hold.

<specification> must be given and is expected to be a file which will be
stored on the side branch under the name "spec".
Remote-to-local mapping and the revisions of files are stored in "have",
and the client definition - in "client".

--client client Specify client name (saved in .git/p4/client for the next time)
--full          Perform full import, don't even try to figure out what changed
-y|--yes        Commit automatically (by default only index updated)
--just-commit   To be used after you forgot to run with --yes first time
-n|--dry-run    Do not update the index and do not commit
-e|--edit       Edit commit description before committing
-v|--verbose    Be more verbose. Can be given many times, increases verbosity
-F file         Take description for the commit from a file in the
                next parameter
-C change       Take description for the commit from this p4 change
--p4|--ptr p4-path-and/or-revision Take description for the commit from the p4
                change described by this p4 path, possibly including revision
                specification
--p4-desc       Increase amount of junk from p4 change description

The descriptions taken from p4 changes given by -C and --p4 will
be concatenated if the options given multiple times.

EOF
	    exit(0);
	}
	warn "$0: spec was already set, $SPEC ignored\n" if defined($SPEC);
	$SPEC = $f;
    }
}
read_args(@ARGV);

local ($GIT_DIR) = qx{git rev-parse --git-dir};
$GIT_DIR =~ s/\r?\n$//s if defined($GIT_DIR);
die "$0: git directory not found\n" if !defined($GIT_DIR) or !-d $GIT_DIR;

local $editor = $ENV{VISUAL};
$editor = $ENV{EDITOR} unless defined($editor);
$editor = 'd:/Programs/Vim/vim70/gvim.exe' unless defined($editor);
die "$0: no editor defined\n" unless defined($editor);

# P4 client was given in command-line. Store it
if ( defined($P4CLIENT) ) {
    mkdir "$GIT_DIR/p4", 0777;
    if ( open(F, '>', "$GIT_DIR/p4/client") ) {
	print F "$P4CLIENT\n";
	close(F);
    } else {
	die "$0: cannot store client name: $!\n"
    }
} else {
    if ( open(F, '<', "$GIT_DIR/p4/client") ) {
	($P4CLIENT) = <F>;
	close(F);
	$P4CLIENT =~ s/^\s*//,$P4CLIENT =~ s/\s*$// if defined($P4CLIENT);
    }
}
die "P4 client not defined\n" if !defined($P4CLIENT) or !length($P4CLIENT);
print "reading P4 client $P4CLIENT\n" if $VERBOSE;
local ($P4ROOT, $p4clnt, $P4HOST);
open(my $fdo, '>', "$GIT_DIR/p4/client.def") or die "p4/client.def: $!\n";
binmode($fdo);
open(my $fdi, '-|', "p4 client -o $P4CLIENT") or die "p4 client: $!\n";
binmode($fdi);
my $last_line_len = 0;
while (<$fdi>) {
    next if /^#/o;
    if ( m/^\s*Root:\s*(\S+)[\\\/]*\s*$/so ) { $P4ROOT = $1 }
    elsif ( m/^\s*Client:\s*(\S+)/o ) { $p4clnt = $1 }
    elsif ( m/^\s*Host:\s*(\S+)/o ) { $P4HOST = $1 }
    ($VERBOSE and print), next if /^(Access|Update):/;
    s/\r?\n$//so;
    my $len = length($_);
    print $fdo "$_\n" if $len or $len != $last_line_len;
    $last_line_len = $len;
}
close($fdi);
close($fdo);

die "Client root not defined\n" unless defined($P4ROOT);
if ( $VERBOSE ) {
    print "GIT_DIR: $GIT_DIR\n";
    print "Root: $P4ROOT (cwd: $start_dir)\n";
    print "Host: $P4HOST\n";
    print "Client: $p4clnt\n" if $p4clnt ne $P4CLIENT;
}
my ($git_head,$git_p4_head,$git_p4_have) = &git_p4_init;

if ($JUST_COMMIT) {
    git_p4_commit($git_head, $git_p4_head);
    exit 0;
}

local %gitignore_dirs = ();
$gitignore_dirs{'/'} = read_filter_file("$GIT_DIR/info/exclude");
push(@{$gitignore_dirs{'/'}}, @{read_filter_file('.gitignore')});

my %git_index = ();
$/ = "\0";
my @git_X = ();
print "Reading git file list(git ls-files @git_X --cached -z)...\n" if $VERBOSE;
foreach ( qx{git ls-files @git_X --cached -z} ) {
    chop; # chop \0
    next if m/^\.gitignore$/o;
    next if m/\/\.gitignore$/o;
    next if filtered($_);
    $git_index{$_} = 1;
}

my @git_add = ();
my @git_addx = ();
my @git_del = ();
my @git_upd = ();

print "Reading P4 file list...\n" if $VERBOSE;
local ($Conflicts,$Ignored,$Added,$Deleted,$Updated) = (0,0,0,0,0);
$/ = "\n";
my $in_name = 0;
my @root = split(/[\/\\]+/, $P4ROOT);
my %p4_index = ();
my %p4_a_lc = ();
my %lnames = ();
my %lconflicts = ();
if (opendir(DIR, '.')) {
    $lnames{'.'} = [grep {$_ ne '.' and $_ ne '..'} readdir(DIR)];
    closedir(DIR);
    #print "read $start_dir (",scalar(@{$lnames{'.'}}),")\n";
}
open(my $have, "p4 -G @P4ARGS -c $P4CLIENT -H $P4HOST -d $P4ROOT have |") or
    die "$0: failed to start p4: $!\n";
binmode($have);
$P4HAVE_FILE = "$GIT_DIR/p4/have";
open(my $storedhave, '>', $P4HAVE_FILE) or die "$P4HAVE_FILE: $!\n";
binmode($storedhave);
my $ent;
while (defined($ent=read_pydict_entry($have))) {
    next if !defined($ent->{depotFile}) or !defined($ent->{clientFile});
    my $a = $ent->{depotFile};
    $ent->{clientFile} =~ m!^//[^/]+/(.*)!o;
    my $b = $1;
    my @bb = split(/\/+/, $b);
    print $storedhave "$a\0$ent->{clientFile}\0$ent->{haveRev}\0\n";

    if ( $^O eq 'MSWin32' ) {
	# stupid windows, daft activestate, dumb P4
	# This piece below is checking for file name conflicts
	# which happen on windows because of it mangling the names.
	my $blc = lc $b;
	if ( $#bb > 0 ) {
	    my $path = '.';
	    foreach my $n (@bb[0 .. $#bb -1]) {
		my @conflicts =
		    grep {lc $_ eq lc $n and $_ ne $n} @{$lnames{$path}};
		if (@conflicts and !exists($lconflicts{"$path/$n"})) {
		    warn "warning: $a -> $b\n".
			 "warning: conflict between path \"$path/$n\" and ".
			 "local filesystem in \"@conflicts\"\n";
		    $Conflicts++;
		    $lconflicts{"$path/$n"} = 1;
		}
		$path .= "/$n";
		if (!exists($lnames{$path})) {
		    if (opendir(DIR, $path)) {
			$lnames{$path} =
			    [grep {$_ ne '.' and $_ ne '..'} readdir(DIR)];
			closedir(DIR);
			#print "read $path (",scalar(@{$lnames{$path}}),")\n";
		    }
		}
	    }
	}
	if (!exists($p4_a_lc{$blc})) {
	    $p4_a_lc{$blc} = [$a, $b];
	} else {
	    warn("warning: $a -> $b\n".
		 "warning: conflicts with ".
		 $p4_a_lc{$blc}->[0]." -> ".
		 $p4_a_lc{$blc}->[1]."\n");
	    $Conflicts++;
	    next;
	}
    }

    my $i;
    for ($i = 0; $i < $#bb; ++$i) {
	my $bdir = join('/',@bb[0 .. $i]) . '/';
	if ( !exists($gitignore_dirs{$bdir}) ) {
	    $gitignore_dirs{$bdir} = read_filter_file("$bdir.gitignore");
	}
    }
    if (filtered($b)) {
	print " i $b\n" if $VERBOSE > 3;
	$Ignored++;
	next
    }
    $p4_index{$b} = $a;
    if ( exists($git_index{$b}) ) {
	my $needup = 1;
	if (defined($git_p4_have)) {
	    $prev = $git_p4_have->{$a};
	    if (defined($prev)) {
		$prev->[0] =~ m!^//[^/]+/(.*)!o;
		$needup = 0 if ($b eq $1) and ($prev->[1] eq $ent->{haveRev});
		if ($needup and $VERBOSE > 1) {
		    my $reason;
		    $reason = 'local file' if $b ne $1;
		    $reason = 'revision' if $prev->[1] ne $ent->{haveRev};
		    print "$a ($reason changed)\n";
		}
	    }
	}
	if ($needup) {
	    $Updated++;
	    push(@git_upd, $b);
	}
    } else {
	$Added++;
	if ( $b =~ m/\.(bat|cmd|pl|sh|exe|dll)$/io )
	{ push(@git_addx, $b) } else { push(@git_add, $b) }
    }
}
close($storedhave);
close($have);
undef %p4_a_lc;

@git_del = grep { !exists($p4_index{$_}) } keys %git_index;
$Deleted = $#git_del + 1;

#foreach (keys %git_index)
#{ push(@git_del, $_) if !exists($p4_index{$_}) }

if ( $DRYRUN ) {
    print($#git_add+$#git_addx+ 2," files to add\n") if $VERBOSE;
    print map {" a $_\n"} @git_add if $VERBOSE > 2;
    print map {" a $_\n"} @git_addx if $VERBOSE > 2;
    print($#git_del+1," files to unreg\n") if $VERBOSE;
    print map {" d $_\n"} @git_del if $VERBOSE > 2;
    print($#git_upd+1," files to update\n") if $VERBOSE;
    print map {" u $_\n"} @git_upd if $VERBOSE > 2;
    print "added: $Added, unregd: $Deleted, updated: $Updated, ignored: $Ignored";
    print ", conflicts: $Conflicts" if $Conflicts;
    print "\n";
} else {
    if (@git_add || @git_addx) {
	print($#git_add+$#git_addx+ 2,
	      " files | git update-index --add -z --stdin\n")
	    if $VERBOSE;
	if (@git_add) {
	    open(GIT, '| git update-index --add --chmod=-x -z --stdin') or
		die "$0 git-update-index(add): $!\n";
	    print GIT map {print " a $_\n" if $VERBOSE > 1; "$_\0"} @git_add;
	    close(GIT);
	}
	if (@git_addx) {
	    open(GIT, '| git update-index --add --chmod=+x -z --stdin') or
		die "$0 git-update-index(add): $!\n";
	    print GIT map {print " a $_\n" if $VERBOSE > 1; "$_\0"} @git_addx;
	    close(GIT);
	}
    }

    if (@git_del) {
	print($#git_del+1," files | git update-index --remove -z --stdin\n")
	    if $VERBOSE;
	open(GIT, '| git update-index --force-remove -z --stdin') or
	    die "$0 git-update-index(del): $!\n";
	print GIT map {print " d $_\n" if $VERBOSE > 1; "$_\0"} @git_del;
	close(GIT);
    }

    if (@git_upd) {
	print($#git_upd+1," files | git update-index -z --stdin\n")
	    if $VERBOSE;
	open(GIT, '| git update-index -z --stdin') or
	    die "$0 git-update-index(upd): $!\n";
	print GIT map {print " u $_\n" if $VERBOSE > 1; "$_\0"} @git_upd;
	close(GIT);
    }
    
    print "added: $Added, unregd: $Deleted, updated: $Updated, ignored: $Ignored";
    print ", conflicts: $Conflicts" if $Conflicts;
    print "\n";
    git_p4_commit($git_head, $git_p4_head) if $AUTO_COMMIT;
}

exit 0;

sub filtered {
    my $name = shift;
    study($name);
    my @path = split(/\/+/o, $name);
    my $dir = '';
    $name = '';
    
    foreach my $d (@path) {
	$name .= $d;
#	print STDERR "$dir: $name $d\n" if $v;
	foreach my $re (@{$gitignore_dirs{'/'}}) {
	    return 1 if $name =~ m/$re/;
	    return 1 if $d =~ m/$re/;
	}
	if ( length($dir) and exists($gitignore_dirs{$dir}) ) {
	    foreach my $re (@{$gitignore_dirs{$dir}}) {
		return 1 if $name =~ m/$re/;
		return 1 if $d =~ m/$re/;
	    }
	}
	$name .= '/';
	$dir = $name;
    }
#    print STDERR "$name not filtered\n" if $v;
    return 0;
}

sub read_filter_file {
    my @filts = ();
    my $file = shift;
    if ( open(my $if, '<', $file) ) {
	print "added ignore file $file\n" if $VERBOSE;
	$/ = "\n";
	while (my $l = <$if>) {
	    next if $l =~ /^\s*#/o;
	    next if $l =~ /^\s*$/o;
	    $l =~ s/[\r\n]+$//so;
	    $l =~ s/\./\\./go;
	    $l =~ s/\*/.*/go;
	    if ( $l =~ m/\// ) {
		$l = "^$l($|/)";
	    } else {
		$l = "(^|/)$l\$";
	    }
	    print " filter $l\n" if $VERBOSE > 1;
	    push(@filts, qr/$l/);
	}
	close($if);
    }
    return \@filts;
}

sub r_pystr
{
    my $fd = shift;
    my ($len,$str)=('','');
    my ($c,$rd,$b) = (4,0,'');
    while ($c > 0) {
	$rd = sysread($fd,$b,$c);
	warn("failed to read len: $!"), return undef if !defined($rd);
	warn("not enough data for len"), return undef if !$rd;
	$len .= $b;
	$c -= $rd;
    }
    $len = unpack('V',$len);
    while ($len > 0) {
	$rd = sysread($fd,$b,$len);
	warn("failed to read data: $!"), return undef if !defined($rd);
	warn("not enough data"), return undef if !$rd;
	$str .= $b;
	$len -= $rd;
    }
    return $str;
}

sub read_pydict_entry
{
    my $f = shift;
    my ($buf,$rd);
    FIL: while (1) {
	# object type identifier
	$rd = sysread($f, $buf, 1);
	last FIL if $rd == 0;
	warn("object type: $!\n"),last if $rd != 1;
	# '{' is a python marshalled dict
	warn("object type: not {\n"),last if $buf ne '{';
	my $ent = {};
	PAIR: while (1) {
	    my ($b,$key);
	    # key type identifier
	    $rd = sysread($f, $b, 1);
	    warn("key type: $!\n"),last FIL if $rd != 1;
	    if ($b eq 's') { # length-prefixed string
		$key = r_pystr($f);
		warn("key: $!\n"),last FIL if !defined($b);
	    } elsif ($b eq '0') { # NULL-element, end of entry
		last PAIR;
	    } else {
		warn("key type: not s");
		last FIL;
	    }
	    # value type identifier
	    $rd = sysread($f, $b, 1);
	    warn("$key value type: $!\n"),last FIL if $rd != 1;
	    if ($b eq 's') { # length-prefixed string
		$b = r_pystr($f);
		warn("$key value: $!"),last FIL if !defined($b);
		$ent->{$key} = $b;
	    } else {
		warn("$key value type: not s ($b)");
		last FIL;
	    }
	}
	return $ent;
    }
    return undef;
}

sub cl2msg {
    my $cl = shift;
    my($o1,$o2,$i);
    if(!open($o1, '>>', "$GIT_DIR/p4/msg")) {
	warn "p4/msg: $!\n";
	return;
    }
    binmode($o1);
    if(!open($o2, '>>', "$GIT_DIR/p4/p4msg")) {
	warn "p4/p4msg: $!\n";
	close($o1);
	return
    }
    binmode($o2);
    if(!open($i, '-|', "p4 describe -s $cl")){
	warn "p4 describe: $!\n";
	close($o1);
	close($o2);
	return
    }
    binmode($i);
    print $o1 "$cl: " if $FULL_DESC;
    print $o2 "$cl: ";
    my @a;
    my $u = undef;
    while (my $l = <$i>) {
	if ($l =~ /^Change \d+ by (\S+)@[^ ]* on ([^\r\n]*)/so) {
	    $u = $1;
	    $ENV{GIT_AUTHOR_DATE} = $2 if length($2);
	}
	last if $FULL_DESC < 2 and $l =~ /^\s*Affected files \.{3}\s*$/so;
	$l =~ s/\r?\n$//so;
	push @a, $l;
    }
    close($i);
    print $o2 substr($a[2],1),"\n"; # p4 side-branch commit description
    close($o2);
    # import branch commit description
    if ($FULL_DESC > 1) {
	# desc level 2+: keep the Change line
	print $o1 map {"$_\n"} (substr($a[2],1),"\n",@a);
    } else {
	# levels 0 and 1: remove the Change line
	print $o1 map { (length($_) ? substr($_,1):'')."\n" } @a[2..$#a];
    }
    close($o1);
    if (defined($u)) {
	if (!exists($P4USERS{$u})) {
	    my ($mail,$name) = grep {/^(Email|FullName):/} qx{p4 user -o $u};
	    if ($? == 0 and defined($mail) and defined($name)) {
		s/^\S+:	([^\r\n]*)\r?\n$/$1/so for ($mail,$name);
		if (length($name) and length($mail)) {
		    $P4USERS{$u} = {name=>$name, email=>$mail};
		}
	    }
	}
	if ($P4USERS{$u}) {
	    $p4u = $P4USERS{$u};
	    $ENV{GIT_AUTHOR_NAME}  = $p4u->{name};
	    $ENV{GIT_AUTHOR_EMAIL} = $p4u->{email};
	}
    }
}

sub git_p4_init {
    my ($commit,$parent,$p4commit,$p4parent);
    my ($HEAD) = qx{git rev-parse HEAD};
    $HEAD = '' if $?;
    my ($p4head) = qx{git rev-parse refs/p4import/$P4CLIENT};
    $p4head = '' if $?;
    s/\r?\n//gs for ($HEAD, $p4head);
    die "No HEAD commit! Refusing to import.\n" if !length($HEAD);
    if (length($p4head)) {
	($commit,$p4parent) =
	    grep { s/^parent (.{40}).*/$1/s }
	    qx{git cat-file commit $p4head};
	$commit = $p4parent = '' if $?;
	$p4parent = '' if !defined($p4parent);
    } else {
	$commit = $p4parent = '';
    }
    while (($commit ne $HEAD) and length($p4parent)) {
	$p4head = $p4parent;
	($commit,$p4parent) =
	    grep { s/^parent (.{40}).*/$1/s }
	    qx{git cat-file commit $p4head};
	$commit = $p4parent = '' if $?;
	if ($VERBOSE and ($HEAD eq $commit)) {
	    print "found p4 import commit ";
	    system('git','name-rev',$p4head);
	}
    }
    warn "Current HEAD was not imported from $P4CLIENT, doing full import\n"
	if $HEAD ne $commit;
    my $p4have = undef;
    if (!$FULL_IMPORT and ($HEAD eq $commit) and length($p4head)) {
	if (open(my $f, '-|', "git cat-file blob $p4head:have")) {
	    my $old = $/;
	    $/ = "\0";
	    my $cnt = 0;
	    while(1) {
		my $p4name = <$f>;
		last if !defined($p4name);
		$p4name =~ s/^.//so if $cnt; # remove \n
		my $name = <$f>;
		my $rev = <$f>;
		last if !defined($name) or !defined($rev);
		chop($p4name,$name,$rev);
		++$cnt;
		if (defined($p4have)) {
		    $p4have->{$p4name} = [$name,$rev];
		} else {
		    $p4have = {$p4name=>[$name,$rev]};
		}
	    }
	    $/ = $old;
	    close($f);
	    print "loaded $cnt revisions from $p4head\n" if $VERBOSE;
	}
    }
    return ($HEAD, $p4head, $p4have);
}

sub git_p4_commit {
    my ($HEAD, $p4head) = @_;
    my ($commit,$parent,$p4commit,$p4parent);

    my ($fdo,$fdi,$rc);
    $rc = system('git','diff-index','--exit-code','--quiet','--cached','HEAD');
    if ($rc == 0) {
	warn("No changes\n");
	return;
    }

    return if $DRYRUN;

    my $p4x = "$GIT_DIR/p4/idx.tmp";
    unlink($p4x);
    my $oldidx = $ENV{GIT_INDEX_FILE};

    $ENV{PAGER} = 'cat';
    $ENV{GIT_INDEX_FILE} = $p4x;

    if (!defined($SPEC) or !open(STDIN, '<', $SPEC)) {
	if ( $^O eq 'MSWin32' ) {
	    open(STDIN, '<', 'NUL') or die "$SPEC: $!\n";
	} else {
	    open(STDIN, '<', '/dev/null') or die "$SPEC: $!\n";
	}
    }
    my ($p4spec) = qx{git hash-object -t blob -w --stdin};
    die "Failed to store $SPEC in git repo\n" if $?;

    open(STDIN, '<', "$GIT_DIR/p4/client.def") or die "cldef: $!\n";
    my ($p4clnt) = qx{git hash-object -t blob -w --stdin};
    die "Failed to save mappings of $P4CLIENT in git repo" if $?;

    if (!defined($P4HAVE_FILE)) {
	print "reading state of $P4CLIENT\n" if $VERBOSE;
	$P4HAVE_FILE = "$GIT_DIR/p4/have"; 
	open($fdo, '>', $P4HAVE_FILE) or die "p4/have: $!\n";
	binmode($fdo);
	open($fdi, "p4 -G @P4ARGS -c $P4CLIENT -H $P4HOST -d $P4ROOT have|") or
	    die "p4 have: $!\n";
	binmode($fdi);
	my $ent;
	while (defined($ent=read_pydict_entry($fdi))) {
	    next if !defined($ent->{depotFile});
	    next if !defined($ent->{clientFile});
	    print $fdo "$ent->{depotFile}\0",
		       "$ent->{clientFile}\0",
		       "$ent->{haveRev}\0\n";
	}
	close($fdi);
	close($fdo);
    }

    open(STDIN, '<', $P4HAVE_FILE) or die "$P4HAVE_FILE: $!\n";
    my ($p4have) = qx{git hash-object -t blob -w --stdin};
    die "Failed to save state of $P4CLIENT in git repo" if $?;

    unlink("$GIT_DIR/p4/msg", "$GIT_DIR/p4/p4msg");
    foreach my $i (@DESC) {
	$i =~ s/^(.)//o;
	if ('c' eq $1) {
	    print "reading changes for $i\n" if $VERBOSE;
	    cl2msg($i);
	} elsif ('f' eq $1) {
	    my($o1,$o2,$i);
	    if (open($o1, '>>', "$GIT_DIR/p4/msg")) {
		if (open($o2, '>>', "$GIT_DIR/p4/p4msg")) {
		    if (open($i, '<', $i)) {
			my $n = 0;
			while(<$i>) {
			    $n++;
			    print $o1 $_;
			    print $o2 $_ if $n == 1;
			}
			close($i);
		    }
		    close($o2);
		}
		close($o1);
	    }
	} elsif ('4' eq $1) {
	    print "reading changes for $i\n" if $VERBOSE;
	    my ($change)=qx{p4 changes -m1 $i};
	    if (!defined($change) or $change !~ m/\s+(\d+)\s/) {
		die "$i does not resolve into a change number\n";
	    }
	    cl2msg($1);
	}
    }
    system("$editor $GIT_DIR/p4/msg") if $EDIT_COMMIT;

    if (defined($oldidx)) { $ENV{GIT_INDEX_FILE} = $oldidx }
    else { delete $ENV{GIT_INDEX_FILE} }

    #
    # Store the imported file data
    #

    if ( $^O eq 'MSWin32' ) { open(STDERR, "NUL") }
    else { open(STDERR, "/dev/null") }

    my ($tree) = qx{git write-tree};
    die "Failed to write current tree\n" if $?;
    $parent = length($HEAD) ? "-p $HEAD": '';
    open(STDIN, '<', "$GIT_DIR/p4/msg") or die "p4/msg: $!\n";
    $tree =~ s/\r?\n//gs;
    ($commit)=qx{git commit-tree $tree $parent};
    die "failed to commit current tree\n" if $?;
    s/\r?\n//gs for ($commit);

    #
    # Storing import control data
    #
    $ENV{GIT_INDEX_FILE} = $p4x;
    open($fdo, '|-', 'git update-index --add --index-info') or
	die "could not start git update-index\n";
    binmode($fdo);
    s/\r?\n//gs for ($p4spec,$p4clnt,$p4have);
    print $fdo "100644 $p4spec\tspec\n";
    print $fdo "100644 $p4clnt\tclient\n";
    print $fdo "100644 $p4have\thave\n";
    close($fdo);
    if($?) {
	die "Failed to store $SPEC in p4import index and git repo\n".
	    "Failed to save mappings of $P4CLIENT in p4import index and git repo\n".
	    "Failed to save state of $P4CLIENT in p4import index and git repo\n"
    }
    my ($p4tree)=qx{git write-tree};
    die "Failed to store $SPEC (tree) in git repo\n" if $?;

    # Bind import control data to the file data
    $p4parent="-p $commit";
    $p4parent="$p4parent -p $p4head" if length($p4head);
    open(STDIN, '<', "$GIT_DIR/p4/p4msg") or die "p4/p4msg: $!\n";
    $p4tree =~ s/\r?\n//gs;
    ($p4commit)=qx{git commit-tree $p4tree $p4parent};
    die "Failed to store $SPEC (commit) in git repo\n" if $?;
    $p4commit =~ s/\r?\n//gs;

    # Finishing touches: update references
    system('git','update-ref','-m','backup ref of current branch',
	   'p4/backup-HEAD','HEAD');
    system('git','update-ref','-m','backup ref of p4import',
	   'p4/backup-p4import',"refs/p4import/$P4CLIENT");
    $rc = system('git','update-ref','-m','data of p4import','HEAD',$commit);
    die "Failed to update HEAD\n" if $rc;
    $rc = system('git','update-ref','-m','p4import',"refs/p4import/$P4CLIENT",$p4commit);
    die "Failed to store $SPEC (reference) in git repo\n" if $rc;

    if ($VERBOSE) {
	print STDOUT (grep {s/\r?\n//gs;s/.*?\s//} qx{git name-rev refs/p4import/$P4CLIENT}), ":\n";
	system('git','log','--max-count=1','--pretty=format:%h %s%n',$p4commit);
    }
    print STDOUT (grep {s/\r?\n//gs;s/.*?\s//} qx{git name-rev HEAD}), ":\n";
    system('git','log','--max-count=1','--pretty=format:%h %s%n',$commit);
}


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux