git-svn: handle empty files marked as symlinks in SVN

Broken SVN clients generate empty files with the svn:special set
to '*'.  This attempts to denote a symlink pointing to a file
with an empty path (""), which cannot be generated on a POSIX
system.

Thus, we mimic the behavior of svn(1) and create a zero-byte
file in our tree.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
This commit is contained in:
Eric Wong 2009-01-11 16:51:10 -08:00
parent a83c88525e
commit dbc6c74d08
2 changed files with 140 additions and 5 deletions

View File

@ -3200,7 +3200,10 @@ sub new {
my ($class, $git_svn) = @_;
my $self = SVN::Delta::Editor->new;
bless $self, $class;
$self->{c} = $git_svn->{last_commit} if exists $git_svn->{last_commit};
if (exists $git_svn->{last_commit}) {
$self->{c} = $git_svn->{last_commit};
$self->{empty_symlinks} = _mark_empty_symlinks($git_svn);
}
$self->{empty} = {};
$self->{dir_prop} = {};
$self->{file_prop} = {};
@ -3210,6 +3213,34 @@ sub new {
$self;
}
# this uses the Ra object, so it must be called before do_{switch,update},
# not inside them (when the Git::SVN::Fetcher object is passed) to
# do_{switch,update}
sub _mark_empty_symlinks {
my ($git_svn) = @_;
my %ret;
my ($rev, $cmt) = $git_svn->last_rev_commit;
return {} unless ($rev && $cmt);
chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`);
my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt);
local $/ = "\0";
my $pfx = $git_svn->{path};
$pfx .= '/' if length($pfx);
while (<$ls>) {
chomp;
s/\A100644 blob $empty_blob\t//o or next;
my $path = $_;
my (undef, $props) =
$git_svn->ra->get_file($pfx.$path, $rev, undef);
if ($props->{'svn:special'}) {
$ret{$path} = 1;
}
}
command_close_pipe($ls, $ctx);
\%ret;
}
sub set_path_strip {
my ($self, $path) = @_;
$self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path;
@ -3268,6 +3299,9 @@ sub open_file {
unless (defined $mode && defined $blob) {
die "$path was not found in commit $self->{c} (r$rev)\n";
}
if ($mode eq '100644' && $self->{empty_symlinks}->{$path}) {
$mode = '120000';
}
{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
pool => SVN::Pool->new, action => 'M' };
}
@ -3346,7 +3380,10 @@ sub apply_textdelta {
open my $dup, '<&', $fh or croak $!;
my $base = $::_repository->temp_acquire('git_blob');
if ($fb->{blob}) {
print $base 'link ' if ($fb->{mode_a} == 120000);
if ($fb->{mode_a} eq '120000' &&
! $self->{empty_symlinks}->{$fb->{path}}) {
print $base 'link ' or die "print $!\n";
}
my $size = $::_repository->cat_blob($fb->{blob}, $base);
die "Failed to read object $fb->{blob}" if ($size < 0);
@ -3379,9 +3416,17 @@ sub close_file {
}
if ($fb->{mode_b} == 120000) {
sysseek($fh, 0, 0) or croak $!;
sysread($fh, my $buf, 5) == 5 or croak $!;
my $rd = sysread($fh, my $buf, 5);
unless ($buf eq 'link ') {
if (!defined $rd) {
croak "sysread: $!\n";
} elsif ($rd == 0) {
warn "$path has mode 120000",
" but it points to nothing\n",
"converting to an empty file with mode",
" 100644\n";
$fb->{mode_b} = '100644';
} elsif ($buf ne 'link ') {
warn "$path has mode 120000",
" but is not a link\n";
} else {

View File

@ -0,0 +1,90 @@
#!/bin/sh
test_description='test that git handles an svn repository with empty symlinks'
. ./lib-git-svn.sh
test_expect_success 'load svn dumpfile' '
svnadmin load "$rawsvnrepo" <<EOF
SVN-fs-dump-format-version: 2
UUID: 60780f9a-7df5-43b4-83ab-60e2c0673ef7
Revision-number: 0
Prop-content-length: 56
Content-length: 56
K 8
svn:date
V 27
2008-11-26T07:17:27.590577Z
PROPS-END
Revision-number: 1
Prop-content-length: 111
Content-length: 111
K 7
svn:log
V 4
test
K 10
svn:author
V 12
normalperson
K 8
svn:date
V 27
2008-11-26T07:18:03.511836Z
PROPS-END
Node-path: bar
Node-kind: file
Node-action: add
Prop-content-length: 33
Text-content-length: 0
Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
Content-length: 33
K 11
svn:special
V 1
*
PROPS-END
Revision-number: 2
Prop-content-length: 121
Content-length: 121
K 7
svn:log
V 13
bar => doink
K 10
svn:author
V 12
normalperson
K 8
svn:date
V 27
2008-11-27T03:55:31.601672Z
PROPS-END
Node-path: bar
Node-kind: file
Node-action: change
Text-content-length: 10
Text-content-md5: 92ca4fe7a9721f877f765c252dcd66c9
Content-length: 10
link doink
EOF
'
test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x'
test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar'
test_expect_success 'get "bar" => symlink fix from svn' \
'(cd x && git svn rebase)'
test_expect_success '"bar" becomes a symlink' 'test -L x/bar'
test_done