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.
5 mr - a Multiple Repository management tool
9 B<mr> [options] checkout
11 B<mr> [options] update
13 B<mr> [options] status
15 B<mr> [options] commit -m "message"
17 B<mr> [options] action [params ...]
21 B<mr> is a Multiple Repository management tool. It allows you to register a
22 set of repositories in a .mrconfig file, and then checkout, update, or
23 perform other actions on all of the repositories at once.
25 Any mix of revision control systems can be used with B<mr>, and you can
26 define arbitrary actions like "update", "checkout", or "commit".
34 Specifies the topmost directory that B<mr> should work in. The default is
35 the current working directory. B<mr> will operate on all registered
36 repositories at or under the directory.
40 Use the specified mrconfig file, instead of looking for on in your home
51 B<mr> is configured by .mrconfig files. It searches for .mrconfig files in
52 your home directory, and in the root directory of each repository specified
53 in a .mrconfig file. So you could have a ~/.mrconfig that registers a
54 repository ~/src, that itself contains a ~/src/.mrconfig file, that in turn
55 registers several additional repositories.
57 The .mrconfig file uses a variant of the INI file format. Lines starting with
58 "#" are comments. Lines ending with "\" are continued on to the next line.
59 Sections specify where each repository is located, relative to the
60 directory that contains the .mrconfig file.
62 Within a section, each parameter defines a shell command to run to handle a
63 given action. Note that these shell commands are run in a "set -e" shell
64 environment, and B<mr> cds into the repository directory before running
65 them, except for the "checkout" command, which is run in the parent of the
66 repository directory, since the repository isn't checked out yet.
68 There are two special parameters. If the "skip" parameter is set and
69 its command returns nonzero, then B<mr> will skip acting on that repository.
71 The "default" section allows setting up default handlers for each action,
72 and is overridden by the contents of other sections. mr contains default
73 handlers for the "update", "status", and "commit" actions, so normally
74 you only need to specify what to do for "checkout".
79 checkout = svn co svn://svn.example.com/src/trunk src
82 # only check this out on kodama
83 skip = test $(hostname) != kodama
84 checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
88 Copyright 2007 Joey Hess <joey@kitenet.net>
90 Licensed under the GNU GPL version 2 or higher.
92 http://kitenet.net/~joey/code/mr/
99 use Cwd qw(getcwd abs_path);
101 my $directory=getcwd();
102 my $config="$ENV{HOME}/.mrconfig";
106 Getopt::Long::Configure("no_permute");
107 my $result=GetOptions(
108 "d=s" => sub { $directory=abs_path($_[1]) },
112 if (! $result || @ARGV < 1) {
113 die("Usage: mr [-d directory] action [params ...]\n");
115 my $action=shift @ARGV;
120 #print Dumper(\%config);
122 my (@failures, @successes, @skipped);
124 foreach my $topdir (sort keys %config) {
125 foreach my $subdir (sort keys %{$config{$topdir}}) {
126 next if $subdir eq 'default';
128 my $dir=$topdir.$subdir;
130 if (defined $directory &&
131 $dir ne $directory &&
132 $dir !~ /^\Q$directory\E\//) {
133 print "mr $action: $dir skipped per -d parameter ($directory)\n" if $verbose;
138 print "\n" unless $first;
141 if (exists $config{$topdir}{$subdir}{skip}) {
142 my $ret=system($config{$topdir}{$subdir}{skip});
143 if ($ret >> 8 == 0) {
144 print "mr $action: $dir skipped per config file\n" if $verbose;
150 if ($action eq 'checkout') {
152 print "mr $action: $dir already exists, skipping checkout\n";
156 $dir=~s/^(.*)\/[^\/]+\/?$/$1/;
159 print STDERR "mr $action: failed to chdir to $dir: $!\n";
162 elsif (! exists $config{$topdir}{$subdir}{$action}) {
163 print STDERR "mr $action: no defined $action command for $topdir$subdir, skipping\n";
167 print "mr $action: in $dir\n";
168 my $command="set -e; my_action(){ $config{$topdir}{$subdir}{$action} ; }; my_action @ARGV";
169 my $ret=system($command);
171 print STDERR "mr $action: failed to run: $command\n" if $verbose;
172 push @failures, $topdir.$subdir;
173 if ($ret >> 8 != 0) {
174 print STDERR "mr $action: command failed\n";
177 print STDERR "mr $action: command died ($ret)\n";
181 push @successes, $dir;
192 return "$count ".($count > 1 ? $plural : $singular);
196 print "\nmr $action: finished (".join("; ",
197 showstat($#successes+1, "success", "successes"),
198 showstat($#failures+1, "failure", "failures"),
199 showstat($#skipped+1, "skipped", "skipped"),
201 exit @failures ? 1 : 0;
211 if (ref $f eq 'GLOB') {
216 # $f might be a symlink
217 my $absf=abs_path($f);
218 if ($loaded{$absf}) {
223 print "mr: loading config $f\n" if $verbose;
224 open($in, "<", $f) || die "mr: open $f: $!\n";
225 ($dir)=$f=~/^(.*\/)[^\/]+$/;
226 $dir=abs_path($dir)."/";
228 # copy in defaults from first parent
230 while ($parent=~s/^(.*)\/[^\/]+\/?$/$1/) {
231 if (exists $config{$parent} &&
232 exists $config{$parent}{default}) {
233 $config{$dir}{default}={ %{$config{$parent}{default}} };
242 next if /^\s*\#/ || /^\s*$/;
243 if (/^\s*\[([^\]]*)\]\s*$/) {
245 if (length $dir && $section ne "default" &&
246 -e $dir.$section."/.mrconfig") {
247 push @toload, $dir.$section."/.mrconfig";
250 elsif (/^\s*(\w+)\s*=\s*(.*)/) {
255 while ($value=~/(.*)\\$/) {
260 if (! defined $section) {
261 die "$f line $.: parameter ($parameter) not in section\n";
263 if (! exists $config{$dir}{$section} &&
264 exists $config{$dir}{default}) {
266 $config{$dir}{$section}={ %{$config{$dir}{default}} };
268 $config{$dir}{$section}{$parameter}=$value;
271 die "$f line $.: parse error\n";
282 # Some useful actions that mr knows about by default.
283 # These can be overridden in ~/.mrconfig.
286 if [ -d .svn ]; then \
288 elif [ -d .git ]; then \
289 git pull origin master; \
291 echo "mr update: unknown RCS"; \
295 if [ -d .svn ]; then \
297 elif [ -d .git ]; then \
298 git status || true; \
300 echo "mr status: unknown RCS"; \
304 if [ -d .svn ]; then \
306 elif [ -d .git ]; then \
307 git commit -a "$@"; \
309 echo "mr commit: unknown RCS"; \