[PATCH 5/5] git-svn: fix empty dir tracking in branches

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



  - 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]