Skip to content

Commit d2a9a87

Browse files
Eric WongJunio C Hamano
authored andcommitted
git-svn: enable logging of information not supported by git
The changes are now tracked in $GIT_DIR/svn/$GIT_SVN_ID/untracked.log Information in the untracked.log include: * the addition and removal of empty directories (changes of these will also warn the user) * file and directory property changes, including (but not limited to) svk:merge and svn:externals * revision properties (revprops) are also tracked * users will be warned of 'absent' file and directories (if users are forbidden access) Fields in entries are separated by spaces; "unsafe" characters are URI-encoded so that each entry takes exactly one line. There is currently no automated parser for dealing with the data in untracked.log, but it should be possible to write one to create empty directories on checkout and manage externals/subprojects. Signed-off-by: Eric Wong <normalperson@yhbt.net> Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 6f98725 commit d2a9a87

File tree

1 file changed

+184
-18
lines changed

1 file changed

+184
-18
lines changed

git-svn.perl

Lines changed: 184 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@
2121
$ENV{LC_ALL} = 'C';
2222
$| = 1; # unbuffer STDOUT
2323

24+
# properties that we do not log:
25+
my %SKIP = ( 'svn:wc:ra_dav:version-url' => 1,
26+
'svn:special' => 1,
27+
'svn:executable' => 1,
28+
'svn:entry:committed-rev' => 1,
29+
'svn:entry:last-author' => 1,
30+
'svn:entry:uuid' => 1,
31+
'svn:entry:committed-date' => 1,
32+
);
33+
2434
sub fatal (@) { print STDERR $@; exit 1 }
2535
# If SVN:: library support is added, please make the dependencies
2636
# optional and preserve the capability to use the command-line client.
@@ -2902,7 +2912,7 @@ sub libsvn_dup_ra {
29022912
}
29032913

29042914
sub libsvn_get_file {
2905-
my ($gui, $f, $rev, $chg) = @_;
2915+
my ($gui, $f, $rev, $chg, $untracked) = @_;
29062916
$f =~ s#^/##;
29072917
print "\t$chg\t$f\n" unless $_q;
29082918

@@ -2940,20 +2950,91 @@ sub libsvn_get_file {
29402950
waitpid $pid, 0;
29412951
$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
29422952
}
2953+
%{$untracked->{file_prop}->{$f}} = %$props;
29432954
print $gui $mode,' ',$hash,"\t",$f,"\0" or croak $!;
29442955
}
29452956

