6

With something like git log --abbrev-commit --pretty=format:'%h %<(72,trunc)%s %d', you can get a fairly well aligned git commit message with a graph. It is completely aligned if you drop the --graph option. Here is roughly what the command looks like

*   40f1481 Commit title message        (HEAD, dev)
|\                                                                     
| * 9b5122d Commit title message        (debug)
| * fe7ddfe Commit title message               
| * 8e4d414 Commit title message      
| * 53affdd Commit title message    
| * a45fbaf Commit title message             
|/                                                                     
* 36b23c3 Commit title message                     
* 5b7bfe1 Commit title message        (master)

The issue is that with the graph symbols the alignment is messed up, as you can see in the first two commits. Here is what it should ideally look like

*   40f1481 Commit title message        (HEAD, dev)
|\                                                                     
| * 9b5122d Commit title message        (debug)
| * fe7ddfe Commit title message               
| * 8e4d414 Commit title message      
| * 53affdd Commit title message    
| * a45fbaf Commit title message             
|/                                                                     
*   36b23c3 Commit title message                     
*   5b7bfe1 Commit title message        (master)

My question is there an option for getting the correct alignment when using the graphing option? Or to get the width of the graph so that you can pad the log accordingly?


I know a quick hack would be to just pad the first option by tab (%x09), and it should work for probably most projects, but I'm wondering if their is a aesthetically superior, foolproof option that gets by with the minimum padding but also works in cases where 5 wouldn't be enough. Here is an example where the tab solution fails

enter image description here


Log with using columns, without colored graph.

enter image description here


Complete success! Will try to update later.

enter image description here

4
  • 1
    In order to use the minimum padding, Git would need to process the entire history to figure out how wide the graph was, and then re-process it for display. I doubt you can make that happen. Dropping the “minimum padding” part, I bet that adding a tab character to the start of the format string is the best option.
    – Daniel H
    Commented Sep 14, 2017 at 23:23
  • I see. I was mistaken what %x did and I thought when I was messing around I stumbled upon %x09 looking like 9 spaces (complete coincident I tried 9). Turned out I lucked into the tab function, and that %x05 didn't do what I thought I did. I suppose I'll do that for now.
    – Novice C
    Commented Sep 14, 2017 at 23:27
  • I should note that %x09 or a tab is also not a perfect option, depending on the graph some lines might be two tabs over or one. I updated the question with an image to show when it fails.
    – Novice C
    Commented Sep 16, 2017 at 6:23
  • 1
    @NoviceC It would be great if you could actually post your solution rather than just an image of it working!
    – Tom
    Commented Oct 11, 2019 at 9:03

5 Answers 5

11

Modern versions of Git (I have git 2.17.0 on cygwin environment) work fine with colorized graph and indentation without any additional tools or scripts.

My .gitconfig aliases part:

[alias]
    l   = log --graph --abbrev-commit --decorate=no --date=format:'%Y-%m-%d %H:%I:%S' --format=format:'%C(03)%>|(26)%h%C(reset)  %C(04)%ad%C(reset)  %C(green)%<(16,trunc)%an%C(reset)  %C(bold 1)%d%C(reset) %C(bold 0)%>|(1)%s%C(reset)' --all

I have tested it on django official repository and it works great:

3
  • 1
    Am I missing something, or does this just assume the graph width is less than 26 minus the hash width?
    – Tom
    Commented Oct 11, 2019 at 9:02
  • I think for the minutes %I is wrong. I think the correct would be %M. Can any body confirm that? Commented Jul 18, 2021 at 13:42
  • 2
    This is the correct answer but implies that newer versions of git do this automatically with any format, when in fact the magic lies in the right padding %<|(<N>)
    – Codebling
    Commented Mar 31, 2022 at 0:38
3

The closest I have seen a git log properly aligned is in garybernhardt/dotfiles/.githelpers.

Gary uses:

I tested with within a clone of the repo git:

vonc@VONCAVN7:~/gits/src/git$ 
git -c color.ui=auto log --graph --pretty=tformat:"%C(yellow)%h%Creset}%Cgreen(%ar)%Creset}%C(bold blue)<%an>%Creset}%C(bold red)%d%Creset %s" -100|  column -s '}' -t

In multiple lines:

git -c color.ui=auto log --graph \
  --pretty=tformat:"%C(yellow)%h%Creset}%Cgreen(%ar)%Creset}%C(bold blue)<%an>%Creset}%C(bold red)%d%Creset %s" \
  -100|  column -s '}' -t

BUT: it only works with color.ui set to false or auto, not set to always: colors will mess up the colum alignment.

Without colors:

