X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/8200c01ad21ff0fbe3e9fcff6f25b645a2b813be..51a00c77d7d399ee5bc7493d03d374fc0312975a:/mr?ds=inline

diff --git a/mr b/mr
index 2932ea3..f8b78c0 100755
--- a/mr
+++ b/mr
@@ -54,6 +54,11 @@ like git.)
 
 The optional -m parameter allows specifying a commit message.
 
+=back
+
+Actions can be abbreviated to any unambiguous subsctring, so
+"mr st" is equivilant to "mr status".
+
 =head1 OPTIONS
 
 =over 4
@@ -77,9 +82,10 @@ Be verbose.
 
 =head1 FILES
 
-B<mr> is configured by .mrconfig files. It searches for .mrconfig files in
-your home directory, and in the root directory of each repository specified
-in a .mrconfig file. So you could have a ~/.mrconfig that registers a
+B<mr> is configured by .mrconfig files. It starts by reading the .mrconfig
+file in your home directory. Each repository specified in a .mrconfig file
+can also have its own .mrconfig file in its root directory that can
+optionally be used as well. So you could have a ~/.mrconfig that registers a
 repository ~/src, that itself contains a ~/src/.mrconfig file, that in turn
 registers several additional repositories.
 
@@ -95,8 +101,14 @@ environment, where any additional parameters you pass are available in
 a command, except for the "checkout" command, which is run in the parent
 of the repository directory, since the repository isn't checked out yet.
 
-There are two special parameters. If the "skip" parameter is set and
+There are three special parameters. If the "skip" parameter is set and
 its command returns nonzero, then B<mr> will skip acting on that repository.
+If the "chain" parameter is set and its command returns nonzero, then B<mr>
+will try to load a .mrconfig file from the root of the repository. (You
+should avoid chaining from repositories with untrusted committers.) The
+"lib" parameter can specify some shell code that will be run before each
+command, this can be a useful way to define shell functions other commands
+can use.
 
 The "default" section allows setting up default handlers for each action,
 and is overridden by the contents of other sections. mr contains default
@@ -107,12 +119,22 @@ For example:
 
   [src]
   checkout = svn co svn://svn.example.com/src/trunk src
+  chain = true
 
   [src/linux-2.6]
-  # only check this out on kodama
-  skip = test $(hostname) != kodama
+  skip = small
   checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
 
+  [default]
+  lib = \
+  small() {
+  	case "$(hostname)" in; \
+  	slug|snail); \
+  		return 0; ;; ; \
+  	esac; \
+  	return 1; \
+  }
+
 =head1 AUTHOR
 
 Copyright 2007 Joey Hess <joey@kitenet.net>
@@ -132,6 +154,7 @@ my $directory=getcwd();
 my $config="$ENV{HOME}/.mrconfig";
 my $verbose=0;
 my %config;
