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
53
git-svn.perl
53
git-svn.perl
@ -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 {
|
||||
|
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