X-Git-Url: https://git.madduck.net/code/myrepos.git/blobdiff_plain/a80452ac1f76168ec147d8fabf742ddbb3d57326..b56d4742d0ab350800aad62ce671b7a968be0448:/mr?ds=sidebyside

diff --git a/mr b/mr
index 68772a0..357b2d2 100755
--- a/mr
+++ b/mr
@@ -160,10 +160,8 @@ recurse into deeper repositories.
 =item -j number
 
 Run the specified number of jobs in parallel. This can greatly speed up
-operations such as updates.
-
-Note that in -j mode, all output of the jobs goes to standard output, even
-output that would normally go to standard error.
+operations such as updates. It is not recommended for interactive
+operations.
 
 =back
 
@@ -231,7 +229,17 @@ mr is run by joey. The second uses the hours_since function
 been at least 12 hours since the last update.
 
   skip = test $(whoami) != joey
-  skip = [ "$1" = update ] && [ $(hours_since "$1") -lt 12 ]
+  skip = [ "$1" = update ] && ! hours_since "$1" 12
+
+=item order
+
+The "order" parameter can be used to override the default ordering of
+repositories. The default order value is 10. Use smaller values to make
+repositories be processed earlier, and larger values to make repositories
+be processed later.
+
+Note that if a repository is located in a subdirectory of another
+repository, ordering it to be processed earlier is not recommended.
 
 =item chain
 
