X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/6976c4433469d551a6be890659d59ab8418f897b..60b9f581dbccffea5167347d474c34d0a6ad4c77:/mr?ds=sidebyside

diff --git a/mr b/mr
index bc73d40..87618a2 100755
--- a/mr
+++ b/mr
@@ -16,6 +16,8 @@ B<mr> [options] status
 
 B<mr> [options] commit [-m "message"]
 
+B<mr> [options] record [-m "message"]
+
 B<mr> [options] diff
 
 B<mr> [options] log
@@ -30,7 +32,7 @@ B<mr> [options] action [params ...]
 
 B<mr> is a Multiple Repository management tool. It can checkout, update, or
 perform other actions on a set of repositories as if they were one combined
-respository. It supports any combination of subversion, git, cvs, mecurial,
+repository. It supports any combination of subversion, git, cvs, mecurial,
 bzr and darcs repositories, and support for other revision control systems can
 easily be added.
 
@@ -62,7 +64,16 @@ uncommitted changes are present in the repository.
 =item commit (or ci)
 
 Commits changes to each repository. (By default, changes are pushed to the
-remote repository too, when using distributed systems like git.)
+remote repository too, when using distributed systems like git. If you
+don't like this default, you can change it in your .mrconfig, or use record
+instead.)
+
+The optional -m parameter allows specifying a commit message.
+
+=item record
+
+Records changes to the local repository, but does not push them to the
+remote repository. Only supported for distributed revision control systems.
 
 The optional -m parameter allows specifying a commit message.
 
@@ -121,8 +132,8 @@ Displays this help.
 
 =back
 
-Actions can be abbreviated to any unambiguous subsctring, so
-"mr st" is equivilant to "mr status", and "mr up" is equivilant to "mr
+Actions can be abbreviated to any unambiguous substring, so
+"mr st" is equivalent to "mr status", and "mr up" is equivalent to "mr
 update"
 
 Additional parameters can be passed to most commands, and are passed on
@@ -147,21 +158,33 @@ Use the specified mrconfig file. The default is B<~/.mrconfig>
 
 Be verbose.
 
+=item -q
+
+Be quiet.
+
 =item -s
 
 Expand the statistics line displayed at the end to include information
 about exactly which repositories failed and were skipped, if any.
 
-=item -n
+=item -n [number]
+
+If no number if specified, just operate on the repository for the current
+directory, do not recurse into deeper repositories.
 
-Just operate on the repository for the current directory, do not 
-recurse into deeper repositories.
+If a number is specified, will recurse into repositories at most that many
+subdirectories deep. For example, with -n 2 it would recurse into ./src/foo,
+but not ./src/packages/bar.
 
-=item -j number
+=item -j [number]
 
-Run the specified number of jobs in parallel. This can greatly speed up
-operations such as updates. It is not recommended for interactive
-operations.
+Run the specified number of jobs in parallel, or an unlimited number of jobs
+with no number specified. This can greatly speed up operations such as updates.
+It is not recommended for interactive operations.
+
+Note that running more than 10 jobs at a time is likely to run afoul of
+ssh connection limits. Running between 3 and 5 jobs at a time will yeild
+a good speedup in updates without loading the machine too much.
 
 =back
 
