git-svn: add a generic tree traversal to fetch SVN properties
authorBenoit Sigoure <tsuna@lrde.epita.fr>
Tue, 16 Oct 2007 14:36:48 +0000 (16:36 +0200)
committerShawn O. Pearce <spearce@spearce.org>
Wed, 17 Oct 2007 06:47:36 +0000 (02:47 -0400)
* git-svn.perl (&traverse_ignore): Remove.
(&prop_walk): New.
(&cmd_show_ignore): Use prop_walk.

[ew: This will ease the implementation of the `create-ignore',
     `propget', and `proplist' commands]

Signed-off-by: Benoit Sigoure <tsuna@lrde.epita.fr>
Acked-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
git-svn.perl

index 1130a0949ad77b0e15579ff1bf4d75ba6644f8cd..e217cdf2746ea9b2d154aea041156d335ec6b3ec 100755 (executable)
@@ -488,7 +488,15 @@ sub cmd_show_ignore {
        my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
        $gs ||= Git::SVN->new;
        my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
-       $gs->traverse_ignore(\*STDOUT, $gs->{path}, $r);
+       $gs->prop_walk($gs->{path}, $r, sub {
+               my ($gs, $path, $props) = @_;
+               print STDOUT "\n# $path\n";
+               my $s = $props->{'svn:ignore'} or return;
+               $s =~ s/[\r\n]+/\n/g;
+               chomp $s;
+               $s =~ s#^#$path#gm;
+               print STDOUT "$s\n";
+       });
 }
 
 sub cmd_multi_init {
@@ -1480,28 +1488,45 @@ sub rel_path {
        $url;
 }
 
-sub traverse_ignore {
-       my ($self, $fh, $path, $r) = @_;
-       $path =~ s#^/+##g;
-       my $ra = $self->ra;
-       my ($dirent, undef, $props) = $ra->get_dir($path, $r);
+# prop_walk(PATH, REV, SUB)
+# -------------------------
+# Recursively traverse PATH at revision REV and invoke SUB for each
+# directory that contains a SVN property.  SUB will be invoked as
+# follows:  &SUB(gs, path, props);  where `gs' is this instance of
+# Git::SVN, `path' the path to the directory where the properties
+# `props' were found.  The `path' will be relative to point of checkout,
+# that is, if url://repo/trunk is the current Git branch, and that
+# directory contains a sub-directory `d', SUB will be invoked with `/d/'
+# as `path' (note the trailing `/').
+sub prop_walk {
+       my ($self, $path, $rev, $sub) = @_;
+
+       my ($dirent, undef, $props) = $self->ra->get_dir($path, $rev);
+       $path =~ s#^/*#/#g;
        my $p = $path;
-       $p =~ s#^\Q$self->{path}\E(/|$)##;
-       print $fh length $p ? "\n# $p\n" : "\n# /\n";
-       if (my $s = $props->{'svn:ignore'}) {
-               $s =~ s/[\r\n]+/\n/g;
-               chomp $s;
-               if (length $p == 0) {
-                       $s =~ s#\n#\n/$p#g;
-                       print $fh "/$s\n";
-               } else {
-                       $s =~ s#\n#\n/$p/#g;
-                       print $fh "/$p/$s\n";
-               }
-       }
+       # Strip the irrelevant part of the path.
+       $p =~ s#^/+\Q$self->{path}\E(/|$)#/#;
+       # Ensure the path is terminated by a `/'.
+       $p =~ s#/*$#/#;
+
+       # The properties contain all the internal SVN stuff nobody
+       # (usually) cares about.
+       my $interesting_props = 0;
+       foreach (keys %{$props}) {
+               # If it doesn't start with `svn:', it must be a
+               # user-defined property.
+               ++$interesting_props and next if $_ !~ /^svn:/;
+               # FIXME: Fragile, if SVN adds new public properties,
+               # this needs to be updated.
+               ++$interesting_props if /^svn:(?:ignore|keywords|executable
+                                                |eol-style|mime-type
+                                                |externals|needs-lock)$/x;
+       }
+       &$sub($self, $p, $props) if $interesting_props;
+
        foreach (sort keys %$dirent) {
                next if $dirent->{$_}->{kind} != $SVN::Node::dir;
-               $self->traverse_ignore($fh, "$path/$_", $r);
+               $self->prop_walk($path . '/' . $_, $rev, $sub);
        }
 }