@@ -278,7 +286,6 @@ $SIG{INT}=sub {
 
 $ENV{MR_CONFIG}="$ENV{HOME}/.mrconfig";
 my $config_overridden=0;
-my $directory=getcwd();
 my $verbose=0;
 my $stats=0;
 my $no_recurse=0;
@@ -287,6 +294,7 @@ my %config;
 my %configfiles;
 my %knownactions;
 my %alias;
+my $directory=getcwd();
 
 Getopt::Long::Configure("no_permute");
 my $result=GetOptions(
@@ -302,6 +310,9 @@ if (! $result || @ARGV < 1) {
 	    "(Use mr help for man page.)\n");
 
 }
+if (! defined $directory) {
+	die("mr: failed to determine working directory\n");
+}
 
 # Make sure MR_CONFIG is an absolute path, but don't use abs_path since
 # the config file might be a symlink to elsewhere, and the directory it's
@@ -402,34 +413,55 @@ elsif ($action eq 'register') {
 	exec($command) || die "exec: $!";
 }
 
+# an ordered list of repos
+my @list;
+foreach my $topdir (sort keys %config) {
+	foreach my $subdir (sort keys %{$config{$topdir}}) {
+		push @list, {
+			topdir => $topdir,
+			subdir => $subdir,
+			order => $config{$topdir}{$subdir}{order},
+		};
+	}
+}
+@list = sort {
+		$a->{order}  <=> $b->{order}
+		             ||
+		$a->{topdir} cmp $b->{topdir}
+		             ||
+		$a->{subdir} cmp $b->{subdir}
+	} @list;
+
 # work out what repos to act on
 my @repos;
 my $nochdir=0;
-foreach my $topdir (sort keys %config) {
-	foreach my $subdir (sort keys %{$config{$topdir}}) {
+foreach my $repo (@list) {
+	my $topdir=$repo->{topdir};
+	my $subdir=$repo->{subdir};
+
+	next if $subdir eq 'DEFAULT';
+	my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
+	my $d=$directory;
+	$dir.="/" unless $dir=~/\/$/;
+	$d.="/" unless $d=~/\/$/;
+	next if $no_recurse && $d ne $dir;
+	next if $dir ne $d && $dir !~ /^\Q$d\E/;
+	push @repos, [$dir, $topdir, $subdir];
+}
+if (! @repos) {
+	# fallback to find a leaf repo
+	foreach my $repo (reverse @list) {
+		my $topdir=$repo->{topdir};
+		my $subdir=$repo->{subdir};
+		
 		next if $subdir eq 'DEFAULT';
 		my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
 		my $d=$directory;
 		$dir.="/" unless $dir=~/\/$/;
 		$d.="/" unless $d=~/\/$/;
-		next if $no_recurse && $d ne $dir;
-		next if $dir ne $d && $dir !~ /^\Q$d\E/;
-		push @repos, [$dir, $topdir, $subdir];
-	}
-}
-if (! @repos) {
-	# fallback to find a leaf repo
-	LEAF: foreach my $topdir (reverse sort keys %config) {
-		foreach my $subdir (reverse sort keys %{$config{$topdir}}) {
-			next if $subdir eq 'DEFAULT';
-			my $dir=($subdir =~/^\//) ? $subdir : $topdir.$subdir;
-			my $d=$directory;
-			$dir.="/" unless $dir=~/\/$/;
-			$d.="/" unless $d=~/\/$/;
-			if ($d=~/^\Q$dir\E/) {
-				push @repos, [$dir, $topdir, $subdir];
-				last LEAF;
-			}
+		if ($d=~/^\Q$dir\E/) {
+			push @repos, [$dir, $topdir, $subdir];
+			last;
 		}
 	}
 	$nochdir=1;
@@ -489,7 +521,7 @@ sub action { #{{{
 			system("mkdir", "-p", $dir);
 		}
 	}
-	elsif ($action eq 'update') {
+	elsif ($action =~ /update/) {
 		if (! -d $dir) {
 			return action("checkout", $dir, $topdir, $subdir);
 		}
@@ -538,7 +570,6 @@ sub action { #{{{
 			join(" ", map { s/\//\/\//g; s/"/\"/g; '"'.$_.'"' } @ARGV);
 		print STDERR "mr $action: running >>$command<<\n" if $verbose;
 		my $ret=system($command);
-		print "\n";
 		if ($ret != 0) {
 			if (($? & 127) == 2) {
 				print STDERR "mr $action: interrupted\n";
@@ -579,43 +610,59 @@ sub mrs { #{{{
 		while ($running < $jobs && @repos) {
 			$running++;
 			my $repo = shift @repos;
-			my $pid = open(my $fh, "-|");
-			if (! $pid) {
-				open(STDERR, ">&STDOUT");
+			pipe(my $outfh, CHILD_STDOUT);
+			pipe(my $errfh, CHILD_STDERR);
+			my $pid;
+			unless ($pid = fork) {
+				die "mr $action: cannot fork: $!" unless defined $pid;
+				open(STDOUT, ">&CHILD_STDOUT") || die "mr $action cannot reopen stdout: $!";
+				open(STDERR, ">&CHILD_STDERR") || die "mr $action cannot reopen stderr: $!";
+				close CHILD_STDOUT;
+				close CHILD_STDERR;
+				close $outfh;
+				close $errfh;
 				exit action($action, @$repo);
 			}
-			push @active, $repo;
-			push @fhs, $fh;
-			push @out, "";
+			close CHILD_STDOUT;
+			close CHILD_STDERR;
+			push @active, [$pid, $repo];
+			push @fhs, [$outfh, $errfh];
+			push @out, ['',     ''];
 		}
 		my ($rin, $rout) = ('','');
 		my $nfound;
-		foreach my $x (@fhs) {
-			next unless defined $x;
-			vec($rin, fileno($x), 1) = 1;
+		foreach my $fh (@fhs) {
+			next unless defined $fh;
+			vec($rin, fileno($fh->[0]), 1) = 1 if defined $fh->[0];
+			vec($rin, fileno($fh->[1]), 1) = 1 if defined $fh->[1];
 		}
 		$nfound = select($rout=$rin, undef, undef, 1);
-		foreach my $i (0..$#fhs) {
-			my $fh = $fhs[$i];
-			next unless defined $fh;
-			if (vec($rout, fileno($fh), 1) == 1) {
-				my $r = '';
-				if (sysread($fh, $r, 1024) == 0) {
-					close($fh);
-					record($active[$i], $? >> 8);
-					$fhs[$i] = undef;
-					$running--;
-					print $out[$i];
-					$out[$i]='';
+		foreach my $channel (0, 1) {
+			foreach my $i (0..$#fhs) {
+				next unless defined $fhs[$i];
+				my $fh = $fhs[$i][$channel];
+				next unless defined $fh;
+				if (vec($rout, fileno($fh), 1) == 1) {
+					my $r = '';
+					if (sysread($fh, $r, 1024) == 0) {
+						close($fh);
+						$fhs[$i][$channel] = undef;
+						if (! defined $fhs[$i][0] &&
+						    ! defined $fhs[$i][1]) {
+						    	waitpid($active[$i][0], 0);
+							print STDOUT $out[$i][0];
+							print STDERR $out[$i][1];
+							record($active[$i][1], $? >> 8);
+						    	splice(@fhs, $i, 1);
+						    	splice(@active, $i, 1);
+						    	splice(@out, $i, 1);
+							$running--;
+						}
+					}
+					$out[$i][$channel] .= $r;
 				}
-				$out[$i] .= $r;
 			}
 		}
-		while (@fhs and !defined $fhs[0]) {
-			shift @active;
-			shift @fhs;
-			shift @out;
-		}
 	}
 } #}}}
 
@@ -625,9 +672,11 @@ sub record { #{{{
 
 	if ($ret == OK) {
 		push @ok, $dir;
+		print "\n";
 	}
 	elsif ($ret == FAILED) {
 		push @failed, $dir;
+		print "\n";
 	}
 	elsif ($ret == SKIPPED) {
 		push @skipped, $dir;
@@ -878,12 +927,16 @@ ci = commit
 ls = list
 
 [DEFAULT]
+order = 10
 lib =
 	error() {
 		echo "mr: $@" >&2
 		exit 1
 	}
 	hours_since() {
+		if [ -z "$1" ] || [ -z "$2" ]; then
+			error "mr: usage: hours_since action num"
+		fi
 		for dir in .git .svn .bzr CVS .hg; do
 			if [ -e "$MR_REPO/$dir" ]; then
 				flagfile="$MR_REPO/$dir/.mr_last$1"
@@ -893,8 +946,13 @@ lib =
 		if [ -z "$flagfile" ]; then
 			error "cannot determine flag filename"
 		fi
-		perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile"
-		touch "$flagfile"
+		delta=$(perl -wle 'print -f shift() ? int((-M _) * 24) : 9999' "$flagfile")
+		if [ "$delta" -lt "$2" ]; then
+			exit 0
+		else
+			touch "$flagfile"
+			exit 1
+		fi
 	}
 
 update =