代码之家  ›  专栏  ›  技术社区  ›  Gav

如何计算Git存储库中特定作者更改的总行数?

  •  382
  • Gav  · 技术社区  · 16 年前

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

    21 回复  |  直到 6 年前
        1
  •  268
  •   CB Bailey    16 年前

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

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

    这将为当前头部的所有提交提供统计信息。如果要在其他分支中添加统计信息,则必须将它们作为参数提供给 git log .

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

    git log --author="<authorname>" --pretty=tformat: --numstat
    
        2
  •  521
  •   velop    8 年前

    这提供了一些关于作者的统计信息,并根据需要进行修改。

    使用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 }' -
    

    编辑(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
    

    enter image description here

        3
  •  167
  •   Dan    11 年前

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

    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";}'
    

    (花几分钟时间完成我们的回购,回购承诺约为10-15K。)

        4
  •  126
  •   Ciro Santilli OurBigBook.com    7 年前

    吉特名气 https://github.com/oleander/git-fame-rb

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

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

    还有python版本 https://github.com/casperdcl/git-fame (由@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数据,还不确定这是否可以改进。

        5
  •  100
  •   nickf    14 年前

    我发现以下内容对于查看谁拥有代码库中当前最多的行很有用:

    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-blank(-c-m)添加一些选项,以获得考虑文件移动和文件之间的行移动的更好数字,但如果这样做,该命令可能会运行更长时间。

    此外,如果您要查找在所有提交中为所有提交者更改的行,那么下面的小脚本很有用:

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

        6
  •  83
  •   Jakub Narębski adamtaub    11 年前

    计数 提交 由给定分支上的给定作者(或所有作者)使用 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
    
        7
  •  67
  •   Jared Burrows    11 年前

    看了之后 亚历克斯 GETY3000 我的回答是,我试图缩短一行:

    基本上,使用git log numstat和 跟踪 文件夹 改变。

    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
  •  23
  •   Erik Živković    6 年前

    这个 Answer AaronM 使用shell一行程序是很好的,但实际上还有另一个bug,如果用户名和日期之间有不同数量的空格,空格将损坏用户名。损坏的用户名将为用户计数提供多行,您必须自己对它们进行汇总。

    这个小小的变化解决了我的问题:

    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中工作。
        9
  •  21
  •   Community CDub    8 年前

    这里有一个简短的一行程序,可以为所有作者生成统计信息。比上面丹的方法快得多 https://stackoverflow.com/a/20414465/1102119 (我的时间复杂度是O(n),而不是O(n m),其中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
    
        10
  •  20
  •   Stéphane Gourichon    9 年前

    @mmrobins@aaronm@erikz@jamesmishra提供的变体都有一个共同的问题:它们要求git生成一个不用于脚本使用的混合信息,包括来自同一行存储库的行内容,然后将这些信息与regexp匹配。

    当某些行不是有效的UTF-8文本时,以及当某些行恰好与regexp匹配时(此处发生这种情况),这是一个问题。

    这里有一条修改过的线,没有这些问题。它要求Git在单独的行上干净地输出数据,这使得我们可以很容易地过滤我们想要的内容:

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

    您可以对其他字符串进行grep,如author mail、committer等。

    也许是第一件事 export LC_ALL=C (假设 bash )为了强制字节级处理(这也会极大地加速grep从基于utf-8的区域设置的速度)。

        11
  •  16
  •   AaronM    13 年前

    中间有一个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
    
        12
  •  14
  •   Community CDub    8 年前

    除了 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(-)
    

    在我看来,后者给出了一个更现实的观点,即一个人对项目有多大的影响,因为重命名文件比从头开始写文件要小得多。

        13
  •  11
  •   Nevir    13 年前

    下面是一个快速的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
    
        14
  •  6
  •   victor.cheval    7 年前

    你可以用谁( 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>
    =====================================================
    
        15
  •  5
  •   AaronM    10 年前

    我在上面提供了一个简短的答案的修改,但它不足以满足我的需要。我需要能够在最终代码中对提交的行和行进行分类。我还想按档案分类。这段代码不会重复出现,它只返回一个目录的结果,但是如果有人想更进一步,这是一个很好的开始。复制并粘贴到一个文件中,使其成为可执行文件或使用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};
    }
    
        16
  •  5
  •   edrich13    8 年前

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

    git shortlog -s -n
    
        17
  •  1
  •   ZX2C4    13 年前

    这里的这个脚本可以做到。把它放进authority.sh,chmod+x it,你就一切就绪了。

    #!/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 ":"
    
        18
  •  1
  •   Amen Ayach    7 年前

    使用以下方法将日志保存到文件中:

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

    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
  •  0
  •   gbjbaanb    16 年前

    你想要 Git blame .

    有一个--show stats选项可以打印一些,嗯,stats。

        20
  •  0
  •   James Mishra    12 年前

    这个问题要求提供关于 具体的 作者,但许多答案都是基于他们的代码行更改返回排名的作者列表的解决方案。

    这正是我所寻找的,但现有的解决方案并不十分完美。为了让那些可能通过谷歌找到这个问题的人感兴趣,我对他们做了一些改进,并将他们制作成一个shell脚本,我将在下面显示。一个注释的(我将继续维护)可以是 found on my Github .

    依赖于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
  •  0
  •   Ravikiran Reddy Kotapati    8 年前

    到目前为止,我认为最好的工具是gitinspector。它为每个用户、每周等提供设置报告。 你可以用NPM安装如下

    NPM安装-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