Combine color with conditional newlines in git log output
Asked Answered
C

2

9

When I invoke the following command:

git log --format=format:"%C(yellow)%h %C(blue)%s %C(green)%ad %C(reset)%an%n%n%-b"

I get output that looks like this:

enter image description here

I would like the commit bodies to be dimmed, so I tried inserting the instruction %C(dim) at the end of my format string. However, there does not seem to be an insertion location that achieves my goal:

  • Inserting %C(dim) after the newlines and before the (conditional newline-chomping) %-b command correctly applies the dimming effect, but breaks conditional newline-chomping:

    git log --format=format:"%C(yellow)%h %C(blue)%s %C(green)%ad %C(reset)%an%n%n%C(dim)%-b"
    

    enter image description here

  • Inserting %C(dim) before both the newlines and the (conditional newline-chomping) %-b command correctly retains conditional newline-chomping, but fails to apply the dimming effect (i.e. no change from original output):

    git log --format=format:"%C(yellow)%h %C(blue)%s %C(green)%ad %C(reset)%an%C(dim)%n%n%-b"
    

    enter image description here

Additionally, I cannot move the 'chomp' operator - to the color command, since that always appears to "evaluate" to a non-empty string (and therefore, newlines are not chomped).

Is there a way to achieve my goal?

Celestecelestia answered 27/7, 2017 at 16:54 Comment(0)
M
3

The final solution (without any known quirks) is at the bottom of this answer.

A workaround is to put a custom placeholder for the newlines in the format string (without using the %-b magic) and then postprocess the output with sed. In the example below the placeholder is the CHOMPABLENEWLINES string (of course you can replace it with any text of your choice, just make sure that it can't appear in your commit messages):

git log --format=format:"%C(yellow)%h %C(blue)%s %C(green)%ad %C(reset)%an%C(dim)CHOMPABLENEWLINES%b"|sed -e 's/CHOMPABLENEWLINES$//; s/CHOMPABLENEWLINES/\n\n/; $ s/$/\n/'

Colored output of git log with sed postprocessing

Note that this approach also fixes the problem that the effect of the %C directive extends only till the end of the current line (at least in git 2.7.4). As a result, in case of a multi-line body the color is applied only to its first line. Compare:

# Only the first line of a multiline message body is colored
git log --format=format:"%C(yellow)%h %C(blue)%s %C(green)%ad %C(reset)%an%n%n%Cred%b"

Colored output of git log without sed postprocessing

# Entire multiline message body is colored (though a byproduct of this
# is that the color setting persists beyond the current
# command - note the red prompt following the output)
git log --format=format:"%C(yellow)%h %C(blue)%s %C(green)%ad %C(reset)%an%CredCHOMPABLENEWLINES%b"|sed -e 's/CHOMPABLENEWLINES$//; s/CHOMPABLENEWLINES/\n\n/; $ s/$/\n/'

Colored output of git log with sed postprocessing

The undesirable side effect of persisted color setting can be counteracted by ending the format string with a %C(reset) directive. Then we also need a custom marker for the end of the %b placeholder, since the visual end of line is no longer an actual end of line from sed's point of view. In the example below the string ENDOFBODY is used as such a marker (and of course you must select it so that it doesn't appear in your expected output).

# This version works with GNU sed. For a portable version (including BSD
# and MacOS X systems) scroll down a little more
git log --format=tformat:"%C(yellow)%h %C(blue)%s %C(green)%ad %C(reset)%an%CredCHOMPABLENEWLINES%bENDOFBODY%C(reset)"|sed -e 's/CHOMPABLENEWLINESENDOFBODY//; s/CHOMPABLENEWLINES/\n\n/; s/ENDOFBODY//'

Output of the final version

Some versions or setups of git disable colored output when it doesn't directly go to terminal. In such cases you must also provide the --color=always option to git log.


The final solution, using only portable features of sed, is as follows:

git log --color=always --format=tformat:"%C(yellow)%h %C(blue)%s %C(green)%ad %C(reset)%an%C(red)CHOMPABLENEWLINES%bENDOFBODY%C(reset)"|sed -e 's/CHOMPABLENEWLINESENDOFBODY//; s/CHOMPABLENEWLINES/\'$'\n''\'$'\n/; s/ENDOFBODY//'
Misogynist answered 16/5, 2018 at 11:36 Comment(12)
@Celestecelestia --format=tformat: is similar to --format=format: but it adds a newline after the last line of output which helps to slightly shorten the sed scriptMisogynist
@Celestecelestia Re formatting. Do you mean that coloring of the output disappears? What OS are you using?Misogynist
Yup, all output is white. I'm on macOS 10.13.Celestecelestia
@Celestecelestia Can you try to add the --color=always option to git log? If it gets back the color to the output then I will provide a correct sed command for macOS (which is a little different from Linux).Misogynist
Adding --color=always brings back the color! There's one final issue I can't figure out; the newlines are printing as literal ns. I tried to double escape them but they print as literal \ns in that case... :SCelestecelestia
@Celestecelestia the newlines are printing as literal ns That's what I had in mind when I promised to provide a correct sed command for macOS. I updated the answer with a portable version, working identically under Linux and BSD (including macOS) systems.Misogynist
Brilliant, looks perfect now. Thanks for your hard work on this!Celestecelestia
@Misogynist Solid work! Since this uses sed I cannot put this into my git aliases, right?Gallopade
@Misogynist In addition, I presume the sed workaround could be used to address this? #34830247Gallopade
@Gallopade sed can be used in aliases indirectly - you must wrap the pipeline in a bash script and refer to it in the alias via the ! syntax (if the alias expansion is prefixed with an exclamation point, it will be treated as a shell command).Misogynist
@Gallopade Regarding the other question, for linear histories a sed-based solution most probably exists, but I am not sure if a universal solution handling non-linear histories is at all possible.Misogynist
@Misogynist I see. Sadly that means I won't be able to use this sed workaround to solve q/34829747 :(Gallopade
A
2

I think I found a way to do this without relying on external tools like sed.

The way I would expect it to work, I would just add a newline control plus a color formatter, and both of those would work, like

things things %C(yellow)%+d%C(reset)

But the yellow color is ignored. However, changing yellow to auto like this

things things %C(auto)%+d%C(reset)

applies some color, but it's some set of default colors. You can change those colors in your gitconfig though! So the pretty format won't be 100% portable (like you couldn't inline it into a git command to give to your coworker) but it works in my terminal.

To find the config value to change, I had to do some experimenting. Run git help -c | grep color to see a list of colors config values. The ones that colorize %d (all the branch names on a given commit) are here:

color.decorate.HEAD
color.decorate.branch
color.decorate.grafted
color.decorate.remoteBranch
color.decorate.stash
color.decorate.tag

There's probably another similar config value that would dim the commit message like the OP wants. Then you can run git config --global color.decorate.HEAD yellow to add this to your gitconfig file:

[color "decorate"]
    HEAD = yellow

and other config values follow suit.

So ultimately this is what my config ends up like:

[format]
    pretty = format:%C(bold cyan)%h%Creset %C(cyan)<%an>%Creset %Cgreen(%cr)%Creset%  %s%C(auto)%+d%C(reset)
[color "decorate"]
    HEAD = yellow
    branch = yellow
    grafted = yellow
    remoteBranch = yellow
    stash = yellow
    tag = yellow
Ambivalence answered 25/2, 2021 at 17:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.