B<mr> [options] status
-B<mr> [options] commit -m "message"
+B<mr> [options] commit [-m "message"]
B<mr> [options] action [params ...]
perform other actions on all of the repositories at once.
Any mix of revision control systems can be used with B<mr>, and you can
-define arbitrary actions like "update", "checkout", or "commit".
+define arbitrary actions for commands like "update", "checkout", or "commit".
+
+The predefined commands should be fairly familiar to users of any revision
+control system:
+
+=over 4
+
+=item checkout
+
+Checks out all the registered repositories that are not already checked
+out.
+
+=item update
+
+Updates each registered repository from its configured remote repository.
+
+If a repository isn't checked out yet, it will first check it out.
+
+=item status
+
+Displays a status report for each registered repository, showing what
+uncommitted changes are present in the repository.
+
+=item commit
+
+Commits changes to each registered repository. (By default, changes
+are pushed to the remote repository too, when using distributed systems
+like git.)
+
+The optional -m parameter allows specifying a commit message.
+
+=back
+
+Actions can be abbreviated to any unambiguous subsctring, so
+"mr st" is equivilant to "mr status".
=head1 OPTIONS
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
-environment, and B<mr> cds into the repository directory before running
-them, except for the "checkout" command, which is run in the parent of the
-repository directory, since the repository isn't checked out yet.
+environment, where any additional parameters you pass are available in
+"$@". B<mr> cds into the repository directory before running
+a command, except for the "checkout" command, which is run in the parent
+of the repository directory, since the repository isn't checked out yet.
There are two special parameters. If the "skip" parameter is set and
its command returns nonzero, then B<mr> will skip acting on that repository.
my $config="$ENV{HOME}/.mrconfig";
my $verbose=0;
my %config;
+my %knownactions;
Getopt::Long::Configure("no_permute");
my $result=GetOptions(
if (! $result || @ARGV < 1) {
die("Usage: mr [-d directory] action [params ...]\n");
}
-my $action=shift @ARGV;
loadconfig(\*DATA);
loadconfig($config);
#use Data::Dumper;
#print Dumper(\%config);
-my (@failures, @successes, @skipped);
+my $action=shift @ARGV;
+if (! $knownactions{$action}) {
+ my @matches = grep { /^\Q$action\E/ } keys %knownactions;
+ if (@matches == 1) {
+ $action=$matches[0];
+ }
+ else {
+ die "mr: ambiguous action \"$action\" (matches @matches)\n";
+ }
+}
+
+my (@failed, @successful, @skipped);
my $first=1;
foreach my $topdir (sort keys %config) {
foreach my $subdir (sort keys %{$config{$topdir}}) {
my $dir=$topdir.$subdir;
if (defined $directory &&
+ $dir ne $directory &&
$dir !~ /^\Q$directory\E\//) {
print "mr $action: $dir skipped per -d parameter ($directory)\n" if $verbose;
push @skipped, $dir;
}
}
- if ($action eq 'checkout') {
- if (-e $dir) {
- print "mr $action: $dir already exists, skipping checkout\n";
- push @skipped, $dir;
- next;
- }
- $dir=~s/^(.*)\/[^\/]+\/?$/$1/;
- }
- if (! chdir($dir)) {
- print STDERR "mr $action: failed to chdir to $dir: $!\n";
+ action($action, $dir, $topdir, $subdir);
+
+ }
+}
+
+sub action {
+ my ($action, $dir, $topdir, $subdir) = @_;
+
+ if ($action eq 'checkout') {
+ if (-d $dir) {
+ print "mr $action: $dir already exists, skipping checkout\n";
push @skipped, $dir;
+ next;
}
- elsif (! exists $config{$topdir}{$subdir}{$action}) {
- print STDERR "mr $action: no defined $action command for $topdir$subdir, skipping\n";
- push @skipped, $dir;
+ $dir=~s/^(.*)\/[^\/]+\/?$/$1/;
+ }
+ if ($action eq 'update') {
+ if (! -d $dir) {
+ return action("checkout", $dir, $topdir, $subdir);
}
- else {
- print "mr $action: in $dir\n";
- my $command="set -e; my_action(){ $config{$topdir}{$subdir}{$action} ; }; my_action @ARGV";
- my $ret=system($command);
- if ($ret != 0) {
- print STDERR "mr $action: failed to run: $command\n" if $verbose;
- push @failures, $topdir.$subdir;
- if ($ret >> 8 != 0) {
- print STDERR "mr $action: command failed\n";
- }
- elsif ($ret != 0) {
- print STDERR "mr $action: command died ($ret)\n";
- }
+ }
+
+ if (! chdir($dir)) {
+ print STDERR "mr $action: failed to chdir to $dir: $!\n";
+ push @skipped, $dir;
+ }
+ elsif (! exists $config{$topdir}{$subdir}{$action}) {
+ print STDERR "mr $action: no defined $action command for $topdir$subdir, skipping\n";
+ push @skipped, $dir;
+ }
+ else {
+ print "mr $action: in $dir\n";
+ my $command="set -e; my_action(){ $config{$topdir}{$subdir}{$action} ; }; my_action ".
+ join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
+ my $ret=system($command);
+ if ($ret != 0) {
+ print STDERR "mr $action: failed to run: $command\n" if $verbose;
+ push @failed, $topdir.$subdir;
+ if ($ret >> 8 != 0) {
+ print STDERR "mr $action: command failed\n";
}
- else {
- push @successes, $dir;
+ elsif ($ret != 0) {
+ print STDERR "mr $action: command died ($ret)\n";
}
}
+ else {
+ push @successful, $dir;
+ }
}
}
return;
}
print "\nmr $action: finished (".join("; ",
- showstat($#successes+1, "success", "successes"),
- showstat($#failures+1, "failure", "failures"),
+ showstat($#successful+1, "successful", "successful"),
+ showstat($#failed+1, "failed", "failed"),
showstat($#skipped+1, "skipped", "skipped"),
).")\n";
-exit @failures ? 1 : 0;
+exit @failed ? 1 : 0;
my %loaded;
sub loadconfig {
print "mr: loading config $f\n" if $verbose;
open($in, "<", $f) || die "mr: open $f: $!\n";
($dir)=$f=~/^(.*\/)[^\/]+$/;
+ if (! defined $dir) {
+ $dir=".";
+ }
$dir=abs_path($dir)."/";
# copy in defaults from first parent
$config{$dir}{$section}={ %{$config{$dir}{default}} };
}
$config{$dir}{$section}{$parameter}=$value;
+ $knownactions{$parameter}=1;
}
else {
die "$f line $.: parse error\n";
elif [ -d .git ]; then \
git pull origin master; \
else \
- echo "mr update: unknown RCS"; \
+ echo "mr update: unknown repo type"; \
exit 1; \
fi
status = \
elif [ -d .git ]; then \
git status || true; \
else \
- echo "mr status: unknown RCS"; \
+ echo "mr status: unknown repo type"; \
exit 1; \
fi
commit = \
if [ -d .svn ]; then \
svn commit "$@"; \
elif [ -d .git ]; then \
- git commit -a "$@"; \
+ git commit -a "$@" \
else \
- echo "mr commit: unknown RCS"; \
+ echo "mr commit: unknown repo type"; \
exit 1; \
fi