X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/978a746fee46fc391edf0f53db3876326e204776..18a5c6c0242c7ca6ca07ea47cb33bbf47ce8e464:/mr?ds=sidebyside

diff --git a/mr b/mr
index ff5b60d..ddc0738 100755
--- a/mr
+++ b/mr
@@ -1,8 +1,8 @@
-#!/usr/bin/perl
+#!/usr/bin/env perl
 
 =head1 NAME
 
-mr - a Multiple Repository management tool
+mr - a tool to manage all your version control repos
 
 =head1 SYNOPSIS
 
@@ -16,12 +16,16 @@ B<mr> [options] commit [-m "message"]
 
 B<mr> [options] record [-m "message"]
 
+B<mr> [options] fetch
+
 B<mr> [options] push
 
 B<mr> [options] diff
 
 B<mr> [options] log
 
+B<mr> [options] grep pattern
+
 B<mr> [options] run command [param ...]
 
 B<mr> [options] bootstrap url [directory]
@@ -38,11 +42,11 @@ B<mr> [options] remember action [params ...]
 
 =head1 DESCRIPTION
 
-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
-repository. It supports any combination of subversion, git, cvs, mercurial,
-bzr, darcs and fossil repositories, and support for other version
-control systems can easily be added.
+B<mr> is a tool to manage all your version control repos. It can checkout,
+update, or perform other actions on a set of repositories as if they were
+one combined repository. It supports any combination of subversion, git,
+cvs, mercurial, bzr, darcs, fossil and veracity repositories, and support
+for other version control systems can easily be added.
 
 B<mr> cds into and operates on all registered repositories at or below your
 working directory. Or, if you are in a subdirectory of a repository that
@@ -73,7 +77,8 @@ If a repository isn't checked out yet, it will first check it out.
 =item status
 
 Displays a status report for each repository, showing what
-uncommitted changes are present in the repository.
+uncommitted changes are present in the repository. For distributed version
+control systems, also shows unpushed local branches.
 
 =item commit (or ci)
 
@@ -91,6 +96,12 @@ remote repository. Only supported for distributed version control systems.
 
 The optional -m parameter allows specifying a commit message.
 
+=item fetch
+
+Fetches from each repository's remote repository, but does not
+update the working copy. Only supported for some distributed version
+control systems.
+
 =item push
 
 Pushes committed local changes to the remote repository. A no-op for
@@ -104,6 +115,11 @@ Show a diff of uncommitted changes.
 
 Show the commit log.
 
+=item grep pattern
+
+Searches for a pattern in each repository using the grep subcommand. Uses
+ack-grep on VCS that do not have their own.
+
 =item run command [param ...]
 
 Runs the specified command in each repository.
@@ -116,8 +132,10 @@ These commands are also available:
 
 =item bootstrap url [directory]
 
-Causes mr to download the url, and use it as a .mrconfig file
-to checkout the repositories listed in it, into the specified directory.
+Causes mr to download the url, and use it as a .mrconfig file to checkout
+the repositories listed in it, into the specified directory.
+
+To use scp to download, the url may have the form ssh://[user@]host:file
 
 The directory will be created if it does not exist. If no directory is
 specified, the current directory will be used.
@@ -212,6 +230,13 @@ Use the specified mrconfig file. The default is to use both F<~/.mrconfig>
 as well as look for a F<.mrconfig> file in the current directory, or in one
 of its parent directories.
 
+=item -f
+
+=item --force
+
+Force mr to act on repositories that would normally be skipped due to their
+configuration.
+
 =item -v
 
 =item --verbose
@@ -367,7 +392,7 @@ been at least 12 hours since the last update.
  
 Another way to use skip is for a lazy checkout. This makes mr skip
 operating on a repo unless it already exists. To enable the 
-repo, you have to explicitly check it out (using "mr -d foo checkout").
+repo, you have to explicitly check it out (using "mr --force -d foo checkout").
 
   [foo]
   checkout = ...
@@ -398,7 +423,7 @@ Unlike all other parameters, this parameter does not need to be placed
 within a section.
 
 B<mr> ships several libraries that can be included to add support for
