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"]
23 B<mr> [options] register [repository]
25 B<mr> [options] config section ["parameter=[value]" ...]
27 B<mr> [options] action [params ...]
31 B<mr> is a Multiple Repository management tool. It can checkout, update, or
32 perform other actions on a set of repositories as if they were one combined
33 repository. It supports any combination of subversion, git, cvs, mecurial,
34 bzr and darcs repositories, and support for other revision control systems can
37 B<mr> cds into and operates on all registered repositories at or below your
38 working directory. Or, if you are in a subdirectory of a repository that
39 contains no other registered repositories, it will stay in that directory,
40 and work on only that repository,
42 These predefined commands should be fairly familiar to users of any revision
47 =item checkout (or co)
49 Checks out any repositories that are not already checked out.
53 Updates each repository from its configured remote repository.
55 If a repository isn't checked out yet, it will first check it out.
59 Displays a status report for each repository, showing what
60 uncommitted changes are present in the repository.
64 Commits changes to each repository. (By default, changes are pushed to the
65 remote repository too, when using distributed systems like git.)
67 The optional -m parameter allows specifying a commit message.
71 Show a diff of uncommitted changes.
79 These commands are also available:
85 List the repositories that mr will act on.
89 Register an existing repository in a mrconfig file. By default, the
90 repository in the current directory is registered, or you can specify a
91 directory to register.
93 The mrconfig file that is modified is chosen by either the -c option, or by
94 looking for the closest known one at or below the current directory.
98 Adds, modifies, removes, or prints a value from a mrconfig file. The next
99 parameter is the name of the section the value is in. To add or modify
100 values, use one or more instances of "parameter=value". Use "parameter=" to
101 remove a parameter. Use just "parameter" to get the value of a parameter.
103 For example, to add (or edit) a repository in src/foo:
105 mr config src/foo checkout="svn co svn://example.com/foo/trunk foo"
107 To show the command that mr uses to update the repository in src/foo:
109 mr config src/foo update
111 To see the built-in library of shell functions contained in mr:
113 mr config DEFAULT lib
115 The ~/.mrconfig file is used by default. To use a different config file,
124 Actions can be abbreviated to any unambiguous substring, so
125 "mr st" is equivalent to "mr status", and "mr up" is equivalent to "mr
128 Additional parameters can be passed to most commands, and are passed on
129 unchanged to the underlying revision control system. This is mostly useful
130 if the repositories mr will act on all use the same revision control
139 Specifies the topmost directory that B<mr> should work in. The default is
140 the current working directory.
144 Use the specified mrconfig file. The default is B<~/.mrconfig>
156 Expand the statistics line displayed at the end to include information
157 about exactly which repositories failed and were skipped, if any.
161 If no number if specified, just operate on the repository for the current
162 directory, do not recurse into deeper repositories.
164 If a number is specified, will recurse into repositories at most that many
165 subdirectories deep. For example, with -n 2 it would recurse into ./src/foo,
166 but not ./src/packages/bar.
170 Run the specified number of jobs in parallel, or an unlimited number of jobs
171 with no number specified. This can greatly speed up operations such as updates.
172 It is not recommended for interactive operations.
174 Note that running more than 10 jobs at a time is likely to run afoul of
175 ssh connection limits. Running between 3 and 5 jobs at a time will yeild
176 a good speedup in updates without loading the machine too much.
182 B<mr> is configured by .mrconfig files. It starts by reading the .mrconfig
183 file in your home directory, and this can in turn chain load .mrconfig files
186 Here is an example .mrconfig file:
189 checkout = svn co svn://svn.example.com/src/trunk src
193 checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git &&
195 git checkout -b mybranch origin/master
197 The .mrconfig file uses a variant of the INI file format. Lines starting with
198 "#" are comments. Values can be continued to the following line by
199 indenting the line with whitespace.
201 The "DEFAULT" section allows setting default values for the sections that
204 The "ALIAS" section allows adding aliases for actions. Each parameter
205 is an alias, and its value is the action to use.
207 All other sections add repositories. The section header specifies the
208 directory where the repository is located. This is relative to the directory
209 that contains the mrconfig file, but you can also choose to use absolute
212 Within a section, each parameter defines a shell command to run to handle a
213 given action. mr contains default handlers for "update", "status",
214 "commit", and other standard actions. Normally you only need to specify what
215 to do for "checkout".
217 Note that these shell commands are run in a "set -e" shell
218 environment, where any additional parameters you pass are available in
219 "$@". The "checkout" command is run in the parent of the repository
220 directory, since the repository isn't checked out yet. All other commands
221 are run inside the repository, though not necessarily at the top of it.
223 The "MR_REPO" environment variable is set to the path to the top of the
224 repository. (For the "register" action, "MR_REPO" is instead set to the
225 basename of the directory that should be created when checking the
228 The "MR_CONFIG" environment variable is set to the .mrconfig file
229 that defines the repo being acted on, or, if the repo is not yet in a config
230 file, the .mrconfig file that should be modified to register the repo.
232 A few parameters have special meanings:
238 If the "skip" parameter is set and its command returns true, then B<mr>
239 will skip acting on that repository. The command is passed the action
242 Here are two examples. The first skips the repo unless
243 mr is run by joey. The second uses the hours_since function
244 (included in mr's built-in library) to skip updating the repo unless it's
245 been at least 12 hours since the last update.
247 skip = test `whoami` != joey
248 skip = [ "$1" = update ] && ! hours_since "$1" 12
252 The "order" parameter can be used to override the default ordering of
253 repositories. The default order value is 10. Use smaller values to make
254 repositories be processed earlier, and larger values to make repositories
257 Note that if a repository is located in a subdirectory of another
258 repository, ordering it to be processed earlier is not recommended.
262 If the "chain" parameter is set and its command returns true, then B<mr>
263 will try to load a .mrconfig file from the root of the repository. (You
264 should avoid chaining from repositories with untrusted committers.)
268 If the "include" parameter is set, its command is ran, and should output
269 additional mrconfig file content. The content is included as if it were
270 part of the including file.
272 Unlike all other parameters, this parameter does not need to be placed
277 The "lib" parameter can specify some shell code that will be run before each
278 command, this can be a useful way to define shell functions for other commands
283 When looking for a command to run for a given action, mr first looks for
284 a parameter with the same name as the action. If that is not found, it
285 looks for a parameter named "rcs_action" (substituting in the name of the
286 revision control system and the action). The name of the revision control
287 system is itself determined by running each defined "rcs_test" action,
290 Internally, mr has settings for "git_update", "svn_update", etc. To change
291 the action that is performed for a given revision control system, you can
292 override these rcs specific actions. To add a new revision control system,
293 you can just add rcs specific actions for it.
297 Copyright 2007 Joey Hess <joey@kitenet.net>
299 Licensed under the GNU GPL version 2 or higher.
301 http://kitenet.net/~joey/code/mr/
310 use Cwd qw(getcwd abs_path);
312 # things that can happen when mr runs a command
321 my $config_overridden=0;
328 my $directory=getcwd();
329 $ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
336 my (@ok, @failed, @skipped);
342 my ($action, $dir, $topdir, $subdir) = @_;
344 if (exists $rcs{$dir}) {
349 foreach my $rcs_test (
351 length $a <=> length $b
354 } grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) {
355 my ($rcs)=$rcs_test=~/(.*)_test/;
356 $test="my_$rcs_test() {\n$config{$topdir}{$subdir}{$rcs_test}\n}\n".$test;
357 $test.="if my_$rcs_test; then echo $rcs; fi\n";
359 $test=$config{$topdir}{$subdir}{lib}."\n".$test
360 if exists $config{$topdir}{$subdir}{lib};
362 print "mr $action: running rcs test >>$test<<\n" if $verbose;
367 print STDERR "mr $action: found multiple possible repository types ($rcs) for $topdir$subdir\n";
371 return $rcs{$dir}=undef;
374 return $rcs{$dir}=$rcs;
378 sub findcommand { #{{{
379 my ($action, $dir, $topdir, $subdir, $is_checkout) = @_;
381 if (exists $config{$topdir}{$subdir}{$action}) {
382 return $config{$topdir}{$subdir}{$action};
389 my $rcs=rcs_test(@_);
392 exists $config{$topdir}{$subdir}{$rcs."_".$action}) {
393 return $config{$topdir}{$subdir}{$rcs."_".$action};
401 my ($action, $dir, $topdir, $subdir) = @_;
403 $ENV{MR_CONFIG}=$configfiles{$topdir};
404 my $lib=exists $config{$topdir}{$subdir}{lib} ?
405 $config{$topdir}{$subdir}{lib}."\n" : "";
406 my $is_checkout=($action eq 'checkout');
410 print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
414 $dir=~s/^(.*)\/[^\/]+\/?$/$1/;
416 elsif ($action =~ /update/) {
418 return action("checkout", $dir, $topdir, $subdir);
424 my $skiptest=findcommand("skip", $dir, $topdir, $subdir, $is_checkout);
425 my $command=findcommand($action, $dir, $topdir, $subdir, $is_checkout);
427 if (defined $skiptest) {
428 my $test="set -e;".$lib.
429 "my_action(){ $skiptest\n }; my_action '$action'";
430 print "mr $action: running skip test >>$test<<\n" if $verbose;
431 my $ret=system($test);
433 if (($? & 127) == 2) {
434 print STDERR "mr $action: interrupted\n";
438 print STDERR "mr $action: skip test received signal ".($? & 127)."\n";
442 if ($ret >> 8 == 0) {
443 print "mr $action: $dir skipped per config file\n" if $verbose;
448 if ($is_checkout && ! -d $dir) {
449 print "mr $action: creating parent directory $dir\n" if $verbose;
450 system("mkdir", "-p", $dir);
453 if (! $no_chdir && ! chdir($dir)) {
454 print STDERR "mr $action: failed to chdir to $dir: $!\n";
457 elsif (! defined $command) {
458 my $rcs=rcs_test(@_);
459 if (! defined $rcs) {
460 print STDERR "mr $action: unknown repository type and no defined $action command for $topdir$subdir\n";
464 print STDERR "mr $action: no defined action for $rcs repository $topdir$subdir, skipping\n";
470 print "mr $action: $topdir$subdir\n" unless $quiet;
474 $s=~s/^\Q$topdir$subdir\E\/?//;
475 print "mr $action: $topdir$subdir (in subdir $s)\n" unless $quiet;
477 $command="set -e; ".$lib.
478 "my_action(){ $command\n }; my_action ".
479 join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
480 print "mr $action: running >>$command<<\n" if $verbose;
481 my $ret=system($command);
483 if (($? & 127) == 2) {
484 print STDERR "mr $action: interrupted\n";
488 print STDERR "mr $action: received signal ".($? & 127)."\n";
491 print STDERR "mr $action: failed ($ret)\n" if $verbose;
492 if ($ret >> 8 != 0) {
493 print STDERR "mr $action: command failed\n";
496 print STDERR "mr $action: command died ($ret)\n";
501 if ($action eq 'checkout' && ! -d $dir) {
502 print STDERR "mr $action: $dir missing after checkout\n";;
511 # run actions on multiple repos, in parallel
521 while (@fhs or @repos) {
522 while ((!$jobs || $running < $jobs) && @repos) {
524 my $repo = shift @repos;
525 pipe(my $outfh, CHILD_STDOUT);
526 pipe(my $errfh, CHILD_STDERR);
528 unless ($pid = fork) {
529 die "mr $action: cannot fork: $!" unless defined $pid;
530 open(STDOUT, ">&CHILD_STDOUT") || die "mr $action cannot reopen stdout: $!";
531 open(STDERR, ">&CHILD_STDERR") || die "mr $action cannot reopen stderr: $!";
536 exit action($action, @$repo);
540 push @active, [$pid, $repo];
541 push @fhs, [$outfh, $errfh];
544 my ($rin, $rout) = ('','');
546 foreach my $fh (@fhs) {
547 next unless defined $fh;
548 vec($rin, fileno($fh->[0]), 1) = 1 if defined $fh->[0];
549 vec($rin, fileno($fh->[1]), 1) = 1 if defined $fh->[1];
551 $nfound = select($rout=$rin, undef, undef, 1);
552 foreach my $channel (0, 1) {
553 foreach my $i (0..$#fhs) {
554 next unless defined $fhs[$i];
555 my $fh = $fhs[$i][$channel];
556 next unless defined $fh;
557 if (vec($rout, fileno($fh), 1) == 1) {
559 if (sysread($fh, $r, 1024) == 0) {
561 $fhs[$i][$channel] = undef;
562 if (! defined $fhs[$i][0] &&
563 ! defined $fhs[$i][1]) {
564 waitpid($active[$i][0], 0);
565 print STDOUT $out[$i][0];
566 print STDERR $out[$i][1];
567 record($active[$i][1], $? >> 8);
569 splice(@active, $i, 1);
574 $out[$i][$channel] .= $r;
582 my $dir=shift()->[0];
589 elsif ($ret == FAILED) {
593 elsif ($ret == SKIPPED) {
596 elsif ($ret == ABORT) {
600 die "unknown exit status $ret";
606 if (! @ok && ! @failed && ! @skipped) {
607 die "mr $action: no repositories found to work on\n";
609 print "mr $action: finished (".join("; ",
610 showstat($#ok+1, "ok", "ok"),
611 showstat($#failed+1, "failed", "failed"),
612 showstat($#skipped+1, "skipped", "skipped"),
613 ).")\n" unless $quiet;
616 print "mr $action: (skipped: ".join(" ", @skipped).")\n" unless $quiet;
619 print STDERR "mr $action: (failed: ".join(" ", @failed).")\n";
629 return "$count ".($count > 1 ? $plural : $singular);
634 # an ordered list of repos
637 foreach my $topdir (sort keys %config) {
638 foreach my $subdir (sort keys %{$config{$topdir}}) {
642 order => $config{$topdir}{$subdir}{order},
647 $a->{order} <=> $b->{order}
649 $a->{topdir} cmp $b->{topdir}
651 $a->{subdir} cmp $b->{subdir}
655 # figure out which repos to act on
656 sub selectrepos { #{{{
658 foreach my $repo (repolist()) {
659 my $topdir=$repo->{topdir};
660 my $subdir=$repo->{subdir};
662 next if $subdir eq 'DEFAULT';
663 my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
665 $dir.="/" unless $dir=~/\/$/;
666 $d.="/" unless $d=~/\/$/;
667 next if $dir ne $d && $dir !~ /^\Q$d\E/;
668 if (defined $max_depth) {
669 my @a=split('/', $dir);
670 my @b=split('/', $d);
671 do { } while (@a && @b && shift(@a) eq shift(@b));
672 next if @a > $max_depth || @b > $max_depth;
674 push @repos, [$dir, $topdir, $subdir];
677 # fallback to find a leaf repo
678 foreach my $repo (reverse repolist()) {
679 my $topdir=$repo->{topdir};
680 my $subdir=$repo->{subdir};
682 next if $subdir eq 'DEFAULT';
683 my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
685 $dir.="/" unless $dir=~/\/$/;
686 $d.="/" unless $d=~/\/$/;
687 if ($d=~/^\Q$dir\E/) {
688 push @repos, [$dir, $topdir, $subdir];
698 sub loadconfig { #{{{
705 if (ref $f eq 'GLOB') {
714 my $absf=abs_path($f);
715 if ($loaded{$absf}) {
720 ($dir)=$f=~/^(.*\/)[^\/]+$/;
721 if (! defined $dir) {
724 $dir=abs_path($dir)."/";
726 if (! exists $configfiles{$dir}) {
727 $configfiles{$dir}=$f;
730 # copy in defaults from first parent
732 while ($parent=~s/^(.*\/)[^\/]+\/?$/$1/) {
733 if ($parent eq '/') {
736 if (exists $config{$parent} &&
737 exists $config{$parent}{DEFAULT}) {
738 $config{$dir}{DEFAULT}={ %{$config{$parent}{DEFAULT}} };
743 print "mr: loading config $f\n" if $verbose;
744 open($in, "<", $f) || die "mr: open $f: $!\n";
755 next if /^\s*\#/ || /^\s*$/;
756 if (/^\[([^\]]*)\]\s*$/) {
759 elsif (/^(\w+)\s*=\s*(.*)/) {
764 while (@lines && $lines[0]=~/^\s(.+)/) {
771 if ($parameter eq "include") {
772 print "mr: including output of \"$value\"\n" if $verbose;
773 unshift @lines, `$value`;
777 if (! defined $section) {
778 die "$f line $.: parameter ($parameter) not in section\n";
780 if ($section ne 'ALIAS' &&
781 ! exists $config{$dir}{$section} &&
782 exists $config{$dir}{DEFAULT}) {
784 $config{$dir}{$section}={ %{$config{$dir}{DEFAULT}} };
786 if ($section eq 'ALIAS') {
787 $alias{$parameter}=$value;
789 elsif ($parameter eq 'lib') {
790 $config{$dir}{$section}{lib}.=$value."\n";
793 $config{$dir}{$section}{$parameter}=$value;
794 if ($parameter =~ /.*_(.*)/) {
798 $knownactions{$parameter}=1;
800 if ($parameter eq 'chain' &&
801 length $dir && $section ne "DEFAULT" &&
802 -e $dir.$section."/.mrconfig") {
803 my $ret=system($value);
805 if (($? & 127) == 2) {
806 print STDERR "mr: chain test interrupted\n";
810 print STDERR "mr: chain test received signal ".($? & 127)."\n";
814 push @toload, $dir.$section."/.mrconfig";
820 die "$f line $line: parse error\n";
829 sub modifyconfig { #{{{
831 # the section to modify or add
832 my $targetsection=shift;
833 # fields to change in the section
834 # To remove a field, set its value to "".
841 open(my $in, "<", $f) || die "mr: open $f: $!\n";
846 my $formatfield=sub {
848 my @value=split(/\n/, shift);
850 return "$field = ".shift(@value)."\n".
851 join("", map { "\t$_\n" } @value);
855 while ($out[$#out] =~ /^\s*$/) {
856 unshift @blanks, pop @out;
858 foreach my $field (sort keys %changefields) {
859 if (length $changefields{$field}) {
860 push @out, "$field = $changefields{$field}\n";
861 delete $changefields{$field};
871 if (/^\s*\#/ || /^\s*$/) {
874 elsif (/^\[([^\]]*)\]\s*$/) {
875 if (defined $section &&
876 $section eq $targetsection) {
884 elsif (/^(\w+)\s*=\s(.*)/) {
889 while (@lines && $lines[0]=~/^\s(.+)/) {
895 if ($section eq $targetsection) {
896 if (exists $changefields{$parameter}) {
897 if (length $changefields{$parameter}) {
898 $value=$changefields{$parameter};
900 delete $changefields{$parameter};
904 push @out, $formatfield->($parameter, $value);
908 if (defined $section &&
909 $section eq $targetsection) {
912 elsif (%changefields) {
913 push @out, "\n[$targetsection]\n";
914 foreach my $field (sort keys %changefields) {
915 if (length $changefields{$field}) {
916 push @out, $formatfield->($field, $changefields{$field});
921 open(my $out, ">", $f) || die "mr: write $f: $!\n";
929 # actions that do not operate on all repos
930 if ($action eq 'help') {
933 elsif ($action eq 'config') {
936 elsif ($action eq 'register') {
940 if (!$jobs || $jobs > 1) {
941 mrs($action, selectrepos());
944 foreach my $repo (selectrepos()) {
945 record($repo, action($action, @$repo));
951 exec($config{''}{DEFAULT}{help}) || die "exec: $!";
956 die "mr config: not enough parameters\n";
959 if ($section=~/^\//) {
960 # try to convert to a path relative to the config file
961 my ($dir)=$ENV{MR_CONFIG}=~/^(.*\/)[^\/]+$/;
963 $dir.="/" unless $dir=~/\/$/;
964 if ($section=~/^\Q$dir\E(.*)/) {
970 if (/^([^=]+)=(.*)$/) {
971 $changefields{$1}=$2;
975 foreach my $topdir (sort keys %config) {
976 if (exists $config{$topdir}{$section} &&
977 exists $config{$topdir}{$section}{$_}) {
978 print $config{$topdir}{$section}{$_}."\n";
980 last if $section eq 'DEFAULT';
984 die "mr config: $section $_ not set\n";
988 modifyconfig($ENV{MR_CONFIG}, $section, %changefields) if %changefields;
993 if (! $config_overridden) {
994 # Find the closest known mrconfig file to the current
996 $directory.="/" unless $directory=~/\/$/;
998 foreach my $topdir (reverse sort keys %config) {
999 next unless length $topdir;
1000 if ($directory=~/^\Q$topdir\E/) {
1001 $ENV{MR_CONFIG}=$configfiles{$topdir};
1007 if (! $foundconfig) {
1008 $directory=""; # no config file, use builtin
1012 my $subdir=shift @ARGV;
1013 if (! chdir($subdir)) {
1014 print STDERR "mr register: failed to chdir to $subdir: $!\n";
1018 $ENV{MR_REPO}=getcwd();
1019 my $command=findcommand("register", $ENV{MR_REPO}, $directory, 'DEFAULT', 0);
1020 if (! defined $command) {
1021 die "mr register: unknown repository type\n";
1024 $ENV{MR_REPO}=~s/.*\/(.*)/$1/;
1025 $command="set -e; ".$config{$directory}{DEFAULT}{lib}."\n".
1026 "my_action(){ $command\n }; my_action ".
1027 join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
1028 print "mr register: running >>$command<<\n" if $verbose;
1029 exec($command) || die "exec: $!";
1032 # alias expansion and command stemming
1033 sub expandaction { #{{{
1035 if (exists $alias{$action}) {
1036 $action=$alias{$action};
1038 if (! exists $knownactions{$action}) {
1039 my @matches = grep { /^\Q$action\E/ }
1040 keys %knownactions, keys %alias;
1041 if (@matches == 1) {
1042 $action=$matches[0];
1044 elsif (@matches == 0) {
1045 die "mr: unknown action \"$action\" (known actions: ".
1046 join(", ", sort keys %knownactions).")\n";
1049 die "mr: ambiguous action \"$action\" (matches: ".
1050 join(", ", @matches).")\n";
1057 Getopt::Long::Configure("bundling", "no_permute");
1058 my $result=GetOptions(
1059 "d|directory=s" => sub { $directory=abs_path($_[1]) },
1060 "c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
1061 "v|verbose" => \$verbose,
1062 "q|quiet" => \$quiet,
1063 "s|stats" => \$stats,
1064 "n|no-recurse:i" => \$max_depth,
1065 "j|jobs:i" => \$jobs,
1067 if (! $result || @ARGV < 1) {
1068 die("Usage: mr [-d directory] action [params ...]\n".
1069 "(Use mr help for man page.)\n");
1075 print STDERR "mr: interrupted\n";
1079 # This can happen if it's run in a directory that was removed
1080 # or other strangeness.
1081 if (! defined $directory) {
1082 die("mr: failed to determine working directory\n");
1084 # Make sure MR_CONFIG is an absolute path, but don't use abs_path since
1085 # the config file might be a symlink to elsewhere, and the directory it's
1086 # in is significant.
1087 if ($ENV{MR_CONFIG} !~ /^\//) {
1088 $ENV{MR_CONFIG}=getcwd()."/".$ENV{MR_CONFIG};
1090 # Try to set MR_PATH to the path to the program.
1092 use FindBin qw($Bin $Script);
1093 $ENV{MR_PATH}=$Bin."/".$Script;
1101 loadconfig($ENV{MR_CONFIG});
1102 #use Data::Dumper; print Dumper(\%config);
1104 my $action=expandaction(shift @ARGV);
1111 elsif (! @ok && @skipped) {
1119 # Finally, some useful actions that mr knows about by default.
1120 # These can be overridden in ~/.mrconfig.
1136 echo "mr (warning): $@" >&2
1142 if [ -z "$1" ] || [ -z "$2" ]; then
1143 error "mr: usage: hours_since action num"
1145 for dir in .git .svn .bzr CVS .hg _darcs; do
1146 if [ -e "$MR_REPO/$dir" ]; then
1147 flagfile="$MR_REPO/$dir/.mr_last$1"
1151 if [ -z "$flagfile" ]; then
1152 error "cannot determine flag filename"
1154 delta=`perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile"`
1155 if [ "$delta" -lt "$2" ]; then
1163 svn_test = test -d "$MR_REPO"/.svn
1164 git_test = test -d "$MR_REPO"/.git
1165 bzr_test = test -d "$MR_REPO"/.bzr
1166 cvs_test = test -d "$MR_REPO"/CVS
1167 hg_test = test -d "$MR_REPO"/.hg
1168 darcs_test = test -d "$MR_REPO"/_darcs
1170 test -d "$MR_REPO"/refs/heads && test -d "$MR_REPO"/refs/tags &&
1171 test -d "$MR_REPO"/objects && test -f "$MR_REPO"/config &&
1172 test "`GIT_CONFIG="$MR_REPO"/config git config --get core.bare`" = true
1174 svn_update = svn update "$@"
1181 bzr_update = bzr merge "$@"
1182 cvs_update = cvs update "$@"
1183 hg_update = hg pull "$@" && hg update "$@"
1184 darcs_update = darcs pull -a "$@"
1186 svn_status = svn status "$@"
1187 git_status = git status "$@" || true
1188 bzr_status = bzr status "$@"
1189 cvs_status = cvs status "$@"
1190 hg_status = hg status "$@"
1191 darcs_status = darcs whatsnew -ls "$@"
1193 svn_commit = svn commit "$@"
1194 git_commit = git commit -a "$@" && git push --all
1195 bzr_commit = bzr commit "$@" && bzr push
1196 cvs_commit = cvs commit "$@"
1197 hg_commit = hg commit -m "$@" && hg push
1198 darcs_commit = darcs record -a -m "$@" && darcs push -a
1200 svn_diff = svn diff "$@"
1201 git_diff = git diff "$@"
1202 bzr_diff = bzr diff "$@"
1203 cvs_diff = cvs diff "$@"
1204 hg_diff = hg diff "$@"
1205 darcs_diff = darcs diff "$@"
1207 svn_log = svn log "$@"
1208 git_log = git log "$@"
1209 bzr_log = bzr log "$@"
1210 cvs_log = cvs log "$@"
1211 hg_log = hg log "$@"
1212 darcs_log = darcs changes "$@"
1213 git_bare_log = git log "$@"
1216 url=`LC_ALL=C svn info . | grep -i '^URL:' | cut -d ' ' -f 2`
1217 if [ -z "$url" ]; then
1218 error "cannot determine svn url"
1220 echo "Registering svn url: $url in $MR_CONFIG"
1221 mr -c "$MR_CONFIG" config "`pwd`" checkout="svn co '$url' '$MR_REPO'"
1223 url="`LC_ALL=C git config --get remote.origin.url`" || true
1224 if [ -z "$url" ]; then
1225 error "cannot determine git url"
1227 echo "Registering git url: $url in $MR_CONFIG"
1228 mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone '$url' '$MR_REPO'"
1230 url=`cat .bzr/branch/parent`
1231 if [ -z "$url" ]; then
1232 error "cannot determine bzr url"
1234 echo "Registering bzr url: $url in $MR_CONFIG"
1235 mr -c "$MR_CONFIG" config "`pwd`" checkout="bzr clone '$url' '$MR_REPO'"
1237 repo=`cat CVS/Repository`
1239 if [ -z "$root" ]; then
1240 error "cannot determine cvs root"
1242 echo "Registering cvs repository $repo at root $root"
1243 mr -c "$MR_CONFIG" config "`pwd`" checkout="cvs -d '$root' co -d '$MR_REPO' '$repo'"
1245 url=`hg showconfig paths.default`
1246 echo "Registering mercurial repo url: $url in $MR_CONFIG"
1247 mr -c "$MR_CONFIG" config "`pwd`" checkout="hg clone '$url' '$MR_REPO'"
1249 url=`cat _darcs/prefs/defaultrepo`
1250 echo "Registering darcs repository $url in $MR_CONFIG"
1251 mr -c "$MR_CONFIG" config "`pwd`" checkout="darcs get '$url' '$MR_REPO'"
1253 url="`LC_ALL=C GIT_CONFIG=config git config --get remote.origin.url`" || true
1254 if [ -z "$url" ]; then
1255 error "cannot determine git url"
1257 echo "Registering git url: $url in $MR_CONFIG"
1258 mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone --bare '$url' '$MR_REPO'"
1261 if [ ! -e "$MR_PATH" ]; then
1262 error "cannot find program path"
1264 tmp=$(mktemp -t mr.XXXXXXXXXX) || error "mktemp failed"
1265 trap "rm -f $tmp" exit
1266 pod2man -c mr "$MR_PATH" > "$tmp" || error "pod2man failed"
1267 man -l "$tmp" || error "man failed"
1271 ed = echo "A horse is a horse, of course, of course.."
1272 T = echo "I pity the fool."
1273 right = echo "Not found."
1276 # vim:sw=8:sts=0:ts=8:noet