]> git.madduck.net Git - code/myrepos.git/blobdiff - mr

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

VCS_dir_test optmisation
[code/myrepos.git] / mr
diff --git a/mr b/mr
index ff30c16cefefc05d634638cb353bfa24c4229a15..1923506a42cf4522e403d23f9cd63e01d46b42dd 100755 (executable)
--- a/mr
+++ b/mr
@@ -435,9 +435,7 @@ whenever the repository is changed.
 When looking for a command to run for a given action, mr first looks for
 a parameter with the same name as the action. If that is not found, it
 looks for a parameter named "VCS_action" (substituting in the name of the
-version control system and the action). The name of the version control
-system is itself determined by running each defined "VCS_test" action,
-until one succeeds.
+version control system and the action).
 
 Internally, mr has settings for "git_update", "svn_update", etc. To change
 the action that is performed for a given version control system, you can
@@ -457,6 +455,18 @@ Any parameter can be suffixed with C<_append>, to add an additional value
 to the existing value of the parameter. In this way, actions 
 can be constructed accumulatively.
 
+=item VCS_test
+
+The name of the version control system is itself determined by
+running each defined "VCS_test" action, until one succeeds.
+
+=item VCS_dir_test
+
+This is a more optimised way to test for the version control system.
+Each "VCS_dir_test" action is run once, and can output lines consisting
+of the name of a VCS, and a directory to look for in the top of a repo
+to detect that VCS.
+
 =back
 
 =head1 UNTRUSTED MRCONFIG FILES
@@ -539,7 +549,53 @@ my (@ok, @failed, @skipped);
 
 main();
 
+sub shellquote {
+       my $i=shift;
+       $i=~s/'/'"'"'/g;
+       return "'$i'";
+}
+
+# Runs a shell command using a supplied function.
+# The lib will be included in the shell command line, and any params
+# will be available in the shell as $1, $2, etc.
+my $lastlib;
+sub runsh {
+       my ($action, $topdir, $subdir, $command, $params, $runner) = @_;
+
+       # optimisation: avoid running the shell for true and false
+       if ($command =~ /^\s*true\s*$/) {
+               $?=0;
+               return 0;
+       }
+       elsif ($command =~ /^\s*false\s*$/) {
+               $?=0;
+               return 1;
+       }
+       
+       my $quotedparams=join(" ", (map { shellquote($_) } @$params));
+       my $lib=exists $config{$topdir}{$subdir}{lib} ?
+                      $config{$topdir}{$subdir}{lib}."\n" : "";
+       if ($verbose && (! defined $lastlib || $lastlib ne $lib)) {
+               print "mr library now: >>$lib<<\n";
+               $lastlib=$lib;
+       }
+       my $shellcode="set -e;".$lib.
+               "my_sh(){ $command\n }; my_sh $quotedparams";
+       print "mr $action: running $action >>$command<<\n" if $verbose;
+       $runner->($shellcode);
+}
+
+sub runshpipe {
+       runsh @_, sub {
+               my $sh=shift;
+               my $ret=`$sh`;
+               chomp $ret;
+               return $ret;
+       };
+}
+
 my %vcs;
