git-svn.perl: fix a false-positive in the "already exists" test

open_or_add_dir checks to see if the directory already exists or not. If it already exists and is not a directory, then we fail. However, open_or_add_dir did not previously account for the possibility that the path did exist as a file, but is deleted in the current commit. In order to prevent this legitimate case from failing, open_or_add_dir needs to know what files are deleted in the current commit. Unfortunately that information has to be plumbed through a couple of layers. Signed-off-by: Steven Walter <stevenrwalter@gmail.com> Acked-by: Eric Wong <normalperson@yhbt.net>

Steven Walter committed Feb 20, 2012 at 09:17 UTC 379862ec5a413e636d977a6ea3d618f0b3eafceb
2 files changed +44 -32
git-svn.perl
+26 -17
@@ -5137,7 +5137,7 @@ sub rmdirs {
5137 }
5138
5139 sub open_or_add_dir {
5140 - my ($self, $full_path, $baton) = @_;
5140 + my ($self, $full_path, $baton, $deletions) = @_;
5141 my $t = $self->{types}->{$full_path};
5142 if (!defined $t) {
5143 die "$full_path not known in r$self->{r} or we have a bug!\n";
@@ -5146,7 +5146,7 @@ sub open_or_add_dir {
5146 no warnings 'once';
5147 # SVN::Node::none and SVN::Node::file are used only once,
5148 # so we're shutting up Perl's warnings about them.
5149 - if ($t == $SVN::Node::none) {
5149 + if ($t == $SVN::Node::none || defined($deletions->{$full_path})) {
5150 return $self->add_directory($full_path, $baton,
5151 undef, -1, $self->{pool});
5152 } elsif ($t == $SVN::Node::dir) {
@@ -5161,17 +5161,18 @@ sub open_or_add_dir {
5161 }
5162
5163 sub ensure_path {
5164 - my ($self, $path) = @_;
5164 + my ($self, $path, $deletions) = @_;
5165 my $bat = $self->{bat};
5166 my $repo_path = $self->repo_path($path);
5167 return $bat->{''} unless (length $repo_path);
5168 +
5169 my @p = split m#/+#, $repo_path;
5170 my $c = shift @p;
5170 - $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''});
5171 + $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{''}, $deletions);
5172 while (@p) {
5173 my $c0 = $c;
5174 $c .= '/' . shift @p;
5174 - $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0});
5175 + $bat->{$c} ||= $self->open_or_add_dir($c, $bat->{$c0}, $deletions);
5176 }
5177 return $bat->{$c};
5178 }
@@ -5228,9 +5229,9 @@ sub apply_autoprops {
5229 }
5230
5231 sub A {
5231 - my ($self, $m) = @_;
5232 + my ($self, $m, $deletions) = @_;
5233 my ($dir, $file) = split_path($m->{file_b});
5233 - my $pbat = $self->ensure_path($dir);
5234 + my $pbat = $self->ensure_path($dir, $deletions);
5235 my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
5236 undef, -1);
5237 print "\tA\t$m->{file_b}\n" unless $::_q;
@@ -5240,9 +5241,9 @@ sub A {
5241 }
5242
5243 sub C {
5243 - my ($self, $m) = @_;
5244 + my ($self, $m, $deletions) = @_;
5245 my ($dir, $file) = split_path($m->{file_b});
5245 - my $pbat = $self->ensure_path($dir);
5246 + my $pbat = $self->ensure_path($dir, $deletions);
5247 my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
5248 $self->url_path($m->{file_a}), $self->{r});
5249 print "\tC\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
@@ -5259,9 +5260,9 @@ sub delete_entry {
5260 }
5261
5262 sub R {
5262 - my ($self, $m) = @_;
5263 + my ($self, $m, $deletions) = @_;
5264 my ($dir, $file) = split_path($m->{file_b});
5264 - my $pbat = $self->ensure_path($dir);
5265 + my $pbat = $self->ensure_path($dir, $deletions);
5266 my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat,
5267 $self->url_path($m->{file_a}), $self->{r});
5268 print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q;
@@ -5270,14 +5271,14 @@ sub R {
5271 $self->close_file($fbat,undef,$self->{pool});
5272
5273 ($dir, $file) = split_path($m->{file_a});
5273 - $pbat = $self->ensure_path($dir);
5274 + $pbat = $self->ensure_path($dir, $deletions);
5275 $self->delete_entry($m->{file_a}, $pbat);
5276 }
5277
5278 sub M {
5278 - my ($self, $m) = @_;
5279 + my ($self, $m, $deletions) = @_;
5280 my ($dir, $file) = split_path($m->{file_b});
5280 - my $pbat = $self->ensure_path($dir);
5281 + my $pbat = $self->ensure_path($dir, $deletions);
5282 my $fbat = $self->open_file($self->repo_path($m->{file_b}),
5283 $pbat,$self->{r},$self->{pool});
5284 print "\t$m->{chg}\t$m->{file_b}\n" unless $::_q;
@@ -5347,9 +5348,9 @@ sub chg_file {
5348 }
5349
5350 sub D {
5350 - my ($self, $m) = @_;
5351 + my ($self, $m, $deletions) = @_;
5352 my ($dir, $file) = split_path($m->{file_b});
5352 - my $pbat = $self->ensure_path($dir);
5353 + my $pbat = $self->ensure_path($dir, $deletions);
5354 print "\tD\t$m->{file_b}\n" unless $::_q;
5355 $self->delete_entry($m->{file_b}, $pbat);
5356 }
@@ -5382,10 +5383,18 @@ sub apply_diff {
5383 my ($self) = @_;
5384 my $mods = $self->{mods};
5385 my %o = ( D => 0, C => 1, R => 2, A => 3, M => 4, T => 5 );
5386 + my %deletions;
5387 +
5388 + foreach my $m (@$mods) {
5389 + if ($m->{chg} eq "D") {
5390 + $deletions{$m->{file_b}} = 1;
5391 + }
5392 + }
5393 +
5394 foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
5395 my $f = $m->{chg};
5396 if (defined $o{$f}) {
5388 - $self->$f($m);
5397 + $self->$f($m, \%deletions);
5398 } else {
5399 fatal("Invalid change type: $f");
5400 }
t/t9100-git-svn-basic.sh
+18 -15
@@ -92,9 +92,11 @@ test_expect_success "$name" '
92 echo yyy > bar/zzz/yyy &&
93 git update-index --add bar/zzz/yyy &&
94 git commit -m "$name" &&
95 - test_must_fail git svn set-tree --find-copies-harder --rmdir \
96 - ${remotes_git_svn}..mybranch3' || true
97 -
95 + git svn set-tree --find-copies-harder --rmdir \
96 + ${remotes_git_svn}..mybranch3 &&
97 + svn_cmd up "$SVN_TREE" &&
98 + test -d "$SVN_TREE"/bar/zzz &&
99 + test -e "$SVN_TREE"/bar/zzz/yyy ' || true
100
101 name='detect node change from directory to file #2'
102 test_expect_success "$name" '
@@ -134,10 +136,10 @@ test_expect_success "$name" '
136 test -x "$SVN_TREE"/exec.sh'
137
138
137 -name='executable file becomes a symlink to bar/zzz (file)'
139 +name='executable file becomes a symlink to file'
140 test_expect_success "$name" '
141 rm exec.sh &&
140 - ln -s bar/zzz exec.sh &&
142 + ln -s file exec.sh &&
143 git update-index exec.sh &&
144 git commit -m "$name" &&
145 git svn set-tree --find-copies-harder --rmdir \
@@ -148,14 +150,14 @@ test_expect_success "$name" '
150 name='new symlink is added to a file that was also just made executable'
151
152 test_expect_success "$name" '
151 - chmod +x bar/zzz &&
152 - ln -s bar/zzz exec-2.sh &&
153 - git update-index --add bar/zzz exec-2.sh &&
153 + chmod +x file &&
154 + ln -s file exec-2.sh &&
155 + git update-index --add file exec-2.sh &&
156 git commit -m "$name" &&
157 git svn set-tree --find-copies-harder --rmdir \
158 ${remotes_git_svn}..mybranch5 &&
159 svn_cmd up "$SVN_TREE" &&
158 - test -x "$SVN_TREE"/bar/zzz &&
160 + test -x "$SVN_TREE"/file &&
161 test -h "$SVN_TREE"/exec-2.sh'
162
163 name='modify a symlink to become a file'
@@ -195,14 +197,15 @@ name='check imported tree checksums expected tree checksums'
197 rm -f expected
198 if test_have_prereq UTF8
199 then
198 - echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected
200 + echo tree dc68b14b733e4ec85b04ab6f712340edc5dc936e > expected
201 fi
202 cat >> expected <<\EOF
201 -tree 83654bb36f019ae4fe77a0171f81075972087624
202 -tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1
203 -tree 0b094cbff17168f24c302e297f55bfac65eb8bd3
204 -tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
205 -tree 56a30b966619b863674f5978696f4a3594f2fca9
203 +tree c3322890dcf74901f32d216f05c5044f670ce632
204 +tree d3ccd5035feafd17b030c5732e7808cc49122853
205 +tree d03e1630363d4881e68929d532746b20b0986b83
206 +tree 149d63cd5878155c846e8c55d7d8487de283f89e
207 +tree 312b76e4f64ce14893aeac8591eb3960b065e247
208 +tree 149d63cd5878155c846e8c55d7d8487de283f89e
209 tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
210 tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
211 EOF