NAME
Git::Message - A Git commit message
VERSION
version 4.0.0
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.
Footer Syntax
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 lowercase 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.
footer
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.
get_footer_keys
This returns the list of footer keys. Multi-valued keys appear only once in the list, in lower case.
delete_footer_key KEY
This deletes KEY from the footer, along with all of its values.
get_footer_values KEY
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.
add_footer_values KEY, VALUE...
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) 2024 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.