App-Multigit
view release on metacpan or search on metacpan
examples/mg-closes view on Meta::CPAN
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
# We'll be using more options than the defaults in App::Multigit::Script.
use Getopt::Long qw(:config gnu_getopt);
use App::Multigit qw(mg_each);
use App::Multigit::Script;
# We'll also be triggering Futures to be completed.
use Future;
use curry;
# %options is provided by App::Multigit::Script, so we can add to the ones we
# already collated by simply adding it to the hash and using GetOptions again.
%options = (
%options,
'repos-only' => 0
);
# In this case we could have omitted adding repos-only to the %options hash
# entirely, but this exemplifies how to set defaults as well.
GetOptions(
\%options,
'repos-only'
);
# For each repository...
my $future = mg_each(sub {
my $repo = shift;
# ... get a log of everything ...
$repo->run([ qw(git log --all --pretty=oneline) ])
# ... then show the results!
->then(show_results($repo))
;
});
# This hash is influenced by the Futures returned later, in show_results.
# The Futures that complete with nothing contribute nothing to this hash.
# We are careful to make sure our Futures complete with even-sized lists,
# because we're cognisant of the fact they'll end up here.
my %done = $future->get;
for my $dir (keys %done) {
chomp $done{$dir};
if ($done{$dir}) {
say for "$dir:", $done{$dir};
}
else {
say $dir;
}
}
# show_results returns a closure. The closure accepts a C<%data> hash,
# documented in L<App::Multigit::Repo|App::Multigit::Repo/run>. The subref is
# run by Future for each individual repository, when the first command
# completes.
sub show_results {
my $repo = shift;
return sub {
my (%data) = @_;
my $dir = $repo->config->{dir};
my @commits = parse_log($data{stdout});
# No commits that match - complete an empty Future.
if (! @commits) {
return Future->done;
}
if ($repos_only) {
# We complete a Future with a 2-piece list with no value for the
# list of commits. This ensures nothing is printed later. We could
# have put this test further up, where the for loop iterates over
# the results.
return Future->done($dir => undef);
}
else {
# We concatenate the commits that matched the filter, and complete a
# future with a 2-piece list mapping the directory to the list.
# Later, the list will be printed by the for loop above.
local $" = "\n";
return Future->done(
$dir => "@commits"
);
}
}
}
# We search the list of commits based on certain criteria. We have bugzillas for
# internal stuff and trackers for external stuff, so we find one or the other
# this way. We return the list of commits by grabbing their SHAs out of the log.
sub parse_log {
my $log = shift;
my $bz_re = qr/\b(bugzilla|bug|bz)\s*\Q$ARGV[0]/i;
my $tracker_re = qr/\btracker\s*\Q$ARGV[0]/i;
my @commits = map { /([[:xdigit:]]+)/ } grep { /$bz_re/ or /$tracker_re/ } split /\n/, $log;
}
=head1 SYNOPSIS
mg closes [--repos-only] ID
Searches the repositories for commits that mention ID.
Commits are recognised as either bugs or trackers with the given ID by
checking their commit messages for certain strings:
BZ $id
Bug $id
( run in 0.835 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )