X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/900fe2548852e975ce41e04bfd5ef155c44821f0..9dccb005268468946b415529c40978265174ef11:/mr diff --git a/mr b/mr index d48fd61..ab71f36 100755 --- a/mr +++ b/mr @@ -54,6 +54,11 @@ 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 =over 4 @@ -77,9 +82,10 @@ Be verbose. =head1 FILES -B is configured by .mrconfig files. It searches for .mrconfig files in -your home directory, and in the root directory of each repository specified -in a .mrconfig file. So you could have a ~/.mrconfig that registers a +B is configured by .mrconfig files. It starts by reading the .mrconfig +file in your home directory. Each repository specified in a .mrconfig file +can also have its own .mrconfig file in its root directory that can +optionally be used as well. So you could have a ~/.mrconfig that registers a repository ~/src, that itself contains a ~/src/.mrconfig file, that in turn registers several additional repositories. @@ -95,8 +101,14 @@ environment, where any additional parameters you pass are available in 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 +There are three special parameters. If the "skip" parameter is set and its command returns nonzero, then B will skip acting on that repository. +If the "chain" parameter is set and its command returns nonzero, then B +will try to load a .mrconfig file from the root of the repository. (You +should avoid chaining from repositories with untrusted committers.) The +"lib" parameter can specify some shell code that will be run before each +command, this can be a useful way to define shell functions other commands +can use. The "default" section allows setting up default handlers for each action, and is overridden by the contents of other sections. mr contains default @@ -107,12 +119,22 @@ For example: [src] checkout = svn co svn://svn.example.com/src/trunk src + chain = true [src/linux-2.6] - # only check this out on kodama - skip = test $(hostname) != kodama + skip = small checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git + [default] + lib = \ + small() { + case "$(hostname)" in; \ + slug|snail); \ + return 0; ;; ; \ + esac; \ + return 1; \ + } + =head1 AUTHOR Copyright 2007 Joey Hess @@ -132,6 +154,7 @@ my $directory=getcwd(); my $config="$ENV{HOME}/.mrconfig"; my $verbose=0; my %config; +my %knownactions; Getopt::Long::Configure("no_permute"); my $result=GetOptions( @@ -142,14 +165,24 @@ 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}}) { @@ -168,15 +201,6 @@ foreach my $topdir (sort keys %config) { print "\n" unless $first; $first=0; - if (exists $config{$topdir}{$subdir}{skip}) { - my $ret=system($config{$topdir}{$subdir}{skip}); - if ($ret >> 8 == 0) { - print "mr $action: $dir skipped per config file\n" if $verbose; - push @skipped, $dir; - next; - } - } - action($action, $dir, $topdir, $subdir); } @@ -187,33 +211,47 @@ sub action { if ($action eq 'checkout') { if (-d $dir) { - print "mr $action: $dir already exists, skipping checkout\n"; + print "mr $action: $dir already exists, skipping checkout\n" if $verbose; push @skipped, $dir; - next; + return; } $dir=~s/^(.*)\/[^\/]+\/?$/$1/; } - if ($action eq 'update') { + elsif ($action eq 'update') { if (! -d $dir) { return action("checkout", $dir, $topdir, $subdir); } } - + if (! chdir($dir)) { print STDERR "mr $action: failed to chdir to $dir: $!\n"; push @skipped, $dir; } - elsif (! exists $config{$topdir}{$subdir}{$action}) { + + if (exists $config{$topdir}{$subdir}{skip}) { + my $ret=system($config{$topdir}{$subdir}{skip}); + if ($ret >> 8 == 0) { + print "mr $action: $dir skipped per config file\n" if $verbose; + push @skipped, $dir; + next; + } + } + + if (! 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 @ARGV"; + my $command="set -e; ". + (exists $config{$topdir}{$subdir}{lib} ? + $config{$topdir}{$subdir}{lib} : ""). + "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 @failures, $topdir.$subdir; + push @failed, $topdir.$subdir; if ($ret >> 8 != 0) { print STDERR "mr $action: command failed\n"; } @@ -222,7 +260,7 @@ sub action { } } else { - push @successes, $dir; + push @successful, $dir; } } } @@ -237,11 +275,17 @@ sub showstat { 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; +if (@failed) { + exit 1; +} +elsif (! @successful && @skipped) { + exit 1; +} +exit 0; my %loaded; sub loadconfig { @@ -288,10 +332,6 @@ sub loadconfig { next if /^\s*\#/ || /^\s*$/; if (/^\s*\[([^\]]*)\]\s*$/) { $section=$1; - if (length $dir && $section ne "default" && - -e $dir.$section."/.mrconfig") { - push @toload, $dir.$section."/.mrconfig"; - } } elsif (/^\s*(\w+)\s*=\s*(.*)/) { my $parameter=$1; @@ -311,7 +351,20 @@ sub loadconfig { # copy in defaults $config{$dir}{$section}={ %{$config{$dir}{default}} }; } - $config{$dir}{$section}{$parameter}=$value; + if ($parameter ne 'lib') { + $config{$dir}{$section}{$parameter}=$value; + $knownactions{$parameter}=1; + } + else { + $config{$dir}{$section}{$parameter}.=$value." ; "; + } + + if ($parameter eq 'chain' && + length $dir && $section ne "default" && + -e $dir.$section."/.mrconfig" && + system($value) >> 8 == 0) { + push @toload, $dir.$section."/.mrconfig"; + } } else { die "$f line $.: parse error\n"; @@ -324,18 +377,22 @@ sub loadconfig { } } -__DATA__ -# Some useful actions that mr knows about by default. +# Finally, some useful actions that mr knows about by default. # These can be overridden in ~/.mrconfig. +__DATA__ [default] +lib = \ + error() { \ + echo "mr: $@" >&2; \ + exit 1; \ + } update = \ if [ -d .svn ]; then \ svn update; \ elif [ -d .git ]; then \ git pull origin master; \ else \ - echo "mr update: unknown RCS"; \ - exit 1; \ + error "unknown repo type"; \ fi status = \ if [ -d .svn ]; then \ @@ -343,15 +400,13 @@ status = \ elif [ -d .git ]; then \ git status || true; \ else \ - echo "mr status: unknown RCS"; \ - exit 1; \ + error "unknown repo type"; \ fi commit = \ if [ -d .svn ]; then \ svn commit "$@"; \ elif [ -d .git ]; then \ - git commit -a "$@"; \ + git commit -a "$@" && git push --all; \ else \ - echo "mr commit: unknown RCS"; \ - exit 1; \ + error "unknown repo type"; \ fi