package Analizo::Batch::Git;
use strict;
use warnings;
use parent qw(
Analizo::Batch
Class::Accessor::Fast
);
use Analizo::Batch::Job::Git;
use Cwd 'abs_path';
use YAML::XS;
__PACKAGE__->mk_ro_accessors(qw( directory ));
sub new {
my ($class, $directory) = @_;
$directory ||= '.';
$directory = abs_path($directory);
$class->SUPER::new(directory => $directory);
}
sub fetch_next {
my ($self) = @_;
$self->initialize();
my $nextcommit = $self->{relevant}->[$self->{index}];
if ($nextcommit) {
$self->{index}++;
return $nextcommit;
} else {
return undef;
}
}
sub initialize {
my ($self) = @_;
unless(defined($self->{index})) {
# initialize filter: by default look only for files in known languages
unless ($self->has_filters) {
$self->filters(Analizo::LanguageFilter->new('all'));
}
# read in list of commits
my $data = `(cd $self->{directory} && git log --name-status --format='---%nid: %H%nparents: %P%nauthor_date: %at%nauthor_name: %aN%nauthor_email: %aE%n--- |')`;
$data =~ s/^([[:upper:]])+\d*\t/ $1 /sgm;
my @data = ();
eval { @data = Load($data) };
if ($@) {
die $!;
}
my @jobs = ();
while($#data > 0) {
my $commit_data = shift(@data);
my @parents = ();
@parents = split(/\s+/, $commit_data->{parents}) if defined($commit_data->{parents});
$commit_data->{parents} = \@parents;
my $changed_files = shift(@data);
if(scalar(@parents) > 1) {
# merge commits do not have their changed files listed in `git log`, no
# matter what. This way we *need* to do a `git show` here.
$changed_files = `(cd $self->{directory} && git show --name-status --format='%n' $commit_data->{id})`;
}
chomp($changed_files);
$changed_files =~ s/^\s*//; # remove leading whitespace
my %changed_files = map { my ($status, $file) = split(/\s+/, $_); $file => $status } (split("\n", $changed_files));
for my $file (keys(%changed_files)) {
if (!$self->filename_matches_filters($file)) {
delete $changed_files{$file};
}
}
$commit_data->{changed_files} = \%changed_files;
my $job = Analizo::Batch::Job::Git->new($self->{directory}, $commit_data->{id}, $commit_data);
$job->batch($self);
push @jobs, $job;
}
my %jobs = map { $_->id => $_ } @jobs;
my @relevant = grep { $_->relevant } @jobs;
$self->{jobs} = \%jobs;
$self->{relevant} = \@relevant;
$self->{count} = scalar(@relevant);
$self->{index} = 0;
for my $job (@relevant) {
# force calculating previous relevant right now
$job->previous_relevant();
}
}
}
sub count {
my ($self) = @_;
return $self->{count};
}
sub find {
my ($self, $id) = @_;
return $self->{jobs}->{$id};
}
1;