@matheusbsbs / matheus-bagliano / commits / b03a71a660
Fork 0

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 <normalperson@yhbt.net>

Eric Wong committed Jan 11, 2009 at 18:23 UTC b03a71a660d15d76b63d7d3c5205b896f89f34b5
2 files changed +131 -4
git-svn.perl
+30 -4
@@ -3291,6 +3291,11 @@ sub _mark_empty_symlinks {
3291 \%ret;
3292 }
3293
3294 +# returns true if a given path is inside a ".git" directory
3295 +sub in_dot_git {
3296 + $_[0] =~ m{(?:^|/)\.git(?:/|$)};
3297 +}
3298 +
3299 sub set_path_strip {
3300 my ($self, $path) = @_;
3301 $self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path;
@@ -3316,6 +3321,7 @@ sub git_path {
3321
3322 sub delete_entry {
3323 my ($self, $path, $rev, $pb) = @_;
3324 + return undef if in_dot_git($path);
3325
3326 my $gpath = $self->git_path($path);
3327 return undef if ($gpath eq '');
@@ -3343,8 +3349,12 @@ sub delete_entry {
3349
3350 sub open_file {
3351 my ($self, $path, $pb, $rev) = @_;
3352 + my ($mode, $blob);
3353 +
3354 + goto out if in_dot_git($path);
3355 +
3356 my $gpath = $self->git_path($path);
3347 - my ($mode, $blob) = (command('ls-tree', $self->{c}, '--', $gpath)
3357 + ($mode, $blob) = (command('ls-tree', $self->{c}, '--', $gpath)
3358 =~ /^(\d{6}) blob ([a-f\d]{40})\t/);
3359 unless (defined $mode && defined $blob) {
3360 die "$path was not found in commit $self->{c} (r$rev)\n";
@@ -3352,20 +3362,27 @@ sub open_file {
3362 if ($mode eq '100644' && $self->{empty_symlinks}->{$path}) {
3363 $mode = '120000';
3364 }
3365 +out:
3366 { path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
3367 pool => SVN::Pool->new, action => 'M' };
3368 }
3369
3370 sub add_file {
3371 my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
3361 - my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
3362 - delete $self->{empty}->{$dir};
3363 - { path => $path, mode_a => 100644, mode_b => 100644,
3372 + my $mode;
3373 +
3374 + if (!in_dot_git($path)) {
3375 + my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
3376 + delete $self->{empty}->{$dir};
3377 + $mode = '100644';
3378 + }
3379 + { path => $path, mode_a => $mode, mode_b => $mode,
3380 pool => SVN::Pool->new, action => 'A' };
3381 }
3382
3383 sub add_directory {
3384 my ($self, $path, $cp_path, $cp_rev) = @_;
3385 + goto out if in_dot_git($path);
3386 my $gpath = $self->git_path($path);
3387 if ($gpath eq '') {
3388 my ($ls, $ctx) = command_output_pipe(qw/ls-tree
@@ -3383,11 +3400,13 @@ sub add_directory {
3400 my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
3401 delete $self->{empty}->{$dir};
3402 $self->{empty}->{$path} = 1;
3403 +out:
3404 { path => $path };
3405 }
3406
3407 sub change_dir_prop {
3408 my ($self, $db, $prop, $value) = @_;
3409 + return undef if in_dot_git($db->{path});
3410 $self->{dir_prop}->{$db->{path}} ||= {};
3411 $self->{dir_prop}->{$db->{path}}->{$prop} = $value;
3412 undef;
@@ -3395,6 +3414,7 @@ sub change_dir_prop {
3414
3415 sub absent_directory {
3416 my ($self, $path, $pb) = @_;
3417 + return undef if in_dot_git($pb->{path});
3418 $self->{absent_dir}->{$pb->{path}} ||= [];
3419 push @{$self->{absent_dir}->{$pb->{path}}}, $path;
3420 undef;
@@ -3402,6 +3422,7 @@ sub absent_directory {
3422
3423 sub absent_file {
3424 my ($self, $path, $pb) = @_;
3425 + return undef if in_dot_git($pb->{path});
3426 $self->{absent_file}->{$pb->{path}} ||= [];
3427 push @{$self->{absent_file}->{$pb->{path}}}, $path;
3428 undef;
@@ -3409,6 +3430,7 @@ sub absent_file {
3430
3431 sub change_file_prop {
3432 my ($self, $fb, $prop, $value) = @_;
3433 + return undef if in_dot_git($fb->{path});
3434 if ($prop eq 'svn:executable') {
3435 if ($fb->{mode_b} != 120000) {
3436 $fb->{mode_b} = defined $value ? 100755 : 100644;
@@ -3424,11 +3446,13 @@ sub change_file_prop {
3446
3447 sub apply_textdelta {
3448 my ($self, $fb, $exp) = @_;
3449 + return undef if (in_dot_git($fb->{path}));
3450 my $fh = $::_repository->temp_acquire('svn_delta');
3451 # $fh gets auto-closed() by SVN::TxDelta::apply(),
3452 # (but $base does not,) so dup() it for reading in close_file
3453 open my $dup, '<&', $fh or croak $!;
3454 my $base = $::_repository->temp_acquire('git_blob');
3455 +
3456 if ($fb->{blob}) {
3457 my ($base_is_link, $size);
3458
@@ -3469,6 +3493,8 @@ sub apply_textdelta {
3493
3494 sub close_file {
3495 my ($self, $fb, $exp) = @_;
3496 + return undef if (in_dot_git($fb->{path}));
3497 +
3498 my $hash;
3499 my $path = $self->git_path($fb->{path});
3500 if (my $fh = $fb->{fh}) {
t/t9133-git-svn-nested-git-repo.sh new
+101
@@ -0,0 +1,101 @@
1 +#!/bin/sh
2 +#
3 +# Copyright (c) 2009 Eric Wong
4 +#
5 +
6 +test_description='git svn property tests'
7 +. ./lib-git-svn.sh
8 +
9 +test_expect_success 'setup repo with a git repo inside it' '
10 + svn co "$svnrepo" s &&
11 + (
12 + cd s &&
13 + git init &&
14 + test -f .git/HEAD &&
15 + > .git/a &&
16 + echo a > a &&
17 + svn add .git a &&
18 + svn commit -m "create a nested git repo" &&
19 + svn up &&
20 + echo hi >> .git/a &&
21 + svn commit -m "modify .git/a" &&
22 + svn up
23 + )
24 +'
25 +
26 +test_expect_success 'clone an SVN repo containing a git repo' '
27 + git svn clone "$svnrepo" g &&
28 + echo a > expect &&
29 + test_cmp expect g/a
30 +'
31 +
32 +test_expect_success 'SVN-side change outside of .git' '
33 + (
34 + cd s &&
35 + echo b >> a &&
36 + svn commit -m "SVN-side change outside of .git" &&
37 + svn up &&
38 + svn log -v | fgrep "SVN-side change outside of .git"
39 + )
40 +'
41 +
42 +test_expect_success 'update git svn-cloned repo' '
43 + (
44 + cd g &&
45 + git svn rebase &&
46 + echo a > expect &&
47 + echo b >> expect &&
48 + test_cmp a expect &&
49 + rm expect
50 + )
51 +'
52 +
53 +test_expect_success 'SVN-side change inside of .git' '
54 + (
55 + cd s &&
56 + git add a &&
57 + git commit -m "add a inside an SVN repo" &&
58 + git log &&
59 + svn add --force .git &&
60 + svn commit -m "SVN-side change inside of .git" &&
61 + svn up &&
62 + svn log -v | fgrep "SVN-side change inside of .git"
63 + )
64 +'
65 +
66 +test_expect_success 'update git svn-cloned repo' '
67 + (
68 + cd g &&
69 + git svn rebase &&
70 + echo a > expect &&
71 + echo b >> expect &&
72 + test_cmp a expect &&
73 + rm expect
74 + )
75 +'
76 +
77 +test_expect_success 'SVN-side change in and out of .git' '
78 + (
79 + cd s &&
80 + echo c >> a &&
81 + git add a &&
82 + git commit -m "add a inside an SVN repo" &&
83 + svn commit -m "SVN-side change in and out of .git" &&
84 + svn up &&
85 + svn log -v | fgrep "SVN-side change in and out of .git"
86 + )
87 +'
88 +
89 +test_expect_success 'update git svn-cloned repo again' '
90 + (
91 + cd g &&
92 + git svn rebase &&
93 + echo a > expect &&
94 + echo b >> expect &&
95 + echo c >> expect &&
96 + test_cmp a expect &&
97 + rm expect
98 + )
99 +'
100 +
101 +test_done