Git Blame Коммит Статистика

199

Как я могу «оскорбить» вину (или какую-то более подходящую функцию и / или в сочетании с командами оболочки), чтобы дать мне статистику того, сколько строк (кода) в настоящее время находится в репозитории, исходящем от каждого коммиттера?

Пример вывода:

Committer 1: 8046 Lines
Committer 2: 4378 Lines
Эрик Айгнер
источник
11
Для этого действительно должна быть встроенная команда ... есть команды для гораздо менее распространенных случаев использования.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
@CiroSantilli, но легко добавить сценарий, который можно вызвать из git.
Алекс
Возможный дубликат Как подсчитать общее количество строк, измененных конкретным автором в репозитории Git? потому что это может быть легко сведено к этому: просто
обведите
1
это довольно круто code.google.com/p/gitinspector, особенно если вы оцениваете задания по группам студентов (большие проекты не нужно применять ... это медленно, потому что винит каждый отдельный файл)
смотри

Ответы:

166

Обновить

git ls-tree -r -z --name-only HEAD -- */*.c | xargs -0 -n1 git blame \
--line-porcelain HEAD |grep  "^author "|sort|uniq -c|sort -nr

Я обновил некоторые вещи по пути.

Для удобства вы также можете поместить это в свою собственную команду:

#!/bin/bash

# save as i.e.: git-authors and set the executable flag
git ls-tree -r -z --name-only HEAD -- $1 | xargs -0 -n1 git blame \
 --line-porcelain HEAD |grep  "^author "|sort|uniq -c|sort -nr

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

  • git authors '*/*.c' # look for all files recursively ending in .c
  • git authors '*/*.[ch]' # look for all files recursively ending in .c or .h
  • git authors 'Makefile' # just count lines of authors in the Makefile

Оригинальный ответ

Пока принятый ответ делает работу, он очень медленный.

$ git ls-tree --name-only -z -r HEAD|egrep -z -Z -E '\.(cc|h|cpp|hpp|c|txt)$' \
  |xargs -0 -n1 git blame --line-porcelain|grep "^author "|sort|uniq -c|sort -nr

почти мгновенно.

Чтобы получить список отслеживаемых файлов, вы можете использовать

git ls-tree --name-only -r HEAD

Это решение позволяет избежать вызовов fileдля определения типа файла и использует grep для сопоставления с желаемым расширением по соображениям производительности. Если все файлы должны быть включены, просто удалите это из строки.

grep -E '\.(cc|h|cpp|hpp|c)$' # for C/C++ files
grep -E '\.py$'               # for Python files

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

git ls-tree -z --name-only -r HEAD | egrep -Z -z '\.py'|xargs -0 ... # passes newlines as '\0'

Дайте список файлов (через канал), можно использовать xargs для вызова команды и распределения аргументов. Команды, позволяющие обрабатывать несколько файлов, не допускают -n1. В этом случае мы вызываем git blame --line-porcelainи для каждого вызова мы используем ровно 1 аргумент.

xargs -n1 git blame --line-porcelain

Затем мы фильтруем выходные данные по появлению «автора», сортируем список и подсчитываем повторяющиеся строки по:

grep "^author "|sort|uniq -c|sort -nr

Заметка

Другие ответы фактически отфильтровывают строки, которые содержат только пробелы.

grep -Pzo "author [^\n]*\n([^\n]*\n){10}[\w]*[^\w]"|grep "author "

Команда выше будет печатать авторов строк, содержащих хотя бы один непробельный символ. Вы также можете использовать совпадение, \w*[^\w#]которое также исключит строки, в которых первый непробельный символ отсутствует #(комментарий на многих языках сценариев).

Alex
источник
2
@nilbus: ты не можешь. echo "a\nb\nc"|xargs -n1 cmdбудет расширяться доcmd a; cmd b; cmd d
Алекс
2
--line-фарфор больше не работает (git 1.7.5.4), вместо этого используйте --porcelain
isoiphone
4
Пользователи OSX, попробуйте следующее (все еще не работает с файлами с символами новой строки в их имени):git ls-tree --name-only -r HEAD | grep -E '\.(cc|h|m|hpp|c)$' | xargs -n1 git blame --line-porcelain | grep "^author "|sort|uniq -c|sort -nr
Wayne
3
Если вы хотите, чтобы все находилось под текущим путем, на любой глубине, используйте «./» в качестве фильтра пути (там, где ответчик поставил « / .c»).
Бен Дилтс
2
Возможно, используйте «винить -w», чтобы лучше владеть кодом, когда код был переформатирован только stackoverflow.com/questions/4112410/…
sleeplessnerd
124

Я написал гем под названием git-fame, который может быть полезен.

Установка и использование:

  1. $ gem install git_fame
  2. $ cd /path/to/gitdir
  3. $ git fame

Вывод:

Statistics based on master
Active files: 21
Active lines: 967
Total commits: 109

Note: Files matching MIME type image, binary has been ignored

+----------------+-----+---------+-------+---------------------+
| name           | loc | commits | files | distribution (%)    |
+----------------+-----+---------+-------+---------------------+
| Linus Oleander | 914 | 106     | 21    | 94.5 / 97.2 / 100.0 |
| f1yegor        | 47  | 2       | 7     |  4.9 /  1.8 / 33.3  |
| David Selassie | 6   | 1       | 2     |  0.6 /  0.9 /  9.5  |
+----------------+-----+---------+-------+---------------------+
Линус Олеандр
источник
5
+1 наконец 1, который работает и выглядит так, как будто он дает разумные числа, остальные из командной строки либо не работают на OSX из-за несовместимости утилит, либо дают крошечные числа в моем репо. Это на OSX и ruby ​​1.9.3 (варево)
Karthik T
9
Не будь глупым, @tcaswell. Это не спам, чтобы указывать на что-то полезное, даже если вы оказались тем, кто написал это что-то.
Уэйн
5
Отвечая на мой собственный вопрос: git fame --exclude = paths / to / files, paths / to / other / files
Maciej Swic
2
@ Адам: У вас все еще есть проблемы с этим? У меня очень хорошо работает на OS X 10.9.5.
Сэм Даттон
2
Для любого репо, превышающего несколько, требуется время, необходимое для этого драгоценного камня, его работа астрономическая
Эрик Эйгнер
48
git ls-tree -r HEAD|sed -re 's/^.{53}//'|while read filename; do file "$filename"; done|grep -E ': .*text'|sed -r -e 's/: .*//'|while read filename; do git blame -w "$filename"; done|sed -r -e 's/.*\((.*)[0-9]{4}-[0-9]{2}-[0-9]{2} .*/\1/' -e 's/ +$//'|sort|uniq -c

Пошаговое объяснение:

Перечислите все файлы под контролем версий

git ls-tree -r HEAD|sed -re 's/^.{53}//'

Сократить список до только текстовых файлов

|while read filename; do file "$filename"; done|grep -E ': .*text'|sed -r -e 's/: .*//'

Git обвиняет все текстовые файлы, игнорируя изменения пробелов

|while read filename; do git blame -w "$filename"; done

Вытащите имена авторов

|sed -r -e 's/.*\((.*)[0-9]{4}-[0-9]{2}-[0-9]{2} .*/\1/' -e 's/ +$//'

Сортировать список авторов и подсчитать количество последовательно повторяющихся строк.

|sort|uniq -c

Пример вывода:

   1334 Maneater
   1924 Another guy
  37195 Brian Ruby
   1482 Anna Lambda
оборота нильбус
источник
1
Кажется, у меня есть другая sedверсия, моя не понимает -rфлаг и имеет проблемы с регулярным выражением (жалуется на несбалансированные парены, даже когда я удаляю излишки ().
Эрик Эйгнер
7
Неважно, sudo brew install gnu-sedрешил это. Работает как шарм!
Эрик Эйгнер
5
Или port install gsedдля пользователей MacPorts.
Гэвин Брок
Я сделал sudo brew install gnu-sed(который работал), но я все еще получаю ошибки, которые sed не распознает -r. :(
Адам Татл
1
На OSX после установки gsed через macports я запустил эту команду, чтобы она заработала (заменил sed на gsed):git ls-tree -r HEAD|gsed -re 's/^.{53}//'|while read filename; do file "$filename"; done|grep -E ': .*text'|gsed -r -e 's/: .*//'|while read filename; do git blame -w "$filename"; done|gsed -r -e 's/.*\((.*)[0-9]{4}-[0-9]{2}-[0-9]{2} .*/\1/' -e 's/ +$//'|sort|uniq -c
nerdherd
38

git summaryобеспечивается GIT-статисты пакет именно то , что вам нужно. Ознакомьтесь с документацией в git-extras - git-summary :

git summary --line

Дает вывод, который выглядит следующим образом:

project  : TestProject
lines    : 13397
authors  :
8927 John Doe            66.6%
4447 Jane Smith          33.2%
  23 Not Committed Yet   0.2%
adius
источник
1
Хорошо, но, кажется, не поддерживает фильтр пути или, по крайней мере, аргумент подкаталога. Было бы лучше.
Спинкус
1
Хорошее и чистое решение. @ Ответ Алекса по какой-то причине дал очень маленькое число строк. Это просто сработало из коробки. Потребовалось около 30 секунд для ~ 200 тыс. Строк, распределенных по нескольким сотням файлов.
fgblomqvist
6

Решение Эрика было потрясающим, но у меня были некоторые проблемы с диакритическими знаками (несмотря на то, что мои LC_*переменные окружения были якобы правильно установлены) и утечкой шума в строках кода, в которых действительно были даты. Мой sed-fu беден, поэтому я получил этот фрагмент Франкенштейна с рубином, но он работает для меня безупречно на 200 000+ LOC и сортирует результаты:

git ls-tree -r HEAD | gsed -re 's/^.{53}//' | \
while read filename; do file "$filename"; done | \
grep -E ': .*text' | gsed -r -e 's/: .*//' | \
while read filename; do git blame "$filename"; done | \
ruby -ne 'puts $1.strip if $_ =~ /^\w{8} \((.*?)\s*\d{4}-\d{2}-\d{2}/' | \
sort | uniq -c | sort -rg

Кроме того, обратите внимание , gsedвместо того , sedпотому что бинарные доморощенные Установки, оставляя систему СЭДА нетронутой.

GTD
источник
4

git shortlog -sn

Это покажет список коммитов на автора.

moinudin
источник
17
Это возвращает количество коммитов на автора, а не количество строк.
v64
Очень помогает в определении основных участников проекта / каталога / файла
Ares
4

Вот основной фрагмент ответа @Alex, который фактически выполняет операцию агрегации линий обвинения. Я сократил его, чтобы работать с одним файлом, а не с набором файлов.

git blame --line-porcelain path/to/file.txt | grep  "^author " | sort | uniq -c | sort -nr

Я публикую это здесь, потому что я часто возвращаюсь к этому ответу, перечитываю пост и перевариваю примеры, чтобы извлечь часть, которую я оцениваю, которая облагается налогом. И при этом это не достаточно универсально для моего случая использования; его объем для всего проекта C.


Мне нравится перечислять статистику по каждому файлу, полученную с помощью forитератора bash, а не так, xargsкак я нахожу xargs менее читабельными и сложными в использовании / запоминании. Преимущества / недостатки xargs vs для должны обсуждаться в другом месте.

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

for file in $(git ls-files); do \
    echo $file; \
    git blame --line-porcelain $file \
        | grep  "^author " | sort | uniq -c | sort -nr; \
    echo; \
done

И я проверил, что запуск этого режима в оболочке bash безопасен ctrl + c, если вам нужно поместить его в сценарий bash, вам может понадобиться перехватывать SIGINT и SIGTERM, если вы хотите, чтобы пользователь мог разорвать цикл for.

ThorSummoner
источник
1
git blame -w -M -C -C --line-porcelain path/to/file.txt | grep -I '^author ' | sort | uniq -ic | sort -nrНайден небольшая подстройка к git blame здесь , что более точно изображает статистику я искал. В частности, опции -M и -C -C (это два C по назначению). -M обнаруживает перемещения внутри файла, а -C -C обнаруживает скопированные строки из других файлов. Смотрите документ здесь . Для полноты, -w игнорирует пробелы.
Джон Ли
1

У меня есть это решение, которое подсчитывает обвиняемые строки во всех текстовых файлах (исключая двоичные файлы, даже версионные):

IFS=$'\n'
for file in $(git ls-files); do
    git blame `git symbolic-ref --short HEAD` --line-porcelain "$file" | \
        grep  "^author " | \
        grep -v "Binary file (standard input) matches" | \
        grep -v "Not Committed Yet" | \
        cut -d " " -f 2-
    done | \
        sort | \
        uniq -c | \
        sort -nr
Габриэль Диего
источник
1

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

find . -name '*.c' | xargs -n1 git blame --line-porcelain | grep "^author "|sort|uniq -c|sort -nr
Мартин Г
источник
0

Я принял главный ответ на Powershell:

(git ls-tree -rz --name-only HEAD).Split(0x00) | where {$_ -Match '.*\.py'} |%{git blame -w --line-porcelain HEAD $_} | Select-String -Pattern '^author ' | Group-Object | Select-Object -Property Count, Name | Sort-Object -Property Count -Descending

Необязательно, работаете ли вы git blameс -wкоммутатором, я добавил его, потому что он игнорирует изменения пробелов.

Производительность на моей машине была в пользу Powershell (~ 50 с против ~ 65 с для того же репо), хотя решение Bash работало под WSL2

Мэтт М.
источник
-1

Сделал свой собственный скрипт, который представляет собой комбинацию @nilbus и @Alex

#!/bin/sh

for f in $(git ls-tree -r  --name-only HEAD --);
do
    j=$(file "$f" | grep -E ': .*text'| sed -r -e 's/: .*//');
    if [ "$f" != "$j" ]; then
        continue;
    fi
    git blame -w --line-porcelain HEAD "$f" | grep  "^author " | sed 's/author //'`enter code here`
done | sort | uniq -c | sort -nr
vossman77
источник
Для меня твоя вещь enter code hereвызывала проблемы .... это работает правильно?
Menios
-1

Функция Bash, предназначенная для одного исходного файла, запускается в MacOS.

function glac {
    # git_line_author_counts
    git blame -w "$1" |  sed -E "s/.*\((.*) +[0-9]{4}-[0-9]{2}.*/\1/g" | sort | uniq -c | sort -nr
}
jxramos
источник