Skip to content

Commit c67a9e2

Browse files
committed
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
2 parents 70d9895 + 510b094 commit c67a9e2

2 files changed

Lines changed: 156 additions & 36 deletions

File tree

git-svn.perl

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,7 @@ sub md5sum {
12651265
my $arg = shift;
12661266
my $ref = ref $arg;
12671267
my $md5 = Digest::MD5->new();
1268-
if ($ref eq 'GLOB' || $ref eq 'IO::File') {
1268+
if ($ref eq 'GLOB' || $ref eq 'IO::File' || $ref eq 'File::Temp') {
12691269
$md5->addfile($arg) or croak $!;
12701270
} elsif ($ref eq 'SCALAR') {
12711271
$md5->add($$arg) or croak $!;
@@ -1328,6 +1328,7 @@ BEGIN
13281328
}
13291329
}
13301330

1331+
13311332
my (%LOCKFILES, %INDEX_FILES);
13321333
END {
13331334
unlink keys %LOCKFILES if %LOCKFILES;
@@ -3230,13 +3231,11 @@ sub change_file_prop {
32303231

32313232
sub apply_textdelta {
32323233
my ($self, $fb, $exp) = @_;
3233-
my $fh = IO::File->new_tmpfile;
3234-
$fh->autoflush(1);
3234+
my $fh = Git::temp_acquire('svn_delta');
32353235
# $fh gets auto-closed() by SVN::TxDelta::apply(),
32363236
# (but $base does not,) so dup() it for reading in close_file
32373237
open my $dup, '<&', $fh or croak $!;
3238-
my $base = IO::File->new_tmpfile;
3239-
$base->autoflush(1);
3238+
my $base = Git::temp_acquire('git_blob');
32403239
if ($fb->{blob}) {
32413240
print $base 'link ' if ($fb->{mode_a} == 120000);
32423241
my $size = $::_repository->cat_blob($fb->{blob}, $base);
@@ -3251,9 +3250,9 @@ sub apply_textdelta {
32513250
}
32523251
}
32533252
seek $base, 0, 0 or croak $!;
3254-
$fb->{fh} = $dup;
3253+
$fb->{fh} = $fh;
32553254
$fb->{base} = $base;
3256-
[ SVN::TxDelta::apply($base, $fh, undef, $fb->{path}, $fb->{pool}) ];
3255+
[ SVN::TxDelta::apply($base, $dup, undef, $fb->{path}, $fb->{pool}) ];
32573256
}
32583257

32593258
sub close_file {
@@ -3269,35 +3268,36 @@ sub close_file {
32693268
"expected: $exp\n got: $got\n";
32703269
}
32713270
}
3272-
sysseek($fh, 0, 0) or croak $!;
32733271
if ($fb->{mode_b} == 120000) {
3274-
eval {
3275-
sysread($fh, my $buf, 5) == 5 or croak $!;
3276-
$buf eq 'link ' or die "$path has mode 120000",
3277-
" but is not a link";
3278-
};
3279-
if ($@) {
3280-
warn "$@\n";
3281-
sysseek($fh, 0, 0) or croak $!;
3282-
}
3283-
}
3272+
sysseek($fh, 0, 0) or croak $!;
3273+
sysread($fh, my $buf, 5) == 5 or croak $!;
32843274

3285-
my ($tmp_fh, $tmp_filename) = File::Temp::tempfile(UNLINK => 1);
3286-
my $result;
3287-
while ($result = sysread($fh, my $string, 1024)) {
3288-
my $wrote = syswrite($tmp_fh, $string, $result);
3289-
defined($wrote) && $wrote == $result
3290-
or croak("write $tmp_filename: $!\n");
3291-
}
3292-
defined $result or croak $!;
3293-
close $tmp_fh or croak $!;
3275+
unless ($buf eq 'link ') {
3276+
warn "$path has mode 120000",
3277+
" but is not a link\n";
3278+
} else {
3279+
my $tmp_fh = Git::temp_acquire('svn_hash');
3280+
my $res;
3281+
while ($res = sysread($fh, my $str, 1024)) {
3282+
my $out = syswrite($tmp_fh, $str, $res);
3283+
defined($out) && $out == $res
3284+
or croak("write ",
3285+
$tmp_fh->filename,
3286+
": $!\n");
3287+
}
3288+
defined $res or croak $!;
32943289

3295-
close $fh or croak $!;
3290+
($fh, $tmp_fh) = ($tmp_fh, $fh);
3291+
Git::temp_release($tmp_fh, 1);
3292+
}
3293+
}
32963294

3297-
$hash = $::_repository->hash_and_insert_object($tmp_filename);
3298-
unlink($tmp_filename);
3295+
$hash = $::_repository->hash_and_insert_object(
3296+
$fh->filename);
32993297
$hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
3300-
close $fb->{base} or croak $!;
3298+
3299+
Git::temp_release($fb->{base}, 1);
3300+
Git::temp_release($fh, 1);
33013301
} else {
33023302
$hash = $fb->{blob} or die "no blob information\n";
33033303
}
@@ -3667,7 +3667,7 @@ sub chg_file {
36673667
} elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
36683668
$self->change_file_prop($fbat,'svn:executable',undef);
36693669
}
3670-
my $fh = IO::File->new_tmpfile or croak $!;
3670+
my $fh = Git::temp_acquire('git_blob');
36713671
if ($m->{mode_b} =~ /^120/) {
36723672
print $fh 'link ' or croak $!;
36733673
$self->change_file_prop($fbat,'svn:special','*');
@@ -3686,9 +3686,8 @@ sub chg_file {
36863686
my $atd = $self->apply_textdelta($fbat, undef, $pool);
36873687
my $got = SVN::TxDelta::send_stream($fh, @$atd, $pool);
36883688
die "Checksum mismatch\nexpected: $exp\ngot: $got\n" if ($got ne $exp);
3689+
Git::temp_release($fh, 1);
36893690
$pool->clear;
3690-
3691-
close $fh or croak $!;
36923691
}
36933692

36943693
sub D {

perl/Git.pm

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ require Exporter;
5757
command_output_pipe command_input_pipe command_close_pipe
5858
command_bidi_pipe command_close_bidi_pipe
5959
version exec_path hash_object git_cmd_try
60-
remote_refs);
60+
remote_refs
61+
temp_acquire temp_release temp_reset);
6162

6263

6364
=head1 DESCRIPTION
@@ -99,7 +100,9 @@ use Carp qw(carp croak); # but croak is bad - throw instead
99100
use Error qw(:try);
100101
use Cwd qw(abs_path);
101102
use IPC::Open2 qw(open2);
102-
103+
use File::Temp ();
104+
require File::Spec;
105+
use Fcntl qw(SEEK_SET SEEK_CUR);
103106
}
104107

105108

@@ -933,6 +936,124 @@ sub _close_cat_blob {
933936
delete @$self{@vars};
934937
}
935938

939+
940+
{ # %TEMP_* Lexical Context
941+
942+
my (%TEMP_LOCKS, %TEMP_FILES);
943+
944+
=item temp_acquire ( NAME )
945+
946+
Attempts to retreive the temporary file mapped to the string C<NAME>. If an
947+
associated temp file has not been created this session or was closed, it is
948+
created, cached, and set for autoflush and binmode.
949+
950+
Internally locks the file mapped to C<NAME>. This lock must be released with
951+
C<temp_release()> when the temp file is no longer needed. Subsequent attempts
952+
to retrieve temporary files mapped to the same C<NAME> while still locked will
953+
cause an error. This locking mechanism provides a weak guarantee and is not
954+
threadsafe. It does provide some error checking to help prevent temp file refs
955+
writing over one another.
956+
957+
In general, the L<File::Handle> returned should not be closed by consumers as
958+
it defeats the purpose of this caching mechanism. If you need to close the temp
959+
file handle, then you should use L<File::Temp> or another temp file faculty
960+
directly. If a handle is closed and then requested again, then a warning will
961+
issue.
962+
963+
=cut
964+
965+
sub temp_acquire {
966+
my ($self, $name) = _maybe_self(@_);
967+
968+
my $temp_fd = _temp_cache($name);
969+
970+
$TEMP_LOCKS{$temp_fd} = 1;
971+
$temp_fd;
972+
}
973+
974+
=item temp_release ( NAME )
975+
976+
=item temp_release ( FILEHANDLE )
977+
978+
Releases a lock acquired through C<temp_acquire()>. Can be called either with
979+
the C<NAME> mapping used when acquiring the temp file or with the C<FILEHANDLE>
980+
referencing a locked temp file.
981+
982+
Warns if an attempt is made to release a file that is not locked.
983+
984+
The temp file will be truncated before being released. This can help to reduce
985+
disk I/O where the system is smart enough to detect the truncation while data
986+
is in the output buffers. Beware that after the temp file is released and
987+
truncated, any operations on that file may fail miserably until it is
988+
re-acquired. All contents are lost between each release and acquire mapped to
989+
the same string.
990+
991+
=cut
992+
993+
sub temp_release {
994+
my ($self, $temp_fd, $trunc) = _maybe_self(@_);
995+
996+
if (ref($temp_fd) ne 'File::Temp') {
997+
$temp_fd = $TEMP_FILES{$temp_fd};
998+
}
999+
unless ($TEMP_LOCKS{$temp_fd}) {
1000+
carp "Attempt to release temp file '",
1001+
$temp_fd, "' that has not been locked";
1002+
}
1003+
temp_reset($temp_fd) if $trunc and $temp_fd->opened;
1004+
1005+
$TEMP_LOCKS{$temp_fd} = 0;
1006+
undef;
1007+
}
1008+
1009+
sub _temp_cache {
1010+
my ($name) = @_;
1011+
1012+
my $temp_fd = \$TEMP_FILES{$name};
1013+
if (defined $$temp_fd and $$temp_fd->opened) {
1014+
if ($TEMP_LOCKS{$$temp_fd}) {
1015+
throw Error::Simple("Temp file with moniker '",
1016+
$name, "' already in use");
1017+
}
1018+
} else {
1019+
if (defined $$temp_fd) {
1020+
# then we're here because of a closed handle.
1021+
carp "Temp file '", $name,
1022+
"' was closed. Opening replacement.";
1023+
}
1024+
$$temp_fd = File::Temp->new(
1025+
TEMPLATE => 'Git_XXXXXX',
1026+
DIR => File::Spec->tmpdir
1027+
) or throw Error::Simple("couldn't open new temp file");
1028+
$$temp_fd->autoflush;
1029+
binmode $$temp_fd;
1030+
}
1031+
$$temp_fd;
1032+
}
1033+
1034+
=item temp_reset ( FILEHANDLE )
1035+
1036+
Truncates and resets the position of the C<FILEHANDLE>.
1037+
1038+
=cut
1039+
1040+
sub temp_reset {
1041+
my ($self, $temp_fd) = _maybe_self(@_);
1042+
1043+
truncate $temp_fd, 0
1044+
or throw Error::Simple("couldn't truncate file");
1045+
sysseek($temp_fd, 0, SEEK_SET) and seek($temp_fd, 0, SEEK_SET)
1046+
or throw Error::Simple("couldn't seek to beginning of file");
1047+
sysseek($temp_fd, 0, SEEK_CUR) == 0 and tell($temp_fd) == 0
1048+
or throw Error::Simple("expected file position to be reset");
1049+
}
1050+
1051+
sub END {
1052+
unlink values %TEMP_FILES if %TEMP_FILES;
1053+
}
1054+
1055+
} # %TEMP_* Lexical Context
1056+
9361057
=back
9371058
9381059
=head1 ERROR HANDLING

0 commit comments

Comments
 (0)