Git Blame Commit Statistics


198

Come posso "abusare" della colpa (o di una funzione più adatta, e / o in combinazione con i comandi di shell) per darmi una statistica di quante righe (di codice) sono attualmente nel repository originate da ciascun committer?

Esempio di output:

Committer 1: 8046 Lines
Committer 2: 4378 Lines

11
Dovrebbe esserci davvero un comando integrato per questo ... ci sono comandi per casi d'uso molto meno comuni.
Ciro Santilli 15 冠状 病 六四 事件 法轮功

@CiroSantilli ma è facile aggiungere uno shellscript invocabile da git.
Alex,

possibile duplicato di Come contare le righe totali modificate da un autore specifico in un repository Git? perché può essere facilmente ridotto a quello: basta fare un giro su tutti gli autori
Ciro Santilli 27 冠状 病 六四 事件 法轮功

1
questo è abbastanza fantastico code.google.com/p/gitinspector soprattutto se stai assegnando voti ai team di studenti (non è necessario applicare progetti di grandi dimensioni ... è lento perché incolpa ogni singolo file)
vedi

Risposte:


166

Aggiornare

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

Ho aggiornato alcune cose sulla strada.

Per comodità, puoi anche metterlo nel suo 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

memorizzalo da qualche parte nel tuo percorso o modifica il tuo percorso e usalo come

  • 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

Risposta originale

Mentre la risposta accettata fa il lavoro è molto 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

è quasi istantaneo.

Per ottenere un elenco di file attualmente tracciati è possibile utilizzare

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

Questa soluzione evita di chiamare fileper determinare il tipo di file e utilizza grep per abbinare l'estensione desiderata per motivi di prestazioni. Se tutti i file devono essere inclusi, rimuovili dalla riga.

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

se i file possono contenere spazi, che sono dannosi per le shell puoi usare:

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

Fornisci un elenco di file (tramite una pipe) che puoi usare xargs per chiamare un comando e distribuire gli argomenti. I comandi che consentono l'elaborazione di più file non accettano -n1. In questo caso chiamiamo git blame --line-porcelaine per ogni chiamata usiamo esattamente 1 argomento.

xargs -n1 git blame --line-porcelain

Quindi filtriamo l'output per le occorrenze di "autore" ordinando l'elenco e contando le righe duplicate per:

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

Nota

Altre risposte filtrano effettivamente le righe che contengono solo spazi bianchi.

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

