X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/20f5a82ced16c52a87aa4df2edadf405b1ee0bb1..677f2c478ccc89aa2f30c4407e26bc568d8b5532:/mr diff --git a/mr b/mr index d3f8efb..fc63605 100755 --- a/mr +++ b/mr @@ -41,7 +41,7 @@ B [options] remember action [params ...] B 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 repository. It supports any combination of subversion, git, cvs, mercurial, -bzr, darcs and fossil repositories, and support for other revision +bzr, darcs and fossil repositories, and support for other version control systems can easily be added. B cds into and operates on all registered repositories at or below your @@ -55,7 +55,7 @@ in turn chain load .mrconfig files from repositories. It also automatically looks for a .mrconfig file in the current directory, or in one of its parent directories. -These predefined commands should be fairly familiar to users of any revision +These predefined commands should be fairly familiar to users of any version control system: =over 4 @@ -87,14 +87,14 @@ 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. +remote repository. Only supported for distributed version control systems. The optional -m parameter allows specifying a commit message. =item push Pushes committed local changes to the remote repository. A no-op for -centralized revision control systems. +centralized version control systems. =item diff @@ -189,8 +189,8 @@ Actions can be abbreviated to any unambiguous substring, so update" Additional parameters can be passed to most commands, and are passed on -unchanged to the underlying revision control system. This is mostly useful -if the repositories mr will act on all use the same revision control +unchanged to the underlying version control system. This is mostly useful +if the repositories mr will act on all use the same version control system. =head1 OPTIONS @@ -352,14 +352,25 @@ If the "skip" parameter is set and its command returns true, then B will skip acting on that repository. The command is passed the action name in $1. -Here are three examples. The first skips the repo unless +Here are two examples. The first skips the repo unless mr is run by joey. The second uses the hours_since function (included in mr's built-in library) to skip updating the repo unless it's -been at least 12 hours since the last update. The third skips operating -on the repo unless it already exists (use mr checkout to enable the repo). +been at least 12 hours since the last update. + [mystuff] + checkout = ... skip = test `whoami` != joey + + [linux] + checkout = ... skip = [ "$1" = update ] && ! hours_since "$1" 12 + +Another way to use skip is for a lazy checkout. This makes mr skip +operating on a repo unless it already exists. To enable the +repo, you have to explicitly check it out (using "mr -d foo checkout"). + + [foo] + checkout = ... skip = lazy =item order @@ -386,6 +397,14 @@ part of the including file. Unlike all other parameters, this parameter does not need to be placed within a section. +B ships several libraries that can be included to add support for +additional version control type things (unison, git-svn, vcsh, git-fake-bare, +git-subtree). To include them all, you could use: + + include = cat /usr/share/mr/* + +See the individual files for details. + =item deleted If the "deleted" parameter is set and its command returns true, then @@ -418,15 +437,15 @@ run before committing; "post_update" is run after updating. When looking for a command to run for a given action, mr first looks for a parameter with the same name as the action. If that is not found, it -looks for a parameter named "rcs_action" (substituting in the name of the -revision control system and the action). The name of the revision control -system is itself determined by running each defined "rcs_test" action, +looks for a parameter named "VCS_action" (substituting in the name of the +version control system and the action). The name of the version control +system is itself determined by running each defined "VCS_test" action, until one succeeds. Internally, mr has settings for "git_update", "svn_update", etc. To change -the action that is performed for a given revision control system, you can -override these rcs specific actions. To add a new revision control system, -you can just add rcs specific actions for it. +the action that is performed for a given version control system, you can +override these VCS specific actions. To add a new version control system, +you can just add VCS specific actions for it. =head1 UNTRUSTED MRCONFIG FILES @@ -508,41 +527,41 @@ my (@ok, @failed, @skipped); main(); -my %rcs; -sub rcs_test { +my %vcs; +sub vcs_test { my ($action, $dir, $topdir, $subdir) = @_; - if (exists $rcs{$dir}) { - return $rcs{$dir}; + if (exists $vcs{$dir}) { + return $vcs{$dir}; } my $test="set -e\n"; - foreach my $rcs_test ( + foreach my $vcs_test ( sort { length $a <=> length $b || $a cmp $b } grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) { - my ($rcs)=$rcs_test=~/(.*)_test/; - $test="my_$rcs_test() {\n$config{$topdir}{$subdir}{$rcs_test}\n}\n".$test; - $test.="if my_$rcs_test; then echo $rcs; fi\n"; + my ($vcs)=$vcs_test=~/(.*)_test/; + $test="my_$vcs_test() {\n$config{$topdir}{$subdir}{$vcs_test}\n}\n".$test; + $test.="if my_$vcs_test; then echo $vcs; fi\n"; } $test=$config{$topdir}{$subdir}{lib}."\n".$test if exists $config{$topdir}{$subdir}{lib}; - print "mr $action: running rcs test >>$test<<\n" if $verbose; - my $rcs=`$test`; - chomp $rcs; - if ($rcs=~/\n/s) { - $rcs=~s/\n/, /g; - print STDERR "mr $action: found multiple possible repository types ($rcs) for ".fulldir($topdir, $subdir)."\n"; + print "mr $action: running vcs test >>$test<<\n" if $verbose; + my $vcs=`$test`; + chomp $vcs; + if ($vcs=~/\n/s) { + $vcs=~s/\n/, /g; + print STDERR "mr $action: found multiple possible repository types ($vcs) for ".fulldir($topdir, $subdir)."\n"; return undef; } - if (! length $rcs) { - return $rcs{$dir}=undef; + if (! length $vcs) { + return $vcs{$dir}=undef; } else { - return $rcs{$dir}=$rcs; + return $vcs{$dir}=$vcs; } } @@ -557,11 +576,11 @@ sub findcommand { return undef; } - my $rcs=rcs_test(@_); + my $vcs=vcs_test(@_); - if (defined $rcs && - exists $config{$topdir}{$subdir}{$rcs."_".$action}) { - return $config{$topdir}{$subdir}{$rcs."_".$action}; + if (defined $vcs && + exists $config{$topdir}{$subdir}{$vcs."_".$action}) { + return $config{$topdir}{$subdir}{$vcs."_".$action}; } else { return undef; @@ -583,7 +602,7 @@ sub action { my $is_checkout=($action eq 'checkout'); my $is_update=($action =~ /update/); - $ENV{MR_REPO}=$dir; + ($ENV{MR_REPO}=$dir) =~ s!/$!!; $ENV{MR_ACTION}=$action; foreach my $testname ("skip", "deleted") { @@ -645,13 +664,13 @@ sub action { return FAILED; } elsif (! defined $command) { - my $rcs=rcs_test(@_); - if (! defined $rcs) { + my $vcs=vcs_test(@_); + if (! defined $vcs) { print STDERR "mr $action: unknown repository type and no defined $action command for $fulldir\n"; return FAILED; } else { - print STDERR "mr $action: no defined action for $rcs repository $fulldir, skipping\n"; + print STDERR "mr $action: no defined action for $vcs repository $fulldir, skipping\n"; return SKIPPED; } } @@ -927,7 +946,10 @@ sub repodir { return $ret; } -# figure out which repos to act on +# Figure out which repos to act on. Returns a list of array refs +# in the format: +# +# [ "$full_repo_path/", "$mr_config_path/", $section_header ] sub selectrepos { my @repos; foreach my $repo (repolist()) { @@ -1160,11 +1182,43 @@ sub loadconfig { close $in unless ref $f eq 'GLOB'; my $section; + + # Keep track of the current line in the config file; + # when a file is included track the current line from the include. my $line=0; + my $included=undef; + my $includeline=0; + my $nextline = sub { + if ($included) { + $includeline++; + $included--; + } + else { + $included=undef; + $includeline=0; + $line++; + } + my $l=shift @lines; + chomp $l; + return $l + }; + my $lineerror = sub { + my $msg=shift; + if (defined $included) { + die "mr: $f line $line include line $includeline: $msg\n"; + } + else { + die "mr: $f line $line: $msg\n"; + } + }; + while (@lines) { - $_=shift @lines; - $line++; - chomp; + $_=$nextline->(); + + if (! $trusted && /[[:cntrl:]]/) { + trusterror("mr: illegal control character", $f, $line, $bootstrap_url); + } + next if /^\s*\#/ || /^\s*$/; if (/^\[([^\]]*)\]\s*$/) { $section=$1; @@ -1190,10 +1244,9 @@ sub loadconfig { # continued value while (@lines && $lines[0]=~/^\s(.+)/) { - shift(@lines); - $line++; $value.="\n$1"; chomp $value; + $nextline->(); } if (! $trusted) { @@ -1219,15 +1272,17 @@ sub loadconfig { if ($parameter eq "include") { print "mr: including output of \"$value\"\n" if $verbose; - unshift @lines, `$value`; + my @inc=`$value`; if ($?) { print STDERR "mr: include command exited nonzero ($?)\n"; } + $included += @inc; + unshift @lines, @inc; next; } if (! defined $section) { - die "$f line $.: parameter ($parameter) not in section\n"; + $lineerror->("parameter ($parameter) not in section"); } if ($section eq 'ALIAS') { $alias{$parameter}=$value; @@ -1246,7 +1301,7 @@ sub loadconfig { if ($parameter eq 'chain' && length $dir && $section ne "DEFAULT") { my $chaindir="$section"; - if ($chaindir !~ m!/!) { + if ($chaindir !~ m!^/!) { $chaindir=$dir.$chaindir; } if (-e "$chaindir/.mrconfig") { @@ -1268,7 +1323,7 @@ sub loadconfig { } } else { - die "$f line $line: parse error\n"; + $lineerror->("parse error"); } }