]> 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 ability to skip doing eg, updates, more frequently than a specified number of...
[code/myrepos.git] / mr
diff --git a/mr b/mr
index 314de600f7708a2ec026e309c31587262260a6c1..06829d2c9abaaddcdab5861cfadb6888c76ee8f5 100755 (executable)
--- a/mr
+++ b/mr
@@ -18,6 +18,10 @@ B<mr> [options] diff
 
 B<mr> [options] log
 
+B<mr> [options] register [repository]
+
+B<mr> [options] config section [parameter=[value] ...]
+
 B<mr> [options] action [params ...]
 
 =head1 DESCRIPTION
@@ -74,6 +78,24 @@ Show the commit log.
 
 List the repositories that mr will act on.
 
+=item register
+
+Register an existing repository in the mrconfig file. By default, the
+epository in the current directory is registered, or you can specify a
+directory to register.
+
+=item config
+
+Adds, modifies, removed, or prints a value from the mrconfig file. The next
+parameter is the name of the section the value is in. To add or modify a
+value, follow it by one or more instances of "parameter=value". Use
+"parameter=" to remove a parameter. Use just "parameter" to get the value
+of a parameter.
+
+For example, to add (or edit) a repository in src/foo:
+
+  mr config src/foo checkout="svn co svn://example.com/foo/trunk foo"
+
 =item help
 
 Displays this help.
@@ -84,10 +106,10 @@ Actions can be abbreviated to any unambiguous subsctring, so
 "mr st" is equivilant to "mr status", and "mr up" is equivilant to "mr
 update"
 
-Additional parameters can be passed to other commands than "commit", they
-will be 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 system.
+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
+system.
 
 =head1 OPTIONS
 
@@ -158,7 +180,16 @@ A few parameters have special meanings:
 =item skip
 
 If the "skip" parameter is set and its command returns nonzero, then B<mr>
-will skip acting on that repository.
+will skip acting on that repository. The command is passed the action
+name in $1.
+
+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.
+
+  skip = test $(whoami) != joey
+  skip = [ "$1" = update ] && [ $(hours_since "$1") -lt 12 ]
 
 =item chain
 
@@ -166,6 +197,14 @@ If the "chain" parameter is set and its command returns nonzero, then B<mr>
 will try to load a .mrconfig file from the root of the repository. (You
 should avoid chaining from repositories with untrusted committers.)
 
+=item deleted
+
+If the "deleted" parameter is set and its command returns nonzero, 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 repsoitory'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
@@ -240,7 +279,43 @@ if (! exists $knownactions{$action}) {
 }
 
 if ($action eq 'help') {
-       exec($config{''}{DEFAULT}{help});
+       exec($config{''}{DEFAULT}{$action}) || die "exec: $!";
+}
+elsif ($action eq 'config') {
+       if (@ARGV < 2) {
+               die "mr config: not enough parameters\n";
+       }
+       my $section=shift;
+       if ($section=~/^\//) {
+               # try to convert to a path relative to $config's dir
+               my ($dir)=$config=~/^(.*\/)[^\/]+$/;
+               if ($section=~/^\Q$dir\E(.*)/) {
+                       $section=$1;
+               }
+       }
+       my %changefields;
+       foreach (@ARGV) {
+               if (/^([^=]+)=(.*)$/) {
+                       $changefields{$1}=$2;
+               }
+               else {
+                       foreach my $topdir (sort keys %config) {
+                               if (exists $config{$topdir}{$section} &&
+                                   exists $config{$topdir}{$section}{$_}) {
+                                       print $config{$topdir}{$section}{$_}."\n";
+                               }
+                       }
+               }
+       }
+       modifyconfig($config, $section, %changefields) if %changefields;
+       exit 0;
+}
+elsif ($action eq 'register') {
+       my $command="set -e; ".$config{''}{DEFAULT}{lib}."\n".
+               "my_action(){ $config{''}{DEFAULT}{$action}\n }; my_action ".
+               join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
+       print STDERR "mr $action: running >>$command<<\n" if $verbose;
+       exec($command) || die "exec: $!";
 }
 
 # work out what repos to act on
