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