- When creating a tag or branch from a subdir, a disjoint branch is
    created. Then git-svn re-imports the commits using this dir as strip
    path.

    During this re-import the variable %added_placeholder is not up to
    date. Because the branch is disjoint, this variable should be empty
    in the beginning, but it's not. Because of that git-svn tries to
    delete non-existent .gitignore files and dies.

  - When creating a tag or branch from a subdir, the strip path is e.g.
    "trunk/module", but change_dir_prop() can be called with just
    "trunk". This breaks tracking of placeholder files, because it
    relise on the hash {dir_prop}, filled in change_dir_prop().

  - When creating a normal tag or branch, git-svn creates a normal
    branch without reimport, but the placeholder files in the new
    branch are not added to %added_placeholder.

This patch does 3 things:

  - It makes git-svn store paths in %added_placeholder already
    translated from "trunk/subdir/" to "tags/subdir_1.0/" during
    reimport.

  - When strip path is "trunk/subdir", don't add "trunk" to {dir_prop}
    in change_dir_prop().

  - When a normal branch is created, it takes entries in
    %added_placeholder belonging to the source branch, translates them
    to target branch and adds them to %added_placeholder.
---
 perl/Git/SVN.pm                        |  2 +
 perl/Git/SVN/Fetcher.pm                | 72 ++++++++++++++++++++++++++++++----
 t/t9160-git-svn-preserve-empty-dirs.sh | 51 ++++++++++++++++++++++--
 3 files changed, 114 insertions(+), 11 deletions(-)

diff --git a/perl/Git/SVN.pm b/perl/Git/SVN.pm
index 5273ee8..660921d 100644
--- a/perl/Git/SVN.pm
+++ b/perl/Git/SVN.pm
@@ -1143,6 +1143,7 @@ sub find_parent_branch {
                ($r0, $parent) = $gs->find_rev_before($r, 1);
        }
        if (defined $r0 && defined $parent) {
+               Git::SVN::Fetcher::_end_reimport($self, $branch_from, 
$self->path);
                print STDERR "Found branch parent: ($self->{ref_id}) $parent\n"
                             unless $::_q > 1;
                my $ed;
@@ -1395,6 +1396,7 @@ sub other_gs {
                        last if ($url eq $gs->metadata_url);
                        $ref_id .= '-';
                }
+               Git::SVN::Fetcher::_begin_reimport($self->path);
                print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
        }
        $gs
diff --git a/perl/Git/SVN/Fetcher.pm b/perl/Git/SVN/Fetcher.pm
index a5ad4cd..aaf5d9a 100644
--- a/perl/Git/SVN/Fetcher.pm
+++ b/perl/Git/SVN/Fetcher.pm
@@ -1,6 +1,7 @@
 package Git::SVN::Fetcher;
 use vars qw/@ISA $_ignore_regex $_preserve_empty_dirs $_placeholder_filename
             $_package_inited
+            $_reimportpath
             @deleted_gpath %added_placeholder $repo_id/;
 use strict;
 use warnings;
@@ -162,13 +163,59 @@ sub git_path {
                require Encode;
                Encode::from_to($path, 'UTF-8', $enc);
        }
-       if ($self->{path_strip}) {
-               $path =~ s!$self->{path_strip}!! or
-                 die "Failed to strip path '$path' ($self->{path_strip})\n";
+       _strip_path($path, $self->{path_strip})
+}
+
+sub _strip_path {
+       my ($path, $re_strip) = @_;
+       if ($re_strip) {
+               $path =~ s!$re_strip!! or
+                 die "Failed to strip path '$path' ($re_strip)\n";
        }
        $path;
 }
 