2957+
sub uri_encode {
2958+
my ($f) = @_;
2959+
$f =~ s#([^a-zA-Z0-9\*!\:_\./\-])#uc sprintf("%%%02x",ord($1))#eg;
2960+
$f
2961+
}
2962+
2963+
sub uri_decode {
2964+
my ($f) = @_;
2965+
$f =~ tr/+/ /;
2966+
$f =~ s/%([A-F0-9]{2})/chr hex($1)/ge;
2967+
$f
2968+
}
2969+
29462970
sub libsvn_log_entry {
2947-
my ($rev, $author, $date, $msg, $parents) = @_;
2971+
my ($rev, $author, $date, $msg, $parents, $untracked) = @_;
29482972
my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
29492973
(\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
29502974
or die "Unable to parse date: $date\n";
29512975
if (defined $_authors && ! defined $users{$author}) {
29522976
die "Author: $author not defined in $_authors file\n";
29532977
}
29542978
$msg = '' if ($rev == 0 && !defined $msg);
2955-
return { revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
2956-
author => $author, msg => $msg."\n", parents => $parents || [] }
2979+
2980+
open my $un, '>>', "$GIT_SVN_DIR/unhandled.log" or croak $!;
2981+
my $h;
2982+
print $un "r$rev\n" or croak $!;
2983+
$h = $untracked->{empty};
2984+
foreach (sort keys %$h) {
2985+
my $act = $h->{$_} ? '+empty_dir' : '-empty_dir';
2986+
print $un " $act: ", uri_encode($_), "\n" or croak $!;
2987+
warn "W: $act: $_\n";
2988+
}
2989+
foreach my $t (qw/dir_prop file_prop/) {
2990+
$h = $untracked->{$t} or next;
2991+
foreach my $path (sort keys %$h) {
2992+
my $ppath = $path eq '' ? '.' : $path;
2993+
foreach my $prop (sort keys %{$h->{$path}}) {
2994+
next if $SKIP{$prop};
2995+
my $v = $h->{$path}->{$prop};
2996+
if (defined $v) {
2997+
print $un " +$t: ",
2998+
uri_encode($ppath), ' ',
2999+
uri_encode($prop), ' ',
3000+
uri_encode($v), "\n"
3001+
or croak $!;
3002+
} else {
3003+
print $un " -$t: ",
3004+
uri_encode($ppath), ' ',
3005+
uri_encode($prop), "\n"
3006+
or croak $!;
3007+
}
3008+
}
3009+
}
3010+
}
3011+
foreach my $t (qw/absent_file absent_directory/) {
3012+
$h = $untracked->{$t} or next;
3013+
foreach my $parent (sort keys %$h) {
3014+
foreach my $path (sort @{$h->{$parent}}) {
3015+
print $un " $t: ",
3016+
uri_encode("$parent/$path"), "\n"
3017+
or croak $!;
3018+
warn "W: $t: $parent/$path ",
3019+
"Insufficient permissions?\n";
3020+
}
3021+
}
3022+
}
3023+
3024+
# revprops (make this optional? it's an extra network trip...)
3025+
my $pool = SVN::Pool->new;
3026+
my $rp = $SVN->rev_proplist($rev, $pool);
3027+
foreach (sort keys %$rp) {
3028+
next if /^svn:(?:author|date|log)$/;
3029+
print $un " rev_prop: ", uri_encode($_), ' ',
3030+
uri_encode($rp->{$_}), "\n";
3031+
}
3032+
$pool->clear;
3033+
close $un or croak $!;
3034+
3035+
{ revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
3036+
author => $author, msg => $msg."\n", parents => $parents || [],
3037+
revprops => $rp }
29573038
}
29583039

29593040
sub process_rm {
@@ -2972,9 +3053,11 @@ sub process_rm {
29723053
}
29733054
print "\tD\t$f/\n" unless $q;
29743055
close $ls or croak $?;
3056+
return $SVN::Node::dir;
29753057
} else {
29763058
print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
29773059
print "\tD\t$f\n" unless $q;
3060+
return $SVN::Node::file;
29783061
}
29793062
}
29803063

@@ -2995,13 +3078,14 @@ sub libsvn_fetch_delta {
29953078
unless ($ed->{git_commit_ok}) {
29963079
die "SVN connection failed somewhere...\n";
29973080
}
2998-
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
3081+
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ed);
29993082
}
30003083

