* Re: Yet another Perforce importer
From: Alex Riesen @ 2007-05-31 9:23 UTC (permalink / raw)
To: git
In-Reply-To: <20070522211334.GC30871@steel.home>
Fixed another bug which could commit an empty tree.
Improved error handling: the p4 program sends errors in the data stream,
which were ignored. Now the scripts attempts to print them out.
---
@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 $SHOW_DIFFS = 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;
local $HEAD_FROM_P4 = 0;
local $P4_EDIT_CHANGED = 0;
local $P4_EDIT_HEAD = undef;
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, $no_opt) = (0,0,0,0,0);
foreach my $f ( @_ ) {
goto _files if $no_opt;
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 }
$no_opt=1, next if $f eq '--';
$DRYRUN=1, next if $f eq '-n' or $f eq '--dry-run';
$SHOW_DIFFS=$DRYRUN=1, next if $f eq '--diffs';
$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';
$P4_EDIT_CHANGED=1, next if $f eq '--p4-edit-changed';
if ($f =~ /^--p4-edit-changed=(.*)/) {
$P4_EDIT_CHANGED=1;
$P4_EDIT_HEAD=$1;
next
}
$VERBOSE++, next if $f eq '-v' or $f eq '--verbose';
$in_client = 1, next if $f eq '--client';
$P4CLIENT = $1, next if $f =~ /^--client=(.*)/;
$in_cl = 1, next if $f eq '-C';
push(@DESC,"c$1"), next if $f =~ /^--changelist=(.*)/;
$in_fi = 1, next if $f eq '-F';
push(@DESC,"f$1"), next if $f =~ /^--file=(.*)/;
$in_p4 = 1, next if ($f eq '--ptr') or ($f eq '--p4');
push(@DESC,"4$2"), next if $f =~ /^--(p4|ptr)=(.*)/;
if ($f eq '--help' or $f eq '-h') {
print <<EOF;
$0 [-n|--dry-run] [-y|--yes] [--client <client-name>] [--diffs] \
[-e|--edit] [--just-commit] [--full] [-v|--verbose] [-C <change-number>] \
[-F <filename>] [--ptr|--p4 <p4-path-and/or-revision>] [--p4-desc] \
[--] [<specification>]
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
--file=file
-F file Take description for the commit from a file in the
next parameter
--changelist=change
-C change Take description for the commit from this p4 change
--p4|--ptr p4-path-and/or-revision
--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
--diffs Show files which are different between local filesystem, index,
and the current HEAD. Does not do anything else
--p4-edit-changed
--p4-edit-changed=sha1
Merge with the given sha1 and prepare a p4 submission.
Current HEAD must fast-forward to sha1. If sha1 is omited,
the current HEAD is assumed. The working directory must
have no local changes.
The descriptions taken from p4 changes given by -C and --p4 will
be concatenated if the options given multiple times.
"--" can be used to separate options from description files.
EOF
exit(0);
}
die "$0: unknown option $f\n" if $f =~ /^-/;
_files:
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);
if ($SHOW_DIFFS) {
my $sep = $/;
$/="\0";
my ($show, $cnt) = (0, 0);
if (open(F, '-|', 'git diff-files -r --name-only -z')) {
while (<F>) {
my $c = chop;
$_ .= $c if $c ne "\0";
print "Changed files:\n" if !$show;
print " $_\n";
$show = 1;
$cnt++;
}
close(F);
}
if (open(F, '-|', 'git diff-index --cached -r -z HEAD')) {
$show = 0;
my ($diff, $info) = (0, 1);
while (<F>) {
my $c = chop;
$_ .= $c if $c ne "\0";
if ($info) {
next if !/^:(\d{6}) (\d{6}) ([0-9a-f]{40}) ([0-9a-f]{40}) ./o;
# show only content changes, p4 does not support exec-bit anyway
$diff = $3 ne $4;
} elsif ($diff) {
print "Changes between index and HEAD:\n" if !$show;
print " $_\n";
$show = 1;
$cnt++;
}
$info = !$info;
}
close(F);
}
$/ = $sep;
exit($cnt ? 1: 0);
}
# 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;
}
if ($P4_EDIT_CHANGED) {
my $rc = system('git', 'diff-files', '--quiet');
exit(127) if $rc & 0xff; # error starting the program
die "$0: there are changes in $P4ROOT. Import them first.\n" if $rc;
$P4_EDIT_HEAD = 'HEAD' if !defined($P4_EDIT_HEAD);
my ($mergehead) = qx{git rev-parse $P4_EDIT_HEAD};
exit(127) if $?;
exit(1) if !defined($mergehead);
$mergehead =~ s/\r?\n//gs;
exit(1) if !length($mergehead);
print "Checking out $P4_EDIT_HEAD ($mergehead) for p4 edit\n" if $VERBOSE;
# Check if the give reference is a direct descendant of current branch
if ($P4_EDIT_HEAD ne 'HEAD') {
my ($sha1) = qx{git rev-list --max-count=1 $mergehead..HEAD};
exit(127) if $?;
die "$0: HEAD does not fast-forward to $P4_EDIT_HEAD\n"
if defined($sha1) and $sha1 =~ /^[0-9a-f]{40}\b/;
}
my $cnt;
my @files = ();
my $sep = $/;
$/="\0";
if (open(F, '-|', "git diff-index -R --cached -r -z $mergehead")) {
my ($diff, $info, $M) = (0, 1, '');
while (<F>) {
my $c = chop;
$_ .= $c if $c ne "\0";
if ($info) {
next if !/^:\d{6} \d{6} ([0-9a-f]{40}) ([0-9a-f]{40}) (\w+)/o;
# use only content changes, p4 does not support exec-bit
$diff = $1 ne $2;
$M = $3; # change type marker
} elsif ($diff) {
print "$M $_\n" if $VERBOSE;
die "File contains characters which p4 cannot support\n"
if /[\n@#%*]/s;
push @files, "$M$_";
$cnt++;
}
$info = !$info;
}
close(F);
}
$/ = $sep;
if (!$cnt) {
warn "$0: No content changes found between HEAD and $P4_EDIT_HEAD";
exit(0);
}
# Create a new changelist
my $p4;
open($p4, "p4 -c $P4CLIENT -H $P4HOST -d $P4ROOT change -o|") or
die "$0: failed to create changelist\n";
my @desc = map {s/\r?\n//so; $_} <$p4>;
close($p4);
open($p4, '>', "$GIT_DIR/p4/changelist") or
die "$GIT_DIR/p4/changelist: $!\n";
foreach (@desc) {
print $p4 "$_\n";
if (/^Description:/o) {
my $range = "..$mergehead";
$range = "${mergehead}^..$mergehead" if $P4_EDIT_HEAD eq 'HEAD';
if (open(my $fd, '-|', "git log $range")) {
while(<$fd>) {
# I believe it is not possible to save this information
# in Perforce.
next if /^(commit |Author:|Date:)/;
s/\r?\n$//so;
next if !length($_);
s/^\s+//o;
print $p4 " $_\n";
}
close($fd);
}
}
}
close($p4);
open(STDIN, '<', "$GIT_DIR/p4/changelist") or
die "$GIT_DIR/p4/changelist: $!\n";
open($p4, "p4 -c $P4CLIENT -H $P4HOST -d $P4ROOT change -i|") or
die "$0: failed to create changelist\n";
my ($newchange) = grep {s/^Change (\d+) created\b.*/$1/so} <$p4>;
close($p4);
print "Checking out P4 files in changelist $newchange\n" if $VERBOSE;
# open files for edit
$cnt = 0;
open($p4, '>', "$GIT_DIR/p4/files") or die "$GIT_DIR/p4/files: $!\n";
print $p4 "-c\n$newchange\n";
print $p4 (map {++$cnt; substr($_,1)."\n"} grep {/^M/} @files);
close($p4);
sub runp4 {
return system('p4','-c',$P4CLIENT,'-H',$P4HOST,'-d',$P4ROOT,@_);
}
runp4('-x',"$GIT_DIR/p4/files", 'edit') if $cnt;
$cnt = 0;
open($p4, '>', "$GIT_DIR/p4/files") or die "$GIT_DIR/p4/files: $!\n";
print $p4 "-c\n$newchange\n";
print $p4 (map {++$cnt; substr($_,1)."\n"} grep {/^A/} @files);
close($p4);
runp4('-x',"$GIT_DIR/p4/files", 'add') if $cnt;
$cnt = 0;
open($p4, '>', "$GIT_DIR/p4/files") or die "$GIT_DIR/p4/files: $!\n";
print $p4 "-c\n$newchange\n";
print $p4 (map {++$cnt; substr($_,1)."\n"} grep {/^D/} @files);
close($p4);
runp4('-x',"$GIT_DIR/p4/files", 'delete') if $cnt;
# p4 modifies working directory on checkout, stupid thing
system('git', 'update-index', '--refresh');
$rc = system('git', 'read-tree', '-m', '-u', $mergehead);
exit(127) if $rc & 0xff;
exit(1) if $rc;
print "The state of $P4_EDIT_HEAD($mergehead) is checked out.\n";
print "A p4 changelist $newchange is prepared.\n";
exit(0);
}
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 ($cnt,$err,$ent) = (0,0,undef);
while (defined($ent=read_pydict_entry($have))) {
if (defined($ent->{code}) and defined($ent->{data})) {
++$err if $ent->{code} eq 'error';
print STDERR 'p4: '.$ent->{code}.': '.$ent->{data}."\n";
next;
}
next if !defined($ent->{depotFile}) or !defined($ent->{clientFile});
++$cnt;
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);
exit 1 if $err; # the error already reported
die "Nothing in the client $P4CLIENT\n" if !$cnt;
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("p4: object type: $!\n"),last if $rd != 1;
# '{' is a python marshalled dict
warn("p4: 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("p4: key type: $!\n"),last FIL if $rd != 1;
if ($b eq 's') { # length-prefixed string
$key = r_pystr($f);
warn("p4: key: $!\n"),last FIL if !defined($b);
} elsif ($b eq '0') { # NULL-element, end of entry
last PAIR;
} else {
die("p4: key type: not s (string)\n");
last FIL;
}
# value type identifier
$rd = sysread($f, $b, 1);
warn("p4: $key value type: $!\n"),last FIL if $rd != 1;
if ($b eq 's') { # length-prefixed string
$b = r_pystr($f);
warn("p4: $key value: $!\n"),last FIL if !defined($b);
$ent->{$key} = $b;
} elsif ($b eq 'i') { # 4-byte integer
$rd = sysread($f, $b, 4);
warn("p4: $key value data: $!\n"),last FIL if $rd != 4;
$ent->{$key} = $b;
} else {
warn("p4: $key value type: not s ($b)\n");
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 $?;
$p4parent = '' if !defined($p4parent);
if ($VERBOSE and ($HEAD eq $commit)) {
print "found p4 import commit ";
system('git','name-rev',$p4head);
}
}
if ($HEAD ne $commit) {
$HEAD_FROM_P4 = 0;
warn "Current HEAD is not from $P4CLIENT, doing full import\n";
} else {
$HEAD_FROM_P4 = 1;
}
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;
if (!@DESC && !$EDIT_COMMIT) {
warn "$0: no commit description given\n";
return;
}
my $p4x = "$GIT_DIR/p4/idx.tmp";
unlink($p4x);
$ENV{PAGER} = 'cat';
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 ($cnt,$err,$ent) = (0,0,undef);
while (defined($ent=read_pydict_entry($fdi))) {
if (defined($ent->{code}) and defined($ent->{data})) {
++$err if $ent->{code} eq 'error';
print STDERR 'p4: '.$ent->{code}.': '.$ent->{data}."\n";
next;
}
next if !defined($ent->{depotFile});
next if !defined($ent->{clientFile});
++$cnt;
print $fdo "$ent->{depotFile}\0",
"$ent->{clientFile}\0",
"$ent->{haveRev}\0\n";
}
close($fdi);
close($fdo);
exit 1 if $err; # the error already reported
die "The client $P4CLIENT has nothing\n" if !$cnt;
}
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 $?;
#
# Prepare commit messages
#
unlink("$GIT_DIR/p4/msg", "$GIT_DIR/p4/p4msg");
open($fdo, '>', "$GIT_DIR/p4/msg"); close($fdo);
open($fdo, '>', "$GIT_DIR/p4/p4msg"); close($fdo);
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;
# copy mirror-branch commit message into side-branch
# commit message if no other description were given.
if (!-s "$GIT_DIR/p4/p4msg") {
open($fdi, '<', "$GIT_DIR/p4/msg") or die "$GIT_DIR/p4/msg: $!\n";
sysread($fdi,$buf,-s "$GIT_DIR/p4/msg");
close($fdi);
open($fdo, '>>', "$GIT_DIR/p4/p4msg") or die "$GIT_DIR/p4/p4msg: $!\n";
syswrite($fdo,$buf);
close($fdo);
}
#
# Store the imported file data
#
if ($VERBOSE < 2) {
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);
print "current tree stored in commit $commit\n" if $VERBOSE;
#
# 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);
}
^ permalink raw reply
* Re: Fetch from remote A, push to remote B
From: Martin Langhoff @ 2007-05-31 7:28 UTC (permalink / raw)
To: Git Mailing List
In-Reply-To: <46a038f90705302253s68b70311h3feac7404fa23933@mail.gmail.com>
On 5/31/07, Martin Langhoff <martin.langhoff@gmail.com> wrote:
> Is there an easy way to get a "passthrough" repo setup so I can say on a cronjob
>
> git-fetch remoteA
> git-push ssh+git://host/path/to/repoB.git remotes/remoteA/<all>
After mucking about with stuff like...
find .git/refs/remotes/origin -type f -printf '%P\0' | xargs -0
-IHEADNAME git-push
git+ssh://git.catalyst.net.nz/var/git/moodle-r2-testing.git
origin/HEADNAME:HEADNAME
I figured out that a bare repo did the trick
# set things up
git-clone --bare git+ssh://host-a/bla.git bla-transfer.git
# on cron do
GIT_DIR=bla-transfer.git git-fetch git+ssh://host-a/bla.git
GIT_DIR=bla-transfer.git git-push --all git+ssh://host-b/bla.git
cheers!
martin
^ permalink raw reply
* Re: [PATCH] Introduce git version --list-features for porcelain use
From: Alex Riesen @ 2007-05-31 6:50 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: Junio C Hamano, git, Johannes Schindelin
In-Reply-To: <20070531002030.GA15714@spearce.org>
On 5/31/07, Shawn O. Pearce <spearce@spearce.org> wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> >
> > On Wed, 30 May 2007, Alex Riesen wrote:
> >
> > > git-version --features?
> >
> > Melikes.
>
> Good?
>
Was just a suggestion. Good.
^ permalink raw reply
* Breaking up repositories
From: Jason Sewall @ 2007-05-31 6:50 UTC (permalink / raw)
To: git
I recently imported my subversion repo with git-svn and I'm curious
what the best way to break up the monolithic repo (my many disparate
projects from my single svn repo) into individual git repos of their
own.
I'm still trying to get a grasp on the considerable git toolbox and I
can't seem to find the functionality I'm describing, but I'm sure it
exists - I heard Linus talk about it in that Google talk on git!
Thanks,
Jason
^ permalink raw reply
* Fetch from remote A, push to remote B
From: Martin Langhoff @ 2007-05-31 5:53 UTC (permalink / raw)
To: Git Mailing List
Is there an easy way to get a "passthrough" repo setup so I can say on a cronjob
git-fetch remoteA
git-push ssh+git://host/path/to/repoB.git remotes/remoteA/<all>
The 2 repositories cannot talk to each other directly.
cheers,
martin
^ permalink raw reply
* Re: [PATCH] always start looking up objects in the last used pack first
From: Shawn O. Pearce @ 2007-05-31 5:02 UTC (permalink / raw)
To: Nicolas Pitre; +Cc: Junio C Hamano, git
In-Reply-To: <alpine.LFD.0.99.0705302152180.11491@xanadu.home>
Nicolas Pitre <nico@cam.org> wrote:
> Pack Sort w/o this patch w/ this patch
> -------------------------------------------------------------
> recent objects last 26.4s 20.9s
> recent objects first 24.9s 18.4s
Looks pretty good.
> + next:
> + if (p == last_found)
> + p = packed_git;
> + else
> + p = p->next;
> + if (p == last_found)
> + p = p->next;
> + } while (p);
So if we didn't find the object in the pack that we found the
last object in, we restart our search with the most recent pack?
Why not just go to p->next and loop around? If we missed in this
pack and the packs are sorted by recency, wouldn't we want to just
search the next pack?
--
Shawn.
^ permalink raw reply
* Re: [RFH] QGit: how to cram a patch in a crowded screen
From: Marco Costalba @ 2007-05-31 4:39 UTC (permalink / raw)
To: Pavel Roskin; +Cc: Andy Parkins, git
In-Reply-To: <1180571013.3582.32.camel@dv>
On 5/31/07, Pavel Roskin <proski@gnu.org> wrote:
> On Wed, 2007-05-30 at 21:18 +0300, Marco Costalba wrote:
> > My (crazy) idea is:
> >
> > - Let switch from message to diff content scrolling down after the end
> > of message.
> ...
>
> I'll appreciate if you follow standard conventions for standard GUI
> actions and don't change the behavior of such basic keys as arrows.
>
> It would be better to use something more advanced for application
> specific behavior. I belong to the post-vi generation, so my mind
> cannot easily associate letters (like 'i' and 'k') with up and down
> actions. But we could use some key combinations with obvious
> "directional meaning", such as Ctrl-arrows.
>
> I've said it before, and it's worth repeating - any key must be
> discoverable through the GUI. No key or feature should be discovered by
> accident - it would be a sign of bad design.
>
yes I agree with you. The only thing that until now stopped me to add
buttons to tool bar is that it seems to me already over crowded. But I
agree keys should be discoverable. Also regarding arrow keys, they
still have the same good old action to go up and down in text content,
and will keep that.
> > So I imagine two labels for each content type:
> >
> > - for message content a top right label called "Up" and one in bottom
> > right position called "Diff"
> ...
> > I plan also to change the labels in something more intuitive with
> > scroll action, as example adding an up and down direction little
> > arrows next to them.
>
> I'm afraid it would misuse the paradigm of labels. It should be
> buttons, perhaps in the toolbar.
>
The idea of labels comes from using gmail, where in long threads you
can see the following with a coloured label with an arrow and the name
of the sender.
The labels, used in this way and decorated with small up and down
arrows, give more the meaning of "scroll down to see what's next" then
using a button, but when yhe machinery is implemented it should be
straightforward change from labels to buttons and test also with them.
> > P.S: In case someone wonders what's the goal of this label madness. It
> > is to be able to browse a repo in either both sequential directions,
> > up or down, using only the mouse wheel.
>
> It may be a cool feature, but users don't expect the mouse wheel to
> change anything other than position of the text. Switching the revision
> by the scrolling the wheel would be a feature discovered by accident.
>
Yes, this problem is still open. I think the key is the graphical
rapresentation of the labels/butons and their placement inside the
window to give the user the hint that when scrolling with the mouse
he'll reach what's advertised in the label. Again gmail use of "go to
next thread" labels gave me some ideas.
Thanks
Marco
^ permalink raw reply
* Re: [PATCH] always start looking up objects in the last used pack first
From: Nicolas Pitre @ 2007-05-31 3:24 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <alpine.LFD.0.99.0705302152180.11491@xanadu.home>
On Wed, 30 May 2007, Nicolas Pitre wrote:
> To test this I split the Linux repository into 66 packs and performed a
> "time git-rev-list --objects --all > /dev/null". Best results are as
> follows:
>
> Pack Sort w/o this patch w/ this patch
> -------------------------------------------------------------
> recent objects last 26.4s 20.9s
> recent objects first 24.9s 18.4s
>
> This shows that the pack order based on object age has some influence,
> but that the last-used-pack heuristic is even more significant in
> reducing object lookup.
For reference, the same operation on a fully packed into a single pack
repository takes 17.1s. So this looks damn good to me.
Nicolas
^ permalink raw reply
* [PATCH] always start looking up objects in the last used pack first
From: Nicolas Pitre @ 2007-05-31 2:48 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Jon Smirl said:
| Once an object reference hits a pack file it is very likely that
| following references will hit the same pack file. So first place to
| look for an object is the same place the previous object was found.
This is indeed a good heuristic so here it is. The search always start
with the pack where the last object lookup succeeded. If the wanted
object is not available there then the search continues with the normal
pack ordering.
To test this I split the Linux repository into 66 packs and performed a
"time git-rev-list --objects --all > /dev/null". Best results are as
follows:
Pack Sort w/o this patch w/ this patch
-------------------------------------------------------------
recent objects last 26.4s 20.9s
recent objects first 24.9s 18.4s
This shows that the pack order based on object age has some influence,
but that the last-used-pack heuristic is even more significant in
reducing object lookup.
Signed-off-by: Nicolas Pitre <nico@cam.org> --- Note: the
--max-pack-size to git-repack currently produces packs with old objects
after those containing recent objects. The pack sort based on
filesystem timestamp is therefore backward for those. This needs to be
fixed of course, but at least it made me think about this variable for
the test.
diff --git a/sha1_file.c b/sha1_file.c
index a3637d7..aa6d499 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1687,20 +1688,25 @@ static int matches_pack_name(struct packed_git *p, const char *ig)
static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, const char **ignore_packed)
{
+ static struct packed_git *last_found = (void *)1;
struct packed_git *p;
off_t offset;
prepare_packed_git();
+ if (!packed_git)
+ return 0;
+ p = (last_found == (void *)1) ? packed_git : last_found;
- for (p = packed_git; p; p = p->next) {
+ do {
if (ignore_packed) {
const char **ig;
for (ig = ignore_packed; *ig; ig++)
if (!matches_pack_name(p, *ig))
break;
if (*ig)
- continue;
+ goto next;
}
+
offset = find_pack_entry_one(sha1, p);
if (offset) {
/*
@@ -1713,14 +1719,23 @@ static int find_pack_entry(const unsigned char *sha1, struct pack_entry *e, cons
*/
if (p->pack_fd == -1 && open_packed_git(p)) {
error("packfile %s cannot be accessed", p->pack_name);
- continue;
+ goto next;
}
e->offset = offset;
e->p = p;
hashcpy(e->sha1, sha1);
+ last_found = p;
return 1;
}
- }
+
+ next:
+ if (p == last_found)
+ p = packed_git;
+ else
+ p = p->next;
+ if (p == last_found)
+ p = p->next;
+ } while (p);
return 0;
}
^ permalink raw reply related
* [PATCH] fix repack with --max-pack-size
From: Nicolas Pitre @ 2007-05-31 1:43 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Two issues here:
1) git-repack -a --max-pack-size=10 on the GIT repo dies pretty quick.
There is a lot of confusion about deltas that were suposed to be
reused from another pack but that get stored undeltified due to pack
limit and object size doesn't match entry->size anymore. This test
is not really worth the complexity for determining when it is valid
so get rid of it.
2) If pack limit is reached, the object buffer is freed, including when
it comes from a cached delta data. In practice the object will be
stored in a subsequent pack undeltified, but let's make sure no
pointer to freed data subsists by clearing entry->delta_data.
I also reorganized that code a bit to make it more readable.
Signed-off-by: Nicolas Pitre <nico@cam.org>
---
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 41472fc..ccb25f6 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -410,31 +410,24 @@ static unsigned long write_object(struct sha1file *f,
z_stream stream;
unsigned long maxsize;
void *out;
- if (entry->delta_data && usable_delta) {
- buf = entry->delta_data;
+ if (!usable_delta) {
+ buf = read_sha1_file(entry->sha1, &obj_type, &size);
+ if (!buf)
+ die("unable to read %s", sha1_to_hex(entry->sha1));
+ } else if (entry->delta_data) {
size = entry->delta_size;
+ buf = entry->delta_data;
+ entry->delta_data = NULL;
obj_type = (allow_ofs_delta && entry->delta->offset) ?
OBJ_OFS_DELTA : OBJ_REF_DELTA;
} else {
buf = read_sha1_file(entry->sha1, &type, &size);
if (!buf)
die("unable to read %s", sha1_to_hex(entry->sha1));
- if (size != entry->size)
- die("object %s size inconsistency (%lu vs %lu)",
- sha1_to_hex(entry->sha1), size, entry->size);
- if (usable_delta) {
- buf = delta_against(buf, size, entry);
- size = entry->delta_size;
- obj_type = (allow_ofs_delta && entry->delta->offset) ?
- OBJ_OFS_DELTA : OBJ_REF_DELTA;
- } else {
- /*
- * recover real object type in case
- * check_object() wanted to re-use a delta,
- * but we couldn't since base was in previous split pack
- */
- obj_type = type;
- }
+ buf = delta_against(buf, size, entry);
+ size = entry->delta_size;
+ obj_type = (allow_ofs_delta && entry->delta->offset) ?
+ OBJ_OFS_DELTA : OBJ_REF_DELTA;
}
/* compress the data to store and put compressed length in datalen */
memset(&stream, 0, sizeof(stream));
^ permalink raw reply related
* [PATCH] git-config: Improve documentation of git-config file handling
From: Frank Lichtenheld @ 2007-05-31 0:35 UTC (permalink / raw)
To: Git Mailing List; +Cc: Junio C Hamano, Frank Lichtenheld
In-Reply-To: <11805717372779-git-send-email-frank@lichtenheld.de>
The description which files git-config uses and how the various
command line options and environment variables affect its
behaviour was incomplete, outdated and confusing. Try to fix that.
Signed-off-by: Frank Lichtenheld <frank@lichtenheld.de>
---
Documentation/git-config.txt | 66 +++++++++++++++++++++++++++++++++++++----
1 files changed, 59 insertions(+), 7 deletions(-)
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 8f4149f..bd2fff7 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -75,11 +75,22 @@ OPTIONS
Like --get-all, but interprets the name as a regular expression.
--global::
- Use global ~/.gitconfig file rather than the repository .git/config.
+ For writing options: Write to global ~/.gitconfig file rather than
+ the repository .git/config.
++
+For reading options: Read only from global ~/.gitconfig rather than
+from all available files.
++
+See also <<FILES>>.
--system::
- Use system-wide $(prefix)/etc/gitconfig rather than the repository
- .git/config.
+ For writing options: Write to system-wide $(prefix)/etc/gitconfig
+ rather than the repository .git/config.
++
+For reading options: Read only from system-wide $(prefix)/etc/gitconfig
+rather than from all available files.
++
+See also <<FILES>>.
--remove-section::
Remove the given section from the configuration file.
@@ -106,18 +117,59 @@ OPTIONS
by 1024, 1048576, or 1073741824 prior to output.
+[[FILES]]
+FILES
+-----
+
+There are three files where git-config will search for configuration
+options:
+
+.git/config::
+ Repository specific configuration file. (The filename is
+ of course relative to the repository root, not the working
+ directory.)
+
+~/.gitconfig::
+ User-specific configuration file. Also called "global"
+ configuration file.
+
+$(prefix)/etc/gitconfig::
+ System-wide configuration file.
+
+If no further options are given, all reading options will read all of these
+files that are available. If the global or the system-wide configuration
+file are not available they will be ignored. If the repository configuration
+file is not available or readable, git-config will exit with a non-zero
+error code. However, in neither case will an error message be issued.
+
+All writing options will per default write to the repository specific
+configuration file. Note that this also affects options like '--replace-all'
+and '--unset'. *git-config will only ever change one file at a time*.
+
+You can overwrite these rules either by command line options or by environment
+variables. The '--global' and the '--system' options will limit the file used
+to the global or system-wide file respectively. The GIT_CONFIG environment
+variable has a similar effect, but you can specify any filename you want.
+
+The GIT_CONFIG_LOCAL environment variable on the other hand only changes
+the name used instead of the repository configuration file. The global and
+the system-wide configuration files will still be read. (For writing options
+this will obviously result in the same behavior as using GIT_CONFIG.)
+
+
ENVIRONMENT
-----------
GIT_CONFIG::
Take the configuration from the given file instead of .git/config.
- Using the "--global" option forces this to ~/.gitconfig.
+ Using the "--global" option forces this to ~/.gitconfig. Using the
+ "--system" option forces this to $(prefix)/etc/gitconfig.
GIT_CONFIG_LOCAL::
- Currently the same as $GIT_CONFIG; when Git will support global
- configuration files, this will cause it to take the configuration
- from the global configuration file in addition to the given file.
+ Take the configuration from the given file instead if .git/config.
+ Still read the global and the system-wide configuration files, though.
+See also <<FILES>>.
[[EXAMPLES]]
--
1.5.2-rc3.GIT
^ permalink raw reply related
* [PATCH] git-config: Various small fixes to asciidoc documentation
From: Frank Lichtenheld @ 2007-05-31 0:35 UTC (permalink / raw)
To: Git Mailing List; +Cc: Junio C Hamano, Frank Lichtenheld
Add '' around the only mentioned commandline option that didn't
had it.
Make reference to section EXAMPLE a link and rename it to
EXAMPLES because it actually contains a lot of examples ;)
Signed-off-by: Frank Lichtenheld <frank@lichtenheld.de>
---
Documentation/git-config.txt | 10 ++++++----
1 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 827a499..8f4149f 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -31,7 +31,7 @@ If you want to update or unset an option which can occur on multiple
lines, a POSIX regexp `value_regex` needs to be given. Only the
existing values that match the regexp are updated or unset. If
you want to handle the lines that do *not* match the regex, just
-prepend a single exclamation mark in front (see EXAMPLES).
+prepend a single exclamation mark in front (see also <<EXAMPLES>>).
The type specifier can be either '--int' or '--bool', which will make
'git-config' ensure that the variable(s) are of the given type and
@@ -48,7 +48,7 @@ This command will fail if:
. the section or key is invalid,
. you try to unset an option which does not exist,
. you try to unset/set an option for which multiple lines match, or
-. you use --global option without $HOME being properly set.
+. you use '--global' option without $HOME being properly set.
OPTIONS
@@ -119,8 +119,10 @@ GIT_CONFIG_LOCAL::
from the global configuration file in addition to the given file.
-EXAMPLE
--------
+
+[[EXAMPLES]]
+EXAMPLES
+--------
Given a .git/config like this:
--
1.5.2-rc3.GIT
^ permalink raw reply related
* Re: [PATCH] Introduce git version --list-features for porcelain use
From: Johannes Schindelin @ 2007-05-31 0:25 UTC (permalink / raw)
To: Shawn O. Pearce; +Cc: Junio C Hamano, git, Alex Riesen
In-Reply-To: <20070531002030.GA15714@spearce.org>
Hi,
On Wed, 30 May 2007, Shawn O. Pearce wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
> >
> > On Wed, 30 May 2007, Alex Riesen wrote:
> >
> > > git-version --features?
> >
> > Melikes.
>
> Good?
Yep. No more complaints from my side.
Ciao,
Dscho
^ permalink raw reply
* Re: [RFH] QGit: how to cram a patch in a crowded screen
From: Pavel Roskin @ 2007-05-31 0:23 UTC (permalink / raw)
To: Marco Costalba; +Cc: Andy Parkins, git
In-Reply-To: <e5bfff550705301118k4ca8720bna260e171f0eb83ca@mail.gmail.com>
On Wed, 2007-05-30 at 21:18 +0300, Marco Costalba wrote:
> My (crazy) idea is:
>
> - Let switch from message to diff content scrolling down after the end
> of message.
...
I'll appreciate if you follow standard conventions for standard GUI
actions and don't change the behavior of such basic keys as arrows.
It would be better to use something more advanced for application
specific behavior. I belong to the post-vi generation, so my mind
cannot easily associate letters (like 'i' and 'k') with up and down
actions. But we could use some key combinations with obvious
"directional meaning", such as Ctrl-arrows.
I've said it before, and it's worth repeating - any key must be
discoverable through the GUI. No key or feature should be discovered by
accident - it would be a sign of bad design.
> So I imagine two labels for each content type:
>
> - for message content a top right label called "Up" and one in bottom
> right position called "Diff"
...
> I plan also to change the labels in something more intuitive with
> scroll action, as example adding an up and down direction little
> arrows next to them.
I'm afraid it would misuse the paradigm of labels. It should be
buttons, perhaps in the toolbar.
> P.S: In case someone wonders what's the goal of this label madness. It
> is to be able to browse a repo in either both sequential directions,
> up or down, using only the mouse wheel.
It may be a cool feature, but users don't expect the mouse wheel to
change anything other than position of the text. Switching the revision
by the scrolling the wheel would be a feature discovered by accident.
--
Regards,
Pavel Roskin
^ permalink raw reply
* [PATCH] Introduce git version --list-features for porcelain use
From: Shawn O. Pearce @ 2007-05-31 0:20 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Alex Riesen, Johannes Schindelin
In-Reply-To: <Pine.LNX.4.64.0705301333410.4011@racer.site>
As a porcelain author I'm finding it difficult to keep track of
what features I can use in git-gui. Newer versions of Git have
newer capabilities but they don't always immediately get newer
version numbers that I can easily test for.
This is a simple plumbing option that lets a porcelain ask the
plumbing for its capabilities, at which point the porcelain can
work around anything missing, or recommend to the user that they
upgrade their plumbing layer.
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:
>
> On Wed, 30 May 2007, Alex Riesen wrote:
>
> > git-version --features?
>
> Melikes.
Good?
help.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
t/t0000-basic.sh | 16 +++++++++++
2 files changed, 92 insertions(+), 2 deletions(-)
diff --git a/help.c b/help.c
index 6a9af4d..9d9cccd 100644
--- a/help.c
+++ b/help.c
@@ -9,6 +9,12 @@
#include "common-cmds.h"
#include <sys/ioctl.h>
+static const char *supported_features[] = {
+ "git-k",
+ "list-features",
+ "redirect-stderr",
+};
+
/* most GUI terminals set COLUMNS (although some don't export it) */
static int term_columns(void)
{
@@ -190,10 +196,78 @@ void help_unknown_cmd(const char *cmd)
exit(1);
}
+static int is_feature_name_sane(const char *a)
+{
+ if (!*a || *a == '-')
+ return 0;
+ for (; *a; a++) {
+ if (! ((*a >= 'a' && *a <= 'z')
+ || (*a >= '0' && *a <= '9')
+ || *a == '-'))
+ return 0;
+ }
+ return 1;
+}
+
+static int cmp_feature(const void *a_, const void *b_)
+{
+ const char *a = *((const char **)a_);
+ const char *b = *((const char **)b_);
+ return strcmp(a, b);
+}
+
+static void list_features()
+{
+ unsigned cnt = ARRAY_SIZE(supported_features);
+ unsigned i;
+
+ qsort(supported_features, cnt,
+ sizeof(supported_features[0]), cmp_feature);
+ for (i = 0; i < cnt; i++) {
+ const char *f = supported_features[i];
+ if (!is_feature_name_sane(f))
+ die("feature name \"%s\" is bogus", f);
+ printf("%s\n", f);
+ }
+}
+
+static int supports_feature(const char *the_feature)
+{
+ unsigned cnt = ARRAY_SIZE(supported_features);
+ unsigned i;
+
+ for (i = 0; i < cnt; i++) {
+ if (!strcmp(supported_features[i], the_feature))
+ return 0;
+ }
+ return 1;
+}
+
+static const char version_usage[] =
+"git-version [(--list-features | --supports-feature=<name>*)]";
+
int cmd_version(int argc, const char **argv, const char *prefix)
{
- printf("git version %s\n", git_version_string);
- return 0;
+ int i, ret = 0, list = 0, test = 0;
+
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (!strcmp(arg, "--list-features"))
+ list = 1;
+ else if (!prefixcmp(arg, "--supports-feature=")) {
+ test = 1;
+ ret |= supports_feature(arg + 19);
+ } else
+ usage(version_usage);
+ }
+
+ if (list && test)
+ die("cannot use both --list-features and --supports-feature");
+ if (list)
+ list_features();
+ if (!list && !test)
+ printf("git version %s\n", git_version_string);
+ return ret;
}
int cmd_help(int argc, const char **argv, const char *prefix)
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 186de70..874bb04 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -31,6 +31,22 @@ fi
. ./test-lib.sh
################################################################
+# git version
+
+test_expect_success \
+ 'git version is functional' \
+ 'git version'
+test_expect_success \
+ 'git version --list-features' \
+ 'git version --list-features'
+test_expect_success \
+ 'feature "list-features" is supported' \
+ 'git version --supports-feature=list-features'
+test_expect_failure \
+ 'feature "THISNEVERWILLBEAGITFEATURE" is not supported' \
+ 'git version --supports-feature=THISNEVERWILLBEAGITFEATURE'
+
+################################################################
# git-init has been done in an empty repository.
# make sure it is empty.
--
1.5.2.869.g6b3ba
^ permalink raw reply related
* Re: [PATCH] Let .git/config specify the url for submodules
From: Lars Hjemli @ 2007-05-31 0:17 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <1180385483418-git-send-email-hjemli@gmail.com>
On 5/28/07, Lars Hjemli <hjemli@gmail.com> wrote:
> This changes git-submodule in a few ways:
Please don't apply the "Let .git/config specify the url for
submodules" patch, I'm having second thoughts ;-)
Your design outline in
http://article.gmane.org/gmane.comp.version-control.git/48287 is
obviously superior, and I'd like to take a stab at it with something
like this:
1. 'git-submodule init' saves submodule name and suggested url from
.gitmodules into .git/config (submodule.$name.url)
2. 'git-submodule update' keeps the work tree updated for submodules
with five separate (and optional) operations:
a) git-clone --bare $url .git/submodules/$name.git
b) git-clone -l -s .git/submodules/$name.git $path
c) cd .git/submodules/$name.git && git-fetch
d) cd $path && git-fetch
e) cd $path && git-checkout $sha1
3) 'git-submodule push' runs something like 'cd $path && git push
origin $branch', where $branch is found in .gitmodules
(path.$path.branch).
A remaining issue is how to detect if step 2b is necessary if a
submodule is already checked out at the submodule path, but I guess
remote.origin.url in the checked out submodule would be the thing to
peek into.
Also, step 2c/2d should obviously only be performed if the requested
sha1 isn't available, which should be trivial to detect with
'git-cat-file -e'.
Could this turn out to be an acceptable solution?
--
larsh
^ permalink raw reply
* gitk bug
From: Han-Wen Nienhuys @ 2007-05-30 23:31 UTC (permalink / raw)
To: git
git init
mkdir -p x/y/z
echo hoi > x/y/z/file1
echo hoi > x/y/z/file2
git add .
git commit -m 'start'
gitk
->
click "Tree". Open "x" directory.
Result:
can't read "treeheight(y/)": no such element in array
can't read "treeheight(y/)": no such element in array
(reading value of variable to increment)
invoked from within
"incr treeheight($x) $n"
(procedure "treeopendir" line 15)
invoked from within
"treeopendir $w $e"
(procedure "treeclick" line 20)
invoked from within
this is with
commit 322bcd9a9a2c0081c66414bde64e0d443c9ec922
Merge: a77a33a... 8558fd9...
Author: Junio C Hamano <junkio@cox.net>
Date: Tue May 29 01:24:20 2007 -0700
--
Han-Wen Nienhuys - hanwen@xs4all.nl - http://www.xs4all.nl/~hanwen
^ permalink raw reply
* Re: [PATCH] Makefile: Use generic rule to build test programs
From: Shawn O. Pearce @ 2007-05-30 23:27 UTC (permalink / raw)
To: Julian Phillips; +Cc: Junio C Hamano, git
In-Reply-To: <20070530232206.25366.89397.julian@quantumfyre.co.uk>
Julian Phillips <julian@quantumfyre.co.uk> wrote:
> Use a generic make rule to build all the test programs, rather than
> specifically mentioning each one.
>
> Signed-off-by: Julian Phillips <julian@quantumfyre.co.uk>
Yes. ;-)
I was going to do this tonight. Thank you for beating me to it!
--
Shawn.
^ permalink raw reply
* [PATCH] Makefile: Use generic rule to build test programs
From: Julian Phillips @ 2007-05-30 23:18 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Shawn O. Pearce
Use a generic make rule to build all the test programs, rather than
specifically mentioning each one.
Signed-off-by: Julian Phillips <julian@quantumfyre.co.uk>
---
On Tue, 29 May 2007, Junio C Hamano wrote:
> Hmmm. One wonders if we can do something about these apparent
> similarities with all these rules...
Something like this?
Makefile | 24 +++++-------------------
1 files changed, 5 insertions(+), 19 deletions(-)
diff --git a/Makefile b/Makefile
index 7527734..cac0a4a 100644
--- a/Makefile
+++ b/Makefile
@@ -942,7 +942,7 @@ endif
### Testing rules
-TEST_PROGRAMS = test-chmtime$X test-genrandom$X
+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X
all:: $(TEST_PROGRAMS)
@@ -955,26 +955,12 @@ export NO_SVN_TESTS
test: all
$(MAKE) -C t/ all
-test-date$X: test-date.c date.o ctype.o
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o
+test-date$X: date.o ctype.o
-test-delta$X: test-delta.o diff-delta.o patch-delta.o $(GITLIBS)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+test-delta$X: diff-delta.o patch-delta.o
-test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
-
-test-sha1$X: test-sha1.o $(GITLIBS)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
-
-test-match-trees$X: test-match-trees.o $(GITLIBS)
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
-
-test-chmtime$X: test-chmtime.c
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
-
-test-genrandom$X: test-genrandom.c
- $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $<
+test-%$X: test-%.o $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
check-sha1:: test-sha1$X
./test-sha1.sh
--
1.5.2
^ permalink raw reply related
* Re: [PATCH] gitweb: handle non UTF-8 text
From: Robin Rosenberg @ 2007-05-30 20:18 UTC (permalink / raw)
To: Martin Koegler
Cc: Jakub Narebski, Petr Baudis, git, Martin Langhoff, Martyn Smith
In-Reply-To: <20070529215536.GA13250@auto.tuwien.ac.at>
tisdag 29 maj 2007 skrev Martin Koegler:
> On Tue, May 29, 2007 at 11:21:11AM +0200, Jakub Narebski wrote:
> > On Tue, 29 May 2007, Petr Baudis wrote:
> > > On Mon, May 28, 2007 at 10:47:34PM CEST, Martin Koegler wrote:
> >
> > >> gitweb assumes, that everything is in UTF-8. If a text contains invalid
> > >> UTF-8 character sequences, the text must be in a different encoding.
> >
> > But it doesn't tell us _what_ is the encoding. For commit messages,
> > with reasonable new git, we have 'encoding' header if git known that
> > commit message was not in utf-8.
> >
> > By the way, I winder why we don't have such header for tag objects
> > (i18n.tagEncoding ;-)...
>
> Why do I need to set i18n.commitEncoding on a normal Linux systems? We
I've asked the same question.. :(
> have a locale, which contains this information. With this, its more
> likely, that the commits can be read correctly later, if somebody
> forget to set "i18n.commitEncoding" in a repository.
No 'if'. Users are virtually guaranteed to forget this setting.
>
> UTF-8 is not the universal, dropin solution for ISO-8859-1. It has some drawbacks:
> - Some operations are slower, eg.
> - Anything using string length/character position is more complicated.
We'll have to live with that. A nice property of valid UTF-8 is that many operations can
be performed without decoding (like looking for a substring).
>
> For some problems, UTF-16 might be a simpler solution.
UTF-16 is also variable width (one or two code units). Most apps get away by pretending it is
fixed width, simply because that works for most people, but then I'm not sure people in asia
aren't really happy with that assumption either.
> I would use i18n.commitEncoding only as last fallback. In a project
> more different encodings could be used and the guessing logic may need
> additional parameter, so I would create a own set of config parameters
> for this.
There aren't many simple ways of guessing. The UTF-8 vs other test is simple
and very reliable for western encodings (and merely good for others, if I'm not misinformed).
The i18n.commitEncoding is just a hint. Another hint is the host's encoding.
1. if lookslike(UTF-8) => assume UTF-8 else...
2. commit's encoding is valid for the text => use it else...
3. i18n.commitEncoding ...
4. gitweb.commitencoding ....
5. server's location charset ...
6. assume iso-8859-1
Yet another would be to have an extra option to switch encoding on-demand in the gui.
BTW, there's another thread on notes. Maybe they be used to "fix" badly encoded messages
if and when they get a final implementation.
-- robn
^ permalink raw reply
* Re: [MinGW port] Unable to repack on Clearcase dynamic views
From: Nguyen Thai Ngoc Duy @ 2007-05-30 19:48 UTC (permalink / raw)
To: Robin Rosenberg
Cc: Paolo Teti, Git Mailing List, Matthias Lederhofer, Junio C Hamano
In-Reply-To: <200705302128.34111.robin.rosenberg.lists@dewire.com>
On 5/30/07, Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
> onsdag 30 maj 2007 skrev Nguyen Thai Ngoc Duy:
> > Clearcase symlinks require checkout/checkin stuffs that I really don't
> > want to mess up with. Moreover, it seems not work if the link
> > destination is out of clearcase view (which is what I wanted).
>
> Well obviously.. :/
>
> I guess junction points in dynamic views are out of the question too. Would
> setting GIT_DIR be too cumbersome?
Well, I've got the same errors as before in this thread :)
http://thread.gmane.org/gmane.comp.version-control.git/41586
--
Duy
^ permalink raw reply
* Re: [PATCH] Don't ignore write failure from git-diff, git-log, etc.
From: Jim Meyering @ 2007-05-30 19:43 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vd50i2o9c.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano <junkio@cox.net> wrote:
> Jim Meyering <jim@meyering.net> writes:
>> Junio C Hamano <junkio@cox.net> wrote:
>>> Jim Meyering <jim@meyering.net> writes:
>>>
>>>> Of course error messages are annoying when your short-pipe-read is
>>>> _deliberate_ (tho, most real uses of git tools will actually get no
>>>> message to be annoyed about[*]), but what if there really *is* a mistake?
>>>> Try this:
>>>>
>>>> # You want to force git to ignore the error.
>>>> $ trap '' PIPE; git-rev-list HEAD | sync
>>>> $
>>>
>>> It is perfectly valid (although it is stupid) for a Porcelain
>>> script to do this:
>>>
>>> latest_by_jim=$(git log --pretty=oneline --author='Jim' | head -n 1)
>>> case "$latest_by_jim" in
>>> '') echo "No commit by Jim" ;;
>>> *) # do something interesting on the commit
>>> ;;;
>>> esac
>>
>> Hi Junio,
>>
>> The above snippet (prepending a single #!/bin/bash line) doesn't provoke
>> an EPIPE diagnostic from my patched git. In fact, even if you're using
>> an old, unpatched version of bash, it provokes *no* diagnostic at all.
>
>> To provoke a diagnostic (from bash, not git), using old unpatched bash,
>> you need a script doing output from a subshell, e.g.:
>>
>> #!/tmp/bash-3.0/bash
>> for x in 1; do
>> git-log
>> done | head -1
>
> I haven't thought it through, but isn't the above example only
> talking about the "Broken pipe" message? Surely, you would get
I'm not sure which "Broken pipe" message you mean.
There are two types of "Broken pipe" messages.
There's the old, verbose one from bash that includes script line-number,
pid, and killed command name. Old, unpatched versions of bash
print that message whenever bash kills a process with SIGPIPE.
Then there's the application (EPIPE) one, that can be printed
by a writing application like git,cat,seq,etc. only when SIGPIPE
stops a write but doesn't kill the writer.
In that case, the write syscall fails with errno==EPIPE,
and if it's diagnosed by the application, you get e.g.,
seq: write error: Broken pipe
Since the script above is not ignoring SIGPIPE, the git-log process
is killed by bash-3.0, and that old version of bash announces the killing
with the verbose message:
/t/bp-demo: line 2: 14474 Broken pipe git-log
Any other patched or newer version of bash will print the
single requested line on stdout and nothing on stderr.
> that message from older Bash if you have a shell loop on the
> upstream side of the pipe no matter what we (the command that is
> run by the shell loop) do, and trap is needed to squelch it.
Right.
That's why no one is using such broken shells anymore.
And why no porcelain tools incur the penalty of ignoring
SIGQUIT anymore either.
> But I do not see how this pipeline, where git-rev-list produces
> more than what fits in the in-kernel pipe buffer:
>
> "git-rev-list a lot of data | head -n 1"
>
> would not catch EPIPE and say "Broken Pipe" with your patch.
I haven't debugged the old bash to see why that first one
fails to provoke a broken pipe message (from bash).
Unless you add a line like "trap '' PIPE" before it, bash kills
the writer with SIGPIPE, and so my patch is irrelevant,
because the failing write syscall never returns.
> Especially if the downstream is sufficiently slow (say, replace
> it with "(sleep 10 && head -n 1)", perhaps), wouldn't the
> upstream produce enough without being read, gets stuck on write,
> and when the downstream exits, it would notice its write(2)
> failed with EPIPE, wouldn't it?
Are you presuming SIGPIPE is ignored?
> Maybe you are talking about your updated patch?
I was talking about the initial patch, or the one that
also removed the errno == EPIPE tests.
^ permalink raw reply
* Re: [MinGW port] Unable to repack on Clearcase dynamic views
From: Robin Rosenberg @ 2007-05-30 19:28 UTC (permalink / raw)
To: Nguyen Thai Ngoc Duy
Cc: Paolo Teti, Git Mailing List, Matthias Lederhofer, Junio C Hamano
In-Reply-To: <fcaeb9bf0705301208p6ce00315uc3cde4d43903ec0c@mail.gmail.com>
onsdag 30 maj 2007 skrev Nguyen Thai Ngoc Duy:
> Clearcase symlinks require checkout/checkin stuffs that I really don't
> want to mess up with. Moreover, it seems not work if the link
> destination is out of clearcase view (which is what I wanted).
Well obviously.. :/
I guess junction points in dynamic views are out of the question too. Would
setting GIT_DIR be too cumbersome?
-- robin
^ permalink raw reply
* Re: [PATCH] missing return
From: Jerald Fitzjerald @ 2007-05-30 19:16 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
In-Reply-To: <7vk5uq2pio.fsf@assigned-by-dhcp.cox.net>
Junio C Hamano wrote:
>I realize that I've seen this pointed out by somebody already.
>
It was suggested on irc a couple of days ago.
Signed-off-by: Jerald Fitzjerald <jfj@freemail.gr>
---
diff --git a/base85.c b/base85.c
--- a/base85.c
+++ b/base85.c
@@ -66,7 +66,7 @@ int decode_85(char *dst, const char *buf
*/
if (0x03030303 < acc ||
0xffffffff - de < (acc *= 85))
- error("invalid base85 sequence %.5s", buffer-5);
+ return error("invalid base85 sequence %.5s", buffer-5);
acc += de;
say1(" %08x", acc);
^ permalink raw reply
* Re: [MinGW port] Unable to repack on Clearcase dynamic views
From: Nguyen Thai Ngoc Duy @ 2007-05-30 19:08 UTC (permalink / raw)
To: Robin Rosenberg
Cc: Paolo Teti, Git Mailing List, Matthias Lederhofer, Junio C Hamano
In-Reply-To: <200705302028.15549.robin.rosenberg.lists@dewire.com>
On 5/30/07, Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:
> onsdag 30 maj 2007 skrev Nguyen Thai Ngoc Duy:
> > On 5/30/07, Paolo Teti <paolo.teti@gmail.com> wrote:
> > > 2007/5/30, Nguyen Thai Ngoc Duy <pclouds@gmail.com>:
> > > > Hi,
> > > >
> > > > On clearcase dynamic views (kind of virtual drives), "mv -f a b" won't
> > > > work if "a" is read-only. Because git-repack.sh removes all write
> > > > permission before moving packs, it fails on clearcase dynamic views.
> > > >
> > > > My approach is rather ugly. Does anyone have a better solution?
> > >
> > > I hate clearcase, but honestly I have used it forced by a customer..
> > > The real problem is why you need to do a repack in a CC view, but..
> >
> > I don't like it either :) But you remind me the Matthias' workdir
> > patchset. With that I can move out .git to a real drive/filesystem
> > (which should also give better performance).
> >
> > I think it's a valid use case. Anyone remember why Matthias' patchset
> > was dropped?
> > It was last mentioned in
> > http://article.gmane.org/gmane.comp.version-control.git/43041
> >
> > Junio, Matthias? May I help?
>
> Wasn't because it's better to make .git a link to a repository somewhere
> else? Just a guess.
>
> ln -s /somewhere/repo/.git .git
>
> Works fine for me (yes, in a clearcase dynamic view).
Clearcase symlinks require checkout/checkin stuffs that I really don't
want to mess up with. Moreover, it seems not work if the link
destination is out of clearcase view (which is what I wanted).
--
Duy
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox