App-KGB
view release on metacpan or search on metacpan
lib/App/KGB/Client/Git.pm view on Meta::CPAN
$log[0] .= " ($log[1])";
$#log = 0;
}
}
return App::KGB::Commit::Tag->new(
{ id => substr( $ref, 0, 7 ),
author => $author_login,
author_name => $author_name,
log => join( "\n", @log ),
branch => $signed ? 'signed tags' : 'tags',
changes => [ App::KGB::Change->new("(A)$tag") ],
tag_name => $tag,
}
);
}
=item format_git_stat I<text>
returns a colored version of I<text>, which is expected to be the result
of C<git diff --shortstat>.
=cut
sub format_git_stat {
my ( $self, $text ) = @_;
my $result = '';
$self->init_painter;
while ( length($text) ) {
warn "$text" if 0;
if ( $text =~ s/\s*(.*?)(\d+ files? changed)// ) {
$result .= $1 . $self->colorize( modification => $2 );
next;
}
if ( $text =~ s/\s*(.*?)(\d+) insertions?\(\++\)// ) {
$result .= $1 . $self->colorize( addition => "$2(+)" );
next;
}
if ( $text =~ s/\s*(.*?)(\d+) deletions?\(-+\)// ) {
$result .= $1 . $self->colorize( deletion => "$2(-)" );
next;
}
# nothing matched
$result .= $text;
last;
}
return $result;
}
sub _describe_branch_updates {
my ( $self ) = @_;
my %ref_branch;
my %ref_parent;
my @new_branches;
my %new_branches;
my @updated_branches;
my %branch_updates;
my %branch_head;
my %updated_heads;
my @old_revs;
# keys are sha1s, values are hashrefs with keys branch names
my %branch_tips;
warn "# ======== processing changesets" if 0;
my @params = qw(--topo-order --parents --first-parent);
my @updated;
my %branch_has_commits;
for my $cs ( @{ $self->changesets } ) {
my ( $old, $new, $ref ) = @$cs;
warn "# considering $old $new $ref" if 0;
next unless $ref =~ m{^refs/heads/(.+)}; # not interested in tags
my $branch = $1;
next if $new =~ /^0+$/; # nor dropped branches
$ref_branch{$new} = $branch;
warn "# $new is on $branch" if 0;
warn "$branch head is $new" if 0;
$branch_head{$branch} = $new;
$branch_tips{$new}{$branch} = 1;
$updated_heads{$branch} = 1;
if ( $old =~ /^0+$/ ) {
push @new_branches, $branch;
$new_branches{$branch} = 1;
}
else {
push @updated, "$new", "^$old";
push @old_revs, $old;
push @updated_branches, $branch;
$branch_updates{$branch} = [ $old => $new ];
}
}
my @existing_branches;
my @old_branches;
my @lines
= $self->_git->command( 'branch', '-v', '--no-abbrev' );
for my $l (@lines) {
$l =~ s/^[ *]+//;
my ( $ref, $sha, $ignore ) = split( ' ', $l );
$branch_head{$ref} = $sha;
$branch_tips{$sha}{$ref} = 1;
$ref_branch{$sha} //= $ref;
push @existing_branches, $ref unless $new_branches{$ref};
push @old_branches, $ref
unless $new_branches{$ref}
or $branch_updates{$ref};
}
warn "existing branches: @existing_branches" if 0;
warn "old branches: @old_branches" if 0;
my @commits;
my %reported;
if (@updated) {
push @params, map( "^$_", @old_branches );
warn "# git rev-list @params @updated" if 0;
my @lines = $self->_git->command( 'rev-list', @params, @updated);
do { warn $_ for @lines } if 0;
if ( $self->squash_threshold
and scalar(@lines) > $self->squash_threshold )
{
for my $branch (@updated_branches) {
my ($old,$new) = @{ $branch_updates{$branch} };
my $stat = $self->_git->command( 'diff', '--shortstat',
"$old..$new" );
my @commit_lines
= $self->_git->command( 'rev-list', '--topo-order', $new,
"^$old" );
push @commits,
$self->format_message(
$self->squash_msg_template,
branch => $branch,
commit_id => substr( $new, 0, 7 ),
author_login => $ENV{USER},
author_name => $self->_get_full_user_name,
log => sprintf(
'%d commits pushed, %s',
scalar(@commit_lines), $self->format_git_stat($stat),
),
);
warn "# $commits[-1]" if 0;
$branch_has_commits{$branch} = 1;
}
}
else {
my @refs;
for (@lines) {
my ( $ref, @parents ) = split(/\s+/);
push @refs, $ref;
if ( @parents and not $ref_branch{ $parents[0] } ) {
$ref_branch{ $parents[0] } = $ref_branch{$ref}
or confess
"Ref $ref with parent $parents[0] is of unknown branch";
warn
"# $parents[0] determined to be on branch $ref_branch{$ref}"
if 0;
}
}
warn "# revisions to describe: " . join( ' ', @refs ) if 0;
for my $ref (@refs) {
if ( $reported{$ref} ) {
warn "$ref already reported" if 0;
next;
}
my $cmt = App::KGB::Commit->new( $self->_describe_ref($ref) );
warn "# putting $ref on $ref_branch{$ref}" if 0;
$cmt->branch( $ref_branch{$ref} );
unshift @commits, $cmt;
$reported{$ref} = 1;
$branch_has_commits{ $ref_branch{$ref} } = 1;
}
}
# see if some updated branch was without any reported commits
# if this case put a fast-forward notification
if ( $self->enable_branch_ff_notification ) {
for ( @updated_branches ) {
next if $branch_has_commits{$_};
push @commits,
App::KGB::Commit->new(
{ branch => $_,
id => substr( $branch_updates{$_}[1], 0, 7 ),
author => $ENV{USER},
author_name => $self->_get_full_user_name,
log => 'fast forward',
}
);
}
}
}
# walk the branch until it is exhausted or a revision with multiple
# children (branch point) is reached
# when walking skip all commits already reported
# terminate walk on old revs
if ( @new_branches ) {
# exclude commits in all branches that aren't part of this push
my @exclude;
for ( @existing_branches ) {
push @exclude, $branch_head{$_} unless $updated_heads{$_};
};
push @exclude, @old_revs;
$_ = "^$_" for @exclude;
for my $b (@new_branches) {
warn "# Looking into new branch $b" if 0;
warn "# git rev-list --topo-order --first-parent --parents $b @exclude" if 0;
my @lines = $self->_git->command( 'rev-list', '--topo-order',
'--first-parent', '--parents', $b, @exclude );
my @br_commits;
my $branch_point;
my $last_rev;
for my $line (@lines) {
warn "# $line" if 0;
my ( $rev, $parent, @other_parents ) = split( /\s+/, $line );
$last_rev = $rev;
if ( $reported{$rev} ) {
warn "$rev is already reported" if 0;
next;
}
my $pipe = $self->_git->command_output_pipe( 'rev-list',
'--children', $rev );
my $in = <$pipe>;
$self->_git->command_close_pipe($pipe);
my @children;
if ($in) {
chomp($in);
warn "# Children of $rev: @children" if 0;
@children = split(/\s+/, $in);
shift @children;
}
# a branch point is:
# * a commit with more than one child
# * a tip of another branch
if (@children > 1
or ( exists $branch_tips{$rev}
and not exists $branch_tips{$rev}{$b} )
)
{
unshift @br_commits,
App::KGB::Commit->new(
{ log => "Branch '$b' created",
id => substr( $rev, 0, 7 ),
branch => $b,
}
);
$branch_point = $rev;
warn "$b branched at $rev" if 0;
last;
}
if ($parent) {
$ref_branch{$parent} //= $ref_branch{$rev};
$ref_parent{$rev} = $parent;
}
( run in 0.585 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )