Come contare le linee totali modificate da un autore specifico in un repository Git?

C’è un comando che posso richiamare che conterà le linee modificate da un autore specifico in un repository Git? So che ci devono essere modi per contare il numero di commit come Github fa questo per il loro grafico di impatto.

L’output del seguente comando dovrebbe essere ragionevolmente facile da inviare allo script per sumre i totali:

 git log --author="" --oneline --shortstat 

Questo fornisce statistiche per tutti i commit sul TESTO corrente. Se vuoi sumre le statistiche in altri rami dovrai fornirle come argomenti per git log .

Per passare a uno script, rimuovere anche il formato “oneline” può essere fatto con un formato di log vuoto, e come commentato da Jakub Narębski, --numstat è un’altra alternativa. Genera statistiche per-file anziché per-line ma è ancora più semplice da analizzare.

 git log --author="" --pretty=tformat: --numstat 

Questo dà alcune statistiche sull’autore, modificare come richiesto.

Utilizzando 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 }' - 

Usando Awk su Mac OSX:

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

EDIT (2017)

C’è un nuovo pacchetto su github che sembra shiny e usa bash come dipendenze (testato su linux). È più adatto per l’uso diretto piuttosto che per gli script.

È git-quick-stats (link github) .

Copia git-quick-stats in una cartella e aggiungi la cartella al percorso.

 mkdir ~/source cd ~/source git clone [email protected]: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 

Uso:

 git-quick-stats 

inserisci la descrizione dell'immagine qui

Nel caso in cui qualcuno volesse vedere le statistiche per ogni utente nella sua base di codice, un paio dei miei colleghi di recente hanno inventato questa orribile linea:

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

(Prende alcuni minuti per crunch attraverso il nostro repo, che ha circa 10-15k commit.)

Git fame https://github.com/oleander/git-fame-rb

è uno strumento utile per ottenere il conteggio di tutti gli autori contemporaneamente, incluso il conteggio dei commit e dei file modificati:

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

C’è anche la versione di Python su https://github.com/casperdcl/git-fame (citato da @fracz):

 sudo apt-get install python-pip python-dev build-essential pip install --user git-fame cd /path/to/gitdir && git fame 

Uscita di esempio:

 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 | +------------------------+--------+---------+-------+--------------------+ 

Ma attenzione: come accennato da Jared nel commento, farlo su un repository molto grande richiederà ore. Non sono sicuro che potrebbe essere migliorato, considerando che deve elaborare così tanti dati Git.

Ho trovato il seguente per essere utile per vedere chi ha la maggior parte delle linee che erano attualmente nel codice di base:

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

Le altre risposte si sono concentrate principalmente sulle linee modificate in commit, ma se i commit non sopravvivono e vengono sovrascritti, potrebbero essere stati semplicemente scambiati. L’incantesimo di cui sopra ottiene anche voi tutti i committer ordinati per linee invece che solo uno alla volta. Puoi aggiungere alcune opzioni a git blame (-C -M) per ottenere numeri migliori che tengano conto del movimento dei file e del movimento delle linee tra i file, ma il comando potrebbe essere molto più lungo se lo fai.

Inoltre, se stai cercando le linee modificate in tutti i commit per tutti i committer, il seguente script è utile:

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

Per contare il numero di commit di un determinato autore (o di tutti gli autori) su un determinato ramo puoi usare git-shortlog ; vedere soprattutto le sue opzioni --numbered e --summary , ad esempio quando si esegue il repository 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 

Dopo aver esaminato la risposta di Alex e Gerty3000 , ho cercato di accorciare la one-liner:

Fondamentalmente, usando git log numstat e non tenendo traccia del numero di file modificati.

Git versione 2.1.0 su 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 += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done 

Esempio:

 Jared Burrows added lines: 6826, removed lines: 2825, total lines: 4001 

La risposta di AaronM che usa la shell one-liner è buona, ma in realtà c’è un altro bug, in cui gli spazi corrompono i nomi utente se ci sono diverse quantità di spazi bianchi tra il nome utente e la data. I nomi utente corrotti forniranno più righe per i conteggi degli utenti e dovrai riassumerle tu stesso.

Questo piccolo cambiamento ha risolto il problema per me:

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

Notare i + dopo che consumeranno tutti gli spazi bianchi dal nome alla data.

In realtà aggiungo questa risposta tanto per il mio ricordo quanto per aiutare qualcun altro, dato che questa è almeno la seconda volta che cerco l’argomento 🙂

@mmrobins @AaronM @ErikZ @JamesMishra ha fornito varianti che hanno tutti un problema in comune: chiedono a git di produrre una mistura di informazioni non destinate al consumo di script, inclusi i contenuti di riga dal repository sulla stessa riga, quindi corrispondono al pasticcio con un’espressione regolare .

Questo è un problema quando alcune linee non sono un testo UTF-8 valido, e anche quando alcune righe capita di corrispondere alla regexp (questo è successo qui).

