如何在一行中获取带有简短统计信息的 Git 日志?

Posted

技术标签:

【中文标题】如何在一行中获取带有简短统计信息的 Git 日志?【英文标题】:How to get Git log with short stat in one line? 【发布时间】:2014-02-03 22:12:21 【问题描述】:

以下命令在控制台上输出以下文本行

git log --pretty=format:"%h;%ai;%s" --shortstat
ed6e0ab;2014-01-07 16:32:39 +0530;Foo
 3 files changed, 14 insertions(+), 13 deletions(-)

cdfbb10;2014-01-07 14:59:48 +0530;Bar
 1 file changed, 21 insertions(+)

5fde3e1;2014-01-06 17:26:40 +0530;Merge Baz
772b277;2014-01-06 17:09:42 +0530;Qux
 7 files changed, 72 insertions(+), 7 deletions(-)

我有兴趣将上述格式显示为这样

ed6e0ab;2014-01-07 16:32:39 +0530;Foo;3;14;13
cdfbb10;2014-01-07 14:59:48 +0530;Bar;1;21;0
5fde3e1;2014-01-06 17:26:40 +0530;Merge Baz;0;0;0
772b277;2014-01-06 17:09:42 +0530;Qux;7;72;7

这将在一些可以解析分号分隔值的报告中使用。 问题是文本"\n 3 files changed, 14 insertions(+), 13 deletions(-)"(包括新行)被转换为3;14;13(没有新行) 一种可能的极端情况是像"5fde3e1;2014-01-06 17:26:40 +0530;Merge Baz" 这样的文本,它没有这样的行。在那种情况下,我想要;0;0;0

总体目标是分析一段时间内的文件更改统计信息。 我阅读了 git log 文档,但找不到任何可以帮助我以这种格式呈现的格式。我想出的最好的是上面提到的命令。

因此,任何可以生成预期格式的命令或 shell 脚本都会有很大帮助。

谢谢!

【问题讨论】:

【参考方案1】:
git log  --oneline --pretty="@%h"  --stat   |grep -v \| |  tr "\n" " "  |  tr "@" "\n"

这将显示如下内容:

a596f1e   1 file changed, 6 insertions(+), 3 deletions(-) 
4a9a4a1   1 file changed, 6 deletions(-) 
b8325fd   1 file changed, 65 insertions(+), 4 deletions(-) 
968ef81   1 file changed, 4 insertions(+), 5 deletions(-) 

【讨论】:

仅供参考,--pretty 将覆盖 --oneline,因此无需同时指定两者。此外,--stat 还将输出我们需要使用grep -v \| 删除的文件名,因此您可以使用--shortstat 来避免解析另一个行项(example output)。所以整个事情可以简化为git log --pretty="@%h" --shortstat | tr "\n" " " | tr "@" "\n"【参考方案2】:

不幸的是,仅使用git log 是不可能实现的。必须使用其他脚本来弥补大多数人不知道的情况:一些提交没有统计信息,即使它们不是合并。

我一直致力于将git log 转换为JSON 的项目,为了完成它,我必须做你需要做的事情:在一行中获取每个提交和统计信息。该项目名为 Gitlogg,欢迎您根据需要对其进行调整:https://github.com/dreamyguy/gitlogg

以下是 Gitlogg 的相关部分,它将让您接近您想要的:

git log --all --no-merges --shortstat --reverse --pretty=format:'commits\tcommit_hash\t%H\tcommit_hash_abbreviated\t%h\ttree_hash\t%T\ttree_hash_abbreviated\t%t\tparent_hashes\t%P\tparent_hashes_abbreviated\t%p\tauthor_name\t%an\tauthor_name_mailmap\t%aN\tauthor_email\t%ae\tauthor_email_mailmap\t%aE\tauthor_date\t%ad\tauthor_date_RFC2822\t%aD\tauthor_date_relative\t%ar\tauthor_date_unix_timestamp\t%at\tauthor_date_iso_8601\t%ai\tauthor_date_iso_8601_strict\t%aI\tcommitter_name\t%cn\tcommitter_name_mailmap\t%cN\tcommitter_email\t%ce\tcommitter_email_mailmap\t%cE\tcommitter_date\t%cd\tcommitter_date_RFC2822\t%cD\tcommitter_date_relative\t%cr\tcommitter_date_unix_timestamp\t%ct\tcommitter_date_iso_8601\t%ci\tcommitter_date_iso_8601_strict\t%cI\tref_names\t%d\tref_names_no_wrapping\t%D\tencoding\t%e\tsubject\t%s\tsubject_sanitized\t%f\tcommit_notes\t%N\tstats\t' |
  sed '/^[ \t]*$/d' |               # remove all newlines/line-breaks, including those with empty spaces
  tr '\n' 'ò' |                     # convert newlines/line-breaks to a character, so we can manipulate it without much trouble
  tr '\r' 'ò' |                     # convert carriage returns to a character, so we can manipulate it without much trouble
  sed 's/tòcommits/tòòcommits/g' |  # because some commits have no stats, we have to create an extra line-break to make `paste -d ' ' - -` consistent
  tr 'ò' '\n' |                     # bring back all line-breaks
  sed '
      N
      s/[)]\n\ncommits/)\
  commits/g
  ' |                              # some rogue mystical line-breaks need to go down to their knees and beg for mercy, which they're not getting
  paste -d ' ' - -                  # collapse lines so that the `shortstat` is merged with the rest of the commit data, on a single line

请注意,我使用制表符 (\t) 来分隔字段,因为 ; 可能已用于提交消息。

此脚本的另一个重要部分是每一行必须以唯一的字符串开头(在本例中为 commits)。那是因为我们的脚本需要知道该行的开始位置。事实上,git log 命令之后的任何内容都是为了弥补某些提交可能没有统计信息的事实。

但令我震惊的是,您想要实现的目标是以您可以可靠使用的格式整齐地输出提交。 Gitlogg 非常适合!它的一些特点是:

多个存储库的git log 解析为一个JSON 文件。 引入了repository键/值。 引入了 files changedinsertionsdeletions 键/值。 引入了 impact 键/值,表示提交的累积更改 (insertions - deletions)。 清理双引号 ",将所有允许或由用户输入创建的值(例如 subject)上的双引号转换为单引号 '。 几乎所有的pretty=format: 占位符都可用。 通过注释掉/取消注释可用的键/值,轻松包含/排除哪些键/值将被解析为JSON。 经过彻底注释的代码易于阅读。 控制台上的脚本执行反馈。 错误处理(因为需要正确设置存储库路径)。

成功,JSON被解析并保存。

错误 001:存储库路径不存在。

错误 002:存储库路径存在,但为空。

【讨论】:

【参考方案3】:

综合以上所有答案,这是我的 2 美分,以防有人在看:

echo "commit id,author,date,comment,changed files,lines added,lines deleted" > res.csv 
git log --since='last year'  --date=local --all --pretty="%x40%h%x2C%an%x2C%ad%x2C%x22%s%x22%x2C" --shortstat | tr "\n" " " | tr "@" "\n" >> res.csv
sed -i 's/ files changed//g' res.csv
sed -i 's/ file changed//g' res.csv
sed -i 's/ insertions(+)//g' res.csv
sed -i 's/ insertion(+)//g' res.csv
sed -i 's/ deletions(-)//g' res.csv
sed -i 's/ deletion(-)//g' res.csv

然后将其保存到git-logs-into-csv.sh 文件中,或者只是复制/粘贴到控制台中。

我认为这是相对不言自明的,但以防万一:

--all 从所有分支获取日志 --since 限制了我们要查看的提交次数 --shortstat - 了解提交中做了什么

【讨论】:

sed -i 在 macosx 上出现错误。这是我修改脚本的方式...echo "commit id,author,date,comment,changed files,lines added,lines deleted" > res.csv git log --since='last 35 days' --date=local --all --pretty="%x40%h%x2C%an%x2C%ad%x2C%x22%s%x22%x2C" --shortstat | tr "\n" " " | tr "@" "\n" >> res.csv cat res.csv | sed -E 's/ files changed//g' | sed -E 's/ file changed//g' | sed -E 's/ insertions?//g' | \ sed -E 's/ insertions?//g' | sed -E 's/ deletions?//g' | sed -E 's/\(\+\)//g' | sed -E 's/\(-\)//g' > commits.csv rm res.csv cat commits.csv【参考方案4】:

git 不支持使用纯 --format 的统计信息,这很遗憾 :( 但是编写脚本很容易,这是我的快速而肮脏的解决方案,应该很容易阅读:

#!/bin/bash

format_log_entry ()

    read commit
    read date
    read summary
    local statnum=0
    local add=0
    local rem=0
    while true; do
        read statline
        if [ -z "$statline" ]; then break; fi
        ((statnum += 1))
        ((add += $(echo $statline | cut -d' ' -f1)))
        ((rem += $(echo $statline | cut -d' ' -f2)))
    done
    if [ -n "$commit" ]; then
        echo "$commit;$date;$summary;$statnum;$add;$rem"
    else
        exit 0
    fi


while true; do
    format_log_entry
done

我敢肯定,它可以编写得更好,但是嘿 - 它既快又脏;)

用法:

$ git log --pretty=format:"%h%n%ai%n%s" --numstat | ./script

请注意,您指定的格式不是防弹的。分号可以出现在提交摘要中,这会破坏该行中的字段数量 - 您可以将摘要移动到行尾或以某种方式转义 - 你想怎么做?

【讨论】:

【参考方案5】:

这是awk 的一种方法。

awk 'BEGINFS="[,;]"; OFS=";" /;/ a=$0 /^ /gsub(/[a-z(+-) ]/,"") gsub(",",";"); print a,$0'

对于给定的输入,它返回:

ed6e0ab;2014-01-07 16:32:39 +0530;Foo;3;14;13
cdfbb10;2014-01-07 14:59:48 +0530;Bar;1;21
772b277;2014-01-06 17:09:42 +0530;Qux;7;72;7

对于像5fde3e1;2014-01-06 17:26:40 +0530;Merge Baz 这样后面没有3 files changed, 14 insertions(+), 13 deletions(-) 的行仍然不起作用。

【讨论】:

好吧...我不是 awk 专家,但我收到了以下文字 ";1;10+);10-)" 最后...基本上是额外的 +)-) ...我确定这可以改变......不知道如何。 也许您需要在 gsub() 函数中转义 + 和这些符号。在我的 awk 中没有必要。【参考方案6】:

跟进@user2461539 将其解析为列。也适用于更复杂的列,如“主题”。破解以选择您自己合适的分隔符。目前需要剪切主题行,因为它会在溢出时截断其他列。

#!/bin/bash
# assumes "_Z_Z_Z_" and "_Y_Y_" "_X_X_" as unused characters 
# Truncate subject line sanitized (%f) or not (%s) to 79 %<(79,trunc)%f
echo commit,author_name,time_sec,subject,files_changed,lines_inserted,lines_deleted>../tensorflow_log.csv;
git log --oneline --pretty="_Z_Z_Z_%h_Y_Y_\"%an\"_Y_Y_%at_Y_Y_\"%<(79,trunc)%f\"_Y_Y__X_X_"  --stat    \
    | grep -v \| \
    | sed -E 's/@//g' \
    | sed -E 's/_Z_Z_Z_/@/g' \
    |  tr "\n" " "   \
    |  tr "@" "\n" |sed -E 's/,//g'  \
    | sed -E 's/_Y_Y_/, /g' \
    | sed -E 's/(changed [0-9].*\+\))/,\1,/'  \
    | sed -E 's/(changed [0-9]* deleti.*-\)) /,,\1/' \
    | sed -E 's/insertion.*\+\)//g' \
    | sed -E 's/deletion.*\-\)//g' \
    | sed -E 's/,changed/,/' \
    | sed -E 's/files? ,/,/g'  \
    | sed -E 's/_X_X_ $/,,/g'  \
    | sed -E 's/_X_X_//g'>>../tensorflow_log.csv

【讨论】:

【参考方案7】:

我在我的~/.bashrc 中放了这样的东西:

function git-lgs() 
   git --no-pager log --numstat --format=%ai "$1" | sed ':a;N;$!ba;s/\n\n/\t/g' | sed 's/\(\t[0-9]*\t*[0-9]*\).*/\1/'

git-lgs 的参数是您要为其显示日志的文件名。

【讨论】:

以上是关于如何在一行中获取带有简短统计信息的 Git 日志?的主要内容,如果未能解决你的问题,请参考以下文章

markdown 一些命令在命令行上获取存储库的git commit日志统计信息。

如何在 git 中显示作者贡献的统计信息?

Git命令进阶操作:日志格式化

git log

git ls-remote --tags:如何获取日期信息?

如何在 git-rebase TODO 编辑器会话中查看更多日志行?