* 238e487ea                    (3 weeks ago)  <Junio C Hamano>         (HEAD -> master, tag: v2.14.1-b5, origin/master, origin/HEAD) The fifth batch post 2.14
*   6e6ba65a7                  (3 weeks ago)  <Junio C Hamano>         Merge branch 'mg/killed-merge'
|\
| * 9d89b3552                  (3 weeks ago)  <Michael J Gruber>       merge: save merge state earlier
| * 8e6a6bb36                  (3 weeks ago)  <Michael J Gruber>       merge: split write_merge_state in two
| * 62dc42b93                  (3 weeks ago)  <Michael J Gruber>       merge: clarify call chain
| * e2de82f27                  (4 weeks ago)  <Michael J Gruber>       Documentation/git-merge: explain --continue
* |   eabdcd4ab                (3 weeks ago)  <Junio C Hamano>         Merge branch 'jt/packmigrate'
|\ \
| * | 7709f468f                (4 weeks ago)  <Jonathan Tan>           pack: move for_each_packed_object()
| * | f9a8672a8                (4 weeks ago)  <Jonathan Tan>           pack: move has_pack_index()
...
...
...
| * | | | | | | | fdbdb64f4    (5 weeks ago)  <Jeff King>              interpret-trailers: add an option to show only existing trailers
| * | | | | | | | 56c493ed1    (5 weeks ago)  <Jeff King>              interpret-trailers: add an option to show only the trailers
| * | | | | | | | 8abc89800    (5 weeks ago)  <Jeff King>              trailer: put process_trailers() options into a struct
* | | | | | | | |   bfd91b413  (3 weeks ago)  <Junio C Hamano>         Merge branch 'pb/trailers-from-command-line'
|\ \ \ \ \ \ \ \ \
| * | | | | | | | | c88bf5436  (7 weeks ago)  <Paolo Bonzini>          interpret-trailers: fix documentation typo
| * | | | | | | | | 0ea5292e6  (7 weeks ago)  <Paolo Bonzini>          interpret-trailers: add options for actions

The first 100 commits are perfectly aligned.

With colors...

colors

You would need additional pre-processing of the git log output before piping it to column.
See "Color escape codes in pretty printed columns".

6
  • Thanks, a solid solution without colors. For colors though it seems you need to know a priori what colors you will use where. That is fine for the formatted output, but the graph itself seems to pick its colors at runtime depending on its structure. Is there someway to read the colors it picked and re-add them in the awk command from your linked reference?
    – Novice C
    Commented Sep 16, 2017 at 9:06
  • @NoviceC you could instead used separators like (yellow) instead of the variable %C(yellow), and as mentioned in stackoverflow.com/a/20152025/6309, use awk to replace them with the color. You need to do so after piping to colum, not before, because of superuser.com/a/1149849/141. You will see a similar approach (column + awk) in unix.stackexchange.com/a/251757/7490
    – VonC
    Commented Sep 16, 2017 at 9:13
  • 1
    I see, so then it's not possible to get alignment with a colored graph (the *, |, / and \ part of the log). I already have a working version with colored text, just couldn't get the graph part. The text part can be colored before the column command since each column has the same color and so the character look the same. However, the graph part has different colors and different number of colors which leads to the large gap between the the dates in different rows.
    – Novice C
    Commented Sep 16, 2017 at 9:23
  • @NoviceC the idea is to produce an aligned version with "color string separator" (instead of non-printable color ansi characters), that you then replace with ansi colors with awk.
    – VonC
    Commented Sep 16, 2017 at 9:25
  • 1
    I agree that is a cleaner solution, but I still don't see how that helps you with the graph, which colors you do not pick or know beforehand.
    – Novice C
    Commented Sep 16, 2017 at 9:28
0

The bash function below is my solution to aligning git log columns with wrapped text and having support for color and graph lines.

Features :

  • Supports Linux bash as well as macOS/BSD variants.
  • Written to support lowest common denominator for installed OS and sed versions.
  • Automatically determines appropriate width for commit hashes
  • Works with or without the following git log options : --color , --graph , --name-status.
  • Keeps column alignment for graph lines while using the --color option by removing color only on graph lines from wrapped text. all other color perserved.
  • Shows tags and other reference markers immediately below the message column and preserves their colors.

Known issue :

  • Since I support both BSD and linux variants of sed, the posix compliant ($) sed commands did not take advantage of back references. This is because BSD does not support back-references in that mode. Therefore, Instead of using one sed command to remove color from the graph lines I used 4.

Requires : Git 1.9.5 or later for non-graph options. Versions slightly newer than 1.9.5 of git are needed for correct alignment of wrapped text when graph’s are present

Future improvements :

  • Automatically adjust columns widths for author.
  • Consider alternate solution which keeps the color of the graph lines by adjusting alignment based on the number of control characters encountered instead of removing the color codes.

