App-Gitc

 view release on metacpan or  search on metacpan

bin/gitc-submit  view on Meta::CPAN

            . "reviewer with any instructions he might need to correctly\n"
            . "resolve the conflicts.\n"
            . "Press ENTER to continue.\n";
            ;
        my $junk = <STDIN>;
    }

    # export the patch emails and let the user edit the cover
    my ( $tmpdir, $cover_letter ) = export_patches($changeset);
    let_user_edit($cover_letter) unless ($skip_email || $skip_edit);

    # die if the user deleted the entire cover letter
    die "Aborting submit at the user's request\n" if -s $cover_letter <= 10;
    update_email_headers( $tmpdir, $cover_letter );


    # record the submit action in meta data
    my $id = meta_data_add({
        action    => 'submit',
        changeset => $changeset,
        reviewer  => $reviewer,
    });
    to_undo { meta_data_rm(id => $id, changeset => $changeset) };

    # record the review action for self-review
    if ( $self_review ) {
        my $id = meta_data_add({
            action    => 'review',
            changeset => $changeset,
        });
        to_undo { meta_data_rm(id => $id, changeset => $changeset) };
    }

    # put the changeset branch where others can see it
    if ( not $self_review ) {
        git "push --force origin $changeset:pu/$changeset";
        to_undo { git "push origin :pu/$changeset" };
    }

    # delete the local branch
    if ( not $keep ) {
        git "checkout master";
        to_undo { git "checkout -f $changeset" };
        local $@;
        eval { # failing in here is ok
            my $sha1 = is_valid_ref($changeset);
            git "branch -D $changeset";
            to_undo { git "branch $changeset $sha1" };
        };
        warn "Unable to delete branch $changeset: $@\n" if $@;
    }

    # blast out the emails
    git "send-email --to " . get_user_email($reviewer)
      . '           --from "' . author_email() . '"'
      . '           --no-chain-reply-to'
      . '           --signed-off-by-cc'
      . '           --suppress-cc author'
      . '           --suppress-from'
      . '           --quiet'
      . '           --no-validate'
      . "           $tmpdir/*.patch"
      if not $skip_email
      ;

    if ($its) {
        # update the Issue status
        my $issue = $its->get_issue($changeset, reload => 1);
        my $project = project_name();
        my $what_happened = $its->transition_state({
            command   => 'submit',
            issue     => $issue,
            reviewer  => $reviewer,
            message   => "Submitted $project#$changeset to $reviewer for code review",
            changeset => $changeset,
        });
    }
    return;
};

# reinstate any changes present when we started
git "stash apply $stash" if $stash;
exec "gitc pass --from-self-review" if $self_review;
exit;

################################ helper subs ########################

# decide who should review this changeset
sub find_reviewer {
    my $reviewer = shift or die "You must specify a reviewer\n";

    # handle self-review for dataload projects
    my $current_user = get_user_name();
    if ( $reviewer eq $current_user ) {
        if ( project_config()->{'self submit'} ) {
            $self_review = 1;
            $skip_email  = 1;
            $keep        = 1;
            return $reviewer;
        }
    }

    validate_reviewer($reviewer);
    return $reviewer;
}

# determine whether the given reviewer is valid.  if not, suggest
# an alternative based on possible mis-spellings
sub validate_reviewer {
    my ($reviewer) = @_;
   
    fetch_tags(); 
    my @users = user_lookup_class()->users();

    return if any { $_ eq $reviewer } @users;

    # an invalid reviewer.  make some suggestions
    require Text::Levenshtein;
    require List::Util;
    my @suggestions =
        map  { $_->[0] }
        sort { $a->[1] <=> $b->[1] }
        grep { $_->[1] < 4 }           # only "close" matches
        map  { [ $_, scalar Text::Levenshtein::distance( $reviewer, $_ ) ] }
        @users;
    my @short_list = @suggestions[ 0 .. List::Util::min($#suggestions, 2) ];

    my $msg = "The user name '$reviewer' is invalid. ";
    if (@suggestions) {
        $msg   .= "Perhaps you meant one of:\n";
        $msg   .= "  - $_\n" for @short_list;
    }
    else {
        $msg .= "\n";
    }
    die $msg;
}

# Execute a rebase (and never return) if this changeset hasn't accounted
# for the most recent commits on its 'onto' branch.
sub launch_auto_rebase {
    my ($branch_point) = @_;

    # are there new commits since the changeset branch?
    my $basis = branch_basis($branch_point);
    return if $basis !~ /^(master|test|stage|prod)$/;
    my @upstream = git "rev-list --first-parent origin/$basis ^HEAD";
    return if not @upstream;

    # yup, so start a rebase
    my $count = @upstream;
    my $s = $count == 1 ? '' : 's';
    warn "Uh oh, $basis has $count commit$s since you started.\n"
        . "I'm rebasing for you.  When it's done, resubmit.\n"
        . "\n"
        ;
    exec "git rebase --onto origin/$basis $branch_point";
}

sub export_patches {
    my ($changeset) = @_;

    # generate the patches
    require File::Temp;
    my $tmpdir = File::Temp::tempdir(
        'gitc-submit-XXXXX',
        TMPDIR  => 1,
        CLEANUP => 1,
    );



( run in 1.015 second using v1.01-cache-2.11-cpan-39bf76dae61 )