]>
git.madduck.net Git - code/myrepos.git/blobdiff - mr
madduck's git repository
Every one of the projects in this repository is available at the canonical
URL git://git.madduck.net/madduck/pub/<projectpath> — see
each project's metadata for the exact URL.
All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@ git. madduck. net .
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
SSH access, as well as push access can be individually
arranged .
If you use my repositories frequently, consider adding the following
snippet to ~/.gitconfig and using the third clone URL listed for each
project:
[url "git://git.madduck.net/madduck/"]
insteadOf = madduck:
B<mr> [options] commit [-m "message"]
B<mr> [options] commit [-m "message"]
+B<mr> [options] record [-m "message"]
+
B<mr> [options] diff
B<mr> [options] log
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
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
-res pository. 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.
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
=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.
The optional -m parameter allows specifying a commit message.
-Actions can be abbreviated to any unambiguous subsc tring, so
-"mr st" is equivilant to "mr status", and "mr up" is equivila nt to "mr
+Actions can be abbreviated to any unambiguous substring, so
+"mr st" is equivalent to "mr status", and "mr up" is equivale nt to "mr
update"
Additional parameters can be passed to most commands, and are passed on
update"
Additional parameters can be passed to most commands, and are passed on
=item -s
Expand the statistics line displayed at the end to include information
about exactly which repositories failed and were skipped, if any.
=item -s
Expand the statistics line displayed at the end to include information
about exactly which repositories failed and were skipped, if any.
+=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.
+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.
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
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. (Note that you can use environment variables in section names; they
+will be passed through the shell for expansion. For example,
+"[$HOSTNAME]", or "[${HOSTNAME}foo]")
Within a section, each parameter defines a shell command to run to handle a
given action. mr contains default handlers for "update", "status",
Within a section, each parameter defines a shell command to run to handle a
given action. mr contains default handlers for "update", "status",
(included in mr's built-in library) to skip updating the repo unless it's
been at least 12 hours since the last update.
(included in mr's built-in library) to skip updating the repo unless it's
been at least 12 hours since the last update.
- skip = test $(whoami) != joey
+ skip = test `whoami` != joey
skip = [ "$1" = update ] && ! hours_since "$1" 12
=item order
skip = [ "$1" = update ] && ! hours_since "$1" 12
=item order
# configurables
my $config_overridden=0;
my $verbose=0;
# configurables
my $config_overridden=0;
my $verbose=0;
+my $interactive=0;
+my $max_depth;
my $no_chdir=0;
my $jobs=1;
my $directory=getcwd();
my $no_chdir=0;
my $jobs=1;
my $directory=getcwd();
+$ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
# globals :-(
my %config;
# globals :-(
my %config;
sub rcs_test { #{{{
my ($action, $dir, $topdir, $subdir) = @_;
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 {
my $test="set -e\n";
foreach my $rcs_test (
sort {
return undef;
}
if (! length $rcs) {
return undef;
}
if (! length $rcs) {
+ return $rcs{$dir}= undef;
+ return $rcs{$dir}=$rcs ;
}
} #}}}
sub findcommand { #{{{
}
} #}}}
sub findcommand { #{{{
- my ($action, $dir, $topdir, $subdir) = @_;
+ my ($action, $dir, $topdir, $subdir, $is_checkout ) = @_;
if (exists $config{$topdir}{$subdir}{$action}) {
return $config{$topdir}{$subdir}{$action};
}
if (exists $config{$topdir}{$subdir}{$action}) {
return $config{$topdir}{$subdir}{$action};
}
+ if ($is_checkout) {
+ return undef;
+ }
+
my $rcs=rcs_test(@_);
if (defined $rcs &&
my $rcs=rcs_test(@_);
if (defined $rcs &&
$ENV{MR_CONFIG}=$configfiles{$topdir};
my $lib=exists $config{$topdir}{$subdir}{lib} ?
$config{$topdir}{$subdir}{lib}."\n" : "";
$ENV{MR_CONFIG}=$configfiles{$topdir};
my $lib=exists $config{$topdir}{$subdir}{lib} ?
$config{$topdir}{$subdir}{lib}."\n" : "";
+ my $is_checkout=($action eq 'checkout');
+
+ $ENV{MR_REPO}=$dir;
- if ($action eq 'checkout' ) {
if (-d $dir) {
print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
return SKIPPED;
if (-d $dir) {
print "mr $action: $dir already exists, skipping checkout\n" if $verbose;
return SKIPPED;
- $ENV{MR_REPO}=$dir;
- my $skiptest=findcommand("skip", $dir, $topdir, $subdir);
- my $command=findcommand($action, $dir, $topdir, $subdir);
+ my $skiptest=findcommand("skip", $dir, $topdir, $subdir, $is_checkout);
+ my $command=findcommand($action, $dir, $topdir, $subdir, $is_checkout);
if (defined $skiptest) {
my $test="set -e;".$lib.
if (defined $skiptest) {
my $test="set -e;".$lib.
- if ($action eq 'checkout' && ! -d $dir) {
+ if ($is_checkout && ! -d $dir) {
print "mr $action: creating parent directory $dir\n" if $verbose;
system("mkdir", "-p", $dir);
}
print "mr $action: creating parent directory $dir\n" if $verbose;
system("mkdir", "-p", $dir);
}
- 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) {
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\/?//;
}
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 ".
}
$command="set -e; ".$lib.
"my_action(){ $command\n }; my_action ".
my @out;
my $running=0;
while (@fhs or @repos) {
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);
$running++;
my $repo = shift @repos;
pipe(my $outfh, CHILD_STDOUT);
print "\n";
}
elsif ($ret == FAILED) {
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";
}
push @failed, $dir;
print "\n";
}
showstat($#ok+1, "ok", "ok"),
showstat($#failed+1, "failed", "failed"),
showstat($#skipped+1, "skipped", "skipped"),
showstat($#ok+1, "ok", "ok"),
showstat($#failed+1, "failed", "failed"),
showstat($#skipped+1, "skipped", "skipped"),
if ($stats) {
if (@skipped) {
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";
}
if (@failed) {
print STDERR "mr $action: (failed: ".join(" ", @failed).")\n";
my $d=$directory;
$dir.="/" unless $dir=~/\/$/;
$d.="/" unless $d=~/\/$/;
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/;
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) {
push @repos, [$dir, $topdir, $subdir];
}
if (! @repos) {
+sub expandenv { #{{{
+ my $val=shift;
+
+
+ if ($val=~/\$/) {
+ $val=`echo "$val"`;
+ chomp $val;
+ }
+
+ return $val;
+} #}}}
+
my %loaded;
sub loadconfig { #{{{
my $f=shift;
my %loaded;
sub loadconfig { #{{{
my $f=shift;
chomp;
next if /^\s*\#/ || /^\s*$/;
if (/^\[([^\]]*)\]\s*$/) {
chomp;
next if /^\s*\#/ || /^\s*$/;
if (/^\[([^\]]*)\]\s*$/) {
+ $section=expandenv($1) ;
}
elsif (/^(\w+)\s*=\s*(.*)/) {
my $parameter=$1;
}
elsif (/^(\w+)\s*=\s*(.*)/) {
my $parameter=$1;
+ $section=expandenv($1) ;
+ if (!$jobs || $jobs > 1) {
mrs($action, selectrepos());
}
else {
mrs($action, selectrepos());
}
else {
}
$ENV{MR_REPO}=getcwd();
}
$ENV{MR_REPO}=getcwd();
- my $command=findcommand("register", $ENV{MR_REPO}, $directory, 'DEFAULT');
+ my $command=findcommand("register", $ENV{MR_REPO}, $directory, 'DEFAULT', 0 );
if (! defined $command) {
die "mr register: unknown repository type\n";
}
if (! defined $command) {
die "mr register: unknown repository type\n";
}
"d|directory=s" => sub { $directory=abs_path($_[1]) },
"c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
"v|verbose" => \$verbose,
"d|directory=s" => sub { $directory=abs_path($_[1]) },
"c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
"v|verbose" => \$verbose,
- "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".
);
if (! $result || @ARGV < 1) {
die("Usage: mr [-d directory] action [params ...]\n".
- $ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
-
# This can happen if it's run in a directory that was removed
# or other strangeness.
if (! defined $directory) {
# This can happen if it's run in a directory that was removed
# or other strangeness.
if (! defined $directory) {
if [ -z "$flagfile" ]; then
error "cannot determine flag filename"
fi
if [ -z "$flagfile" ]; then
error "cannot determine flag filename"
fi
- delta=$(perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile")
+ delta=`perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile"`
if [ "$delta" -lt "$2" ]; then
exit 0
else
if [ "$delta" -lt "$2" ]; then
exit 0
else
git_bare_test =
test -d "$MR_REPO"/refs/heads && test -d "$MR_REPO"/refs/tags &&
test -d "$MR_REPO"/objects && test -f "$MR_REPO"/config &&
git_bare_test =
test -d "$MR_REPO"/refs/heads && test -d "$MR_REPO"/refs/tags &&
test -d "$MR_REPO"/objects && test -f "$MR_REPO"/config &&
- test "$(GIT_CONFIG="$MR_REPO"/config git config --get core.bare) " = true
+ test "`GIT_CONFIG="$MR_REPO"/config git config --get core.bare` " = true
svn_update = svn update "$@"
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_update = bzr merge "$@"
cvs_update = cvs update "$@"
hg_update = hg pull "$@" && hg update "$@"
bzr_status = bzr status "$@"
cvs_status = cvs status "$@"
hg_status = hg status "$@"
bzr_status = bzr status "$@"
cvs_status = cvs status "$@"
hg_status = hg status "$@"
-darcs_status = darcs whatsnew -ls "$@"
+darcs_status = darcs whatsnew -ls "$@" || true
svn_commit = svn commit "$@"
git_commit = git commit -a "$@" && git push --all
bzr_commit = bzr commit "$@" && bzr push
cvs_commit = cvs commit "$@"
hg_commit = hg commit -m "$@" && hg push
svn_commit = svn commit "$@"
git_commit = git commit -a "$@" && git push --all
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 "$@"
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 "$@"
svn_log = svn log "$@"
git_log = git log "$@"
git_bare_log = git log "$@"
svn_register =
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 =
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 =
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
echo "Registering bzr url: $url in $MR_CONFIG"
mr -c "$MR_CONFIG" config "`pwd`" checkout="bzr clone '$url' '$MR_REPO'"
cvs_register =
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' '$MR_REPO'"
cvs_register =
- repo=$(cat CVS/Repository)
- root=$(cat CVS/Root)
+ 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 '$MR_REPO' '$repo'"
hg_register =
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 '$MR_REPO' '$repo'"
hg_register =
- url=$(hg showconfig paths.default)
+ url=`hg showconfig paths.default`
echo "Registering mercurial repo url: $url in $MR_CONFIG"
mr -c "$MR_CONFIG" config "`pwd`" checkout="hg clone '$url' '$MR_REPO'"
darcs_register =
echo "Registering mercurial repo url: $url in $MR_CONFIG"
mr -c "$MR_CONFIG" config "`pwd`" checkout="hg clone '$url' '$MR_REPO'"
darcs_register =
- url=$(cat _darcs/prefs/defaultrepo)
+ url=`cat _darcs/prefs/defaultrepo`
echo "Registering darcs repository $url in $MR_CONFIG"
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'"
- 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 [ -z "$url" ]; then
error "cannot determine git url"
fi
if [ ! -e "$MR_PATH" ]; then
error "cannot find program path"
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"