NAME

Git::Message - A Git commit message

VERSION

version 2.9.9

SYNOPSIS

    use Git::Repository 'GitHooks';
    my $git = Git::Repository->new();

    use Git::Message;
    my $msg = Git::Message->new($git->read_commit_msg_file($filename));

    if (my $title = $msg->title) {
        if ($title =~ s/\.$//) {
            $msg->title($title); # remove trailing period from title
        }
    }

    my $body = $msg->body;

    $msg->add_footer_value(Issue => 'JIRA-100');

    unless ($msg->get_footer_value('Signed-off-by')) {
        die "Missing Signed-off-by in footer.\n";
    }

    $gitmore->write_commit_msg_file($filename, $msg->as_string);

DESCRIPTION

This class represents a Git commit message. Generally speaking, a commit message can be any string whatsoever. However, the Git community came up with a few conventions for how to best format a message and this class embraces those conventions making it easier for you to validate and change a commit message structure.

A conventional Git commit message consists of a sequence of non-blank-line blocks, or paragraphs, separated by one of more blank lines. Let's call them blocks, for short. These blocks are aggregated in three components: the title, the body, and the footer.

  • The title is the first block of the message. Conventionally, it must have a single line, but this class doesn't require this. You have to check this for yourself, if it matters to you.

  • The footer is the last block of the message, if there are any blocks left after the title. But the footer has to follow a strict syntax which is checked during construction. If the last block does not follow that syntax, it's not considered a footer but just the last block of the body.

  • The body is comprised by all the blocks between the title and the footer, if any.

Note that all three components are undefined if the message doesn't have any blocks. If there is at least one block, the title is the first one. In this case, either the body or the footer can be undefined independently, depending on the number of blocks left and on the specific contents of the last one.

The footer is a set of key:value specifications, much like the headers of a SMTP email message or of a HTTP request. There is, however, a notion of "in footer comments" which turn the parsing a little more involved. These comments are used, apparently, by the Linux kernel hackers. Well, they must know what they're doing. ;-)

The specific syntax we parse is the one implemented by Gerrit's standard Git commit-msg hook. After the parsing, which occurs during construction, we aggregate, for each key, all the values and comments associated with it in the footer. Since a key may appear multiple times with different letter case, we use their lowercased form as the aggregation keys to avoid spurious differences. As an example, suppose we have a message with the following footer in it:

    Issue: JIRA-100
    [what: the hell is this comment
           doing here?]
    issue: JIRA-101
    Signed-off-by: John Contributor <jc@cpan.org>
    Signed-off-by: Gustavo Chaves <gnustavo@cpan.org>

Internally, it's kept in a data structure like this:

    {
        'issue' => [
            ['Issue' => 'JIRA-100'],
            "[what: the hell is this comment\n           doing here?]",
            ['issue' => 'JIRA-101'],
        ],
        'signed-off-by' => [
            ['Signed-off-by' => 'John Contributor <jc@cpan.org>'],
            ['Signed-off-by' => 'Gustavo Chaves <gnustavo@cpan.org>'],
        ],
    }

This way we can reconstruct the footer in string form preserving the letter case of its keys and the order of the values and comments inside each key. Note, however, that we do not preserve the exact order of each line in the footer, which isn't relevant normally. The footer stringification outputs the keys in lexicographical order with the exception of the Signed-off-by key, which, if present, is always output last.

METHODS

new MSG

The constructor receives the commit message contents in a string, parses it and saves the message structure internally.

IMPORTANT: The constructor parser assumes that the message contents are cleaned up as if you had passed it through the git stripspace -s command. You can do that yourself or use the Git::Repository::Plugin::GitHooks::read_commit_msg_file method to read the message from a file and clean it up automatically.

title [TITLE]

This returns the message title or undef if there is none.

You can change the message's title by passing a string to it.

body [BODY]

This returns the message body or undef if there is none.

You can change the message's body by passing a string to it.

This returns the message footer or undef it there is none.

Note that the result string may be different from the original footer in the message, because the lines may be reordered as we told "Footer syntax" in above.

This returns the list of footer keys. Multivalued keys appear only once in the list, in lower case.

This deletes KEY from the footer, along with all of its values.

This returns the list of values associated with KEY, which may be in any letter case form. The values are strings and the list will be empty if the key doesn't appear in the footer at all.

This adds a list of VALUEs to KEY.

as_string

This returns the complete message by joining its title, body, and footer separating them with empty lines.

SEE ALSO

  • Git::Repository::Plugin::GitHooks

    A Git::Repository plugin with some goodies for hook developers.

  • git-commit(1) Manual Page

    This Git manual page has a section called DISCUSSION which discusses some common log message policies.

  • MediaWiki Git/Commit message guidelines

    This document defines the MediaWiki's project commit log message guidelines.

  • Proper Git Commit Messages and an Elegant Git History

    This is a good discussion about commit log message formatting and the reasons behind them.

  • GIT Commit Good Practice

    This document defines the OpenStack's project commit policies.

AUTHOR

Gustavo L. de M. Chaves <gnustavo@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2018 by CPqD <www.cpqd.com.br>.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.