B<mr> [options] register [repository]
-B<mr> [options] config section [parameter=[value] ...]
+B<mr> [options] config section ["parameter=[value]" ...]
B<mr> [options] action [params ...]
=head1 DESCRIPTION
-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 respository. It
-supports any combination of subversion, git, cvs, and bzr repositories,
-and support for other revision control systems can easily be added.
+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
+respository. It supports any combination of subversion, git, cvs, mecurial,
+bzr and darcs repositories, and support for other revision control systems can
+easily be added.
B<mr> cds into and operates on all registered repositories at or below your
working directory. Or, if you are in a subdirectory of a repository that
=item register
-Register an existing repository in the mrconfig file. By default, the
+Register an existing repository in a mrconfig file. By default, the
repository in the current directory is registered, or you can specify a
directory to register.
-By default it registers it to the ~/.mrconfig file. To make it write to a
-different file, use the -c option.
+The mrconfig file that is modified is chosen by either the -c option, or by
+looking for the closest known one at or below the current directory.
=item config
-Adds, modifies, removes, or prints a value from the mrconfig file. The next
+Adds, modifies, removes, or prints a value from a mrconfig file. The next
parameter is the name of the section the value is in. To add or modify
values, use one or more instances of "parameter=value". Use "parameter=" to
remove a parameter. Use just "parameter" to get the value of a parameter.
mr config src/foo update
+To see the built-in library of shell functions contained in mr:
+
+ mr config DEFAULT lib
+
+The ~/.mrconfig file is used by default. To use a different config file,
+use the -c option.
+
=item help
Displays this help.
=item -c mrconfig
-Use the specified mrconfig file, instead of looking for one in your home
-directory.
+Use the specified mrconfig file. The default is B<~/.mrconfig>
=item -v
Just operate on the repository for the current directory, do not
recurse into deeper repositories.
+=item -j number
+
+Run the specified number of jobs in parallel. This can greatly speed up
+operations such as updates. It is not recommended for interactive
+operations.
+
=back
=head1 FILES
paths.
Within a section, each parameter defines a shell command to run to handle a
-given action. mr contains default handlers for the "update", "status", and
-"commit" actions, so normally you only need to specify what to do for
-"checkout".
+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".
Note that these shell commands are run in a "set -e" shell
environment, where any additional parameters you pass are available in
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, and "MR_CONFIG" 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 mr thinks it should be added to.
+repository. 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.
A few parameters have special meanings:
been at least 12 hours since the last update.
skip = test $(whoami) != joey
- skip = [ "$1" = update ] && [ $(hours_since "$1") -lt 12 ]
+ skip = [ "$1" = update ] && ! hours_since "$1" 12
+
+=item order
+
+The "order" parameter can be used to override the default ordering of
+repositories. The default order value is 10. Use smaller values to make
+repositories be processed earlier, and larger values to make repositories
+be processed later.
+
+Note that if a repository is located in a subdirectory of another
+repository, ordering it to be processed earlier is not recommended.
=item chain
will try to load a .mrconfig file from the root of the repository. (You
should avoid chaining from repositories with untrusted committers.)
-=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
use strict;
use Getopt::Long;
use Cwd qw(getcwd abs_path);
+use POSIX "WNOHANG";
+use constant {
+ OK => 0,
+ FAILED => 1,
+ SKIPPED => 2,
+ ABORT => 3,
+};
$SIG{INT}=sub {
print STDERR "mr: interrupted\n";
};
$ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
-my $directory=getcwd();
+my $config_overridden=0;
my $verbose=0;
my $stats=0;
my $no_recurse=0;
+my $jobs=1;
my %config;
my %configfiles;
my %knownactions;
my %alias;
+my $directory=getcwd();
Getopt::Long::Configure("no_permute");
my $result=GetOptions(
"d|directory=s" => sub { $directory=abs_path($_[1]) },
- "c|config=s" => sub { $ENV{MR_CONFIG}=abs_path($_[1]) },
+ "c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
"v|verbose" => \$verbose,
"s|stats" => \$stats,
"n|no-recurse" => \$no_recurse,
+ "j|jobs=i" => \$jobs,
);
if (! $result || @ARGV < 1) {
die("Usage: mr [-d directory] action [params ...]\n".
"(Use mr help for man page.)\n");
}
+if (! defined $directory) {
+ die("mr: failed to determine working directory\n");
+}
-loadconfig(\*DATA);
-loadconfig($ENV{MR_CONFIG});
-#use Data::Dumper;
-#print Dumper(\%config);
-
+# Make sure MR_CONFIG is an absolute path, but don't use abs_path since
+# the config file might be a symlink to elsewhere, and the directory it's
+# in is significant.
+if ($ENV{MR_CONFIG} !~ /^\//) {
+ $ENV{MR_CONFIG}=getcwd()."/".$ENV{MR_CONFIG};
+}
+# Try to set MR_PATH to the path to the program.
eval {
use FindBin qw($Bin $Script);
$ENV{MR_PATH}=$Bin."/".$Script;
};
+loadconfig(\*DATA);
+loadconfig($ENV{MR_CONFIG});
+#use Data::Dumper;
+#print Dumper(\%config);
+
# alias expansion and command stemming
my $action=shift @ARGV;
if (exists $alias{$action}) {
}
}
+# commands that do not operate on all repos
if ($action eq 'help') {
exec($config{''}{DEFAULT}{$action}) || die "exec: $!";
}
if ($section=~/^\//) {
# try to convert to a path relative to the config file
my ($dir)=$ENV{MR_CONFIG}=~/^(.*\/)[^\/]+$/;
+ $dir=abs_path($dir);
+ $dir.="/" unless $dir=~/\/$/;
if ($section=~/^\Q$dir\E(.*)/) {
$section=$1;
}
exists $config{$topdir}{$section}{$_}) {
print $config{$topdir}{$section}{$_}."\n";
$found=1;
+ last if $section eq 'DEFAULT';
}
}
if (! $found) {
exit 0;
}
elsif ($action eq 'register') {
+ if (! $config_overridden) {
+ # Find the closest known mrconfig file to the current
+ # directory.
+ $directory.="/" unless $directory=~/\/$/;
+ foreach my $topdir (reverse sort keys %config) {
+ next unless length $topdir;
+ if ($directory=~/^\Q$topdir\E/) {
+ $ENV{MR_CONFIG}=$configfiles{$topdir};
+ last;
+ }
+ }
+ }
my $command="set -e; ".$config{''}{DEFAULT}{lib}."\n".
"my_action(){ $config{''}{DEFAULT}{$action}\n }; my_action ".
join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
exec($command) || die "exec: $!";
}
+# an ordered list of repos
+my @list;
+foreach my $topdir (sort keys %config) {
+ foreach my $subdir (sort keys %{$config{$topdir}}) {
+ push @list, {
+ topdir => $topdir,
+ subdir => $subdir,
+ order => $config{$topdir}{$subdir}{order},
+ };
+ }
+}
+@list = sort {
+ $a->{order} <=> $b->{order}
+ ||
+ $a->{topdir} cmp $b->{topdir}
+ ||
+ $a->{subdir} cmp $b->{subdir}
+ } @list;
+
# work out what repos to act on
my @repos;
my $nochdir=0;
-foreach my $topdir (sort keys %config) {
- foreach my $subdir (sort keys %{$config{$topdir}}) {
+foreach my $repo (@list) {
+ my $topdir=$repo->{topdir};
+ my $subdir=$repo->{subdir};
+
+ next if $subdir eq 'DEFAULT';
+ my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
+ my $d=$directory;
+ $dir.="/" unless $dir=~/\/$/;
+ $d.="/" unless $d=~/\/$/;
+ next if $no_recurse && $d ne $dir;
+ next if $dir ne $d && $dir !~ /^\Q$d\E/;
+ push @repos, [$dir, $topdir, $subdir];
+}
+if (! @repos) {
+ # fallback to find a leaf repo
+ foreach my $repo (reverse @list) {
+ my $topdir=$repo->{topdir};
+ my $subdir=$repo->{subdir};
+
next if $subdir eq 'DEFAULT';
my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
my $d=$directory;
$dir.="/" unless $dir=~/\/$/;
$d.="/" unless $d=~/\/$/;
- next if $no_recurse && $d ne $dir;
- next if $dir ne $d && $dir !~ /^\Q$d\E/;
- push @repos, [$dir, $topdir, $subdir];
- }
-}
-if (! @repos) {
- # fallback to find a leaf repo
- LEAF: foreach my $topdir (reverse sort keys %config) {
- foreach my $subdir (reverse sort keys %{$config{$topdir}}) {
- next if $subdir eq 'DEFAULT';
- my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
- my $d=$directory;
- $dir.="/" unless $dir=~/\/$/;
- $d.="/" unless $d=~/\/$/;
- if ($d=~/^\Q$dir\E/) {
- push @repos, [$dir, $topdir, $subdir];
- last LEAF;
- }
+ if ($d=~/^\Q$dir\E/) {
+ push @repos, [$dir, $topdir, $subdir];
+ last;
}
}
$nochdir=1;
}
-my (@failed, @ok, @skipped);
-foreach my $repo (@repos) {
- action($action, @$repo);
+# run the action on each repository and print stats
+my (@ok, @failed, @skipped);
+if ($jobs > 1) {
+ mrs(@repos);
+}
+else {
+ foreach my $repo (@repos) {
+ record($repo, action($action, @$repo));
+ }
+}
+if (! @ok && ! @failed && ! @skipped) {
+ die "mr $action: no repositories found to work on\n";
+}
+print "mr $action: finished (".join("; ",
+ showstat($#ok+1, "ok", "ok"),
+ showstat($#failed+1, "failed", "failed"),
+ showstat($#skipped+1, "skipped", "skipped"),
+).")\n";
+if ($stats) {
+ if (@skipped) {
+ print "mr $action: (skipped: ".join(" ", @skipped).")\n";
+ }
+ if (@failed) {
+ print STDERR "mr $action: (failed: ".join(" ", @failed).")\n";
+ }
+}
+if (@failed) {
+ exit 1;
+}
+elsif (! @ok && @skipped) {
+ exit 1;
}
+exit 0;
sub action { #{{{
my ($action, $dir, $topdir, $subdir) = @_;
my $lib=exists $config{$topdir}{$subdir}{lib} ?
$config{$topdir}{$subdir}{lib}."\n" : "";
- if (exists $config{$topdir}{$subdir}{deleted}) {
- my $test="set -e;".$lib.$config{$topdir}{$subdir}{deleted};
- print "mr $action: running deleted test >>$test<<\n" if $verbose;
- my $ret=system($test);
- if ($ret != 0) {
- if (($? & 127) == 2) {
- print STDERR "mr $action: interrupted\n";
- exit 2;
- }
- elsif ($? & 127) {
- print STDERR "mr $action: deleted test received signal ".($? & 127)."\n";
- }
- }
- if ($ret >> 8 == 0) {
- if (-d $dir) {
- print STDERR "mr error: $dir should be deleted yet still exists\n\n";
- push @failed, $dir;
- return;
- }
- else {
- print "mr $action: $dir skipped (as deleted) per config file\n" if $verbose;
- push @skipped, $dir;
- return;
- }
- }
- }
-
if ($action eq 'checkout') {
if (-d $dir) {
print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
- push @skipped, $dir;
- return;
+ return SKIPPED;
}
$dir=~s/^(.*)\/[^\/]+\/?$/$1/;
if (! -d $dir) {
print "mr $action: creating parent directory $dir\n" if $verbose;
- my $ret=system("mkdir", "-p", $dir);
+ system("mkdir", "-p", $dir);
}
}
- elsif ($action eq 'update') {
+ elsif ($action =~ /update/) {
if (! -d $dir) {
return action("checkout", $dir, $topdir, $subdir);
}
if ($ret != 0) {
if (($? & 127) == 2) {
print STDERR "mr $action: interrupted\n";
- exit 2;
+ return ABORT;
}
elsif ($? & 127) {
print STDERR "mr $action: skip test received signal ".($? & 127)."\n";
- exit 1;
+ return ABORT;
}
}
if ($ret >> 8 == 0) {
print "mr $action: $dir skipped per config file\n" if $verbose;
- push @skipped, $dir;
- return;
+ return SKIPPED;
}
}
if (! $nochdir && ! chdir($dir)) {
print STDERR "mr $action: failed to chdir to $dir: $!\n";
- push @failed, $dir;
+ return FAILED;
}
elsif (! exists $config{$topdir}{$subdir}{$action}) {
print STDERR "mr $action: no defined $action command for $topdir$subdir, skipping\n";
- push @skipped, $dir;
+ return SKIPPED;
}
else {
if (! $nochdir) {
print "mr $action: $topdir$subdir\n";
}
else {
- print "mr $action: $topdir$subdir (in subdir $directory)\n";
+ my $s=$directory;
+ $s=~s/^\Q$topdir$subdir\E\/?//;
+ print "mr $action: $topdir$subdir (in subdir $s)\n";
}
my $command="set -e; ".$lib.
"my_action(){ $config{$topdir}{$subdir}{$action}\n }; my_action ".
if ($ret != 0) {
if (($? & 127) == 2) {
print STDERR "mr $action: interrupted\n";
- exit 2;
+ return ABORT;
}
elsif ($? & 127) {
print STDERR "mr $action: received signal ".($? & 127)."\n";
+ return ABORT;
}
print STDERR "mr $action: failed ($ret)\n" if $verbose;
- push @failed, $dir;
if ($ret >> 8 != 0) {
print STDERR "mr $action: command failed\n";
}
elsif ($ret != 0) {
print STDERR "mr $action: command died ($ret)\n";
}
+ return FAILED;
}
else {
if ($action eq 'checkout' && ! -d $dir) {
print STDERR "mr $action: $dir missing after checkout\n";;
- push @failed, $dir;
- return;
+ return FAILED;
}
- push @ok, $dir;
+ return OK;
+ }
+ }
+} #}}}
+
+# run actions on multiple repos, in parallel
+sub mrs { #{{{
+ $| = 1;
+ my @active;
+ my @fhs;
+ my @out;
+ my $running=0;
+ while (@fhs or @repos) {
+ while ($running < $jobs && @repos) {
+ $running++;
+ my $repo = shift @repos;
+ pipe(my $outfh, CHILD_STDOUT);
+ pipe(my $errfh, CHILD_STDERR);
+ my $pid;
+ unless ($pid = fork) {
+ die "mr $action: cannot fork: $!" unless defined $pid;
+ open(STDOUT, ">&CHILD_STDOUT") || die "mr $action cannot reopen stdout: $!";
+ open(STDERR, ">&CHILD_STDERR") || die "mr $action cannot reopen stderr: $!";
+ close CHILD_STDOUT;
+ close CHILD_STDERR;
+ close $outfh;
+ close $errfh;
+ exit action($action, @$repo);
+ }
+ close CHILD_STDOUT;
+ close CHILD_STDERR;
+ push @active, [$pid, $repo];
+ push @fhs, [$outfh, $errfh];
+ push @out, ['', ''];
+ }
+ my ($rin, $rout) = ('','');
+ my $nfound;
+ foreach my $fh (@fhs) {
+ next unless defined $fh;
+ vec($rin, fileno($fh->[0]), 1) = 1 if defined $fh->[0];
+ vec($rin, fileno($fh->[1]), 1) = 1 if defined $fh->[1];
+ }
+ $nfound = select($rout=$rin, undef, undef, 1);
+ foreach my $channel (0, 1) {
+ foreach my $i (0..$#fhs) {
+ next unless defined $fhs[$i];
+ my $fh = $fhs[$i][$channel];
+ next unless defined $fh;
+ if (vec($rout, fileno($fh), 1) == 1) {
+ my $r = '';
+ if (sysread($fh, $r, 1024) == 0) {
+ close($fh);
+ $fhs[$i][$channel] = undef;
+ if (! defined $fhs[$i][0] &&
+ ! defined $fhs[$i][1]) {
+ waitpid($active[$i][0], 0);
+ print STDOUT $out[$i][0];
+ print STDERR $out[$i][1];
+ record($active[$i][1], $? >> 8);
+ splice(@fhs, $i, 1);
+ splice(@active, $i, 1);
+ splice(@out, $i, 1);
+ $running--;
+ }
+ }
+ $out[$i][$channel] .= $r;
+ }
+ }
}
+ }
+} #}}}
+
+sub record { #{{{
+ my $dir=shift()->[0];
+ my $ret=shift;
+ if ($ret == OK) {
+ push @ok, $dir;
print "\n";
}
+ elsif ($ret == FAILED) {
+ push @failed, $dir;
+ print "\n";
+ }
+ elsif ($ret == SKIPPED) {
+ push @skipped, $dir;
+ }
+ elsif ($ret == ABORT) {
+ exit 1;
+ }
+ else {
+ die "unknown exit status $ret";
+ }
} #}}}
sub showstat { #{{{
}
return;
} #}}}
-if (! @ok && ! @failed && ! @skipped) {
- die "mr $action: no repositories found to work on\n";
-}
-print "mr $action: finished (".join("; ",
- showstat($#ok+1, "ok", "ok"),
- showstat($#failed+1, "failed", "failed"),
- showstat($#skipped+1, "skipped", "skipped"),
-).")\n";
-if ($stats) {
- if (@skipped) {
- print "mr $action: (skipped: ".join(" ", @skipped).")\n";
- }
- if (@failed) {
- print STDERR "mr $action: (failed: ".join(" ", @failed).")\n";
- }
-}
-if (@failed) {
- exit 1;
-}
-elsif (! @ok && @skipped) {
- exit 1;
-}
-exit 0;
my %loaded;
sub loadconfig { #{{{
$dir=".";
}
$dir=abs_path($dir)."/";
+
+ if (! exists $configfiles{$dir}) {
+ $configfiles{$dir}=$f;
+ }
# copy in defaults from first parent
my $parent=$dir;
else {
$config{$dir}{$section}{$parameter}=$value;
$knownactions{$parameter}=1;
- if (! exists $configfiles{$dir}) {
- $configfiles{$dir}=abs_path($f);
- }
if ($parameter eq 'chain' &&
length $dir && $section ne "DEFAULT" &&
-e $dir.$section."/.mrconfig") {
ls = list
[DEFAULT]
+order = 10
lib =
+ PWD="$pwd"
error() {
echo "mr: $@" >&2
exit 1
}
hours_since() {
- for dir in .git .svn .bzr CVS; do
+ if [ -z "$1" ] || [ -z "$2" ]; then
+ error "mr: usage: hours_since action num"
+ fi
+ for dir in .git .svn .bzr CVS .hg _darcs; do
if [ -e "$MR_REPO/$dir" ]; then
flagfile="$MR_REPO/$dir/.mr_last$1"
break
if [ -z "$flagfile" ]; then
error "cannot determine flag filename"
fi
- perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile"
- touch "$flagfile"
+ delta=$(perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile")
+ if [ "$delta" -lt "$2" ]; then
+ exit 0
+ else
+ touch "$flagfile"
+ exit 1
+ fi
+ }
+ get_git_repo_type()
+ {
+ if [ -d "$1"/.git ] && [ -d "$1"/.git/refs/heads ] &&
+ [ -d "$1"/.git/objects ] && [ -f "$1"/.git/config ];
+ then
+ echo non-bare
+ elif [ -d "$1"/refs/heads ] && [ -d "$1"/refs/tags ] &&
+ [ -d "$1"/objects ] && [ -f "$1"/config ]; then
+ local bare
+ bare="$(GIT_CONFIG="$1"/config git-config --get core.bare)"
+ case "$bare" in
+ true) echo bare;;
+ false) echo fake-bare;;
+ *) return 255;;
+ esac
+ else
+ return 1
+ fi
+ }
+ is_git_repo() {
+ get_git_repo_type "$1" >/dev/null
+ }
+ get_repo_type() {
+ if [ -d "$1"/.svn ]; then
+ echo svn
+ elif is_git_repo "$1"; then
+ echo git
+ elif [ -d "$1"/.bzr ]; then
+ echo bzr
+ elif [ -d "$1"/CVS ]; then
+ echo CVS
+ elif [ -d "$1"/.hg ]; then
+ echo hg
+ elif [ -d "$1"/_darcs ]; then
+ echo darcs
+ else
+ echo unknown
+ fi
}
update =
- if [ -d "$MR_REPO"/.svn ]; then
- svn update "$@"
- elif [ -d "$MR_REPO"/.git ]; then
- git pull origin master "$@"
- elif [ -d "$MR_REPO"/.bzr ]; then
- bzr merge "$@"
- elif [ -d "$MR_REPO"/CVS ]; then
- cvs update "$@"
- else
- error "unknown repo type"
- fi
+ case "$(get_repo_type "$MR_REPO")" in
+ svn) svn update "$@";;
+ git)
+ # all this is because of a bug in git-fetch, which requires GIT_DIR set
+ local git_dir_override; git_dir_override=.git
+ case "$(get_git_repo_type "$MR_REPO")" in
+ fake-bare) git_dir_override="$MR_REPO";;
+ esac
+ args="$@"
+ [ -z "$args" ] && args="-t origin master"
+ eval GIT_DIR="$git_dir_override" git pull "$args"
+ ;;
+ bzr) bzr merge "$@";;
+ CVS) cvs update "$@";;
+ hg) hg pull "$@" && hg update "$@";;
+ darcs) darcs pull -a "$@";;
+ *) error "unknown repo type";;
+ esac
+
status =
- if [ -d "$MR_REPO"/.svn ]; then
- svn status "$@"
- elif [ -d "$MR_REPO"/.git ]; then
- git status "$@" || true
- elif [ -d "$MR_REPO"/.bzr ]; then
- bzr status "$@"
- elif [ -d "$MR_REPO"/CVS ]; then
- cvs status "$@"
- else
- error "unknown repo type"
- fi
+ case "$(get_repo_type "$MR_REPO")" in
+ svn) svn status "$@";;
+ git) git status "$@" || :;;
+ bzr) bzr status "$@";;
+ CVS) cvs status "$@";;
+ hg) hg status "$@";;
+ darcs) darcs whatsnew -ls "$@";;
+ *) error "unknown repo type";;
+ esac
+
commit =
- if [ -d "$MR_REPO"/.svn ]; then
- svn commit "$@"
- elif [ -d "$MR_REPO"/.git ]; then
+ case "$(get_repo_type "$MR_REPO")" in
+ svn) svn commit "$@";;
+ git)
+ case "$(get_git_repo_type "$MR_REPO")" in
+ bare) error "cannot commit to bare git repositories";;
+ fake-bare) error "commit does not work for fake bare git repositories (yet).";;
+ esac
git commit -a "$@" && git push --all
- elif [ -d "$MR_REPO"/.bzr ]; then
- bzr commit "$@" && bzr push
- elif [ -d "$MR_REPO"/CVS ]; then
- cvs commit "$@"
- else
- error "unknown repo type"
- fi
+ ;;
+ bzr) bzr commit "$@" && bzr push;;
+ CVS) cvs commit "$@";;
+ hg) hg commit -m "$@" && hg push;;
+ darcs) darcs commit -a -m "$@" && darcs push -a;;
+ *) error "unknown repo type";;
+ esac
+
diff =
- if [ -d "$MR_REPO"/.svn ]; then
- svn diff "$@"
- elif [ -d "$MR_REPO"/.git ]; then
+ case "$(get_repo_type "$MR_REPO")" in
+ svn) svn diff "$@";;
+ git)
+ case "$(get_git_repo_type "$MR_REPO")" in
+ bare) error "cannot diff in bare git repositories";;
+ fake-bare) error "diff does not work for fake bare git repositories (yet).";;
+ esac
git diff "$@"
- elif [ -d "$MR_REPO"/.bzr ]; then
- bzr diff "$@"
- elif [ -d "$MR_REPO"/CVS ]; then
- cvs diff "$@"
- else
- error "unknown repo type"
- fi
+ ;;
+ bzr) bzr diff "$@";;
+ CVS) cvs diff "$@";;
+ hg) hg diff "$@";;
+ darcs) darcs diff "$@";;
+ *) error "unknown repo type";;
+ esac
+
log =
- if [ -d "$MR_REPO"/.svn ]; then
- svn log"$@"
- elif [ -d "$MR_REPO"/.git ]; then
- git log "$@"
- elif [ -d "$MR_REPO"/.bzr ]; then
- bzr log "$@"
- elif [ -d "$MR_REPO"/CVS ]; then
- cvs log "$@"
- else
- error "unknown repo type"
- fi
+ case "$(get_repo_type "$MR_REPO")" in
+ svn) svn log"$@";;
+ git) git log "$@";;
+ bzr) bzr log "$@";;
+ CVS) cvs log "$@";;
+ hg) hg log "$@";;
+ darcs) darcs changes "$@";;
+ *) error "unknown repo type";;
+ esac
+
register =
if [ -n "$1" ]; then
cd "$1"
fi
- basedir="$(basename $(pwd))"
- if [ -d .svn ]; then
+ basedir="$(basename $PWD)"
+ case "$(get_repo_type .)" in
+ svn)
url=$(LANG=C svn info . | grep -i ^URL: | cut -d ' ' -f 2)
if [ -z "$url" ]; then
error "cannot determine svn url"
fi
echo "Registering svn url: $url in $MR_CONFIG"
- mr -c "$MR_CONFIG" config "$(pwd)" checkout="svn co $url $basedir"
- elif [ -d .git ]; then
- url=$(LANG=C git-config --get remote.origin.url)
+ mr -c "$MR_CONFIG" config "$PWD" checkout="svn co $url $basedir"
+ ;;
+ git)
+ local repo_type; repo_type="$(get_git_repo_type .)"
+ local config;
+ case "$repo_type" in
+ non-bare) config=.git/config;;
+ bare|fake-bare) config=config;;
+ esac
+ url="$(LANG=C GIT_CONFIG="$config" git-config --get remote.origin.url)"
if [ -z "$url" ]; then
error "cannot determine git url"
fi
- echo "Registering git url: $url in $MR_CONFIG"
- mr -c "$MR_CONFIG" config "$(pwd)" checkout="git clone $url $basedir"
- elif [ -d .bzr ]; then
+ case "$repo_type" in
+ bare|fake-bare)
+ # this seems like a bare repo as it has no
+ # worktree.
+ local work_tree
+ work_tree="$(git-config --get core.worktree)" || :
+ if [ ! -d "$work_tree" ]; then
+ error "git worktree $work_tree does not exist"
+ fi
+ suffix=" (with worktree $work_tree)"
+ mr -c "$MR_CONFIG" config "$PWD" \
+ lib="GIT_WORK_TREE=$work_tree; export GIT_WORK_TREE"
+ ;;
+ esac
+ echo "Registering git url: $url in $MR_CONFIG${suffix:-}"
+ mr -c "$MR_CONFIG" config "$PWD" checkout="git clone $url $basedir"
+ ;;
+ bzr)
url=$(cat .bzr/branch/parent)
if [ -z "$url" ]; then
error "cannot determine bzr url"
fi
echo "Registering bzr url: $url in $MR_CONFIG"
- mr -c "$MR_CONFIG" config "$(pwd)" checkout="bzr clone $url $basedir"
- else
- error "unable to register this repo type"
- fi
+ mr -c "$MR_CONFIG" config "$PWD" checkout="bzr clone $url $basedir"
+ ;;
+ CVS)
+ repo=$(cat CVS/Repository)
+ root=$(cat CVS/Root)
+ if [ -z "$root" ]; then
+ error "cannot determine cvs root"
+ fi
+ echo "Registering cvs repository $repo at root $root"
+ mr -c "$MR_CONFIG" config "$PWD" \
+ checkout="cvs -d '$root' co -d $basedir $repo"
+ ;;
+ hg)
+ url=$(hg showconfig paths.default)
+ echo "Registering mercurial repo url: $url in $MR_CONFIG"
+ mr -c "$MR_CONFIG" config "$PWD" \
+ checkout="hg clone $url $basedir"
+ ;;
+ darcs)
+ url=$(cat _darcs/prefs/defaultrepo)
+ echo "Registering darcs repository $url in $MR_CONFIG"
+ mr -c "$MR_CONFIG" config "$PWD" \
+ checkout="darcs get $url $basedir"
+ ;;
+ *) error "unable to register this repo type";;
+ esac
+
help =
if [ ! -e "$MR_PATH" ]; then
error "cannot find program path"
T = echo "I pity the fool."
right = echo "Not found."
#}}}
+
+# vim:sw=8:sts=0:ts=8:noet