@@ -232,7 +255,7 @@ mr is run by joey. The second uses the hours_since function
 (included in mr's built-in library) to skip updating the repo unless it's
 been at least 12 hours since the last update.
 
-  skip = test $(whoami) != joey
+  skip = test `whoami` != joey
   skip = [ "$1" = update ] && ! hours_since "$1" 12
 
 =item order
@@ -308,11 +331,13 @@ use constant {
 # configurables
 my $config_overridden=0;
 my $verbose=0;
+my $quiet=0;
 my $stats=0;
-my $no_recurse=0;
+my $max_depth;
 my $no_chdir=0;
 my $jobs=1;
 my $directory=getcwd();
+$ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
 
 # globals :-(
 my %config;
@@ -323,33 +348,14 @@ my (@ok, @failed, @skipped);
 
 main();
 
-sub dispatch { #{{{
-	my $action=shift;
-
-	# actions that do not operate on all repos
-	if ($action eq 'help') {
-		help(@ARGV);
-	}
-	elsif ($action eq 'config') {
-		config(@ARGV);
-	}
-	elsif ($action eq 'register') {
-		register(@ARGV);
-	}
-
-	if ($jobs > 1) {
-		mrs($action, selectrepos());
-	}
-	else {
-		foreach my $repo (selectrepos()) {
-			record($repo, action($action, @$repo));
-		}
-	}
-} #}}}
-
+my %rcs;
 sub rcs_test { #{{{
 	my ($action, $dir, $topdir, $subdir) = @_;
 
+	if (exists $rcs{$dir}) {
+		return $rcs{$dir};
+	}
+
 	my $test="set -e\n";
 	foreach my $rcs_test (
 			sort {
@@ -367,21 +373,30 @@ sub rcs_test { #{{{
 	print "mr $action: running rcs test >>$test<<\n" if $verbose;
 	my $rcs=`$test`;
 	chomp $rcs;
-	if (! length $rcs) {
+	if ($rcs=~/\n/s) {
+		$rcs=~s/\n/, /g;
+		print STDERR "mr $action: found multiple possible repository types ($rcs) for $topdir$subdir\n";
 		return undef;
 	}
+	if (! length $rcs) {
+		return $rcs{$dir}=undef;
+	}
 	else {
-		return $rcs;
+		return $rcs{$dir}=$rcs;
 	}
 } #}}}
 	
 sub findcommand { #{{{
-	my ($action, $dir, $topdir, $subdir) = @_;
+	my ($action, $dir, $topdir, $subdir, $is_checkout) = @_;
 	
 	if (exists $config{$topdir}{$subdir}{$action}) {
 		return $config{$topdir}{$subdir}{$action};
 	}
 
+	if ($is_checkout) {
+		return undef;
+	}
+
 	my $rcs=rcs_test(@_);
 
 	if (defined $rcs && 
@@ -399,19 +414,15 @@ sub action { #{{{
 	$ENV{MR_CONFIG}=$configfiles{$topdir};
 	my $lib=exists $config{$topdir}{$subdir}{lib} ?
 	               $config{$topdir}{$subdir}{lib}."\n" : "";
+	my $is_checkout=($action eq 'checkout');
 
-	if ($action eq 'checkout') {
+	if ($is_checkout) {
 		if (-d $dir) {
 			print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
 			return SKIPPED;
 		}
 
 		$dir=~s/^(.*)\/[^\/]+\/?$/$1/;
-
-		if (! -d $dir) {
-			print "mr $action: creating parent directory $dir\n" if $verbose;
-			system("mkdir", "-p", $dir);
-		}
 	}
 	elsif ($action =~ /update/) {
 		if (! -d $dir) {
@@ -421,8 +432,8 @@ sub action { #{{{
 
 	$ENV{MR_REPO}=$dir;
 
-	my $skiptest=findcommand("skip", $dir, $topdir, $subdir);
-	my $command=findcommand($action, $dir, $topdir, $subdir);
+	my $skiptest=findcommand("skip", $dir, $topdir, $subdir, $is_checkout);
+	my $command=findcommand($action, $dir, $topdir, $subdir, $is_checkout);
 
 	if (defined $skiptest) {
 		my $test="set -e;".$lib.
@@ -444,7 +455,12 @@ sub action { #{{{
 			return SKIPPED;
 		}
 	}
-	
+
+	if ($is_checkout && ! -d $dir) {
+		print "mr $action: creating parent directory $dir\n" if $verbose;
+		system("mkdir", "-p", $dir);
+	}
+
 	if (! $no_chdir && ! chdir($dir)) {
 		print STDERR "mr $action: failed to chdir to $dir: $!\n";
 		return FAILED;
@@ -456,18 +472,18 @@ sub action { #{{{
 			return FAILED;
 		}
 		else {
-			print STDERR "mr $action: no defined $action command for $rcs repository $topdir$subdir, skipping\n";
+			print STDERR "mr $action: no defined action for $rcs repository $topdir$subdir, skipping\n";
 			return SKIPPED;
 		}
 	}
 	else {
 		if (! $no_chdir) {
-			print "mr $action: $topdir$subdir\n";
+			print "mr $action: $topdir$subdir\n" unless $quiet;
 		}
 		else {
 			my $s=$directory;
 			$s=~s/^\Q$topdir$subdir\E\/?//;
-			print "mr $action: $topdir$subdir (in subdir $s)\n";
+			print "mr $action: $topdir$subdir (in subdir $s)\n" unless $quiet;
 		}
 		$command="set -e; ".$lib.
 			"my_action(){ $command\n }; my_action ".
@@ -514,7 +530,7 @@ sub mrs { #{{{
 	my @out;
 	my $running=0;
 	while (@fhs or @repos) {
-		while ($running < $jobs && @repos) {
+		while ((!$jobs || $running < $jobs) && @repos) {
 			$running++;
 			my $repo = shift @repos;
 			pipe(my $outfh, CHILD_STDOUT);
@@ -605,10 +621,10 @@ sub showstats { #{{{
 		showstat($#ok+1, "ok", "ok"),
 		showstat($#failed+1, "failed", "failed"),
 		showstat($#skipped+1, "skipped", "skipped"),
-	).")\n";
+	).")\n" unless $quiet;
 	if ($stats) {
 		if (@skipped) {
-			print "mr $action: (skipped: ".join(" ", @skipped).")\n";
+			print "mr $action: (skipped: ".join(" ", @skipped).")\n" unless $quiet;
 		}
 		if (@failed) {
 			print STDERR "mr $action: (failed: ".join(" ", @failed).")\n";
@@ -659,8 +675,13 @@ sub selectrepos { #{{{
 		my $d=$directory;
 		$dir.="/" unless $dir=~/\/$/;
 		$d.="/" unless $d=~/\/$/;
-		next if $no_recurse && $d ne $dir;
 		next if $dir ne $d && $dir !~ /^\Q$d\E/;
+		if (defined $max_depth) {
+			my @a=split('/', $dir);
+			my @b=split('/', $d);
+			do { } while (@a && @b && shift(@a) eq shift(@b));
+			next if @a > $max_depth || @b > $max_depth;
+		}
 		push @repos, [$dir, $topdir, $subdir];
 	}
 	if (! @repos) {
@@ -912,7 +933,31 @@ sub modifyconfig { #{{{
 	print $out @out;
 	close $out;	
 } #}}}
-	
+
+sub dispatch { #{{{
+	my $action=shift;
+
+	# actions that do not operate on all repos
+	if ($action eq 'help') {
+		help(@ARGV);
+	}
+	elsif ($action eq 'config') {
+		config(@ARGV);
+	}
+	elsif ($action eq 'register') {
+		register(@ARGV);
+	}
+
+	if (!$jobs || $jobs > 1) {
+		mrs($action, selectrepos());
+	}
+	else {
+		foreach my $repo (selectrepos()) {
+			record($repo, action($action, @$repo));
+		}
+	}
+} #}}}
+
 sub help { #{{{
 	exec($config{''}{DEFAULT}{help}) || die "exec: $!";
 } #}}}
@@ -960,14 +1005,19 @@ sub register { #{{{
 		# Find the closest known mrconfig file to the current
 		# directory.
 		$directory.="/" unless $directory=~/\/$/;
+		my $foundconfig=0;
 		foreach my $topdir (reverse sort keys %config) {
 			next unless length $topdir;
 			if ($directory=~/^\Q$topdir\E/) {
 				$ENV{MR_CONFIG}=$configfiles{$topdir};
 				$directory=$topdir;
+				$foundconfig=1;
 				last;
 			}
 		}
+		if (! $foundconfig) {
+			$directory=""; # no config file, use builtin
+		}
 	}
 	if (@ARGV) {
 		my $subdir=shift @ARGV;
@@ -977,7 +1027,7 @@ sub register { #{{{
 	}
 
 	$ENV{MR_REPO}=getcwd();
-	my $command=findcommand("register", $ENV{MR_REPO}, $directory, 'DEFAULT');
+	my $command=findcommand("register", $ENV{MR_REPO}, $directory, 'DEFAULT', 0);
 	if (! defined $command) {
 		die "mr register: unknown repository type\n";
 	}
@@ -1015,14 +1065,15 @@ sub expandaction { #{{{
 } #}}}
 
 sub getopts { #{{{
-	Getopt::Long::Configure("no_permute");
+	Getopt::Long::Configure("bundling", "no_permute");
 	my $result=GetOptions(
 		"d|directory=s" => sub { $directory=abs_path($_[1]) },
 		"c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
 		"v|verbose" => \$verbose,
+		"q|quiet" => \$quiet,
 		"s|stats" => \$stats,
-		"n|no-recurse" => \$no_recurse,
-		"j|jobs=i" => \$jobs,
+		"n|no-recurse:i" => \$max_depth,
+		"j|jobs:i" => \$jobs,
 	);
 	if (! $result || @ARGV < 1) {
 		die("Usage: mr [-d directory] action [params ...]\n".
@@ -1036,8 +1087,6 @@ sub init { #{{{
 		exit 2;
 	};
 	
-	$ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
-
 	# This can happen if it's run in a directory that was removed
 	# or other strangeness.
 	if (! defined $directory) {
@@ -1113,7 +1162,7 @@ lib =
 		if [ -z "$flagfile" ]; then
 			error "cannot determine flag filename"
 		fi
-		delta=$(perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile")
+		delta=`perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile"`
 		if [ "$delta" -lt "$2" ]; then
 			exit 0
 		else
@@ -1131,10 +1180,15 @@ darcs_test = test -d "$MR_REPO"/_darcs
 git_bare_test =
 	test -d "$MR_REPO"/refs/heads && test -d "$MR_REPO"/refs/tags &&
 	test -d "$MR_REPO"/objects && test -f "$MR_REPO"/config &&
-	test "$(GIT_CONFIG="$MR_REPO"/config git-config --get core.bare)" = true
+	test "`GIT_CONFIG="$MR_REPO"/config git config --get core.bare`" = true
 
 svn_update = svn update "$@"
-git_update = if [ "$@" ]; then git pull "$@"; else git pull -t origin master; fi
+git_update =
+	if [ "$@" ]; then
+		git pull "$@"
+	else
+		git pull
+	fi
 bzr_update = bzr merge "$@"
 cvs_update = cvs update "$@"
 hg_update  = hg pull "$@" && hg update "$@"
@@ -1152,14 +1206,19 @@ git_commit = git commit -a "$@" && git push --all
 bzr_commit = bzr commit "$@" && bzr push
 cvs_commit = cvs commit "$@"
 hg_commit  = hg commit -m "$@" && hg push
-darcs_commit = darcs commit -a -m "$@" && darcs push -a
+darcs_commit = darcs record -a -m "$@" && darcs push -a
+
+git_record = git commit -a "$@"
+bzr_record = bzr commit "$@"
+hg_record  = hg commit -m "$@"
+darcs_record = darcs record -a -m "$@"
 
 svn_diff = svn diff "$@"
 git_diff = git diff "$@"
 bzr_diff = bzr diff "$@"
 cvs_diff = cvs diff "$@"
 hg_diff  = hg diff "$@"
-darcs_diff = darcs diff "$@"
+darcs_diff = darcs diff -u "$@"
 
 svn_log = svn log "$@"
 git_log = git log "$@"
@@ -1170,44 +1229,44 @@ darcs_log = darcs changes "$@"
 git_bare_log = git log "$@"
 
 svn_register =
-	url=$(LANG=C svn info . | grep -i ^URL: | cut -d ' ' -f 2)
+	url=`LC_ALL=C svn info . | grep -i '^URL:' | cut -d ' ' -f 2`
 	if [ -z "$url" ]; then
 		error "cannot determine svn url"
 	fi
 	echo "Registering svn url: $url in $MR_CONFIG"
 	mr -c "$MR_CONFIG" config "`pwd`" checkout="svn co '$url' '$MR_REPO'"
 git_register = 
-	url="$(LANG=C git-config --get remote.origin.url)" || true
+	url="`LC_ALL=C git config --get remote.origin.url`" || true
 	if [ -z "$url" ]; then
 		error "cannot determine git url"
 	fi
 	echo "Registering git url: $url in $MR_CONFIG"
 	mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone '$url' '$MR_REPO'"
 bzr_register =
-	url=$(cat .bzr/branch/parent)
+	url="`LC_ALL=C bzr info . | egrep -i 'checkout of branch|parent branch' | awk '{print $NF}'`"
 	if [ -z "$url" ]; then
 		error "cannot determine bzr url"
 	fi
 	echo "Registering bzr url: $url in $MR_CONFIG"
 	mr -c "$MR_CONFIG" config "`pwd`" checkout="bzr clone '$url' '$MR_REPO'"
 cvs_register =
-	repo=$(cat CVS/Repository)
-	root=$(cat CVS/Root)
+	repo=`cat CVS/Repository`
+	root=`cat CVS/Root`
 	if [ -z "$root" ]; then
 		error "cannot determine cvs root"
 		fi
 	echo "Registering cvs repository $repo at root $root"
 	mr -c "$MR_CONFIG" config "`pwd`" checkout="cvs -d '$root' co -d '$MR_REPO' '$repo'"
 hg_register = 
-	url=$(hg showconfig paths.default)
+	url=`hg showconfig paths.default`
 	echo "Registering mercurial repo url: $url in $MR_CONFIG"
 	mr -c "$MR_CONFIG" config "`pwd`" checkout="hg clone '$url' '$MR_REPO'"
 darcs_register = 
-	url=$(cat _darcs/prefs/defaultrepo)
+	url=`cat _darcs/prefs/defaultrepo`
 	echo "Registering darcs repository $url in $MR_CONFIG"
-	mr -c "$MR_CONFIG" config "`pwd`" checkout="darcs get '$url'p '$MR_REPO'"
+	mr -c "$MR_CONFIG" config "`pwd`" checkout="darcs get '$url' '$MR_REPO'"
 git_bare_register = 
-	url="$(LANG=C GIT_CONFIG=config git-config --get remote.origin.url)" || true
+	url="`LC_ALL=C GIT_CONFIG=config git config --get remote.origin.url`" || true
 	if [ -z "$url" ]; then
 		error "cannot determine git url"
 	fi
@@ -1218,7 +1277,10 @@ help =
 	if [ ! -e "$MR_PATH" ]; then
 		error "cannot find program path"
 	fi
-	(pod2man -c mr "$MR_PATH" | man -l -) || error "pod2man or man failed"
+	tmp=$(mktemp -t mr.XXXXXXXXXX) || error "mktemp failed"
+	trap "rm -f $tmp" exit
+	pod2man -c mr "$MR_PATH" > "$tmp" || error "pod2man failed"
+	man -l "$tmp" || error "man failed"
 list = true
 config =