2

I am trying to make a command for having a nice-looking git log. Say I have the following:

git log --color --pretty=format:"%C(cyan)<%an>" --abbrev-commit

What I would like to do is to make a variable that takes %an and decides what colour it will be based on the output string.
So far, I have

USER=$(if [ ! %an = "my own user name" ]
    then
        echo "%C(cyan)<%an>"
    else
        echo %C(bold blue)<%an>
    fi)

So then I put the variable into the git log such that I have

git log --color --pretty=format:"$USER" --abbrev-commit

Which should make is so that if a commit has been done by me, it will print my username in cyan; else, it will print the collaborator's username in bold blue. However, it doesn't.

I'm just wondering if someone knows what I'm doing wrong here? I suspect it is something to do with printing the variable into an environment that is already a string, but I don't know how to fix it. Any help is appreciated!

2

You are trying to put a shell script into Git's formatting parameter. Of course, it won't work.1

  • There are some Git parameters that actually accept shell script, like ones of git filter-branch; but they will be explicitly specified as such in the corresponding manual page.

What you are trying to accomplish could actually be done, not by Git, but by using external filter to match names, and mark them up with colors by using ANSI escape sequences.

For example, when author name matches with "Joe Average", highlight the name and angle brackets in cyan; otherwise, highlight name and angle brackets in bold blue.2 Leave other parts of the text intact in both cases...

Implemented by using Perl 5 as a filter:

git log "--pretty=tformat:%h <%an> %s" | perl -n -e '
chomp;
if(/^(.*)<([^>]*)>(.*)$/) {
    if ($2 eq "Joe Average") {
        print $1."\e[36m<".$2.">\e[39m".$3."\n";
    } else {
        print $1."\e[1m\e[34m<".$2.">\e[39m\e[0m$3\n";
    }
} else {
    print $_."\n";
}'

Implemented by using sed as a filter (a bit hacky3)...

git log "--pretty=tformat:%h <%an> %s" | sed '
s/<\(Joe Average\)>/\x1B[36m<\1>\x1B[39m/g;
t matched;
s/<\([^>]*\)>/\x1B[1m\x1B[34m<\1>\x1B[39m\x1B[0m/g;
:matched'

Regular output (without filter) from a test repository:

$ git log "--pretty=tformat:%h <%an> %s"
e7f3209 <Joe Average> improving portability
e3ebe74 <Jim Sixpack> adding first working prototype

Highlighted output (passed through either filter):

Note that these filter commands would still work even when you changed the format of git log output, provided that:

  • Author name is always enclosed in < >.
  • There's no more than one author in a single line.
  • There's no other thing getting enclosed in < >.

A different way to do what you asked is doing two different git log call with different --author and --pretty, merge results together, and sort them by timestamp; but the tricky part is making sure that output of both git log calls don't give overlapping output. (You can't use uniq since doing this marks both of them with different escape sequences)


Footnotes

  • 1 Actually, due to a lack of quoting, your if [ ! %an = "my own user name" ] ... would be already be executed at the USER= line (%an will be compared literally— it doesn't have special meaning in shell); making it effectively USER="%C(cyan)<%an>" before you could even invoke git log command.
  • 2 Also, you said you would like to highlight your own name in cyan, and others bolded in blue; but your original code actually tried the reverse: if (author name) is not (my own user name), highlight in cyan, else highlight in bold blue. I'm sticking to your description.
  • 3 \x is an extension of GNU sed, your mileage may vary if you are using this on non-GNU system.
  • +1. This works too, and is a little simpler: git log "--pretty=tformat:%h <%an> %s" | perl -pe ' if(/<([^>]*)>/) { if ($1 eq "Joe Average") { s/<$1>/\e[1m\e[36m$&\e[39m\e[0m/; } else { s/<$1>/\e[1m\e[34m$&\e[39m\e[0m/; } }' – cas Sep 23 at 2:37
  • BTW, you could use GNU uniq if you use e.g. -w 7 (aka --check-chars=7) option, so that it only uses the commit id for uniqueness. Still overly complicated, and probably not worth doing. using awk or something to implement a custom uniq would work too. – cas Sep 23 at 2:47
  • Thank you @cas, your addition to xwindows' answer worked for me. Thank you to xwindows also! Works well now. – Christopher Tatlock Sep 23 at 6:31
2

[ ! %an = "my own user name" ] is always true because %an will never equal your user name or any other user name for that matter: the only thing that it is equal to is %an, because it is just a literal string as far as bash is concerned. It only has a special meaning ("author name") in git.

There might be a clever way to do what you want, but I can't think of one ATM. And I don't think you can do it by mixing bash and git as you are trying to do. If there is a solution like this, it is likely to be extremely clumsy: you'll basically have to trampoline between the bash level and the git level, with git looping over all commits, but somehow telling bash the author name of each commit, so that the bash level can telll git what color to use.

If it can be done, I think it's likely to be a pure git solution, not a mixture of bash and git.

-1

Just for the sake of good practices:

Variables hold data. Functions hold code. Don't put code inside variables!

See wiki wooledge

  • Thanks for this. Very new to bash! – Christopher Tatlock Sep 22 at 9:36
  • 1
    Except in Lisp :-) And any language where functions are first-class objects... – NickD Sep 22 at 11:56
  • @NickD, sure i agree. I answered according to the op context. – Pier Sep 25 at 18:12
  • My comment was tongue-in-cheek, although I don't really agree with the sentiment (but I did not downvote your answer). – NickD Sep 25 at 20:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.