+my %knownactions;
 
 Getopt::Long::Configure("no_permute");
 my $result=GetOptions(
@@ -142,13 +165,23 @@ my $result=GetOptions(
 if (! $result || @ARGV < 1) {
 	die("Usage: mr [-d directory] action [params ...]\n");
 }
-my $action=shift @ARGV;
 
 loadconfig(\*DATA);
 loadconfig($config);
 #use Data::Dumper;
 #print Dumper(\%config);
 
+my $action=shift @ARGV;
+if (! $knownactions{$action}) {
+	my @matches = grep { /^\Q$action\E/ } keys %knownactions;
+	if (@matches == 1) {
+		$action=$matches[0];
+	}
+	else {
+		die "mr: ambiguous action \"$action\" (matches @matches)\n";
+	}
+}
+
 my (@failed, @successful, @skipped);
 my $first=1;
 foreach my $topdir (sort keys %config) {
@@ -168,15 +201,6 @@ foreach my $topdir (sort keys %config) {
 		print "\n" unless $first;
 		$first=0;
 
-		if (exists $config{$topdir}{$subdir}{skip}) {
-			my $ret=system($config{$topdir}{$subdir}{skip});
-			if ($ret >> 8 == 0) {
-				print "mr $action: $dir skipped per config file\n" if $verbose;
-				push @skipped, $dir;
-				next;
-			}
-		}
-
 		action($action, $dir, $topdir, $subdir);
 
 	}
@@ -184,32 +208,47 @@ foreach my $topdir (sort keys %config) {
 
 sub action {
 	my ($action, $dir, $topdir, $subdir) = @_;
+	
+	my $lib= exists $config{$topdir}{$subdir}{lib} ?
+	                $config{$topdir}{$subdir}{lib} : "";
 
 	if ($action eq 'checkout') {
 		if (-d $dir) {
-			print "mr $action: $dir already exists, skipping checkout\n";
+			print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
 			push @skipped, $dir;
-			next;
+			return;
 		}
 		$dir=~s/^(.*)\/[^\/]+\/?$/$1/;
 	}
-	if ($action eq 'update') {
+	elsif ($action eq 'update') {
 		if (! -d $dir) {
 			return action("checkout", $dir, $topdir, $subdir);
 		}
 	}
-
+	
 	if (! chdir($dir)) {
 		print STDERR "mr $action: failed to chdir to $dir: $!\n";
 		push @skipped, $dir;
 	}
-	elsif (! exists $config{$topdir}{$subdir}{$action}) {
+
+	if (exists $config{$topdir}{$subdir}{skip}) {
+		my $ret=system($lib.$config{$topdir}{$subdir}{skip});
+		if ($ret >> 8 == 0) {
+			print "mr $action: $dir skipped per config file\n" if $verbose;
+			push @skipped, $dir;
+			next;
+		}
+	}
+
+	if (! exists $config{$topdir}{$subdir}{$action}) {
 		print STDERR "mr $action: no defined $action command for $topdir$subdir, skipping\n";
 		push @skipped, $dir;
 	}
 	else {
 		print "mr $action: in $dir\n";
-		my $command="set -e; my_action(){ $config{$topdir}{$subdir}{$action} ; }; my_action @ARGV";
+		my $command="set -e; ".$lib.
+			"my_action(){ $config{$topdir}{$subdir}{$action} ; }; my_action ".
+			join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
 		my $ret=system($command);
 		if ($ret != 0) {
 			print STDERR "mr $action: failed to run: $command\n" if $verbose;
@@ -241,7 +280,13 @@ print "\nmr $action: finished (".join("; ",
 	showstat($#failed+1, "failed", "failed"),
 	showstat($#skipped+1, "skipped", "skipped"),
 ).")\n";
-exit @failed ? 1 : 0;
+if (@failed) {
+	exit 1;
+}
+elsif (! @successful && @skipped) {
+	exit 1;
+}
+exit 0;
 
 my %loaded;
 sub loadconfig {
@@ -288,10 +333,6 @@ sub loadconfig {
 		next if /^\s*\#/ || /^\s*$/;
 		if (/^\s*\[([^\]]*)\]\s*$/) {
 			$section=$1;
-			if (length $dir && $section ne "default" &&
-			    -e $dir.$section."/.mrconfig") {
-				push @toload, $dir.$section."/.mrconfig";
-		        }
 		}
 		elsif (/^\s*(\w+)\s*=\s*(.*)/) {
 			my $parameter=$1;
@@ -311,7 +352,20 @@ sub loadconfig {
 				# copy in defaults
 				$config{$dir}{$section}={ %{$config{$dir}{default}} };
 			}
-			$config{$dir}{$section}{$parameter}=$value;
+			if ($parameter ne 'lib') {
+				$config{$dir}{$section}{$parameter}=$value;
+				$knownactions{$parameter}=1;
+			}
+			else {
+				$config{$dir}{$section}{$parameter}.=$value." ; ";
+			}
+
+			if ($parameter eq 'chain' &&
+			    length $dir && $section ne "default" &&
+			    -e $dir.$section."/.mrconfig" &&
+		    	    system($value) >> 8 == 0) {
+				push @toload, $dir.$section."/.mrconfig";
+		        }
 		}
 		else {
 				die "$f line $.: parse error\n";
@@ -324,18 +378,22 @@ sub loadconfig {
 	}
 }
 
-__DATA__
-# Some useful actions that mr knows about by default.
+# Finally, some useful actions that mr knows about by default.
 # These can be overridden in ~/.mrconfig.
+__DATA__
 [default]
+lib = \
+	error() { \
+		echo "mr: $@" >&2; \
+		exit 1; \
+	}
 update = \
 	if [ -d .svn ]; then \
 		svn update; \
 	elif [ -d .git ]; then \
 		git pull origin master; \
 	else \
-		echo "mr update: unknown RCS"; \
-		exit 1; \
+		error "unknown repo type"; \
 	fi
 status = \
 	if [ -d .svn ]; then \
@@ -343,16 +401,13 @@ status = \
 	elif [ -d .git ]; then \
 		git status || true; \
 	else \
-		echo "mr status: unknown RCS"; \
-		exit 1; \
+		error "unknown repo type"; \
 	fi
 commit = \
 	if [ -d .svn ]; then \
 		svn commit "$@"; \
 	elif [ -d .git ]; then \
-		git commit -a "$@"; \
-		git push --all; \
+		git commit -a "$@" && git push --all; \
 	else \
-		echo "mr commit: unknown RCS"; \
-		exit 1; \
+		error "unknown repo type"; \
 	fi