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+
2434sub 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
29042914sub 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+
29462970sub 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
29593040sub process_rm {
@@ -2972,9 +3053,11 @@ sub process_rm {
29723053 }
29733054 print " \t D\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 " \t D\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
30013084sub 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
30433144sub svn_grab_base_rev {
@@ -3098,25 +3199,38 @@ sub libsvn_parse_revision {
30983199}
30993200
31003201sub 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
31223236sub 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
32773394sub 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+
34633594sub 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
34803612sub 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+
34863649sub 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