X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/b1811adc60f81bcc868bd5484e2b5f365e068a51..be8197cd3ebbd893e7d791f20ff7685d2815b55f:/mr?ds=inline diff --git a/mr b/mr index 06b58e3..613f86a 100755 --- a/mr +++ b/mr @@ -1,13 +1,11 @@ #!/usr/bin/perl -#man{{{ - =head1 NAME mr - a Multiple Repository management tool =head1 SYNOPSIS -/ + B [options] checkout B [options] update @@ -81,6 +79,11 @@ remote repository. Only supported for distributed revision control systems. The optional -m parameter allows specifying a commit message. +=item push + +Pushes committed local changes to the remote repository. A no-op for +centralized revision control systems. + =item diff Show a diff of uncommitted changes. @@ -211,7 +214,7 @@ with no number specified. This can greatly speed up operations such as updates. It is not recommended for interactive operations. Note that running more than 10 jobs at a time is likely to run afoul of -ssh connection limits. Running between 3 and 5 jobs at a time will yeild +ssh connection limits. Running between 3 and 5 jobs at a time will yield a good speedup in updates without loading the machine too much. =back @@ -338,6 +341,12 @@ the action that is performed for a given revision control system, you can override these rcs specific actions. To add a new revision control system, you can just add rcs specific actions for it. +=head1 EXTENSIONS + +mr can be extended to support things such as unison and git-svn. Some +files providing such extensions are available in /usr/share/mr/. See +the documentation in the files for details about using them. + =head1 AUTHOR Copyright 2007 Joey Hess @@ -348,8 +357,6 @@ http://kitenet.net/~joey/code/mr/ =cut -#}}} - use warnings; use strict; use Getopt::Long; @@ -381,12 +388,11 @@ my %configfiles; my %knownactions; my %alias; my (@ok, @failed, @skipped); -my @SWITCHES=@ARGV; main(); my %rcs; -sub rcs_test { #{{{ +sub rcs_test { my ($action, $dir, $topdir, $subdir) = @_; if (exists $rcs{$dir}) { @@ -412,7 +418,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 $dir\n"; return undef; } if (! length $rcs) { @@ -421,9 +427,9 @@ sub rcs_test { #{{{ else { return $rcs{$dir}=$rcs; } -} #}}} +} -sub findcommand { #{{{ +sub findcommand { my ($action, $dir, $topdir, $subdir, $is_checkout) = @_; if (exists $config{$topdir}{$subdir}{$action}) { @@ -443,11 +449,11 @@ sub findcommand { #{{{ else { return undef; } -} #}}} +} -sub action { #{{{ +sub action { my ($action, $dir, $topdir, $subdir) = @_; - + $ENV{MR_CONFIG}=$configfiles{$topdir}; my $lib=exists $config{$topdir}{$subdir}{lib} ? $config{$topdir}{$subdir}{lib}."\n" : ""; @@ -505,22 +511,22 @@ 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 $dir\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 $dir, skipping\n"; return SKIPPED; } } else { if (! $no_chdir) { - print "mr $action: $topdir$subdir\n" unless $quiet; + print "mr $action: $dir\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$dir\E\/?//; + print "mr $action: $dir (in subdir $s)\n" unless $quiet; } $command="set -e; ".$lib. "my_action(){ $command\n }; my_action ". @@ -540,8 +546,12 @@ sub action { #{{{ if ($ret >> 8 != 0) { print STDERR "mr $action: command failed\n"; if (-e "$ENV{HOME}/.mrlog" && $action ne 'remember') { - @ARGV=(@SWITCHES, $action); + # recreate original command line to + # remember, and avoid recursing + my @orig=@ARGV; + @ARGV=('-n', $action, @orig); action("remember", $dir, $topdir, $subdir); + @ARGV=@orig; } } elsif ($ret != 0) { @@ -558,10 +568,10 @@ sub action { #{{{ return OK; } } -} #}}} +} # run actions on multiple repos, in parallel -sub mrs { #{{{ +sub mrs { my $action=shift; my @repos=@_; @@ -628,9 +638,9 @@ sub mrs { #{{{ } } } -} #}}} +} -sub record { #{{{ +sub record { my $dir=shift()->[0]; my $ret=shift; @@ -656,9 +666,9 @@ sub record { #{{{ else { die "unknown exit status $ret"; } -} #}}} +} -sub showstats { #{{{ +sub showstats { my $action=shift; if (! @ok && ! @failed && ! @skipped) { die "mr $action: no repositories found to work on\n"; @@ -676,9 +686,9 @@ sub showstats { #{{{ print STDERR "mr $action: (failed: ".join(" ", @failed).")\n"; } } -} #}}} +} -sub showstat { #{{{ +sub showstat { my $count=shift; my $singular=shift; my $plural=shift; @@ -686,10 +696,10 @@ sub showstat { #{{{ return "$count ".($count > 1 ? $plural : $singular); } return; -} #}}} +} # an ordered list of repos -sub repolist { #{{{ +sub repolist { my @list; foreach my $topdir (sort keys %config) { foreach my $subdir (sort keys %{$config{$topdir}}) { @@ -707,10 +717,10 @@ sub repolist { #{{{ || $a->{subdir} cmp $b->{subdir} } @list; -} #}}} +} # figure out which repos to act on -sub selectrepos { #{{{ +sub selectrepos { my @repos; foreach my $repo (repolist()) { my $topdir=$repo->{topdir}; @@ -749,9 +759,9 @@ sub selectrepos { #{{{ $no_chdir=1; } return @repos; -} #}}} +} -sub expandenv { #{{{ +sub expandenv { my $val=shift; @@ -761,10 +771,10 @@ sub expandenv { #{{{ } return $val; -} #}}} +} my %loaded; -sub loadconfig { #{{{ +sub loadconfig { my $f=shift; my @toload; @@ -840,6 +850,9 @@ sub loadconfig { #{{{ if ($parameter eq "include") { print "mr: including output of \"$value\"\n" if $verbose; unshift @lines, `$value`; + if ($?) { + print STDERR "mr: include command exited nonzero ($?)\n"; + } next; } @@ -893,9 +906,9 @@ sub loadconfig { #{{{ foreach (@toload) { loadconfig($_); } -} #}}} +} -sub modifyconfig { #{{{ +sub modifyconfig { my $f=shift; # the section to modify or add my $targetsection=shift; @@ -990,9 +1003,9 @@ sub modifyconfig { #{{{ open(my $out, ">", $f) || die "mr: write $f: $!\n"; print $out @out; close $out; -} #}}} +} -sub dispatch { #{{{ +sub dispatch { my $action=shift; # actions that do not operate on all repos @@ -1005,7 +1018,9 @@ sub dispatch { #{{{ elsif ($action eq 'register') { register(@ARGV); } - elsif ($action eq 'remember') { + elsif ($action eq 'remember' || + $action eq 'offline' || + $action eq 'online') { my @repos=selectrepos; action($action, @{$repos[0]}) if @repos; exit 0; @@ -1019,13 +1034,13 @@ sub dispatch { #{{{ record($repo, action($action, @$repo)); } } -} #}}} +} -sub help { #{{{ +sub help { exec($config{''}{DEFAULT}{help}) || die "exec: $!"; -} #}}} +} -sub config { #{{{ +sub config { if (@_ < 2) { die "mr config: not enough parameters\n"; } @@ -1061,9 +1076,9 @@ sub config { #{{{ } modifyconfig($ENV{MR_CONFIG}, $section, %changefields) if %changefields; exit 0; -} #}}} +} -sub register { #{{{ +sub register { if ($config_overridden) { # Find the directory that the specified config file is # located in. @@ -1106,10 +1121,10 @@ sub register { #{{{ join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV); print "mr register: running >>$command<<\n" if $verbose; exec($command) || die "exec: $!"; -} #}}} +} # alias expansion and command stemming -sub expandaction { #{{{ +sub expandaction { my $action=shift; if (exists $alias{$action}) { $action=$alias{$action}; @@ -1130,9 +1145,10 @@ sub expandaction { #{{{ } } return $action; -} #}}} +} -sub getopts { #{{{ +sub getopts { + my @saved=@ARGV; Getopt::Long::Configure("bundling", "no_permute"); my $result=GetOptions( "d|directory=s" => sub { $directory=abs_path($_[1]) }, @@ -1148,9 +1164,15 @@ sub getopts { #{{{ die("Usage: mr [-d directory] action [params ...]\n". "(Use mr help for man page.)\n"); } -} #}}} + + $ENV{MR_SWITCHES}=""; + foreach my $option (@saved) { + last if $option eq $ARGV[0]; + $ENV{MR_SWITCHES}.="$option "; + } +} -sub init { #{{{ +sub init { $SIG{INT}=sub { print STDERR "mr: interrupted\n"; exit 2; @@ -1172,9 +1194,9 @@ sub init { #{{{ use FindBin qw($Bin $Script); $ENV{MR_PATH}=$Bin."/".$Script; }; -} #}}} +} -sub main { #{{{ +sub main { getopts(); init(); @@ -1182,10 +1204,7 @@ sub main { #{{{ loadconfig($ENV{MR_CONFIG}); #use Data::Dumper; print Dumper(\%config); - my $action=shift @ARGV; - @SWITCHES = grep { $_ ne $action } @SWITCHES; - $action=expandaction($action); - + my $action=expandaction(shift @ARGV); dispatch($action); showstats($action); @@ -1198,11 +1217,10 @@ sub main { #{{{ else { exit 0; } -} #}}} +} # Finally, some useful actions that mr knows about by default. # These can be overridden in ~/.mrconfig. -#DATA{{{ __DATA__ [ALIAS] co = checkout @@ -1281,6 +1299,13 @@ bzr_record = bzr commit "$@" hg_record = hg commit -m "$@" darcs_record = darcs record -a -m "$@" +svn_push = : +git_push = git push "$@" +bzr_push = bzr push "$@" +cvs_push = : +hg_push = hg push "$@" +darcs_push = darcs push -a "$@" + svn_diff = svn diff "$@" git_diff = git diff "$@" bzr_diff = bzr diff "$@" @@ -1353,22 +1378,23 @@ list = true config = online = - if [ -e ~/.mrlog ]; then + if [ -s ~/.mrlog ]; then info "running offline commands" mv -f ~/.mrlog ~/.mrlog.old - if ! sh ~/.mrlog.old; then - error "offline commands failed; left in ~/.mrlog.old" + if ! sh -e ~/.mrlog.old; then + error "offline command failed; left in ~/.mrlog.old" fi rm -f ~/.mrlog.old else info "no offline commands to run" fi offline = + umask 077 touch ~/.mrlog info "offline mode enabled" remember = info "remembering command: 'mr $@'" - command="mr -d '$(pwd)'" + command="mr -d '$(pwd)' $MR_SWITCHES" for w in "$@"; do command="$command '$w'" done @@ -1379,6 +1405,5 @@ remember = ed = echo "A horse is a horse, of course, of course.." T = echo "I pity the fool." right = echo "Not found." -#}}} # vim:sw=8:sts=0:ts=8:noet