提交 c67a9e26 编写于 作者: J Junio C Hamano

Merge git://git.bogomips.org/git-svn

* git://git.bogomips.org/git-svn:
  git-svn: Reduce temp file usage when dealing with non-links
  git-svn: Make it incrementally faster by minimizing temp files
  Git.pm: Add faculties to allow temp files to be cached
...@@ -1265,7 +1265,7 @@ sub md5sum { ...@@ -1265,7 +1265,7 @@ sub md5sum {
my $arg = shift; my $arg = shift;
my $ref = ref $arg; my $ref = ref $arg;
my $md5 = Digest::MD5->new(); my $md5 = Digest::MD5->new();
if ($ref eq 'GLOB' || $ref eq 'IO::File') { if ($ref eq 'GLOB' || $ref eq 'IO::File' || $ref eq 'File::Temp') {
$md5->addfile($arg) or croak $!; $md5->addfile($arg) or croak $!;
} elsif ($ref eq 'SCALAR') { } elsif ($ref eq 'SCALAR') {
$md5->add($$arg) or croak $!; $md5->add($$arg) or croak $!;
...@@ -1328,6 +1328,7 @@ BEGIN ...@@ -1328,6 +1328,7 @@ BEGIN
} }
} }
my (%LOCKFILES, %INDEX_FILES); my (%LOCKFILES, %INDEX_FILES);
END { END {
unlink keys %LOCKFILES if %LOCKFILES; unlink keys %LOCKFILES if %LOCKFILES;
...@@ -3230,13 +3231,11 @@ sub change_file_prop { ...@@ -3230,13 +3231,11 @@ sub change_file_prop {
sub apply_textdelta { sub apply_textdelta {
my ($self, $fb, $exp) = @_; my ($self, $fb, $exp) = @_;
my $fh = IO::File->new_tmpfile; my $fh = Git::temp_acquire('svn_delta');
$fh->autoflush(1);
# $fh gets auto-closed() by SVN::TxDelta::apply(), # $fh gets auto-closed() by SVN::TxDelta::apply(),
# (but $base does not,) so dup() it for reading in close_file # (but $base does not,) so dup() it for reading in close_file
open my $dup, '<&', $fh or croak $!; open my $dup, '<&', $fh or croak $!;
my $base = IO::File->new_tmpfile; my $base = Git::temp_acquire('git_blob');
$base->autoflush(1);
if ($fb->{blob}) { if ($fb->{blob}) {
print $base 'link ' if ($fb->{mode_a} == 120000); print $base 'link ' if ($fb->{mode_a} == 120000);
my $size = $::_repository->cat_blob($fb->{blob}, $base); my $size = $::_repository->cat_blob($fb->{blob}, $base);
...@@ -3251,9 +3250,9 @@ sub apply_textdelta { ...@@ -3251,9 +3250,9 @@ sub apply_textdelta {
} }
} }
seek $base, 0, 0 or croak $!; seek $base, 0, 0 or croak $!;
$fb->{fh} = $dup; $fb->{fh} = $fh;
$fb->{base} = $base; $fb->{base} = $base;
[ SVN::TxDelta::apply($base, $fh, undef, $fb->{path}, $fb->{pool}) ]; [ SVN::TxDelta::apply($base, $dup, undef, $fb->{path}, $fb->{pool}) ];
} }
sub close_file { sub close_file {
...@@ -3269,35 +3268,36 @@ sub close_file { ...@@ -3269,35 +3268,36 @@ sub close_file {
"expected: $exp\n got: $got\n"; "expected: $exp\n got: $got\n";
} }
} }
sysseek($fh, 0, 0) or croak $!;
if ($fb->{mode_b} == 120000) { if ($fb->{mode_b} == 120000) {
eval { sysseek($fh, 0, 0) or croak $!;
sysread($fh, my $buf, 5) == 5 or croak $!; sysread($fh, my $buf, 5) == 5 or croak $!;
$buf eq 'link ' or die "$path has mode 120000",
" but is not a link";
};
if ($@) {
warn "$@\n";
sysseek($fh, 0, 0) or croak $!;
}
}
my ($tmp_fh, $tmp_filename) = File::Temp::tempfile(UNLINK => 1); unless ($buf eq 'link ') {
my $result; warn "$path has mode 120000",
while ($result = sysread($fh, my $string, 1024)) { " but is not a link\n";
my $wrote = syswrite($tmp_fh, $string, $result); } else {
defined($wrote) && $wrote == $result my $tmp_fh = Git::temp_acquire('svn_hash');
or croak("write $tmp_filename: $!\n"); my $res;
} while ($res = sysread($fh, my $str, 1024)) {
defined $result or croak $!; my $out = syswrite($tmp_fh, $str, $res);
close $tmp_fh or croak $!; defined($out) && $out == $res
or croak("write ",
$tmp_fh->filename,
": $!\n");
}
defined $res or croak $!;
close $fh or croak $!; ($fh, $tmp_fh) = ($tmp_fh, $fh);
Git::temp_release($tmp_fh, 1);
}
}
$hash = $::_repository->hash_and_insert_object($tmp_filename); $hash = $::_repository->hash_and_insert_object(
unlink($tmp_filename); $fh->filename);
$hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n"; $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
close $fb->{base} or croak $!;
Git::temp_release($fb->{base}, 1);
Git::temp_release($fh, 1);
} else { } else {
$hash = $fb->{blob} or die "no blob information\n"; $hash = $fb->{blob} or die "no blob information\n";
} }
...@@ -3667,7 +3667,7 @@ sub chg_file { ...@@ -3667,7 +3667,7 @@ sub chg_file {
} elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) { } elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
$self->change_file_prop($fbat,'svn:executable',undef); $self->change_file_prop($fbat,'svn:executable',undef);
} }
my $fh = IO::File->new_tmpfile or croak $!; my $fh = Git::temp_acquire('git_blob');
if ($m->{mode_b} =~ /^120/) { if ($m->{mode_b} =~ /^120/) {
print $fh 'link ' or croak $!; print $fh 'link ' or croak $!;
$self->change_file_prop($fbat,'svn:special','*'); $self->change_file_prop($fbat,'svn:special','*');
...@@ -3686,9 +3686,8 @@ sub chg_file { ...@@ -3686,9 +3686,8 @@ sub chg_file {
my $atd = $self->apply_textdelta($fbat, undef, $pool); my $atd = $self->apply_textdelta($fbat, undef, $pool);
my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool); my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool);
die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp); die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
Git::temp_release($fh, 1);
$pool->clear; $pool->clear;
close $fh or croak $!;
} }
sub D { sub D {
......
...@@ -57,7 +57,8 @@ =head1 SYNOPSIS ...@@ -57,7 +57,8 @@ =head1 SYNOPSIS
command_output_pipe command_input_pipe command_close_pipe command_output_pipe command_input_pipe command_close_pipe
command_bidi_pipe command_close_bidi_pipe command_bidi_pipe command_close_bidi_pipe
version exec_path hash_object git_cmd_try version exec_path hash_object git_cmd_try
remote_refs); remote_refs
temp_acquire temp_release temp_reset);
=head1 DESCRIPTION =head1 DESCRIPTION
...@@ -99,7 +100,9 @@ =head1 DESCRIPTION ...@@ -99,7 +100,9 @@ =head1 DESCRIPTION
use Error qw(:try); use Error qw(:try);
use Cwd qw(abs_path); use Cwd qw(abs_path);
use IPC::Open2 qw(open2); use IPC::Open2 qw(open2);
use File::Temp ();
require File::Spec;
use Fcntl qw(SEEK_SET SEEK_CUR);
} }
...@@ -933,6 +936,124 @@ sub _close_cat_blob { ...@@ -933,6 +936,124 @@ sub _close_cat_blob {
delete @$self{@vars}; delete @$self{@vars};
} }
{ # %TEMP_* Lexical Context
my (%TEMP_LOCKS, %TEMP_FILES);
=item temp_acquire ( NAME )
Attempts to retreive the temporary file mapped to the string C<NAME>. If an
associated temp file has not been created this session or was closed, it is
created, cached, and set for autoflush and binmode.
Internally locks the file mapped to C<NAME>. This lock must be released with
C<temp_release()> when the temp file is no longer needed. Subsequent attempts
to retrieve temporary files mapped to the same C<NAME> while still locked will
cause an error. This locking mechanism provides a weak guarantee and is not
threadsafe. It does provide some error checking to help prevent temp file refs
writing over one another.
In general, the L<File::Handle> returned should not be closed by consumers as
it defeats the purpose of this caching mechanism. If you need to close the temp
file handle, then you should use L<File::Temp> or another temp file faculty
directly. If a handle is closed and then requested again, then a warning will
issue.
=cut
sub temp_acquire {
my ($self, $name) = _maybe_self(@_);
my $temp_fd = _temp_cache($name);
$TEMP_LOCKS{$temp_fd} = 1;
$temp_fd;
}
=item temp_release ( NAME )
=item temp_release ( FILEHANDLE )
Releases a lock acquired through C<temp_acquire()>. Can be called either with
the C<NAME> mapping used when acquiring the temp file or with the C<FILEHANDLE>
referencing a locked temp file.
Warns if an attempt is made to release a file that is not locked.
The temp file will be truncated before being released. This can help to reduce
disk I/O where the system is smart enough to detect the truncation while data
is in the output buffers. Beware that after the temp file is released and
truncated, any operations on that file may fail miserably until it is
re-acquired. All contents are lost between each release and acquire mapped to
the same string.
=cut
sub temp_release {
my ($self, $temp_fd, $trunc) = _maybe_self(@_);
if (ref($temp_fd) ne 'File::Temp') {
$temp_fd = $TEMP_FILES{$temp_fd};
}
unless ($TEMP_LOCKS{$temp_fd}) {
carp "Attempt to release temp file '",
$temp_fd, "' that has not been locked";
}
temp_reset($temp_fd) if $trunc and $temp_fd->opened;
$TEMP_LOCKS{$temp_fd} = 0;
undef;
}
sub _temp_cache {
my ($name) = @_;
my $temp_fd = \$TEMP_FILES{$name};
if (defined $$temp_fd and $$temp_fd->opened) {
if ($TEMP_LOCKS{$$temp_fd}) {
throw Error::Simple("Temp file with moniker '",
$name, "' already in use");
}
} else {
if (defined $$temp_fd) {
# then we're here because of a closed handle.
carp "Temp file '", $name,
"' was closed. Opening replacement.";
}
$$temp_fd = File::Temp->new(
TEMPLATE => 'Git_XXXXXX',
DIR => File::Spec->tmpdir
) or throw Error::Simple("couldn't open new temp file");
$$temp_fd->autoflush;
binmode $$temp_fd;
}
$$temp_fd;
}
=item temp_reset ( FILEHANDLE )
Truncates and resets the position of the C<FILEHANDLE>.
=cut
sub temp_reset {
my ($self, $temp_fd) = _maybe_self(@_);
truncate $temp_fd, 0
or throw Error::Simple("couldn't truncate file");
sysseek($temp_fd, 0, SEEK_SET) and seek($temp_fd, 0, SEEK_SET)
or throw Error::Simple("couldn't seek to beginning of file");
sysseek($temp_fd, 0, SEEK_CUR) == 0 and tell($temp_fd) == 0
or throw Error::Simple("expected file position to be reset");
}
sub END {
unlink values %TEMP_FILES if %TEMP_FILES;
}
} # %TEMP_* Lexical Context
=back =back
=head1 ERROR HANDLING =head1 ERROR HANDLING
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册