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 Interactive mode. If a repository fails to be processed, a subshell will be
173 started which you can use to resolve or investigate the problem. Exit the
174 subshell to continue the mr run.
178 If no number if specified, just operate on the repository for the current
179 directory, do not recurse into deeper repositories.
181 If a number is specified, will recurse into repositories at most that many
182 subdirectories deep. For example, with -n 2 it would recurse into ./src/foo,
183 but not ./src/packages/bar.
187 Run the specified number of jobs in parallel, or an unlimited number of jobs
188 with no number specified. This can greatly speed up operations such as updates.
189 It is not recommended for interactive operations.
191 Note that running more than 10 jobs at a time is likely to run afoul of
192 ssh connection limits. Running between 3 and 5 jobs at a time will yeild
193 a good speedup in updates without loading the machine too much.
199 B<mr> is configured by .mrconfig files. It starts by reading the .mrconfig
200 file in your home directory, and this can in turn chain load .mrconfig files
203 Here is an example .mrconfig file:
206 checkout = svn co svn://svn.example.com/src/trunk src
210 checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git &&
212 git checkout -b mybranch origin/master
214 The .mrconfig file uses a variant of the INI file format. Lines starting with
215 "#" are comments. Values can be continued to the following line by
216 indenting the line with whitespace.
218 The "DEFAULT" section allows setting default values for the sections that
221 The "ALIAS" section allows adding aliases for actions. Each parameter
222 is an alias, and its value is the action to use.
224 All other sections add repositories. The section header specifies the
225 directory where the repository is located. This is relative to the directory
226 that contains the mrconfig file, but you can also choose to use absolute
229 Within a section, each parameter defines a shell command to run to handle a
230 given action. mr contains default handlers for "update", "status",
231 "commit", and other standard actions. Normally you only need to specify what
232 to do for "checkout".
234 Note that these shell commands are run in a "set -e" shell
235 environment, where any additional parameters you pass are available in
236 "$@". The "checkout" command is run in the parent of the repository
237 directory, since the repository isn't checked out yet. All other commands
238 are run inside the repository, though not necessarily at the top of it.
240 The "MR_REPO" environment variable is set to the path to the top of the
241 repository. (For the "register" action, "MR_REPO" is instead set to the
242 basename of the directory that should be created when checking the
245 The "MR_CONFIG" environment variable is set to the .mrconfig file
246 that defines the repo being acted on, or, if the repo is not yet in a config
247 file, the .mrconfig file that should be modified to register the repo.
249 A few parameters have special meanings:
255 If the "skip" parameter is set and its command returns true, then B<mr>
256 will skip acting on that repository. The command is passed the action
259 Here are two examples. The first skips the repo unless
260 mr is run by joey. The second uses the hours_since function
261 (included in mr's built-in library) to skip updating the repo unless it's
262 been at least 12 hours since the last update.
264 skip = test `whoami` != joey
265 skip = [ "$1" = update ] && ! hours_since "$1" 12
269 The "order" parameter can be used to override the default ordering of
270 repositories. The default order value is 10. Use smaller values to make
271 repositories be processed earlier, and larger values to make repositories
274 Note that if a repository is located in a subdirectory of another
275 repository, ordering it to be processed earlier is not recommended.
279 If the "chain" parameter is set and its command returns true, then B<mr>
280 will try to load a .mrconfig file from the root of the repository. (You
281 should avoid chaining from repositories with untrusted committers.)
285 If the "include" parameter is set, its command is ran, and should output
286 additional mrconfig file content. The content is included as if it were
287 part of the including file.
289 Unlike all other parameters, this parameter does not need to be placed
294 The "lib" parameter can specify some shell code that will be run before each
295 command, this can be a useful way to define shell functions for other commands
300 When looking for a command to run for a given action, mr first looks for
301 a parameter with the same name as the action. If that is not found, it
302 looks for a parameter named "rcs_action" (substituting in the name of the
303 revision control system and the action). The name of the revision control
304 system is itself determined by running each defined "rcs_test" action,
307 Internally, mr has settings for "git_update", "svn_update", etc. To change
308 the action that is performed for a given revision control system, you can
309 override these rcs specific actions. To add a new revision control system,
310 you can just add rcs specific actions for it.
314 Copyright 2007 Joey Hess <joey@kitenet.net>
316 Licensed under the GNU GPL version 2 or higher.
318 http://kitenet.net/~joey/code/mr/
327 use Cwd qw(getcwd abs_path);
329 # things that can happen when mr runs a command
338 my $config_overridden=0;
346 my $directory=getcwd();
347 $ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
354 my (@ok, @failed, @skipped);
360 my ($action, $dir, $topdir, $subdir) = @_;
362 if (exists $rcs{$dir}) {
367 foreach my $rcs_test (
369 length $a <=> length $b
372 } grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) {
373 my ($rcs)=$rcs_test=~/(.*)_test/;
374 $test="my_$rcs_test() {\n$config{$topdir}{$subdir}{$rcs_test}\n}\n".$test;
375 $test.="if my_$rcs_test; then echo $rcs; fi\n";
377 $test=$config{$topdir}{$subdir}{lib}."\n".$test
378 if exists $config{$topdir}{$subdir}{lib};
380 print "mr $action: running rcs test >>$test<<\n" if $verbose;
385 print STDERR "mr $action: found multiple possible repository types ($rcs) for $topdir$subdir\n";
389 return $rcs{$dir}=undef;
392 return $rcs{$dir}=$rcs;
396 sub findcommand { #{{{
397 my ($action, $dir, $topdir, $subdir, $is_checkout) = @_;
399 if (exists $config{$topdir}{$subdir}{$action}) {
400 return $config{$topdir}{$subdir}{$action};
407 my $rcs=rcs_test(@_);
410 exists $config{$topdir}{$subdir}{$rcs."_".$action}) {
411 return $config{$topdir}{$subdir}{$rcs."_".$action};
419 my ($action, $dir, $topdir, $subdir) = @_;
421 $ENV{MR_CONFIG}=$configfiles{$topdir};
422 my $lib=exists $config{$topdir}{$subdir}{lib} ?
423 $config{$topdir}{$subdir}{lib}."\n" : "";
424 my $is_checkout=($action eq 'checkout');
428 print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
432 $dir=~s/^(.*)\/[^\/]+\/?$/$1/;
434 elsif ($action =~ /update/) {
436 return action("checkout", $dir, $topdir, $subdir);
442 my $skiptest=findcommand("skip", $dir, $topdir, $subdir, $is_checkout);
443 my $command=findcommand($action, $dir, $topdir, $subdir, $is_checkout);
445 if (defined $skiptest) {
446 my $test="set -e;".$lib.
447 "my_action(){ $skiptest\n }; my_action '$action'";
448 print "mr $action: running skip test >>$test<<\n" if $verbose;
449 my $ret=system($test);
451 if (($? & 127) == 2) {
452 print STDERR "mr $action: interrupted\n";
456 print STDERR "mr $action: skip test received signal ".($? & 127)."\n";
460 if ($ret >> 8 == 0) {
461 print "mr $action: $dir skipped per config file\n" if $verbose;
466 if ($is_checkout && ! -d $dir) {
467 print "mr $action: creating parent directory $dir\n" if $verbose;
468 system("mkdir", "-p", $dir);
471 if (! $no_chdir && ! chdir($dir)) {
472 print STDERR "mr $action: failed to chdir to $dir: $!\n";
475 elsif (! defined $command) {
476 my $rcs=rcs_test(@_);
477 if (! defined $rcs) {
478 print STDERR "mr $action: unknown repository type and no defined $action command for $topdir$subdir\n";
482 print STDERR "mr $action: no defined action for $rcs repository $topdir$subdir, skipping\n";
488 print "mr $action: $topdir$subdir\n" unless $quiet;
492 $s=~s/^\Q$topdir$subdir\E\/?//;
493 print "mr $action: $topdir$subdir (in subdir $s)\n" unless $quiet;
495 $command="set -e; ".$lib.
496 "my_action(){ $command\n }; my_action ".
497 join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
498 print "mr $action: running >>$command<<\n" if $verbose;
499 my $ret=system($command);
501 if (($? & 127) == 2) {
502 print STDERR "mr $action: interrupted\n";
506 print STDERR "mr $action: received signal ".($? & 127)."\n";
509 print STDERR "mr $action: failed ($ret)\n" if $verbose;
510 if ($ret >> 8 != 0) {
511 print STDERR "mr $action: command failed\n";
514 print STDERR "mr $action: command died ($ret)\n";
519 if ($action eq 'checkout' && ! -d $dir) {
520 print STDERR "mr $action: $dir missing after checkout\n";;
529 # run actions on multiple repos, in parallel
539 while (@fhs or @repos) {
540 while ((!$jobs || $running < $jobs) && @repos) {
542 my $repo = shift @repos;
543 pipe(my $outfh, CHILD_STDOUT);
544 pipe(my $errfh, CHILD_STDERR);
546 unless ($pid = fork) {
547 die "mr $action: cannot fork: $!" unless defined $pid;
548 open(STDOUT, ">&CHILD_STDOUT") || die "mr $action cannot reopen stdout: $!";
549 open(STDERR, ">&CHILD_STDERR") || die "mr $action cannot reopen stderr: $!";
554 exit action($action, @$repo);
558 push @active, [$pid, $repo];
559 push @fhs, [$outfh, $errfh];
562 my ($rin, $rout) = ('','');
564 foreach my $fh (@fhs) {
565 next unless defined $fh;
566 vec($rin, fileno($fh->[0]), 1) = 1 if defined $fh->[0];
567 vec($rin, fileno($fh->[1]), 1) = 1 if defined $fh->[1];
569 $nfound = select($rout=$rin, undef, undef, 1);
570 foreach my $channel (0, 1) {
571 foreach my $i (0..$#fhs) {
572 next unless defined $fhs[$i];
573 my $fh = $fhs[$i][$channel];
574 next unless defined $fh;
575 if (vec($rout, fileno($fh), 1) == 1) {
577 if (sysread($fh, $r, 1024) == 0) {
579 $fhs[$i][$channel] = undef;
580 if (! defined $fhs[$i][0] &&
581 ! defined $fhs[$i][1]) {
582 waitpid($active[$i][0], 0);
583 print STDOUT $out[$i][0];
584 print STDERR $out[$i][1];
585 record($active[$i][1], $? >> 8);
587 splice(@active, $i, 1);
592 $out[$i][$channel] .= $r;
600 my $dir=shift()->[0];
607 elsif ($ret == FAILED) {
609 chdir($dir) unless $no_chdir;
610 print STDERR "mr: starting interactive shell. Exit shell to continue.\n";
611 system((getpwuid($<))[8]);
616 elsif ($ret == SKIPPED) {
619 elsif ($ret == ABORT) {
623 die "unknown exit status $ret";
629 if (! @ok && ! @failed && ! @skipped) {
630 die "mr $action: no repositories found to work on\n";
632 print "mr $action: finished (".join("; ",
633 showstat($#ok+1, "ok", "ok"),
634 showstat($#failed+1, "failed", "failed"),
635 showstat($#skipped+1, "skipped", "skipped"),
636 ).")\n" unless $quiet;
639 print "mr $action: (skipped: ".join(" ", @skipped).")\n" unless $quiet;
642 print STDERR "mr $action: (failed: ".join(" ", @failed).")\n";
652 return "$count ".($count > 1 ? $plural : $singular);
657 # an ordered list of repos
660 foreach my $topdir (sort keys %config) {
661 foreach my $subdir (sort keys %{$config{$topdir}}) {
665 order => $config{$topdir}{$subdir}{order},
670 $a->{order} <=> $b->{order}
672 $a->{topdir} cmp $b->{topdir}
674 $a->{subdir} cmp $b->{subdir}
678 # figure out which repos to act on
679 sub selectrepos { #{{{
681 foreach my $repo (repolist()) {
682 my $topdir=$repo->{topdir};
683 my $subdir=$repo->{subdir};
685 next if $subdir eq 'DEFAULT';
686 my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
688 $dir.="/" unless $dir=~/\/$/;
689 $d.="/" unless $d=~/\/$/;
690 next if $dir ne $d && $dir !~ /^\Q$d\E/;
691 if (defined $max_depth) {
692 my @a=split('/', $dir);
693 my @b=split('/', $d);
694 do { } while (@a && @b && shift(@a) eq shift(@b));
695 next if @a > $max_depth || @b > $max_depth;
697 push @repos, [$dir, $topdir, $subdir];
700 # fallback to find a leaf repo
701 foreach my $repo (reverse repolist()) {
702 my $topdir=$repo->{topdir};
703 my $subdir=$repo->{subdir};
705 next if $subdir eq 'DEFAULT';
706 my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
708 $dir.="/" unless $dir=~/\/$/;
709 $d.="/" unless $d=~/\/$/;
710 if ($d=~/^\Q$dir\E/) {
711 push @repos, [$dir, $topdir, $subdir];
721 sub loadconfig { #{{{
728 if (ref $f eq 'GLOB') {
737 my $absf=abs_path($f);
738 if ($loaded{$absf}) {
743 ($dir)=$f=~/^(.*\/)[^\/]+$/;
744 if (! defined $dir) {
747 $dir=abs_path($dir)."/";
749 if (! exists $configfiles{$dir}) {
750 $configfiles{$dir}=$f;
753 # copy in defaults from first parent
755 while ($parent=~s/^(.*\/)[^\/]+\/?$/$1/) {
756 if ($parent eq '/') {
759 if (exists $config{$parent} &&
760 exists $config{$parent}{DEFAULT}) {
761 $config{$dir}{DEFAULT}={ %{$config{$parent}{DEFAULT}} };
766 print "mr: loading config $f\n" if $verbose;
767 open($in, "<", $f) || die "mr: open $f: $!\n";
778 next if /^\s*\#/ || /^\s*$/;
779 if (/^\[([^\]]*)\]\s*$/) {
782 elsif (/^(\w+)\s*=\s*(.*)/) {
787 while (@lines && $lines[0]=~/^\s(.+)/) {
794 if ($parameter eq "include") {
795 print "mr: including output of \"$value\"\n" if $verbose;
796 unshift @lines, `$value`;
800 if (! defined $section) {
801 die "$f line $.: parameter ($parameter) not in section\n";
803 if ($section ne 'ALIAS' &&
804 ! exists $config{$dir}{$section} &&
805 exists $config{$dir}{DEFAULT}) {
807 $config{$dir}{$section}={ %{$config{$dir}{DEFAULT}} };
809 if ($section eq 'ALIAS') {
810 $alias{$parameter}=$value;
812 elsif ($parameter eq 'lib') {
813 $config{$dir}{$section}{lib}.=$value."\n";
816 $config{$dir}{$section}{$parameter}=$value;
817 if ($parameter =~ /.*_(.*)/) {
821 $knownactions{$parameter}=1;
823 if ($parameter eq 'chain' &&
824 length $dir && $section ne "DEFAULT" &&
825 -e $dir.$section."/.mrconfig") {
826 my $ret=system($value);
828 if (($? & 127) == 2) {
829 print STDERR "mr: chain test interrupted\n";
833 print STDERR "mr: chain test received signal ".($? & 127)."\n";
837 push @toload, $dir.$section."/.mrconfig";
843 die "$f line $line: parse error\n";
852 sub modifyconfig { #{{{
854 # the section to modify or add
855 my $targetsection=shift;
856 # fields to change in the section
857 # To remove a field, set its value to "".
864 open(my $in, "<", $f) || die "mr: open $f: $!\n";
869 my $formatfield=sub {
871 my @value=split(/\n/, shift);
873 return "$field = ".shift(@value)."\n".
874 join("", map { "\t$_\n" } @value);
878 while ($out[$#out] =~ /^\s*$/) {
879 unshift @blanks, pop @out;
881 foreach my $field (sort keys %changefields) {
882 if (length $changefields{$field}) {
883 push @out, "$field = $changefields{$field}\n";
884 delete $changefields{$field};
894 if (/^\s*\#/ || /^\s*$/) {
897 elsif (/^\[([^\]]*)\]\s*$/) {
898 if (defined $section &&
899 $section eq $targetsection) {
907 elsif (/^(\w+)\s*=\s(.*)/) {
912 while (@lines && $lines[0]=~/^\s(.+)/) {
918 if ($section eq $targetsection) {
919 if (exists $changefields{$parameter}) {
920 if (length $changefields{$parameter}) {
921 $value=$changefields{$parameter};
923 delete $changefields{$parameter};
927 push @out, $formatfield->($parameter, $value);
931 if (defined $section &&
932 $section eq $targetsection) {
935 elsif (%changefields) {
936 push @out, "\n[$targetsection]\n";
937 foreach my $field (sort keys %changefields) {
938 if (length $changefields{$field}) {
939 push @out, $formatfield->($field, $changefields{$field});
944 open(my $out, ">", $f) || die "mr: write $f: $!\n";
952 # actions that do not operate on all repos
953 if ($action eq 'help') {
956 elsif ($action eq 'config') {
959 elsif ($action eq 'register') {
963 if (!$jobs || $jobs > 1) {
964 mrs($action, selectrepos());
967 foreach my $repo (selectrepos()) {
968 record($repo, action($action, @$repo));
974 exec($config{''}{DEFAULT}{help}) || die "exec: $!";
979 die "mr config: not enough parameters\n";
982 if ($section=~/^\//) {
983 # try to convert to a path relative to the config file
984 my ($dir)=$ENV{MR_CONFIG}=~/^(.*\/)[^\/]+$/;
986 $dir.="/" unless $dir=~/\/$/;
987 if ($section=~/^\Q$dir\E(.*)/) {
993 if (/^([^=]+)=(.*)$/) {
994 $changefields{$1}=$2;
998 foreach my $topdir (sort keys %config) {
999 if (exists $config{$topdir}{$section} &&
1000 exists $config{$topdir}{$section}{$_}) {
1001 print $config{$topdir}{$section}{$_}."\n";
1003 last if $section eq 'DEFAULT';
1007 die "mr config: $section $_ not set\n";
1011 modifyconfig($ENV{MR_CONFIG}, $section, %changefields) if %changefields;
1016 if (! $config_overridden) {
1017 # Find the closest known mrconfig file to the current
1019 $directory.="/" unless $directory=~/\/$/;
1021 foreach my $topdir (reverse sort keys %config) {
1022 next unless length $topdir;
1023 if ($directory=~/^\Q$topdir\E/) {
1024 $ENV{MR_CONFIG}=$configfiles{$topdir};
1030 if (! $foundconfig) {
1031 $directory=""; # no config file, use builtin
1035 my $subdir=shift @ARGV;
1036 if (! chdir($subdir)) {
1037 print STDERR "mr register: failed to chdir to $subdir: $!\n";
1041 $ENV{MR_REPO}=getcwd();
1042 my $command=findcommand("register", $ENV{MR_REPO}, $directory, 'DEFAULT', 0);
1043 if (! defined $command) {
1044 die "mr register: unknown repository type\n";
1047 $ENV{MR_REPO}=~s/.*\/(.*)/$1/;
1048 $command="set -e; ".$config{$directory}{DEFAULT}{lib}."\n".
1049 "my_action(){ $command\n }; my_action ".
1050 join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
1051 print "mr register: running >>$command<<\n" if $verbose;
1052 exec($command) || die "exec: $!";
1055 # alias expansion and command stemming
1056 sub expandaction { #{{{
1058 if (exists $alias{$action}) {
1059 $action=$alias{$action};
1061 if (! exists $knownactions{$action}) {
1062 my @matches = grep { /^\Q$action\E/ }
1063 keys %knownactions, keys %alias;
1064 if (@matches == 1) {
1065 $action=$matches[0];
1067 elsif (@matches == 0) {
1068 die "mr: unknown action \"$action\" (known actions: ".
1069 join(", ", sort keys %knownactions).")\n";
1072 die "mr: ambiguous action \"$action\" (matches: ".
1073 join(", ", @matches).")\n";
1080 Getopt::Long::Configure("bundling", "no_permute");
1081 my $result=GetOptions(
1082 "d|directory=s" => sub { $directory=abs_path($_[1]) },
1083 "c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
1084 "v|verbose" => \$verbose,
1085 "q|quiet" => \$quiet,
1086 "s|stats" => \$stats,
1087 "i|interactive" => \$interactive,
1088 "n|no-recurse:i" => \$max_depth,
1089 "j|jobs:i" => \$jobs,
1091 if (! $result || @ARGV < 1) {
1092 die("Usage: mr [-d directory] action [params ...]\n".
1093 "(Use mr help for man page.)\n");
1099 print STDERR "mr: interrupted\n";
1103 # This can happen if it's run in a directory that was removed
1104 # or other strangeness.
1105 if (! defined $directory) {
1106 die("mr: failed to determine working directory\n");
1108 # Make sure MR_CONFIG is an absolute path, but don't use abs_path since
1109 # the config file might be a symlink to elsewhere, and the directory it's
1110 # in is significant.
1111 if ($ENV{MR_CONFIG} !~ /^\//) {
1112 $ENV{MR_CONFIG}=getcwd()."/".$ENV{MR_CONFIG};
1114 # Try to set MR_PATH to the path to the program.
1116 use FindBin qw($Bin $Script);
1117 $ENV{MR_PATH}=$Bin."/".$Script;
1125 loadconfig($ENV{MR_CONFIG});
1126 #use Data::Dumper; print Dumper(\%config);
1128 my $action=expandaction(shift @ARGV);
1135 elsif (! @ok && @skipped) {
1143 # Finally, some useful actions that mr knows about by default.
1144 # These can be overridden in ~/.mrconfig.
1160 echo "mr (warning): $@" >&2
1166 if [ -z "$1" ] || [ -z "$2" ]; then
1167 error "mr: usage: hours_since action num"
1169 for dir in .git .svn .bzr CVS .hg _darcs; do
1170 if [ -e "$MR_REPO/$dir" ]; then
1171 flagfile="$MR_REPO/$dir/.mr_last$1"
1175 if [ -z "$flagfile" ]; then
1176 error "cannot determine flag filename"
1178 delta=`perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile"`
1179 if [ "$delta" -lt "$2" ]; then
1187 svn_test = test -d "$MR_REPO"/.svn
1188 git_test = test -d "$MR_REPO"/.git
1189 bzr_test = test -d "$MR_REPO"/.bzr
1190 cvs_test = test -d "$MR_REPO"/CVS
1191 hg_test = test -d "$MR_REPO"/.hg
1192 darcs_test = test -d "$MR_REPO"/_darcs
1194 test -d "$MR_REPO"/refs/heads && test -d "$MR_REPO"/refs/tags &&
1195 test -d "$MR_REPO"/objects && test -f "$MR_REPO"/config &&
1196 test "`GIT_CONFIG="$MR_REPO"/config git config --get core.bare`" = true
1198 svn_update = svn update "$@"
1199 git_update = git pull "$@"
1200 bzr_update = bzr merge "$@"
1201 cvs_update = cvs update "$@"
1202 hg_update = hg pull "$@" && hg update "$@"
1203 darcs_update = darcs pull -a "$@"
1205 svn_status = svn status "$@"
1206 git_status = git status "$@" || true
1207 bzr_status = bzr status "$@"
1208 cvs_status = cvs status "$@"
1209 hg_status = hg status "$@"
1210 darcs_status = darcs whatsnew -ls "$@"
1212 svn_commit = svn commit "$@"
1213 git_commit = git commit -a "$@" && git push --all
1214 bzr_commit = bzr commit "$@" && bzr push
1215 cvs_commit = cvs commit "$@"
1216 hg_commit = hg commit -m "$@" && hg push
1217 darcs_commit = darcs record -a -m "$@" && darcs push -a
1219 git_record = git commit -a "$@"
1220 bzr_record = bzr commit "$@"
1221 hg_record = hg commit -m "$@"
1222 darcs_record = darcs record -a -m "$@"
1224 svn_diff = svn diff "$@"
1225 git_diff = git diff "$@"
1226 bzr_diff = bzr diff "$@"
1227 cvs_diff = cvs diff "$@"
1228 hg_diff = hg diff "$@"
1229 darcs_diff = darcs diff -u "$@"
1231 svn_log = svn log "$@"
1232 git_log = git log "$@"
1233 bzr_log = bzr log "$@"
1234 cvs_log = cvs log "$@"
1235 hg_log = hg log "$@"
1236 darcs_log = darcs changes "$@"
1237 git_bare_log = git log "$@"
1240 url=`LC_ALL=C svn info . | grep -i '^URL:' | cut -d ' ' -f 2`
1241 if [ -z "$url" ]; then
1242 error "cannot determine svn url"
1244 echo "Registering svn url: $url in $MR_CONFIG"
1245 mr -c "$MR_CONFIG" config "`pwd`" checkout="svn co '$url' '$MR_REPO'"
1247 url="`LC_ALL=C git config --get remote.origin.url`" || true
1248 if [ -z "$url" ]; then
1249 error "cannot determine git url"
1251 echo "Registering git url: $url in $MR_CONFIG"
1252 mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone '$url' '$MR_REPO'"
1254 url="`LC_ALL=C bzr info . | egrep -i 'checkout of branch|parent branch' | awk '{print $NF}'`"
1255 if [ -z "$url" ]; then
1256 error "cannot determine bzr url"
1258 echo "Registering bzr url: $url in $MR_CONFIG"
1259 mr -c "$MR_CONFIG" config "`pwd`" checkout="bzr clone '$url' '$MR_REPO'"
1261 repo=`cat CVS/Repository`
1263 if [ -z "$root" ]; then
1264 error "cannot determine cvs root"
1266 echo "Registering cvs repository $repo at root $root"
1267 mr -c "$MR_CONFIG" config "`pwd`" checkout="cvs -d '$root' co -d '$MR_REPO' '$repo'"
1269 url=`hg showconfig paths.default`
1270 echo "Registering mercurial repo url: $url in $MR_CONFIG"
1271 mr -c "$MR_CONFIG" config "`pwd`" checkout="hg clone '$url' '$MR_REPO'"
1273 url=`cat _darcs/prefs/defaultrepo`
1274 echo "Registering darcs repository $url in $MR_CONFIG"
1275 mr -c "$MR_CONFIG" config "`pwd`" checkout="darcs get '$url' '$MR_REPO'"
1277 url="`LC_ALL=C GIT_CONFIG=config git config --get remote.origin.url`" || true
1278 if [ -z "$url" ]; then
1279 error "cannot determine git url"
1281 echo "Registering git url: $url in $MR_CONFIG"
1282 mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone --bare '$url' '$MR_REPO'"
1285 if [ ! -e "$MR_PATH" ]; then
1286 error "cannot find program path"
1288 tmp=$(mktemp -t mr.XXXXXXXXXX) || error "mktemp failed"
1289 trap "rm -f $tmp" exit
1290 pod2man -c mr "$MR_PATH" > "$tmp" || error "pod2man failed"
1291 man -l "$tmp" || error "man failed"
1295 ed = echo "A horse is a horse, of course, of course.."
1296 T = echo "I pity the fool."
1297 right = echo "Not found."
1300 # vim:sw=8:sts=0:ts=8:noet