]> 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:

Initial add of veracity (http://veracity-scm.com/) support.
[code/myrepos.git] / mr
diff --git a/mr b/mr
index f14ba4dddebfd38fb55f9a823e7cea2e7050b2b2..1307b545a919ffa0bf8f344a19e79fa5aec6edf6 100755 (executable)
--- a/mr
+++ b/mr
@@ -41,7 +41,7 @@ B<mr> [options] remember action [params ...]
 B<mr> 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 version
+bzr, darcs, fossil and veracity repositories, and support for other version
 control systems can easily be added.
 
 B<mr> cds into and operates on all registered repositories at or below your
@@ -212,6 +212,13 @@ Use the specified mrconfig file. The default is to use both F<~/.mrconfig>
 as well as look for a F<.mrconfig> file in the current directory, or in one
 of its parent directories.
 
+=item -f
+
+=item --force
+
+Force mr to act on repositories that would normally be skipped due to their
+configuration.
+
 =item -v
 
 =item --verbose
@@ -367,7 +374,7 @@ been at least 12 hours since the last update.
  
 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").
+repo, you have to explicitly check it out (using "mr --force -d foo checkout").
 
   [foo]
   checkout = ...
@@ -435,9 +442,7 @@ whenever the repository is changed.
 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 "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.
+version control system and the action).
 
 Internally, mr has settings for "git_update", "svn_update", etc. To change
 the action that is performed for a given version control system, you can
@@ -457,6 +462,11 @@ Any parameter can be suffixed with C<_append>, to add an additional value
 to the existing value of the parameter. In this way, actions 
 can be constructed accumulatively.
 
+=item VCS_test
+
+The name of the version control system is itself determined by
+running each defined "VCS_test" action, until one succeeds.
+
 =back
 
 =head1 UNTRUSTED MRCONFIG FILES
@@ -520,6 +530,7 @@ my $config_overridden=0;
 my $verbose=0;
 my $quiet=0;
 my $stats=0;
+my $force=0;
 my $insecure=0;
 my $interactive=0;
 my $max_depth;
@@ -528,6 +539,7 @@ my $jobs=1;
 my $trust_all=0;
 my $directory=getcwd();
 
+my $HOME_MR_CONFIG = "$ENV{HOME}/.mrconfig";
 $ENV{MR_CONFIG}=find_mrconfig();
 
 # globals :-(
@@ -539,6 +551,57 @@ my (@ok, @failed, @skipped);
 
 main();
 
