我见过一些书籍和文章中有一些非常漂亮的Git分支和提交的图表。如何制作Git历史的高质量可打印图像?


我不知道有什么直接的工具,但也许你可以破解一个脚本,将数据导出为点格式,并用Graphviz渲染。


尝试gitk或gitk——全部。但是,它没有打印/保存img功能。


这取决于他们的长相。我用gitx制作这样的图片:

您可以在24路章鱼合并(最初来自http://clojure-log.n01se.net/date/2008-12-24.html):


对于文本输出,您可以尝试:

git log --graph --abbrev-commit --decorate --date=relative --all

Or:

git log --graph --oneline --decorate --all

或者:这里有一个Graphviz别名,用于绘制DAG图。

我个人使用gitx、gitk-all和gitnub。


gitg:基于gtk的存储库查看器。这是新的,但有趣且有用。

我目前正在使用它。


要获得更详细的文本输出,请尝试:

git log --graph --date-order -C -M --pretty=format:"<%h> %ad [%an] %Cgreen%d%Creset %s" --all --date=short

您可以在文件$HOME/.gitconfig中添加别名:

[alias]
    graph = log --graph --date-order -C -M --pretty=format:\"<%h> %ad [%an] %Cgreen%d%Creset %s\" --all --date=short

有一个时髦的Git提交图作为Raphael web图形库的演示之一。

这个演示是静态的,但应该很容易获取代码并将静态数据交换为一组实时数据——我认为这只是JSON格式的Git提交数据。

演示在这里:http://dmitrybaranovskiy.github.io/raphael/github/impact.html


更新2:我发布了Git中可视化分支拓扑问题的改进版本,因为它更合适。该版本包含lg3,它显示了作者和提交者的信息,因此您确实应该检查一下。出于历史原因(我承认)留下这个答案,尽管我真的很想删除它。

我的两分钱:我通常在~/.gitconfig文件中使用两个别名:

[alias]
lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
lg = !"git lg1"

git lg/git lg1如下所示:

git lg2看起来像这样:


(注意:现在有更多适用于此问题的答案,例如fracz、Jubobs或Harry Lee的!)


基于我在回答相关问题时发现的Graphviz脚本,我黑了一个Ruby脚本,该脚本创建了Git存储库的摘要视图。它消除了所有的线性历史,只显示“有趣的”提交,即那些有多个父母、多个子女或由分支或标记指向的提交。下面是它为jQuery生成的图形片段:

git big picture和BranchMaster是类似的工具,它们试图通过仅显示标记、分支、合并等的关联方式,仅显示图形的高级结构。

这个问题还有其他选择。


Sourcetree是一个非常好的工具。它确实打印出了一个好看的中等大小的历史和分支图:(以下是在一个实验性Git项目上完成的,只是为了查看一些分支)。支持Windows 7+和Mac OS X 10.6+。


Gitg是GNOME的Gitk和GitX的克隆(它也适用于KDE等),它显示了一个漂亮的彩色图形。

它正在积极开发(截至2012年)。它允许您按时间顺序或拓扑顺序对提交(图形节点)进行排序,并隐藏不会导致选定分支的提交。

它适用于大型存储库和复杂的依赖关系图。

示例屏幕截图,显示linuxgit和linux-2.6存储库:


稍微调整一下Slipp的精彩回答,你可以使用他的别名记录一个分支:

[alias]
lgBranch1 = log --graph --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(bold white)— %an%C(reset)%C(bold yellow)%d%C(reset)' --abbrev-commit --date=relative
lgBranch2 = log --graph --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(bold white)— %an%C(reset)' --abbrev-commit
lg = !"git lg1"

你现在可以做的一切

git lgBranch1 <branch name>

甚至

git lgBranch1 --all

git forest是一个优秀的Perl脚本,我已经使用了一年多,我几乎不再直接使用git log命令。

以下是我喜欢这个剧本的一些地方:

它使用Unicode字符在图形中绘制线条,使图形线条看起来更加连续。您可以将--reverse与图形输出相结合,这在常规gitlog命令中是不可能的。它在内部使用gitlog获取提交列表,因此传递给gitlog的所有选项也可以传递给该脚本。

我有一个使用git forest的别名,如下所示:

[alias]
tree = "forest --pretty=format:\"%C(red)%h %C(magenta)(%ar) %C(blue)%an %C(reset)%s\" --style=15 --reverse"

这是终端输出的外观:


GitGraph(GitGraph)

它生成Git存储库提交历史的PNG或SVG表示。


我刚刚编写了一个工具,它可以使用HTML/Ccanvas生成一个漂亮的Git提交图。

并提供一个jQuery插件,使其易于使用。

[GitHub]https://github.com/tclh123/commits-graph

预览:


我添加了三个自定义命令:gittree、gitstree和gitvtree。我会按顺序检查它们。

[alias]
    tree = log --all --graph --decorate=short --color --format=format:'%C(bold blue)%h%C(reset) %C(auto)%d%C(reset)\n         %C(black)[%cr]%C(reset)  %x09%C(black)%an: %s %C(reset)'


使用gitstree和gitvtree,我使用Bash来帮助格式化。

[alias]
    logx = log --all --graph --decorate=short --color --format=format:'%C(bold blue)%h%C(reset)+%C(dim black)(%cr)%C(reset)+%C(auto)%d%C(reset)++\n+++       %C(bold black)%an%C(reset)%C(black): %s%C(reset)'
    stree = !bash -c '"                                                                             \
        while IFS=+ read -r hash time branch message; do                                            \
            timelength=$(echo \"$time\" | sed -r \"s:[^ ][[]([0-9]{1,2}(;[0-9]{1,2})?)?m::g\");     \
            timelength=$(echo \"16+${#time}-${#timelength}\" | bc);                                 \
            printf \"%${timelength}s    %s %s %s\n\" \"$time\" \"$hash\" \"$branch\" \"\";          \
        done < <(git logx && echo);"'


[alias]
    logx = log --all --graph --decorate=short --color --format=format:'%C(bold blue)%h%C(reset)+%C(dim black)(%cr)%C(reset)+%C(auto)%d%C(reset)++\n+++       %C(bold black)%an%C(reset)%C(black): %s%C(reset)'
    vtree = !bash -c '"                                                                             \
        while IFS=+ read -r hash time branch message; do                                            \
            timelength=$(echo \"$time\" | sed -r \"s:[^ ][[]([0-9]{1,2}(;[0-9]{1,2})?)?m::g\");     \
            timelength=$(echo \"16+${#time}-${#timelength}\" | bc);                                 \
            printf \"%${timelength}s    %s %s %s\n\" \"$time\" \"$hash\" \"$branch\" \"$message\";  \
        done < <(git logx && echo);"'


这适用于Git 1.9a版本。颜色值“auto”显然在这个版本中首次出现。这是一个很好的补充,因为分支名称将获得不同的颜色。例如,这使得区分本地分支和远程分支更加容易。


虽然我有时使用gitg,但我总是回到命令行:

[alias]
    # Quick look at all repositories
    loggsa = log --color --date-order --graph --oneline --decorate --simplify-by-decoration --all
    # Quick look at active branch (or refs pointed)
    loggs  = log --color --date-order --graph --oneline --decorate --simplify-by-decoration
    # Extend look at all repo
    logga  = log --color --date-order --graph --oneline --decorate --all
    # Extend look at active branch
    logg   = log --color --date-order --graph --oneline --decorate
    # Look with the date
    logda  = log --color --date-order --date=local --graph --format=\"%C(auto)%h%Creset %C(blue bold)%ad%Creset %C(auto)%d%Creset %s\" --all
    logd   = log --color --date-order --date=local --graph --format=\"%C(auto)%h%Creset %C(blue bold)%ad%Creset %C(auto)%d%Creset %s\"
    # Look with the relative date
    logdra = log --color --date-order --graph --format=\"%C(auto)%h%Creset %C(blue bold)%ar%Creset %C(auto)%d%Creset %s\" --all
    logdr = log --color --date-order --graph --format=\"%C(auto)%h%Creset %C(blue bold)%ar%Creset %C(auto)%d%Creset %s\"

    loga   = log --graph --color --decorate --all

    # For repositories without subject body commits (Vim repository, git-svn clones)
    logt  = log --graph --color --format=\"%C(auto)%h %d %<|(100,trunc) %s\"
    logta  = log --graph --color --format=\"%C(auto)%h %d %<|(100,trunc) %s\" --all
    logtsa = log --graph --color --format=\"%C(auto)%h %d %<|(100,trunc) %s\" --all --simplify-by-decoration

正如您所看到的,它几乎是一个按键保存别名,基于:

--颜色:清晰外观--图形:可视化父对象--日期顺序:回购最容易理解--装饰:谁是谁--oneline:很多时候你需要知道的关于提交--通过修饰来简化:初步了解的基础(仅标记、相关合并、分支)--all:保存带有或不带有此选项的所有别名的键击--date=relative(%ar):了解回购中的活动(有时分支机构很少在主服务器附近提交,但几个月前就提交了)

请参阅Git的最新版本(1.8.5及以上版本),您可以从装饰占位符%d中的%C(auto)中获益。

从这里开始,您只需要很好地理解gitrevisions,以过滤您需要的任何内容(比如master…develop,其中--简化合并可能有助于实现长期分支)。

命令行背后的强大功能是根据您的需要快速配置(了解存储库不是唯一的密钥日志配置,因此有时需要添加--numstat、--raw或--name状态)。在这里,git日志和别名快速、强大,(随着时间的推移)是您可以实现的最漂亮的图形。更重要的是,默认情况下通过寻呼机显示输出(更不用说),您可以始终快速搜索结果。不相信?您始终可以使用gitgraph等项目解析结果。


Gitgraph.js允许在没有存储库的情况下绘制漂亮的Git分支。只需编写一个JavaScript代码来配置分支和提交并在浏览器中呈现。交互式文档可用。

var gitGraph = new GitGraph({
   template: "blackarrow",
   mode: "compact",
   orientation: "horizontal",
   reverseArrow: true
});

var master = gitGraph.branch("master").commit().commit();
var develop = gitGraph.branch("develop").commit();
master.commit();
develop.commit().commit();
develop.merge(master);

或使用地铁模板:

或者使用提交消息、作者和标签:

用JSFiddle测试它。

通过@bsara使用GitGraper生成它。


gitdags构建在TikZ&PGF之上,是一个小型LaTeX包,它允许您轻松地生成矢量图形提交图等。

自动生成现有存储库的提交图不是gitdags的目的;它生成的图形仅用于教育目的。

我经常使用它为Git问题的答案生成图形,作为ASCII提交图形的替代:

如何在master上执行错误修复并将其集成到不太稳定的分支中?git是如何提交修改工作的?为什么在我运行“Git checkout origin/<branch>”后,Git会告诉我“当前不在任何分支”?将主分支合并到分支和将分支合并到主分支之间有什么区别?Git rebase--保留合并失败

下面是这样一个图表的示例,演示了简单的重新设置基础的效果:

\documentclass{article}

\usepackage{subcaption}
\usepackage{gitdags}

\begin{document}

\begin{figure}
  \begin{subfigure}[b]{\textwidth}
    \centering
    \begin{tikzpicture}
      % Commit DAG
      \gitDAG[grow right sep = 2em]{
        A -- B -- { 
          C,
          D -- E,
        }
      };
      % Tag reference
      \gittag
        [v0p1]       % node name
        {v0.1}       % node text
        {above=of A} % node placement
        {A}          % target
      % Remote branch
      \gitremotebranch
        [origmaster]    % node name
        {origin/master} % node text
        {above=of C}    % node placement
        {C}             % target
      % Branch
      \gitbranch
        {master}     % node name and text 
        {above=of E} % node placement
        {E}          % target
      % HEAD reference
      \gitHEAD
        {above=of master} % node placement
        {master}          % target
    \end{tikzpicture}
    \subcaption{Before\ldots}
  \end{subfigure}

  \begin{subfigure}[b]{\textwidth}
    \centering
    \begin{tikzpicture}
      \gitDAG[grow right sep = 2em]{
        A -- B -- { 
          C -- D' -- E',
          {[nodes=unreachable] D -- E },
        }
      };
      % Tag reference
      \gittag
        [v0p1]       % node name
        {v0.1}       % node text
        {above=of A} % node placement
        {A}          % target
      % Remote branch
      \gitremotebranch
        [origmaster]    % node name
        {origin/master} % node text
        {above=of C}    % node placement
        {C}             % target
      % Branch
      \gitbranch
        {master}      % node name and text 
        {above=of E'} % node placement
        {E'}          % target
      % HEAD reference
      \gitHEAD
        {above=of master} % node placement
        {master}          % target
    \end{tikzpicture}
    \subcaption{\ldots{} and after \texttt{git rebase origin/master}}
  \end{subfigure}
  \caption{Demonstrating a typical \texttt{rebase}}
\end{figure}

\end{document}

对于OS X用户,我采用了@gospes示例,并针对gsed(通过Homebrew安装的gnu-sed)对其进行了轻微修改,并调整了颜色(以使用黑色背景,但不确定原始示例可能会以示例中的方式呈现,因为它在具有黑色背景的终端上指定了黑色文本)。

[alias]
    # tree, vtree, stree support
    logx = log --all --graph --decorate=short --color --format=format:'%C(bold blue)%h%C(reset)+%C(bold black)(%cr)%C(reset)+%C(auto)%d%C(reset)++\n+++       %C(bold black)%an%C(reset)%C(bold black): %s%C(reset)'
    tree = log --all --graph --decorate=short --color --format=format:'%C(bold blue)%h%C(reset) %C(auto)%d%C(reset)\n         %C(bold black)[%cr]%C(reset)  %x09%C(bold black)%an: %s %C(reset)'
    stree = !bash -c '" \
    while IFS=+ read -r hash time branch message; do \
        timelength=$(echo \"$time\" | gsed -r \"s:[^ ][[]([0-9]{1,2}(;[0-9]{1,2})?)?m::g\"); \
        timelength=$(echo \"16+${#time}-${#timelength}\" | bc); \
        printf \"%${timelength}s    %s %s %s\n\" \"$time\" \"$hash\" \"$branch\" \"\"; \
    done < <(git logx && echo);"' | less -r
    vtree = !bash -c '" \
    while IFS=+ read -r hash time branch message; do \
      timelength=$(echo \"$time\" | gsed -r \"s:[^ ][[]([0-9]{1,2}(;[0-9]{1,2})?)?m::g\"); \
      timelength=$(echo \"16+${#time}-${#timelength}\" | bc); \
      printf \"%${timelength}s    %s %s %s\n\" \"$time\" \"$hash\" \"$branch\" \"$message\"; \
    done < <(git logx && echo);"' | less -r

OS X的关键是首先安装GNU sed(它具有-r选项)。使用Homebrew最容易做到这一点,它不会覆盖已安装的sed系统,而是将GNU sed安装为“gsed”。我希望这有助于@SlippD.Thompson,他在上面评论了OS X不工作。


git -c core.pager='less -SRF' log --oneline --graph --decorate

这是我的终端变体,类似于这里的许多答案。我喜欢将传递的标志调整为较小,以防止换行。

为了快速访问,我将其设置为别名,因为命令有点麻烦。


我编写了一个web工具,用于将Git日志转换为漂亮的SVG图形:

Bit Booster-离线提交图形绘制工具

将git-log的输出--prey='%h|%p|%d'直接上载到工具中,然后单击“download-grap.svg”链接。

该工具是纯客户端的,因此您的Git数据不会与我的服务器共享。您还可以在本地保存HTML+JavaScript,并使用“file:///”URL运行它。它在Ubuntu 12.04(精确穿山甲)上的Chrome 48和Firefox 43上进行了验证。

它生成的HTML可以直接发布到任何页面(包括blogspot博客引擎!)。看看这里的一些博客帖子:

http://bit-booster.blogspot.ca/

下面是该工具生成的示例HTML文件的屏幕截图:

http://bit-booster.com/graph.html(工具)


这里的许多答案都很好,但对于那些只想得到一个简单的一行到位的答案而不必设置别名或其他任何东西的人来说,这里是:

git log --all --decorate --oneline --graph

不是每个人都会一直做git日志,但当你需要它时,请记住:

“A Dog”=git log--all--decorate--oneline--graph

如果您输入

git config --global alias.adog "log --all --decorate --oneline --graph"

在命令提示符下,可以使用

git adog

即使您关闭并重新打开它,也可以从该提示中删除。


试试dita。它可以将任何ASCII图转换为图像。虽然它的设计没有考虑Git分支,但我对结果印象深刻。

源(txt文件):

        +--------+
        | hotfix |
        +---+----+
            |
--*<---*<---*
       ^
       |
       \--*<---*
               |
           +---+----+
           | master |
           +--------+

命令:

java -jar ditaa0_9.jar ascii-graph.txt

结果:

它还支持背景色、虚线、不同形状等。请参见示例。


我在~/.gitconfig中有这个git日志别名来查看图形历史:

[alias]
l = log --all --graph --pretty=format:'%C(auto)%h%C(auto)%d %s %C(dim white)(%aN, %ar)'

有了这一点,git l将输出如下内容:

在Git2.12+中,您甚至可以使用log.graphColors配置选项自定义图形的线条颜色。

至于日志的格式,它类似于--oneline,添加了作者名(尊重.mailmap)和相对作者日期。请注意,在Git>=1.8.3中支持%C(auto)语法,告诉Git使用提交散列等的默认颜色。


这是我对此事的看法:

截图:

用法:

githist-显示当前分支的历史记录

githist--all-显示所有分支(包括remotes)的图形

git-hist-master-devel-显示两个或多个分支之间的关系

git-hist--分支-显示所有本地分支

添加--按拓扑顺序对提交进行排序,而不是按日期排序(此别名中的默认值)

优点:

看起来就像普通的装饰,所以不同的分支名称使用不同的颜色添加提交人电子邮件添加提交相对日期和绝对日期按日期排序提交

设置:

git config --global alias.hist "log --graph --date-order --date=short \
--pretty=format:'%C(auto)%h%d %C(reset)%s %C(bold blue)%ce %C(reset)%C(green)%cr (%cd)'"

我建议tighttps://github.com/jonas/tig,一个更好的Git命令行工具。

您可以使用Homebrew在macOS上安装tig:

$ brew install tig
$ tig


除了“Slipp D.Thompson”的答案外,我建议您添加此别名以具有相同的装饰,但通过提交将其添加到单行中:

git config --global alias.tre "log --graph --decorate --pretty=oneline --abbrev-commit --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)'"

看着这段对话,我试着用我最喜欢的git可乐和git dag。

正在运行视图->DAG。。。从git-cola中删除并替换Log:master-with-都显示了一个包含所有分支的漂亮图形。


文件~/.oh-my-zsh/plugins/git/git.plugin.zsh中的一些别名:

gke='\gitk --all $(git log -g --pretty=%h)'
glg='git log --stat'
glgg='git log --graph'
glgga='git log --graph --decorate --all'
glgm='git log --graph --max-count=10'
glgp='git log --stat -p'
glo='git log --oneline --decorate'
glog='git log --oneline --decorate --graph'
gloga='git log --oneline --decorate --graph --all'
glol='git log --graph --pretty='\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --abbrev-commit'
glola='git log --graph --pretty='\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --abbrev-commit --all'

如果您的存储库位于GitLab上,您可以使用它的图形表示,因为它在浏览器中呈现为SVG。

转到存储库的图形视图,例如。,https://gitlab.com/gitlab-org/gitter/webapp/network/develop将图形向下滚动到底部(它会延迟加载提交!)使用浏览器的检查器将SVG元素复制到新文件在您选择的渲染器中打开它,例如Inkscape


一个漂亮整洁的表,如外壳的Git图形输出

除了图树之外,通常使用哈希

或在额外的列中

编辑:你想在不阅读所有解释的情况下立即开始吗?跳转至EDIT 6。

信息:对于外壳的更像分支的彩色版本,请参见我的第二个答案(https://stackoverflow.com/a/63253135/).

在对这个问题的所有回答中,到目前为止,没有一个显示出类似于查找外壳输出的干净表格。最接近的答案是我刚开始时的小鹅。

我的方法的核心点是只计算显示给用户的树字符。然后用空格填充到个人长度。

除了Git,你还需要这些工具

希腊语粘贴输出函数已安装的序列号第三方厕所

大多数是在任何Linux发行版上。

代码段是

while IFS=+ read -r graph hash time branch message;do

  # Count needed amount of white spaces and create them
  whitespaces=$((9-$(sed -nl1000 'l' <<< "$graph" | grep -Eo '\\\\|\||\/|\ |\*|_' | wc -l)))
  whitespaces=$(seq -s' ' $whitespaces|tr -d '[:digit:]')

  # Show hashes besides the tree ...
  #graph_all="$graph_all$graph$(printf '%7s' "$hash")$whitespaces \n"

  # ... or in an own column
  graph_all="$graph_all$graph$whitespaces\n"
  hash_all="$hash_all$(printf '%7s' "$hash")  \n"

  # Format all other columns
  time_all="$time_all$(printf '%12s' "$time") \n"
  branch_all="$branch_all$(printf '%15s' "$branch")\n"
  message_all="$message_all$message\n"
done < <(git log --all --graph --decorate=short --color --pretty=format:'+%C(bold 214)%<(7,trunc)%h%C(reset)+%C(dim white)%>(12,trunc)%cr%C(reset)+%C(214)%>(15,trunc)%d%C(reset)+%C(white)%s%C(reset)' && echo);

# Paste the columns together and show the table-like output
paste -d' ' <(echo -e "$time_all") <(echo -e "$branch_all") <(echo -e "$graph_all") <(echo -e "$hash_all") <(echo -e "$message_all")

为了计算所需的空白空间,我们使用

  sed -nl1000 'l' <<< "$graph"

要获取所有字符(直到每行1000个),请仅选择树字符:*|/\ _和空格

  grep -Eo '\\\\|\||\/|\ |\*|_'

最后对它们进行计数,并从我们选择的长度值中减去结果,在示例中为9。

为了生成计算出的空白量,我们使用

  seq -s' ' $whitespaces

并用截断位置号

  tr -d '[:digit:]'

然后将它们添加到图形线的末尾。就是这样!

Git有一个很好的选项,可以用语法“%><(amount_of_characters,truncate_option)”格式化输出说明符的长度,它从左侧“>”或右侧“<”添加空格,并可以从开头“ltrunc”、中间“mtrunc”或结尾“trunk”截断字符。

重要的是,上面的printf cmd对相应的Git列使用相同的长度值。

享受自己的风格,就像根据自己的需要寻找输出一样。

额外:

要获得正确的长度值,可以使用以下代码段

while read -r graph;do
    chars=$(sed -nl1000 'l' <<< "$graph" | grep -Eo '\\\\|\||\/|\ |\*|_' | wc -l)
    [[ $chars -gt ${max_chars:-0} ]] && max_chars=$chars
done < <(git log --all --graph --pretty=format:' ')

并使用$max_chars作为上面的正确长度值。

编辑1:请注意,在git树中也使用下划线字符,并相应地编辑上面的代码片段。如果缺少其他字符,请留下评论。

编辑2:如果要去掉分支和标记项周围的括号,只需在git命令中使用“%D”而不是“%D”,就像在EDIT 3中一样。

编辑3:也许对于分支和标记条目,“自动”颜色选项是您最喜欢的选项?

更改git命令的这部分(颜色214)

%C(214)%>(15,trunc)%D%C(reset)

自动

%C(auto)%>(15,trunc)%D%C(reset)

编辑4:或者你喜欢你自己的颜色组合,一个闪烁着脑袋的花哨输出?

为了能够首先设置头部、分支名称和标记的样式,我们需要在git命令中使用“auto”颜色选项,如EDIT3中所示。

然后,我们可以通过添加这3行来替换已知的颜色值

 # branch name styling
 branch=${branch//1;32m/38;5;214m}
 # head styling
 branch=${branch//1;36m/3;5;1;38;5;196m}
 # tag styling
 branch=${branch//1;33m/1;38;5;222m}

就在行前

 branch_all="$branch_all$(printf '%15s' "$branch")\n"

在我们的代码段中。替换值产生上述颜色。

例如,水头的替换值为

3;5;1;38;5;196

其中3;代表斜体,5;用于闪烁和1;38;5.196表示颜色。有关更多信息,请从这里开始。注意:此行为取决于您喜爱的终端,因此可能不可用。

但你可以选择任何你喜欢的颜色值。

数字颜色值和ANSI等效值概述

您可以在这里找到带有git颜色/样式选项的列表。

如果您需要控制台上的输出以获得准确的颜色(上面的图片由堆栈溢出缩小),您可以使用

for ((i=0;i<=255;i++));do
  while IFS='+' read -r tree hash;do
    echo -e "$(printf '%-10s' "(bold $i)") $hash  $(sed -nl500 'l' <<< "$hash"|grep -Eom 1 '[0-9;]*[0-9]m'|tr -d 'm')"
  done < <(git log --all --graph --decorate=short --color --pretty=format:'+%C(bold '$i')%h%C(reset)'|head -n 1)
done

在Git项目路径中,该路径使用Git日志输出中的第一个提交。

编辑5:正如成员“Andras Deak”所提到的,有一些方法可以使用此代码:

1) 作为别名:

别名不接受参数,但函数可以,因此只需在.bashrc中定义

   function git_tably () {
     unset branch_all graph_all hash_all message_all time_all max_chars

     ### add here the same code as under "2) as a shell-script" ###

   }

并直接在git项目路径下调用函数gittable(从类表中派生),或者从任何您想要的地方调用,将git项目的路径作为第一个参数。

2) 作为shell脚本:

我使用它的选项是将Git项目目录作为第一个参数传递给它,如果为空,则像正常行为一样使用工作目录。整体而言,我们拥有

# Edit your color/style preferences here or use empty values for git auto style
tag_style="1;38;5;222"
head_style="1;3;5;1;38;5;196"
branch_style="38;5;214"

# Determine the max character length of your git tree
while IFS=+ read -r graph;do
  chars_count=$(sed -nl1000 'l' <<< "$graph" | grep -Eo '\\\\|\||\/|\ |\*|_' | wc -l)
  [[ $chars_count -gt ${max_chars:-0} ]] && max_chars=$chars_count
done < <(cd "${1:-"$PWD"}" && git log --all --graph --pretty=format:' ')

# Create the columns for your preferred table-like git graph output
while IFS=+ read -r graph hash time branch message;do

  # Count needed amount of white spaces and create them
  whitespaces=$(($max_chars-$(sed -nl1000 'l' <<< "$graph" | grep -Eo '\\\\|\||\/|\ |\*|_' | wc -l)))
  whitespaces=$(seq -s' ' $whitespaces|tr -d '[:digit:]')

  # Show hashes besides the tree ...
  #graph_all="$graph_all$graph$(printf '%7s' "$hash")$whitespaces \n"

  # ... or in an own column
  graph_all="$graph_all$graph$whitespaces\n"
  hash_all="$hash_all$(printf '%7s' "$hash")  \n"

  # Format all other columns
  time_all="$time_all$(printf '%12s' "$time") \n"
  branch=${branch//1;32m/${branch_style:-1;32}m}
  branch=${branch//1;36m/${head_style:-1;36}m}
  branch=${branch//1;33m/${tag_style:-1;33}m}
  branch_all="$branch_all$(printf '%15s' "$branch")\n"
  message_all="$message_all$message\n"

done < <(cd "${1:-"$PWD"}" && git log --all --graph --decorate=short --color --pretty=format:'+%C(bold 214)%<(7,trunc)%h%C(reset)+%C(dim white)%>(12,trunc)%cr%C(reset)+%C(auto)%>(15,trunc)%D%C(reset)+%C(white)%s%C(reset)' && echo);

# Paste the columns together and show the table-like output
paste -d' ' <(echo -e "$time_all") <(echo -e "$branch_all") <(echo -e "$graph_all") <(echo -e "$hash_all") <(echo -e "$message_all")

3) 作为git别名:

也许最舒服的方法是在.gitconfig中添加git别名

[color "decorate"]
    HEAD = bold blink italic 196
    branch = 214
    tag = bold 222

[alias]
    count-log = log --all --graph --pretty=format:' '
    tably-log = log --all --graph --decorate=short --color --pretty=format:'+%C(bold 214)%<(7,trunc)%h%C(reset)+%C(dim white)%>(12,trunc)%cr%C(reset)+%C(auto)%>(15,trunc)%D%C(reset)+%C(white)%s%C(reset)'
    tably     = !bash -c '"                                                                                                    \
                  while IFS=+ read -r graph;do                                                                                 \
                    chars_count=$(sed -nl1000 \"l\" <<< \"$graph\" | grep -Eo \"\\\\\\\\\\\\\\\\|\\||\\/|\\ |\\*|_\" | wc -l); \
                    [[ $chars_count -gt ${max_chars:-0} ]] && max_chars=$chars_count;                                          \
                  done < <(git count-log && echo);                                                                             \
                  while IFS=+ read -r graph hash time branch message;do                                                        \
                    chars=$(sed -nl1000 \"l\" <<< \"$graph\" | grep -Eo \"\\\\\\\\\\\\\\\\|\\||\\/|\\ |\\*|_\" | wc -l);       \
                    whitespaces=$(($max_chars-$chars));                                                                        \
                    whitespaces=$(seq -s\" \" $whitespaces|tr -d \"[:digit:]\");                                               \
                    graph_all=\"$graph_all$graph$whitespaces\n\";                                                              \
                    hash_all=\"$hash_all$(printf \"%7s\" \"$hash\")  \n\";                                                     \
                    time_all=\"$time_all$(printf \"%12s\" \"$time\") \n\";                                                     \
                    branch_all=\"$branch_all$(printf \"%15s\" \"$branch\")\n\";                                                \
                    message_all=\"$message_all$message\n\";                                                                    \
                  done < <(git tably-log && echo);                                                                             \
                  paste -d\" \" <(echo -e \"$time_all\") <(echo -e \"$branch_all\") <(echo -e \"$graph_all\")                  \
                                <(echo -e \"$hash_all\") <(echo -e \"$message_all\");                                          \
                '"

而不是在任何项目路径下调用git tably。

Git非常强大,你可以改变头像、标签。。。直接如上面所示并从这里取出。

另一个有趣的选择是选择你最喜欢的树颜色

[log]
    graphColors = bold 160, blink 231 bold 239, bold 166, bold black 214, bold green, bold 24, cyan

这给了你疯狂的外观,但总是像表一样的git日志输出

眨眼太多!只是为了证明什么是可能的。指定的颜色太少会导致颜色重复。

只需单击一下即可获得完整的.gitconfig引用。

编辑6:由于你的支持,我改进了这个片段。现在,您可以用几乎任何gitlog命令来输入它,而不必再调整代码。试试看!

它是如何工作的?

一如既往地在.gitconfig中定义Git日志命令(格式如下)定义一个正树列编号,其中显示git图(可选)

那就打电话

git tably YourLogAlias

在任何git项目路径下或

git tably YourLogAlias目录号

其中TreeClNumber覆盖上面始终定义的值。

git tably YourLogAlias | less-r

将把输出管道输送到较少的位置,这对于巨大的历史记录非常有用。您的Git日志别名必须遵循以下格式规则:

每一列都必须由一个列分隔符表示,您必须选择该分隔符,如果不是唯一的,可能会导致问题即^ in。。。格式:“^%h^%cr^%s”生成树、哈希、时间和提交列在日志命令中必须使用的每个提交占位符之前%><(<N>[,ltrunc|mtrunc|trunk]),带有trunk选项之一(有关语法解释,请参见https://git-scm.com/docs/pretty-formats),但是,任何换行符的最后一个提交占位符都可以在没有它的情况下使用即…格式:“^%<(7,trunc)%h^%<如果装饰需要额外的字符,如(committer:,<and>)…%C(暗白色)(提交人:%cn%<%ce>)%C(重置)。。。要获得类似表的输出,必须在提交占位符之前和之后直接写入它们即…%C(暗淡的白色)%<(25,trunc)(提交人:%cn%<(25,trunk)<%ce>)%C(重置)。。。使用列颜色,如%C(白色)…%C(重置)需要彩色输出的--color选项即…--颜色…格式:'^%C(白色)%<(7,trunk)%h%C(重置)。。。如果使用--stat选项或类似选项,请在末尾添加一个换行符%n即…--stat.…格式:“…%n”。。。只要不使用换行符或仅使用空字符格式,就可以在每一列中放置git图形:'…%没有对于非空换行符…%n%提交占位符。。。只有当每行的所有第n列都存在并且使用相同的宽度时,才能将git图放置在每列n+1处为特定日志别名定义的树列编号的名称必须为YourLogAlias col

与正常的git日志输出相比,这个输出速度慢,但很好。

现在将改进的代码段添加到.gitconfig中

[color "decorate"]
    HEAD   = bold blink italic 196
    branch = 214
    tag    = bold 222

[alias]

    # Delimiter used in every mylog alias as column seperator
    delim     = ^

    # Short overview about the last hashes without graph
    mylog     = log --all --decorate=short --color --pretty=format:'^%C(dim white)%>(12,trunc)%cr%C(reset)^%C(bold 214)%<(7,trunc)%h%C(reset)' -5

    # Log with hashes besides graph tree
    mylog2    = log --all --graph --decorate=short --color --pretty=format:'%C(bold 214)%<(7,trunc)%h%C(reset)^%C(dim white)%>(12,trunc)%cr%C(reset)^%C(auto)%>(15,trunc)%D%C(reset)^%C(white)%<(80,trunc)%s%C(reset)'
    mylog2-col= 3

    # Log with hashes in an own column and more time data
    mylog3    = log --all --graph --decorate=short --color --pretty=format:'^%C(dim white)%>(12,trunc)%cr%C(reset)^%C(cyan)%<(10,trunc)%cs%C(reset)^%C(bold 214)%<(7,trunc)%h%C(reset)^%C(auto)%<(15,trunc)%D%C(reset)^%C(white)%s%C(reset)'
    mylog3-col= 4

    tably     = !bash -c '" \
                \
                \
                declare -A col_length; \
                apost=$(echo -e \"\\u0027\"); \
                delim=$(git config alias.delim); \
                git_log_cmd=$(git config alias.$1); \
                git_tre_col=${2:-$(git config alias.$1-col)}; \
                [[ -z "$git_tre_col" ]] && git_tre_col=1; \
                [[ -z "$git_log_cmd" ]] && { git $1;exit; }; \
                \
                \
                i=0; \
                n=0; \
                while IFS= read -r line;do \
                  ((n++)); \
                  while read -d\"$delim\" -r col_info;do \
                    ((i++)); \
                    [[ -z \"$col_info\" ]] && col_length[\"$n:$i\"]=${col_length[\"${last[$i]:-1}:$i\"]} && ((i--)) && continue; \
                    [[ $i -gt ${i_max:-0} ]] && i_max=$i; \
                    col_length[\"$n:$i\"]=$(grep -Eo \"\\([0-9]*,[lm]*trunc\\)\" <<< \"$col_info\" | grep -Eo \"[0-9]*\" | head -n 1); \
                    [[ -n \"${col_length[\"$n:$i\"]}\" ]] && last[$i]=$n; \
                    chars_extra=$(grep -Eo \"trunc\\).*\" <<< \"$col_info\"); \
                    chars_extra=${chars_extra#trunc)}; \
                    chars_begin=${chars_extra%%\\%*}; \
                    chars_extra=${chars_extra%$apost*}; \
                    chars_extra=${chars_extra#*\\%}; \
                    case \" ad aD ae aE ai aI al aL an aN ar as at b B cd cD ce cE ci cI cl cL cn cN cr \
                            cs ct d D e f G? gd gD ge gE GF GG GK gn gN GP gs GS GT h H N p P s S t T \" in \
                      *\" ${chars_extra:0:2} \"*) \
                        chars_extra=${chars_extra:2}; \
                        chars_after=${chars_extra%%\\%*}; \
                        ;; \
                      *\" ${chars_extra:0:1} \"*) \
                        chars_extra=${chars_extra:1}; \
                        chars_after=${chars_extra%%\\%*}; \
                        ;; \
                      *) \
                        echo \"No Placeholder found. Probably no tablelike output.\"; \
                        continue; \
                        ;; \
                    esac; \
                    if [[ -n \"$chars_begin$chars_after\" ]];then \
                      len_extra=$(echo \"$chars_begin$chars_after\" | wc -m); \
                      col_length["$n:$i"]=$((${col_length["$n:$i"]}+$len_extra-1)); \
                    fi; \
                  done <<< \"${line#*=format:}$delim\"; \
                  i=1; \
                done <<< \"$(echo -e \"${git_log_cmd//\\%n/\\\\n}\")\"; \
                \
                \
                git_log_fst_part=\"${git_log_cmd%%\"$apost\"*}\"; \
                git_log_lst_part=\"${git_log_cmd##*\"$apost\"}\"; \
                git_log_tre_part=\"${git_log_cmd%%\"$delim\"*}\"; \
                git_log_tre_part=\"${git_log_tre_part##*\"$apost\"}\"; \
                git_log_cmd_count=\"$git_log_fst_part$apost $git_log_tre_part$apost$git_log_lst_part\"; \
                col_length[\"1:1\"]=$(eval git \"${git_log_cmd_count// --color}\" | wc -L); \
                \
                \
                i=0; \
                while IFS=\"$delim\" read -r graph rest;do \
                  ((i++)); \
                  graph_line[$i]=\"$graph\"; \
                done < <(eval git \"${git_log_cmd/ --color}\" && echo); \
                \
                \
                i=0; \
                l=0; \
                while IFS= read -r line;do \
                  c=0; \
                  ((i++)); \
                  ((l++)); \
                  [[ $l -gt $n ]] && l=1; \
                  while IFS= read -d\"$delim\" -r col_content;do \
                    ((c++)); \
                    [[ $c -le $git_tre_col ]] && c_corr=-1 || c_corr=0; \
                    if [[ $c -eq 1 ]];then \
                      [[ \"${col_content/\\*}\" = \"$col_content\" ]] && [[ $l -eq 1 ]] && l=$n; \
                      count=$(wc -L <<< \"${graph_line[$i]}\"); \
                      whitespaces=$(seq -s\" \" $((${col_length[\"1:1\"]}-$count))|tr -d \"[:digit:]\"); \
                      col_content[$git_tre_col]=\"${col_content}$whitespaces\"; \
                    else \
                      col_content[$c+$c_corr]=\"$(printf \"%-${col_length[\"$l:$c\"]}s\" \"${col_content:-\"\"}\")\"; \
                    fi; \
                  done <<< \"$line$delim\"; \
                  for ((k=$c+1;k<=$i_max;k++));do \
                    [[ $k -le $git_tre_col ]] && c_corr=-1 || c_corr=0; \
                    col_content[$k+$c_corr]=\"$(printf \"%-${col_length[\"$l:$k\"]:-${col_length[\"${last[$k]:-1}:$k\"]:-0}}s\" \"\")\"; \
                  done; \
                  unset col_content[0]; \
                  echo -e \"${col_content[*]}\"; \
                  unset col_content[*]; \
                done < <(eval git \"$git_log_cmd\" && echo); \
                "' "git-tably"

在tably

第一段将delim(iter)、YourLogAlias和YourLogAlias列加载到shell变量中第二个读取每列的长度第三个计算树的最大长度第四个将树加载到数组中第五个组织并打印类似表格的输出

结果:

或使用新的TreeColNumber

再次:尽情地设计自己的干净桌子,就像根据自己的需要寻找输出一样。

您还可以在评论中共享您首选的格式化Git日志别名。我会不时地在上面的文字中加入评分最高的,并添加图片。


贝壳的颜色更像树枝。

其他一些答案显示了外部工具对图形树进行了很好的着色,部分带有彩色线条信息。这是我的shell方法,它与我在这里对类似表的输出的第一个答案相结合(https://stackoverflow.com/a/61487052).

特征:

您可以定义所有树的颜色你可以用相应的分支颜色染每一列可以将树列放置在不同的位置您可以使用各种git日志别名可以为每个别名定义树列编号你可以通过管道将其输入到较小的-r中,以获取巨大的历史记录

如何使用:

根据下面提到的格式说明,定义您的git日志别名,如所示的几个答案,例如Slipp D.、albfan、kaoru,并将其与代码片段一起粘贴到.gitconfig文件中。从项目路径调用它,通常使用

git-colord YourLogAlias或

git colored YourLogAlias TreeColumnNumber以动态放置树列。

格式化说明:

您的git日志别名必须遵循以下格式规则:

使用唯一字符作为每个提交占位符的列分隔符^…格式:“%h%cr%s”-->。。。格式:“^%h^%cr^%s”将整列用一种颜色着色,或将其留空以采用相应的分支颜色…格式:“^%h^%cr^%s”-->。。。格式:“^%h^%cr^%C(白色)%s%C(重置)”(哈希值和时间为分支颜色)必须使用trunc选项之一通过%><(<N>[,ltrunc|mtrunc|trunc])指定任何列的宽度,但是可以使用行上的任何最后一个提交占位符而不使用它…格式:“^%h^%cr^%C(白色)%s%C(重置)”-->。。。格式:“^%<(7,trunc)%h^%<如果需要额外的修饰字符,请将它们直接放置在提交占位符周围,即提交:…^%C(白色)%<(50,截断)%s%C(重置)…-->…^%C(白色)%<(50,trunc)提交:%s%C(重置)。。。如果使用换行符%n请将它们放在列分隔符之前或末尾…^%C(白色)%<(50,trunc)提交:%s%C(重置)'-->…%n ^%C(白色)%<(50,trunc)提交:%s%C(重置)%n'如果使用列颜色,如上面的%C(白色),则需要添加--color选项…格式:'^%<(7,trunc)%h.…-->--颜色格式:'^%<(7,trunc)%h。。。如果使用--stat选项或类似选项,请在末尾添加一个换行符%n…--stat..格式:“…”-->--stat..格式:“…%n”

其他:

对于具有非空换行符的git日志别名…%n%提交占位符。。。,只有当每行的所有第n列都存在并且使用相同的宽度时,才能将Git图放置在每列n+1处如果在文件.gitconfig中为YourLogAlias定义TreeColumnNumber,则YourLogAlias col必须是名称与正常的git日志输出相比,这个输出速度慢,但很好

示例:

git彩色lgc1

git彩色lgc2

git彩色lgc3

git彩色lgc4

代码段:

将以下行添加到.gitconfig文件中

[alias]

# Define your unique column separator
delim      = ^

# Define your 'git log' aliases and optional tree column numbers
lgc1       = log --all --graph --color --pretty=format:'^%<(7,trunc)%h^%C(white)%<(15,trunc)- %ar -%C(reset)^%<(35,trunc)%s^%C(white)%an%C(reset)'

lgc2       = log --all --graph --color --pretty=format:'%D^%<(7,trunc)%h^%<(35,trunc)%s^%C(white)%<(20,trunc)%an%C(reset)^%C(white) (%ar)%C(reset)'
lgc2-col   = 2

lgc3       = log --all --graph --color --pretty=format:'%<(7,trunc)%h%d^%<(11,trunc)%cs%C(reset)^%s%n^%C(white)%<(11,trunc)%cr%C(reset)^%C(white)%<(25,trunc)From %an%C(reset)^%C(white)%ae%C(reset)%n'
lgc3-col   = 2

lgc4       = log --all --graph --color --pretty=format:'%h^%C(white)%<(25,trunc)%an%C(reset)^%C(white)%<(31,trunc)%aD%C(reset)^%s%n^%C(dim white)%<(25,trunc)%ae%C(reset)^%>(31,trunc)%D%C(reset)%n'
lgc4-col   = 3

# Define your whitespace seperated tree color list
color-list = "1;38;5;222 1;38;5;69 1;38;5;250 1;38;5;70 1;31 1;38;5;93 1;33 2;38;5;11 1;38;5;48 1;35 1;32 1;38;5;111 1;38;5;160 1;38;5;130 1;36 38;5;21"

将Bash代码段也添加到.gitconfig文件中

# This is the Bash snippet which does all the magic
colored = !bash -c '" \
  \
  \
  declare -A col_length col_colored; \
  apost=$(echo -e \"\\u0027\"); \
  delim=$(git config alias.delim); \
  git_log_cmd=$(git config alias.$1); \
  graph_col=${2:-$(git config alias.$1-col)}; \
  color_list=( $(git config alias.color-list) ); \
  [[ -z \"$graph_col\" ]] && graph_col=1; \
  [[ -z \"$git_log_cmd\" ]] && { git $1;exit; }; \
  \
  \
  i=0; \
  n=0; \
  while IFS= read -r line; do \
    ((n++)); \
    while read -d\"$delim\" -r col_info;do \
      ((i++)); \
      [[ -z \"$col_info\" ]] && col_length[\"$n:$i\"]=${col_length[\"${last[$i]:-1}:$i\"]} && ((i--)) && continue; \
      [[ $i -gt ${i_max:-0} ]] && i_max=$i; \
      [[ \"${col_info:1:1}\" = \"C\" ]] && col_colored[\"$n:$i\"]=1; \
      col_length[\"$n:$i\"]=$(grep -Eo \"\\([0-9]*,[lm]*trunc\\)\" <<< \"$col_info\" | grep -Eo \"[0-9]*\" | head -n 1); \
      [[ -n \"${col_length[\"$n:$i\"]}\" ]] && last[$i]=$n; \
      chars_extra=$(grep -Eo \"\\trunc\\).*\" <<< \"$col_info\"); \
      chars_extra=${chars_extra#trunc)}; \
      chars_begin=${chars_extra%%\\%*}; \
      chars_extra=${chars_extra%$apost*}; \
      chars_extra=${chars_extra#*\\%}; \
      case \" ad aD ae aE ai aI al aL an aN ar as at b B cd cD ce cE ci cI cl cL cn cN cr \
              cs ct d D e f G? gd gD ge gE GF GG GK gn gN GP gs GS GT h H N p P s S t T \" in \
        *\" ${chars_extra:0:2} \"*) \
          chars_extra=${chars_extra:2}; \
          chars_after=${chars_extra%%\\%*}; \
          ;; \
        *\" ${chars_extra:0:1} \"*) \
          chars_extra=${chars_extra:1}; \
          chars_after=${chars_extra%%\\%*}; \
          ;; \
        *) \
          echo \"No Placeholder found. Probably no table-like output.\"; \
          continue; \
          ;; \
      esac; \
      if [[ -n \"$chars_begin$chars_after\" ]];then \
        len_extra=$(echo \"$chars_begin$chars_after\" | wc -m); \
        col_length[\"$n:$i\"]=$((${col_length[\"$n:$i\"]}+$len_extra-1)); \
      fi; \
    done <<< \"${line#*=format:}$delim\"; \
    i=1; \
  done <<< \"$(echo -e \"${git_log_cmd//\\%n/\\\\n}\")\"; \
  \
  \
  git_log_fst_part=\"${git_log_cmd%%\"$apost\"*}\"; \
  git_log_lst_part=\"${git_log_cmd##*\"$apost\"}\"; \
  git_log_tre_part=\"${git_log_cmd%%\"$delim\"*}\"; \
  git_log_tre_part=\"${git_log_tre_part##*\"$apost\"}\"; \
  git_log_cmd_count=\"$git_log_fst_part$apost $git_log_tre_part$apost$git_log_lst_part\"; \
  col_length[\"1:1\"]=$(eval git \"${git_log_cmd_count// --color}\" | wc -L); \
  \
  \
  i=0; \
  while IFS=\"$delim\" read -r graph rest;do \
    ((i++)); \
    graph_line[$i]=\"$graph\"; \
  done < <(eval git \"${git_log_cmd/ --color}\" && echo); \
  \
  \
  i=0; \
  l=0; \
  msg_err=; \
  color_list_ind=-1; \
  color_list_num=${#color_list[*]}; \
  color_repeat_ind=1; \
  if [[ $color_list_num -eq 0 ]];then \
    echo \"No tree colors specified via color-list under section [alias] in your .gitconfig\"; \
    echo \"Therefore collecting available Git colors, which may take a while ...\"; \
    while read -d\"[\" -r char;do \
      color=$(sed -nl99 \"l\" <<< \"$char\"); \
      case \"$color\" in \
        *\"m\"*) \
          color=${color%%m*}; \
          ;; \
        *) \
          continue; \
          ;; \
      esac; \
      case \" $color_list \" in \
        *\" $color \"*) \
          continue; \
          ;; \
        *) \
          color_list=\"$color_list$color \"; \
          ;; \
      esac; \
    done <<< \"$(git log --all --color --graph --pretty=format:)\"; \
    echo -e \"Temporary used color-list = \\\"${color_list% }\\\"\\n\"; \
    color_list=( ${color_list% } ); \
    color_list_num=${#color_list[*]}; \
  fi; \
  while IFS= read -r line;do \
    ((i++)); \
    j=-1; \
    case_off=; \
    graph_colored=; \
    graph_line_last=\"${graph_line[$i-1]}\"; \
    graph_line=\"${graph_line[$i]}\"; \
    graph_line_next=\"${graph_line[$i+1]}\"; \
    while IFS= read -r char;do \
      ((j++)); \
      case \"$case_off$char\" in \
        [^\\ \\_\\*\\/\\|\\\\]|\"case_off\"*) \
          graph_colored=\"${graph_colored}\\033[${point_color}m$char\\033[0m\"; \
          case_off=\"case_off\"; \
          ;; \
        \" \") \
          graph_colored=\"${graph_colored}$char\"; \
          case \"$char_last\" in \
            \" \") \
              unset color_ind[$j]; \
              ;; \
          esac; \
          ;; \
        \"*\") \
          case \"${graph_line_last:$j:1}\" in \
            \"*\") \
              :; \
              ;; \
            \"|\") \
              case \"${graph_line_last:$(($j-1)):1}\" in \
                \"\\\\\") \
                  color_ind[$j]=${color_ind_last[$j-1]:-${color_ind[$j-1]}}; \
                  ;; \
                *) \
                  :; \
                  ;; \
              esac; \
              ;; \
            \" \") \
              case \"${graph_line_last:$(($j-1)):1}\" in \
                \"\\\\\") \
                  color_ind[$j]=${color_ind_last[$j-1]:-${color_ind[$j-1]}}; \
                  ;; \
                \"/\") \
                  case \"${graph_line_last:$(($j+1)):1}\" in \
                    \"/\") \
                      color_ind[$j]=${color_ind[$j+1]}; \
                      ;; \
                    \" \") \
                      new_col_ind=${#color[*]}; \
                      while true;do \
                        ((color_list_ind++)); \
                        [[ $color_list_ind -ge $color_list_num ]] && color_list_ind=$color_repeat_ind; \
                        [[ $color_list_ind -ge $color_list_num ]] && break; \
                        new_color=${color_list[$color_list_ind]}; \
                        case \"$new_color\" in \
                          \"\"|[\\ ]*) \
                            continue; \
                            ;; \
                          \"${color[${color_ind[$j-1]}]}\") \
                            [[ $(($color_list_num-$color_repeat_ind)) -gt 1 ]] && continue; \
                            ;;& \
                          *) \
                            color[$new_col_ind]=$new_color; \
                            color_ind[$j]=$new_col_ind; \
                            last_new_colored_line=$i; \
                            break; \
                            ;; \
                        esac 2>/dev/null; \
                      done; \
                      ;; \
                    *) \
                      [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                      ;; \
                  esac; \
                  ;; \
                \" \") \
                  case \"${graph_line_last:$(($j+1)):1}\" in \
                    \"/\") \
                      color_ind[$j]=${color_ind[$j+1]}; \
                      ;; \
                    *) \
                      new_col_ind=${#color[*]}; \
                      while true;do \
                        ((color_list_ind++)); \
                        [[ $color_list_ind -ge $color_list_num ]] && color_list_ind=$color_repeat_ind; \
                        [[ $color_list_ind -ge $color_list_num ]] && break; \
                        new_color=${color_list[$color_list_ind]}; \
                        case \"$new_color\" in \
                          \"\"|[\\ ]*) \
                            continue; \
                            ;; \
                          \"${color[${color_ind[$j-1]}]}\") \
                            [[ $(($color_list_num-$color_repeat_ind)) -gt 1 ]] && continue; \
                            ;;& \
                          *) \
                            color[$new_col_ind]=$new_color; \
                            color_ind[$j]=$new_col_ind; \
                            last_new_colored_line=$i; \
                            break; \
                            ;; \
                        esac 2>/dev/null; \
                      done; \
                      ;; \
                  esac; \
                  ;; \
                *) \
                  [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                  ;; \
              esac; \
              ;; \
            \"\"|[^\\ \\_\\*\\/\\|\\\\]) \
              new_col_ind=${#color[*]}; \
              while true;do \
                ((color_list_ind++)); \
                [[ $color_list_ind -ge $color_list_num ]] && color_list_ind=$color_repeat_ind; \
                [[ $color_list_ind -ge $color_list_num ]] && break; \
                new_color=${color_list[$color_list_ind]}; \
                case \"$new_color\" in \
                  \"\"|[\\ ]*) \
                    continue; \
                    ;; \
                  \"${color[${color_ind[$j-1]}]}\") \
                    [[ $(($color_list_num-$color_repeat_ind)) -gt 1 ]] && continue; \
                    ;;& \
                  *) \
                    color[$new_col_ind]=$new_color; \
                    color_ind[$j]=$new_col_ind; \
                    last_new_colored_line=$i; \
                    break; \
                    ;; \
                esac 2>/dev/null; \
              done; \
              ;; \
            *) \
              [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
              ;; \
          esac; \
          graph_colored=\"${graph_colored}\\033[${color[${color_ind[$j]}]}m$char\\033[0m\"; \
          point_color=${color[${color_ind[$j]}]}; \
          ;; \
        \"|\") \
          case \"${graph_line_last:$j:1}\" in \
            \" \") \
              case \"${graph_line_last:$(($j-1)):1}\" in \
                \"/\") \
                  color_ind[$j]=${color_ind[$j+1]}; \
                  ;; \
                \"\\\\\") \
                  color_ind[$j]=${color_ind_last[$j-1]:-${color_ind[$j-1]}}; \
                  ;; \
                *) \
                  case \"${graph_line_last:$(($j+1)):1}\" in \
                    \"/\") \
                      color_ind[$j]=${color_ind[$j+1]}; \
                      ;; \
                    *) \
                      [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                      ;; \
                  esac; \
                  ;; \
              esac; \
              ;; \
            \"|\") \
              case \"${graph_line_last:$(($j-1)):1}\" in \
                \"\\\\\") \
                  case \"${graph_line:$(($j+1)):1}\" in \
                    \"\\\\\") \
                       :; \
                       ;; \
                    \" \") \
                      color_ind[$j]=${color_ind_last[$j-1]}; \
                      ;; \
                    *) \
                      [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                      ;; \
                  esac; \
                  ;; \
                *) \
                  :; \
                  ;; \
              esac; \
              ;; \
            \"*\") \
              case \"${graph_line:$(($j-1)):1}\" in \
                \"/\") \
                  if [[ $last_new_colored_line -eq $(($i-1)) ]];then \
                    new_col_ind=${#color[*]}; \
                    while true;do \
                      ((color_list_ind++)); \
                      [[ $color_list_ind -ge $color_list_num ]] && color_list_ind=$color_repeat_ind; \
                      [[ $color_list_ind -ge $color_list_num ]] && break; \
                      new_color=${color_list[$color_list_ind]}; \
                      case \"$new_color\" in \
                        \"\"|[\\ ]*) \
                          continue; \
                          ;; \
                        \"${color[${color_ind[$j-1]}]}\") \
                          [[ $(($color_list_num-$color_repeat_ind)) -gt 1 ]] && continue; \
                          ;;& \
                        *) \
                          color[$new_col_ind]=$new_color; \
                          color_ind[$j]=$new_col_ind; \
                          break; \
                          ;; \
                      esac 2>/dev/null; \
                    done; \
                  else \
                    color_ind[$j]=${color_ind_last[$j]}; \
                  fi; \
                  ;; \
                *) \
                  :; \
                  ;; \
              esac; \
              ;; \
            \"/\") \
              color_ind[$j]=${color_ind[$j]}; \
              ;; \
            *) \
              [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
              ;; \
          esac; \
          graph_colored=\"${graph_colored}\\033[${color[${color_ind[$j]}]}m$char\\033[0m\"; \
          ;; \
        \"/\") \
          case \"${graph_line_last:$(($j)):1}\" in \
            \"|\") \
              case \"${graph_line_last:$(($j+1)):1}\" in \
                \"/\") \
                  case \"${graph_line_next:$j:1}\" in \
                    \"|\") \
                      color_ind[$j]=${color_ind[$j+1]}; \
                      ;; \
                    \" \") \
                      color_ind[$j]=${color_ind[$j]}; \
                      ;; \
                    *) \
                      [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                      ;; \
                  esac; \
                  ;; \
                *) \
                  color_ind[$j]=${color_ind[$j]}; \
                  ;; \
              esac; \
              ;; \
            *) \
              case \"${graph_line_last:$(($j+2)):1}\" in \
                \"/\"|\"_\") \
                  color_ind[$j]=${color_ind[$j+2]}; \
                  ;; \
                *) \
                  case \"${graph_line_last:$(($j+1)):1}\" in \
                    \"/\"|\"_\"|\"|\") \
                      color_ind[$j]=${color_ind[$j+1]}; \
                      ;; \
                    \"*\") \
                      case \"${graph_line:$(($j+1)):1}\" in \
                        \"|\") \
                          if [[ $last_new_colored_line -eq $(($i-1)) ]];then \
                            color_ind[$j]=${color_ind_last[$j+1]}; \
                          else \
                            new_col_ind=${#color[*]}; \
                            while true;do \
                              ((color_list_ind++)); \
                              [[ $color_list_ind -ge $color_list_num ]] && color_list_ind=$color_repeat_ind; \
                              [[ $color_list_ind -ge $color_list_num ]] && break; \
                              new_color=${color_list[$color_list_ind]}; \
                              case \"$new_color\" in \
                                \"\"|[\\ ]*) \
                                  continue; \
                                  ;; \
                                \"${color[${color_ind[$j-1]}]}\") \
                                  [[ $(($color_list_num-$color_repeat_ind)) -gt 1 ]] && continue; \
                                  ;;& \
                                *) \
                                  color[$new_col_ind]=$new_color; \
                                  color_ind[$j]=$new_col_ind; \
                                  break; \
                                  ;; \
                              esac 2>/dev/null; \
                            done; \
                          fi; \
                          ;; \
                        *) \
                          color_ind[$j]=${color_ind_last[$j+1]}; \
                          ;; \
                      esac; \
                      ;; \
                    *) \
                      case \"${graph_line_last:$j:1}\" in \
                        \"\\\\\") \
                          :; \
                          ;; \
                        \" \") \
                          case \"${graph_line_last:$(($j+1)):1}\" in \
                            \"*\") \
                              color_ind[$j]=${color_ind[$j+1]}; \
                              ;; \
                            *) \
                              [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                              ;; \
                          esac; \
                          ;; \
                        *) \
                          [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                          ;; \
                      esac; \
                      ;; \
                  esac; \
                  ;; \
              esac; \
              ;; \
          esac; \
          graph_colored=\"${graph_colored}\\033[${color[${color_ind[$j]}]}m$char\\033[0m\"; \
          ;; \
        \"\\\\\") \
          case \"${graph_line_last:$(($j-1)):1}\" in \
            \"|\"|\"\\\\\") \
              color_ind[$j]=${color_ind_last[$j-1]:-${color_ind[$j-1]}}; \
              ;; \
            \"*\") \
              new_col_ind=${#color[*]}; \
              while true;do \
                ((color_list_ind++)); \
                [[ $color_list_ind -ge $color_list_num ]] && color_list_ind=$color_repeat_ind; \
                [[ $color_list_ind -ge $color_list_num ]] && break; \
                new_color=${color_list[$color_list_ind]}; \
                case \"$new_color\" in \
                  \"\"|[\\ ]*) \
                    continue; \
                    ;; \
                  \"${color[${color_ind[$j-1]}]}\") \
                    [[ $(($color_list_num-$color_repeat_ind)) -gt 1 ]] && continue; \
                    ;;& \
                  *) \
                    color[$new_col_ind]=$new_color; \
                    color_ind[$j]=$new_col_ind; \
                    break; \
                    ;; \
                esac 2>/dev/null; \
              done; \
              ;; \
            \" \") \
              case \"${graph_line_last:$(($j-2)):1}\" in \
                \"\\\\\"|\"_\") \
                  color_ind[$j]=${color_ind_last[$j-2]:-${color_ind[$j-2]}}; \
                  ;; \
                *) \
                  case \"${graph_line_last:$j:1}\" in \
                    \"|\") \
                      color_ind[$j]=${color_ind_last[$j]:-${color_ind[$j]}}; \
                      ;; \
                    *) \
                      [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                      ;; \
                  esac; \
                  ;; \
              esac; \
              ;; \
            *) \
              [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
              ;; \
          esac; \
          graph_colored=\"${graph_colored}\\033[${color[${color_ind[$j]}]}m$char$char\\033[0m\"; \
          ;; \
        \"_\") \
          case \"${graph_line:$(($j-2)):1}\" in \
            \"\\\\\"|\"_\") \
              color_ind[$j]=${color_ind[$j-2]}; \
              ;; \
            \" \"|\"/\") \
              k=2; \
              while [[ \"${graph_line:$(($j+$k)):1}\" = \"_\" ]];do \
                k=$(($k+2)); \
              done; \
              case \"${graph_line:$(($j+$k)):1}\" in \
                \"/\") \
                  case \"${graph_line_last:$(($j+$k+1)):1}\" in \
                    \"*\") \
                      color_ind[$j]=${color_ind[$j+$k+1]}; \
                      ;; \
                    \" \") \
                      case \"${graph_line_last:$(($j+$k)):1}\" in \
                        \"\\\\\") \
                          color_ind[$j]=${color_ind[$j+$k]}; \
                          ;; \
                        *) \
                          [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                          ;; \
                      esac; \
                      ;; \
                    \"|\") \
                      case \"${graph_line:$(($j+$k+1)):1}\" in \
                        \"|\") \
                          color_ind[$j]=${color_ind[$j+$k+2]}; \
                          ;; \
                        \" \") \
                          color_ind[$j]=${color_ind[$j+$k+1]}; \
                          ;; \
                        *) \
                          [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                          ;; \
                      esac; \
                      ;; \
                    *) \
                      [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                      ;; \
                  esac; \
                  ;; \
                *) \
                  [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
                  ;; \
              esac; \
              ;; \
            *) \
              [[ -n \"$msg_err\" ]] && echo -e \"Unknown case in graph_line $i: $graph_line for char $char at position $j\nwith the former graph_line $(($i-1)): $graph_line_last\"; \
              ;; \
          esac; \
          graph_colored=\"${graph_colored}\\033[${color[${color_ind[$j]}]}m$char\\033[0m\"; \
          ;; \
      esac; \
      char_last=$char; \
    done <<< \"$(grep -Eo \".\" <<< \"${graph_line%%$delim*}\")\"; \
    for key in ${!color_ind[*]};do \
      color_ind_last[$key]=${color_ind[$key]}; \
    done; \
    \
    \
    c=0; \
    ((l++)); \
    [[ $l -gt $n ]] && l=1; \
    while IFS= read -d\"$delim\" -r col_content;do \
      ((c++)); \
      [[ $c -le $graph_col ]] && c_corr=-1 || c_corr=0; \
      if [[ $c -eq 1 ]];then \
        [[ \"${col_content/\\*}\" = \"$col_content\" ]] && [[ $l -eq 1 ]] && l=$n; \
        whitespaces=$(seq -s\" \" $((${col_length[\"1:1\"]}-$j))|tr -d \"[:digit:]\"); \
        col_content[$graph_col]=\"${graph_colored}$whitespaces\"; \
      elif [[ ${col_colored[\"$l:$c\"]:-0} -eq 0 ]];then \
        col_content[$c+$c_corr]=\"\\033[${point_color:-0}m$(printf \"%-${col_length[\"$l:$c\"]}s\" \"${col_content:-\"\"}\")\\033[0m\"; \
      else \
        col_content[$c+$c_corr]=\"$(printf \"%-${col_length[\"$l:$c\"]}s\" \"${col_content:-\"\"}\")\"; \
      fi; \
    done <<< \"$line$delim\"; \
    for ((k=$c+1;k<=$i_max;k++));do \
      [[ $k -le $graph_col ]] && c_corr=-1 || c_corr=0; \
      col_content[$k+$c_corr]=\"$(printf \"%-${col_length[\"$l:$k\"]:-${col_length[\"${last[$k]:-1}:$k\"]:-0}}s\" \"\")\"; \
    done; \
    unset col_content[0]; \
    echo -e \"${col_content[*]}\"; \
    unset col_content[*]; \
  done < <(git $1 && echo); \
  "' "git-colored"

解释:

第一段将delim(iter)、颜色列表和YourLogAlias加载到shell变量中第二个读取每列的长度第三个计算树的最大长度第四个将树加载到数组中第五部分根据案例分析为树上色第六个为非树列着色,并打印类似表的输出

最大的部分是设置树颜色的案例分析。其他部分在我对类似于表的shell输出的链接答案中进行了解释。

请在评论中显示您最喜欢的格式化日志别名,因为我的只是示例。


如果您正在使用macOS,可以尝试GitUp:

x轴:分支y轴:时间


Visual Studio代码有一个惊人的扩展-Git Graph:


这里是我的社区别名:gitls。使用gitls,您可以查看图形,每次提交只覆盖一行。一切都是彩色的,并带有有用的信息。您还可以检查其他分支上的提交历史记录:

git config --global alias.ls '!f() { git log $1 --pretty=format:"%C(yellow)%h%Cred%d\\ %Creset%s%Cgreen\\ [%ae,%ar]" --decorate --graph; }; f'

用法:

# shows commits graph on the current branch
git ls

# shows commits graph on the develop branch
git ls develop 

以下是图表视图: