【问题标题】:How to count total lines changed by a specific author in a Git repository?如何计算 Git 存储库中特定作者更改的总行数?
【发布时间】:2010-11-18 21:35:12
【问题描述】:

我是否可以调用一个命令来计算 Git 存储库中特定作者更改的行数?我知道必须有方法来计算提交的数量,因为 Github 会为他们的影响图这样做。

【问题讨论】:

  • 您可能会考虑收集Linux内核开发统计信息的著名工具,例如,Repository is here git://git.lwn.net/gitdm.git

标签: git command-line statistics repository


【解决方案1】:

这给出了一些关于作者的统计数据,根据需要进行修改。

使用Gawk:

git log --author="_Your_Name_Here_" --pretty=tformat: --numstat \
| gawk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s removed lines: %s total lines: %s\n", add, subs, loc }' -

在 Mac OSX 上使用 Awk

git log --author="_Your_Name_Here_" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -

使用count-lines git-alias:

只需创建count-lines 别名(每个系统一次),例如:

git config --global alias.count-lines "! git log --author=\"\$1\" --pretty=tformat: --numstat | awk '{ add += \$1; subs += \$2; loc += \$1 - \$2 } END { printf \"added lines: %s, removed lines: %s, total lines: %s\n\", add, subs, loc }' #"

并在以后每次使用,例如:

git count-lines email@example.com

对于 Windows,将 Git-Bash 添加到 PATH(环境变量)后即可工作。
对于 Linux,可以将 awk 部分替换为 gawk
对于 MacOS,无需任何更改即可工作。

使用退出脚本 (2017 年更新)

github 上有一个新的包,看起来很漂亮,并使用 bash 作为依赖项(在 linux 上测试)。它更适合直接使用而不是脚本。

我是git-quick-stats (github link)

git-quick-stats复制到一个文件夹,并将该文件夹添加到路径中。

mkdir ~/source
cd ~/source
git clone git@github.com:arzzen/git-quick-stats.git
mkdir ~/bin
ln -s ~/source/git-quick-stats/git-quick-stats ~/bin/git-quick-stats
chmod +x ~/bin/git-quick-stats
export PATH=${PATH}:~/bin

用法:

git-quick-stats

【讨论】:

  • 正如this 所示,要获得特定作者的准确计数,您可能需要排除一些由他们提交但并非由他们真正创作的文件(例如库等)。跨度>
  • 这是错误的。您必须将-M -C 提供给命令行。
  • @samthebest,因为移动文件没有反映正确的统计信息。线条没有改变。致 Alex:我说的是 Git。顺便说一句,请参阅我对原始问题的评论。
  • 如果 url 不适合你,试试这个:git clone https://github.com/arzzen/git-quick-stats.git
  • 你实际上可以做到brew install git-quick-stats
【解决方案2】:

以下命令的输出应该相当容易发送到脚本以将总数相加:

git log --author="<authorname>" --oneline --shortstat

这给出了当前 HEAD 上所有提交的统计信息。如果您想在其他分支中添加统计信息,您必须将它们作为参数提供给git log

对于传递给脚本,甚至可以使用空日志格式删除“单行”格式,正如 Jakub Narębski 评论的那样,--numstat 是另一种选择。它生成每个文件而不是每行的统计信息,但更容易解析。

git log --author="<authorname>" --pretty=tformat: --numstat

【讨论】:

  • 可能还想在其中添加“--no-merges”。
  • 很抱歉这个问题,但数字告诉我什么?有两行,我不知道他们在告诉我什么。行被修改和添加?
  • -M -C 也错过了。
  • @Informatic0re git help log 告诉我第一行是添加的,第二行是删除的。
【解决方案3】:

如果有人想在他们的代码库中查看每个用户的统计数据,我的几个同事最近想出了这个可怕的单线:

git log --shortstat --pretty="%cE" | sed 's/\(.*\)@.*/\1/' | grep -v "^$" | awk 'BEGIN { line=""; } !/^ / { if (line=="" || !match(line, $0)) {line = $0 "," line }} /^ / { print line " # " $0; line=""}' | sort | sed -E 's/# //;s/ files? changed,//;s/([0-9]+) ([0-9]+ deletion)/\1 0 insertions\(+\), \2/;s/\(\+\)$/\(\+\), 0 deletions\(-\)/;s/insertions?\(\+\), //;s/ deletions?\(-\)//' | awk 'BEGIN {name=""; files=0; insertions=0; deletions=0;} {if ($1 != name && name != "") { print name ": " files " files changed, " insertions " insertions(+), " deletions " deletions(-), " insertions-deletions " net"; files=0; insertions=0; deletions=0; name=$1; } name=$1; files+=$2; insertions+=$3; deletions+=$4} END {print name ": " files " files changed, " insertions " insertions(+), " deletions " deletions(-), " insertions-deletions " net";}'

(需要几分钟来处理我们的 repo,它有大约 10-15k 次提交。)

【讨论】:

  • @EugenKonkov 在代码中被定义为插入 - 删除。
  • 这是唯一可以为存储库提供总结果且无需任何插件即可运行的命令。
  • 我将一堆用户列在一起,几乎所有可能的开发人员组合都回来了。我到底有什么奇怪的?
  • 它是由提交者分组,而不是作者,所以结果可能有点混乱。
  • @Damon,我通过添加 --no-merges git log --shortstat --no-merges --pretty="%cE" ... 解决了用户一起列出的问题 ...
【解决方案4】:

git-fame

https://github.com/oleander/git-fame-rb

这是一个很好的工具,可以一次获取所有作者的计数,包括提交和修改的文件计数:

sudo apt-get install ruby-dev
sudo gem install git_fame
cd /path/to/gitdir && git fame

https://github.com/casperdcl/git-fame 也有 Python 版本(@fracz 提到):

sudo apt-get install python-pip python-dev build-essential 
pip install --user git-fame
cd /path/to/gitdir && git fame

样本输出:

Total number of files: 2,053
Total number of lines: 63,132
Total number of commits: 4,330

+------------------------+--------+---------+-------+--------------------+
| name                   | loc    | commits | files | percent            |
+------------------------+--------+---------+-------+--------------------+
| Johan Sørensen         | 22,272 | 1,814   | 414   | 35.3 / 41.9 / 20.2 |
| Marius Mathiesen       | 10,387 | 502     | 229   | 16.5 / 11.6 / 11.2 |
| Jesper Josefsson       | 9,689  | 519     | 191   | 15.3 / 12.0 / 9.3  |
| Ole Martin Kristiansen | 6,632  | 24      | 60    | 10.5 / 0.6 / 2.9   |
| Linus Oleander         | 5,769  | 705     | 277   | 9.1 / 16.3 / 13.5  |
| Fabio Akita            | 2,122  | 24      | 60    | 3.4 / 0.6 / 2.9    |
| August Lilleaas        | 1,572  | 123     | 63    | 2.5 / 2.8 / 3.1    |
| David A. Cuadrado      | 731    | 111     | 35    | 1.2 / 2.6 / 1.7    |
| Jonas Ängeslevä        | 705    | 148     | 51    | 1.1 / 3.4 / 2.5    |
| Diego Algorta          | 650    | 6       | 5     | 1.0 / 0.1 / 0.2    |
| Arash Rouhani          | 629    | 95      | 31    | 1.0 / 2.2 / 1.5    |
| Sofia Larsson          | 595    | 70      | 77    | 0.9 / 1.6 / 3.8    |
| Tor Arne Vestbø        | 527    | 51      | 97    | 0.8 / 1.2 / 4.7    |
| spontus                | 339    | 18      | 42    | 0.5 / 0.4 / 2.0    |
| Pontus                 | 225    | 49      | 34    | 0.4 / 1.1 / 1.7    |
+------------------------+--------+---------+-------+--------------------+

但请注意:正如 Jared 在评论中提到的,在非常大的存储库上执行此操作将需要数小时。不确定是否可以改进,考虑到它必须处理如此多的 Git 数据。

