X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/cbe6d498a888331bb94c052abc0d240a1f4b7811..ae4ab73b5d793b03f91fdcef0fff7405f9f9d75b:/mr

diff --git a/mr b/mr
index 2c7c4da..b37d9a5 100755
--- a/mr
+++ b/mr
@@ -41,7 +41,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 revision
+bzr, darcs and fossil 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
@@ -55,7 +55,7 @@ in turn chain load .mrconfig files from repositories. It also automatically
 looks for a .mrconfig file in the current directory, or in one of its
 parent directories.
 
-These predefined commands should be fairly familiar to users of any revision
+These predefined commands should be fairly familiar to users of any version
 control system:
 
 =over 4
@@ -87,14 +87,14 @@ 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.
+remote repository. Only supported for distributed version control systems.
 
 The optional -m parameter allows specifying a commit message.
 
 =item push
 
 Pushes committed local changes to the remote repository. A no-op for
-centralized revision control systems.
+centralized version control systems.
 
 =item diff
 
@@ -189,8 +189,8 @@ Actions can be abbreviated to any unambiguous substring, so
 update"
 
 Additional parameters can be passed to most commands, and are passed on
-unchanged to the underlying revision control system. This is mostly useful
-if the repositories mr will act on all use the same revision control
+unchanged to the underlying version control system. This is mostly useful
+if the repositories mr will act on all use the same version control
 system.
 
 =head1 OPTIONS
@@ -222,7 +222,9 @@ Be verbose.
 
 =item --quiet
 
-Be quiet.
+Be quiet. This suppresses mr's usual output, as well as any output from
+commands that are run (including stderr output). If a command fails,
+the output will be shown.
 
 =item -k
 
@@ -288,7 +290,7 @@ This obsolete flag is ignored.
 Here is an example .mrconfig file:
 
   [src]
-  checkout = svn co svn://svn.example.com/src/trunk src
+  checkout = svn checkout svn://svn.example.com/src/trunk src
   chain = true
 
   [src/linux-2.6]
@@ -315,14 +317,18 @@ will be passed through the shell for expansion. For example,
 
 Within a section, each parameter defines a shell command to run to handle a
 given action. mr contains default handlers for "update", "status",
-"commit", and other standard actions. Normally you only need to specify what
-to do for "checkout".
+"commit", and other standard actions.
+
+Normally you only need to specify what to do for "checkout". Here you
+specify the command to run in order to create a checkout of the repository.
+The command will be run in the parent directory, and must create the
+repository's directory. So use "git clone", "svn checkout", "bzr branch"
+or "bzr checkout" (for a bound branch), etc.
 
 Note that these shell commands are run in a "set -e" shell
 environment, where any additional parameters you pass are available in