@@ -284,7 +359,23 @@ sub action {
        my ($action, $dir, $topdir, $subdir) = @_;
        
        my $lib= exists $config{$topdir}{$subdir}{lib} ?
-                       $config{$topdir}{$subdir}{lib} : "";
+                       $config{$topdir}{$subdir}{lib}."\n" : "";
+
+       if (exists $config{$topdir}{$subdir}{deleted}) {
+               if (! -d $dir) {
+                       return;
+               }
+               else {
+                       my $test="set -e;".$lib.$config{$topdir}{$subdir}{deleted};
+                       print "mr $action: running deleted test >>$test<<\n" if $verbose;
+                       my $ret=system($test);
+                       if ($ret >> 8 == 0) {
+                               print STDERR "mr error: $dir should be deleted yet still exists\n\n";
+                               push @failed, $dir;
+                               return;
+                       }
+               }
+       }
 
        if ($action eq 'checkout') {
                if (-d $dir) {
@@ -303,8 +394,9 @@ sub action {
        $ENV{MR_REPO}=$dir;
 
        if (exists $config{$topdir}{$subdir}{skip}) {
-               my $test="set -e;".$lib.$config{$topdir}{$subdir}{skip};
-               print "mr $action: running skip test $test\n" if $verbose;
+               my $test="set -e;".$lib.
+                       "my_action(){ $config{$topdir}{$subdir}{skip}\n }; my_action '$action'";
+               print "mr $action: running skip test >>$test<<\n" if $verbose;
                my $ret=system($test);
                if ($ret >> 8 == 0) {
                        print "mr $action: $dir skipped per config file\n" if $verbose;
@@ -329,9 +421,9 @@ sub action {
                        print "mr $action: $topdir$subdir (in subdir $directory)\n";
                }
                my $command="set -e; ".$lib.
-                       "my_action(){ $config{$topdir}{$subdir}{$action} ; }; my_action ".
+                       "my_action(){ $config{$topdir}{$subdir}{$action}\n }; my_action ".
                        join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
-               print STDERR "mr $action: running $command\n" if $verbose;
+               print STDERR "mr $action: running >>$command<<\n" if $verbose;
                my $ret=system($command);
                if ($ret != 0) {
                        print STDERR "mr $action: failed ($ret)\n" if $verbose;
@@ -389,7 +481,10 @@ sub loadconfig {
                $dir="";
        }
        else {
-               # $f might be a symlink
+               if (! -e $f) {
+                       return;
+               }
+
                my $absf=abs_path($f);
                if ($loaded{$absf}) {
                        return;
@@ -427,8 +522,8 @@ sub loadconfig {
                        my $value=$2;
 
                        # continuation line
-                       while ($value=~/(.*)\\$/) {
-                               $value=$1.<$in>;
+                       while ($value=~/(.*)\\$/s) {
+                               $value=$1."\n".<$in>;
                                chomp $value;
                        }
 
@@ -445,7 +540,7 @@ sub loadconfig {
                                $alias{$parameter}=$value;
                        }
                        elsif ($parameter eq 'lib') {
-                               $config{$dir}{$section}{lib}.=$value." ; ";
+                               $config{$dir}{$section}{lib}.=$value."\n";
                        }
                        else {
                                $config{$dir}{$section}{$parameter}=$value;
@@ -469,6 +564,94 @@ sub loadconfig {
        }
 }
 
+sub modifyconfig {
+       my $f=shift;
+       # the section to modify or add
+       my $targetsection=shift;
+       # fields to change in the section
+       # To remove a field, set its value to "".
+       my %changefields=@_;
+
+       my @lines;
+       my @out;
+
+       if (-e $f) {
+               open(my $in, "<", $f) || die "mr: open $f: $!\n";
+               @lines=<$in>;
+               close $in;
+       }
+
+       my $addfields=sub {
+               my @blanks;
+               while ($out[$#out] =~ /^\s*$/) {
+                       unshift @blanks, pop @out;
+               }
+               foreach my $field (sort keys %changefields) {
+                       if (length $changefields{$field}) {
+                               push @out, "$field = $changefields{$field}\n";
+                       }
+               }
+               push @out, @blanks;
+       };
+
+       my $section;
+       while (@lines) {
+               $_=shift(@lines);
+
+               if (/^\s*\#/ || /^\s*$/) {
+                       push @out, $_;
+               }
+               elsif (/^\s*\[([^\]]*)\]\s*$/) {
+                       if (defined $section && 
+                           $section eq $targetsection) {
+                               $addfields->();
+                       }
+
+                       $section=$1;
+
+                       push @out, $_;
+               }
+               elsif (/^\s*(\w+)\s*=\s(.*)/) {
+                       my $parameter=$1;
+                       my $value=$2;
+
+                       # continuation line
+                       while ($value=~/(.*\\)$/s) {
+                               $value=$1."\n".shift(@lines);
+                               chomp $value;
+                       }
+
+                       if ($section eq $targetsection) {
+                               if (exists $changefields{$parameter}) {
+                                       if (length $changefields{$parameter}) {
+                                               $value=$changefields{$parameter};
+                                       }
+                                       delete $changefields{$parameter};
+                               }
+                       }
+
+                       push @out, "$parameter = $value\n";
+               }
+       }
+
+       if (defined $section && 
+           $section eq $targetsection) {
+                       $addfields->();
+       }
+       elsif (%changefields) {
+               push @out, "\n[$targetsection]\n";
+               foreach my $field (sort keys %changefields) {
+                       if (length $changefields{$field}) {
+                               push @out, "$field = $changefields{$field}\n";
+                       }
+               }
+       }
+
+       open(my $out, ">", $f) || die "mr: write $f: $!\n";
+       print $out @out;
+       close $out;     
+}
+
 # Finally, some useful actions that mr knows about by default.
 # These can be overridden in ~/.mrconfig.
 __DATA__
@@ -478,79 +661,122 @@ __DATA__
        ls = list
 
 [DEFAULT]
-lib = \
-       error() { \
-               echo "mr: $@" >&2; \
-               exit 1; \
+lib =                                                                  \
+       error() {                                                       \
+               echo "mr: $@" >&2                                       \
+               exit 1                                                  \
+       }                                                               \
+       hours_since() {                                                 \
+               for dir in .git .svn .bzr CVS; do                       \
+                       if [ -e "$MR_REPO/$dir" ]; then                 \
+                               flagfile="$MR_REPO/$dir/.mr_last$1"     \
+                               break                                   \
+                       fi                                              \
+               done                                                    \
+               if [ -z "$flagfile" ]; then                             \
+                       error "cannot determine flag filename"          \
+               fi                                                      \
+               perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile" \
+               touch "$flagfile"                                       \
        }
 
-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 \
-               error "unknown repo type"; \
+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                                                            \
+               error "unknown repo type"                               \
        fi
-status = \
-       if [ -d "$MR_REPO"/.svn ]; then \
-               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 \
-               error "unknown repo type"; \
+status =                                                               \
+       if [ -d "$MR_REPO"/.svn ]; then                                 \
+               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                                                            \
+               error "unknown repo type"                               \
        fi
-commit = \
-       if [ -d "$MR_REPO"/.svn ]; then \
-               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 \
-               error "unknown repo type"; \
+commit =                                                               \
+       if [ -d "$MR_REPO"/.svn ]; then                                 \
+               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                                                            \
+               error "unknown repo type"                               \
        fi
-diff = \
-       if [ -d "$MR_REPO"/.svn ]; then \
-               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 \
-               error "unknown repo type"; \
+diff =                                                                 \
+       if [ -d "$MR_REPO"/.svn ]; then                                 \
+               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                                                            \
+               error "unknown repo type"                               \
        fi
-log = \
-       if [ -d "$MR_REPO"/.svn ]; then \
-               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"; \
+log =                                                                  \
+       if [ -d "$MR_REPO"/.svn ]; then                                 \
+               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
+register =                                                             \
+       if [ -n "$1" ]; then                                            \
+               cd "$1"                                                 \
+       fi                                                              \
+       basedir="$(basename $(pwd))"                                    \
+       if [ -d .svn ]; then                                            \
+               url=$(LANG=C svn info . |                               \
+                     grep -i ^URL: | cut -d ' ' -f 2)                  \
+               if [ -z "$url" ]; then                                  \
+                       error "cannot determine svn url"                \
+               fi                                                      \
+               echo "Registering svn url: $url"                        \
+               mr config "$(pwd)" checkout="svn co $url $basedir"      \
+       elif [ -d .git ]; then                                          \
+               url=$(LANG=C git-config --get remote.origin.url)        \
+               if [ -z "$url" ]; then                                  \
+                       error "cannot determine git url"                \
+               fi                                                      \
+               echo "Registering git url: $url"                        \
+               mr config "$(pwd)" checkout="git clone $url $basedir"   \
+       elif [ -d .bzr ]; then                                          \
+               url=$(cat .bzr/branch/parent)                           \
+               if [ -z "$url" ]; then                                  \
+                       error "cannot determine bzr url"                \
+               fi                                                      \
+               echo "Registering bzr url: $url"                        \
+               mr config "$(pwd)" checkout="bzr clone $url $basedir"   \
+       else                                                            \
+               error "unable to register this repo type"               \
        fi
 list = true
-
-help = \
-       if [ ! -e "$MR_PATH" ]; then \
-               error "cannot find program path";\
-       fi; \
-       (pod2man -c mr "$MR_PATH" | man -l -) || \
+config = 
+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.."