Bash Code

function gitlog
{

   # Check for the existence of certain git log arguments 
   local using_graph=false
   local arg_i
   for arg_i in "$@"
   do 
      if [ "$arg_i" = "--graph" ]; then
         using_graph=true
      fi 
      if [ "$arg_i" = "--oneline" ]; then
         printf -- "--oneline not allowed for this shell function.\n  use regular git log for oneline.\n" 
         return 1
      fi 
   done

   # Set the OS dependant options
   # my_system_type can be set by sourcing my .bashrc
   local sed_regex_sym="r"
   if [ ! -z "$my_system_type" ]; then
      if [ "$my_system_type" = Mac ]; then
         sed_regex_sym="E"
      fi
   fi

   # Set the pre-determined column sizes here
   local N_AUTH_DATE_CHARS=11
   local N_AUTH_NAME_CHARS=15

   # Determine where to place columns for the various content
   local scrn_sz_arr=( $(stty size) )  #stty not tput in case of missing TERMINFO
   local N_SCRN_COLS=${scrn_sz_arr[1]}
   local N_HASH_CHARS=$(git log --pretty=format:%h -1 | tr -d '\n' | wc -c | tr -d '[:space:]')
   if [ "$using_graph" = true ]; then 
      local N_GRPH_MAX_CHARS=$(expr $N_SCRN_COLS / 5) #Use no more then 20% of screen
      local N_GRPH_ACT_CHARS=$(git log --graph --pretty=format:"%<(1,trunc)%ad" | tr -d '..' | \
                               awk '{print length}' | sort -nr | head -1 | tr -d '[:space:]')
      if [ "$N_GRPH_ACT_CHARS" -lt "$N_GRPH_MAX_CHARS" ]; then
         local N_GRPH_RSRV_CHARS=$N_GRPH_ACT_CHARS
      else
         local N_GRPH_RSRV_CHARS=$N_GRPH_MAX_CHARS
      fi
      #Extend space of N_HASH_CHARS to keep alignment when --graph option used.
      N_HASH_CHARS=$(expr $N_HASH_CHARS + $N_GRPH_RSRV_CHARS)
   fi
   local N_MSG_INDENT_CHARS=$(expr $N_HASH_CHARS + 1 + $N_AUTH_DATE_CHARS + $N_AUTH_NAME_CHARS + 1)
   local N_STAT_INDENT_CHARS=$(expr $N_MSG_INDENT_CHARS + 2)

   # Check that there is sufficient room to place all the content
   local N_MIN_COLS=$(expr $N_STAT_INDENT_CHARS + 12)
   if [ "$N_MIN_COLS" -gt "$N_SCRN_COLS" ]; then
      printf -- "Terminal window too narrow.\nNeed at least $N_MIN_COLS cols for this mode.\n"
      return 1
   fi

   # Git log logic below is as follows
   #   - use date=short to minimize space used by that column.  Linked with 
   #        N_AUTH_DATE_CHARS
   #   - use --pretty-format to specify columns as 
   #        hash--author date--author name--commit message.
   #   - first 4 sed statement removes color (if present) from graph lines 
   #        containing wrapped pretty-print text. Done for aligment of wrapped 
   #        text. Was one sed using back references, but that was not compatible
   #        with BSD (OSX) sed with -E.
   #   - fifth sed blanks out graph lines from newlines that were introduced 
   #        between commits by tformat.
   #   - sixth sed aligns wrapped text from pretty-print into message column.
   #   - last three sed statements insert a tab as a delimiter (recall tabs 
   #        removed by expand) between any other text and the --name-status text.
   #        This is in anticipation of the awk statement at the next pipe 
   #   - awk used to right pad the first column up to N_STAT_INDENT_CHARS and
   #        place any --name-status fields present to its right in order to 
   #        align it with the message text.
   #   - use tr to delete the inserted tabs that were used for column alignment
   #   - sed to remove any trailing white space.
   #   - awk removes extraneous blank lines
   #   - finally pipe to less -R to force the color option
   git log \
           --date=short \
           --pretty=tformat:"%C(auto)%w($N_SCRN_COLS,0,$N_MSG_INDENT_CHARS)%>|($N_HASH_CHARS)%h%x09%<($N_AUTH_DATE_CHARS,trunc)%ad%<($N_AUTH_NAME_CHARS,trunc)%an%x09%C(bold blue)%s%C(auto)%+d" \
           $* | \
           expand -t 1 | \
           sed -$sed_regex_sym $'/.*[*]/!{/.*[|]{1}/s/\x1b\\[([0-9](;[0-9])*)*[mGK]([_])\x1b\\[([0-9](;[0-9])*)*[mGK]/_/g;}' | \
           sed -$sed_regex_sym $'/.*[*]/!{/.*[|]{1}/s/\x1b\\[([0-9](;[0-9])*)*[mGK]([|])\x1b\\[([0-9](;[0-9])*)*[mGK]/|/g;}' | \
           sed -$sed_regex_sym $'/.*[*]/!{/.*[|]{1}/s/\x1b\\[([0-9](;[0-9])*)*[mGK]([\\])\x1b\\[([0-9](;[0-9])*)*[mGK]/\\\/g;}' | \
           sed -$sed_regex_sym $'/.*[*]/!{/.*[|]{1}/s/\x1b\\[([0-9](;[0-9])*)*[mGK]([\/])\x1b\\[([0-9](;[0-9])*)*[mGK]/\//g;}' | \
           sed -$sed_regex_sym "/^[|[:space:]]+[^[:alnum:]\\\/]+$/s/[|]//g" | \
           sed -$sed_regex_sym "/^[|]/s/^(.{$N_MSG_INDENT_CHARS})[[:space:]]*/\1/" | \
           sed -$sed_regex_sym "s/^([[:space:]_|\\\/]{0,$N_HASH_CHARS})([A-Z][[:space:]])/\1$(printf -- '\t')\2/" | \
           sed -$sed_regex_sym "s/^([[:space:]_|\\\/]{0,$N_HASH_CHARS})([R][0-9][0-9][0-9][[:space:]])/\1$(printf -- '\t')\2/" | \
           sed -$sed_regex_sym "s/^([[:space:]_|\\\/]{0,$N_HASH_CHARS})([C][0-9][0-9][[:space:]])/\1$(printf -- '\t')\2/" | \
           awk 'BEGIN{ FS="\t" }{ printf "%-'$N_STAT_INDENT_CHARS's%s\n", $1, $2 }' | \
           tr -d '\t' | \
           sed -$sed_regex_sym 's/[[:space:]]*$//' | \
           awk NF | \
           less -R --chop-long-lines
}