-additional version control type things (unison, git-svn, vcsh, git-fake-bare,
+additional version control type things (unison, git-svn, git-fake-bare,
 git-subtree). To include them all, you could use:
 
   include = cat /usr/share/mr/*
@@ -415,9 +440,13 @@ to keep track of and remember to delete old repositories.
 
 =item lib
 
-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 for other commands
-to use.
+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 for other commands to use. 
+
+Unlike most other parameters, this can be specified multiple times, in
+which case the chunks of shell code are accumulatively concatenated
+together.
 
 =item fixups
 
@@ -426,27 +455,38 @@ is checked out, or updated. This provides an easy way to do things
 like permissions fixups, or other tweaks to the repository content,
 whenever the repository is changed.
 
-=item pre_ and post_
-
-If a "pre_action" parameter is set, its command is run before mr performs the
-specified action. Similarly, "post_action" parameters are run after mr
-successfully performs the specified action. For example, "pre_commit" is
-run before committing; "post_update" is run after updating.
-
-=back
+=item VCS_action
 
 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
 override these VCS specific actions. To add a new version control system,
 you can just add VCS specific actions for it.
 
+=item pre_ and post_
+
+If a "pre_action" parameter is set, its command is run before mr performs the
+specified action. Similarly, "post_action" parameters are run after mr
+successfully performs the specified action. For example, "pre_commit" is
+run before committing; "post_update" is run after updating.
+
+=item _append
+
+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.
+
+=back
+
 =head1 UNTRUSTED MRCONFIG FILES
 
 Since mrconfig files can contain arbitrary shell commands, they can do
@@ -486,7 +526,7 @@ Copyright 2007-2011 Joey Hess <joey@kitenet.net>
 
 Licensed under the GNU GPL version 2 or higher.
 
-http://kitenet.net/~joey/code/mr/
+http://myrepos.branchable.com/
 
 =cut
 
@@ -508,6 +548,7 @@ my $config_overridden=0;
 my $verbose=0;
 my $quiet=0;
 my $stats=0;
+my $force=0;
 my $insecure=0;
 my $interactive=0;
 my $max_depth;
@@ -516,6 +557,7 @@ my $jobs=1;
 my $trust_all=0;
 my $directory=getcwd();
 
+my $HOME_MR_CONFIG = "$ENV{HOME}/.mrconfig";
 $ENV{MR_CONFIG}=find_mrconfig();
 
 # globals :-(
@@ -527,6 +569,57 @@ 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);
+}
+
+my %perl_cache;
+sub perl {
+	my $id=shift;
+	my $s=shift;
+	if ($s =~ m/^perl:\s+(.*)/s) {
+		return $perl_cache{$1} if exists $perl_cache{$1};
+		my $sub=eval "sub {$1}";
+		if (! defined $sub) {
+			print STDERR "mr: bad perl code in $id: $@\n";
+		}
+		return $perl_cache{$1} = $sub;
+	}
+	return undef;
+}
+
 my %vcs;
 sub vcs_test {
 	my ($action, $dir, $topdir, $subdir) = @_;
@@ -535,33 +628,47 @@ sub vcs_test {
 		return $vcs{$dir};
 	}
 
-	my $test="set -e\n";
+	my $test="";
+	my %perltest;
 	foreach my $vcs_test (
 			sort {
 				length $a <=> length $b 
 				          ||
 				       $a cmp $b
 			} grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) {
-		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";
+		my ($vcs)=$vcs_test =~ /(.*)_test/;
+		my $p=perl($vcs_test, $config{$topdir}{$subdir}{$vcs_test});
+		if (defined $p) {
+			$perltest{$vcs}=$p;
+		}
+		else {
+			$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 ($vcs=~/\n/s) {
-		$vcs=~s/\n/, /g;
-		print STDERR "mr $action: found multiple possible repository types ($vcs) for ".fulldir($topdir, $subdir)."\n";
+
+	my @vcs;
+	foreach my $vcs (keys %perltest) {
+		if ($perltest{$vcs}->()) {
+			push @vcs, $vcs;
+		}
+	}
+
+	push @vcs, split(/\n/,
+		runsh("vcs test", $topdir, $subdir, $test, [], sub {
+			my $sh=shift;
+			my $ret=`$sh`;
+			return $ret;
+		})) if length $test;
+	if (@vcs > 1) {
+		print STDERR "mr $action: found multiple possible repository types (@vcs) for ".fulldir($topdir, $subdir)."\n";
 		return undef;
 	}
-	if (! length $vcs) {
+	if (! @vcs) {
 		return $vcs{$dir}=undef;
 	}
 	else {
-		return $vcs{$dir}=$vcs;
+		return $vcs{$dir}=$vcs[0];
 	}
 }
 	
@@ -595,10 +702,9 @@ sub fulldir {
 sub action {
 	my ($action, $dir, $topdir, $subdir, $force_checkout) = @_;
 	my $fulldir=fulldir($topdir, $subdir);
+	my $checkout_dir;
 
 	$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/);
 
@@ -606,13 +712,14 @@ sub action {
 	$ENV{MR_ACTION}=$action;
 	
 	foreach my $testname ("skip", "deleted") {
+		next if $force && $testname eq "skip";
+
 		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";
@@ -637,6 +744,7 @@ sub action {
 	}
 
 	if ($is_checkout) {
+		$checkout_dir=$dir;
 		if (! $force_checkout) {
 			if (-d $dir) {
 				print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
@@ -689,22 +797,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";
@@ -740,7 +848,13 @@ sub action {
 			my $ret=hook("post_$action", $topdir, $subdir);
 			return $ret if $ret != OK;
 			
-			if (($is_checkout || $is_update)) {
+			if ($is_checkout || $is_update) {
+				if ($is_checkout && ! $no_chdir) {
+					if (! chdir($checkout_dir)) {
+				                print STDERR "mr $action: failed to chdir to $checkout_dir: $!\n";
+						return FAILED;
+					}
+				}
 				my $ret=hook("fixups", $topdir, $subdir);
 				return $ret if $ret != OK;
 			}
@@ -755,22 +869,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";
@@ -1008,14 +1120,14 @@ sub is_trusted_config {
 	my $config=shift; # must be abs_pathed already
 
 	# We always trust ~/.mrconfig.
-	return 1 if $config eq abs_path("$ENV{HOME}/.mrconfig");
+	return 1 if $config eq abs_path($HOME_MR_CONFIG);
 
 	return 1 if $trust_all;
 
 	my $trustfile=$ENV{HOME}."/.mrtrust";
 
 	if (! %trusted) {
-		$trusted{"$ENV{HOME}/.mrconfig"}=1;
+		$trusted{$HOME_MR_CONFIG}=1;
 		if (open (TRUST, "<", $trustfile)) {
 			while (<TRUST>) {
 				chomp;
@@ -1107,20 +1219,6 @@ sub is_trusted_checkout {
 	return 0;
 }
 
-sub trusterror {
-	my ($err, $file, $line, $url)=@_;
-	
-	if (defined $url) {
-		die "$err in untrusted $url line $line\n".
-			"(To trust this url, --trust-all can be used; but please use caution;\n".
-			"this can allow arbitrary code execution!)\n";
-	}
-	else {
-		die "$err in untrusted $file line $line\n".
-			"(To trust this file, list it in ~/.mrtrust.)\n";
-	}
-}
-
 my %loaded;
 sub loadconfig {
 	my $f=shift;
@@ -1185,41 +1283,54 @@ sub loadconfig {
 
 	# Keep track of the current line in the config file;
 	# when a file is included track the current line from the include.
-	my $line=0;
+	my $lineno=0;
 	my $included=undef;
-	my $includeline=0;
+
+	my $line;
 	my $nextline = sub {
 		if ($included) {
-			$includeline++;
 			$included--;
 		}
 		else {
 			$included=undef;
-			$includeline=0;
-			$line++;
+			$lineno++;
 		}
-		my $l=shift @lines;
-		chomp $l;
-		return $l
+		$line=shift @lines;
+		chomp $line;
+		return $line;
 	};
 	my $lineerror = sub {
 		my $msg=shift;
 		if (defined $included) {
-			die "mr: $f line $line include line $includeline: $msg\n";
+			die "mr: $msg at $f line $lineno, included line: $line\n";
+		}
+		else {
+			die "mr: $msg at $f line $lineno\n";
+		}
+	};
+	my $trusterror = sub {
+		my $msg=shift;
+	
+		if (defined $bootstrap_url) {
+			die "mr: $msg in untrusted $bootstrap_url line $lineno\n".
+				"(To trust this url, --trust-all can be used; but please use caution;\n".
+				"this can allow arbitrary code execution!)\n";
 		}
 		else {
-			die "mr: $f line $line: $msg\n";
+			die "mr: $msg in untrusted $f line $lineno\n".
+				"(To trust this file, list it in ~/.mrtrust.)\n";
 		}
 	};
 
 	while (@lines) {
 		$_=$nextline->();
 
+		next if /^\s*\#/ || /^\s*$/;
+
 		if (! $trusted && /[[:cntrl:]]/) {
-			trusterror("mr: illegal control character", $f, $line, $bootstrap_url);
+			$trusterror->("illegal control character");
 		}
 
-		next if /^\s*\#/ || /^\s*$/;
 		if (/^\[([^\]]*)\]\s*$/) {
 			$section=$1;
 
@@ -1227,7 +1338,7 @@ sub loadconfig {
 				if (! is_trusted_repo($section) ||
 				    $section eq 'ALIAS' ||
 				    $section eq 'DEFAULT') {
-					trusterror("mr: illegal section \"[$section]\"", $f, $line, $bootstrap_url)
+					$trusterror->("illegal section \"[$section]\"");
 				}
 			}
 			$section=expandenv($section) if $trusted;
@@ -1254,7 +1365,7 @@ sub loadconfig {
 				# settings in specific known-safe formats.
 				if ($parameter eq 'checkout') {
 					if (! is_trusted_checkout($value)) {
-						trusterror("mr: illegal checkout command \"$value\"", $f, $line, $bootstrap_url);
+						$trusterror->("illegal checkout command \"$value\"");
 					}
 				}
 				elsif ($parameter eq 'order') {
@@ -1266,7 +1377,7 @@ sub loadconfig {
 					# safe.
 				}
 				else {
-					trusterror("mr: illegal setting \"$parameter=$value\"", $f, $line, $bootstrap_url);
+					$trusterror->("illegal setting \"$parameter=$value\"");
 				}
 			}
 
@@ -1287,8 +1398,8 @@ sub loadconfig {
 			if ($section eq 'ALIAS') {
 				$alias{$parameter}=$value;
 			}
-			elsif ($parameter eq 'lib') {
-				$config{$dir}{$section}{lib}.=$value."\n";
+			elsif ($parameter eq 'lib' or $parameter =~ s/_append$//) {
+				$config{$dir}{$section}{$parameter}.="\n".$value."\n";
 			}
 			else {
 				$config{$dir}{$section}{$parameter}=$value;
@@ -1569,11 +1680,18 @@ sub bootstrap {
 	eval q{use File::Temp};
 	die $@ if $@;
 	my $tmpconfig=File::Temp->new();
-	my @curlargs = ("curl", "-A", "mr", "-L", "-s", $url, "-o", $tmpconfig);
-	push(@curlargs, "-k") if $insecure;
-	my $curlstatus = system(@curlargs);
-	die "mr bootstrap: invalid SSL certificate for $url (consider -k)\n" if $curlstatus >> 8 == 60;
-	die "mr bootstrap: download of $url failed\n" if $curlstatus != 0;
+	my @downloader;
+	if ($url =~ m!^ssh://(.*)!) {
+		@downloader = ("scp", $1, $tmpconfig);
+	}
+	else {
+		@downloader = ("curl", "-A", "mr", "-L", "-s", $url, "-o", $tmpconfig);
+		push(@downloader, "-k") if $insecure;
+	}
+	my $status = system(@downloader);
+	die "mr bootstrap: invalid SSL certificate for $url (consider -k)\n"
+		if $downloader[0] eq 'curl' && $status >> 8 == 60;
+	die "mr bootstrap: download of $url failed\n" if $status != 0;
 
 	if (! -e $dir) {
 		system("mkdir", "-p", $dir);
@@ -1639,7 +1757,7 @@ sub find_mrconfig {
 		}
 		$dir=~s/\/[^\/]*$//;
 	}
-	return "$ENV{HOME}/.mrconfig";
+	return $HOME_MR_CONFIG;
 }
 
 sub getopts {
@@ -1649,6 +1767,7 @@ sub getopts {
 		"d|directory=s" => sub { $directory=abs_path($_[1]) },
 		"c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
 		"p|path" => sub { }, # now default, ignore
+		"f|force" => \$force,
 		"v|verbose" => \$verbose,
 		"q|quiet" => \$quiet,
 		"s|stats" => \$stats,
@@ -1708,7 +1827,7 @@ sub main {
 	init();
 
 	startingconfig();
-	loadconfig("$ENV{HOME}/.mrconfig");
+	loadconfig($HOME_MR_CONFIG);
 	loadconfig($ENV{MR_CONFIG});
 	#use Data::Dumper; print Dumper(\%config);
 	
@@ -1765,24 +1884,29 @@ lib =
 		LANG=C bzr info | egrep -q '^Checkout'
 	}
 	lazy() {
-		if [ "$MR_ACTION" = checkout ] || [ -d "$MR_REPO" ]; then
+		if [ -d "$MR_REPO" ]; then
 			return 1
 		else
 			return 0
 		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
-fossil_test = test -f "$MR_REPO"/_FOSSIL_
-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
+svn_test = perl: -d "$ENV{MR_REPO}/.svn"
+git_test = perl: -e "$ENV{MR_REPO}/.git"
+bzr_test = perl: -d "$ENV{MR_REPO}/.bzr"
+cvs_test = perl: -d "$ENV{MR_REPO}/CVS"
+hg_test  = perl: -d "$ENV{MR_REPO}/.hg"
+darcs_test = perl: -d "$ENV{MR_REPO}/_darcs"
+fossil_test = perl: -f "$ENV{MR_REPO}/_FOSSIL_"
+git_bare_test = perl: 
+	-d "$ENV{MR_REPO}/refs/heads" && -d "$ENV{MR_REPO}/refs/tags" &&
+	-d "$ENV{MR_REPO}/objects" && -f "$ENV{MR_REPO}/config" &&
+	`GIT_CONFIG="$ENV{MR_REPO}"/config git config --get core.bare` =~ /true/
+vcsh_test = perl:
+	-d "$ENV{MR_REPO}/refs/heads" && -d "$ENV{MR_REPO}/refs/tags" &&
+	-d "$ENV{MR_REPO}/objects" && -f "$ENV{MR_REPO}/config" &&
+	`GIT_CONFIG="$ENV{MR_REPO}"/config git config --get vcsh.vcsh` =~ /true/
+veracity_test  = perl: -d "$ENV{MR_REPO}/.sgdrawer"
 
 svn_update = svn update "$@"
 git_update = git pull "$@"
@@ -1792,18 +1916,27 @@ bzr_update =
 	else
 		bzr merge --pull "$@"
 	fi
-cvs_update = cvs update "$@"
-hg_update  = hg pull "$@" && hg update "$@"
+cvs_update = cvs -q update "$@"
+hg_update  = hg pull "$@"; hg update "$@"
 darcs_update = darcs pull -a "$@"
 fossil_update = fossil pull "$@"
+vcsh_update = vcsh run "$MR_REPO" git pull "$@"
+veracity_update = vv pull "$@" && vv update "$@"
+
+git_fetch = git fetch --all --prune --tags
+git_svn_fetch = git svn fetch
+darcs_fetch = darcs fetch
+hg_fetch = hg pull
 
 svn_status = svn status "$@"
-git_status = git status -s "$@" || true
-bzr_status = bzr status --short "$@"
-cvs_status = cvs status "$@"
-hg_status  = hg status "$@"
+git_status = git status -s "$@" || true; git --no-pager log --branches --not --remotes --simplify-by-decoration --decorate --oneline || true
+bzr_status = bzr status --short "$@"; bzr missing
+cvs_status = cvs -q status | grep -E '^(File:.*Status:|\?)' | grep -v 'Status: Up-to-date'
+hg_status  = hg status "$@"; hg summary --quiet | grep -v 'parent: 0:'
 darcs_status = darcs whatsnew -ls "$@" || true
 fossil_status = fossil changes "$@"
+vcsh_status = vcsh run "$MR_REPO" git -c status.relativePaths=false status -s "$@" || true
+veracity_status = vv status "$@"
 
 svn_commit = svn commit "$@"
 git_commit = git commit -a "$@" && git push --all
@@ -1814,9 +1947,11 @@ bzr_commit =
 		bzr commit "$@" && bzr push
 	fi
 cvs_commit = cvs commit "$@"
-hg_commit  = hg commit -m "$@" && hg push
-darcs_commit = darcs record -a -m "$@" && darcs push -a
+hg_commit  = hg commit "$@" && hg push
+darcs_commit = darcs record -a "$@" && darcs push -a
 fossil_commit = fossil commit "$@"
+vcsh_commit = vcsh run "$MR_REPO" git commit -a "$@" && vcsh run "$MR_REPO" git push --all
+veracity_commit = vv commit "$@" && vv push
 
 git_record = git commit -a "$@"
 bzr_record =
@@ -1825,9 +1960,11 @@ bzr_record =
 	else
 		bzr commit "$@"
 	fi
-hg_record  = hg commit -m "$@"
-darcs_record = darcs record -a -m "$@"
+hg_record  = hg commit "$@"
+darcs_record = darcs record -a "$@"
 fossil_record = fossil commit "$@"
+vcsh_record = vcsh run "$MR_REPO" git commit -a "$@"
+veracity_record = vv commit "$@"
 
 svn_push = :
 git_push = git push "$@"
@@ -1836,14 +1973,18 @@ cvs_push = :
 hg_push = hg push "$@"
 darcs_push = darcs push -a "$@"
 fossil_push = fossil push "$@"
+vcsh_push = vcsh run "$MR_REPO" git push "$@"
+veracity_push = vv push "$@"
 
 svn_diff = svn diff "$@"
 git_diff = git diff "$@"
 bzr_diff = bzr diff "$@"
-cvs_diff = cvs diff "$@"
+cvs_diff = cvs -q diff "$@"
 hg_diff  = hg diff "$@"
 darcs_diff = darcs diff -u "$@"
 fossil_diff = fossil diff "$@"
+vcsh_diff = vcsh run "$MR_REPO" git diff "$@"
+veracity_diff = vv diff "$@"
 
 svn_log = svn log "$@"
 git_log = git log "$@"
@@ -1853,6 +1994,15 @@ hg_log  = hg log "$@"
 darcs_log = darcs changes "$@"
 git_bare_log = git log "$@"
 fossil_log = fossil timeline "$@"
+vcsh_log = vcsh run "$MR_REPO" git log "$@"
+veracity_log = vv log "$@"
+
+hg_grep = hg grep "$@"
+cvs_grep = ack-grep "$@"
+svn_grep = ack-grep "$@"
+git_svn_grep = git grep "$@"
+git_grep = git grep "$@"
+bzr_grep = ack-grep "$@"
 
 run = "$@"
 
@@ -1871,7 +2021,7 @@ git_register =
 	echo "Registering git url: $url in $MR_CONFIG"
 	mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone '$url' '$MR_REPO'"
 bzr_register =
-	url="`LC_ALL=C bzr info . | egrep -i 'checkout of branch|parent branch' | awk '{print $NF}'`"
+	url="`LC_ALL=C bzr info . | egrep -i 'checkout of branch|parent branch' | awk '{print $NF}' | head -n 1`"
 	if [ -z "$url" ]; then
 		error "cannot determine bzr url"
 	fi
@@ -1900,11 +2050,23 @@ git_bare_register =
 	fi
 	echo "Registering git url: $url in $MR_CONFIG"
 	mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone --bare '$url' '$MR_REPO'"
+vcsh_register =
+	url="`LC_ALL=C vcsh run "$MR_REPO" 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="vcsh clone '$url' '$MR_REPO'"
 fossil_register =
 	url=`fossil remote-url`
 	repo=`fossil info | grep repository | sed -e 's/repository:*.//g' -e 's/ //g'`
 	echo "Registering fossil repository $url in $MR_CONFIG"
 	mr -c "$MR_CONFIG" config "`pwd`" checkout="mkdir -p '$MR_REPO' && cd '$MR_REPO' && fossil open '$repo'"
+veracity_register =
+	url=`vv config | grep sync_targets | sed -e 's/sync_targets:*.//g' -e 's/ //g'`
+	repo=`vv repo info | grep repository | sed -e 's/Current repository:*.//g' -e 's/ //g'`
+	echo "Registering veracity repository $url in $MR_CONFIG"
+	mr -c "$MR_CONFIG" config "`pwd`" checkout="mkdir -p '$MR_REPO' && cd '$MR_REPO' && vv checkout '$repo'"
 
 svn_trusted_checkout = svn co $url $repo
 svn_alt_trusted_checkout = svn checkout $url $repo
@@ -1914,7 +2076,9 @@ bzr_trusted_checkout = bzr checkout|clone|branch|get $url $repo
 hg_trusted_checkout = hg clone $url $repo
 darcs_trusted_checkout = darcs get $url $repo
 git_bare_trusted_checkout = git clone --bare $url $repo
+vcsh_trusted_checkout = vcsh run "$MR_REPO" git clone $url $repo
 # fossil: messy to do
+veracity_trusted_checkout = vv clone $url $repo
 
 
 help =
@@ -1926,7 +2090,7 @@ help =
 		SHOWMANFILE="man"
 		;;
 		*)
-		SHOWMANFILE="man -l"
+		SHOWMANFILE="man"
 		;;
 	esac
 	if [ ! -e "$MR_PATH" ]; then
@@ -1970,3 +2134,7 @@ T = echo "I pity the fool."
 right = echo "Not found."
 
 # vim:sw=8:sts=0:ts=8:noet
+# Local variables:
+# indent-tabs-mode: t
+# cperl-indent-level: 8
+# End: