X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/f2cabdb155025f5441df135c91e39abf1c4c3756..832fadc6fc84658766e966137633bcfff6aa35c2:/mr?ds=inline diff --git a/mr b/mr index f7509c9..2bb3efa 100755 --- a/mr +++ b/mr @@ -37,8 +37,8 @@ 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 and darcs repositories, and support for other revision control systems can -easily be added. +bzr, darcs and fossil repositories, and support for other revision +control systems can easily be added. B cds into and operates on all registered repositories at or below your working directory. Or, if you are in a subdirectory of a repository that @@ -115,9 +115,6 @@ specified, the current directory will be used. If the .mrconfig file includes a repository named ".", that is checked out into the top of the specified directory. -To bootstrap from a SSL url with an untrusted certificate, you may -need to specify the -k option. - =item list (or ls) List the repositories that mr will act on. @@ -275,7 +272,7 @@ Use with caution. =back -=head1 "MRCONFIG FILES" +=head1 MRCONFIG FILES Here is an example .mrconfig file: @@ -373,6 +370,20 @@ 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. +=item fixups + +If the "fixups" parameter is set, its command is run whenever a repository +is checked out, or updated. This provides an easy way to do things +like permissions fixups, or other tweaks to the repository content, +whenever the repository is changed. + +=item pre_ and post_ + +If a "pre_action" parameter is set, its command is run before mr performs the +specified action. Similarly, "post_action" parameters are run after mr +successfully performs the specified action. For example, "pre_commit" is +run before committing; "post_update" is run after updating. + =back When looking for a command to run for a given action, mr first looks for @@ -392,10 +403,10 @@ due to being offline. You can delete or edit this file to remove commands, or even to add other commands for 'mr online' to run. If the file is present, mr assumes it is in offline mode. -=head1 "UNTRUSTED MRCONFIG FILES" +=head1 UNTRUSTED MRCONFIG FILES Since mrconfig files can contain arbitrary shell commands, they can do -anything. This flexability is good, but it also allows a malicious mrconfig +anything. This flexibility is good, but it also allows a malicious mrconfig file to delete your whole home directory. Such a file might be contained inside a repository that your main ~/.mrconfig checks out and chains to. To avoid worries about evil commands in a mrconfig file, mr @@ -416,7 +427,7 @@ the documentation in the files for details about using them. =head1 AUTHOR -Copyright 2007-2009 Joey Hess +Copyright 2007-2010 Joey Hess Licensed under the GNU GPL version 2 or higher. @@ -487,7 +498,7 @@ sub rcs_test { chomp $rcs; if ($rcs=~/\n/s) { $rcs=~s/\n/, /g; - print STDERR "mr $action: found multiple possible repository types ($rcs) for $topdir$subdir\n"; + print STDERR "mr $action: found multiple possible repository types ($rcs) for ".fulldir($topdir, $subdir)."\n"; return undef; } if (! length $rcs) { @@ -520,13 +531,20 @@ sub findcommand { } } +sub fulldir { + my ($topdir, $subdir) = @_; + return $subdir =~ /^\// ? $subdir : $topdir.$subdir; +} + sub action { my ($action, $dir, $topdir, $subdir, $force_checkout) = @_; - + 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/); $ENV{MR_REPO}=$dir; @@ -540,7 +558,7 @@ sub action { $dir=~s/^(.*)\/[^\/]+\/?$/$1/; } } - elsif ($action =~ /update/) { + elsif ($is_update) { if (! -d $dir) { return action("checkout", $dir, $topdir, $subdir); } @@ -582,23 +600,27 @@ sub action { elsif (! defined $command) { my $rcs=rcs_test(@_); if (! defined $rcs) { - print STDERR "mr $action: unknown repository type and no defined $action command for $topdir$subdir\n"; + 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 $topdir$subdir, skipping\n"; + print STDERR "mr $action: no defined action for $rcs repository $fulldir, skipping\n"; return SKIPPED; } } else { if (! $no_chdir) { - print "mr $action: $topdir$subdir\n" unless $quiet; + print "mr $action: $fulldir\n" unless $quiet; } else { my $s=$directory; - $s=~s/^\Q$topdir$subdir\E\/?//; - print "mr $action: $topdir$subdir (in subdir $s)\n" unless $quiet; + $s=~s/^\Q$fulldir\E\/?//; + print "mr $action: $fulldir (in subdir $s)\n" unless $quiet; } + + 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); @@ -631,16 +653,49 @@ sub action { return FAILED; } else { - if ($action eq 'checkout' && ! -d $dir) { + if ($is_checkout && ! -d $dir) { print STDERR "mr $action: $dir missing after checkout\n";; return FAILED; } + my $ret=hook("post_$action", $topdir, $subdir); + return $ret if $ret != OK; + + if (($is_checkout || $is_update)) { + my $ret=hook("fixups", $topdir, $subdir); + return $ret if $ret != OK; + } + return OK; } } } +sub hook { + my ($hook, $topdir, $subdir) = @_; + + 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=system($shell); + if ($ret != 0) { + if (($? & 127) == 2) { + print STDERR "mr $hook: interrupted\n"; + return ABORT; + } + elsif ($? & 127) { + print STDERR "mr $hook: received signal ".($? & 127)."\n"; + return ABORT; + } + } + + return OK; +} + # run actions on multiple repos, in parallel sub mrs { my $action=shift; @@ -790,6 +845,15 @@ sub repolist { } @list; } +sub repodir { + my $repo=shift; + my $topdir=$repo->{topdir}; + my $subdir=$repo->{subdir}; + my $ret=($subdir =~/^\//) ? $subdir : $topdir.$subdir; + $ret=~s/\/\.$//; + return $ret; +} + # figure out which repos to act on sub selectrepos { my @repos; @@ -798,7 +862,7 @@ sub selectrepos { my $subdir=$repo->{subdir}; next if $subdir eq 'DEFAULT'; - my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir; + my $dir=repodir($repo); my $d=$directory; $dir.="/" unless $dir=~/\/$/; $d.="/" unless $d=~/\/$/; @@ -818,7 +882,7 @@ sub selectrepos { my $subdir=$repo->{subdir}; next if $subdir eq 'DEFAULT'; - my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir; + my $dir=repodir($repo); my $d=$directory; $dir.="/" unless $dir=~/\/$/; $d.="/" unless $d=~/\/$/; @@ -1031,6 +1095,12 @@ sub loadconfig { } } $section=expandenv($section) if $trusted; + if ($section ne 'ALIAS' && + ! exists $config{$dir}{$section} && + exists $config{$dir}{DEFAULT}) { + # copy in defaults + $config{$dir}{$section}={ %{$config{$dir}{DEFAULT}} }; + } } elsif (/^(\w+)\s*=\s*(.*)/) { my $parameter=$1; @@ -1067,12 +1137,6 @@ sub loadconfig { if (! defined $section) { die "$f line $.: parameter ($parameter) not in section\n"; } - if ($section ne 'ALIAS' && - ! exists $config{$dir}{$section} && - exists $config{$dir}{DEFAULT}) { - # copy in defaults - $config{$dir}{$section}={ %{$config{$dir}{DEFAULT}} }; - } if ($section eq 'ALIAS') { $alias{$parameter}=$value; } @@ -1353,7 +1417,7 @@ sub bootstrap { eval q{use File::Temp}; die $@ if $@; my $tmpconfig=File::Temp->new(); - my @curlargs = ("curl", "-A", "mr", "-s", $url, "-o", $tmpconfig); + my @curlargs = ("curl", "-A", "mr", "-L", "-s", $url, "-o", $tmpconfig); push(@curlargs, "-k") if $insecure; my $curlstatus = system(@curlargs); die "mr bootstrap: invalid SSL certificate for $url (consider -k)\n" if $curlstatus >> 8 == 60; @@ -1436,7 +1500,7 @@ sub getopts { "v|verbose" => \$verbose, "q|quiet" => \$quiet, "s|stats" => \$stats, - "k|insecure" => \$insecure, + "k|insecure" => \$insecure, "i|interactive" => \$interactive, "n|no-recurse:i" => \$max_depth, "j|jobs:i" => \$jobs, @@ -1530,7 +1594,7 @@ lib = if [ -z "$1" ] || [ -z "$2" ]; then error "mr: usage: hours_since action num" fi - for dir in .git .svn .bzr CVS .hg _darcs; do + for dir in .git .svn .bzr CVS .hg _darcs _FOSSIL_; do if [ -e "$MR_REPO/$dir" ]; then flagfile="$MR_REPO/$dir/.mr_last$1" break @@ -1541,10 +1605,10 @@ lib = fi delta=`perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile"` if [ "$delta" -lt "$2" ]; then - exit 0 + return 1 else touch "$flagfile" - exit 1 + return 0 fi } @@ -1554,6 +1618,7 @@ 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 && @@ -1565,13 +1630,15 @@ bzr_update = bzr merge --pull "$@" cvs_update = cvs update "$@" hg_update = hg pull "$@" && hg update "$@" darcs_update = darcs pull -a "$@" +fossil_update = fossil pull "$@" svn_status = svn status "$@" -git_status = git status "$@" || true -bzr_status = bzr status "$@" +git_status = git status -s "$@" || true +bzr_status = bzr status --short "$@" cvs_status = cvs status "$@" hg_status = hg status "$@" darcs_status = darcs whatsnew -ls "$@" || true +fossil_status = fossil changes "$@" svn_commit = svn commit "$@" git_commit = git commit -a "$@" && git push --all @@ -1579,11 +1646,13 @@ bzr_commit = bzr commit "$@" && bzr push 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 "$@" hg_record = hg commit -m "$@" darcs_record = darcs record -a -m "$@" +fossil_record = fossil commit "$@" svn_push = : git_push = git push "$@" @@ -1591,6 +1660,7 @@ bzr_push = bzr push "$@" cvs_push = : hg_push = hg push "$@" darcs_push = darcs push -a "$@" +fossil_push = fossil push "$@" svn_diff = svn diff "$@" git_diff = git diff "$@" @@ -1598,6 +1668,7 @@ bzr_diff = bzr diff "$@" cvs_diff = cvs diff "$@" hg_diff = hg diff "$@" darcs_diff = darcs diff -u "$@" +fossil_diff = fossil diff "$@" svn_log = svn log "$@" git_log = git log "$@" @@ -1606,6 +1677,7 @@ cvs_log = cvs log "$@" hg_log = hg log "$@" darcs_log = darcs changes "$@" git_bare_log = git log "$@" +fossil_log = fossil timeline "$@" svn_register = url=`LC_ALL=C svn info . | grep -i '^URL:' | cut -d ' ' -f 2` @@ -1651,6 +1723,11 @@ git_bare_register = fi echo "Registering git url: $url in $MR_CONFIG" mr -c "$MR_CONFIG" config "`pwd`" checkout="git clone --bare '$url' '$MR_REPO'" +fossil_register = + url=`fossil remote-url` + 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'" svn_trusted_checkout = svn co $url $repo svn_alt_trusted_checkout = svn checkout $url $repo @@ -1660,6 +1737,7 @@ bzr_trusted_checkout = bzr clone $url $repo 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 help =