30013084
sub libsvn_fetch_full {
30023085
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
30033086
open my $gui, '| git-update-index -z --index-info' or croak $!;
30043087
my %amr;
3088+
my $ut = { empty => {}, dir_prop => {}, file_prop => {} };
30053089
my $p = $SVN->{svn_path};
30063090
foreach my $f (keys %$paths) {
30073091
my $m = $paths->{$f}->action();
@@ -3012,8 +3096,11 @@ sub libsvn_fetch_full {
30123096
$f =~ s#^/##;
30133097
}
30143098
if ($m =~ /^[DR]$/) {
3015-
process_rm($gui, $last_commit, $f, $_q);
3016-
next if $m eq 'D';
3099+
my $t = process_rm($gui, $last_commit, $f, $_q);
3100+
if ($m eq 'D') {
3101+
$ut->{empty}->{$f} = 0 if $t == $SVN::Node::dir;
3102+
next;
3103+
}
30173104
# 'R' can be file replacements, too, right?
30183105
}
30193106
my $pool = SVN::Pool->new;
@@ -3026,18 +3113,32 @@ sub libsvn_fetch_full {
30263113
}
30273114
} elsif ($t == $SVN::Node::dir && $m =~ /^[AR]$/) {
30283115
my @traversed = ();
3029-
libsvn_traverse($gui, '', $f, $rev, \@traversed);
3030-
foreach (@traversed) {
3031-
$amr{$_} = $m;
3116+
libsvn_traverse($gui, '', $f, $rev, \@traversed, $ut);
3117+
if (@traversed) {
3118+
foreach (@traversed) {
3119+
$amr{$_} = $m;
3120+
}
3121+
} else {
3122+
my ($dir, $file) = ($f =~ m#^(.*?)/?([^/]+)$#);
3123+
delete $ut->{empty}->{$dir};
3124+
$ut->{empty}->{$f} = 1;
30323125
}
30333126
}
30343127
$pool->clear;
30353128
}
30363129
foreach (keys %amr) {
3037-
libsvn_get_file($gui, $_, $rev, $amr{$_});
3130+
libsvn_get_file($gui, $_, $rev, $amr{$_}, $ut);
3131+
my ($d) = ($_ =~ m#^(.*?)/?(?:[^/]+)$#);
3132+
delete $ut->{empty}->{$d};
3133+
}
3134+
unless (exists $ut->{dir_prop}->{''}) {
3135+
my $pool = SVN::Pool->new;
3136+
my (undef, undef, $props) = $SVN->get_dir('', $rev, $pool);
3137+
%{$ut->{dir_prop}->{''}} = %$props;
3138+
$pool->clear;
30383139
}
30393140
close $gui or croak $?;
3040-
return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
3141+
libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ut);
30413142
}
30423143

30433144
sub svn_grab_base_rev {
@@ -3098,25 +3199,38 @@ sub libsvn_parse_revision {
30983199
}
30993200

31003201
sub libsvn_traverse {
3101-
my ($gui, $pfx, $path, $rev, $files) = @_;
3202+
my ($gui, $pfx, $path, $rev, $files, $untracked) = @_;
31023203
my $cwd = length $pfx ? "$pfx/$path" : $path;
31033204
my $pool = SVN::Pool->new;
31043205
$cwd =~ s#^\Q$SVN->{svn_path}\E##;
3206+
my $nr = 0;
31053207
my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
3208+
%{$untracked->{dir_prop}->{$cwd}} = %$props;
31063209
foreach my $d (keys %$dirent) {
31073210
my $t = $dirent->{$d}->kind;
31083211
if ($t == $SVN::Node::dir) {
3109-
libsvn_traverse($gui, $cwd, $d, $rev, $files);
3212+
my $i = libsvn_traverse($gui, $cwd, $d, $rev,
3213+
$files, $untracked);
3214+
if ($i) {
3215+
$nr += $i;
3216+
} else {
3217+
$untracked->{empty}->{"$cwd/$d"} = 1;
3218+
}
31103219
} elsif ($t == $SVN::Node::file) {
3220+
$nr++;
31113221
my $file = "$cwd/$d";
31123222
if (defined $files) {
31133223
push @$files, $file;
31143224
} else {
3115-
libsvn_get_file($gui, $file, $rev, 'A');
3225+
libsvn_get_file($gui, $file, $rev, 'A',
3226+
$untracked);
3227+
my ($dir) = ($file =~ m#^(.*?)/?(?:[^/]+)$#);
3228+
delete $untracked->{empty}->{$dir};
31163229
}
31173230
}
31183231
}
31193232
$pool->clear;
3233+
$nr;
31203234
}
31213235

31223236
sub libsvn_traverse_ignore {
@@ -3255,6 +3369,7 @@ sub libsvn_new_tree {
32553369
return $log_entry;
32563370
}
32573371
my ($paths, $rev, $author, $date, $msg) = @_;
3372+
my $ut;
32583373
if ($_xfer_delta) {
32593374
my $pool = SVN::Pool->new;
32603375
my $ed = SVN::Git::Fetcher->new({q => $_q});
@@ -3266,12 +3381,14 @@ sub libsvn_new_tree {
32663381
unless ($ed->{git_commit_ok}) {
32673382
die "SVN connection failed somewhere...\n";
32683383
}
3384+
$ut = $ed;
32693385
} else {
3386+
$ut = { empty => {}, dir_prop => {}, file_prop => {} };
32703387
open my $gui, '| git-update-index -z --index-info' or croak $!;
3271-
libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
3388+
libsvn_traverse($gui, '', $SVN->{svn_path}, $rev, undef, $ut);
32723389
close $gui or croak $?;
32733390
}
3274-
return libsvn_log_entry($rev, $author, $date, $msg);
3391+
libsvn_log_entry($rev, $author, $date, $msg, [], $ut);
32753392
}
32763393

32773394
sub find_graft_path_commit {
@@ -3456,13 +3573,28 @@ sub new {
34563573
$self->{gui} = $gui;
34573574
$self->{c} = $git_svn->{c} if exists $git_svn->{c};
34583575
$self->{q} = $git_svn->{q};
3576+
$self->{empty} = {};
3577+
$self->{dir_prop} = {};
3578+
$self->{file_prop} = {};
3579+
$self->{absent_dir} = {};
3580+
$self->{absent_file} = {};
34593581
require Digest::MD5;
34603582
$self;
34613583
}
34623584

3585+
sub open_root {
3586+
{ path => '' };
3587+
}
3588+
3589+
sub open_directory {
3590+
my ($self, $path, $pb, $rev) = @_;
3591+
{ path => $path };
3592+
}
3593+
34633594
sub delete_entry {
34643595
my ($self, $path, $rev, $pb) = @_;
3465-
process_rm($self->{gui}, $self->{c}, $path, $self->{q});
3596+
my $t = process_rm($self->{gui}, $self->{c}, $path, $self->{q});
3597+
$self->{empty}->{$path} = 0 if $t == $SVN::Node::dir;
34663598
undef;
34673599
}
34683600

@@ -3479,10 +3611,41 @@ sub open_file {
34793611

34803612
sub add_file {
34813613
my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
3614+
my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
3615+
delete $self->{empty}->{$dir};
34823616
{ path => $path, mode_a => 100644, mode_b => 100644,
34833617
pool => SVN::Pool->new, action => 'A' };
34843618
}
34853619

3620+
sub add_directory {
3621+
my ($self, $path, $cp_path, $cp_rev) = @_;
3622+
my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
3623+
delete $self->{empty}->{$dir};
3624+
$self->{empty}->{$path} = 1;
3625+
{ path => $path };
3626+
}
3627+
3628+
sub change_dir_prop {
3629+
my ($self, $db, $prop, $value) = @_;
3630+
$self->{dir_prop}->{$db->{path}} ||= {};
3631+
$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
3632+
undef;
3633+
}
3634+
3635+
sub absent_directory {
3636+
my ($self, $path, $pb) = @_;
3637+
$self->{absent_dir}->{$pb->{path}} ||= [];
3638+
push @{$self->{absent_dir}->{$pb->{path}}}, $path;
3639+
undef;
3640+
}
3641+
3642+
sub absent_file {
3643+
my ($self, $path, $pb) = @_;
3644+
$self->{absent_file}->{$pb->{path}} ||= [];
3645+
push @{$self->{absent_file}->{$pb->{path}}}, $path;
3646+
undef;
3647+
}
3648+
34863649
sub change_file_prop {
34873650
my ($self, $fb, $prop, $value) = @_;
34883651
if ($prop eq 'svn:executable') {
@@ -3491,6 +3654,9 @@ sub change_file_prop {
34913654
}
34923655
} elsif ($prop eq 'svn:special') {
34933656
$fb->{mode_b} = defined $value ? 120000 : 100644;
3657+
} else {
3658+
$self->{file_prop}->{$fb->{path}} ||= {};
3659+
$self->{file_prop}->{$fb->{path}}->{$prop} = $value;
34943660
}
34953661
undef;
34963662
}

0 commit comments

Comments
 (0)