Ecco una riga modificata che non ha questi problemi. Richiede git per generare dati in modo pulito su linee separate, il che rende facile filtrare ciò che vogliamo in modo robusto:

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

È ansible grep per altre stringhe, come autore-mail, committer, ecc.

Forse prima export LC_ALL=C (presupponendo bash ) per forzare l’elaborazione a livello di byte (questo succede anche a velocizzare enormemente grep dalle locali basate su UTF-8).

Ecco una breve copertina che produce statistiche per tutti gli autori. È molto più veloce della soluzione di Dan sopra a https://stackoverflow.com/a/20414465/1102119 (il mio ha complessità temporale O (N) invece di O (NM) dove N è il numero di commit, e M il numero di autori ).

 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 

Una soluzione è stata data con il ruby nel mezzo, mentre perl è un po ‘più disponibile di default qui è un’alternativa usando perl per le linee correnti per autore.

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

Oltre alla risposta di Charles Bailey , potresti voler aggiungere il parametro -C ai comandi. In caso contrario, le ridenominazioni dei file contano come molte aggiunte e rimozioni (quante sono le righe del file), anche se il contenuto del file non è stato modificato.

Per illustrare, ecco un commit con molti file spostati da uno dei miei progetti, quando si utilizza il comando git log --oneline --shortstat :

 9052459 Reorganized project structure 43 files changed, 1049 insertions(+), 1000 deletions(-) 

E qui lo stesso commit usando il comando git log --oneline --shortstat -C che rileva le copie dei file e rinomina:

 9052459 Reorganized project structure 27 files changed, 134 insertions(+), 85 deletions(-) 

Secondo me quest’ultimo offre una visione più realistica di quanto impatto abbia una persona sul progetto, perché rinominare un file è un’operazione molto più piccola della scrittura del file da zero.

Ecco uno script ruby rapido che corregge l’impatto per utente su una determinata query di registro.

Ad esempio, per rubinius :

 Brian Ford: 4410668 Evan Phoenix: 1906343 Ryan Davis: 855674 Shane Becker: 242904 Alexander Kellett: 167600 Eric Hodel: 132986 Dirkjan Bussink: 113756 ... 

il copione:

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

Ho fornito una modifica di una risposta breve sopra, ma non era sufficiente per le mie esigenze. Dovevo essere in grado di classificare sia le linee che le linee impegnate nel codice finale. Volevo anche una suddivisione per file. Questo codice non viene restituito, restituirà solo i risultati per una singola directory, ma è un buon inizio se qualcuno volesse andare oltre. Copia e incolla in un file e rendilo eseguibile o eseguilo con 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 = ) { 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 = ) { 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 = ) { 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}; } 

questo è il modo migliore e offre anche un’immagine chiara del numero totale di commit da parte di tutti gli utenti

 git shortlog -s -n 

puoi usare whodid ( https://www.npmjs.com/package/whodid )

 $ npm install whodid -g $ cd your-project-dir 

e

 $ whodid author --include-merge=false --path=./ --valid-threshold=1000 --since=1.week 

o semplicemente scrivi

 $ whodid 

allora puoi vedere risultati come questo

 Contribution state ===================================================== score | author ----------------------------------------------------- 3059 | someguy  585 | somelady  212 | niceguy  173 | coolguy  ===================================================== 

Questo script lo farà. Inseriscilo in authorship.sh, chmod + x it e tutto è pronto.

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

Salva i tuoi log in file usando:

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

Per gli amanti di 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') 

Le tue uscite sarebbero come:

 225 files changed 6751 insertions 1379 deletions 

Vuoi dare la colpa a Git .

C’è un’opzione –show-stats per stampare alcune statistiche.

La domanda richiedeva informazioni su un autore specifico , ma molte delle risposte erano soluzioni che restituivano elenchi di autori classificati in base alle loro linee di codice modificate.

Questo era quello che stavo cercando, ma le soluzioni esistenti non erano del tutto perfette. Nell’interesse delle persone che potrebbero trovare questa domanda tramite Google, ho apportato alcuni miglioramenti su di loro e li ho trasformati in uno script di shell, che viene visualizzato di seguito. Un annotato (che continuerò a mantenere) può essere trovato sul mio Github .

Non ci sono dipendenze da Perl o Ruby. Inoltre, gli spazi bianchi, i nomi e i movimenti delle linee vengono presi in considerazione nel conteggio dei cambi di linea. Basta inserirlo in un file e passare il repository Git come primo parametro.

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

Lo strumento migliore finora identificato è gitinspector. Fornisce il rapporto impostato per utente, a settimana ecc. È ansible installare come di seguito con npm

npm install -g gitinspector

I link per ottenere maggiori dettagli

https://www.npmjs.com/package/gitinspector

https://github.com/ejwa/gitinspector/wiki/Documentation

https://github.com/ejwa/gitinspector

i comandi di esempio sono

 gitinspector -lmrTw gitinspector --since=1-1-2017 etc