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 !~ /^\Q$directory\E\//) {
132 print "mr $action: $dir skipped per -d parameter ($directory)\n" if $verbose;
137 print "\n" unless $first;
140 if (exists $config{$topdir}{$subdir}{skip}) {
141 my $ret=system($config{$topdir}{$subdir}{skip});
142 if ($ret >> 8 == 0) {
143 print "mr $action: $dir skipped per config file\n" if $verbose;
149 if ($action eq 'checkout') {
151 print "mr $action: $dir already exists, skipping checkout\n";
155 $dir=~s/^(.*)\/[^\/]+\/?$/$1/;
158 print STDERR "mr $action: failed to chdir to $dir: $!\n";
161 elsif (! exists $config{$topdir}{$subdir}{$action}) {
162 print STDERR "mr $action: no defined $action command for $topdir$subdir, skipping\n";
166 print "mr $action: in $dir\n";
167 my $command="set -e; my_action(){ $config{$topdir}{$subdir}{$action} ; }; my_action @ARGV";
168 my $ret=system($command);
170 print STDERR "mr $action: failed to run: $command\n" if $verbose;
171 push @failures, $topdir.$subdir;
172 if ($ret >> 8 != 0) {
173 print STDERR "mr $action: command failed\n";
176 print STDERR "mr $action: command died ($ret)\n";
180 push @successes, $dir;
191 return "$count ".($count > 1 ? $plural : $singular);
195 print "\nmr $action: finished (".join("; ",
196 showstat($#successes+1, "success", "sucesses"),
197 showstat($#failures+1, "failure", "failures"),
198 showstat($#skipped+1, "skipped", "skipped"),
200 exit @failures ? 1 : 0;
210 if (ref $f eq 'GLOB') {
215 # $f might be a symlink
216 my $absf=abs_path($f);
217 if ($loaded{$absf}) {
222 print "mr: loading config $f\n" if $verbose;
223 open($in, "<", $f) || die "mr: open $f: $!\n";
224 ($dir)=$f=~/^(.*\/)[^\/]+$/;
225 $dir=abs_path($dir)."/";
227 # copy in defaults from first parent
229 while ($parent=~s/^(.*)\/[^\/]+\/?$/$1/) {
230 if (exists $config{$parent} &&
231 exists $config{$parent}{default}) {
232 $config{$dir}{default}={ %{$config{$parent}{default}} };
241 next if /^\s*\#/ || /^\s*$/;
242 if (/^\s*\[([^\]]*)\]\s*$/) {
244 if (length $dir && $section ne "default" &&
245 -e $dir.$section."/.mrconfig") {
246 push @toload, $dir.$section."/.mrconfig";
249 elsif (/^\s*(\w+)\s*=\s*(.*)/) {
254 while ($value=~/(.*)\\$/) {
259 if (! defined $section) {
260 die "$f line $.: parameter ($parameter) not in section\n";
262 if (! exists $config{$dir}{$section} &&
263 exists $config{$dir}{default}) {
265 $config{$dir}{$section}={ %{$config{$dir}{default}} };
267 $config{$dir}{$section}{$parameter}=$value;
270 die "$f line $.: parse error\n";
281 # Some useful actions that mr knows about by default.
282 # These can be overridden in ~/.mrconfig.
285 if [ -d .svn ]; then \
287 elif [ -d .git ]; then \
288 git pull origin master; \
290 echo "mr update: unknown RCS"; \
294 if [ -d .svn ]; then \
296 elif [ -d .git ]; then \
297 git status || true; \
299 echo "mr status: unknown RCS"; \
303 if [ -d .svn ]; then \
305 elif [ -d .git ]; then \
306 git commit -a "$@"; \
308 echo "mr commit: unknown RCS"; \