From b03a71a660d15d76b63d7d3c5205b896f89f34b5 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 11 Jan 2009 18:23:38 -0800 Subject: [PATCH] git-svn: avoid importing nested git repos Some SVN repositories contain git repositories within them (hopefully accidentally checked in). Since git refuses to track nested ".git" repositories, this can be a problem when fetching updates from SVN. Thanks to Morgan Christiansson for the report and testing. Signed-off-by: Eric Wong --- git-svn.perl | 34 ++++++++-- t/t9133-git-svn-nested-git-repo.sh | 101 +++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 4 deletions(-) create mode 100755 t/t9133-git-svn-nested-git-repo.sh diff --git a/git-svn.perl b/git-svn.perl index 71b8ef4265..55c4dfb968 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -3291,6 +3291,11 @@ sub _mark_empty_symlinks { \%ret; } +# returns true if a given path is inside a ".git" directory +sub in_dot_git { + $_[0] =~ m{(?:^|/)\.git(?:/|$)}; +} + sub set_path_strip { my ($self, $path) = @_; $self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path; @@ -3316,6 +3321,7 @@ sub git_path { sub delete_entry { my ($self, $path, $rev, $pb) = @_; + return undef if in_dot_git($path); my $gpath = $self->git_path($path); return undef if ($gpath eq ''); @@ -3343,8 +3349,12 @@ sub delete_entry { sub open_file { my ($self, $path, $pb, $rev) = @_; + my ($mode, $blob); + + goto out if in_dot_git($path); + my $gpath = $self->git_path($path); - my ($mode, $blob) = (command('ls-tree', $self->{c}, '--', $gpath) + ($mode, $blob) = (command('ls-tree', $self->{c}, '--', $gpath) =~ /^(\d{6}) blob ([a-f\d]{40})\t/); unless (defined $mode && defined $blob) { die "$path was not found in commit $self->{c} (r$rev)\n"; @@ -3352,20 +3362,27 @@ sub open_file { if ($mode eq '100644' && $self->{empty_symlinks}->{$path}) { $mode = '120000'; } +out: { path => $path, mode_a => $mode, mode_b => $mode, blob => $blob, pool => SVN::Pool->new, action => 'M' }; } sub add_file { my ($self, $path, $pb, $cp_path, $cp_rev) = @_; - my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#); - delete $self->{empty}->{$dir}; - { path => $path, mode_a => 100644, mode_b => 100644, + my $mode; + + if (!in_dot_git($path)) { + my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#); + delete $self->{empty}->{$dir}; + $mode = '100644'; + } + { path => $path, mode_a => $mode, mode_b => $mode, pool => SVN::Pool->new, action => 'A' }; } sub add_directory { my ($self, $path, $cp_path, $cp_rev) = @_; + goto out if in_dot_git($path); my $gpath = $self->git_path($path); if ($gpath eq '') { my ($ls, $ctx) = command_output_pipe(qw/ls-tree @@ -3383,11 +3400,13 @@ sub add_directory { my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#); delete $self->{empty}->{$dir}; $self->{empty}->{$path} = 1; +out: { path => $path }; } sub change_dir_prop { my ($self, $db, $prop, $value) = @_; + return undef if in_dot_git($db->{path}); $self->{dir_prop}->{$db->{path}} ||= {}; $self->{dir_prop}->{$db->{path}}->{$prop} = $value; undef; @@ -3395,6 +3414,7 @@ sub change_dir_prop { sub absent_directory { my ($self, $path, $pb) = @_; + return undef if in_dot_git($pb->{path}); $self->{absent_dir}->{$pb->{path}} ||= []; push @{$self->{absent_dir}->{$pb->{path}}}, $path; undef; @@ -3402,6 +3422,7 @@ sub absent_directory { sub absent_file { my ($self, $path, $pb) = @_; + return undef if in_dot_git($pb->{path}); $self->{absent_file}->{$pb->{path}} ||= []; push @{$self->{absent_file}->{$pb->{path}}}, $path; undef; @@ -3409,6 +3430,7 @@ sub absent_file { sub change_file_prop { my ($self, $fb, $prop, $value) = @_; + return undef if in_dot_git($fb->{path}); if ($prop eq 'svn:executable') { if ($fb->{mode_b} != 120000) { $fb->{mode_b} = defined $value ? 100755 : 100644; @@ -3424,11 +3446,13 @@ sub change_file_prop { sub apply_textdelta { my ($self, $fb, $exp) = @_; + return undef if (in_dot_git($fb->{path})); my $fh = $::_repository->temp_acquire('svn_delta'); # $fh gets auto-closed() by SVN::TxDelta::apply(), # (but $base does not,) so dup() it for reading in close_file open my $dup, '<&', $fh or croak $!; my $base = $::_repository->temp_acquire('git_blob'); + if ($fb->{blob}) { my ($base_is_link, $size); @@ -3469,6 +3493,8 @@ sub apply_textdelta { sub close_file { my ($self, $fb, $exp) = @_; + return undef if (in_dot_git($fb->{path})); + my $hash; my $path = $self->git_path($fb->{path}); if (my $fh = $fb->{fh}) { diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh new file mode 100755 index 0000000000..893f57ef73 --- /dev/null +++ b/t/t9133-git-svn-nested-git-repo.sh @@ -0,0 +1,101 @@ +#!/bin/sh +# +# Copyright (c) 2009 Eric Wong +# + +test_description='git svn property tests' +. ./lib-git-svn.sh + +test_expect_success 'setup repo with a git repo inside it' ' + svn co "$svnrepo" s && + ( + cd s && + git init && + test -f .git/HEAD && + > .git/a && + echo a > a && + svn add .git a && + svn commit -m "create a nested git repo" && + svn up && + echo hi >> .git/a && + svn commit -m "modify .git/a" && + svn up + ) +' + +test_expect_success 'clone an SVN repo containing a git repo' ' + git svn clone "$svnrepo" g && + echo a > expect && + test_cmp expect g/a +' + +test_expect_success 'SVN-side change outside of .git' ' + ( + cd s && + echo b >> a && + svn commit -m "SVN-side change outside of .git" && + svn up && + svn log -v | fgrep "SVN-side change outside of .git" + ) +' + +test_expect_success 'update git svn-cloned repo' ' + ( + cd g && + git svn rebase && + echo a > expect && + echo b >> expect && + test_cmp a expect && + rm expect + ) +' + +test_expect_success 'SVN-side change inside of .git' ' + ( + cd s && + git add a && + git commit -m "add a inside an SVN repo" && + git log && + svn add --force .git && + svn commit -m "SVN-side change inside of .git" && + svn up && + svn log -v | fgrep "SVN-side change inside of .git" + ) +' + +test_expect_success 'update git svn-cloned repo' ' + ( + cd g && + git svn rebase && + echo a > expect && + echo b >> expect && + test_cmp a expect && + rm expect + ) +' + +test_expect_success 'SVN-side change in and out of .git' ' + ( + cd s && + echo c >> a && + git add a && + git commit -m "add a inside an SVN repo" && + svn commit -m "SVN-side change in and out of .git" && + svn up && + svn log -v | fgrep "SVN-side change in and out of .git" + ) +' + +test_expect_success 'update git svn-cloned repo again' ' + ( + cd g && + git svn rebase && + echo a > expect && + echo b >> expect && + echo c >> expect && + test_cmp a expect && + rm expect + ) +' + +test_done