X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/8b4f49ea7f031b0374df3fc2dcb4700be8ab13ce..d80a044816be0037e3243bf4e4949461d73b1097:/mr

diff --git a/mr b/mr
index 1923506..bdc9ff6 100755
--- a/mr
+++ b/mr
@@ -16,6 +16,8 @@ B<mr> [options] commit [-m "message"]
 
 B<mr> [options] record [-m "message"]
 
+B<mr> [options] fetch
+
 B<mr> [options] push
 
 B<mr> [options] diff
@@ -41,7 +43,7 @@ B<mr> [options] remember 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
 repository. It supports any combination of subversion, git, cvs, mercurial,
-bzr, darcs and fossil repositories, and support for other version
+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
@@ -91,6 +93,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
@@ -116,8 +124,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 +222,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 +384,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 +415,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/*
@@ -460,13 +477,6 @@ can be constructed accumulatively.
 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
@@ -530,6 +540,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;
@@ -538,6 +549,7 @@ my $jobs=1;
 my $trust_all=0;
 my $directory=getcwd();
 
+my $HOME_MR_CONFIG = "$ENV{HOME}/.mrconfig";
 $ENV{MR_CONFIG}=find_mrconfig();
 
 # globals :-(
@@ -585,17 +597,22 @@ sub runsh {
 	$runner->($shellcode);
 }
 
-sub runshpipe {
-	runsh @_, sub {
-		my $sh=shift;
-		my $ret=`$sh`;
-		chomp $ret;
-		return $ret;
-	};
+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;
-my %vcs_dir_test;
 sub vcs_test {
 	my ($action, $dir, $topdir, $subdir) = @_;
 
@@ -604,53 +621,46 @@ sub vcs_test {
 	}
 
 	my $test="";
-	my $dir_test="";
+	my %perltest;
 	foreach my $vcs_test (
 			sort {
 				length $a <=> length $b 
 				          ||
 				       $a cmp $b
 			} grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) {
-		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/;
+		my $p=perl($vcs_test, $config{$topdir}{$subdir}{$vcs_test});
+		if (defined $p) {
+			$perltest{$vcs}=$p;
 		}
-		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";
-	}
-
-	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;
-			}
+		else {
+			$test="my_$vcs_test() {\n$config{$topdir}{$subdir}{$vcs_test}\n}\n".$test;
+			$test.="if my_$vcs_test; then echo $vcs; fi\n";
 		}
 	}
 
-	foreach my $vcs (keys %vcs_dir_test) {
-		if (-d "$ENV{MR_REPO}/$vcs_dir_test{$vcs}") {
-			return $vcs{$dir}=$vcs;
+	my @vcs;
+	foreach my $vcs (keys %perltest) {
+		if ($perltest{$vcs}->()) {
+			push @vcs, $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";
+	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];
 	}
 }
 	
@@ -684,6 +694,7 @@ 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 $is_checkout=($action eq 'checkout');
@@ -693,6 +704,8 @@ 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) {
@@ -723,6 +736,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;
@@ -826,7 +840,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;
 			}
@@ -1092,14 +1112,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;
@@ -1282,15 +1302,14 @@ sub loadconfig {
 	};
 	my $trusterror = sub {
 		my $msg=shift;
-		my ($err, $file, $lineno, $url)=@_;
 	
 		if (defined $bootstrap_url) {
-			die "mr: $err in untrusted $bootstrap_url line $lineno\n".
+			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: $err in untrusted $file line $lineno\n".
+			die "mr: $msg in untrusted $f line $lineno\n".
 				"(To trust this file, list it in ~/.mrtrust.)\n";
 		}
 	};
@@ -1652,11 +1671,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);
@@ -1722,7 +1748,7 @@ sub find_mrconfig {
 		}
 		$dir=~s/\/[^\/]*$//;
 	}
-	return "$ENV{HOME}/.mrconfig";
+	return $HOME_MR_CONFIG;
 }
 
 sub getopts {
@@ -1732,6 +1758,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,
@@ -1791,7 +1818,7 @@ sub main {
 	init();
 
 	startingconfig();
-	loadconfig("$ENV{HOME}/.mrconfig");
+	loadconfig($HOME_MR_CONFIG);
 	loadconfig($ENV{MR_CONFIG});
 	#use Data::Dumper; print Dumper(\%config);
 	
@@ -1848,24 +1875,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_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 &&
-	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 "$@"
@@ -1875,10 +1907,17 @@ 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
@@ -1887,6 +1926,8 @@ cvs_status = cvs status "$@"
 hg_status  = hg status "$@"
 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
@@ -1900,6 +1941,8 @@ cvs_commit = cvs commit "$@"
 hg_commit  = hg commit -m "$@" && hg push
 darcs_commit = darcs record -a -m "$@" && 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 -m "@" && vv push
 
 git_record = git commit -a "$@"
 bzr_record =
@@ -1911,6 +1954,8 @@ bzr_record =
 hg_record  = hg commit -m "$@"
 darcs_record = darcs record -a -m "$@"
 fossil_record = fossil commit "$@"
+vcsh_record = vcsh run "$MR_REPO" git commit -a "$@"
+veracity_record = vv commit -m "@"
 
 svn_push = :
 git_push = git push "$@"
@@ -1919,14 +1964,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 "$@"
@@ -1936,6 +1985,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 = "$@"
 
@@ -1954,7 +2012,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
@@ -1983,11 +2041,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
@@ -1997,7 +2067,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 =