Estatísticas de confirmação de culpa do Git

198

Como posso "abusar" da culpa (ou de alguma função mais adequada e / ou em conjunto com os comandos do shell) para me fornecer uma estatística de quantas linhas (de código) estão atualmente no repositório originárias de cada committer?

Saída de exemplo:

Committer 1: 8046 Lines
Committer 2: 4378 Lines
Erik Aigner
fonte
11
Realmente deve haver um comando interno para isso ... existem comandos para casos de uso muito menos comuns.
Ciro Santilli escreveu:
@CiroSantilli, mas é fácil adicionar um shellscript invocável do git.
11743 Alex
possível duplicação de Como contar o total de linhas alteradas por um autor específico em um repositório Git? porque pode ser facilmente reduzido a esse: basta passar por todos os autores
Ciro Santilli
1
isso é bastante incrível code.google.com/p/gitinspector especialmente se você está atribuindo notas por equipes de estudantes (grandes projectos não aplicar ... é lento porque responsabiliza cada arquivo individual)
sehe

Respostas:

166

Atualizar

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

Eu atualizei algumas coisas no caminho.

Por conveniência, você também pode colocar isso em seu próprio comando:

#!/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

armazene-o em algum lugar do seu caminho ou modifique-o e use-o como

  • 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

Resposta original

Enquanto a resposta aceita faz o trabalho, é muito lenta.

$ 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

é quase instantâneo.

Para obter uma lista dos arquivos rastreados atualmente, você pode usar

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

Essa solução evita chamadas filepara determinar o tipo de arquivo e usa grep para corresponder à extensão desejada por motivos de desempenho. Se todos os arquivos forem incluídos, remova-o da linha.

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

se os arquivos puderem conter espaços, o que é ruim para os shells, você pode usar:

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

Dê uma lista de arquivos (através de um canal) que se pode usar xargs para chamar um comando e distribuir os argumentos. Os comandos que permitem o processamento de vários arquivos obedecem ao -n1. Nesse caso, chamamos git blame --line-porcelaine para cada chamada usamos exatamente 1 argumento.

xargs -n1 git blame --line-porcelain

Em seguida, filtramos a saída para ocorrências de "autor", classificamos a lista e contamos linhas duplicadas por:

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

Nota

Outras respostas, na verdade, filtram linhas que contêm apenas espaços em branco.

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

O comando acima imprimirá autores de linhas que contenham pelo menos um caractere que não seja um espaço em branco. Você também pode usar a correspondência, \w*[^\w#]que também excluirá linhas nas quais o primeiro caractere que não seja um espaço em branco não seja #(comentário em muitas linguagens de script).

Alex
fonte
2
@ nilbus: você não pode. echo "a\nb\nc"|xargs -n1 cmdserá expandido paracmd a; cmd b; cmd d
Alex
2
--line-porcelana não parece mais trabalhar (git 1.7.5.4) em vez usar --porcelain
isoiphone
4
Usuários OSX, tente o seguinte (ainda não funciona em arquivos com novas linhas em seu nome):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
Se você quiser apenas tudo sob o caminho atual, a qualquer profundidade, use "./" como o filtro do caminho (onde o atendedor colocou " / .c").
precisa
2
Talvez use "-w culpa" para obter uma melhor propriedade do código quando o código só foi reformatado stackoverflow.com/questions/4112410/...
sleeplessnerd
124

Eu escrevi uma jóia chamada git-fame que pode ser útil.

Instalação e uso:

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

Resultado:

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  |
+----------------+-----+---------+-------+---------------------+
Linus Oleander
fonte
5
+1 finalmente 1 que funciona e parece que fornece números sensíveis, o restante dos da linha de comando não funciona no OSX devido à incompatibilidade de utilitários ou fornece números pequenininhos no meu repositório. Isso é no OSX e no ruby ​​1.9.3 (brew)
Karthik T 30/10
9
Não seja bobo, @tcaswell. Não é spam apontar para algo útil, mesmo que você tenha escrito isso.
19414 Wayne
5
Respondendo a minha própria pergunta: fama --exclude = caminhos / a / arquivos, caminhos / a / outros arquivos / git
Maciej Swic
2
@ Adam: Você ainda está tendo problemas com isso? Funciona muito bem para mim no OS X 10.9.5.
Sam Dutton
2
Para quaisquer isso é compromissadas maiores do que alguns commits o tempo esta jóia precisa fazer seu trabalho é astronômico
Erik Aigner
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

Explicação passo a passo:

Listar todos os arquivos sob controle de versão

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

Limpe a lista para baixo apenas para arquivos de texto

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

O Git culpa todos os arquivos de texto, ignorando as alterações de espaço em branco

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

Retire os nomes dos autores

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

Classifique a lista de autores e faça com que uniq conte o número de linhas repetidas consecutivamente

|sort|uniq -c

Exemplo de saída:

   1334 Maneater
   1924 Another guy
  37195 Brian Ruby
   1482 Anna Lambda
nilbus
fonte
1
Parece que tenho uma sedversão diferente , a minha não entende a -rbandeira e tem problemas com o regex (reclama de parênteses desequilibrados, mesmo quando removo o excedente ().
Erik Aigner
7
Deixa sudo brew install gnu-sedpra lá , resolveu. Funciona como um encanto!
precisa saber é o seguinte
5
Ou port install gsedpara usuários do MacPorts.
Gavin Brock
Eu fiz um sudo brew install gnu-sed(que funcionou), mas ainda recebo erros que o sed não reconhece -r. :(
Adam Tuttle
1
No OSX após a instalação do gsed via macports, executei este comando para fazê-lo funcionar (substituído sed pelo 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 15/02/16
38

git summaryfornecido pelo pacote git-extras é exatamente o que você precisa. Confira a documentação em git-extras - git-summary :

git summary --line

Fornece uma saída parecida com esta:

project  : TestProject
lines    : 13397
authors  :
8927 John Doe            66.6%
4447 Jane Smith          33.2%
  23 Not Committed Yet   0.2%
adius
fonte
1
Bom, mas não parece suportar um filtro de caminho ou pelo menos um argumento de subdiretório. Seria melhor.
Spinkus
1
Solução agradável e limpa. A resposta de @ Alex rendeu contagens de linhas muito pequenas por algum motivo. Isso acabou de funcionar. Demorou cerca de 30 segundos para ~ 200k linhas espalhadas por algumas centenas de arquivos.
fgblomqvist 30/01
6

A solução de Erik foi incrível, mas tive alguns problemas com sinais diacríticos (apesar de minhas LC_*variáveis ​​de ambiente serem definidas ostensivamente corretamente) e ruído vazando nas linhas de código que realmente tinham datas nelas. Meu sed-fu é ruim, então acabei com esse trecho de frankenstein com rubi, mas funciona perfeitamente para mim em mais de 200.000 LOC e classifica os resultados:

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

Observe também, em gsedvez de sedporque é a instalação do homebrew binário, deixando o sistema intacto.

gtd
fonte
4

git shortlog -sn

Isso mostrará uma lista de confirmações por autor.

moinudin
fonte
17
Isso retorna o número de confirmações por autor, não o número de linhas.
v64
Muito útil na determinação dos principais colaboradores de um projeto / diretório / arquivo
Ares
4

Aqui está o trecho principal da resposta do @Alex que realmente faz a operação de agregar as linhas de culpa. Eu o reduzi para operar em um único arquivo, e não em um conjunto de arquivos.

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

Publico aqui porque volto a esta resposta com frequência e relendo a publicação e digerindo novamente os exemplos para extrair a parte que eu valorizo ​​que está tributando. Nem é genérico o suficiente para o meu caso de uso; seu escopo é para todo um projeto C.


Eu gosto de listar estatísticas por arquivo, obtidas com um foriterador bash em vez de xargsachar xargs menos legível e difícil de usar / memorizar, A vantagem / desvantagens xargs vs for devem ser discutidas em outros lugares.

Aqui está um trecho prático que mostrará resultados para cada arquivo individualmente:

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

E eu testei, executar este stright em um shell bash é ctrl + c seguro; se você precisar colocar isso dentro de um script bash, talvez seja necessário interceptar o SIGINT e o SIGTERM, se desejar que o usuário seja capaz de interromper seu loop for.

ThorSummoner
fonte
1
git blame -w -M -C -C --line-porcelain path/to/file.txt | grep -I '^author ' | sort | uniq -ic | sort -nrEncontrei um pequeno ajuste no git blame aqui que retrata com mais precisão as estatísticas que eu estava procurando. Especificamente, a opção -M e -C -C (esses são dois Cs de propósito). -M detecta movimentos dentro do arquivo e -C -C detecta linhas copiadas de outros arquivos. Veja o documento aqui . Para completar, -w ignora os espaços em branco.
John Lee
1

Eu tenho esta solução que conta as linhas culpadas em todos os arquivos de texto (excluindo os arquivos binários, mesmo os versionados):

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
Gabriel Diego
fonte
1

Isso funciona em qualquer diretório da estrutura de origem do repositório, caso você queira inspecionar um determinado módulo de origem.

find . -name '*.c' | xargs -n1 git blame --line-porcelain | grep "^author "|sort|uniq -c|sort -nr
Martin G
fonte
0

Adotei a principal resposta ao 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

É opcional se você executa git blamecom o -wswitch, eu o adicionei porque ignora as alterações de espaço em branco.

O desempenho em minha máquina era a favor do Powershell (~ 50s vs ~ 65s para o mesmo repo), embora a solução Bash estivesse sendo executada no WSL2

Matt M.
fonte
-1

Criei meu próprio script, que é uma combinação de @nilbus e @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
fonte
Para mim, sua coisa enter code hereestava causando problemas .... isso funciona corretamente?
Menios
-1

Função Bash que visa um único arquivo de origem executado no 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
fonte