]> git.madduck.net Git - code/myrepos.git/blobdiff - mr

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

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.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

add example of including libs, and mention all the currently available libs
[code/myrepos.git] / mr
diff --git a/mr b/mr
index 6cac30c99bb8dfafa9badc7299d9064915be92c0..4253be2680db007a6b806c98b008942f711e0391 100755 (executable)
--- a/mr
+++ b/mr
@@ -222,10 +222,9 @@ Be verbose.
 
 =item --quiet
 
 
 =item --quiet
 
-Be quiet. This supresses mr's usual output. Any output from the
-commands that mr runs will still be displayed.
-
-For example, to see only uncommitted changes, use "mr -q status"
+Be quiet. This supresses mr's usual output, as well as any output from
+commands that are run (including stderr output). If a command fails,
+the output will be shown.
 
 =item -k
 
 
 =item -k
 
@@ -291,7 +290,7 @@ This obsolete flag is ignored.
 Here is an example .mrconfig file:
 
   [src]
 Here is an example .mrconfig file:
 
   [src]
-  checkout = svn co svn://svn.example.com/src/trunk src
+  checkout = svn checkout svn://svn.example.com/src/trunk src
   chain = true
 
   [src/linux-2.6]
   chain = true
 
   [src/linux-2.6]
@@ -318,14 +317,18 @@ will be passed through the shell for expansion. For example,
 
 Within a section, each parameter defines a shell command to run to handle a
 given action. mr contains default handlers for "update", "status",
 
 Within a section, each parameter defines a shell command to run to handle a
 given action. mr contains default handlers for "update", "status",
-"commit", and other standard actions. Normally you only need to specify what
-to do for "checkout".
+"commit", and other standard actions.
+
+Normally you only need to specify what to do for "checkout". Here you
+specify the command to run in order to create a checkout of the repository.
+The command will be run in the parent directory, and must create the
+repository's directory. So use "git clone", "svn checkout", "bzr branch"
+or "bzr checkout" (for a bound branch), etc.
 
 Note that these shell commands are run in a "set -e" shell
 environment, where any additional parameters you pass are available in
 
 Note that these shell commands are run in a "set -e" shell
 environment, where any additional parameters you pass are available in
