Как подсчитать общее количество строк, измененных конкретным автором в репозитории Git?

есть ли команда, которую я могу вызвать, которая будет считать строки, измененные конкретным автором в репозитории Git? Я знаю, что должны быть способы подсчета количества коммитов, поскольку Github делает это для их графика воздействия.

21 ответов


вывод следующей команды должен быть достаточно простым для отправки скрипту, чтобы сложить итоги:

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

это дает статистику для всех коммитов на текущей голове. Если вы хотите добавить статистику в другие ветви, вам придется предоставить их в качестве аргументов для git log.

для перехода к скрипту удаление даже формата "oneline" может быть сделано с пустым форматом журнала, и, как прокомментировал Якуб Нарембский,--numstat является еще одной альтернативой. Он генерирует для каждого файла, а не для каждой строки статистики, но еще проще анализировать.

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

это дает некоторую статистику об авторе, изменить по мере необходимости.

Использование Gawk:

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

использование Awk на Mac OSX:

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

изменить (2017)

на github есть новый пакет, который выглядит гладким и использует bash в качестве зависимостей (протестирован на linux). Он больше подходит для прямого использования, а не для скриптов.

Это git-quick-stats (ссылка github).

скопировать 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


в случае, если кто-то хочет посмотреть статистика каждый пользователь в своей кодовой базе, пара моих коллег недавно придумали этот ужасный однострочный:

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

(требуется несколько минут, чтобы пройти через наше РЕПО, которое имеет около 10-15k коммитов.)


ГИТ славы 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    |
+------------------------+--------+---------+-------+--------------------+

но будьте осторожны: как упоминал Джаред в комментарии, делая это на очень большом хранилище займет несколько часов. Не уверен, что это можно улучшить, хотя, учитывая, что он должен обрабатывать так много данных Git.


Я нашел следующее полезным, чтобы увидеть, у кого было больше строк, которые в настоящее время находятся в базе кода:

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

другие ответы в основном сосредоточены на строках, измененных в коммитах, но если коммиты не выживают и перезаписываются, они могут быть просто сбиты. Вышеуказанное заклинание также позволяет вам сортировать всех коммиттеров по строкам, а не только по одному. Вы можете добавить некоторые опции в git blame (- C-M), чтобы получить лучшие номера, которые принимают движение файла и линию перемещение между файлами в учет, но команда может работать намного дольше, если вы это сделаете.

кроме того, если вы ищете строки, измененные во всех коммитах для всех коммиттеров, следующий маленький скрипт полезен:

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


подсчитать количество commits данным автором (или всеми авторами) в данной ветви вы можете использовать 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

после Алекс и Gerty3000ответ, я попытался сократить однострочный:

в основном, используя git log numstat и не отслеживание количества файлы изменен.

git версии 2.1.0 на Mac OSX:

git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += ; subs += ; loc +=  -  } 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

на ответ С AaronM использование оболочки one-liner хорошо, но на самом деле есть еще одна ошибка, где пробелы будут повреждать имена пользователей, если между именем пользователя и датой существует разное количество пробелов. Поврежденные имена пользователей будут давать несколько строк для подсчета пользователей, и вы должны суммировать их самостоятельно.

Это небольшое изменение исправило проблему для меня:

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

обратите внимание на + after \s, который будет потреблять все пробелы от имени до даты.

фактически добавив этот ответ столько же для моей собственной памяти, сколько для помощи кому-либо еще, так как это, по крайней мере, второй раз, когда я google тема :)


@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 для других строк, таких как author-mail, committer и т. д.

может, сначала сделать export LC_ALL=C (если bash) для принудительной обработки на уровне байтов (это также значительно ускоряет grep из локалей на основе UTF-8).


вот краткий сценарий, который создает статистику для всех авторов. Это намного быстрее, чем решение Дэна выше в https://stackoverflow.com/a/20414465/1102119 (mine имеет временную сложность O(N) вместо O (NM), где N-количество коммитов, а M-количество авторов).

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

решение было дано с ruby в середине, perl был немного более доступен по умолчанию, вот альтернатива, использующая perl для текущих строк автором.

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

кроме ответ Чарльза Бейли, вы, возможно, захотите, чтобы добавить -C параметр для команд. В противном случае переименования файлов считаются множеством добавлений и удалений (столько, сколько в файле строк), даже если содержимое файла не было изменено.

чтобы проиллюстрировать, вот a commit С большим количеством файлов, перемещаемых из одного из моих проектов, при использовании :

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

на мой взгляд, последнее дает более реалистичное представление о том, какое влияние человек оказал на проект, потому что переименование файла-гораздо меньшая операция, чем запись файла с нуля.


вот быстрый скрипт ruby, который загоняет влияние на пользователя против данного запроса журнала.

например,рубиниус:

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

Я предоставил модификацию короткого ответа выше, но этого было недостаточно для моих нужд. Мне нужно было иметь возможность классифицировать как зафиксированные строки, так и строки в окончательном коде. Я также хотел разбить по файлам. Этот код не возвращается, он будет возвращать только результаты для одного каталога, но это хорошее начало, если кто-то хотел идти дальше. Скопируйте и вставьте в файл и сделайте исполняемый файл или запустите его с помощью 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();
        }
        elsif ( $log_line =~ m{^(\d+)\s+(\d+)\s+(.*)} ) {
            my $added = ;
            my $removed = ;
            my $file = ;
            $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 = ;
            $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};
}

Это лучший способ, и он также дает вам четкое представление об общем количестве коммитов всеми пользователями

git shortlog -s -n

вы можете использовать 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>
=====================================================

этот скрипт здесь будет делать. Положите его в 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 ":"

сохранить логи в файл через:

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

вы хотите ГИТ вину.

есть опция --show-stats для печати некоторых, ну, статистики.


вопрос, заданный для получения информации о конкретные автор, но многие из ответов были решениями, которые возвращали ранжированные списки авторов на основе их измененных строк кода.

Это было то, что я искал, но существующие решения были не совсем идеальны. В интересах людей, которые могут найти этот вопрос через Google, я сделал некоторые улучшения на них и сделал их в shell-скрипт, который я показываю ниже. Аннотированный (который я продолжу поддерживать) можно найдено на моем Github.

здесь нет зависимости от Perl или Ruby. Кроме того, при подсчете изменений строк учитываются пробелы, переименования и перемещения строк. Просто поместите это в файл и передать свой репозиторий Git в качестве первого параметра.

#!/bin/bash
git --git-dir="/.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="" --git-dir="/.git" ls-files -z |\
    xargs -0n1 git --work-tree="" --git-dir="/.git" blame -C -M  -w |\
    cut -d'(' -f2 |\
    cut -d2 -f1 |\
    sed -e "s/ \{1,\}$//" |\
    sort |\
    uniq -c |\
    sort -nr
fi

лучшим инструментом до сих пор я identfied является gitinspector. Он дает отчет о комплекта в потребителя, в неделю etc Вы можете установить, как показано ниже, с 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