【讨论】:

  • 在 2015 年中期的 macbook 和中大型 Android 项目(127k LoC 'is)上运行良好。几分钟。
  • @maxweber 我在 Linux 内核上试过了 :-) 听起来和我看到的一致。
  • @Vincent 当前用户的全部 loc / commits / files 百分比。
  • 更改分支、超时和排除文件夹:git fame --branch=dev --timeout=-1 --exclude=Pods/*
  • @AlexanderMills 我猜这是因为您无法有意义地计算 blob 上的行数
【解决方案5】:

我发现以下内容有助于查看当前代码库中谁的行数最多:

git ls-files -z | xargs -0n1 git blame -w | ruby -n -e '$_ =~ /^.*\((.*?)\s[\d]{4}/; puts $1.strip' | sort -f | uniq -c | sort -n

其他答案主要集中在提交中更改的行,但如果提交无法生存并被覆盖,它们可能只是被搅动了。上面的咒语也让你所有的提交者都按行排序,而不是一次只有一个。您可以向 git blame (-C -M) 添加一些选项,以获得更好的数字,将文件移动和文件之间的行移动考虑在内,但如果这样做,该命令可能会运行更长的时间。

此外,如果您要查找所有提交者的所有提交中更改的行,以下小脚本会很有帮助:

http://git-wt-commit.rubyforge.org/#git-rank-contributors

【讨论】:

  • 我正要给 +1,但后来我意识到解决方案取决于 ruby​​... :(
  • 您可以将其修改为不使用 ruby​​,因为我只是使用 ruby​​ 进行字符串替换。你可以使用 perl、sed、python 等
  • 对我不起作用:-e:1:in `
    ': UTF-8 中的无效字节序列(ArgumentError)
  • /^.*\((.*?)\s[\d]{4}/ 应该是 /^.*?\((.*?)\s[\d]{4}/ 以防止作为作者匹配源中的括号。
  • 嗯,由于解析错误,我的执行显示了许多甚至不存在的用户。我认为这不是一个可靠的答案。
【解决方案6】:

要计算给定作者(或所有作者)在给定分支上的提交次数,您可以使用git-shortlog;尤其是它的--numbered--summary 选项,例如在 git 存储库上运行时:

$ git shortlog v1.6.4 --numbered --summary
  6904  Junio C Hamano
  1320  Shawn O. Pearce
  1065  Linus Torvalds
    692  Johannes Schindelin
    443  Eric Wong

【讨论】:

  • 请注意,v1.6.4 在此示例中用于使输出具有确定性:无论您何时克隆和/或从 git 存储库获取,它都是相同的。
  • 包括v1.6.4给我:fatal: ambiguous argument 'v1.6.4': unknown revision or path not in the working tree.
  • 啊,不,我错过了“在 git 存储库上运行时”。公平地说,大多数人不会在 git repo 上运行这个命令。实际上,差距很大。
  • git shortlog -sne 或者,如果您不想包含合并 git shortlog -sne --no-merges
  • @Swards: -s--summary-n--numbered,并且 [new] -e--email 以显示作者的电子邮件(并分别计算同一作者使用不同的电子邮件地址,考虑到.mailmap 更正)。关于--no-merges的好电话。
【解决方案7】:

看了Alex的Gerty3000的回答后,我尝试缩短单行:

基本上,使用 git log numstat 和 not 跟踪 files 更改的数量。

Mac OSX 上的 Git 版本 2.1.0:

git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done

例子:

Jared Burrows   added lines: 6826, removed lines: 2825, total lines: 4001

【讨论】:

    【解决方案8】:

    AaronM 中的 Answer 使用 shell 单线是好的,但实际上还有另一个错误,如果用户名和用户名之间有不同数量的空格,空格会损坏用户名日期。损坏的用户名会给出多行用户数,您必须自己总结。

    这个小改动为我解决了这个问题:

    git ls-files -z | xargs -0n1 git blame -w --show-email | perl -n -e '/^.*?\((.*?)\s+[\d]{4}/; print $1,"\n"' | sort -f | uniq -c | sort -n
    

    注意 \s 之后的 + 将占用从名称到日期的所有空格。

    实际上添加这个答案是为了我自己的记忆和帮助其他人,因为这至少是我第二次在谷歌上搜索这个主题:)

    • 编辑 2019-01-23--show-email 添加到 git blame -w 以改为在电子邮件中聚合,因为有些人在不同的计算机上使用不同的 Name 格式,有时两个同名的人正在工作在同一个 git 中。

    【讨论】:

    • 这个使用 perl 的答案似乎比基于 ruby​​ 的答案好一点。 Ruby 卡在不是实际 UTF-8 文本的行上,perl 没有抱怨。但是 perl 做对了吗?我不知道。
    • 子模块结果为unsupported file type,但除此之外,即使使用它们似乎也可以正常工作(它会跳过它们)。
    【解决方案9】:

    这是为所有作者生成统计数据的简短单行代码。它比上面https://stackoverflow.com/a/20414465/1102119 的 Dan 解决方案快得多(我的时间复杂度为 O(N) 而不是 O(NM),其中 N 是提交数,M 是作者数)。

    git log --no-merges --pretty=format:%an --numstat | awk '/./ && !author { author = $0; next } author { ins[author] += $1; del[author] += $2 } /^$/ { author = ""; next } END { for (a in ins) { printf "%10d %10d %10d %s\n", ins[a] - del[a], ins[a], del[a], a } }' | sort -rn
    

    【讨论】:

    • 很好,但输出是什么意思?
    • 你应该添加--no-show-signature,否则那些对其提交进行pgp签名的人将不会被计算在内。
    • ins[a] - del[a], ins[a], del[a], a ,所以如果我是正确的插入删除,插入,删除,名称
    • 如何将此命令添加到我的 git 配置中,以便我可以使用“git count-lines”调用它?
    • 没关系,我想通了:count-lines = "!f() { git log --no-merges --pretty=format:%an --numstat | awk '/./ &amp;&amp; !author { author = $0; next } author { ins[author] += $1; del[author] += $2 } /^$/ { author = \"\"; next } END { for (a in ins) { printf \"%10d %10d %10d %s\\n\", ins[a] - del[a], ins[a], del[a], a } }' | sort -rn; }; f"。 (注意我在 Windows 上;您可能需要使用不同类型的引号)
    【解决方案10】:

    @mmrobins @AaronM @ErikZ @JamesMishra 提供的变体都有一个共同的问题:他们要求 git 生成不打算用于脚本使用的混合信息,包括来自同一行的存储库中的行内容,然后匹配混乱用正则表达式。

    当某些行不是有效的 UTF-8 文本时,以及某些行恰好与正则表达式匹配时(此处发生),这是一个问题。

    这是一个没有这些问题的修改行。它要求 git 在单独的行上干净地输出数据,这使得我们可以轻松地过滤我们想要的内容:

    git ls-files -z | xargs -0n1 git blame -w --line-porcelain | grep -a "^author " | sort -f | uniq -c | sort -n
    

    您可以使用 grep 查找其他字符串,例如作者邮件、提交者等。

    也许首先做export LC_ALL=C(假设bash)来强制字节级处理(这也恰好从基于UTF-8的语言环境中极大地加速grep)。

    【讨论】:

    • 那里不错,很酷,你可以很容易地把它混合起来,但是这不能满足原始海报的要求,请提供来自 git 的作者计数。当然您可以运行它并执行 wc-l 等操作,但是您需要为存储库中的每个作者重复此操作。
    • @AaronM 我不明白你的批评。这条线 AFAIK 输出与您相同的统计数据,只是更强大。因此,如果我的回答“未能按照原始发帖人的要求执行,请提供来自 git 的作者的计数”,那么您的答案就更多了。请赐教。
    • 对不起,我看错了,我认为必须为每个不同的作者姓名修改命令。您对其他字符串的 grep 的评论使我到达那里,但这是我的误解。
    【解决方案11】:

    在中间给出了一个使用 ruby​​ 的解决方案,默认情况下 perl 更可用这里是作者在当前行中使用 perl 的替代方案。

    git ls-files -z | xargs -0n1 git blame -w | perl -n -e '/^.*\((.*?)\s*[\d]{4}/; print $1,"\n"' | sort -f | uniq -c | sort -n
    

    【讨论】:

    • 更新的正则表达式并没有产生有意义的差异,并且由于您没有逃脱第一个括号,因此它被破坏了。但是,我可以看到一些情况,我的前一个可能会在代码行中找到一些位来锁定。这将更可靠地工作:git ls-files -z | xargs -0n1 混帐责备 -w | perl -n -e '/^.*?\((.*?)\s[\d]{4}/; print $1,"\n"' | sort -f | uniq -c | sort -n
    • 感谢您尝试制作更可靠的正则表达式。请参阅我的答案以获得更强大的变体stackoverflow.com/a/36090245/1429390
    【解决方案12】:

    你可以使用 whodid (https://www.npmjs.com/package/whodid)

    $ npm install whodid -g
    $ cd your-project-dir
    

    $ whodid author --include-merge=false --path=./ --valid-threshold=1000 --since=1.week
    

    或者直接输入

    $ whodid
    

    然后你可以看到这样的结果

    Contribution state
    =====================================================
     score  | author
    -----------------------------------------------------
     3059   | someguy <someguy@tensorflow.org>
     585    | somelady <somelady@tensorflow.org>
     212    | niceguy <nice@google.com>
     173    | coolguy <coolgay@google.com>
    =====================================================
    

    【讨论】:

    • “分数”是什么意思?
    【解决方案13】:

    除了Charles Bailey's answer,您可能还想将-C 参数添加到命令中。否则,即使文件内容没有被修改,文件重命名也算作大量的添加和删除(与文件的行数一样多)。

    为了说明,这里是a commit,当使用git log --oneline --shortstat 命令时,我的一个项目中移动了很多文件:

    9052459 Reorganized project structure
     43 files changed, 1049 insertions(+), 1000 deletions(-)
    

    这里使用检测文件副本和重命名的git log --oneline --shortstat -C 命令进行相同的提交:

    9052459 Reorganized project structure
     27 files changed, 134 insertions(+), 85 deletions(-)
    

    在我看来,后者更真实地说明了一个人对项目的影响有多大,因为重命名文件比从头开始编写文件的操作要小得多。

    【讨论】:

    • 当我执行“git log --oneline --shortstat”时,我没有得到你的结果。我有一个包含版本数但没有总数的提交列表。如何获取所有 git 存储库中编辑的总行数?
    【解决方案14】:

    这是一个快速的 ruby​​ 脚本,可以根据给定的日志查询将每个用户的影响集中起来。

    例如,对于rubinius

    Brian Ford: 4410668
    Evan Phoenix: 1906343
    Ryan Davis: 855674
    Shane Becker: 242904
    Alexander Kellett: 167600
    Eric Hodel: 132986
    Dirkjan Bussink: 113756
    ...
    

    脚本:

    #!/usr/bin/env ruby
    
    impact = Hash.new(0)
    
    IO.popen("git log --pretty=format:\"%an\" --shortstat #{ARGV.join(' ')}") do |f|
      prev_line = ''
      while line = f.gets
        changes = /(\d+) insertions.*(\d+) deletions/.match(line)
    
        if changes
          impact[prev_line] += changes[1].to_i + changes[2].to_i
        end
    
        prev_line = line # Names are on a line of their own, just before the stats
      end
    end
    
    impact.sort_by { |a,i| -i }.each do |author, impact|
      puts "#{author.strip}: #{impact}"
    end
    

    【讨论】:

    • 这个脚本很棒,但不包括只有单行提交的作者!要修复,更改如下: changes = /(\d+) 插入.*(\d+) 删除/.match(line)
    • 我注意到只有最后一个删除数被匹配。 *( 之间需要一个文字空格。例如,在 1 个文件更改、1 个插入 (+)、123 个删除 (-) 的匹配中,只有 3 将匹配删除计数。 (\d+) insertion.* (\d+) deletion 似乎成功了。
    【解决方案15】:

    这是最好的方法,它还可以让您清楚地了解所有用户的提交总数

    git shortlog -s -n
    

    【讨论】:

    • 有用,但这是提交的数量而不是总代码行数
    【解决方案16】:

    这是一个让你的生活更轻松的好回购

    git-quick-stats

    在安装了 brew 的 Mac 上

    brew install git-quick-stats

    运行

    git-quick-stats

    只需输入列出的数字并按 Enter 键,从此列表中选择您想要的选项。

     Generate:
        1) Contribution stats (by author)
        2) Contribution stats (by author) on a specific branch
        3) Git changelogs (last 10 days)
        4) Git changelogs by author
        5) My daily status
        6) Save git log output in JSON format
    
     List:
        7) Branch tree view (last 10)
        8) All branches (sorted by most recent commit)
        9) All contributors (sorted by name)
       10) Git commits per author
       11) Git commits per date
       12) Git commits per month
       13) Git commits per weekday
       14) Git commits per hour
       15) Git commits by author per hour
    
     Suggest:
       16) Code reviewers (based on git history)
    
    

    【讨论】:

      【解决方案17】:

      我提供了对上述简短答案的修改,但这不足以满足我的需求。我需要能够对最终代码中提交的行和行进行分类。我还想按文件细分。此代码不会递归,它只会返回单个目录的结果,但如果有人想更进一步,这是一个好的开始。复制并粘贴到文件中并使其可执行或使用 Perl 运行。

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      use Data::Dumper;
      
      my $dir = shift;
      
      die "Please provide a directory name to check\n"
          unless $dir;
      
      chdir $dir
          or die "Failed to enter the specified directory '$dir': $!\n";
      
      if ( ! open(GIT_LS,'-|','git ls-files') ) {
          die "Failed to process 'git ls-files': $!\n";
      }
      my %stats;
      while (my $file = <GIT_LS>) {
          chomp $file;
          if ( ! open(GIT_LOG,'-|',"git log --numstat $file") ) {
              die "Failed to process 'git log --numstat $file': $!\n";
          }
          my $author;
          while (my $log_line = <GIT_LOG>) {
              if ( $log_line =~ m{^Author:\s*([^<]*?)\s*<([^>]*)>} ) {
                  $author = lc($1);
              }
              elsif ( $log_line =~ m{^(\d+)\s+(\d+)\s+(.*)} ) {
                  my $added = $1;
                  my $removed = $2;
                  my $file = $3;
                  $stats{total}{by_author}{$author}{added}        += $added;
                  $stats{total}{by_author}{$author}{removed}      += $removed;
                  $stats{total}{by_author}{total}{added}          += $added;
                  $stats{total}{by_author}{total}{removed}        += $removed;
      
                  $stats{total}{by_file}{$file}{$author}{added}   += $added;
                  $stats{total}{by_file}{$file}{$author}{removed} += $removed;
                  $stats{total}{by_file}{$file}{total}{added}     += $added;
                  $stats{total}{by_file}{$file}{total}{removed}   += $removed;
              }
          }
          close GIT_LOG;
      
          if ( ! open(GIT_BLAME,'-|',"git blame -w $file") ) {
              die "Failed to process 'git blame -w $file': $!\n";
          }
          while (my $log_line = <GIT_BLAME>) {
              if ( $log_line =~ m{\((.*?)\s+\d{4}} ) {
                  my $author = $1;
                  $stats{final}{by_author}{$author}     ++;
                  $stats{final}{by_file}{$file}{$author}++;
      
                  $stats{final}{by_author}{total}       ++;
                  $stats{final}{by_file}{$file}{total}  ++;
                  $stats{final}{by_file}{$file}{total}  ++;
              }
          }
          close GIT_BLAME;
      }
      close GIT_LS;
      
      print "Total lines committed by author by file\n";
      printf "%25s %25s %8s %8s %9s\n",'file','author','added','removed','pct add';
      foreach my $file (sort keys %{$stats{total}{by_file}}) {
          printf "%25s %4.0f%%\n",$file
                  ,100*$stats{total}{by_file}{$file}{total}{added}/$stats{total}{by_author}{total}{added};
          foreach my $author (sort keys %{$stats{total}{by_file}{$file}}) {
              next if $author eq 'total';
              if ( $stats{total}{by_file}{$file}{total}{added} ) {
                  printf "%25s %25s %8d %8d %8.0f%%\n",'', $author,@{$stats{total}{by_file}{$file}{$author}}{qw{added removed}}
                  ,100*$stats{total}{by_file}{$file}{$author}{added}/$stats{total}{by_file}{$file}{total}{added};
              } else {
                  printf "%25s %25s %8d %8d\n",'', $author,@{$stats{total}{by_file}{$file}{$author}}{qw{added removed}} ;
              }
          }
      }
      print "\n";
      
      print "Total lines in the final project by author by file\n";
      printf "%25s %25s %8s %9s %9s\n",'file','author','final','percent', '% of all';
      foreach my $file (sort keys %{$stats{final}{by_file}}) {
          printf "%25s %4.0f%%\n",$file
                  ,100*$stats{final}{by_file}{$file}{total}/$stats{final}{by_author}{total};
          foreach my $author (sort keys %{$stats{final}{by_file}{$file}}) {
              next if $author eq 'total';
              printf "%25s %25s %8d %8.0f%% %8.0f%%\n",'', $author,$stats{final}{by_file}{$file}{$author}
                  ,100*$stats{final}{by_file}{$file}{$author}/$stats{final}{by_file}{$file}{total}
                  ,100*$stats{final}{by_file}{$file}{$author}/$stats{final}{by_author}{total}
              ;
          }
      }
      print "\n";
      
      
      print "Total lines committed by author\n";
      printf "%25s %8s %8s %9s\n",'author','added','removed','pct add';
      foreach my $author (sort keys %{$stats{total}{by_author}}) {
          next if $author eq 'total';
          printf "%25s %8d %8d %8.0f%%\n",$author,@{$stats{total}{by_author}{$author}}{qw{added removed}}
              ,100*$stats{total}{by_author}{$author}{added}/$stats{total}{by_author}{total}{added};
      };
      print "\n";
      
      
      print "Total lines in the final project by author\n";
      printf "%25s %8s %9s\n",'author','final','percent';
      foreach my $author (sort keys %{$stats{final}{by_author}}) {
          printf "%25s %8d %8.0f%%\n",$author,$stats{final}{by_author}{$author}
              ,100*$stats{final}{by_author}{$author}/$stats{final}{by_author}{total};
      }
      

      【讨论】:

      • 我收到此错误:在 x.pl 第 71 行非法除零。
      • 在第 71 行解决了非法除以零的问题。认为如果没有编辑会发生这种情况,但这是我不久前写的。
      【解决方案18】:

      使用以下命令将日志保存到文件中:

      git log --author="<authorname>" --oneline --shortstat > logs.txt
      

      Python 爱好者:

      with open(r".\logs.txt", "r", encoding="utf8") as f:
          files = insertions = deletions = 0
          for line in f:
              if ' changed' in line:
                  line = line.strip()
                  spl = line.split(', ')
                  if len(spl) > 0:
                      files += int(spl[0].split(' ')[0])
                  if len(spl) > 1:
                      insertions += int(spl[1].split(' ')[0])
                  if len(spl) > 2:
                      deletions += int(spl[2].split(' ')[0])
      
          print(str(files).ljust(10) + ' files changed')
          print(str(insertions).ljust(10) + ' insertions')
          print(str(deletions).ljust(10) + ' deletions')
      

      你的输出是这样的:

      225        files changed
      6751       insertions
      1379       deletions
      

      【讨论】:

        【解决方案19】:

        对于 Windows 用户,您可以使用以下批处理脚本来计算指定作者的添加/删除行数

        @echo off
        
        set added=0
        set removed=0
        
        for /f "tokens=1-3 delims= " %%A in ('git log --pretty^=tformat: --numstat --author^=%1') do call :Count %%A %%B %%C
        
        @echo added=%added%
        @echo removed=%removed%
        goto :eof
        
        :Count
          if NOT "%1" == "-" set /a added=%added% + %1
          if NOT "%2" == "-" set /a removed=%removed% + %2
        goto :eof
        

        https://gist.github.com/zVolodymyr/62e78a744d99d414d56646a5e8a1ff4f

        【讨论】:

          【解决方案20】:

          该问题要求提供有关特定作者的信息,但许多答案是根据更改的代码行返回作者排名列表的解决方案。

          这是我一直在寻找的,但现有的解决方案并不十分完美。为了方便可能通过 Google 找到此问题的人们,我对它们进行了一些改进,并将它们制成了一个 shell 脚本,如下所示。

          对于 Perl 或 Ruby,没有依赖。此外,行更改计数中还考虑了空格、重命名和行移动。只需将其放入文件中并将您的 Git 存储库作为第一个参数传递即可。

          #!/bin/bash
          git --git-dir="$1/.git" log > /dev/null 2> /dev/null
          if [ $? -eq 128 ]
          then
              echo "Not a git repository!"
              exit 128
          else
              echo -e "Lines  | Name\nChanged|"
              git --work-tree="$1" --git-dir="$1/.git" ls-files -z |\
              xargs -0n1 git --work-tree="$1" --git-dir="$1/.git" blame -C -M  -w |\
              cut -d'(' -f2 |\
              cut -d2 -f1 |\
              sed -e "s/ \{1,\}$//" |\
              sort |\
              uniq -c |\
              sort -nr
          fi
          

          【讨论】:

            【解决方案21】:

            这里的脚本可以做到。将其放入 authorship.sh,chmod +x 即可,一切就绪。

            #!/bin/sh
            declare -A map
            while read line; do
                if grep "^[a-zA-Z]" <<< "$line" > /dev/null; then
                    current="$line"
                    if [ -z "${map[$current]}" ]; then 
                        map[$current]=0
                    fi
                elif grep "^[0-9]" <<<"$line" >/dev/null; then
                    for i in $(cut -f 1,2 <<< "$line"); do
                        map[$current]=$((map[$current] + $i))
                    done
                fi
            done <<< "$(git log --numstat --pretty="%aN")"
            
            for i in "${!map[@]}"; do
                echo -e "$i:${map[$i]}"
            done | sort -nr -t ":" -k 2 | column -t -s ":"
            

            【讨论】:

            • 不,它不会!,你在别处发布了这个,它会在 mac 和 linux 上产生错误,你知道,git 的计算机类型!
            【解决方案22】:

            目前我认为最好的工具是 gitinspector。它为每个用户,每周等提供设置报告 你可以像下面这样使用 npm 安装

            npm install -g gitinspector

            获取更多详细信息的链接

            https://www.npmjs.com/package/gitinspector

            https://github.com/ejwa/gitinspector/wiki/Documentation

            https://github.com/ejwa/gitinspector

            示例命令是

            gitinspector -lmrTw 
            gitinspector --since=1-1-2017 etc
            

            【讨论】:

              【解决方案23】:

              我编写了这个 Perl 脚本来完成这项任务。

              #!/usr/bin/env perl
              
              use strict;
              use warnings;
              
              # save the args to pass to the git log command
              my $ARGS = join(' ', @ARGV);
              
              #get the repo slug
              my $NAME = _get_repo_slug();
              
              #get list of authors
              my @authors = _get_authors();
              my ($projectFiles, $projectInsertions, $projectDeletions) = (0,0,0);
              #for each author
              foreach my $author (@authors) {
                my $command = qq{git log $ARGS --author="$author" --oneline --shortstat --no-merges};
                my ($files, $insertions, $deletions) = (0,0,0);
                my @lines = `$command`;
                foreach my $line (@lines) {
                  if ($line =~ m/^\s(\d+)\s\w+\s\w+,\s(\d+)\s\w+\([\+|\-]\),\s(\d+)\s\w+\([\+|\-]\)$|^\s(\d+)\s\w+\s\w+,\s(\d+)\s\w+\(([\+|\-])\)$/) {
                    my $lineFiles = $1 ? $1 : $4;
                    my $lineInsertions = (defined $6 && $6 eq '+') ? $5 : (defined $2) ? $2 : 0;
                    my $lineDeletions = (defined $6 && $6 eq '-') ? $5 : (defined $3) ? $3 : 0;
                    $files += $lineFiles;
                    $insertions += $lineInsertions;
                    $deletions += $lineDeletions;
                    $projectFiles += $lineFiles;
                    $projectInsertions += $lineInsertions;
                    $projectDeletions += $lineDeletions;
                  }
                }
                if ($files || $insertions || $deletions) {
                  printf(
                    "%s,%s,%s,+%s,-%s,%s\n",
                    $NAME,
                    $author,
                    $files,
                    $insertions,
                    $deletions,
                    $insertions - $deletions
                  );
                }
              }
              
              printf(
                "%s,%s,%s,+%s,-%s,%s\n",
                $NAME,
                'PROJECT_TOTAL',
                $projectFiles,
                $projectInsertions,
                $projectDeletions,
                $projectInsertions - $projectDeletions
              );
              
              exit 0;
              
              #get the remote.origin.url joins that last two pieces (project and repo folder)
              #and removes any .git from the results. 
              sub _get_repo_slug {
                my $get_remote_url = "git config --get remote.origin.url";
                my $remote_url = `$get_remote_url`;
                chomp $remote_url;
              
                my @parts = split('/', $remote_url);
              
                my $slug = join('-', @parts[-2..-1]);
                $slug =~ s/\.git//;
              
                return $slug;
              }
              
              sub _get_authors {
                my $git_authors = 'git shortlog -s | cut -c8-';
                my @authors = `$git_authors`;
                chomp @authors;
              
                return @authors;
              }
              

              我将其命名为git-line-changes-by-author 并放入/usr/local/bin。因为它保存在我的路径中,所以我可以发出命令git line-changes-by-author --before 2018-12-31 --after 2020-01-01 来获取 2019 年的报告。举个例子。如果我拼错了名字 git 会提示正确的拼写。

              您可能想要调整 _get_repo_slug 子以仅包含 remote.origin.url 的最后一部分,因为我的存储库保存为 project/repo 而您可能不会。

              【讨论】:

                【解决方案24】:

                你想要Git blame

                有一个 --show-stats 选项可以打印一些统计数据。

                【讨论】:

                • 我试过 blame,但它并没有真正提供我认为 OP 需要的统计数据?
                猜你喜欢
                • 2017-07-02
                • 2019-05-06
                • 2014-11-20
                • 2018-03-19
                • 2011-06-16
                • 1970-01-01
                • 2021-06-17
                • 2013-10-21
                • 2012-08-12
                相关资源
                最近更新 更多