-"$@". 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.
+"$@". All commands other than "checkout" 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. (For the "register" action, "MR_REPO" is instead set to the 
@@ -333,6 +339,9 @@ The "MR_CONFIG" environment variable is set to the .mrconfig file
 that defines the repo being acted on, or, if the repo is not yet in a config
 file, the .mrconfig file that should be modified to register the repo.
 
+The "MR_ACTION" environment variable is set to the command being run
+(update, checkout, etc).
+
 A few parameters have special meanings:
 
 =over 4
@@ -348,8 +357,21 @@ 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.
 
+  [mystuff]
+  checkout = ...
   skip = test `whoami` != joey
+
+  [linux]
+  checkout = ...
   skip = [ "$1" = update ] && ! hours_since "$1" 12
+ 
+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").
+
+  [foo]
+  checkout = ...
+  skip = lazy
 
 =item order
 
@@ -375,6 +397,22 @@ part of the including file.
 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,
+git-subtree). To include them all, you could use:
+
+  include = cat /usr/share/mr/*
+
+See the individual files for details.
+
+=item deleted
+
+If the "deleted" parameter is set and its command returns true, then
+B<mr> will treat the repository as deleted. It won't ever actually delete
+the repository, but it will warn if it sees the repository's directory.
+This is useful when one mrconfig file is shared amoung multiple machines,
+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
@@ -399,15 +437,15 @@ run before committing; "post_update" is run after updating.
 
 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 "rcs_action" (substituting in the name of the
-revision control system and the action). The name of the revision control
-system is itself determined by running each defined "rcs_test" action,
+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.
 
 Internally, mr has settings for "git_update", "svn_update", etc. To change
-the action that is performed for a given revision control system, you can
-override these rcs specific actions. To add a new revision control system,
-you can just add rcs specific actions for it.
+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.
 
 =head1 UNTRUSTED MRCONFIG FILES
 
@@ -489,41 +527,41 @@ my (@ok, @failed, @skipped);
 
 main();
 
-my %rcs;
-sub rcs_test {
+my %vcs;
+sub vcs_test {
 	my ($action, $dir, $topdir, $subdir) = @_;
 
-	if (exists $rcs{$dir}) {
-		return $rcs{$dir};
+	if (exists $vcs{$dir}) {
+		return $vcs{$dir};
 	}
 
 	my $test="set -e\n";
-	foreach my $rcs_test (
+	foreach my $vcs_test (
 			sort {
 				length $a <=> length $b 
 				          ||
 				       $a cmp $b
 			} grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) {
-		my ($rcs)=$rcs_test=~/(.*)_test/;
-		$test="my_$rcs_test() {\n$config{$topdir}{$subdir}{$rcs_test}\n}\n".$test;
-		$test.="if my_$rcs_test; then echo $rcs; fi\n";
+		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";
 	}
 	$test=$config{$topdir}{$subdir}{lib}."\n".$test
 		if exists $config{$topdir}{$subdir}{lib};
 	
-	print "mr $action: running rcs test >>$test<<\n" if $verbose;
-	my $rcs=`$test`;
-	chomp $rcs;
-	if ($rcs=~/\n/s) {
-		$rcs=~s/\n/, /g;
-		print STDERR "mr $action: found multiple possible repository types ($rcs) for ".fulldir($topdir, $subdir)."\n";
+	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";
 		return undef;
 	}
-	if (! length $rcs) {
-		return $rcs{$dir}=undef;
+	if (! length $vcs) {
+		return $vcs{$dir}=undef;
 	}
 	else {
-		return $rcs{$dir}=$rcs;
+		return $vcs{$dir}=$vcs;
 	}
 }
 	
@@ -538,11 +576,11 @@ sub findcommand {
 		return undef;
 	}
 
-	my $rcs=rcs_test(@_);
+	my $vcs=vcs_test(@_);
 
-	if (defined $rcs && 
-	    exists $config{$topdir}{$subdir}{$rcs."_".$action}) {
-		return $config{$topdir}{$subdir}{$rcs."_".$action};
+	if (defined $vcs && 
+	    exists $config{$topdir}{$subdir}{$vcs."_".$action}) {
+		return $config{$topdir}{$subdir}{$vcs."_".$action};
 	}
 	else {
 		return undef;
@@ -564,7 +602,39 @@ sub action {
 	my $is_checkout=($action eq 'checkout');
 	my $is_update=($action =~ /update/);
 
-	$ENV{MR_REPO}=$dir;
+	($ENV{MR_REPO}=$dir) =~ s!/$!!;
+	$ENV{MR_ACTION}=$action;
+	
+	foreach my $testname ("skip", "deleted") {
+		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);
+			if ($ret != 0) {
+				if (($? & 127) == 2) {
+					print STDERR "mr $action: interrupted\n";
+					return ABORT;
+				}
+				elsif ($? & 127) {
+					print STDERR "mr $action: $testname test received signal ".($? & 127)."\n";
+					return ABORT;
+				}
+			}
+			if ($ret >> 8 == 0) {
+				if ($testname eq "deleted") {
+					if (-d $dir) {
+						print STDERR "mr error: $dir should be deleted yet still exists\n";
+						return FAILED;
+					}
+				}
+				print "mr $action: skip $dir skipped\n" if $verbose;
+				return SKIPPED;
+			}
+		}
+	}
 
 	if ($is_checkout) {
 		if (! $force_checkout) {
@@ -582,30 +652,8 @@ sub action {
 		}
 	}
 
-	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.
-			"my_action(){ $skiptest\n }; my_action '$action'";
-		print "mr $action: running skip test >>$test<<\n" if $verbose;
-		my $ret=system($test);
-		if ($ret != 0) {
-			if (($? & 127) == 2) {
-				print STDERR "mr $action: interrupted\n";
-				return ABORT;
-			}
-			elsif ($? & 127) {
-				print STDERR "mr $action: skip test received signal ".($? & 127)."\n";
-				return ABORT;
-			}
-		}
-		if ($ret >> 8 == 0) {
-			print "mr $action: $dir skipped per config file\n" if $verbose;
-			return SKIPPED;
-		}
-	}
-
 	if ($is_checkout && ! -d $dir) {
 		print "mr $action: creating parent directory $dir\n" if $verbose;
 		system("mkdir", "-p", $dir);
@@ -616,25 +664,27 @@ sub action {
 		return FAILED;
 	}
 	elsif (! defined $command) {
-		my $rcs=rcs_test(@_);
-		if (! defined $rcs) {
+		my $vcs=vcs_test(@_);
+		if (! defined $vcs) {
 			print STDERR "mr $action: unknown repository type and no defined $action command for $fulldir\n";
 			return FAILED;
 		}
 		else {
-			print STDERR "mr $action: no defined action for $rcs repository $fulldir, skipping\n";
+			print STDERR "mr $action: no defined action for $vcs repository $fulldir, skipping\n";
 			return SKIPPED;
 		}
 	}
 	else {
+		my $actionmsg;
 		if (! $no_chdir) {
-			print "mr $action: $fulldir\n" unless $quiet;
+			$actionmsg="mr $action: $fulldir";
 		}
 		else {
 			my $s=$directory;
 			$s=~s/^\Q$fulldir\E\/?//;
-			print "mr $action: $fulldir (in subdir $s)\n" unless $quiet;
+			$actionmsg="mr $action: $fulldir (in subdir $s)";
 		}
+		print "$actionmsg\n" unless $quiet;
 
 		my $hookret=hook("pre_$action", $topdir, $subdir);
 		return $hookret if $hookret != OK;
@@ -643,7 +693,18 @@ sub action {
 			"my_action(){ $command\n }; my_action ".
 			join(" ", map { s/\\/\\\\/g; s/"/\"/g; '"'.$_.'"' } @ARGV);
 		print "mr $action: running >>$command<<\n" if $verbose;
-		my $ret=system($command);
+		my $ret;
+		if ($quiet) {
+			my $output = qx/$command 2>&1/;
+			$ret = $?;
+			if ($ret != 0) {
+				print "$actionmsg\n";
+				print STDERR $output;
+			}
+		}
+		else {
+			$ret=system($command);
+		}
 		if ($ret != 0) {
 			if (($? & 127) == 2) {
 				print STDERR "mr $action: interrupted\n";
@@ -699,7 +760,17 @@ sub hook {
 	my $shell="set -e;".$lib.
 		"my_hook(){ $command\n }; my_hook";
 	print "mr $hook: running >>$shell<<\n" if $verbose;
-	my $ret=system($shell);
+	my $ret;
+	if ($quiet) {
+		my $output = qx/$shell 2>&1/;
+		$ret = $?;
+		if ($ret != 0) {
+			print STDERR $output;
+		}
+	}
+	else {
+		$ret=system($shell);
+	}
 	if ($ret != 0) {
 		if (($? & 127) == 2) {
 			print STDERR "mr $hook: interrupted\n";
@@ -709,6 +780,9 @@ sub hook {
 			print STDERR "mr $hook: received signal ".($? & 127)."\n";
 			return ABORT;
 		}
+		else {
+			return FAILED;
+		}
 	}
 
 	return OK;
@@ -872,7 +946,10 @@ sub repodir {
 	return $ret;
 }
 
-# figure out which repos to act on
+# Figure out which repos to act on.  Returns a list of array refs
+# in the format:
+#
+#   [ "$full_repo_path/", "$mr_config_path/", $section_header ]
 sub selectrepos {
 	my @repos;
 	foreach my $repo (repolist()) {
@@ -1017,7 +1094,7 @@ sub is_trusted_checkout {
 					is_trusted_repo($words[$c])
 				);
 			}
-			elsif (defined $words[$c] && $twords[$c] eq $words[$c]) {
+			elsif (defined $words[$c] && $words[$c]=~/^($twords[$c])$/) {
 				$match=1;
 			}
 			else {
@@ -1031,14 +1108,24 @@ sub is_trusted_checkout {
 }
 
 sub trusterror {
-	die shift()."\n".
-		"(To trust this file, list it in ~/.mrtrust.)\n";
+	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;
 	my $dir=shift;
+	my $bootstrap_url=shift;
 
 	my @toload;
 
@@ -1050,10 +1137,6 @@ sub loadconfig {
 		$trusted=1;
 	}
 	else {
-		if (! -e $f) {
-			return;
-		}
-
 		my $absf=abs_path($f);
 		if ($loaded{$absf}) {
 			return;
@@ -1088,6 +1171,10 @@ sub loadconfig {
 			}
 		}
 		
+		if (! -e $f) {
+			return;
+		}
+
 		print "mr: loading config $f\n" if $verbose;
 		open($in, "<", $f) || die "mr: open $f: $!\n";
 	}
@@ -1095,11 +1182,43 @@ sub loadconfig {
 	close $in unless ref $f eq 'GLOB';
 
 	my $section;
+
+	# 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 $included=undef;
+	my $includeline=0;
+	my $nextline = sub {
+		if ($included) {
+			$includeline++;
+			$included--;
+		}
+		else {
+			$included=undef;
+			$includeline=0;
+			$line++;
+		}
+		my $l=shift @lines;
+		chomp $l;
+		return $l
+	};
+	my $lineerror = sub {
+		my $msg=shift;
+		if (defined $included) {
+			die "mr: $f line $line include line $includeline: $msg\n";
+		}
+		else {
+			die "mr: $f line $line: $msg\n";
+		}
+	};
+
 	while (@lines) {
-		$_=shift @lines;
-		$line++;
-		chomp;
+		$_=$nextline->();
+
+		if (! $trusted && /[[:cntrl:]]/) {
+			trusterror("mr: illegal control character", $f, $line, $bootstrap_url);
+		}
+
 		next if /^\s*\#/ || /^\s*$/;
 		if (/^\[([^\]]*)\]\s*$/) {
 			$section=$1;
@@ -1108,7 +1227,7 @@ sub loadconfig {
 				if (! is_trusted_repo($section) ||
 				    $section eq 'ALIAS' ||
 				    $section eq 'DEFAULT') {
-					trusterror "mr: illegal section \"[$section]\" in untrusted $f line $line";
+					trusterror("mr: illegal section \"[$section]\"", $f, $line, $bootstrap_url)
 				}
 			}
 			$section=expandenv($section) if $trusted;
@@ -1125,34 +1244,45 @@ sub loadconfig {
 
 			# continued value
 			while (@lines && $lines[0]=~/^\s(.+)/) {
-				shift(@lines);
-				$line++;
 				$value.="\n$1";
 				chomp $value;
+				$nextline->();
 			}
 
 			if (! $trusted) {
-				# Untrusted files can only contain checkout
-				# parameters.
-				if ($parameter ne 'checkout') {
-					trusterror "mr: illegal setting \"$parameter=$value\" in untrusted $f line $line";
+				# Untrusted files can only contain a few
+				# 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);
+					}
+				}
+				elsif ($parameter eq 'order') {
+					# not interpreted as a command, so
+					# safe.
 				}
-				if (! is_trusted_checkout($value)) {
-					trusterror "mr: illegal checkout command \"$value\" in untrusted $f line $line";
+				elsif ($value eq 'true' || $value eq 'false') {
+					# skip=true , deleted=true etc are
+					# safe.
+				}
+				else {
+					trusterror("mr: illegal setting \"$parameter=$value\"", $f, $line, $bootstrap_url);
 				}
 			}
 
 			if ($parameter eq "include") {
 				print "mr: including output of \"$value\"\n" if $verbose;
-				unshift @lines, `$value`;
+				my @inc=`$value`;
 				if ($?) {
 					print STDERR "mr: include command exited nonzero ($?)\n";
 				}
+				$included += @inc;
+				unshift @lines, @inc;
 				next;
 			}
 
 			if (! defined $section) {
-				die "$f line $.: parameter ($parameter) not in section\n";
+				$lineerror->("parameter ($parameter) not in section");
 			}
 			if ($section eq 'ALIAS') {
 				$alias{$parameter}=$value;
@@ -1169,31 +1299,36 @@ sub loadconfig {
 					$knownactions{$parameter}=1;
 				}
 				if ($parameter eq 'chain' &&
-				    length $dir && $section ne "DEFAULT" &&
-				    -e $dir.$section."/.mrconfig") {
-					my $ret=system($value);
-					if ($ret != 0) {
-						if (($? & 127) == 2) {
-							print STDERR "mr: chain test interrupted\n";
-							exit 2;
+				    length $dir && $section ne "DEFAULT") {
+					my $chaindir="$section";
+					if ($chaindir !~ m!^/!) {
+						$chaindir=$dir.$chaindir;
+					}
+					if (-e "$chaindir/.mrconfig") {
+						my $ret=system($value);
+						if ($ret != 0) {
+							if (($? & 127) == 2) {
+								print STDERR "mr: chain test interrupted\n";
+								exit 2;
+							}
+							elsif ($? & 127) {
+								print STDERR "mr: chain test received signal ".($? & 127)."\n";
+							}
 						}
-						elsif ($? & 127) {
-							print STDERR "mr: chain test received signal ".($? & 127)."\n";
+						else {
+							push @toload, ["$chaindir/.mrconfig", $chaindir];
 						}
 					}
-					else {
-						push @toload, $dir.$section."/.mrconfig";
-					}
 			        }
 			}
 		}
 		else {
-			die "$f line $line: parse error\n";
+			$lineerror->("parse error");
 		}
 	}
 
-	foreach (@toload) {
-		loadconfig($_);
+	foreach my $c (@toload) {
+		loadconfig(@$c);
 	}
 }
 
@@ -1449,7 +1584,7 @@ sub bootstrap {
 	# would normally be skipped.
 	my $topdir=abs_path(".")."/";
 	my @repo=($topdir, $topdir, ".");
-	loadconfig($tmpconfig, $topdir);
+	loadconfig($tmpconfig, $topdir, $url);
 	record(\@repo, action("checkout", @repo, 1))
 		if exists $config{$topdir}{"."}{"checkout"};
 
@@ -1626,6 +1761,16 @@ lib =
 			return 0
 		fi
 	}
+	is_bzr_checkout() {
+		LANG=C bzr info | egrep -q '^Checkout'
+	}
+	lazy() {
+		if [ "$MR_ACTION" = checkout ] || [ -d "$MR_REPO" ]; then
+			return 1
+		else
+			return 0
+		fi
+	}
 
 svn_test = test -d "$MR_REPO"/.svn
 git_test = test -d "$MR_REPO"/.git
@@ -1641,7 +1786,12 @@ git_bare_test =
 
 svn_update = svn update "$@"
 git_update = git pull "$@"
-bzr_update = bzr merge --pull "$@"
+bzr_update = 
+	if is_bzr_checkout; then
+		bzr update "$@"
+	else
+		bzr merge --pull "$@"
+	fi
 cvs_update = cvs update "$@"
 hg_update  = hg pull "$@" && hg update "$@"
 darcs_update = darcs pull -a "$@"
@@ -1657,14 +1807,24 @@ fossil_status = fossil changes "$@"
 
 svn_commit = svn commit "$@"
 git_commit = git commit -a "$@" && git push --all
-bzr_commit = bzr commit "$@" && bzr push
+bzr_commit = 
+	if is_bzr_checkout; then
+		bzr commit "$@"
+	else
+		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
 fossil_commit = fossil commit "$@"
 
 git_record = git commit -a "$@"
-bzr_record = bzr commit "$@"
+bzr_record =
+	if is_bzr_checkout; then
+		bzr commit --local "$@"
+	else
+		bzr commit "$@"
+	fi
 hg_record  = hg commit -m "$@"
 darcs_record = darcs record -a -m "$@"
 fossil_record = fossil commit "$@"
@@ -1716,7 +1876,7 @@ bzr_register =
 		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'"
+	mr -c "$MR_CONFIG" config "`pwd`" checkout="bzr branch '$url' '$MR_REPO'"
 cvs_register =
 	repo=`cat CVS/Repository`
 	root=`cat CVS/Root`
@@ -1749,7 +1909,7 @@ fossil_register =
 svn_trusted_checkout = svn co $url $repo
 svn_alt_trusted_checkout = svn checkout $url $repo
 git_trusted_checkout = git clone $url $repo
-bzr_trusted_checkout = bzr clone $url $repo
+bzr_trusted_checkout = bzr checkout|clone|branch|get $url $repo
 # cvs: too hard
 hg_trusted_checkout = hg clone $url $repo
 darcs_trusted_checkout = darcs get $url $repo