-"$@". The "checkout" command is run in the parent of the repository
-directory, since the repository isn't checked out yet. All other commands
-are run inside the repository, though not necessarily at the top of it.
+"$@". All commands other than "checkout" 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. (For the "register" action, "MR_REPO" is instead set to the 
 
 The "MR_REPO" environment variable is set to the path to the top of the
 repository. (For the "register" action, "MR_REPO" is instead set to the 
@@ -336,6 +339,9 @@ The "MR_CONFIG" environment variable is set to the .mrconfig file
 that defines the repo being acted on, or, if the repo is not yet in a config
 file, the .mrconfig file that should be modified to register the repo.
 
 that defines the repo being acted on, or, if the repo is not yet in a config
 file, the .mrconfig file that should be modified to register the repo.
 
+The "MR_ACTION" environment variable is set to the command being run
+(update, checkout, etc).
+
 A few parameters have special meanings:
 
 =over 4
 A few parameters have special meanings:
 
 =over 4
@@ -351,8 +357,21 @@ 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.
 
 (included in mr's built-in library) to skip updating the repo unless it's
 been at least 12 hours since the last update.
 
+  [mystuff]
+  checkout = ...
   skip = test `whoami` != joey
   skip = test `whoami` != joey
+
+  [linux]
+  checkout = ...
   skip = [ "$1" = update ] && ! hours_since "$1" 12
   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
 
 
 =item order
 
@@ -378,6 +397,22 @@ part of the including file.
 Unlike all other parameters, this parameter does not need to be placed
 within a section.
 
 Unlike all other parameters, this parameter does not need to be placed
 within a section.
 
+B<mr> 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
+B<mr> will treat the repository as deleted. It won't ever actually delete
+the repository, but it will warn if it sees the repository's directory.
+This is useful when one mrconfig file is shared amoung multiple machines,
+to keep track of and remember to delete old repositories.
+
 =item lib
 
 The "lib" parameter can specify some shell code that will be run before each
 =item lib
 
 The "lib" parameter can specify some shell code that will be run before each
@@ -568,6 +603,38 @@ sub action {
        my $is_update=($action =~ /update/);
 
        $ENV{MR_REPO}=$dir;
        my $is_update=($action =~ /update/);
 
        $ENV{MR_REPO}=$dir;
+       $ENV{MR_ACTION}=$action;
+       
+       foreach my $testname ("skip", "deleted") {
+               my $testcommand=findcommand($testname, $dir, $topdir, $subdir, $is_checkout);
+
+               if (defined $testcommand) {
+                       my $test="set -e;".$lib.
+                               "my_action(){ $testcommand\n }; my_action '$action'";
+                       print "mr $action: running $testname test >>$test<<\n" if $verbose;
+                       my $ret=system($test);
+                       if ($ret != 0) {
+                               if (($? & 127) == 2) {
+                                       print STDERR "mr $action: interrupted\n";
+                                       return ABORT;
+                               }
+                               elsif ($? & 127) {
+                                       print STDERR "mr $action: $testname test received signal ".($? & 127)."\n";
+                                       return ABORT;
+                               }
+                       }
+                       if ($ret >> 8 == 0) {
+                               if ($testname eq "deleted") {
+                                       if (-d $dir) {
+                                               print STDERR "mr error: $dir should be deleted yet still exists\n";
+                                               return FAILED;
+                                       }
+                               }
+                               print "mr $action: skip $dir skipped\n" if $verbose;
+                               return SKIPPED;
+                       }
+               }
+       }
 
        if ($is_checkout) {
                if (! $force_checkout) {
 
        if ($is_checkout) {
                if (! $force_checkout) {
@@ -585,30 +652,8 @@ sub action {
                }
        }
 
                }
        }
 
-       my $skiptest=findcommand("skip", $dir, $topdir, $subdir, $is_checkout);
        my $command=findcommand($action, $dir, $topdir, $subdir, $is_checkout);
 
        my $command=findcommand($action, $dir, $topdir, $subdir, $is_checkout);
 
-       if (defined $skiptest) {
-               my $test="set -e;".$lib.
-                       "my_action(){ $skiptest\n }; my_action '$action'";
-               print "mr $action: running skip test >>$test<<\n" if $verbose;
-               my $ret=system($test);
-               if ($ret != 0) {
-                       if (($? & 127) == 2) {
-                               print STDERR "mr $action: interrupted\n";
-                               return ABORT;
-                       }
-                       elsif ($? & 127) {
-                               print STDERR "mr $action: skip test received signal ".($? & 127)."\n";
-                               return ABORT;
-                       }
-               }
-               if ($ret >> 8 == 0) {
-                       print "mr $action: $dir skipped per config file\n" if $verbose;
-                       return SKIPPED;
-               }
-       }
-
        if ($is_checkout && ! -d $dir) {
                print "mr $action: creating parent directory $dir\n" if $verbose;
                system("mkdir", "-p", $dir);
        if ($is_checkout && ! -d $dir) {
                print "mr $action: creating parent directory $dir\n" if $verbose;
                system("mkdir", "-p", $dir);
@@ -630,14 +675,16 @@ sub action {
                }
        }
        else {
                }
        }
        else {
+               my $actionmsg;
                if (! $no_chdir) {
                if (! $no_chdir) {
-                       print "mr $action: $fulldir\n" unless $quiet;
+                       $actionmsg="mr $action: $fulldir";
                }
                else {
                        my $s=$directory;
                        $s=~s/^\Q$fulldir\E\/?//;
                }
                else {
                        my $s=$directory;
                        $s=~s/^\Q$fulldir\E\/?//;
-                       print "mr $action: $fulldir (in subdir $s)\n" unless $quiet;
+                       $actionmsg="mr $action: $fulldir (in subdir $s)";
                }
                }
+               print "$actionmsg\n" unless $quiet;
 
                my $hookret=hook("pre_$action", $topdir, $subdir);
                return $hookret if $hookret != OK;
 
                my $hookret=hook("pre_$action", $topdir, $subdir);
                return $hookret if $hookret != OK;
@@ -646,7 +693,18 @@ sub action {
                        "my_action(){ $command\n }; my_action ".
                        join(" ", map { s/\\/\\\\/g; s/"/\"/g; '"'.$_.'"' } @ARGV);
                print "mr $action: running >>$command<<\n" if $verbose;
                        "my_action(){ $command\n }; my_action ".
                        join(" ", map { s/\\/\\\\/g; s/"/\"/g; '"'.$_.'"' } @ARGV);
                print "mr $action: running >>$command<<\n" if $verbose;
-               my $ret=system($command);
+               my $ret;
+               if ($quiet) {
+                       my $output = qx/$command 2>&1/;
+                       $ret = $?;
+                       if ($ret != 0) {
+                               print "$actionmsg\n";
+                               print STDERR $output;
+                       }
+               }
+               else {
+                       $ret=system($command);
+               }
                if ($ret != 0) {
                        if (($? & 127) == 2) {
                                print STDERR "mr $action: interrupted\n";
                if ($ret != 0) {
                        if (($? & 127) == 2) {
                                print STDERR "mr $action: interrupted\n";
@@ -702,7 +760,17 @@ sub hook {
        my $shell="set -e;".$lib.
                "my_hook(){ $command\n }; my_hook";
        print "mr $hook: running >>$shell<<\n" if $verbose;
        my $shell="set -e;".$lib.
                "my_hook(){ $command\n }; my_hook";
        print "mr $hook: running >>$shell<<\n" if $verbose;
-       my $ret=system($shell);
+       my $ret;
+       if ($quiet) {
+               my $output = qx/$shell 2>&1/;
+               $ret = $?;
+               if ($ret != 0) {
+                       print STDERR $output;
+               }
+       }
+       else {
+               $ret=system($shell);
+       }
        if ($ret != 0) {
                if (($? & 127) == 2) {
                        print STDERR "mr $hook: interrupted\n";
        if ($ret != 0) {
                if (($? & 127) == 2) {
                        print STDERR "mr $hook: interrupted\n";
@@ -712,6 +780,9 @@ sub hook {
                        print STDERR "mr $hook: received signal ".($? & 127)."\n";
                        return ABORT;
                }
                        print STDERR "mr $hook: received signal ".($? & 127)."\n";
                        return ABORT;
                }
+               else {
+                       return FAILED;
+               }
        }
 
        return OK;
        }
 
        return OK;
@@ -1020,7 +1091,7 @@ sub is_trusted_checkout {
                                        is_trusted_repo($words[$c])
                                );
                        }
                                        is_trusted_repo($words[$c])
                                );
                        }
-                       elsif (defined $words[$c] && $twords[$c] eq $words[$c]) {
+                       elsif (defined $words[$c] && $words[$c]=~/^($twords[$c])$/) {
                                $match=1;
                        }
                        else {
                                $match=1;
                        }
                        else {
@@ -1034,14 +1105,24 @@ sub is_trusted_checkout {
 }
 
 sub trusterror {
 }
 
 sub trusterror {
-       die shift()."\n".
-               "(To trust this file, list it in ~/.mrtrust.)\n";
+       my ($err, $file, $line, $url)=@_;
+       
+       if (defined $url) {
+               die "$err in untrusted $url line $line\n".
+                       "(To trust this url, --trust-all can be used; but please use caution;\n".
+                       "this can allow arbitrary code execution!)\n";
+       }
+       else {
+               die "$err in untrusted $file line $line\n".
+                       "(To trust this file, list it in ~/.mrtrust.)\n";
+       }
 }
 
 my %loaded;
 sub loadconfig {
        my $f=shift;
        my $dir=shift;
 }
 
 my %loaded;
 sub loadconfig {
        my $f=shift;
        my $dir=shift;
+       my $bootstrap_url=shift;
 
        my @toload;
 
 
        my @toload;
 
@@ -1053,10 +1134,6 @@ sub loadconfig {
                $trusted=1;
        }
        else {
                $trusted=1;
        }
        else {
-               if (! -e $f) {
-                       return;
-               }
-
                my $absf=abs_path($f);
                if ($loaded{$absf}) {
                        return;
                my $absf=abs_path($f);
                if ($loaded{$absf}) {
                        return;
@@ -1091,6 +1168,10 @@ sub loadconfig {
                        }
                }
                
                        }
                }
                
+               if (! -e $f) {
+                       return;
+               }
+
                print "mr: loading config $f\n" if $verbose;
                open($in, "<", $f) || die "mr: open $f: $!\n";
        }
                print "mr: loading config $f\n" if $verbose;
                open($in, "<", $f) || die "mr: open $f: $!\n";
        }
@@ -1111,7 +1192,7 @@ sub loadconfig {
                                if (! is_trusted_repo($section) ||
                                    $section eq 'ALIAS' ||
                                    $section eq 'DEFAULT') {
                                if (! is_trusted_repo($section) ||
                                    $section eq 'ALIAS' ||
                                    $section eq 'DEFAULT') {
-                                       trusterror "mr: illegal section \"[$section]\" in untrusted $f line $line";
+                                       trusterror("mr: illegal section \"[$section]\"", $f, $line, $bootstrap_url)
                                }
                        }
                        $section=expandenv($section) if $trusted;
                                }
                        }
                        $section=expandenv($section) if $trusted;
@@ -1135,13 +1216,23 @@ sub loadconfig {
                        }
 
                        if (! $trusted) {
                        }
 
                        if (! $trusted) {
-                               # Untrusted files can only contain checkout
-                               # parameters.
-                               if ($parameter ne 'checkout') {
-                                       trusterror "mr: illegal setting \"$parameter=$value\" in untrusted $f line $line";
+                               # Untrusted files can only contain a few
+                               # settings in specific known-safe formats.
+                               if ($parameter eq 'checkout') {
+                                       if (! is_trusted_checkout($value)) {
+                                               trusterror("mr: illegal checkout command \"$value\"", $f, $line, $bootstrap_url);
+                                       }
+                               }
+                               elsif ($parameter eq 'order') {
+                                       # not interpreted as a command, so
+                                       # safe.
                                }
                                }
-                               if (! is_trusted_checkout($value)) {
-                                       trusterror "mr: illegal checkout command \"$value\" in untrusted $f line $line";
+                               elsif ($value eq 'true' || $value eq 'false') {
+                                       # skip=true , deleted=true etc are
+                                       # safe.
+                               }
+                               else {
+                                       trusterror("mr: illegal setting \"$parameter=$value\"", $f, $line, $bootstrap_url);
                                }
                        }
 
                                }
                        }
 
@@ -1172,21 +1263,26 @@ sub loadconfig {
                                        $knownactions{$parameter}=1;
                                }
                                if ($parameter eq 'chain' &&
                                        $knownactions{$parameter}=1;
                                }
                                if ($parameter eq 'chain' &&
-                                   length $dir && $section ne "DEFAULT" &&
-                                   -e $dir.$section."/.mrconfig") {
-                                       my $ret=system($value);
-                                       if ($ret != 0) {
-                                               if (($? & 127) == 2) {
-                                                       print STDERR "mr: chain test interrupted\n";
-                                                       exit 2;
+                                   length $dir && $section ne "DEFAULT") {
+                                       my $chaindir="$section";
+                                       if ($chaindir !~ m!/!) {
+                                               $chaindir=$dir.$chaindir;
+                                       }
+                                       if (-e "$chaindir/.mrconfig") {
+                                               my $ret=system($value);
+                                               if ($ret != 0) {
+                                                       if (($? & 127) == 2) {
+                                                               print STDERR "mr: chain test interrupted\n";
+                                                               exit 2;
+                                                       }
+                                                       elsif ($? & 127) {
+                                                               print STDERR "mr: chain test received signal ".($? & 127)."\n";
+                                                       }
                                                }
                                                }
-                                               elsif ($? & 127) {
-                                                       print STDERR "mr: chain test received signal ".($? & 127)."\n";
+                                               else {
+                                                       push @toload, ["$chaindir/.mrconfig", $chaindir];
                                                }
                                        }
                                                }
                                        }
-                                       else {
-                                               push @toload, $dir.$section."/.mrconfig";
-                                       }
                                }
                        }
                }
                                }
                        }
                }
@@ -1195,8 +1291,8 @@ sub loadconfig {
                }
        }
 
                }
        }
 
-       foreach (@toload) {
-               loadconfig($_);
+       foreach my $c (@toload) {
+               loadconfig(@$c);
        }
 }
 
        }
 }
 
@@ -1452,7 +1548,7 @@ sub bootstrap {
        # would normally be skipped.
        my $topdir=abs_path(".")."/";
        my @repo=($topdir, $topdir, ".");
        # would normally be skipped.
        my $topdir=abs_path(".")."/";
        my @repo=($topdir, $topdir, ".");
-       loadconfig($tmpconfig, $topdir);
+       loadconfig($tmpconfig, $topdir, $url);
        record(\@repo, action("checkout", @repo, 1))
                if exists $config{$topdir}{"."}{"checkout"};
 
        record(\@repo, action("checkout", @repo, 1))
                if exists $config{$topdir}{"."}{"checkout"};
 
@@ -1629,6 +1725,16 @@ lib =
                        return 0
                fi
        }
                        return 0
                fi
        }
+       is_bzr_checkout() {
+               LANG=C bzr info | egrep -q '^Checkout'
+       }
+       lazy() {
+               if [ "$MR_ACTION" = checkout ] || [ -d "$MR_REPO" ]; then
+                       return 1
+               else
+                       return 0
+               fi
+       }
 
 svn_test = test -d "$MR_REPO"/.svn
 git_test = test -d "$MR_REPO"/.git
 
 svn_test = test -d "$MR_REPO"/.svn
 git_test = test -d "$MR_REPO"/.git
@@ -1644,7 +1750,12 @@ git_bare_test =
 
 svn_update = svn update "$@"
 git_update = git pull "$@"
 
 svn_update = svn update "$@"
 git_update = git pull "$@"
-bzr_update = bzr merge --pull "$@"
+bzr_update = 
+       if is_bzr_checkout; then
+               bzr update "$@"
+       else
+               bzr merge --pull "$@"
+       fi
 cvs_update = cvs update "$@"
 hg_update  = hg pull "$@" && hg update "$@"
 darcs_update = darcs pull -a "$@"
 cvs_update = cvs update "$@"
 hg_update  = hg pull "$@" && hg update "$@"
 darcs_update = darcs pull -a "$@"
@@ -1660,14 +1771,24 @@ fossil_status = fossil changes "$@"
 
 svn_commit = svn commit "$@"
 git_commit = git commit -a "$@" && git push --all
 
 svn_commit = svn commit "$@"
 git_commit = git commit -a "$@" && git push --all
-bzr_commit = bzr commit "$@" && bzr push
+bzr_commit = 
+       if is_bzr_checkout; then
+               bzr commit "$@"
+       else
+               bzr commit "$@" && bzr push
+       fi
 cvs_commit = cvs commit "$@"
 hg_commit  = hg commit -m "$@" && hg push
 darcs_commit = darcs record -a -m "$@" && darcs push -a
 fossil_commit = fossil commit "$@"
 
 git_record = git commit -a "$@"
 cvs_commit = cvs commit "$@"
 hg_commit  = hg commit -m "$@" && hg push
 darcs_commit = darcs record -a -m "$@" && darcs push -a
 fossil_commit = fossil commit "$@"
 
 git_record = git commit -a "$@"
-bzr_record = bzr commit "$@"
+bzr_record =
+       if is_bzr_checkout; then
+               bzr commit --local "$@"
+       else
+               bzr commit "$@"
+       fi
 hg_record  = hg commit -m "$@"
 darcs_record = darcs record -a -m "$@"
 fossil_record = fossil commit "$@"
 hg_record  = hg commit -m "$@"
 darcs_record = darcs record -a -m "$@"
 fossil_record = fossil commit "$@"
@@ -1719,7 +1840,7 @@ bzr_register =
                error "cannot determine bzr url"
        fi
        echo "Registering bzr url: $url in $MR_CONFIG"
                error "cannot determine bzr url"
        fi
        echo "Registering bzr url: $url in $MR_CONFIG"
-       mr -c "$MR_CONFIG" config "`pwd`" checkout="bzr clone '$url' '$MR_REPO'"
+       mr -c "$MR_CONFIG" config "`pwd`" checkout="bzr branch '$url' '$MR_REPO'"
 cvs_register =
        repo=`cat CVS/Repository`
        root=`cat CVS/Root`
 cvs_register =
        repo=`cat CVS/Repository`
        root=`cat CVS/Root`
@@ -1752,7 +1873,7 @@ fossil_register =
 svn_trusted_checkout = svn co $url $repo
 svn_alt_trusted_checkout = svn checkout $url $repo
 git_trusted_checkout = git clone $url $repo
 svn_trusted_checkout = svn co $url $repo
 svn_alt_trusted_checkout = svn checkout $url $repo
 git_trusted_checkout = git clone $url $repo
-bzr_trusted_checkout = bzr clone $url $repo
+bzr_trusted_checkout = bzr checkout|clone|branch|get $url $repo
 # cvs: too hard
 hg_trusted_checkout = hg clone $url $repo
 darcs_trusted_checkout = darcs get $url $repo
 # cvs: too hard
 hg_trusted_checkout = hg clone $url $repo
 darcs_trusted_checkout = darcs get $url $repo