X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/66dc478e14c66b2865b39dc41dabd3a05de87656..de3cf4eb2025f65ee6b809277adbcaa287345da9:/mr diff --git a/mr b/mr index eb766d4..4350dad 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 @@ -219,6 +219,12 @@ Be verbose. Be quiet. +=item -k + +=item --insecure + +Accept untrusted SSL certificates when bootstrapping. + =item -s =item --stats @@ -266,7 +272,7 @@ Use with caution. =back -=head1 "MRCONFIG FILES" +=head1 MRCONFIG FILES Here is an example .mrconfig file: @@ -383,10 +389,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. -=head "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 @@ -407,7 +413,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. @@ -433,6 +439,7 @@ my $config_overridden=0; my $verbose=0; my $quiet=0; my $stats=0; +my $insecure=0; my $interactive=0; my $max_depth; my $no_chdir=0; @@ -707,7 +714,7 @@ sub record { if ($ret == OK) { push @ok, $dir; - print "\n"; + print "\n" unless $quiet; } elsif ($ret == FAILED) { if ($interactive) { @@ -716,7 +723,7 @@ sub record { system((getpwuid($<))[8], "-i"); } push @failed, $dir; - print "\n"; + print "\n" unless $quiet; } elsif ($ret == SKIPPED) { push @skipped, $dir; @@ -780,6 +787,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; @@ -788,7 +804,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=~/\/$/; @@ -808,7 +824,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=~/\/$/; @@ -1001,7 +1017,7 @@ sub loadconfig { open($in, "<", $f) || die "mr: open $f: $!\n"; } my @lines=<$in>; - close $in; + close $in unless ref $f eq 'GLOB'; my $section; my $line=0; @@ -1021,6 +1037,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; @@ -1057,12 +1079,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; } @@ -1106,6 +1122,13 @@ sub loadconfig { } } +sub startingconfig { + %alias=%config=%configfiles=%knownactions=%loaded=(); + my $datapos=tell(DATA); + loadconfig(\*DATA); + seek(DATA,$datapos,0); # rewind +} + sub modifyconfig { my $f=shift; # the section to modify or add @@ -1336,9 +1359,11 @@ sub bootstrap { eval q{use File::Temp}; die $@ if $@; my $tmpconfig=File::Temp->new(); - if (system("curl", "-A", "mr", "-s", $url, "-o", $tmpconfig) != 0) { - die "mr: download of $url failed\n"; - } + 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; + die "mr bootstrap: download of $url failed\n" if $curlstatus != 0; if (! -e $dir) { system("mkdir", "-p", $dir); @@ -1354,14 +1379,22 @@ sub bootstrap { if exists $config{$topdir}{"."}{"checkout"}; if (-e ".mrconfig") { - print STDERR "mr: .mrconfig file already exists, not overwriting with $url\n"; + print STDERR "mr bootstrap: .mrconfig file already exists, not overwriting with $url\n"; } else { - rename($tmpconfig, ".mrconfig") || die "rename: $!"; - } - - exec("mr $ENV{MR_SWITCHES} -c .mrconfig checkout"); - die "failed to run mr checkout"; + eval q{use File::Copy}; + die $@ if $@; + move($tmpconfig, ".mrconfig") || die "rename: $!"; + } + + # Reload the config file (in case we got a different version) + # and checkout everything else. + startingconfig(); + loadconfig(".mrconfig"); + dispatch("checkout"); + @skipped=grep { abs_path($_) ne abs_path($topdir) } @skipped; + showstats("bootstrap"); + exitstats(); } # alias expansion and command stemming @@ -1409,6 +1442,7 @@ sub getopts { "v|verbose" => \$verbose, "q|quiet" => \$quiet, "s|stats" => \$stats, + "k|insecure" => \$insecure, "i|interactive" => \$interactive, "n|no-recurse:i" => \$max_depth, "j|jobs:i" => \$jobs, @@ -1449,28 +1483,32 @@ sub init { $ENV{MR_PATH}=$Bin."/".$Script; }; } + +sub exitstats { + if (@failed) { + exit 1; + } + elsif (! @ok && @skipped) { + exit 1; + } + else { + exit 0; + } +} sub main { getopts(); init(); - loadconfig(\*DATA); + startingconfig(); loadconfig($ENV{MR_CONFIG}); #use Data::Dumper; print Dumper(\%config); my $action=expandaction(shift @ARGV); dispatch($action); - showstats($action); - if (@failed) { - exit 1; - } - elsif (! @ok && @skipped) { - exit 1; - } - else { - exit 0; - } + showstats($action); + exitstats(); } # Finally, some useful actions that mr knows about by default. @@ -1498,7 +1536,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 @@ -1509,10 +1547,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 } @@ -1521,11 +1559,12 @@ 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 +darcs_test = test -d "$MR_REPO"/_darcs svn_update = svn update "$@" git_update = git pull "$@" @@ -1533,13 +1572,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 @@ -1547,11 +1588,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 = git commit "$@" svn_push = : git_push = git push "$@" @@ -1559,6 +1602,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 "$@" @@ -1566,6 +1610,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 "$@" @@ -1574,6 +1619,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` @@ -1619,6 +1665,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 @@ -1628,15 +1679,28 @@ 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 = + case `uname -s` in + SunOS) + SHOWMANFILE="man -f" + ;; + Darwin) + SHOWMANFILE="man" + ;; + *) + SHOWMANFILE="man -l" + ;; + esac if [ ! -e "$MR_PATH" ]; then error "cannot find program path" fi tmp=$(mktemp -t mr.XXXXXXXXXX) || error "mktemp failed" trap "rm -f $tmp" exit pod2man -c mr "$MR_PATH" > "$tmp" || error "pod2man failed" - man -l "$tmp" || error "man failed" + $SHOWMANFILE "$tmp" || error "man failed" list = true config = bootstrap =