X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/cec9366aea873ebe2dca710d42f78d080c7d1ff0..ed9aa4b974f78cf0029bd3c27489acae687faa81:/mr?ds=inline diff --git a/mr b/mr index 470927a..04a5cbf 100755 --- a/mr +++ b/mr @@ -112,23 +112,31 @@ Be verbose. =head1 FILES 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. +file in your home directory, and this can in turn chain load .mrconfig files +from repositories. + +Here is an example .mrconfig file: + + [src] + checkout = svn co svn://svn.example.com/src/trunk src + chain = true + + [src/linux-2.6] + checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git The .mrconfig file uses a variant of the INI file format. Lines starting with "#" are comments. Lines ending with "\" are continued on to the next line. -The "default" section allows setting default values for the sections that +The "DEFAULT" section allows setting default values for the sections that come after it. -The "alias" section allows adding aliases for actions. Each parameter +The "ALIAS" section allows adding aliases for actions. Each parameter is an alias, and its value is the action to use. -All other sections specify where each repository is located, relative to the -directory that contains the .mrconfig file. +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. Within a section, each parameter defines a shell command to run to handle a given action. mr contains default handlers for the "update", "status", and @@ -143,34 +151,28 @@ are run inside the repository, though not necessarily at the top of it. The "MR_REPO" environment variable is set to the path to the top of the repository. -There are three special parameters. If the "skip" parameter is set and -its command returns nonzero, then B will skip acting on that repository. +A few parameters have special meanings: + +=over 4 + +=item skip + +If the "skip" parameter is set and its command returns nonzero, then B +will skip acting on that repository. + +=item chain + 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 for other commands -to use. +should avoid chaining from repositories with untrusted committers.) -For example: +=item lib - [src] - checkout = svn co svn://svn.example.com/src/trunk src - chain = true - - [src/linux-2.6] - skip = small - checkout = git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git +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 for other commands +to use. - [default] - lib = \ - small() { - case "$(hostname)" in; \ - slug|snail); \ - return 0; ;; ; \ - esac; \ - return 1; \ - } +=back =head1 AUTHOR @@ -196,9 +198,9 @@ my %alias; Getopt::Long::Configure("no_permute"); my $result=GetOptions( - "d=s" => sub { $directory=abs_path($_[1]) }, - "c=s" => \$config, - "v" => \$verbose, + "d|directory=s" => sub { $directory=abs_path($_[1]) }, + "c|config=s" => \$config, + "verbose" => \$verbose, ); if (! $result || @ARGV < 1) { die("Usage: mr [-d directory] action [params ...]\n". @@ -218,29 +220,27 @@ eval { # alias expansion and command stemming my $action=shift @ARGV; +if (exists $alias{$action}) { + $action=$alias{$action}; +} if (! exists $knownactions{$action}) { - if (exists $alias{$action}) { - $action=$alias{$action}; + my @matches = grep { /^\Q$action\E/ } + keys %knownactions, keys %alias; + if (@matches == 1) { + $action=$matches[0]; + } + elsif (@matches == 0) { + die "mr: unknown action \"$action\" (known actions: ". + join(", ", sort keys %knownactions).")\n"; } else { - my @matches = grep { /^\Q$action\E/ } - keys %knownactions, keys %alias; - if (@matches == 1) { - $action=$matches[0]; - } - elsif (@matches == 0) { - die "mr: unknown action \"$action\" (known actions: ". - join(", ", sort keys %knownactions).")\n"; - } - else { - die "mr: ambiguous action \"$action\" (matches: ". - join(", ", @matches).")\n"; - } + die "mr: ambiguous action \"$action\" (matches: ". + join(", ", @matches).")\n"; } } if ($action eq 'help') { - exec($config{''}{default}{help}); + exec($config{''}{DEFAULT}{help}); } # work out what repos to act on @@ -248,9 +248,12 @@ my @repos; my $nochdir=0; foreach my $topdir (sort keys %config) { foreach my $subdir (sort keys %{$config{$topdir}}) { - next if $subdir eq 'default'; - my $dir=$topdir.$subdir; - next if $dir ne $directory && $dir !~ /^\Q$directory\E\//; + next if $subdir eq 'DEFAULT'; + my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir; + my $d=$directory; + $dir.="/" unless $dir=~/\/$/; + $d.="/" unless $d=~/\/$/; + next if $dir ne $directory && $dir !~ /^\Q$directory\E/; push @repos, [$dir, $topdir, $subdir]; } } @@ -258,8 +261,8 @@ if (! @repos) { # fallback to find a leaf repo LEAF: foreach my $topdir (reverse sort keys %config) { foreach my $subdir (reverse sort keys %{$config{$topdir}}) { - next if $subdir eq 'default'; - my $dir=$topdir.$subdir; + next if $subdir eq 'DEFAULT'; + my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir; my $d=$directory; $dir.="/" unless $dir=~/\/$/; $d.="/" unless $d=~/\/$/; @@ -298,10 +301,6 @@ sub action { } $ENV{MR_REPO}=$dir; - if (! $nochdir && ! chdir($dir)) { - print STDERR "mr $action: failed to chdir to $dir: $!\n"; - push @skipped, $dir; - } if (exists $config{$topdir}{$subdir}{skip}) { my $test="set -e;".$lib.$config{$topdir}{$subdir}{skip}; @@ -313,8 +312,12 @@ sub action { return; } } - - if (! exists $config{$topdir}{$subdir}{$action}) { + + if (! $nochdir && ! chdir($dir)) { + print STDERR "mr $action: failed to chdir to $dir: $!\n"; + push @failed, $dir; + } + elsif (! exists $config{$topdir}{$subdir}{$action}) { print STDERR "mr $action: no defined $action command for $topdir$subdir, skipping\n"; push @skipped, $dir; } @@ -332,7 +335,7 @@ sub action { my $ret=system($command); if ($ret != 0) { print STDERR "mr $action: failed ($ret)\n" if $verbose; - push @failed, $topdir.$subdir; + push @failed, $dir; if ($ret >> 8 != 0) { print STDERR "mr $action: command failed\n"; } @@ -405,8 +408,8 @@ sub loadconfig { my $parent=$dir; while ($parent=~s/^(.*)\/[^\/]+\/?$/$1/) { if (exists $config{$parent} && - exists $config{$parent}{default}) { - $config{$dir}{default}={ %{$config{$parent}{default}} }; + exists $config{$parent}{DEFAULT}) { + $config{$dir}{DEFAULT}={ %{$config{$parent}{DEFAULT}} }; last; } } @@ -432,13 +435,13 @@ sub loadconfig { if (! defined $section) { die "$f line $.: parameter ($parameter) not in section\n"; } - if ($section ne 'alias' && + if ($section ne 'ALIAS' && ! exists $config{$dir}{$section} && - exists $config{$dir}{default}) { + exists $config{$dir}{DEFAULT}) { # copy in defaults - $config{$dir}{$section}={ %{$config{$dir}{default}} }; + $config{$dir}{$section}={ %{$config{$dir}{DEFAULT}} }; } - if ($section eq 'alias') { + if ($section eq 'ALIAS') { $alias{$parameter}=$value; } elsif ($parameter eq 'lib') { @@ -448,7 +451,7 @@ sub loadconfig { $config{$dir}{$section}{$parameter}=$value; $knownactions{$parameter}=1; if ($parameter eq 'chain' && - length $dir && $section ne "default" && + length $dir && $section ne "DEFAULT" && -e $dir.$section."/.mrconfig" && system($value) >> 8 == 0) { push @toload, $dir.$section."/.mrconfig"; @@ -469,21 +472,25 @@ sub loadconfig { # Finally, some useful actions that mr knows about by default. # These can be overridden in ~/.mrconfig. __DATA__ -[alias] +[ALIAS] co = checkout ci = commit ls = list -[default] + +[DEFAULT] lib = \ error() { \ echo "mr: $@" >&2; \ exit 1; \ } + update = \ if [ -d "$MR_REPO"/.svn ]; then \ svn update "$@"; \ elif [ -d "$MR_REPO"/.git ]; then \ git pull origin master "$@"; \ + elif [ -d "$MR_REPO"/.bzr ]; then \ + bzr merge "$@"; \ elif [ -d "$MR_REPO"/CVS ]; then \ cvs update "$@"; \ else \ @@ -494,6 +501,8 @@ status = \ svn status "$@"; \ elif [ -d "$MR_REPO"/.git ]; then \ git status "$@" || true; \ + elif [ -d "$MR_REPO"/.bzr ]; then \ + bzr status "$@"; \ elif [ -d "$MR_REPO"/CVS ]; then \ cvs status "$@"; \ else \ @@ -504,6 +513,8 @@ commit = \ svn commit "$@"; \ elif [ -d "$MR_REPO"/.git ]; then \ git commit -a "$@" && git push --all; \ + elif [ -d "$MR_REPO"/.bzr ]; then \ + bzr commit "$@" && bzr push; \ elif [ -d "$MR_REPO"/CVS ]; then \ cvs commit "$@"; \ else \ @@ -514,6 +525,8 @@ diff = \ svn diff "$@"; \ elif [ -d "$MR_REPO"/.git ]; then \ git diff "$@"; \ + elif [ -d "$MR_REPO"/.bzr ]; then \ + bzr diff "$@"; \ elif [ -d "$MR_REPO"/CVS ]; then \ cvs diff "$@"; \ else \ @@ -524,15 +537,21 @@ log = \ svn log"$@"; \ elif [ -d "$MR_REPO"/.git ]; then \ git log "$@"; \ + elif [ -d "$MR_REPO"/.bzr ]; then \ + bzr log "$@"; \ elif [ -d "$MR_REPO"/CVS ]; then \ cvs log "$@"; \ else \ error "unknown repo type"; \ fi list = true + help = \ if [ ! -e "$MR_PATH" ]; then \ error "cannot find program path";\ fi; \ (pod2man -c mr "$MR_PATH" | man -l -) || \ error "pod2man or man failed" + +ed = echo "A horse is a horse, of course, of course.." +T = echo "I pity the fool."