+sub _begin_reimport {
+       ( $_reimportpath ) = @_;
+       undef
+}
+
+sub _end_reimport {
+       my ( $git_svn, $branch_from, $branch_to ) = @_;
+       _try_init_package($git_svn);
+       if (defined $_reimportpath) {
+               $_reimportpath = undef;
+       } else {
+               my $re_strip = qr/^\Q$branch_from\E(\/|$)/ if length 
$branch_from;
+               foreach (values %added_placeholder) {
+                       my $path = $_;
+                       if ( (!length $branch_from) || $path =~ s!$re_strip!! ) 
{
+                               $path = $branch_to . (length $branch_to && 
length $path ? "/" : "") . $path;
+                               $added_placeholder{ dirname($path) } = $path;
+                       }
+               }
+       }
+       undef
+}
+
+sub svn2ph_path {
+       my ($self, $path) = @_;
+       if (defined $_reimportpath && defined $path) {
+               $path = _strip_path($path, $self->{path_strip});
+               $path = $_reimportpath . (length $_reimportpath && length $path 
? "/" : "") . $path;
+       }
+       $path
+}
+
+sub ph2svn_path {
+       my ($self, $path) = @_;
+       if (defined $_reimportpath && defined $path) {
+               $path = _strip_path($path, qr/^\Q$_reimportpath\E(\/|$)/ ) if 
length $_reimportpath;
+               $path = $self->{pathprefix_strip} . $path; # if not empty, 
pathprefix_strip already ends with slash
+       }
+       $path
+}
+
 sub delete_entry {
        my ($self, $path, $rev, $pb) = @_;
        return undef if $self->is_path_ignored($path);
@@ -197,7 +244,8 @@ sub delete_entry {
                print "\tD\t$gpath\n" unless $::_q;
        }
        # Don't add to @deleted_gpath if we're deleting a placeholder file.
-       push @deleted_gpath, $gpath unless $added_placeholder{dirname($path)};
+       my $phkey = $self->svn2ph_path(dirname($path));
+       push @deleted_gpath, $gpath unless $added_placeholder{$phkey};
        $self->{empty}->{$path} = 0;
        undef;
 }
@@ -231,10 +279,12 @@ sub add_file {
                delete $self->{empty}->{$dir};
                $mode = '100644';
 
+               $dir = $self->svn2ph_path($dir);
                if ($added_placeholder{$dir}) {
                        # Remove our placeholder file, if we created one.
-                       delete_entry($self, $added_placeholder{$dir})
-                               unless $path eq $added_placeholder{$dir};
+                       my $svnph = 
$self->ph2svn_path($added_placeholder{$dir});
+                       delete_entry($self, $svnph)
+                               unless $path eq $svnph;
                        delete $added_placeholder{$dir}
                }
        }