Example Output

  1. gitlog --color --graph gitlog --color --graph

  2. gitlog --color --name-status gitlog --color --name-status

0
#!/bin/bash

#
# git_tree.sh
#

N="$1"
if [ "$N" == "" ] ; then
    N=9
fi
P="$(git log --graph --format=format:'%m%n%m' --all -n "$N" \
    | perl -pe 's/././g' \
    | sort -r \
    | head -n 1 \
    | wc -c)"
P2=$((P+7))
git log --graph \
    --date=format:'%Y-%m-%d %H:%I:%S' \
    --all \
    -n "$N" \
    --format=format:"%C(03)%>|($P2)%h%C(reset) %C(04)%ad%C(reset) %C(green)%<(8,trunc)%an %C(1)%d%C(reset)%n%>|($P) %m %s"
echo
0

Here are other simple git aliases for git log with excellent results.

What is mostly working:

  • Automatically determines appropriate width for each field
  • Right aligns branch/ref and author
  • Works fine with narrow terminal widths

Add this in the $HOME/.gitconfig file:

[alias]
  l = !git log -n $(($(tput lines) / 2)) --date=format:'%y-%m-%d %H:%I' --pretty=format:'%Cred%h %Cgreen%ad %C(bold 0)%s%C(yellow)%>>|('"$(($(tput cols)-18))"')% D%C(bold blue)%<(16,trunc)% aL%Creset'

  ll = !git log -n $(($(tput lines) / 4)) --date=format:'%y-%m-%d %H:%I' --pretty=format:'%Cred%h %Cgreen%ad %C(bold 0)%s%C(yellow)%>>|('"$(($(tput cols)-18))"')% D%C(bold blue)%<(16,trunc)% aL%Creset%b'

  ls = !git log --stat -n $(($(tput lines) / 5)) --date=format:'%y-%m-%d %H:%I' --pretty=format:'%Cred%h %Cgreen%ad %C(bold 0)%s%C(yellow)%>>|('"$(($(tput cols)-18))"')% D%C(bold blue)%<(16,trunc)% aL%Creset'

  lg = !git log --graph -n $(($(tput lines) / 2)) --date=format:'%y-%m-%d %H:%I' --pretty=format:'%Cred%>|(13)%h %Cgreen%ad %C(bold 0)%s%C(yellow)%>>|('"$(($(tput cols)-18))"')% D%C(bold blue)%<(16,trunc)% aL%Creset'

  lb = !git log --simplify-by-decoration -n $(($(tput lines) / 2)) --date=format:'%y-%m-%d %H:%I' --pretty=format:'%Cred%h %Cgreen%ad %C(bold 0)%s%C(yellow)%>>|('"$(($(tput cols)-18))"')% D%C(bold blue)%<(16,trunc)% aL%Creset'

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