+my %vcs_dir_test;
 sub vcs_test {
        my ($action, $dir, $topdir, $subdir) = @_;
 
@@ -547,23 +603,44 @@ sub vcs_test {
                return $vcs{$dir};
        }
 
-       my $test="set -e\n";
+       my $test="";
+       my $dir_test="";
        foreach my $vcs_test (
                        sort {
                                length $a <=> length $b 
                                          ||
                                       $a cmp $b
                        } grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) {
-               my ($vcs)=$vcs_test=~/(.*)_test/;
+               if ($vcs_test =~ /(.*)_dir_test/) {
+                       my $vcs=$1;
+                       if (! defined $vcs_dir_test{$vcs}) {
+                               $dir_test.=$config{$topdir}{$subdir}{$vcs_test}."\n";
+                       }
+                       next;
+               }
+               my $vcs=$vcs_test =~ /(.*)_test/;
                $test="my_$vcs_test() {\n$config{$topdir}{$subdir}{$vcs_test}\n}\n".$test;
                $test.="if my_$vcs_test; then echo $vcs; fi\n";
        }
-       $test=$config{$topdir}{$subdir}{lib}."\n".$test
-               if exists $config{$topdir}{$subdir}{lib};
-       
-       print "mr $action: running vcs test >>$test<<\n" if $verbose;
-       my $vcs=`$test`;
-       chomp $vcs;
+
+       if (length $dir_test) {
+               runsh "vcs dir test", $topdir, $subdir, $dir_test, [], sub {
+                       my $sh=shift;
+                       foreach my $line (`$sh`) {
+                               chomp $line;
+                               my ($vcs, $dir)=split(" ", $line);
+                               $vcs_dir_test{$vcs}=$dir;
+                       }
+               }
+       }
+
+       foreach my $vcs (keys %vcs_dir_test) {
+               if (-d "$ENV{MR_REPO}/$vcs_dir_test{$vcs}") {
+                       return $vcs{$dir}=$vcs;
+               }
+       }
+
+       my $vcs=runshpipe "vcs test", $topdir, $subdir, $test, [];
        if ($vcs=~/\n/s) {
                $vcs=~s/\n/, /g;
                print STDERR "mr $action: found multiple possible repository types ($vcs) for ".fulldir($topdir, $subdir)."\n";
@@ -609,8 +686,6 @@ sub action {
        my $fulldir=fulldir($topdir, $subdir);
 
        $ENV{MR_CONFIG}=$configfiles{$topdir};
-       my $lib=exists $config{$topdir}{$subdir}{lib} ?
-                      $config{$topdir}{$subdir}{lib}."\n" : "";
        my $is_checkout=($action eq 'checkout');
        my $is_update=($action =~ /update/);
 
@@ -621,10 +696,9 @@ sub action {
                my $testcommand=findcommand($testname, $dir, $topdir, $subdir, $is_checkout);
 
                if (defined $testcommand) {
-                       my $test="set -e;".$lib.
-                               "my_action(){ $testcommand\n }; my_action '$action'";
-                       print "mr $action: running $testname test >>$test<<\n" if $verbose;
-                       my $ret=system($test);
+                       my $ret=runsh "$testname test", $topdir, $subdir,
+                               $testcommand, [$action],
+                               sub { system(shift()) };
                        if ($ret != 0) {
                                if (($? & 127) == 2) {
                                        print STDERR "mr $action: interrupted\n";
@@ -701,22 +775,22 @@ sub action {
                my $hookret=hook("pre_$action", $topdir, $subdir);
                return $hookret if $hookret != OK;
 
-               $command="set -e; ".$lib.
-                       "my_action(){ $command\n }; my_action ".
-                       join(" ", map { s/\\/\\\\/g; s/"/\"/g; '"'.$_.'"' } @ARGV);
-               print "mr $action: running >>$command<<\n" if $verbose;
-               my $ret;
-               if ($quiet) {
-                       my $output = qx/$command 2>&1/;
-                       $ret = $?;
-                       if ($ret != 0) {
-                               print "$actionmsg\n";
-                               print STDERR $output;
-                       }
-               }
-               else {
-                       $ret=system($command);
-               }
+               my $ret=runsh $action, $topdir, $subdir,
+                       $command, \@ARGV, sub {
+                               my $sh=shift;
+                               if ($quiet) {
+                                       my $output = qx/$sh 2>&1/;
+                                       my $ret = $?;
+                                       if ($ret != 0) {
+                                               print "$actionmsg\n";
+                                               print STDERR $output;
+                                       }
+                                       return $ret;
+                               }
+                               else {
+                                       system($sh);
+                               }
+                       };
                if ($ret != 0) {
                        if (($? & 127) == 2) {
                                print STDERR "mr $action: interrupted\n";
@@ -767,22 +841,20 @@ sub hook {
 
        my $command=$config{$topdir}{$subdir}{$hook};
        return OK unless defined $command;
-       my $lib=exists $config{$topdir}{$subdir}{lib} ?
-                      $config{$topdir}{$subdir}{lib}."\n" : "";
-       my $shell="set -e;".$lib.
-               "my_hook(){ $command\n }; my_hook";
-       print "mr $hook: running >>$shell<<\n" if $verbose;
-       my $ret;
-       if ($quiet) {
-               my $output = qx/$shell 2>&1/;
-               $ret = $?;
-               if ($ret != 0) {
-                       print STDERR $output;
-               }
-       }
-       else {
-               $ret=system($shell);
-       }
+       my $ret=runsh $hook, $topdir, $subdir, $command, [], sub {
+                       my $sh=shift;
+                       if ($quiet) {
+                               my $output = qx/$sh 2>&1/;
+                               my $ret = $?;
+                               if ($ret != 0) {
+                                       print STDERR $output;
+                               }
+                               return $ret;
+                       }
+                       else {
+                               system($sh);
+                       }
+               };
        if ($ret != 0) {
                if (($? & 127) == 2) {
                        print STDERR "mr $hook: interrupted\n";
@@ -1202,10 +1274,10 @@ sub loadconfig {
        my $lineerror = sub {
                my $msg=shift;
                if (defined $included) {
-                       die "mr: $f line $lineno included line '$line': $msg\n";
+                       die "mr: $msg at $f line $lineno, included line: $line\n";
                }
                else {
-                       die "mr: $f line $lineno: $msg\n";
+                       die "mr: $msg at $f line $lineno\n";
                }
        };
        my $trusterror = sub {
@@ -1783,12 +1855,12 @@ lib =
                fi
        }
 
-svn_test = test -d "$MR_REPO"/.svn
-git_test = test -d "$MR_REPO"/.git
-bzr_test = test -d "$MR_REPO"/.bzr
-cvs_test = test -d "$MR_REPO"/CVS
-hg_test  = test -d "$MR_REPO"/.hg
-darcs_test = test -d "$MR_REPO"/_darcs
+svn_dir_test = echo svn .svn
+git_dir_test = echo git .git
+bzr_dir_test = echo bzr .bzr
+cvs_dir_test = echo cvs CVS
+hg_dir_test  = echo hg .hg
+darcs_dir_test = echo darcs _darcs
 fossil_test = test -f "$MR_REPO"/_FOSSIL_
 git_bare_test =
        test -d "$MR_REPO"/refs/heads && test -d "$MR_REPO"/refs/tags &&