App-Gitc

 view release on metacpan or  search on metacpan

lib/App/Gitc/Util.pm  view on Meta::CPAN

        get_user_name
        get_user_email
        git
        git_config
        guarantee_a_clean_working_directory
        let_user_edit
        meta_data_add
        meta_data_rm
        meta_data_rm_all
        project_config
        its
        its_for_changeset
    );
    our @EXPORT_OK = qw(
        add_current_user
        archived_tags
        branch_basis
        branch_point
        cache_meta_data
        changeset_group
        changeset_merged_to
        changesets_in
        changesets_promoted_between
        command_name
        commit_decorations
        confirm
        current_branch_version
        environment_preceding
        full_changeset_name
        git_fetch_and_clean_up
        git_dir
        git_tag
        highest_quickfix_number
        history
        history_owner
        history_reviewer
        history_status
        history_submitter
        is_auto_fetch
        is_merge_commit
        is_suspendable
        is_valid_ref
        meta_data_rm_project
        new_branch_version
        new_version_tag
        open_packed_refs
        parse_changeset_spec
        project_name
        project_root
        remote_branch_exists
        restore_meta_data
        sendmail
        short_ref_name
        sort_changesets_by_name
        toplevel
        traverse_commits
        unmerged_changesets
        unpromoted
        user_lookup_class
        version_tag_prefix
        state_blocked
    );
}


sub confirm {
    my ($message) = @_;
    die "No message given to 'confirm'" if not defined $message;

    require Term::ReadLine;
    my $term   = Term::ReadLine->new('gitc');
    my $prompt = "$message ";
    my $response;

    # prompt the user
    while ( defined( $response = $term->readline($prompt) ) ) {
        return 1 if $response eq 'y';
        return   if $response eq 'n';
        $prompt = "$message ('y' or 'n') ";
    }
}


# If this ever needs to be faster, it can be implemented by opening
# .git/HEAD and parsing the one line contents.  It would take a bit
# more effort to get it right, but it would avoid the fork+exec overhead
sub current_branch {
    my ($name) = grep /^[*]/, qx{ git branch --no-color };
    chomp $name;
    $name =~ s/^[*] //;
    return $name;
}


sub its_config {
    my $name = lc its()->label_service;

    return project_config()->{ "${name}_statuses" };
}

# Eventum is the name of our internal ticketing system.
sub eventum {
    return its()->get_issue( @_ );
}

sub eventum_transition_status {
    return its()->transition_state( @_ );
}


sub eventum_statuses {
    my ( $self, $command, $target ) = @_;

    my $statuses = project_config()->{'jira_statuses'}{$command}
        or die "No JIRA statuses for $command";

    # handle the common case
    if ( not $target ) {
        die "No initial status" unless $statuses->{from};
        die "No final status" unless $statuses->{to};
        return ( $statuses );

lib/App/Gitc/Util.pm  view on Meta::CPAN

            for my $rx (@rxen) {
                if ( $name =~ $rx ) {
                    my $changeset = $1;
                    next if $seen{$changeset}++;
                    push @included, $changeset;
                }
                elsif ( $name =~ m{^refs/tags/cs/($cs)/rm-$env$}o ) {
                    my $changeset = $1;
                    next if $seen{$changeset}++;
                }
            }
        }
    }

    return @included;
}

# returns a list of changesets that are present in $source_changes but
# missing from $target_changes
sub _missing_changesets {
    my ($source_changes, $target_changes) = @_;

    my %source = map { $_ => 1 } @$source_changes;
    my %target = map { $_ => 1 } @$target_changes;
    delete @source{ keys %target };

    # maintain the same changeset order
    return grep { $source{$_} } @$source_changes;
}



sub command_name {
    my ($command) = $0 =~ m{/gitc-(\w+)$};
    return $command if $command;
    die "Unable to determine the command name from $0\n";
}


