git svn: attempt to create empty dirs on clone+rebase
We parse unhandled.log files for empty_dir statements and make a best effort attempt to recreate empty directories on fresh clones and rebase. This should cover the majority of cases where users work off a single branch or for projects where branches do not differ in empty directories. Since this cannot affect "normal" git commands like "checkout" or "reset", so users switching between branches in a single working directory should use the new "git svn mkdirs" command after switching branches. Signed-off-by: Eric Wong <normalperson@yhbt.net>
Eric Wong committed
Nov 15, 2009 at 18:57 UTC
6111b934991f3ea670ac2442806c976defc7b61c
4 files changed
+139
-2
Documentation/git-svn.txt
+7
@@ -320,6 +320,13 @@ Any other arguments are passed directly to 'git log'
320
directories. The output is suitable for appending to
321
the $GIT_DIR/info/exclude file.
322
323
+'mkdirs'::
324
+ Attempts to recreate empty directories that core git cannot track
325
+ based on information in $GIT_DIR/svn/<refname>/unhandled.log files.
326
+ Empty directories are automatically recreated when using
327
+ "git svn clone" and "git svn rebase", so "mkdirs" is intended
328
+ for use after commands like "git checkout" or "git reset".
329
+
330
'commit-diff'::
331
Commits the diff of two tree-ish arguments from the
332
command-line. This command does not rely on being inside an `git svn
git-svn.perl
+45
@@ -168,6 +168,9 @@ my %cmd = (
168
'Create a .gitignore per svn:ignore',
169
{ 'revision|r=i' => \$_revision
170
} ],
171
+ 'mkdirs' => [ \&cmd_mkdirs ,
172
+ "recreate empty directories after a checkout",
173
+ { 'revision|r=i' => \$_revision } ],
174
'propget' => [ \&cmd_propget,
175
'Print the value of a property on a file or directory',
176
{ 'revision|r=i' => \$_revision } ],
@@ -769,6 +772,7 @@ sub cmd_rebase {
772
$_fetch_all ? $gs->fetch_all : $gs->fetch;
773
}
774
command_noisy(rebase_cmd(), $gs->refname);
775
+ $gs->mkemptydirs;
776
}
777
778
sub cmd_show_ignore {
@@ -830,6 +834,12 @@ sub cmd_create_ignore {
834
});
835
}
836
837
+sub cmd_mkdirs {
838
+ my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
839
+ $gs ||= Git::SVN->new;
840
+ $gs->mkemptydirs($_revision);
841
+}
842
+
843
sub canonicalize_path {
844
my ($path) = @_;
845
my $dot_slash_added = 0;
@@ -1196,6 +1206,7 @@ sub post_fetch_checkout {
1206
command_noisy(qw/read-tree -m -u -v HEAD HEAD/);
1207
print STDERR "Checked out HEAD:\n ",
1208
$gs->full_url, " r", $gs->last_rev, "\n";
1209
+ $gs->mkemptydirs($gs->last_rev);
1210
}
1211
1212
sub complete_svn_url {
@@ -2724,6 +2735,34 @@ sub do_fetch {
2735
$self->make_log_entry($rev, \@parents, $ed);
2736
}
2737
2738
+sub mkemptydirs {
2739
+ my ($self, $r) = @_;
2740
+ my %empty_dirs = ();
2741
+
2742
+ open my $fh, '<', "$self->{dir}/unhandled.log" or return;
2743
+ binmode $fh or croak "binmode: $!";
2744
+ while (<$fh>) {
2745
+ if (defined $r && /^r(\d+)$/) {
2746
+ last if $1 > $r;
2747
+ } elsif (/^ \+empty_dir: (.+)$/) {
2748
+ $empty_dirs{$1} = 1;
2749
+ } elsif (/^ \-empty_dir: (.+)$/) {
2750
+ delete $empty_dirs{$1};
2751
+ }
2752
+ }
2753
+ close $fh;
2754
+ foreach my $d (sort keys %empty_dirs) {
2755
+ $d = uri_decode($d);
2756
+ next if -d $d;
2757
+ if (-e _) {
2758
+ warn "$d exists but is not a directory\n";
2759
+ } else {
2760
+ print "creating empty directory: $d\n";
2761
+ mkpath([$d]);
2762
+ }
2763
+ }
2764
+}
2765
+
2766
sub get_untracked {
2767
my ($self, $ed) = @_;
2768
my @out;
@@ -3556,6 +3595,12 @@ sub uri_encode {
3595
$f
3596
}
3597
3598
+sub uri_decode {
3599
+ my ($f) = @_;
3600
+ $f =~ s#%([0-9a-fA-F]{2})#chr(hex($1))#eg;
3601
+ $f
3602
+}
3603
+
3604
sub remove_username {
3605
$_[0] =~ s{^([^:]*://)[^@]+@}{$1};
3606
}
t/t9115-git-svn-dcommit-funky-renames.sh
+2
-2
@@ -19,7 +19,7 @@ test_expect_success 'init and fetch repository' '
19
'
20
21
test_expect_success 'create file in existing ugly and empty dir' '
22
- mkdir "#{bad_directory_name}" &&
22
+ mkdir -p "#{bad_directory_name}" &&
23
echo hi > "#{bad_directory_name}/ foo" &&
24
git update-index --add "#{bad_directory_name}/ foo" &&
25
git commit -m "new file in ugly parent" &&
@@ -37,7 +37,7 @@ test_expect_success 'rename pretty file' '
37
git update-index --add pretty &&
38
git commit -m "pretty :x" &&
39
git svn dcommit &&
40
- mkdir regular_dir_name &&
40
+ mkdir -p regular_dir_name &&
41
git mv pretty regular_dir_name/pretty &&
42
git commit -m "moved pretty file" &&
43
git svn dcommit
t/t9146-git-svn-empty-dirs.sh
new
+85
@@ -0,0 +1,85 @@
1
+#!/bin/sh
2
+#
3
+# Copyright (c) 2009 Eric Wong
4
+
5
+test_description='git svn creates empty directories'
6
+. ./lib-git-svn.sh
7
+
8
+test_expect_success 'initialize repo' '
9
+ for i in a b c d d/e d/e/f "weird file name"
10
+ do
11
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
12
+ done
13
+'
14
+
15
+test_expect_success 'clone' 'git svn clone "$svnrepo" cloned'
16
+
17
+test_expect_success 'empty directories exist' '
18
+ (
19
+ cd cloned &&
20
+ for i in a b c d d/e d/e/f "weird file name"
21
+ do
22
+ if ! test -d "$i"
23
+ then
24
+ echo >&2 "$i does not exist"
25
+ exit 1
26
+ fi
27
+ done
28
+ )
29
+'
30
+
31
+test_expect_success 'more emptiness' '
32
+ svn_cmd mkdir -m "bang bang" "$svnrepo"/"! !"
33
+'
34
+
35
+test_expect_success 'git svn rebase creates empty directory' '
36
+ ( cd cloned && git svn rebase )
37
+ test -d cloned/"! !"
38
+'
39
+
40
+test_expect_success 'git svn mkdirs recreates empty directories' '
41
+ (
42
+ cd cloned &&
43
+ rm -r * &&
44
+ git svn mkdirs &&
45
+ for i in a b c d d/e d/e/f "weird file name" "! !"
46
+ do
47
+ if ! test -d "$i"
48
+ then
49
+ echo >&2 "$i does not exist"
50
+ exit 1
51
+ fi
52
+ done
53
+ )
54
+'
55
+
56
+test_expect_success 'git svn mkdirs -r works' '
57
+ (
58
+ cd cloned &&
59
+ rm -r * &&
60
+ git svn mkdirs -r7 &&
61
+ for i in a b c d d/e d/e/f "weird file name"
62
+ do
63
+ if ! test -d "$i"
64
+ then
65
+ echo >&2 "$i does not exist"
66
+ exit 1
67
+ fi
68
+ done
69
+
70
+ if test -d "! !"
71
+ then
72
+ echo >&2 "$i should not exist"
73
+ exit 1
74
+ fi
75
+
76
+ git svn mkdirs -r8 &&
77
+ if ! test -d "! !"
78
+ then
79
+ echo >&2 "$i not exist"
80
+ exit 1
81
+ fi
82
+ )
83
+'
84
+
85
+test_done