git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Another Perforce importer for git
@ 2006-08-14 13:04 Alex Riesen
  2006-08-14 14:07 ` Jakub Narebski
  0 siblings, 1 reply; 3+ messages in thread
From: Alex Riesen @ 2006-08-14 13:04 UTC (permalink / raw)
  To: git

[-- Attachment #1: Type: text/plain, Size: 581 bytes --]

Just in case someone might ever need this: it imports
a _synced_ state into git repo. IOW, you sync down
the files as you were commanded (it's corporal, umm...
corporate, remember?) and run the script to create
a commit for you. You still have to run git-commit manually,
but don't have to pass "-a" to it (which can be dangerous,
and does not pick up the files recently added to p4 repo).

For the poor souls unlucky enough to be in windows
environment, a .bat is attached, too.

P4 has a better interoperability with python, so rewriting this
script in Python maybe a good idea.

[-- Attachment #2: git-p4-import.perl --]
[-- Type: application/octet-stream, Size: 5106 bytes --]

#!/usr/bin/perl -w

local $VERBOSE = 0;
local $DRYRUN = 0;
local $YES = 0;
local $AUTO_COMMIT = 1;
local $NOP4_CLIENT = undef;
local @P4ARGS = ();
push(@P4ARGS, '-P', $ENV{P4PASSWD})
    if defined($ENV{P4PASSWD}) and length($ENV{P4PASSWD});
local $editor = $ENV{VISUAL};
$editor = $ENV{EDITOR} unless defined($editor);
die "$0: no editor defined\n" unless defined($editor);

sub read_args {
    my ($in_client) = (0);
    foreach my $f ( @_ ) {
	if ( $in_client ) { $in_client = 0; $NOP4_CLIENT = $f; next }
	$DRYRUN=1, next if $f eq '-n' or $f eq '--dry-run';
	$YES=1, next if $f eq '-y' or $f eq '--yes';
	$VERBOSE=1, next if $f eq '-v' or $f eq '--verbose';
	$in_client = 1, next if $f eq '-c';
    }
}
read_args(@ARGV);

die "$0: P4 client not defined\n" unless defined($NOP4_CLIENT);
local ($P4ROOT, $P4CLIENT, $P4HOST);
my @spec = grep {
    if ( m/^\s*Root:\s*(\S+)[\\\/]*\s*$/so ) { $P4ROOT = $1; 1 }
    elsif ( m/^\s*Client:\s*(\S+)/o ) { $P4CLIENT = $1; 1 }
    elsif ( m/^\s*Host:\s*(\S+)/o ) { $P4HOST = $1; 1 }
    else { 0 }
} qx{p4 @P4ARGS client -o $NOP4_CLIENT};

local ($GIT_DIR)=qx{git rev-parse --git-dir};
chomp($GIT_DIR);
die "$0: $GIT_DIR is not git directory\n" unless -d $GIT_DIR;

$/ = "\0";
my %git_index = ();
foreach ( qx{git ls-files --cached -z} ) {
    chop;
    next if m/^\.(p4sm|nop4)\//o;
    next if m/^\.gitignore$/o;
    next if m/\/\.gitignore$/o;
    $git_index{$_} = 1;
}

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

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

# stats
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 = ();
foreach ( qx{p4 @P4ARGS -c $P4CLIENT -H $P4HOST -d $P4ROOT have} ) {
    next if !m!^(//.*?[#]\d+) - (.*)!o;
    my $a = $1;
    my @b = split(/[\/\\]+/, $2);
    foreach my $d (@root) { # remove root
        last if $d ne $b[0];
        shift @b;
    }
    shift @b while $b[0] eq '.';
    my $b = join('/',@b);
    my $blc = lc $b;

    if ( $^O eq 'MSWin32' ) { # stupid windows, daft activestate
	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");
	    $AUTO_COMMIT = 0;
	    $Conflicts++;
	    next;
	}
    }

    my $i;
    for ($i = 0; $i < $#b; ++$i) {
	my $bdir = join('/',@b[0 .. $i]) . '/';
	if ( !exists($gitignore_dirs{$bdir}) ) {
	    $gitignore_dirs{$bdir} = read_filter_file("$bdir.gitignore");
	}
    }
    if (filtered($b)) {
	print "ign $b\n" if $VERBOSE;
	$Ignored++;
	next
    }
    $p4_index{$b} = $a;
    if ( exists($git_index{$b}) ) { $Updated++; push(@git_upd, $b) }
    else { $Added++; push(@git_add, $b) }
}
undef %p4_a_lc;

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

if ( $DRYRUN ) {
    print map {"add $_\n"} @git_add;
    print map {"del $_\n"} @git_del;
    print map {"upd $_\n"} @git_upd;
} else {
    print "git update-index --add --chmod=-x -z --stdin\n" if $VERBOSE;
    open(GIT, '| git update-index --add --chmod=-x -z --stdin') or
        die "$0 git-update-index(add): $!\n";
    print GIT map {print "  $_\n" if $VERBOSE; "$_\0"} @git_add;
    close(GIT);
    print "git update-index --force-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 "  $_\n" if $VERBOSE; "$_\0"} @git_del;
    close(GIT);
    print "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 "  $_\n" if $VERBOSE; "$_\0"} @git_upd;
    close(GIT);
}
print "updated: $Updated, added: $Added, deleted: $Deleted, " .
      "ignored: $Ignored, conflicts: $Conflicts\n";

exit 0;

sub filtered {
    my $name = shift;
    study($name);
    my @path = split(/\/+/o, $name);
    my $dir = '';
    $name = '';
    
    foreach my $d (@path) {
	$name .= $d;
	foreach my $re (@{$gitignore_dirs{'/'}}) {
	    return 1 if $name =~ m/$re/;
	    return 1 if $d =~ m/$re/;
	}
	if ( length($dir) ) {
	    foreach my $re (@{$gitignore_dirs{$dir}}) {
		return 1 if $name =~ m/$re/;
		return 1 if $d =~ m/$re/;
	    }
	}
	$name .= '/';
	$dir = $name;
    }
    return 0;
}

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


[-- Attachment #3: git-p4-import.windows-bat --]
[-- Type: application/octet-stream, Size: 5192 bytes --]

@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 $YES = 0;
local $AUTO_COMMIT = 1;
local $NOP4_CLIENT = undef;
local @P4ARGS = ();
push(@P4ARGS, '-P', $ENV{P4PASSWD})
    if defined($ENV{P4PASSWD}) and length($ENV{P4PASSWD});
local $editor = $ENV{VISUAL};
$editor = $ENV{EDITOR} unless defined($editor);
die "$0: no editor defined\n" unless defined($editor);

sub read_args {
    my ($in_client) = (0);
    foreach my $f ( @_ ) {
	if ( $in_client ) { $in_client = 0; $NOP4_CLIENT = $f; next }
	$DRYRUN=1, next if $f eq '-n' or $f eq '--dry-run';
	$YES=1, next if $f eq '-y' or $f eq '--yes';
	$VERBOSE=1, next if $f eq '-v' or $f eq '--verbose';
	$in_client = 1, next if $f eq '-c';
    }
}
read_args(@ARGV);

die "$0: P4 client not defined\n" unless defined($NOP4_CLIENT);
local ($P4ROOT, $P4CLIENT, $P4HOST);
my @spec = grep {
    if ( m/^\s*Root:\s*(\S+)[\\\/]*\s*$/so ) { $P4ROOT = $1; 1 }
    elsif ( m/^\s*Client:\s*(\S+)/o ) { $P4CLIENT = $1; 1 }
    elsif ( m/^\s*Host:\s*(\S+)/o ) { $P4HOST = $1; 1 }
    else { 0 }
} qx{p4 @P4ARGS client -o $NOP4_CLIENT};

local ($GIT_DIR)=qx{git rev-parse --git-dir};
chomp($GIT_DIR);
die "$0: $GIT_DIR is not git directory\n" unless -d $GIT_DIR;

$/ = "\0";
my %git_index = ();
foreach ( qx{git ls-files --cached -z} ) {
    chop;
    next if m/^\.(p4sm|nop4)\//o;
    next if m/^\.gitignore$/o;
    next if m/\/\.gitignore$/o;
    $git_index{$_} = 1;
}

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

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

# stats
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 = ();
foreach ( qx{p4 @P4ARGS -c $P4CLIENT -H $P4HOST -d $P4ROOT have} ) {
    next if !m!^(//.*?[#]\d+) - (.*)!o;
    my $a = $1;
    my @b = split(/[\/\\]+/, $2);
    foreach my $d (@root) { # remove root
        last if $d ne $b[0];
        shift @b;
    }
    shift @b while $b[0] eq '.';
    my $b = join('/',@b);
    my $blc = lc $b;

    if ( $^O eq 'MSWin32' ) { # stupid windows, daft activestate
	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");
	    $AUTO_COMMIT = 0;
	    $Conflicts++;
	    next;
	}
    }

    my $i;
    for ($i = 0; $i < $#b; ++$i) {
	my $bdir = join('/',@b[0 .. $i]) . '/';
	if ( !exists($gitignore_dirs{$bdir}) ) {
	    $gitignore_dirs{$bdir} = read_filter_file("$bdir.gitignore");
	}
    }
    if (filtered($b)) {
	print "ign $b\n" if $VERBOSE;
	$Ignored++;
	next
    }
    $p4_index{$b} = $a;
    if ( exists($git_index{$b}) ) { $Updated++; push(@git_upd, $b) }
    else { $Added++; push(@git_add, $b) }
}
undef %p4_a_lc;

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

if ( $DRYRUN ) {
    print map {"add $_\n"} @git_add;
    print map {"del $_\n"} @git_del;
    print map {"upd $_\n"} @git_upd;
} else {
    print "git update-index --add --chmod=-x -z --stdin\n" if $VERBOSE;
    open(GIT, '| git update-index --add --chmod=-x -z --stdin') or
        die "$0 git-update-index(add): $!\n";
    print GIT map {print "  $_\n" if $VERBOSE; "$_\0"} @git_add;
    close(GIT);
    print "git update-index --force-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 "  $_\n" if $VERBOSE; "$_\0"} @git_del;
    close(GIT);
    print "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 "  $_\n" if $VERBOSE; "$_\0"} @git_upd;
    close(GIT);
}
print "updated: $Updated, added: $Added, deleted: $Deleted, " .
      "ignored: $Ignored, conflicts: $Conflicts\n";

exit 0;

sub filtered {
    my $name = shift;
    study($name);
    my @path = split(/\/+/o, $name);
    my $dir = '';
    $name = '';
    
    foreach my $d (@path) {
	$name .= $d;
	foreach my $re (@{$gitignore_dirs{'/'}}) {
	    return 1 if $name =~ m/$re/;
	    return 1 if $d =~ m/$re/;
	}
	if ( length($dir) ) {
	    foreach my $re (@{$gitignore_dirs{$dir}}) {
		return 1 if $name =~ m/$re/;
		return 1 if $d =~ m/$re/;
	    }
	}
	$name .= '/';
	$dir = $name;
    }
    return 0;
}

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


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Another Perforce importer for git
  2006-08-14 13:04 Another Perforce importer for git Alex Riesen
@ 2006-08-14 14:07 ` Jakub Narebski
  2006-08-14 15:11   ` Alex Riesen
  0 siblings, 1 reply; 3+ messages in thread
From: Jakub Narebski @ 2006-08-14 14:07 UTC (permalink / raw)
  To: git

Alex Riesen wrote:

> Just in case someone might ever need this: it imports
> a _synced_ state into git repo. IOW, you sync down
> the files as you were commanded (it's corporal, umm...
> corporate, remember?) and run the script to create
> a commit for you. You still have to run git-commit manually,
> but don't have to pass "-a" to it (which can be dangerous,
> and does not pick up the files recently added to p4 repo).

Could you please add appropriate entry in GitWiki page
 http://git.or.cz/gitwiki/InterfacesFrontendsAndTools
Thanks in advance...

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Another Perforce importer for git
  2006-08-14 14:07 ` Jakub Narebski
@ 2006-08-14 15:11   ` Alex Riesen
  0 siblings, 0 replies; 3+ messages in thread
From: Alex Riesen @ 2006-08-14 15:11 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git

On 8/14/06, Jakub Narebski <jnareb@gmail.com> wrote:
> > Just in case someone might ever need this: it imports
> > a _synced_ state into git repo. IOW, you sync down
> > the files as you were commanded (it's corporal, umm...
> > corporate, remember?) and run the script to create
> > a commit for you. You still have to run git-commit manually,
> > but don't have to pass "-a" to it (which can be dangerous,
> > and does not pick up the files recently added to p4 repo).
>
> Could you please add appropriate entry in GitWiki page
>  http://git.or.cz/gitwiki/InterfacesFrontendsAndTools
> Thanks in advance...

It a bit not enough, I'm afraid. Besides, I'd have to reference the
scriptlet by gmane - there wont ever be a webpage, I think:
http://permalink.gmane.org/gmane.comp.version-control.git/25352

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2006-08-14 15:11 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-08-14 13:04 Another Perforce importer for git Alex Riesen
2006-08-14 14:07 ` Jakub Narebski
2006-08-14 15:11   ` Alex Riesen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).