Search code examples
gitnewlinegit-logformat-string

Format string for consistent separation between entries output by `git log --pretty`


I am trying to develop a format string to pass to git log --pretty so that each log entry ends in a full commit message, yet each log entry is separated by exactly one empty line. The problem is that some full commit messages end in a newline, and some do not.

For example, let's say I have two commits, abc1234 and def5678, but only abc1234 contains a newline at the end of the full commit message. Outputting the raw commit contents on the command line would look something like this:

[prompt]$ git cat-file commit abc1234
(...)

Title FOO

Full commit message FOO
[prompt]$ git cat-file commit def5678
(...)

Title BAR

Full commit message BAR[prompt]$

Note how the new shell prompt appears at the end of the last line of output, demonstrating that commit def5678 does not contain a newline at the end of the full commit message.

Let's say that def5678 is the parent of abc1234 and I want to output a simple log where each entry contains only the short commit hash, title line, and full commit message. I might try something like this:

[prompt]$ git log --graph --pretty='commit %h%n%B' abc1234
* commit abc1234
| Title FOO
| 
| Full commit message FOO
|
* commit def5678
| Title BAR
|
| Full commit message BAR
* commit <parent of def5678>
(...)

Note the spacing between the log entries. The entries for abc1234 and def5678 are separated by a blank line (save for the graph character), yet the entries for def5678 and its parent are not.

How can I construct a format string so that the spacing is consistent, even with inconsistent termination of full commit messages? The builtin pretty formats of medium, full, fuller, and email already do that, but I want to be able to construct arbitrary format strings to do the same thing.

I've experimented with the %+B, %-B and % B sequences (and their %b and %n equivalents), but I just can't seem to get consistent spacing.

I'm using Git 2.17.0 if that makes a difference.


Solution

  • As @jthill alluded to in the comments, and expressed in git-log(1):

    If you add a - (minus sign) after % of a placeholder, all consecutive line-feeds immediately preceding the expansion are deleted if and only if the placeholder expands to an empty string.

    Therefore, if we can find a format sequence %<token> that will always expand to the empty string, we can use %-<token>%n to replace zero or more consecutive newlines with a single newline. As it turns out, there is such a format sequence: %C(), the empty color selector. (Normally the parentheses enclose a non-empty string that specifies coloration to be used in the log output. See git-log(1) and git-config(1) for more details.)

    The fact that %C() evaluates to the empty string instead of causing an error seems like a happy accident instead of something to lay one's hat upon, but at least for Git 2.17, it does the trick. At this point, I have enough information to answer my own question.

    To maintain consistent separation between log entries output by git log --pretty=<tformat>, where <tformat> may or may not evaluate to a string ending in a newline, append %-C()%n to <tformat>. For example:

    [prompt]$ git log --graph --pretty='commit %h%n%B%-C()%n' abc1234
    * commit abc1234
    | Title FOO
    | 
    | Full commit message FOO
    |
    * commit def5678
    | Title BAR
    |
    | Full commit message BAR
    |
    * commit <parent of def5678>
    (...)