使用Git可视化工具展示分支拓扑结构

1087

我在自己的机器上独立使用Git,发现很难保持对所有分支和提交的心理模型。我知道可以使用git log查看从当前位置开始的提交历史记录,但是否有一种方法可以查看整个分支拓扑结构,类似于这些似乎被用于解释分支的ASCII地图?

      .-A---M---N---O---P
     /     /   /   /   /
    I     B   C   D   E
     \   /   /   /   /
      `-------------'

我觉得如果有人想要接手我的代码库,他们可能会很难理解其中的细节。

我想我受到了 AccuRev 的 stream browser 的影响...


无法在终端中显示Git树 - Leif Gruenwoldt
1
@leif81,对我来说是半重复。@Masi在他的问题中明确排除了gitk。 - Benjol
这个回答解决了你的问题吗?Pretty git branch graphs - Gonçalo Peres
33个回答

11

我在~/.gitconfig中设置了这个git log的别名,用于查看图形化历史记录:

[alias]
l = log --all --graph --pretty=format:'%C(auto)%h%C(auto)%d %s %C(dim white)(%aN, %ar)'
使用别名后,git l将显示以下内容: enter image description here 在 Git 2.12+ 中,您甚至可以使用 log.graphColors 配置选项自定义图形的行颜色。
至于日志的格式,它类似于 --oneline,但添加了作者名称(遵守 .mailmap)和相对作者日期。请注意,在 Git >= 1.8.3中支持 %C(auto) 语法,该语法告诉 Git 使用提交哈希等的默认颜色。

1
这是我第一次看到相对作者日期,感觉非常不错!谢谢,我也会把它添加到我的gitconfig中的! - Simon C.
Windows用户应该将单引号替换为双引号。 - pierDipi

9

另一个git log命令。这个命令使用固定宽度列

git log --graph --pretty=format:"%x09%h | %<(10,trunc)%cd |%<(25,trunc)%d | %s" --date=short

示例输出:

在此输入图片描述


9
Gitx是一款非常棒的可视化工具,如果你使用的是OS X操作系统的话。

4
gitk(适用于所有人)非常相似。虽然不完全相同,但非常相似。 - xero
1
有几个Gitx的分支 - 这个 (rowanj) 看起来目前是最好的。 - rjmunro

7

我个人最喜欢的别名,通过 .gitconfig,是:

graph = log --graph --color --all --pretty=format:"%C(yellow)%H%C(green)%d%C(reset)%n%x20%cd%n%x20%cn%x20(%ce)%n%x20%s%n"

您可以像这样直接从命令行进行测试:
git log --graph --color --all --pretty=format:"%C(yellow)%H%C(green)%d%C(reset)%n%x20%cd%n%x20%cn%x20(%ce)%n%x20%s%n"

示例输出:

在此输入图片描述


7
最好加上一些关于指令长什么样的简短解释。 - Max

7

我发现很难以置信,许多答案中都没有提到Gitviz,它可用于Windows/Linux/Mac。
除了提供分支和提交的2D视图外,它还会监听您的git命令,并自动修改图形。

enter image description here


1
我认为它只适用于Windows,你能分享Mac/Linux的链接吗? - nbari
它是 .net 的,理论上你可以使用 Linux 上的 mono 运行它(也许 mono 在 Mac 上也能运行?) 但这可能就是为什么没有多少人提到它的原因:来自 Gitviz GitHub:发布质量 Alpha。到目前为止,整个项目只包括一个 Readify 的家伙坐在培训课程的后面敲打几个小时而已。 - Matthew

7

看看SmartGit吧。它非常像TortoiseHg分支可视化,而且非商业使用是免费的。


5
我想分享我针对git log命令的 compact 预设:
(绿色是我的默认终端颜色)
预设预览

它旨在尽可能紧凑,类似于表格 ( 不添加任何多余空格 ) ,同时仍然信息丰富且易于阅读。这基本上是 Git 默认使用的medium 格式的紧凑版本。

特点:

  • 固定项目位置;
  • 提交哈希和引用名称的默认颜色;
  • 提交作者日期以本地时区显示;
  • 提交消息在128个字符处换行并缩进;
  • 扩展提交消息也会显示(如果有),任何尾随换行符都将被删除。

您可以使用以下命令将其添加到配置文件中:
(请注意,这将更改所有 git log 格式的日期格式!)

$ git config --global log.date 'format-local:%d %b %Y %H:%M'
$ git config --global pretty.compact '%C(auto)%h %C(cyan)%<(17,trunc)%an%C(auto) %D%n        %C(cyan)%ad%C(auto) %w(128,0,26)%s%w(0,0,26)%+b%-(trailers:key=FAKE)'

现在你可以像这样使用它(带有--graph--all或其他任何选项):

$ git log --graph --pretty=compact

如果您想将其设为默认值,可以使用以下命令:
$ git config --global format.pretty compact

如果您喜欢使用别名:

$ git config --global alias.logc "log --date=format-local:'%d %b %Y %H:%M' --pretty='%C(auto)%h %C(cyan)%<(17,trunc)%an%C(auto) %D%n        %C(cyan)%ad%C(auto) %w(128,0,26)%s%w(0,0,26)%+b%-(trailers:key=FAKE)'"

如果您想进行任何更改,请查看git log参考文献的漂亮格式部分


这个答案教会了我很多关于花式格式化的能力,例如%+b。我本来想问一下%-(trailers:key=FAKE)是什么意思,但在文档的漂亮格式部分末尾有解释,你可以搜索“占位符的百分比之后”。不过我不太确定什么是假预告片。也许你可以通过解释来帮助我理解。 - Steven Lu
嗨,@StevenLu。这部分有点像黑客技巧。 正如你已经发现的那样,%-前缀用于删除任何尾随换行符。但问题是,它只在“占位符扩展为空字符串”的情况下才这样做。我正在寻找一个始终扩展为空字符串的占位符,而不存在键的尾随符似乎是唯一的选择。 - EvgenKo423

5

Git 内置工具(无附加组件)带有日期时间格式化

因为文档对于使用内置格式化的说明有点晦涩,这里有两个更多的示例别名,可以立即使用。

git tree - 所有提交的带有时间戳的日志

# Tools for analyzing the merge history of a repo using tree-like graphics
[alias]
    tree = log --no-show-signature --graph --date=format-local:%H:%M:%S --all \
        --pretty="'%C(#ffe97b ul)%h%C(reset) %C(#568ea6)%cs %C(#305f72)%cd%C(reset)%C(auto)%d%C(reset) %s %C(yellow)(%C(reset)%C(#1abc9c)%an%C(reset)%C(yellow),%C(reset) %C(#007055)%cr%C(reset)%C(yellow))%C(reset)'"

git tree

git tree.branches – 所有分支/标签提交的时间戳记录日志

# Some refinements to normal 'git tree' output for alternative perspectives.
[alias "tree"]
    branches = tree --simplify-by-decoration

git tree.branches

颜色代码

规格 颜色 风格
提交ID 黄色 下划线
提交日期 深蓝色
提交时间 浅蓝色
提交信息 白色
提交作者 绿色
提交相对日期 深绿色
远程分支 红色
本地分支 紫色
标签 粉色 下划线

现有的答案都没有展示如何使用内置的 git log 工具更改日期时间格式。我的回答中提供了解决方案,另外两个答案使用预设值,而另一个答案则使用 %aD/%cD 占位符。不过,你可以在这里找到一个很好的例子,演示如何为日期和时间使用不同的颜色。;-) - EvgenKo423
1
@EvgenKo423 重新查看了一下对话,是的,我不知道为什么我说了那句话!非常抱歉。已从回答中删除。 - ardnew

4
最受欢迎的解答显示git log命令是最喜欢的解决方案。
如果您需要像表格一样的,例如列输出,则可以使用稍作修改和.gitconfig alias.tably代码片段中的一些限制与您惊人的git log命令。
修改:
- 您必须在每个提交占位符之前使用%><(<N>[,ltrunc|mtrunc|trunc]) - 添加一个唯一的分隔符作为列分隔符 - 添加--color选项以获得彩色输出
限制:
- 只要不使用非空换行符%n...,就可以将CPU图形放置在每个列上。 - 任何换行符的最后一个提交占位符都可以在没有%><(<N>[,trunc])的情况下使用。 - 如果需要装饰性字符,例如(committer: <>),则它们必须直接在提交占位符之前和之后写入才能获得表格形式输出。例如:...%C(dim white)%<(25,trunc)(committer: %cn%<(25,trunc) <%ce>)%C(reset)... - 如果--format=format:选项不是最后一个,则将其与%C(reset)关闭,如大多数情况下所做的那样。 - 与普通的git log输出相比,这个输出较慢,但很好。
示例来自此网站
thompson1     = log --all --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(auto)%d%C(reset)'

将使用符号 ^ 作为分隔符且不添加任何字符。
thompson1-new = log --all --graph --color --abbrev-commit --decorate --format=format:'^%C(bold blue)%<(7,trunc)%h%C(reset)^%C(bold green)%<(21,trunc)%ar%C(reset)^%C(white)%<(40,trunc)%s%C(reset)^%C(dim white)%<(25,trunc)%an%C(reset)^%C(auto)%d%C(reset)'

以下是内容翻译:

比较方式如下:

在此输入图片描述

在此输入图片描述

或将图形移动到第5列:

在此输入图片描述

要实现此目标,请将以下内容添加到您的 .gitconfig 文件中,并使用git tably YourLogAlias调用您的日志别名:

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

[alias]

    # delimiter used as column seperator
    delim = ^
    # example thompson1
    thompson1     = log --all --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(auto)%d%C(reset)'
    # modified thompson1 example
    thompson1-new = log --all --graph --color --abbrev-commit --decorate --format=format:'^%C(bold blue)%<(7,trunc)%h%C(reset)^%C(bold green)%<(21,trunc)%ar%C(reset)^%C(white)%<(40,trunc)%s%C(reset)^%C(dim white)%<(25,trunc)%an%C(reset)^%C(auto)%d%C(reset)'
    # set a column for the graph
    thompson1-new-col = 1

tably     = !bash -c '"                                                                                                              \
              declare -A col_length;                                                                                                 \
              delim=$(git config alias.delim);                                                                                       \
              git_log_cmd=$(git config alias.$1);                                                                                    \
              git_tre_col=${2:-$(git config alias.$1-col)};                                                                          \
                                                                                                                                     \
              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#*\\%};                                                                                   \
                  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}\")\";                                                                \
                                                                                                                                     \
              while IFS= read -r graph;do                                                                                            \
                chars_count=$(sed -nl1000 \"l\" <<< \"$graph\" | grep -Eo \"\\\\\\\\\\\\\\\\|\\||\\/|\\ |\\*|_\" | wc -l);           \
                [[ ${chars_count:-0} -gt ${col_length["1:1"]:-0} ]] && col_length["1:1"]=$chars_count;                               \
              done < <([[ -n \"$(grep -F graph <<< \"$git_log_cmd\")\" ]] && git log --all --graph --pretty=format:\" \" && echo);   \
                                                                                                                                     \
              l=0;                                                                                                                   \
              while IFS= read -r line;do                                                                                             \
                c=0;                                                                                                                 \
                ((l++));                                                                                                             \
                [[ $l -gt $n ]] && l=1;                                                                                              \
                while IFS= read -d\"$delim\" -r col_content;do                                                                       \
                  ((c++));                                                                                                           \
                  if [[ $c -eq 1 ]];then                                                                                             \
                    [[ -n \"$(grep -F \"*\" <<< \"$col_content\")\" ]] || l=2;                                                       \
                    chars=$(sed -nl1000 \"l\" <<< \"$col_content\" | grep -Eo \"\\\\\\\\\\\\\\\\|\\||\\/|\\ |\\*|_\" | wc -l);       \
                    whitespaces=$((${col_length["1:1"]}-$chars));                                                                    \
                    whitespaces=$(seq -s\" \" $whitespaces|tr -d \"[:digit:]\");                                                     \
                    col_content[1]=\"${col_content[1]}$col_content$whitespaces\n\";                                                  \
                  else                                                                                                               \
                    col_content[$c]=\"${col_content[$c]}$(printf \"%-${col_length[\"$l:$c\"]}s\" \"${col_content:-\"\"}\")\n\";      \
                  fi;                                                                                                                \
                done <<< \"$line$delim\";                                                                                            \
                for ((k=$c+1;k<=$i_max;k++));do                                                                                      \
                  empty_content=\"$(printf \"%-${col_length[\"$l:$k\"]:-${col_length[\"${last[$k]:-1}:$k\"]:-0}}s\" \"\")\";         \
                  col_content[$k]=\"${col_content[$k]}$empty_content\n\";                                                            \
                done;                                                                                                                \
              done < <(git $1 && echo);                                                                                              \
                                                                                                                                     \
              while read col_num;do                                                                                                  \
                if [[ -z \"$cont_all\" ]];then                                                                                       \
                  cont_all=${col_content[$col_num]};                                                                                 \
                else                                                                                                                 \
                  cont_all=$(paste -d\" \" <(echo -e \"$cont_all\") <(echo -e \"${col_content[$col_num]}\"));                        \
                fi;                                                                                                                  \
              done <<< $(seq 2 1 ${git_tre_col:-1};seq 1;seq $((${git_tre_col:-1}+1)) 1 $i_max);                                     \
              echo -e \"$cont_all\";                                                                                                 \
              "' "git-tably"

这只是我答案的一部分,详细解释请访问https://dev59.com/gXNA5IYBdhLWcg3wL6yx#61487052,但是这也适用于这个问题。

如果您的git log命令存在问题,请留言。


3
在Windows上,有一个非常有用的工具可以使用:Git Extensions。它是一个图形用户界面工具,使得Git操作非常容易。
此外,它是开源的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接