X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/32808ba79347c860fd3acfa3f8b731795170bc93..d82be80725445a6f848c7207f77680a66a903311:/mr?ds=sidebyside

diff --git a/mr b/mr
index 823e3df..7c4ff30 100755
--- a/mr
+++ b/mr
@@ -22,11 +22,17 @@ B<mr> [options] action [params ...]
 
 B<mr> is a Multiple Repository management tool. It allows you to register a
 set of repositories in a .mrconfig file, and then checkout, update, or
-perform other actions on all of the repositories at once.
+perform other actions on the repositories as if they were one big
+respository.
 
 Any mix of revision control systems can be used with B<mr>, and you can
 define arbitrary actions for commands like "update", "checkout", or "commit".
 
+B<mr> cds into and operates on all registered repsitories at or below your
+working directory. Or, if you are in a subdirectory of a repository that
+contains no other registered repositories, it will stay in that directory,
+and work on only that repository,
+
 The predefined commands should be fairly familiar to users of any revision
 control system:
 
@@ -69,11 +75,8 @@ Displays this help.
 =back
 
 Actions can be abbreviated to any unambiguous subsctring, so
-"mr st" is equivilant to "mr status".
-
-B<mr> operates on all registered repsitories at or below your working
-directory. Or, if you are in a subdirectory of a repository, it will act on
-only that repository.
+"mr st" is equivilant to "mr status", and "mr up" is equivilant to "mr
+update"
 
 Additional parameters can be passed to other commands than "commit", they
 will be passed on unchanged to the underlying revision control system.
@@ -87,8 +90,7 @@ revision control system.
 =item -d directory
 
 Specifies the topmost directory that B<mr> should work in. The default is
-the current working directory. B<mr> will operate on all registered
-repositories at or under the directory.
+the current working directory.
 
 =item -c mrconfig
 
@@ -118,9 +120,11 @@ directory that contains the .mrconfig file.
 Within a section, each parameter defines a shell command to run to handle a
 given action. Note that these shell commands are run in a "set -e" shell
 environment, where any additional parameters you pass are available in
-"$@". B<mr> cds into the repository directory before running
-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.
+"$@". The "checkout" command is run in the parent of the repository
+directory, since the repository isn't checked out yet. All other commands
+are run inside the repository, though not necessarily at the top of it.
+The "MR_REPO" environment variable is set to the path to the top of the
+repository.
 
 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.
@@ -225,30 +229,38 @@ if ($action eq 'help') {
 	exec($config{''}{default}{help});
 }
 
-# handle being in a subdir of a repository
+# work out what repos to act on
+my @repos;
+my $nochdir=0;
 foreach my $topdir (sort keys %config) {
 	foreach my $subdir (sort keys %{$config{$topdir}}) {
-		if ($directory =~ /^\Q$topdir$subdir\E\//) {
-			$directory=$topdir.$subdir;
+		next if $subdir eq 'default';
+		my $dir=$topdir.$subdir;
+		next if $dir ne $directory && $dir !~ /^\Q$directory\E\//;
+		push @repos, [$dir, $topdir, $subdir];
+	}
+}
+if (! @repos) {
+	# fallback to find a leaf repo
+	LEAF: foreach my $topdir (reverse sort keys %config) {
+		foreach my $subdir (reverse sort keys %{$config{$topdir}}) {
+			next if $subdir eq 'default';
+			my $dir=$topdir.$subdir;
+			my $d=$directory;
+			$dir.="/" unless $dir=~/\/$/;
+			$d.="/" unless $d=~/\/$/;
+			if ($d=~/^\Q$dir\E/) {
+				push @repos, [$dir, $topdir, $subdir];
+				last LEAF;
+			}
 		}
 	}
+	$nochdir=1;
 }
 
 my (@failed, @successful, @skipped);
-foreach my $topdir (sort keys %config) {
-	foreach my $subdir (sort keys %{$config{$topdir}}) {
-		next if $subdir eq 'default';
-		
-		my $dir=$topdir.$subdir;
-
-		if (defined $directory &&
-		    $dir ne $directory &&
-		    $dir !~ /^\Q$directory\E\//) {
-			next;
-		}
-
-		action($action, $dir, $topdir, $subdir);
-	}
+foreach my $repo (@repos) {
+	action($action, @$repo);
 }
 
 sub action {
@@ -271,7 +283,8 @@ sub action {
 		}
 	}
 	
-	if (! chdir($dir)) {
+	$ENV{MR_REPO}=$dir;
+	if (! $nochdir && ! chdir($dir)) {
 		print STDERR "mr $action: failed to chdir to $dir: $!\n";
 		push @skipped, $dir;
 	}
@@ -322,6 +335,9 @@ sub showstat {
 	}
 	return;
 }
+if (! @successful && ! @failed && ! @skipped) {
+	die "mr $action: no repositories found to work on\n";
+}
 print "mr $action: finished (".join("; ",
 	showstat($#successful+1, "successful", "successful"),
 	showstat($#failed+1, "failed", "failed"),
@@ -441,33 +457,33 @@ lib = \
 		exit 1; \
 	}
 update = \
-	if [ -d .svn ]; then \
+	if [ -d "$MR_REPO"/.svn ]; then \
 		svn update "$@"; \
-	elif [ -d .git ]; then \
+	elif [ -d "$MR_REPO"/.git ]; then \
 		git pull origin master "$@"; \
 	else \
 		error "unknown repo type"; \
 	fi
 status = \
-	if [ -d .svn ]; then \
+	if [ -d "$MR_REPO"/.svn ]; then \
 		svn status "$@"; \
-	elif [ -d .git ]; then \
+	elif [ -d "$MR_REPO"/.git ]; then \
 		git status "$@" || true; \
 	else \
 		error "unknown repo type"; \
 	fi
 commit = \
-	if [ -d .svn ]; then \
+	if [ -d "$MR_REPO"/.svn ]; then \
 		svn commit "$@"; \
-	elif [ -d .git ]; then \
+	elif [ -d "$MR_REPO"/.git ]; then \
 		git commit -a "$@" && git push --all; \
 	else \
 		error "unknown repo type"; \
 	fi
 diff = \
-	if [ -d .svn ]; then \
+	if [ -d "$MR_REPO"/.svn ]; then \
 		svn diff "$@"; \
-	elif [ -d .git ]; then \
+	elif [ -d "$MR_REPO"/.git ]; then \
 		git diff "$@"; \
 	else \
 		error "unknown repo type"; \