Il comando sopra stamperà gli autori delle righe che contengono almeno un carattere non bianco. Puoi anche usare la corrispondenza \w*[^\w#]che escluderà anche le linee in cui il primo carattere non bianco non è un #(commento in molti linguaggi di scripting).


2
@nilbus: non puoi. echo "a\nb\nc"|xargs -n1 cmdsi espanderà acmd a; cmd b; cmd d
Alex

2
--line-porcelain non sembra più funzionare (git 1.7.5.4) invece usa --porcelain
isoiphone

4
Utenti OSX, provare quanto segue (continua a non funzionare su file con newline a loro 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 vuoi solo tutto sotto il percorso corrente, a qualsiasi profondità, usa "./" come filtro del percorso (dove il risponditore inserisce " / .c").
Ben Dilts,

2
Forse usa "blame -w" per ottenere una migliore proprietà del codice quando il codice è stato riformattato stackoverflow.com/questions/4112410/…
sleeplessnerd

124

Ho scritto una gemma chiamata git-fame che potrebbe essere utile.

Installazione e utilizzo:

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

Produzione:

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 infine 1 funziona e sembra che fornisca numeri sensibili, il resto di quelli della riga di comando non funzionano su OSX a causa dell'incompatibilità di utils o danno numeri teeny sul mio repository. Questo è su OSX e ruby ​​1.9.3 (birra)
Karthik T

9
Non essere sciocco, @tcaswell. Non è spam indicare qualcosa di utile, anche se ti è capitato di scriverlo.
Wayne,

5
Rispondere alla mia domanda: git fame --exclude = percorsi / verso / file, percorsi / verso / altro / file
Maciej Swic

2
@Adam: hai ancora problemi con questo? Funziona molto bene per me su OS X 10.9.5.
Sam Dutton,

2
Per ogni repo che è più grande di pochi, il tempo necessario a questa gemma per fare il suo lavoro è astronomico
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

Spiegazione dettagliata:

Elenca tutti i file sotto il controllo della versione

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

Elimina l'elenco fino a solo file di testo

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

Git incolpa tutti i file di testo, ignorando le modifiche agli spazi bianchi

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

Estrai i nomi degli autori

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

Ordinare l'elenco degli autori e fare in modo che uniq conteggi il numero di righe ripetute consecutivamente

|sort|uniq -c

Esempio di output:

   1334 Maneater
   1924 Another guy
  37195 Brian Ruby
   1482 Anna Lambda

1
Sembra che io abbia una sedversione diversa , la mia non capisce la -rbandiera e ha problemi con la regex (si lamenta dei genitori sbilanciati, anche quando rimuovo il surplus ().
Erik Aigner,

7
Non importa, sudo brew install gnu-sedrisolto. Funziona come un fascino!
Erik Aigner,

5
O port install gsedper utenti MacPorts.
Gavin Brock,

Ho fatto un sudo brew install gnu-sed(che ha funzionato) ma ricevo ancora errori che sed non riconosce -r. :(
Adam Tuttle,

1
Su OSX dopo l'installazione di gsed tramite macports ho eseguito questo comando per farlo funzionare (sostituito sed con 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 summaryfornito dal pacchetto git-extra è esattamente ciò di cui hai bisogno. Consulta la documentazione su git-extra - riepilogo git :

git summary --line

Fornisce un output simile al seguente:

project  : TestProject
lines    : 13397
authors  :
8927 John Doe            66.6%
4447 Jane Smith          33.2%
  23 Not Committed Yet   0.2%

1
Bello, ma non sembra supportare un filtro percorso, o almeno un argomento della sottodirectory. Sarebbe più bello.
spinkus

1
Soluzione piacevole e pulita. @La risposta di Alex ha prodotto conteggi di linea molto piccoli per qualche motivo. Questo ha funzionato fuori dagli schemi. Ci sono voluti circa 30 secondi per ~ 200k linee distribuite su alcune centinaia di file.
fgblomqvist,

6

La soluzione di Erik è stata fantastica, ma ho avuto alcuni problemi con i segni diacritici (nonostante le mie LC_*variabili di ambiente siano apparentemente impostate correttamente) e il rumore che filtrava su righe di codice che contenevano effettivamente delle date. La mia sed-fu è povera, quindi ho finito con questo frammento di frankenstein con dentro rubino, ma funziona perfettamente per me su oltre 200.000 LOC e ordina i risultati:

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

Nota anche gsedinvece sedche perché è l'installazione homebrew binaria, lasciando intatto il sistema.


4

git shortlog -sn

Questo mostrerà un elenco di commit per autore.


17
Ciò restituisce il numero di commit per autore, non il numero di righe.
v64,

Molto utile nel determinare i principali collaboratori di un progetto / directory / file
Ares

4

Ecco lo snippet principale della risposta di @Alex che svolge effettivamente l'operazione di aggregazione delle righe di colpa. L'ho ridotto per funzionare su un singolo file anziché su un set di file.

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

Pubblico questo qui perché torno spesso a questa risposta e rileggo il post e ri-digerisco gli esempi per estrarre la parte che apprezzo sia tassativa. Né è abbastanza generico per il mio caso d'uso; il suo scopo è per un intero progetto C.


Mi piace elencare le statistiche per file, ottenute tramite un foriteratore bash anziché xargscome trovo xargs meno leggibile e difficile da usare / memorizzare, Il vantaggio / svantaggi xargs vs for dovrebbero essere discussi altrove.

Ecco uno snippet pratico che mostrerà i risultati per ciascun file singolarmente:

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

E ho testato, eseguire questo stright in una shell bash è ctrl + c sicuro, se hai bisogno di inserirlo in uno script bash potresti aver bisogno di Trap su SIGINT e SIGTERM se vuoi che l'utente sia in grado di interrompere il tuo ciclo for.


1
git blame -w -M -C -C --line-porcelain path/to/file.txt | grep -I '^author ' | sort | uniq -ic | sort -nrHo trovato una leggera modifica al git blame qui che ritrae più accuratamente le statistiche che stavo cercando. In particolare, l'opzione -M e -C -C (quelle sono due C in proposito). -M rileva le mosse all'interno del file e -C -C rileva le linee copiate da altri file. Vedi doc qui . Per completezza, -w ignora gli spazi bianchi.
John Lee,


1

Ho questa soluzione che conta le righe incolpate in tutti i file di testo (esclusi i file binari, anche quelli con versione):

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

Funziona in qualsiasi directory della struttura sorgente del repository, nel caso in cui si desideri ispezionare un determinato modulo sorgente.

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

0

Ho adottato la risposta migliore a 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

È facoltativo se si esegue git blamecon-w , l'ho aggiunto perché ignora le modifiche agli spazi bianchi.

Le prestazioni sulla mia macchina erano a favore di Powershell (~ 50s ~ ~ 65s per lo stesso repository), sebbene la soluzione Bash funzionasse con WSL2


-1

Ho creato il mio script, che è una combinazione di @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

Per me, la tua cosa enter code herestava causando problemi ... funziona correttamente?
Menios,

-1

Funzione Bash che ha come target un singolo file sorgente eseguito su 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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.