* 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).