@@ -265,9 +315,11 @@ sub add_directory {
        delete $self->{empty}->{$dir};
        $self->{empty}->{$path} = 1;
 
+       $dir = $self->svn2ph_path($dir);
        if ($added_placeholder{$dir}) {
                # Remove our placeholder file, if we created one.
-               delete_entry($self, $added_placeholder{$dir});
+               my $svnph = $self->ph2svn_path($added_placeholder{$dir});
+               delete_entry($self, $svnph);
                delete $added_placeholder{$dir}
        }
 
@@ -278,6 +330,10 @@ out:
 sub change_dir_prop {
        my ($self, $db, $prop, $value) = @_;
        return undef if $self->is_path_ignored($db->{path});
+       if ($self->{path_strip}) {
+               $db->{path} =~ m!$self->{path_strip}! or
+                       return undef;
+       }
        $self->{dir_prop}->{$db->{path}} ||= {};
        $self->{dir_prop}->{$db->{path}}->{$prop} = $value;
        undef;
@@ -514,6 +570,8 @@ sub add_placeholder_file {
        delete $self->{empty}->{$dir} if exists $self->{empty}->{$dir};
 
        # Keep track of any placeholder files we create.
+       $dir = $self->svn2ph_path($dir);
+       $path = $self->svn2ph_path($path);
        $added_placeholder{$dir} = $path;
 }
 
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh 
b/t/t9160-git-svn-preserve-empty-dirs.sh
index ff06a86..4b0ba75 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -15,7 +15,7 @@ say 'define NO_SVN_TESTS to skip git svn tests'
 GIT_REPO=git-svn-repo
 
 test_expect_success 'initialize source svn repo containing empty dirs' '
-       svn_cmd mkdir -m x "$svnrepo"/trunk &&
+       svn_cmd mkdir -m x "$svnrepo"/trunk "$svnrepo"/tags &&
        svn_cmd co "$svnrepo"/trunk "$SVN_TREE" &&
        (
                cd "$SVN_TREE" &&
@@ -23,8 +23,6 @@ test_expect_success 'initialize source svn repo containing 
empty dirs' '
                echo x > module/foo/file.txt &&
                svn_cmd add module &&
                svn_cmd commit -mx &&
-               svn_cmd mv module/foo/file.txt module/bar/file.txt &&
-               svn_cmd commit -mx &&
                mkdir -p 1 2 3/a 3/b 4 5 6 &&
                echo "First non-empty file"  > 2/file1.txt &&
                echo "Second non-empty file" > 2/file2.txt &&
@@ -50,12 +48,18 @@ test_expect_success 'initialize source svn repo containing 
empty dirs' '
                svn_cmd del 3/b &&
                svn_cmd commit -m "delete non-last entry in directory" &&
 
-               svn_cmd rm -m"x" "$svnrepo"/trunk/module &&
+               svn_cmd mv module/foo/file.txt module/bar/file.txt &&
+               svn_cmd commit -mx &&
+               svn_cmd cp "$svnrepo"/trunk "$svnrepo"/tags/v1.0 -m"create 
standard tag" &&
+               svn_cmd cp "$svnrepo"/trunk/module "$svnrepo"/tags/module_v1.0 
-m"create non-standard tag" &&
+               svn_cmd rm -m"removed dir should not be recreated" 
"$svnrepo"/trunk/module &&
 
                svn_cmd del 2/file1.txt &&
                svn_cmd del 3/a &&
                svn_cmd commit -m "delete last entry in directory" &&
 
+               svn_cmd mkdir "$svnrepo"/tags/v1.0/module/foo/baz 
"$svnrepo"/tags/module_v1.0/foo/baz -m"this commit should remove known 
.gitignore from tags" &&
+
                echo "Conflict file" > 5/.placeholder &&
                mkdir 6/.placeholder &&
                svn_cmd add 5/.placeholder 6/.placeholder &&
@@ -104,6 +108,45 @@ test_expect_success 'remove non-last entry from directory' 
'
        test_must_fail test -f "$GIT_REPO"/3/.gitignore
 '
 
+branchtests() {
+       branchname=$1
+       prefix=$2
+
+       test_expect_success "$branchname: "'existing placeholders are tracked 
when creating a branch' '
+               (
+                       cd "$GIT_REPO" &&
+                       git checkout "$branchname"
+               ) &&
+               test -f "$GIT_REPO"/"$prefix"foo/baz/.gitignore &&
+               test_must_fail test -f "$GIT_REPO"/"$prefix"foo/.gitignore &&
+               test_must_fail test -f "$GIT_REPO"/"$prefix"bar/.gitignore
+       '
+
+       test_expect_success "$branchname: "'remove last entry from a directory' 
'
+               (
+                       cd "$GIT_REPO" &&
+                       git checkout HEAD~1
+               ) &&
+               test -f "$GIT_REPO"/"$prefix"foo/.gitignore
+       '
+
+       test_expect_success "$branchname: "'add entry to previously empty 
directory' '
+               test_must_fail test -f "$GIT_REPO"/"$prefix"bar/.gitignore
+       '
+
+       # Skip 2 commits, one of them is empty commit of tag creation
+       test_expect_success "$branchname: "'create empty directory' '
+               (
+                       cd "$GIT_REPO" &&
+                       git checkout HEAD~2
+               ) &&
+               test -f "$GIT_REPO"/"$prefix"bar/.gitignore
+       '
+}
+
+branchtests "tags/v1.0"        "module/"
+branchtests "tags/module_v1.0" ""
+
 # After re-cloning the repository with --placeholder-file specified, there
 # should be 5 files named ".placeholder" in the local Git repo.
 test_expect_success 'clone svn repo with --placeholder-file specified' '
-- 
1.8.1.5


--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to