B<mr> [options] commit [-m "message"]
+B<mr> [options] record [-m "message"]
+
B<mr> [options] diff
B<mr> [options] log
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,
+repository. It supports any combination of subversion, git, cvs, mecurial,
bzr and darcs repositories, and support for other revision control systems can
easily be added.
=item commit (or ci)
Commits changes to each repository. (By default, changes are pushed to the
-remote repository too, when using distributed systems like git.)
+remote repository too, when using distributed systems like git. If you
+don't like this default, you can change it in your .mrconfig, or use record
+instead.)
+
+The optional -m parameter allows specifying a commit message.
+
+=item record
+
+Records changes to the local repository, but does not push them to the
+remote repository. Only supported for distributed revision control systems.
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", and "mr up" is equivilant to "mr
+Actions can be abbreviated to any unambiguous substring, so
+"mr st" is equivalent to "mr status", and "mr up" is equivalent to "mr
update"
Additional parameters can be passed to most commands, and are passed on
Be verbose.
+=item -q
+
+Be quiet.
+
=item -s
Expand the statistics line displayed at the end to include information
about exactly which repositories failed and were skipped, if any.
-=item -n
+=item -i
+
+Interactive mode. If a repository fails to be processed, a subshell will be
+started which you can use to resolve or investigate the problem. Exit the
+subshell to continue the mr run.
+
+=item -n [number]
-Just operate on the repository for the current directory, do not
-recurse into deeper repositories.
+If no number if specified, just operate on the repository for the current
+directory, do not recurse into deeper repositories.
-=item -j number
+If a number is specified, will recurse into repositories at most that many
+subdirectories deep. For example, with -n 2 it would recurse into ./src/foo,
+but not ./src/packages/bar.
-Run the specified number of jobs in parallel. This can greatly speed up
-operations such as updates. It is not recommended for interactive
-operations.
+=item -j [number]
+
+Run the specified number of jobs in parallel, or an unlimited number of jobs
+with no number specified. This can greatly speed up operations such as updates.
+It is not recommended for interactive operations.
+
+Note that running more than 10 jobs at a time is likely to run afoul of
+ssh connection limits. Running between 3 and 5 jobs at a time will yeild
+a good speedup in updates without loading the machine too much.
=back
# configurables
my $config_overridden=0;
my $verbose=0;
+my $quiet=0;
my $stats=0;
-my $no_recurse=0;
+my $interactive=0;
+my $max_depth;
my $no_chdir=0;
my $jobs=1;
my $directory=getcwd();
main();
+my %rcs;
sub rcs_test { #{{{
my ($action, $dir, $topdir, $subdir) = @_;
+ if (exists $rcs{$dir}) {
+ return $rcs{$dir};
+ }
+
my $test="set -e\n";
foreach my $rcs_test (
sort {
return undef;
}
if (! length $rcs) {
- return undef;
+ return $rcs{$dir}=undef;
}
else {
- return $rcs;
+ return $rcs{$dir}=$rcs;
}
} #}}}
return FAILED;
}
else {
- print STDERR "mr $action: no defined $action command for $rcs repository $topdir$subdir, skipping\n";
+ print STDERR "mr $action: no defined action for $rcs repository $topdir$subdir, skipping\n";
return SKIPPED;
}
}
else {
if (! $no_chdir) {
- print "mr $action: $topdir$subdir\n";
+ print "mr $action: $topdir$subdir\n" unless $quiet;
}
else {
my $s=$directory;
$s=~s/^\Q$topdir$subdir\E\/?//;
- print "mr $action: $topdir$subdir (in subdir $s)\n";
+ print "mr $action: $topdir$subdir (in subdir $s)\n" unless $quiet;
}
$command="set -e; ".$lib.
"my_action(){ $command\n }; my_action ".
my @out;
my $running=0;
while (@fhs or @repos) {
- while ($running < $jobs && @repos) {
+ while ((!$jobs || $running < $jobs) && @repos) {
$running++;
my $repo = shift @repos;
pipe(my $outfh, CHILD_STDOUT);
print "\n";
}
elsif ($ret == FAILED) {
+ if ($interactive) {
+ chdir($dir) unless $no_chdir;
+ print STDERR "mr: Starting interactive shell. Exit shell to continue.\n";
+ system((getpwuid($<))[8]);
+ }
push @failed, $dir;
print "\n";
}
showstat($#ok+1, "ok", "ok"),
showstat($#failed+1, "failed", "failed"),
showstat($#skipped+1, "skipped", "skipped"),
- ).")\n";
+ ).")\n" unless $quiet;
if ($stats) {
if (@skipped) {
- print "mr $action: (skipped: ".join(" ", @skipped).")\n";
+ print "mr $action: (skipped: ".join(" ", @skipped).")\n" unless $quiet;
}
if (@failed) {
print STDERR "mr $action: (failed: ".join(" ", @failed).")\n";
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/;
+ if (defined $max_depth) {
+ my @a=split('/', $dir);
+ my @b=split('/', $d);
+ do { } while (@a && @b && shift(@a) eq shift(@b));
+ next if @a > $max_depth || @b > $max_depth;
+ }
push @repos, [$dir, $topdir, $subdir];
}
if (! @repos) {
register(@ARGV);
}
- if ($jobs > 1) {
+ if (!$jobs || $jobs > 1) {
mrs($action, selectrepos());
}
else {
"d|directory=s" => sub { $directory=abs_path($_[1]) },
"c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
"v|verbose" => \$verbose,
+ "q|quiet" => \$quiet,
"s|stats" => \$stats,
- "n|no-recurse" => \$no_recurse,
- "j|jobs=i" => \$jobs,
+ "i|interactive" => \$interactive,
+ "n|no-recurse:i" => \$max_depth,
+ "j|jobs:i" => \$jobs,
);
if (! $result || @ARGV < 1) {
die("Usage: mr [-d directory] action [params ...]\n".
test "`GIT_CONFIG="$MR_REPO"/config git config --get core.bare`" = true
svn_update = svn update "$@"
-git_update = if [ "$@" ]; then git pull "$@"; else git pull -t origin master; fi
+git_update = git pull "$@"
bzr_update = bzr merge "$@"
cvs_update = cvs update "$@"
hg_update = hg pull "$@" && hg update "$@"
bzr_commit = bzr commit "$@" && bzr push
cvs_commit = cvs commit "$@"
hg_commit = hg commit -m "$@" && hg push
-darcs_commit = darcs commit -a -m "$@" && darcs push -a
+darcs_commit = darcs record -a -m "$@" && darcs push -a
+
+git_record = git commit -a "$@"
+bzr_record = bzr commit "$@"
+hg_record = hg commit -m "$@"
+darcs_record = darcs record -a -m "$@"
svn_diff = svn diff "$@"
git_diff = git diff "$@"
bzr_diff = bzr diff "$@"
cvs_diff = cvs diff "$@"
hg_diff = hg diff "$@"
-darcs_diff = darcs diff "$@"
+darcs_diff = darcs diff -u "$@"
svn_log = svn log "$@"
git_log = git log "$@"
git_bare_log = git log "$@"
svn_register =
- url=`LANG=C svn info . | grep -i '^URL:' | cut -d ' ' -f 2`
+ url=`LC_ALL=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' '$MR_REPO'"
git_register =
- url="`LANG=C git config --get remote.origin.url`" || true
+ url="`LC_ALL=C git config --get remote.origin.url`" || true
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' '$MR_REPO'"
bzr_register =
- url=`cat .bzr/branch/parent`
+ url="`LC_ALL=C bzr info . | egrep -i 'checkout of branch|parent branch' | awk '{print $NF}'`"
if [ -z "$url" ]; then
error "cannot determine bzr url"
fi
darcs_register =
url=`cat _darcs/prefs/defaultrepo`
echo "Registering darcs repository $url in $MR_CONFIG"
- mr -c "$MR_CONFIG" config "`pwd`" checkout="darcs get '$url'p '$MR_REPO'"
+ mr -c "$MR_CONFIG" config "`pwd`" checkout="darcs get '$url' '$MR_REPO'"
git_bare_register =
- url="`LANG=C GIT_CONFIG=config git config --get remote.origin.url`" || true
+ url="`LC_ALL=C GIT_CONFIG=config git config --get remote.origin.url`" || true
if [ -z "$url" ]; then
error "cannot determine git url"
fi
if [ ! -e "$MR_PATH" ]; then
error "cannot find program path"
fi
- (pod2man -c mr "$MR_PATH" | man -l -) || error "pod2man or man failed"
+ tmp=$(mktemp -t mr.XXXXXXXXXX) || error "mktemp failed"
+ trap "rm -f $tmp" exit
+ pod2man -c mr "$MR_PATH" > "$tmp" || error "pod2man failed"
+ man -l "$tmp" || error "man failed"
list = true
config =