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:
parent
a83c88525e
commit
dbc6c74d08
55
git-svn.perl
55
git-svn.perl
@ -3200,7 +3200,10 @@ sub new {
|
|||||||
my ($class, $git_svn) = @_;
|
my ($class, $git_svn) = @_;
|
||||||
my $self = SVN::Delta::Editor->new;
|
my $self = SVN::Delta::Editor->new;
|
||||||
bless $self, $class;
|
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->{empty} = {};
|
||||||
$self->{dir_prop} = {};
|
$self->{dir_prop} = {};
|
||||||
$self->{file_prop} = {};
|
$self->{file_prop} = {};
|
||||||
@ -3210,6 +3213,34 @@ sub new {
|
|||||||
$self;
|
$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 {
|
sub set_path_strip {
|
||||||
my ($self, $path) = @_;
|
my ($self, $path) = @_;
|
||||||
$self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path;
|
$self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path;
|
||||||
@ -3268,6 +3299,9 @@ sub open_file {
|
|||||||
unless (defined $mode && defined $blob) {
|
unless (defined $mode && defined $blob) {
|
||||||
die "$path was not found in commit $self->{c} (r$rev)\n";
|
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,
|
{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
|
||||||
pool => SVN::Pool->new, action => 'M' };
|
pool => SVN::Pool->new, action => 'M' };
|
||||||
}
|
}
|
||||||
@ -3346,7 +3380,10 @@ sub apply_textdelta {
|
|||||||
open my $dup, '<&', $fh or croak $!;
|
open my $dup, '<&', $fh or croak $!;
|
||||||
my $base = $::_repository->temp_acquire('git_blob');
|
my $base = $::_repository->temp_acquire('git_blob');
|
||||||
if ($fb->{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);
|
my $size = $::_repository->cat_blob($fb->{blob}, $base);
|
||||||
die "Failed to read object $fb->{blob}" if ($size < 0);
|
die "Failed to read object $fb->{blob}" if ($size < 0);
|
||||||
|
|
||||||
@ -3379,11 +3416,19 @@ sub close_file {
|
|||||||
}
|
}
|
||||||
if ($fb->{mode_b} == 120000) {
|
if ($fb->{mode_b} == 120000) {
|
||||||
sysseek($fh, 0, 0) or croak $!;
|
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",
|
warn "$path has mode 120000",
|
||||||
" but is not a link\n";
|
" 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 {
|
} else {
|
||||||
my $tmp_fh = $::_repository->temp_acquire(
|
my $tmp_fh = $::_repository->temp_acquire(
|
||||||
'svn_hash');
|
'svn_hash');
|
||||||
|
90
t/t9131-git-svn-empty-symlink.sh
Executable file
90
t/t9131-git-svn-empty-symlink.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user