Show the commit log.
-=item list
+=item list (or ls)
List the repositories that mr will act on.
=item -c mrconfig
-Use the specified mrconfig file, instead of looking for on in your home
+Use the specified mrconfig file, instead of looking for one in your home
directory.
=item -v
=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.
-Sections specify where each repository is located, relative to the
-directory that contains the .mrconfig file.
+
+The "DEFAULT" section allows setting default values for the sections that
+come after it.
+
+The "ALIAS" section allows adding aliases for actions. Each parameter
+is an alias, and its value is the action to use.
+
+All other sections add repositories. The section header specifies the
+directory where the repository is located. This is relative to the directory
+that contains the mrconfig file, but you can also choose to use absolute
+paths.
Within a section, each parameter defines a shell command to run to handle a
-given action. Note that these shell commands are run in a "set -e" shell
+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".
+
+Note that these shell commands are run in a "set -e" shell
environment, where any additional parameters you pass are available in
"$@". The "checkout" command is run in the parent of the repository
directory, since the repository isn't checked out yet. All other commands
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.
-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 other commands
-can use.
+A few parameters have special meanings:
-The "default" section allows setting up default handlers for each action,
-and is overridden by the contents of other sections. mr contains default
-handlers for the "update", "status", and "commit" actions, so normally
-you only need to specify what to do for "checkout".
+=over 4
-The "alias" section allows adding aliases for commands. Each parameter
-is an alias, and its value is the command to run.
+=item skip
-For example:
+If the "skip" parameter is set and its command returns nonzero, then B<mr>
+will skip acting on that repository.
- [src]
- checkout = svn co svn://svn.example.com/src/trunk src
- chain = true
+=item chain
- [src/linux-2.6]
- skip = small
- checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+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.)
- [default]
- lib = \
- small() {
- case "$(hostname)" in; \
- slug|snail); \
- return 0; ;; ; \
- esac; \
- return 1; \
- }
+=item lib
+
+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.
+
+=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';
- my $dir=$topdir.$subdir;
- next if $dir ne $directory && $dir !~ /^\Q$directory\E\//;
+ next if $subdir eq 'DEFAULT';
+ my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
+ my $d=$directory;
+ $dir.="/" unless $dir=~/\/$/;
+ $d.="/" unless $d=~/\/$/;
+ 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';
- my $dir=$topdir.$subdir;
+ next if $subdir eq 'DEFAULT';
+ my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
my $d=$directory;
$dir.="/" unless $dir=~/\/$/;
$d.="/" unless $d=~/\/$/;
}
$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 $ret=system($lib.$config{$topdir}{$subdir}{skip});
+ my $test="set -e;".$lib.$config{$topdir}{$subdir}{skip};
+ print "mr $action: running skip test $test\n" if $verbose;
+ my $ret=system($test);
if ($ret >> 8 == 0) {
print "mr $action: $dir skipped per config file\n" if $verbose;
push @skipped, $dir;
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;
}
else {
if (! $nochdir) {
- print "mr $action: $dir\n";
+ print "mr $action: $topdir$subdir\n";
}
else {
- print "mr $action: $dir (in subdir $directory)\n";
+ print "mr $action: $topdir$subdir (in subdir $directory)\n";
}
my $command="set -e; ".$lib.
"my_action(){ $config{$topdir}{$subdir}{$action} ; }; my_action ".
join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
+ print STDERR "mr $action: running $command\n" if $verbose;
my $ret=system($command);
if ($ret != 0) {
- print STDERR "mr $action: failed to run: $command\n" if $verbose;
- push @failed, $topdir.$subdir;
+ print STDERR "mr $action: failed ($ret)\n" if $verbose;
+ push @failed, $dir;
if ($ret >> 8 != 0) {
print STDERR "mr $action: command failed\n";
}
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
-[default]
+ ls = list
+
+[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 \
error "unknown repo type"; \
fi
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
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 \
error "unknown repo type"; \
fi
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 \
error "unknown repo type"; \
fi
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."