All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
7 mr - a Multiple Repository management tool
11 B<mr> [options] checkout
13 B<mr> [options] update
15 B<mr> [options] status
17 B<mr> [options] commit [-m "message"]
19 B<mr> [options] record [-m "message"]
25 B<mr> [options] register [repository]
27 B<mr> [options] config section ["parameter=[value]" ...]
29 B<mr> [options] action [params ...]
33 B<mr> is a Multiple Repository management tool. It can checkout, update, or
34 perform other actions on a set of repositories as if they were one combined
35 repository. It supports any combination of subversion, git, cvs, mecurial,
36 bzr and darcs repositories, and support for other revision control systems can
39 B<mr> cds into and operates on all registered repositories at or below your
40 working directory. Or, if you are in a subdirectory of a repository that
41 contains no other registered repositories, it will stay in that directory,
42 and work on only that repository,
44 These predefined commands should be fairly familiar to users of any revision
49 =item checkout (or co)
51 Checks out any repositories that are not already checked out.
55 Updates each repository from its configured remote repository.
57 If a repository isn't checked out yet, it will first check it out.
61 Displays a status report for each repository, showing what
62 uncommitted changes are present in the repository.
66 Commits changes to each repository. (By default, changes are pushed to the
67 remote repository too, when using distributed systems like git. If you
68 don't like this default, you can change it in your .mrconfig, or use record
71 The optional -m parameter allows specifying a commit message.
75 Records changes to the local repository, but does not push them to the
76 remote repository. Only supported for distributed revision control systems.
78 The optional -m parameter allows specifying a commit message.
82 Show a diff of uncommitted changes.
90 These commands are also available:
96 List the repositories that mr will act on.
100 Register an existing repository in a mrconfig file. By default, the
101 repository in the current directory is registered, or you can specify a
102 directory to register.
104 The mrconfig file that is modified is chosen by either the -c option, or by
105 looking for the closest known one at or below the current directory.
109 Adds, modifies, removes, or prints a value from a mrconfig file. The next
110 parameter is the name of the section the value is in. To add or modify
111 values, use one or more instances of "parameter=value". Use "parameter=" to
112 remove a parameter. Use just "parameter" to get the value of a parameter.
114 For example, to add (or edit) a repository in src/foo:
116 mr config src/foo checkout="svn co svn://example.com/foo/trunk foo"
118 To show the command that mr uses to update the repository in src/foo:
120 mr config src/foo update
122 To see the built-in library of shell functions contained in mr:
124 mr config DEFAULT lib
126 The ~/.mrconfig file is used by default. To use a different config file,
135 Actions can be abbreviated to any unambiguous substring, so
136 "mr st" is equivalent to "mr status", and "mr up" is equivalent to "mr
139 Additional parameters can be passed to most commands, and are passed on
140 unchanged to the underlying revision control system. This is mostly useful
141 if the repositories mr will act on all use the same revision control
150 Specifies the topmost directory that B<mr> should work in. The default is
151 the current working directory.
155 Use the specified mrconfig file. The default is B<~/.mrconfig>
167 Expand the statistics line displayed at the end to include information
168 about exactly which repositories failed and were skipped, if any.
172 If no number if specified, just operate on the repository for the current
173 directory, do not recurse into deeper repositories.
175 If a number is specified, will recurse into repositories at most that many
176 subdirectories deep. For example, with -n 2 it would recurse into ./src/foo,
177 but not ./src/packages/bar.
181 Run the specified number of jobs in parallel, or an unlimited number of jobs
182 with no number specified. This can greatly speed up operations such as updates.
183 It is not recommended for interactive operations.
185 Note that running more than 10 jobs at a time is likely to run afoul of
186 ssh connection limits. Running between 3 and 5 jobs at a time will yeild
187 a good speedup in updates without loading the machine too much.
193 B<mr> is configured by .mrconfig files. It starts by reading the .mrconfig
194 file in your home directory, and this can in turn chain load .mrconfig files
197 Here is an example .mrconfig file:
200 checkout = svn co svn://svn.example.com/src/trunk src
204 checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git &&
206 git checkout -b mybranch origin/master
208 The .mrconfig file uses a variant of the INI file format. Lines starting with
209 "#" are comments. Values can be continued to the following line by
210 indenting the line with whitespace.
212 The "DEFAULT" section allows setting default values for the sections that
215 The "ALIAS" section allows adding aliases for actions. Each parameter
216 is an alias, and its value is the action to use.
218 All other sections add repositories. The section header specifies the
219 directory where the repository is located. This is relative to the directory
220 that contains the mrconfig file, but you can also choose to use absolute
223 Within a section, each parameter defines a shell command to run to handle a
224 given action. mr contains default handlers for "update", "status",
225 "commit", and other standard actions. Normally you only need to specify what
226 to do for "checkout".
228 Note that these shell commands are run in a "set -e" shell
229 environment, where any additional parameters you pass are available in
230 "$@". The "checkout" command is run in the parent of the repository
231 directory, since the repository isn't checked out yet. All other commands
232 are run inside the repository, though not necessarily at the top of it.
234 The "MR_REPO" environment variable is set to the path to the top of the
235 repository. (For the "register" action, "MR_REPO" is instead set to the
236 basename of the directory that should be created when checking the
239 The "MR_CONFIG" environment variable is set to the .mrconfig file
240 that defines the repo being acted on, or, if the repo is not yet in a config
241 file, the .mrconfig file that should be modified to register the repo.
243 A few parameters have special meanings:
249 If the "skip" parameter is set and its command returns true, then B<mr>
250 will skip acting on that repository. The command is passed the action
253 Here are two examples. The first skips the repo unless
254 mr is run by joey. The second uses the hours_since function
255 (included in mr's built-in library) to skip updating the repo unless it's
256 been at least 12 hours since the last update.
258 skip = test `whoami` != joey
259 skip = [ "$1" = update ] && ! hours_since "$1" 12
263 The "order" parameter can be used to override the default ordering of
264 repositories. The default order value is 10. Use smaller values to make
265 repositories be processed earlier, and larger values to make repositories
268 Note that if a repository is located in a subdirectory of another
269 repository, ordering it to be processed earlier is not recommended.
273 If the "chain" parameter is set and its command returns true, then B<mr>
274 will try to load a .mrconfig file from the root of the repository. (You
275 should avoid chaining from repositories with untrusted committers.)
279 If the "include" parameter is set, its command is ran, and should output
280 additional mrconfig file content. The content is included as if it were
281 part of the including file.
283 Unlike all other parameters, this parameter does not need to be placed
288 The "lib" parameter can specify some shell code that will be run before each
289 command, this can be a useful way to define shell functions for other commands
294 When looking for a command to run for a given action, mr first looks for
295 a parameter with the same name as the action. If that is not found, it
296 looks for a parameter named "rcs_action" (substituting in the name of the
297 revision control system and the action). The name of the revision control
298 system is itself determined by running each defined "rcs_test" action,
301 Internally, mr has settings for "git_update", "svn_update", etc. To change
302 the action that is performed for a given revision control system, you can
303 override these rcs specific actions. To add a new revision control system,
304 you can just add rcs specific actions for it.
308 Copyright 2007 Joey Hess <joey@kitenet.net>
310 Licensed under the GNU GPL version 2 or higher.
312 http://kitenet.net/~joey/code/mr/
321 use Cwd qw(getcwd abs_path);
323 # things that can happen when mr runs a command
332 my $config_overridden=0;
339 my $directory=getcwd();
340 $ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
347 my (@ok, @failed, @skipped);
353 my ($action, $dir, $topdir, $subdir) = @_;
355 if (exists $rcs{$dir}) {
360 foreach my $rcs_test (
362 length $a <=> length $b
365 } grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) {
366 my ($rcs)=$rcs_test=~/(.*)_test/;
367 $test="my_$rcs_test() {\n$config{$topdir}{$subdir}{$rcs_test}\n}\n".$test;
368 $test.="if my_$rcs_test; then echo $rcs; fi\n";
370 $test=$config{$topdir}{$subdir}{lib}."\n".$test
371 if exists $config{$topdir}{$subdir}{lib};
373 print "mr $action: running rcs test >>$test<<\n" if $verbose;
378 print STDERR "mr $action: found multiple possible repository types ($rcs) for $topdir$subdir\n";
382 return $rcs{$dir}=undef;
385 return $rcs{$dir}=$rcs;
389 sub findcommand { #{{{
390 my ($action, $dir, $topdir, $subdir, $is_checkout) = @_;
392 if (exists $config{$topdir}{$subdir}{$action}) {
393 return $config{$topdir}{$subdir}{$action};
400 my $rcs=rcs_test(@_);
403 exists $config{$topdir}{$subdir}{$rcs."_".$action}) {
404 return $config{$topdir}{$subdir}{$rcs."_".$action};
412 my ($action, $dir, $topdir, $subdir) = @_;
414 $ENV{MR_CONFIG}=$configfiles{$topdir};
415 my $lib=exists $config{$topdir}{$subdir}{lib} ?
416 $config{$topdir}{$subdir}{lib}."\n" : "";
417 my $is_checkout=($action eq 'checkout');
421 print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
425 $dir=~s/^(.*)\/[^\/]+\/?$/$1/;
427 elsif ($action =~ /update/) {
429 return action("checkout", $dir, $topdir, $subdir);
435 my $skiptest=findcommand("skip", $dir, $topdir, $subdir, $is_checkout);
436 my $command=findcommand($action, $dir, $topdir, $subdir, $is_checkout);
438 if (defined $skiptest) {
439 my $test="set -e;".$lib.
440 "my_action(){ $skiptest\n }; my_action '$action'";
441 print "mr $action: running skip test >>$test<<\n" if $verbose;
442 my $ret=system($test);
444 if (($? & 127) == 2) {
445 print STDERR "mr $action: interrupted\n";
449 print STDERR "mr $action: skip test received signal ".($? & 127)."\n";
453 if ($ret >> 8 == 0) {
454 print "mr $action: $dir skipped per config file\n" if $verbose;
459 if ($is_checkout && ! -d $dir) {
460 print "mr $action: creating parent directory $dir\n" if $verbose;
461 system("mkdir", "-p", $dir);
464 if (! $no_chdir && ! chdir($dir)) {
465 print STDERR "mr $action: failed to chdir to $dir: $!\n";
468 elsif (! defined $command) {
469 my $rcs=rcs_test(@_);
470 if (! defined $rcs) {
471 print STDERR "mr $action: unknown repository type and no defined $action command for $topdir$subdir\n";
475 print STDERR "mr $action: no defined action for $rcs repository $topdir$subdir, skipping\n";
481 print "mr $action: $topdir$subdir\n" unless $quiet;
485 $s=~s/^\Q$topdir$subdir\E\/?//;
486 print "mr $action: $topdir$subdir (in subdir $s)\n" unless $quiet;
488 $command="set -e; ".$lib.
489 "my_action(){ $command\n }; my_action ".
490 join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
491 print "mr $action: running >>$command<<\n" if $verbose;
492 my $ret=system($command);
494 if (($? & 127) == 2) {
495 print STDERR "mr $action: interrupted\n";
499 print STDERR "mr $action: received signal ".($? & 127)."\n";
502 print STDERR "mr $action: failed ($ret)\n" if $verbose;
503 if ($ret >> 8 != 0) {
504 print STDERR "mr $action: command failed\n";
507 print STDERR "mr $action: command died ($ret)\n";
512 if ($action eq 'checkout' && ! -d $dir) {
513 print STDERR "mr $action: $dir missing after checkout\n";;
522 # run actions on multiple repos, in parallel
532 while (@fhs or @repos) {
533 while ((!$jobs || $running < $jobs) && @repos) {
535 my $repo = shift @repos;
536 pipe(my $outfh, CHILD_STDOUT);
537 pipe(my $errfh, CHILD_STDERR);
539 unless ($pid = fork) {
540 die "mr $action: cannot fork: $!" unless defined $pid;
541 open(STDOUT, ">&CHILD_STDOUT") || die "mr $action cannot reopen stdout: $!";
542 open(STDERR, ">&CHILD_STDERR") || die "mr $action cannot reopen stderr: $!";
547 exit action($action, @$repo);
551 push @active, [$pid, $repo];
552 push @fhs, [$outfh, $errfh];
555 my ($rin, $rout) = ('','');
557 foreach my $fh (@fhs) {
558 next unless defined $fh;
559 vec($rin, fileno($fh->[0]), 1) = 1 if defined $fh->[0];
560 vec($rin, fileno($fh->[1]), 1) = 1 if defined $fh->[1];
562 $nfound = select($rout=$rin, undef, undef, 1);
563 foreach my $channel (0, 1) {
564 foreach my $i (0..$#fhs) {
565 next unless defined $fhs[$i];
566 my $fh = $fhs[$i][$channel];
567 next unless defined $fh;
568 if (vec($rout, fileno($fh), 1) == 1) {
570 if (sysread($fh, $r, 1024) == 0) {
572 $fhs[$i][$channel] = undef;
573 if (! defined $fhs[$i][0] &&
574 ! defined $fhs[$i][1]) {
575 waitpid($active[$i][0], 0);
576 print STDOUT $out[$i][0];
577 print STDERR $out[$i][1];
578 record($active[$i][1], $? >> 8);
580 splice(@active, $i, 1);
585 $out[$i][$channel] .= $r;
593 my $dir=shift()->[0];
600 elsif ($ret == FAILED) {
604 elsif ($ret == SKIPPED) {
607 elsif ($ret == ABORT) {
611 die "unknown exit status $ret";
617 if (! @ok && ! @failed && ! @skipped) {
618 die "mr $action: no repositories found to work on\n";
620 print "mr $action: finished (".join("; ",
621 showstat($#ok+1, "ok", "ok"),
622 showstat($#failed+1, "failed", "failed"),
623 showstat($#skipped+1, "skipped", "skipped"),
624 ).")\n" unless $quiet;
627 print "mr $action: (skipped: ".join(" ", @skipped).")\n" unless $quiet;
630 print STDERR "mr $action: (failed: ".join(" ", @failed).")\n";
640 return "$count ".($count > 1 ? $plural : $singular);
645 # an ordered list of repos
648 foreach my $topdir (sort keys %config) {
649 foreach my $subdir (sort keys %{$config{$topdir}}) {
653 order => $config{$topdir}{$subdir}{order},
658 $a->{order} <=> $b->{order}
660 $a->{topdir} cmp $b->{topdir}
662 $a->{subdir} cmp $b->{subdir}
666 # figure out which repos to act on
667 sub selectrepos { #{{{
669 foreach my $repo (repolist()) {
670 my $topdir=$repo->{topdir};
671 my $subdir=$repo->{subdir};
673 next if $subdir eq 'DEFAULT';
674 my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
676 $dir.="/" unless $dir=~/\/$/;
677 $d.="/" unless $d=~/\/$/;
678 next if $dir ne $d && $dir !~ /^\Q$d\E/;
679 if (defined $max_depth) {
680 my @a=split('/', $dir);
681 my @b=split('/', $d);
682 do { } while (@a && @b && shift(@a) eq shift(@b));
683 next if @a > $max_depth || @b > $max_depth;
685 push @repos, [$dir, $topdir, $subdir];
688 # fallback to find a leaf repo
689 foreach my $repo (reverse repolist()) {
690 my $topdir=$repo->{topdir};
691 my $subdir=$repo->{subdir};
693 next if $subdir eq 'DEFAULT';
694 my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
696 $dir.="/" unless $dir=~/\/$/;
697 $d.="/" unless $d=~/\/$/;
698 if ($d=~/^\Q$dir\E/) {
699 push @repos, [$dir, $topdir, $subdir];
709 sub loadconfig { #{{{
716 if (ref $f eq 'GLOB') {
725 my $absf=abs_path($f);
726 if ($loaded{$absf}) {
731 ($dir)=$f=~/^(.*\/)[^\/]+$/;
732 if (! defined $dir) {
735 $dir=abs_path($dir)."/";
737 if (! exists $configfiles{$dir}) {
738 $configfiles{$dir}=$f;
741 # copy in defaults from first parent
743 while ($parent=~s/^(.*\/)[^\/]+\/?$/$1/) {
744 if ($parent eq '/') {
747 if (exists $config{$parent} &&
748 exists $config{$parent}{DEFAULT}) {
749 $config{$dir}{DEFAULT}={ %{$config{$parent}{DEFAULT}} };
754 print "mr: loading config $f\n" if $verbose;
755 open($in, "<", $f) || die "mr: open $f: $!\n";
766 next if /^\s*\#/ || /^\s*$/;
767 if (/^\[([^\]]*)\]\s*$/) {
770 elsif (/^(\w+)\s*=\s*(.*)/) {
775 while (@lines && $lines[0]=~/^\s(.+)/) {
782 if ($parameter eq "include") {
783 print "mr: including output of \"$value\"\n" if $verbose;
784 unshift @lines, `$value`;
788 if (! defined $section) {
789 die "$f line $.: parameter ($parameter) not in section\n";
791 if ($section ne 'ALIAS' &&
792 ! exists $config{$dir}{$section} &&
793 exists $config{$dir}{DEFAULT}) {
795 $config{$dir}{$section}={ %{$config{$dir}{DEFAULT}} };
797 if ($section eq 'ALIAS') {
798 $alias{$parameter}=$value;
800 elsif ($parameter eq 'lib') {
801 $config{$dir}{$section}{lib}.=$value."\n";
804 $config{$dir}{$section}{$parameter}=$value;
805 if ($parameter =~ /.*_(.*)/) {
809 $knownactions{$parameter}=1;
811 if ($parameter eq 'chain' &&
812 length $dir && $section ne "DEFAULT" &&
813 -e $dir.$section."/.mrconfig") {
814 my $ret=system($value);
816 if (($? & 127) == 2) {
817 print STDERR "mr: chain test interrupted\n";
821 print STDERR "mr: chain test received signal ".($? & 127)."\n";
825 push @toload, $dir.$section."/.mrconfig";
831 die "$f line $line: parse error\n";
840 sub modifyconfig { #{{{
842 # the section to modify or add
843 my $targetsection=shift;
844 # fields to change in the section
845 # To remove a field, set its value to "".
852 open(my $in, "<", $f) || die "mr: open $f: $!\n";
857 my $formatfield=sub {
859 my @value=split(/\n/, shift);
861 return "$field = ".shift(@value)."\n".
862 join("", map { "\t$_\n" } @value);
866 while ($out[$#out] =~ /^\s*$/) {
867 unshift @blanks, pop @out;
869 foreach my $field (sort keys %changefields) {
870 if (length $changefields{$field}) {
871 push @out, "$field = $changefields{$field}\n";
872 delete $changefields{$field};
882 if (/^\s*\#/ || /^\s*$/) {
885 elsif (/^\[([^\]]*)\]\s*$/) {
886 if (defined $section &&
887 $section eq $targetsection) {
895 elsif (/^(\w+)\s*=\s(.*)/) {
900 while (@lines && $lines[0]=~/^\s(.+)/) {
906 if ($section eq $targetsection) {
907 if (exists $changefields{$parameter}) {
908 if (length $changefields{$parameter}) {
909 $value=$changefields{$parameter};
911 delete $changefields{$parameter};
915 push @out, $formatfield->($parameter, $value);
919 if (defined $section &&
920 $section eq $targetsection) {
923 elsif (%changefields) {
924 push @out, "\n[$targetsection]\n";
925 foreach my $field (sort keys %changefields) {
926 if (length $changefields{$field}) {
927 push @out, $formatfield->($field, $changefields{$field});
932 open(my $out, ">", $f) || die "mr: write $f: $!\n";
940 # actions that do not operate on all repos
941 if ($action eq 'help') {
944 elsif ($action eq 'config') {
947 elsif ($action eq 'register') {
951 if (!$jobs || $jobs > 1) {
952 mrs($action, selectrepos());
955 foreach my $repo (selectrepos()) {
956 record($repo, action($action, @$repo));
962 exec($config{''}{DEFAULT}{help}) || die "exec: $!";
967 die "mr config: not enough parameters\n";
970 if ($section=~/^\//) {
971 # try to convert to a path relative to the config file
972 my ($dir)=$ENV{MR_CONFIG}=~/^(.*\/)[^\/]+$/;
974 $dir.="/" unless $dir=~/\/$/;
975 if ($section=~/^\Q$dir\E(.*)/) {
981 if (/^([^=]+)=(.*)$/) {
982 $changefields{$1}=$2;
986 foreach my $topdir (sort keys %config) {
987 if (exists $config{$topdir}{$section} &&
988 exists $config{$topdir}{$section}{$_}) {
989 print $config{$topdir}{$section}{$_}."\n";
991 last if $section eq 'DEFAULT';
995 die "mr config: $section $_ not set\n";
999 modifyconfig($ENV{MR_CONFIG}, $section, %changefields) if %changefields;
1004 if (! $config_overridden) {
1005 # Find the closest known mrconfig file to the current
1007 $directory.="/" unless $directory=~/\/$/;
1009 foreach my $topdir (reverse sort keys %config) {
1010 next unless length $topdir;
1011 if ($directory=~/^\Q$topdir\E/) {
1012 $ENV{MR_CONFIG}=$configfiles{$topdir};
1018 if (! $foundconfig) {
1019 $directory=""; # no config file, use builtin
1023 my $subdir=shift @ARGV;
1024 if (! chdir($subdir)) {
1025 print STDERR "mr register: failed to chdir to $subdir: $!\n";
1029 $ENV{MR_REPO}=getcwd();
1030 my $command=findcommand("register", $ENV{MR_REPO}, $directory, 'DEFAULT', 0);
1031 if (! defined $command) {
1032 die "mr register: unknown repository type\n";
1035 $ENV{MR_REPO}=~s/.*\/(.*)/$1/;
1036 $command="set -e; ".$config{$directory}{DEFAULT}{lib}."\n".
1037 "my_action(){ $command\n }; my_action ".
1038 join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
1039 print "mr register: running >>$command<<\n" if $verbose;
1040 exec($command) || die "exec: $!";
1043 # alias expansion and command stemming
1044 sub expandaction { #{{{
1046 if (exists $alias{$action}) {
1047 $action=$alias{$action};
1049 if (! exists $knownactions{$action}) {
1050 my @matches = grep { /^\Q$action\E/ }
1051 keys %knownactions, keys %alias;
1052 if (@matches == 1) {
1053 $action=$matches[0];
1055 elsif (@matches == 0) {
1056 die "mr: unknown action \"$action\" (known actions: ".
1057 join(", ", sort keys %knownactions).")\n";
1060 die "mr: ambiguous action \"$action\" (matches: ".
1061 join(", ", @matches).")\n";
1068 Getopt::Long::Configure("bundling", "no_permute");
1069 my $result=GetOptions(
1070 "d|directory=s" => sub { $directory=abs_path($_[1]) },
1071 "c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
1072 "v|verbose" => \$verbose,
1073 "q|quiet" => \$quiet,
1074 "s|stats" => \$stats,
1075 "n|no-recurse:i" => \$max_depth,
1076 "j|jobs:i" => \$jobs,
1078 if (! $result || @ARGV < 1) {
1079 die("Usage: mr [-d directory] action [params ...]\n".
1080 "(Use mr help for man page.)\n");
1086 print STDERR "mr: interrupted\n";
1090 # This can happen if it's run in a directory that was removed
1091 # or other strangeness.
1092 if (! defined $directory) {
1093 die("mr: failed to determine working directory\n");
1095 # Make sure MR_CONFIG is an absolute path, but don't use abs_path since
1096 # the config file might be a symlink to elsewhere, and the directory it's
1097 # in is significant.
1098 if ($ENV{MR_CONFIG} !~ /^\//) {
1099 $ENV{MR_CONFIG}=getcwd()."/".$ENV{MR_CONFIG};
1101 # Try to set MR_PATH to the path to the program.
1103 use FindBin qw($Bin $Script);
1104 $ENV{MR_PATH}=$Bin."/".$Script;
1112 loadconfig($ENV{MR_CONFIG});
1113 #use Data::Dumper; print Dumper(\%config);
1115 my $action=expandaction(shift @ARGV);
1122 elsif (! @ok && @skipped) {
1130 # Finally, some useful actions that mr knows about by default.
1131 # These can be overridden in ~/.mrconfig.
1147 echo "mr (warning): $@" >&2
1153 if [ -z "$1" ] || [ -z "$2" ]; then
1154 error "mr: usage: hours_since action num"
1156 for dir in .git .svn .bzr CVS .hg _darcs; do
1157 if [ -e "$MR_REPO/$dir" ]; then
1158 flagfile="$MR_REPO/$dir/.mr_last$1"
1162 if [ -z "$flagfile" ]; then
1163 error "cannot determine flag filename"
1165 delta=`perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile"`
1166 if [ "$delta" -lt "$2" ]; then
1174 svn_test = test -d "$MR_REPO"/.svn
1175 git_test = test -d "$MR_REPO"/.git
1176 bzr_test = test -d "$MR_REPO"/.bzr
1177 cvs_test = test -d "$MR_REPO"/CVS
1178 hg_test = test -d "$MR_REPO"/.hg
1179 darcs_test = test -d "$MR_REPO"/_darcs
1181 test -d "$MR_REPO"/refs/heads && test -d "$MR_REPO"/refs/tags &&
1182 test -d "$MR_REPO"/objects && test -f "$MR_REPO"/config &&
1183 test "`GIT_CONFIG="$MR_REPO"/config git config --get core.bare`" = true
1185 svn_update = svn update "$@"
1192 bzr_update = bzr merge "$@"
1193 cvs_update = cvs update "$@"
1194 hg_update = hg pull "$@" && hg update "$@"
1195 darcs_update = darcs pull -a "$@"
1197 svn_status = svn status "$@"
1198 git_status = git status "$@" || true
1199 bzr_status = bzr status "$@"
1200 cvs_status = cvs status "$@"
1201 hg_status = hg status "$@"
1202 darcs_status = darcs whatsnew -ls "$@"
1204 svn_commit = svn commit "$@"
1205 git_commit = git commit -a "$@" && git push --all
1206 bzr_commit = bzr commit "$@" && bzr push
1207 cvs_commit = cvs commit "$@"
1208 hg_commit = hg commit -m "$@" && hg push
1209 darcs_commit = darcs record -a -m "$@" && darcs push -a
1211 git_record = git commit -a "$@"
1212 bzr_record = bzr commit "$@"
1213 hg_record = hg commit -m "$@"
1214 darcs_record = darcs record -a -m "$@"
1216 svn_diff = svn diff "$@"
1217 git_diff = git diff "$@"
1218 bzr_diff = bzr diff "$@"
1219 cvs_diff = cvs diff "$@"
1220 hg_diff = hg diff "$@"
1221 darcs_diff = darcs diff -u "$@"
1223 svn_log = svn log "$@"
1224 git_log = git log "$@"
1225 bzr_log = bzr log "$@"
1226 cvs_log = cvs log "$@"
1227 hg_log = hg log "$@"
1228 darcs_log = darcs changes "$@"
1229 git_bare_log = git log "$@"
1232 url=`LC_ALL=C svn info . | grep -i '^URL:' | cut -d ' ' -f 2`
1233 if [ -z "$url" ]; then
1234 error "cannot determine svn url"
1236 echo "Registering svn url: $url in $MR_CONFIG"
1237 mr -c "$MR_CONFIG" config "`pwd`" checkout="svn co '$url' '$MR_REPO'"
1239 url="`LC_ALL=C git config --get remote.origin.url`" || true
1240 if [ -z "$url" ]; then
1241 error "cannot determine git url"
1243 echo "Registering git url: $url in $MR_CONFIG"
1244 mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone '$url' '$MR_REPO'"
1246 url=`cat .bzr/branch/parent`
1247 if [ -z "$url" ]; then
1248 error "cannot determine bzr url"
1250 echo "Registering bzr url: $url in $MR_CONFIG"
1251 mr -c "$MR_CONFIG" config "`pwd`" checkout="bzr clone '$url' '$MR_REPO'"
1253 repo=`cat CVS/Repository`
1255 if [ -z "$root" ]; then
1256 error "cannot determine cvs root"
1258 echo "Registering cvs repository $repo at root $root"
1259 mr -c "$MR_CONFIG" config "`pwd`" checkout="cvs -d '$root' co -d '$MR_REPO' '$repo'"
1261 url=`hg showconfig paths.default`
1262 echo "Registering mercurial repo url: $url in $MR_CONFIG"
1263 mr -c "$MR_CONFIG" config "`pwd`" checkout="hg clone '$url' '$MR_REPO'"
1265 url=`cat _darcs/prefs/defaultrepo`
1266 echo "Registering darcs repository $url in $MR_CONFIG"
1267 mr -c "$MR_CONFIG" config "`pwd`" checkout="darcs get '$url' '$MR_REPO'"
1269 url="`LC_ALL=C GIT_CONFIG=config git config --get remote.origin.url`" || true
1270 if [ -z "$url" ]; then
1271 error "cannot determine git url"
1273 echo "Registering git url: $url in $MR_CONFIG"
1274 mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone --bare '$url' '$MR_REPO'"
1277 if [ ! -e "$MR_PATH" ]; then
1278 error "cannot find program path"
1280 tmp=$(mktemp -t mr.XXXXXXXXXX) || error "mktemp failed"
1281 trap "rm -f $tmp" exit
1282 pod2man -c mr "$MR_PATH" > "$tmp" || error "pod2man failed"
1283 man -l "$tmp" || error "man failed"
1287 ed = echo "A horse is a horse, of course, of course.."
1288 T = echo "I pity the fool."
1289 right = echo "Not found."
1292 # vim:sw=8:sts=0:ts=8:noet