sub _states {
    my ( $self, $command, $target ) = @_;

    my $statuses = its_config()->{ $command }
        or die 'No ' . its->label_service . " statuses for $command";

    # handle the common case
    if ( not $target ) {
        die "No initial status" unless $statuses->{from};
        die "No final status" unless $statuses->{to};
        return ( $statuses );
    }

    # promotions need another level of dereference
    die "No initial status for target $target" unless $statuses->{$target}{from};
    die "No final status for target $target" unless $statuses->{$target}{to};

    return $statuses->{$target};
}


sub state_blocked {
    my ( $command, $state ) = @_;

    my $statuses = its_config()->{ $command }
        or die 'No ' . its()->label_service . " statuses for $command";

    # promotions need another level of dereference
    my $block = $statuses->{ block };

    return unless $block;

    return 1 if any { warn " \$_: $_, \$state: $state.\n";$_ eq $state } @{$block};
    
    return;
}

1;

__END__

=pod

=head1 NAME

App::Gitc::Util - The workhorse of gitc

=head1 VERSION

version 0.60

=head1 Exported Subroutines

=head2 confirm($message)

Displays C<$message> and waits for the user to press 'y' or 'n'.  If he enters
'y', a true value is returned.  If he enters 'n', a false value is returned.

=head2 current_branch

Returns the name of the branch that's currently checked out in Git.

=head2 its_config

Returns the config specific to this project's ITS.

=head2 eventum_statuses

Returns the from and to state based on a command provided to GITC.

=head2 its

Returns an ITS pseudo-object

Some day it would be nice to add 'new' to these and return a real instantiated
object... but this fits the bill.

=head2 its_for_changeset

Guesses which ITS object we need based on the changeset name, returns the
object if it supports it, or just the class name.

lib/App/Gitc/Util.pm  view on Meta::CPAN

Sorts a list of changeset names into a sensible order.  Typical usage is:

    sort_changesets_by_name( \@list_of_changesets );
    # @list_of_changesets is now sorted

=head2 split_decorations($decorations)

Converts a string of C<$decorations> as produced by C<git log --decorate> or
C<git log --pretty=format:%d> into a list of full ref names.

=head2 toplevel

Returns the top-level directory for the current branch.

=head2 traverse_commits( $log_string, $callback )

Invokes C<git log $log_string> and calls the code reference C<$callback> for
each commit encountered.  C<$callback> is invoked with the following
arguments in a hashref:

    commit  - this commit's ID
    parents - an arrayref of the commit's parent commit IDs
    message - an arrayref of the commit's message lines

=head2 unmerged_changesets($project_name)

Returns a hashref whose keys are the names of unmerged changesets and whose
values are the respective changeset histories (see L</history>).

=head2 unpromoted($from, $to)

Returns a list of the changesets which are included in C<$from> but not yet
part of C<$to>.  For example, C<unpromoted('origin/master', 'origin/test')>,
returns the changesets that are in C<master> but not in C<test>.

One way to think about it is that if you promoted C<$from> into C<$to>, what
other things would be promoted to C<$to> as a result.  This is the way that
F<gitc-unpromoted> is implemented. This subroutine implements a more
general idea of "not included in" than that command makes available.

C<$from> can also be an arrayref of branches.  In this case it means, "If I
were to promote everything listed in C<@$from>, what all would be promoted.

The order of the returned changesets is significant.  Changesets always appear
in the list before their dependencies.  In Git terms, "child commits are given
before their parents."  Since demotions are not accomodated by Git's data
model, they are placed at the end of the list of unpromoted changesets.

=head1 Private Subroutines

=head2 command_name

Returns the name of the gitc command that started this whole mess.
This is mostly a helper subroutine for eventum_transition_status.
If the command name can't be determined, an exception is thrown.

=head2 _states

Used internally to calculate target for state change.

=head2 state_blocked

Given a C<command> and a specified C<state> this checks the block list in the
project configuration and returns true if the state should block the command
from proceeding.

NOTE: Block list must be an arraref in the project configuration 

=head1 AUTHOR

Grant Street Group <developers@grantstreet.com>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2013 by Grant Street Group.

This is free software, licensed under:

  The GNU Affero General Public License, Version 3, November 2007

=cut



( run in 1.366 second using v1.01-cache-2.11-cpan-40ba7b3775d )