=head1 FILES
B<mr> is configured by .mrconfig files. It starts by reading the .mrconfig
-file in your home directory. Each repository specified in a .mrconfig file
-can also have its own .mrconfig file in its root directory that can
-optionally be used as well. So you could have a ~/.mrconfig that registers a
-repository ~/src, that itself contains a ~/src/.mrconfig file, that in turn
-registers several additional repositories.
+file in your home directory, and this can in turn chain load .mrconfig files
+from repositories.
+
+Here is an example .mrconfig file:
+
+ [src]
+ checkout = svn co svn://svn.example.com/src/trunk src
+ chain = true
+
+ [src/linux-2.6]
+ checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
The .mrconfig file uses a variant of the INI file format. Lines starting with
"#" are comments. Lines ending with "\" are continued on to the next line.
-The "default" section allows setting default values for the sections that
+The "DEFAULT" section allows setting default values for the sections that
come after it.
-The "alias" section allows adding aliases for actions. Each parameter
+The "ALIAS" section allows adding aliases for actions. Each parameter
is an alias, and its value is the action to use.
All other sections specify where each repository is located, relative to the
The "MR_REPO" environment variable is set to the path to the top of the
repository.
-There are three special parameters. If the "skip" parameter is set and
-its command returns nonzero, then B<mr> will skip acting on that repository.
+A few parameters have special meanings:
+
+=over 4
+
+=item skip
+
+If the "skip" parameter is set and its command returns nonzero, then B<mr>
+will skip acting on that repository.
+
+=item chain
+
If the "chain" parameter is set and its command returns nonzero, then B<mr>
will try to load a .mrconfig file from the root of the repository. (You
-should avoid chaining from repositories with untrusted committers.) The
-"lib" parameter can specify some shell code that will be run before each
-command, this can be a useful way to define shell functions for other commands
-to use.
+should avoid chaining from repositories with untrusted committers.)
-For example:
+=item lib
- [src]
- checkout = svn co svn://svn.example.com/src/trunk src
- chain = true
-
- [src/linux-2.6]
- skip = small
- checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+The "lib" parameter can specify some shell code that will be run before each
+command, this can be a useful way to define shell functions for other commands
+to use.
- [default]
- lib = \
- small() {
- case "$(hostname)" in; \
- slug|snail); \
- return 0; ;; ; \
- esac; \
- return 1; \
- }
+=back
=head1 AUTHOR
Getopt::Long::Configure("no_permute");
my $result=GetOptions(
- "d=s" => sub { $directory=abs_path($_[1]) },
- "c=s" => \$config,
- "v" => \$verbose,
+ "d|directory=s" => sub { $directory=abs_path($_[1]) },
+ "c|config=s" => \$config,
+ "verbose" => \$verbose,
);
if (! $result || @ARGV < 1) {
die("Usage: mr [-d directory] action [params ...]\n".
# alias expansion and command stemming
my $action=shift @ARGV;
+if (exists $alias{$action}) {
+ $action=$alias{$action};
+}
if (! exists $knownactions{$action}) {
- if (exists $alias{$action}) {
- $action=$alias{$action};
+ my @matches = grep { /^\Q$action\E/ }
+ keys %knownactions, keys %alias;
+ if (@matches == 1) {
+ $action=$matches[0];
+ }
+ elsif (@matches == 0) {
+ die "mr: unknown action \"$action\" (known actions: ".
+ join(", ", sort keys %knownactions).")\n";
}
else {
- my @matches = grep { /^\Q$action\E/ }
- keys %knownactions, keys %alias;
- if (@matches == 1) {
- $action=$matches[0];
- }
- elsif (@matches == 0) {
- die "mr: unknown action \"$action\" (known actions: ".
- join(", ", sort keys %knownactions).")\n";
- }
- else {
- die "mr: ambiguous action \"$action\" (matches: ".
- join(", ", @matches).")\n";
- }
+ die "mr: ambiguous action \"$action\" (matches: ".
+ join(", ", @matches).")\n";
}
}
if ($action eq 'help') {
- exec($config{''}{default}{help});
+ exec($config{''}{DEFAULT}{help});
}
# work out what repos to act on
my $nochdir=0;
foreach my $topdir (sort keys %config) {
foreach my $subdir (sort keys %{$config{$topdir}}) {
- next if $subdir eq 'default';
+ next if $subdir eq 'DEFAULT';
my $dir=$topdir.$subdir;
next if $dir ne $directory && $dir !~ /^\Q$directory\E\//;
push @repos, [$dir, $topdir, $subdir];
# 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';
+ next if $subdir eq 'DEFAULT';
my $dir=$topdir.$subdir;
my $d=$directory;
$dir.="/" unless $dir=~/\/$/;
}
$ENV{MR_REPO}=$dir;
- if (! $nochdir && ! chdir($dir)) {
- print STDERR "mr $action: failed to chdir to $dir: $!\n";
- push @skipped, $dir;
- }
if (exists $config{$topdir}{$subdir}{skip}) {
my $test="set -e;".$lib.$config{$topdir}{$subdir}{skip};
return;
}
}
-
- if (! exists $config{$topdir}{$subdir}{$action}) {
+
+ if (! $nochdir && ! chdir($dir)) {
+ print STDERR "mr $action: failed to chdir to $dir: $!\n";
+ push @failed, $dir;
+ }
+ elsif (! exists $config{$topdir}{$subdir}{$action}) {
print STDERR "mr $action: no defined $action command for $topdir$subdir, skipping\n";
push @skipped, $dir;
}
my $parent=$dir;
while ($parent=~s/^(.*)\/[^\/]+\/?$/$1/) {
if (exists $config{$parent} &&
- exists $config{$parent}{default}) {
- $config{$dir}{default}={ %{$config{$parent}{default}} };
+ exists $config{$parent}{DEFAULT}) {
+ $config{$dir}{DEFAULT}={ %{$config{$parent}{DEFAULT}} };
last;
}
}
if (! defined $section) {
die "$f line $.: parameter ($parameter) not in section\n";
}
- if ($section ne 'alias' &&
+ if ($section ne 'ALIAS' &&
! exists $config{$dir}{$section} &&
- exists $config{$dir}{default}) {
+ exists $config{$dir}{DEFAULT}) {
# copy in defaults
- $config{$dir}{$section}={ %{$config{$dir}{default}} };
+ $config{$dir}{$section}={ %{$config{$dir}{DEFAULT}} };
}
- if ($section eq 'alias') {
+ if ($section eq 'ALIAS') {
$alias{$parameter}=$value;
}
elsif ($parameter eq 'lib') {
$config{$dir}{$section}{$parameter}=$value;
$knownactions{$parameter}=1;
if ($parameter eq 'chain' &&
- length $dir && $section ne "default" &&
+ length $dir && $section ne "DEFAULT" &&
-e $dir.$section."/.mrconfig" &&
system($value) >> 8 == 0) {
push @toload, $dir.$section."/.mrconfig";
# Finally, some useful actions that mr knows about by default.
# These can be overridden in ~/.mrconfig.
__DATA__
-[alias]
+[ALIAS]
co = checkout
ci = commit
ls = list
-[default]
+
+[DEFAULT]
lib = \
error() { \
echo "mr: $@" >&2; \
exit 1; \
}
+
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 \
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 \
svn commit "$@"; \
elif [ -d "$MR_REPO"/.git ]; then \
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 \
svn diff "$@"; \
elif [ -d "$MR_REPO"/.git ]; then \
git diff "$@"; \
+ elif [ -d "$MR_REPO"/.bzr ]; then \
+ bzr diff "$@"; \
elif [ -d "$MR_REPO"/CVS ]; then \
cvs diff "$@"; \
else \
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
list = true
+
help = \
if [ ! -e "$MR_PATH" ]; then \
error "cannot find program path";\
fi; \
(pod2man -c mr "$MR_PATH" | man -l -) || \
error "pod2man or man failed"
+
+ed = echo "A horse is a horse, of course, of course.."
+T = echo "I pity the fool."