aboutsummaryrefslogtreecommitdiff
path: root/git-svn.perl
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-01-11 16:51:10 -0800
committerEric Wong <normalperson@yhbt.net>2009-01-18 15:38:27 -0800
commitdbc6c74d0858d77e61e092a48d467e725211f8e9 (patch)
treee2bf25ee4cbe252a43e8ada2191d35e066d2bf2b /git-svn.perl
parenta83c88525ed6d8d940f8cf7f21e92b189f7844e3 (diff)
downloadgit-dbc6c74d0858d77e61e092a48d467e725211f8e9.tar.gz
git-dbc6c74d0858d77e61e092a48d467e725211f8e9.tar.xz
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>
Diffstat (limited to 'git-svn.perl')
-rwxr-xr-xgit-svn.perl55
1 files changed, 50 insertions, 5 deletions
diff --git a/git-svn.perl b/git-svn.perl
index ad01e182d..1b87a6529 100755
--- a/git-svn.perl
+++ b/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,11 +3416,19 @@ 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";
+ " but is not a link\n";
} else {
my $tmp_fh = $::_repository->temp_acquire(
'svn_hash');