+sub shellquote {
+       my $i=shift;
+       $i=~s/'/'"'"'/g;
+       return "'$i'";
+}
+
+# Runs a shell command using a supplied function.
+# The lib will be included in the shell command line, and any params
+# will be available in the shell as $1, $2, etc.
+my $lastlib;
+sub runsh {
+       my ($action, $topdir, $subdir, $command, $params, $runner) = @_;
+
+       # optimisation: avoid running the shell for true and false
+       if ($command =~ /^\s*true\s*$/) {
+               $?=0;
+               return 0;
+       }
+       elsif ($command =~ /^\s*false\s*$/) {
+               $?=0;
+               return 1;
+       }
+       
+       my $quotedparams=join(" ", (map { shellquote($_) } @$params));
+       my $lib=exists $config{$topdir}{$subdir}{lib} ?
+                      $config{$topdir}{$subdir}{lib}."\n" : "";
+       if ($verbose && (! defined $lastlib || $lastlib ne $lib)) {
+               print "mr library now: >>$lib<<\n";
+               $lastlib=$lib;
+       }
+       my $shellcode="set -e;".$lib.
+               "my_sh(){ $command\n }; my_sh $quotedparams";
+       print "mr $action: running $action >>$command<<\n" if $verbose;
+       $runner->($shellcode);
+}
+
+my %perl_cache;
+sub perl {
+       my $id=shift;
+       my $s=shift;
+       if ($s =~ m/^perl:\s+(.*)/s) {
+               return $perl_cache{$1} if exists $perl_cache{$1};
+               my $sub=eval "sub {$1}";
+               if (! defined $sub) {
+                       print STDERR "mr: bad perl code in $id: $@\n";
+               }
+               return $perl_cache{$1} = $sub;
+       }
+       return undef;
+}
+
 my %vcs;
 sub vcs_test {
        my ($action, $dir, $topdir, $subdir) = @_;
@@ -547,33 +610,47 @@ sub vcs_test {
                return $vcs{$dir};
        }
 
-       my $test="set -e\n";
+       my $test="";
+       my %perltest;
        foreach my $vcs_test (
                        sort {
                                length $a <=> length $b 
                                          ||
                                       $a cmp $b
                        } grep { /_test$/ } keys %{$config{$topdir}{$subdir}}) {
-               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";
+               my ($vcs)=$vcs_test =~ /(.*)_test/;
+               my $p=perl($vcs_test, $config{$topdir}{$subdir}{$vcs_test});
+               if (defined $p) {
+                       $perltest{$vcs}=$p;
+               }
+               else {
+                       $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 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";
+
+       my @vcs;
+       foreach my $vcs (keys %perltest) {
+               if ($perltest{$vcs}->()) {
+                       push @vcs, $vcs;
+               }
+       }
+
+       push @vcs, split(/\n/,
+               runsh("vcs test", $topdir, $subdir, $test, [], sub {
+                       my $sh=shift;
+                       my $ret=`$sh`;
+                       return $ret;
+               })) if length $test;
+       if (@vcs > 1) {
+               print STDERR "mr $action: found multiple possible repository types (@vcs) for ".fulldir($topdir, $subdir)."\n";
                return undef;
        }
-       if (! length $vcs) {
+       if (! @vcs) {
                return $vcs{$dir}=undef;
        }
        else {
-               return $vcs{$dir}=$vcs;
+               return $vcs{$dir}=$vcs[0];
        }
 }
        
@@ -609,8 +686,6 @@ sub action {
        my $fulldir=fulldir($topdir, $subdir);
 
        $ENV{MR_CONFIG}=$configfiles{$topdir};
-       my $lib=exists $config{$topdir}{$subdir}{lib} ?
-                      $config{$topdir}{$subdir}{lib}."\n" : "";
        my $is_checkout=($action eq 'checkout');
        my $is_update=($action =~ /update/);
 
@@ -618,13 +693,14 @@ sub action {
        $ENV{MR_ACTION}=$action;
        
        foreach my $testname ("skip", "deleted") {
+               next if $force && $testname eq "skip";
+
                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);
+                       my $ret=runsh "$testname test", $topdir, $subdir,
+                               $testcommand, [$action],
+                               sub { system(shift()) };
                        if ($ret != 0) {
                                if (($? & 127) == 2) {
                                        print STDERR "mr $action: interrupted\n";
@@ -701,22 +777,22 @@ sub action {
                my $hookret=hook("pre_$action", $topdir, $subdir);
                return $hookret if $hookret != OK;
 
-               $command="set -e; ".$lib.
-                       "my_action(){ $command\n }; my_action ".
-                       join(" ", map { s/\\/\\\\/g; s/"/\"/g; '"'.$_.'"' } @ARGV);
-               print "mr $action: running >>$command<<\n" if $verbose;
-               my $ret;
-               if ($quiet) {
-                       my $output = qx/$command 2>&1/;
-                       $ret = $?;
-                       if ($ret != 0) {
-                               print "$actionmsg\n";
-                               print STDERR $output;
-                       }
-               }
-               else {
-                       $ret=system($command);
-               }
+               my $ret=runsh $action, $topdir, $subdir,
+                       $command, \@ARGV, sub {
+                               my $sh=shift;
+                               if ($quiet) {
+                                       my $output = qx/$sh 2>&1/;
+                                       my $ret = $?;
+                                       if ($ret != 0) {
+                                               print "$actionmsg\n";
+                                               print STDERR $output;
+                                       }
+                                       return $ret;
+                               }
+                               else {
+                                       system($sh);
+                               }
+                       };
                if ($ret != 0) {
                        if (($? & 127) == 2) {
                                print STDERR "mr $action: interrupted\n";
@@ -767,22 +843,20 @@ sub hook {
 
        my $command=$config{$topdir}{$subdir}{$hook};
        return OK unless defined $command;
-       my $lib=exists $config{$topdir}{$subdir}{lib} ?
-                      $config{$topdir}{$subdir}{lib}."\n" : "";
-       my $shell="set -e;".$lib.
-               "my_hook(){ $command\n }; my_hook";
-       print "mr $hook: running >>$shell<<\n" if $verbose;
-       my $ret;
-       if ($quiet) {
-               my $output = qx/$shell 2>&1/;
-               $ret = $?;
-               if ($ret != 0) {
-                       print STDERR $output;
-               }
-       }
-       else {
-               $ret=system($shell);
-       }
+       my $ret=runsh $hook, $topdir, $subdir, $command, [], sub {
+                       my $sh=shift;
+                       if ($quiet) {
+                               my $output = qx/$sh 2>&1/;
+                               my $ret = $?;
+                               if ($ret != 0) {
+                                       print STDERR $output;
+                               }
+                               return $ret;
+                       }
+                       else {
+                               system($sh);
+                       }
+               };
        if ($ret != 0) {
                if (($? & 127) == 2) {
                        print STDERR "mr $hook: interrupted\n";
@@ -1020,14 +1094,14 @@ sub is_trusted_config {
        my $config=shift; # must be abs_pathed already
 
        # We always trust ~/.mrconfig.
-       return 1 if $config eq abs_path("$ENV{HOME}/.mrconfig");
+       return 1 if $config eq abs_path($HOME_MR_CONFIG);
 
        return 1 if $trust_all;
 
        my $trustfile=$ENV{HOME}."/.mrtrust";
 
        if (! %trusted) {
-               $trusted{"$ENV{HOME}/.mrconfig"}=1;
+               $trusted{$HOME_MR_CONFIG}=1;
                if (open (TRUST, "<", $trustfile)) {
                        while (<TRUST>) {
                                chomp;
@@ -1210,15 +1284,14 @@ sub loadconfig {
        };
        my $trusterror = sub {
                my $msg=shift;
-               my ($err, $file, $lineno, $url)=@_;
        
                if (defined $bootstrap_url) {
-                       die "mr: $err in untrusted $bootstrap_url line $lineno\n".
+                       die "mr: $msg in untrusted $bootstrap_url line $lineno\n".
                                "(To trust this url, --trust-all can be used; but please use caution;\n".
                                "this can allow arbitrary code execution!)\n";
                }
                else {
-                       die "mr: $err in untrusted $file line $lineno\n".
+                       die "mr: $msg in untrusted $f line $lineno\n".
                                "(To trust this file, list it in ~/.mrtrust.)\n";
                }
        };
@@ -1650,7 +1723,7 @@ sub find_mrconfig {
                }
                $dir=~s/\/[^\/]*$//;
        }
-       return "$ENV{HOME}/.mrconfig";
+       return $HOME_MR_CONFIG;
 }
 
 sub getopts {
@@ -1660,6 +1733,7 @@ sub getopts {
                "d|directory=s" => sub { $directory=abs_path($_[1]) },
                "c|config=s" => sub { $ENV{MR_CONFIG}=$_[1]; $config_overridden=1 },
                "p|path" => sub { }, # now default, ignore
+               "f|force" => \$force,
                "v|verbose" => \$verbose,
                "q|quiet" => \$quiet,
                "s|stats" => \$stats,
@@ -1719,7 +1793,7 @@ sub main {
        init();
 
        startingconfig();
-       loadconfig("$ENV{HOME}/.mrconfig");
+       loadconfig($HOME_MR_CONFIG);
        loadconfig($ENV{MR_CONFIG});
        #use Data::Dumper; print Dumper(\%config);
        
@@ -1776,24 +1850,25 @@ lib =
                LANG=C bzr info | egrep -q '^Checkout'
        }
        lazy() {
-               if [ "$MR_ACTION" = checkout ] || [ -d "$MR_REPO" ]; then
+               if [ -d "$MR_REPO" ]; then
                        return 1
                else
                        return 0
                fi
        }
 
-svn_test = test -d "$MR_REPO"/.svn
-git_test = test -d "$MR_REPO"/.git
-bzr_test = test -d "$MR_REPO"/.bzr
-cvs_test = test -d "$MR_REPO"/CVS
-hg_test  = test -d "$MR_REPO"/.hg
-darcs_test = test -d "$MR_REPO"/_darcs
-fossil_test = test -f "$MR_REPO"/_FOSSIL_
-git_bare_test =
-       test -d "$MR_REPO"/refs/heads && test -d "$MR_REPO"/refs/tags &&
-       test -d "$MR_REPO"/objects && test -f "$MR_REPO"/config &&
-       test "`GIT_CONFIG="$MR_REPO"/config git config --get core.bare`" = true
+svn_test = perl: -d "$ENV{MR_REPO}/.svn"
+git_test = perl: -d "$ENV{MR_REPO}/.git"
+bzr_test = perl: -d "$ENV{MR_REPO}/.bzr"
+cvs_test = perl: -d "$ENV{MR_REPO}/CVS"
+hg_test  = perl: -d "$ENV{MR_REPO}/.hg"
+darcs_test = perl: -d "$ENV{MR_REPO}/_darcs"
+fossil_test = perl: -f "$ENV{MR_REPO}/_FOSSIL_"
+git_bare_test = perl: 
+       -d "$ENV{MR_REPO}/refs/heads" && -d "$ENV{MR_REPO}/refs/tags" &&
+       -d "$ENV{MR_REPO}/objects" && -f "$ENV{MR_REPO}/config" &&
+       `GIT_CONFIG="$ENV{MR_REPO}"/config git config --get core.bare` =~ /true/
+veracity_test  = perl: -d "$ENV{MR_REPO}/.sgdrawer"
 
 svn_update = svn update "$@"
 git_update = git pull "$@"
@@ -1807,6 +1882,7 @@ cvs_update = cvs update "$@"
 hg_update  = hg pull "$@" && hg update "$@"
 darcs_update = darcs pull -a "$@"
 fossil_update = fossil pull "$@"
+veracity_update = vv pull "$@" && vv update "$@"
 
 svn_status = svn status "$@"
 git_status = git status -s "$@" || true
@@ -1815,6 +1891,7 @@ cvs_status = cvs status "$@"
 hg_status  = hg status "$@"
 darcs_status = darcs whatsnew -ls "$@" || true
 fossil_status = fossil changes "$@"
+veracity_status = vv status "$@"
 
 svn_commit = svn commit "$@"
 git_commit = git commit -a "$@" && git push --all
@@ -1828,6 +1905,7 @@ cvs_commit = cvs commit "$@"
 hg_commit  = hg commit -m "$@" && hg push
 darcs_commit = darcs record -a -m "$@" && darcs push -a
 fossil_commit = fossil commit "$@"
+veracity_commit = vv commit -m "@"
 
 git_record = git commit -a "$@"
 bzr_record =
@@ -1839,6 +1917,7 @@ bzr_record =
 hg_record  = hg commit -m "$@"
 darcs_record = darcs record -a -m "$@"
 fossil_record = fossil commit "$@"
+veracity_record = vv commit -m "@"
 
 svn_push = :
 git_push = git push "$@"
@@ -1847,6 +1926,7 @@ cvs_push = :
 hg_push = hg push "$@"
 darcs_push = darcs push -a "$@"
 fossil_push = fossil push "$@"
+veracity_push = vv push "$@"
 
 svn_diff = svn diff "$@"
 git_diff = git diff "$@"
@@ -1855,6 +1935,7 @@ cvs_diff = cvs diff "$@"
 hg_diff  = hg diff "$@"
 darcs_diff = darcs diff -u "$@"
 fossil_diff = fossil diff "$@"
+veracity_diff = vv diff "$@"
 
 svn_log = svn log "$@"
 git_log = git log "$@"
@@ -1864,6 +1945,7 @@ hg_log  = hg log "$@"
 darcs_log = darcs changes "$@"
 git_bare_log = git log "$@"
 fossil_log = fossil timeline "$@"
+veracity_log = vv log "$@"
 
 run = "$@"
 
@@ -1916,6 +1998,11 @@ fossil_register =
        repo=`fossil info | grep repository | sed -e 's/repository:*.//g' -e 's/ //g'`
        echo "Registering fossil repository $url in $MR_CONFIG"
        mr -c "$MR_CONFIG" config "`pwd`" checkout="mkdir -p '$MR_REPO' && cd '$MR_REPO' && fossil open '$repo'"
+veracity_register =
+       url=`vv config | grep sync_targets | sed -e 's/sync_targets:*.//g' -e 's/ //g'`
+       repo=`vv repo info | grep repository | sed -e 's/Current repository:*.//g' -e 's/ //g'`
+       echo "Registering veracity repository $url in $MR_CONFIG"
+       mr -c "$MR_CONFIG" config "`pwd`" checkout="mkdir -p '$MR_REPO' && cd '$MR_REPO' && vv checkout '$repo'"
 
 svn_trusted_checkout = svn co $url $repo
 svn_alt_trusted_checkout = svn checkout $url $repo
@@ -1926,6 +2013,7 @@ hg_trusted_checkout = hg clone $url $repo
 darcs_trusted_checkout = darcs get $url $repo
 git_bare_trusted_checkout = git clone --bare $url $repo
 # fossil: messy to do
+veracity_trusted_checkout = vv clone $url $repo
 
 
 help =