Jak policzyć całkowitą liczbę wierszy zmienionych przez określonego autora w repozytorium Git?

458

Czy istnieje polecenie, które mogę wywołać, które zlicza wiersze zmienione przez określonego autora w repozytorium Git? Wiem, że muszą istnieć sposoby na policzenie liczby zatwierdzeń, ponieważ Github robi to dla swojego wykresu wpływu.

Gav
źródło
1
Możesz rozważyć słynne narzędzie, które zbiera statystyki dotyczące rozwoju jądra Linuksa, na przykład Repozytorium jest tutaj git://git.lwn.net/gitdm.git.
0andriy

Odpowiedzi:

310

Dane wyjściowe następującego polecenia powinny być dość łatwe do wysłania do skryptu, aby dodać sumy:

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

Daje to statystyki dla wszystkich zatwierdzeń na bieżącym HEAD. Jeśli chcesz dodać statystyki do innych gałęzi, musisz podać je jako argumenty git log.

W przypadku przekazywania do skryptu usunięcie nawet formatu „oneline” można wykonać przy użyciu pustego formatu dziennika, a jak komentuje Jakub Narębski, --numstatjest inną alternatywą. Generuje statystyki dla poszczególnych plików zamiast dla poszczególnych linii, ale jest jeszcze łatwiejsze do analizy.

git log --author="<authorname>" --pretty=tformat: --numstat
CB Bailey
źródło
2
Zmieniono moją zaakceptowaną odpowiedź, ponieważ daje to wynik zgodny z oczekiwaniami i będzie bardziej pomocny dla innych odwiedzających, którzy chcą to osiągnąć.
Gav
14
Możesz użyć --numstatzamiast, --shortstatjeśli chcesz dodać statystyki nieco łatwiej.
Jakub Narębski,
8
Mogę też dodać tam „--no-scales”.
yoyo
9
przepraszam za te pytania, ale co mi mówią liczby? Są dwa rzędy i nie mam pojęcia, co mi mówią. Linie sprawdzone i dodane?
Informatic0re
2
@ Informatic0re git help logmówi mi, że pierwsze są dodawane, drugie usuwane.
ThomasH
599

Daje to statystyki dotyczące autora, modyfikuj zgodnie z wymaganiami.

Za pomocą 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 }' -

Korzystanie z Awk w 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 }' -

EDYCJA (2017)

Na githubie jest nowy pakiet, który wygląda gładko i używa bash jako zależności (testowane na Linuksie). Bardziej nadaje się do bezpośredniego użycia niż do skryptów.

To git-quick-statystyki (link github) .

Skopiuj git-quick-statsdo folderu i dodaj folder do ścieżki.

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

Stosowanie:

git-quick-stats

wprowadź opis zdjęcia tutaj

Alex
źródło
18
Dzięki za tę uroczą taklinę! To miejsce awk przetarło pokład każdego (dokładne, szybkie, bez dodatkowych dziwnych wyników). Nic dziwnego, biorąc pod uwagę, że jest to coś, do czego awk został zaprojektowany ... Szkoda, że ​​spóźniłeś się na przyjęcie.
zxq9,
4
@ zxq9: Nie zadawałem nawet pytań o przepełnienie stosu, kiedy zadano to pytanie i zainspirowały mnie odpowiedzi tutaj. miejmy nadzieję, że powoli wyprzedzę wszystkich tutaj, ponieważ ludzie będą tego potrzebować.
Alex
9
Działa to niesamowite, ale musiałem zmienić gawksię awk, aby pracować w terminalu OSX
Zach Lysobey
1
@samthebest, ponieważ przenoszenie pliku nie odzwierciedla właściwej statystyki. Linie nie ulegają zmianie. Do Alexa: Mówię o Gicie. Przy okazji, patrz mój komentarz do pierwotnego pytania.
0andriy
2
Jeśli adres URL nie działa dla Ciebie, spróbuj tego:git clone https://github.com/arzzen/git-quick-stats.git
Nicolas
226

Na wypadek, gdyby ktokolwiek chciał zobaczyć statystyki dla każdego użytkownika w swojej bazie kodu, kilku moich współpracowników niedawno wymyśliło ten przerażający jeden linijka:

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

(Zajmuje kilka minut, aby przejrzeć nasze repozytorium, które zawiera około 10-15k zatwierdzeń.)

Dan
źródło
12
To cudownie! michael,: 6057 files changed, 854902 insertions(+), 26973 deletions(-), 827929 net
Michael J. Calkins,
1
@EugenKonkov w kodzie jest zdefiniowany jako wstawianie - usuwanie.
Dan
13
to jedyne polecenie, które daje całkowity wynik dla repozytorium i działa bez żadnej wtyczki.
Ömer Faruk Almalı
1
Dostaję listę użytkowników razem, prawie każda możliwa kombinacja programistów wraca. dziwność na moim końcu?
Damon
2
@BenSewards możesz użyć Bash na Windowsie za pomocą Podsystemu Windows dla Linuxa, więcej informacji tutaj
mjsr
152

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

to miłe narzędzie do zliczania wszystkich autorów jednocześnie, w tym zatwierdzania i modyfikowania plików:

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

Istnieje również wersja Python na https://github.com/casperdcl/git-fame (wspomniana przez @fracz):

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

Przykładowe dane wyjściowe:

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

Ale ostrzegam: jak wspomniał Jared w komentarzu, zrobienie tego w bardzo dużym repozytorium zajmie kilka godzin. Nie jestem jednak pewien, czy można to poprawić, biorąc pod uwagę, że musi przetwarzać tyle danych Git.

Ciro Santilli
źródło
1
To niesamowite, ale takie powolne
Jared Burrows,
1
Działa dobrze na MacBooku w połowie 2015 roku i średnio dużym projekcie na Androida (jest 127k LoC). Kilka minut.
maxweber
2
@Vincent procent toalet loc / commits / files dla bieżącego użytkownika.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
Zmień gałąź, limit czasu i wyklucz folder:git fame --branch=dev --timeout=-1 --exclude=Pods/*
jonmecer
1
@AlexanderMills Zgaduję, że to dlatego, że nie można w sposób znaczący policzyć wierszy na
obiektach
103

Przydatne okazało się, kto ma najwięcej wierszy, które obecnie znajdowały się w bazie kodu:

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

Pozostałe odpowiedzi koncentrowały się głównie na wierszach zmienionych w zatwierdzeniach, ale jeśli zatwierdzenia nie przetrwają i zostaną nadpisane, mogły po prostu zostać odrzucone. Powyższe zaklęcie powoduje również, że wszystkie osoby zatwierdzające posortowane są według linii, a nie tylko pojedynczo. Możesz dodać kilka opcji do git blame (-C -M), aby uzyskać lepsze liczby uwzględniające ruch pliku i ruch linii między plikami, ale jeśli to zrobisz, polecenie może działać o wiele dłużej.

Ponadto, jeśli szukasz linii zmienionych we wszystkich zatwierdzeniach dla wszystkich osób zatwierdzających, pomocny jest następujący mały skrypt:

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

mmrobins
źródło
31
Już miałem dać +1, ale potem zdałem sobie sprawę, że rozwiązanie zależy od ruby ​​... :(
Mac
3
Możesz go zmodyfikować, aby nie używać ruby ​​dość łatwo, ponieważ po prostu używam ruby ​​do podstawiania łańcucha. Możesz użyć perla, seda, pythona itp.
mmrobins,
21
nie działa dla mnie: -e: 1: w `<main> ': niepoprawna sekwencja bajtów w UTF-8 (ArgumentError)
Michał Dębski
1
/^.*\((.*?)\s[\d]{4}/powinno być /^.*?\((.*?)\s[\d]{4}/zapobieganie dopasowywaniu nawiasów w źródle jako autor.
Timothy Gu,
1
mmm moje egzekucje pokazały wielu użytkowników, którzy nawet nie istnieją z powodu złego parsowania. Myślę, że to nie jest wiarygodna odpowiedź.
mjsr
92

Aby policzyć liczbę zatwierdzeń danego autora (lub wszystkich autorów) w danym oddziale, możesz użyć git-shortlog ; zobacz w szczególności jego --numberedi --summaryopcje, np. podczas uruchamiania na repozytorium 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
Jakub Narębski
źródło
2
Zauważ, że v1.6.4jest tutaj w tym przykładzie, aby uczynić wynik deterministycznym: będzie taki sam bez względu na to, kiedy sklonowałeś i / lub pobierałeś z repozytorium git.
Jakub Narębski
w tym v1.6.4daje mi:fatal: ambiguous argument 'v1.6.4': unknown revision or path not in the working tree.
Vlad the Impala
5
Ach, nie, brakowało mi „kiedy działa na repozytorium git”. Szczerze mówiąc, większość ludzi nie uruchomi tego polecenia na repozytorium git. Właściwie to całkiem spory margines.
Vlad the Impala
4
git shortlog -snelub, jeśli wolisz nie uwzględniać fuzjigit shortlog -sne --no-merges
Mark Swardstrom,
1
@Swards: -sis --summary, -nis --numberedi [new] -ema --emailwyświetlać e-maile autorów (i liczyć osobno tego samego autora z innym adresem e-mail, z uwzględnieniem .mailmappoprawek). Dobry telefon --no-merges.
Jakub Narębski
75

Po przeanalizowaniu odpowiedzi Alexa i Gerty3000 starałem się skrócić linijkę:

Zasadniczo, używając git log numstat i nie śledząc liczby zmienionych plików .

Wersja Git 2.1.0 na 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

Przykład:

Jared Burrows   added lines: 6826, removed lines: 2825, total lines: 4001
Jared Burrows
źródło
Nie mogę zrobić aliasu :-(
brat
33

Odpowiedź z AaronM pomocą powłoki jedno-liner jest dobry, ale w rzeczywistości, istnieje jeszcze inny błąd, gdzie obowiązuje będzie uszkodzony nazwy użytkowników, jeśli istnieją różne ilości spacji między nazwą użytkownika i datą. Zepsute nazwy użytkowników dadzą wiele wierszy dla liczby użytkowników i musisz je sam podsumować.

Ta niewielka zmiana naprawiła dla mnie problem:

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

Zwróć uwagę na znaki + po, które zajmą wszystkie białe znaki od nazwy do daty.

Właściwie dodałem tę odpowiedź zarówno dla mojej własnej pamięci, jak i dla pomagania komukolwiek innemu, ponieważ to jest co najmniej drugi raz, gdy szukam w Google tematu :)

  • Edytuj 2019-01-23 Dodano, --show-emailaby git blame -wzamiast tego agregować pocztą e-mail, ponieważ niektóre osoby używają różnych Nameformatów na różnych komputerach, a czasami dwie osoby o tej samej nazwie pracują w tym samym git.
Erik Zivkovic
źródło
Ta odpowiedź przy użyciu perla wydawała się nieco lepsza niż te oparte na rubinie. Ruby zakrztusiła się wierszami, które nie były rzeczywistym tekstem UTF-8, perl nie narzekał. Ale czy Perl zrobił właściwą rzecz? Nie wiem
Stéphane Gourichon
Podmoduły powodują, unsupported file typeale wydaje się, że nawet OK działa z nimi (pomija je).
Vladimír Čunát,
24

Oto krótki linijka, która generuje statystyki dla wszystkich autorów. Jest znacznie szybszy niż powyższe rozwiązanie Dana pod adresem https://stackoverflow.com/a/20414465/1102119 (moje ma złożoność czasową O (N) zamiast O (NM), gdzie N jest liczbą zatwierdzeń, a M liczbą autorów ).

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
kccqzy
źródło
4
Fajnie, ale co oznacza wynik?
Gary Willoughby
Powinieneś dodać --no-show-signature, w przeciwnym razie ludzie, którzy podpiszą swoje pgp, nie zostaną policzeni.
Philihp Busby
2
ins [a] - del [a], ins [a], del [a], a, więc jeśli mam rację wstawianie-usuwanie, wstawianie, usuwanie, nazwa
MrKekson
Jak mogę dodać to polecenie do mojej konfiguracji git, aby móc wywoływać je za pomocą „git count-lines”?
takanuva15
Nie szkodzi, ja zorientowaliśmy się: count-lines = "!f() { 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; }; f". (Uwaga: korzystam z systemu Windows; może być konieczne użycie różnego rodzaju cudzysłowów)
takanuva15
21

@mmrobins @AaronM @ErikZ @JamesMishra dostarczył warianty, które mają wspólny problem: proszą git o stworzenie mieszanki informacji nieprzeznaczonych do użycia skryptu, w tym zawartości linii z repozytorium w tym samym wierszu, a następnie dopasowują bałagan do wyrażenia regularnego .

Jest to problem, gdy niektóre wiersze nie są poprawnym tekstem UTF-8, a także gdy niektóre wiersze pasują do wyrażenia regularnego (zdarzyło się to tutaj).

Oto zmodyfikowana linia, która nie ma tych problemów. Prosi gita o czyste dane wyjściowe w osobnych liniach, co ułatwia solidne filtrowanie tego, co chcemy:

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

Możesz grep dla innych ciągów znaków, takich jak autor-mail, komisarz itp.

Być może najpierw wykonaj export LC_ALL=C(zakładając bash), aby wymusić przetwarzanie na poziomie bajtów (dzieje się tak również, aby znacznie przyspieszyć grep z ustawień regionalnych opartych na UTF-8).

Stéphane Gourichon
źródło
Ładna linia, bardzo fajna, że ​​można ją łatwo pomieszać, ale to nie robi tego, czego żądał oryginalny plakat, podał hrabia według autora z git. Pewnie, że możesz go uruchomić i zrobić wc-l itp., Ale wtedy musisz powtórzyć dla każdego autora w repozytorium.
AaronM,
1
@AaronM Nie rozumiem twojej krytyki. Ta linia AFAIK generuje te same statystyki co twoje, tylko bardziej niezawodne. Tak więc, jeśli moja odpowiedź „nie zrobi tego, o co prosił oryginalny plakat, podaj liczbę od autora z git”, to twoje jeszcze więcej. Proszę, oświeć mnie.
Stéphane Gourichon
przepraszam, że źle przeczytałem, myślałem, że polecenie trzeba zmodyfikować dla każdego nazwiska autora. Twój komentarz na temat grep dla innych łańcuchów doprowadził mnie tam, ale było to moje nieporozumienie.
AaronM
To jest niesamowite. Dzięki!
Tek
16

Podano rozwiązanie z rubinem pośrodku, ponieważ perl jest nieco bardziej dostępny domyślnie tutaj jest alternatywą użycie perla do bieżących linii autora.

git ls-files -z | xargs -0n1 git blame -w | perl -n -e '/^.*\((.*?)\s*[\d]{4}/; print $1,"\n"' | sort -f | uniq -c | sort -n
AaronM
źródło
5
Zaktualizowany regex nie robi znaczącej różnicy i jest zepsuty, ponieważ nie uciekłeś z pierwszego paren. Widzę jednak kilka przypadków, w których mój poprzedni może znaleźć pewne bity w wierszu kodu, na których można się zatrzasnąć. To działałoby bardziej niezawodnie: git ls-files -z | xargs -0n1 git blame -w | perl -n -e '/^.*?\((.*?)\s[\d]{4}/; print $ 1, "\ n"' | sort -f | uniq -c | sort -n
AaronM
dzięki za próbę stworzenia bardziej niezawodnego wyrażenia regularnego. Zobacz moją odpowiedź na bardziej solidny wariant stackoverflow.com/a/36090245/1429390
Stéphane Gourichon,
13

Oprócz odpowiedzi Charlesa Baileya możesz chcieć dodać -Cparametr do poleceń. W przeciwnym razie nazwy plików są liczone jako wiele dodatków i usunięć (tyle, ile plik ma wiersze), nawet jeśli zawartość pliku nie została zmodyfikowana.

Aby to zilustrować, oto zatwierdzenie z dużą ilością plików przenoszonych z jednego z moich projektów, gdy używasz git log --oneline --shortstatpolecenia:

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

A tutaj to samo zatwierdzenie za pomocą git log --oneline --shortstat -Cpolecenia, które wykrywa kopie plików i zmienia nazwy:

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

Moim zdaniem ta ostatnia daje bardziej realistyczny obraz wpływu, jaki dana osoba wywarła na projekt, ponieważ zmiana nazwy pliku jest znacznie mniejszą operacją niż zapisanie pliku od zera.

Esko Luontola
źródło
2
Kiedy wykonuję „git log --oneline --shortstat”, nie otrzymuję twojego wyniku. Mam listę zmian z liczbą wydań, ale nie z całkowitą liczbą. Jak mogę uzyskać łączną liczbę linii edytowanych we wszystkich repozytoriach git?
Mehdi
12

możesz użyć Whodid ( https://www.npmjs.com/package/whodid )

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

i

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

lub po prostu wpisz

$ whodid

wtedy możesz zobaczyć taki wynik

Contribution state
=====================================================
 score  | author
-----------------------------------------------------
 3059   | someguy <[email protected]>
 585    | somelady <[email protected]>
 212    | niceguy <[email protected]>
 173    | coolguy <[email protected]>
=====================================================
victor.cheval
źródło
Co oznacza „wynik”?
user11171,
@Volte npm i to tylko skrót do instalacji npm
Michiel
Tak, jestem świadomy. My -gmieliśmy przyjść przed nazwą pakietu, na macOS. Po prostu próbuję pomóc.
Volte
11

Oto szybki skrypt rubinowy, który koryguje wpływ na użytkownika względem danego zapytania dziennika.

Na przykład dla rubiniusa :

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

scenariusz:

#!/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
Nevir
źródło
2
Ten skrypt jest świetny, ale wyklucza autorów, którzy mają tylko zatwierdzenia w jednym wierszu! Aby to naprawić, zmień w następujący sposób: zmiany = wstawianie / (\ d +). * (\ D +) usuwanie / .match (linia)
Larry Gritz
9

jest to najlepszy sposób, a także daje wyraźny obraz całkowitej liczby zatwierdzeń przez wszystkich użytkowników

git shortlog -s -n
edrich13
źródło
2
Przydatne, ale to liczba zatwierdzeń
niezupełnie
5

Podałem modyfikację krótkiej odpowiedzi powyżej, ale nie była ona wystarczająca dla moich potrzeb. Musiałem być w stanie kategoryzować zarówno zatwierdzone wiersze, jak i wiersze w końcowym kodzie. Chciałem też rozbić na pliki. Ten kod nie powtarza się, zwraca wyniki tylko dla jednego katalogu, ale jest to dobry początek, jeśli ktoś chce pójść dalej. Skopiuj i wklej do pliku, aby był wykonywalny lub uruchom go w Perlu.

#!/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($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 = <GIT_BLAME>) {
        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};
}
AaronM
źródło
Pojawia się ten błąd: Nielegalne dzielenie przez zero na linii x.pl 71.
Vivek Jha
Rozwiązano niedozwolony podział przez zero w wierszu 71. Pomyśl, że ma to miejsce, jeśli nie ma żadnych zmian, ale było to jakiś czas temu, kiedy to napisałem.
AaronM
2

W przypadku użytkowników systemu Windows można użyć następującego skryptu wsadowego, który zlicza dodane / usunięte wiersze dla określonego autora

@echo off

set added=0
set removed=0

for /f "tokens=1-3 delims= " %%A in ('git log --pretty^=tformat: --numstat --author^=%1') do call :Count %%A %%B %%C

@echo added=%added%
@echo removed=%removed%
goto :eof

:Count
  if NOT "%1" == "-" set /a added=%added% + %1
  if NOT "%2" == "-" set /a removed=%removed% + %2
goto :eof

https://gist.github.com/zVolodymyr/62e78a744d99d414d56646a5e8a1ff4f

Volodymyr Baydalka
źródło
2

Oto świetne repozytorium, które ułatwia Ci życie

git-quick-stats

Na komputerze Mac z zainstalowanym zaparzeniem

brew install git-quick-stats

Biegać

git-quick-stats

Wystarczy wybrać opcję z tej listy, wpisując numer na liście i naciskając Enter.

 Generate:
    1) Contribution stats (by author)
    2) Contribution stats (by author) on a specific branch
    3) Git changelogs (last 10 days)
    4) Git changelogs by author
    5) My daily status
    6) Save git log output in JSON format

 List:
    7) Branch tree view (last 10)
    8) All branches (sorted by most recent commit)
    9) All contributors (sorted by name)
   10) Git commits per author
   11) Git commits per date
   12) Git commits per month
   13) Git commits per weekday
   14) Git commits per hour
   15) Git commits by author per hour

 Suggest:
   16) Code reviewers (based on git history)

jasonleonhard
źródło
1

Ten skrypt tutaj to zrobi. Umieść go w authorship.sh, chmod + x it i gotowe.

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

źródło
1
nie, WONT !, opublikowałeś to gdzie indziej, generuje błędy na Macu i Linuksie, wiesz, rodzaj komputerów, na których został stworzony Git!
Pizzaiola Gorgonzola
1

Zapisz dzienniki w pliku, używając:

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

Dla miłośników Pythona:

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

Twoje wyniki wyglądałyby tak:

225        files changed
6751       insertions
1379       deletions
Amen Ayach
źródło
0

Chcesz winić Git .

Istnieje opcja --show-stats, aby wydrukować niektóre, no cóż, statystyki.

gbjbaanb
źródło
Próbowałem blame, ale tak naprawdę to nie dawało statystyk, których myślałem, że OP będzie potrzebował?
CB Bailey,
Dzięki, pomogło mi to również w .mailmap!
Gav
0

Pytanie dotyczyło informacji o konkretnym autorze, ale wiele odpowiedzi dotyczyło rozwiązań, które zwracały rankingowe listy autorów na podstawie zmienionych linii kodu.

Tego właśnie szukałem, ale istniejące rozwiązania nie były do ​​końca idealne. W interesie osób, które mogą znaleźć to pytanie za pośrednictwem Google, ulepszyłem je i przekształciłem w skrypt powłoki, który wyświetlam poniżej. Adnotacje (które będę nadal utrzymywać) można znaleźć na moim Githubie .

Nie ma zależności ani od Perla, ani od Ruby. Ponadto, białe znaki, nazwy i ruchy linii są uwzględniane w liczbie zmian linii. Po prostu umieść to w pliku i przekaż swoje repozytorium Git jako pierwszy parametr.

#!/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
James Mishra
źródło
0

Napisałem ten skrypt Perla, aby wykonać to zadanie.

#!/usr/bin/env perl

use strict;
use warnings;

# save the args to pass to the git log command
my $ARGS = join(' ', @ARGV);

#get the repo slug
my $NAME = _get_repo_slug();

#get list of authors
my @authors = _get_authors();
my ($projectFiles, $projectInsertions, $projectDeletions) = (0,0,0);
#for each author
foreach my $author (@authors) {
  my $command = qq{git log $ARGS --author="$author" --oneline --shortstat --no-merges};
  my ($files, $insertions, $deletions) = (0,0,0);
  my @lines = `$command`;
  foreach my $line (@lines) {
    if ($line =~ m/^\s(\d+)\s\w+\s\w+,\s(\d+)\s\w+\([\+|\-]\),\s(\d+)\s\w+\([\+|\-]\)$|^\s(\d+)\s\w+\s\w+,\s(\d+)\s\w+\(([\+|\-])\)$/) {
      my $lineFiles = $1 ? $1 : $4;
      my $lineInsertions = (defined $6 && $6 eq '+') ? $5 : (defined $2) ? $2 : 0;
      my $lineDeletions = (defined $6 && $6 eq '-') ? $5 : (defined $3) ? $3 : 0;
      $files += $lineFiles;
      $insertions += $lineInsertions;
      $deletions += $lineDeletions;
      $projectFiles += $lineFiles;
      $projectInsertions += $lineInsertions;
      $projectDeletions += $lineDeletions;
    }
  }
  if ($files || $insertions || $deletions) {
    printf(
      "%s,%s,%s,+%s,-%s,%s\n",
      $NAME,
      $author,
      $files,
      $insertions,
      $deletions,
      $insertions - $deletions
    );
  }
}

printf(
  "%s,%s,%s,+%s,-%s,%s\n",
  $NAME,
  'PROJECT_TOTAL',
  $projectFiles,
  $projectInsertions,
  $projectDeletions,
  $projectInsertions - $projectDeletions
);

exit 0;

#get the remote.origin.url joins that last two pieces (project and repo folder)
#and removes any .git from the results. 
sub _get_repo_slug {
  my $get_remote_url = "git config --get remote.origin.url";
  my $remote_url = `$get_remote_url`;
  chomp $remote_url;

  my @parts = split('/', $remote_url);

  my $slug = join('-', @parts[-2..-1]);
  $slug =~ s/\.git//;

  return $slug;
}

sub _get_authors {
  my $git_authors = 'git shortlog -s | cut -c8-';
  my @authors = `$git_authors`;
  chomp @authors;

  return @authors;
}

Nadałem mu nazwę git-line-changes-by-authori włożyłem /usr/local/bin. Ponieważ jest zapisany na mojej ścieżce, mogę wydać polecenie, git line-changes-by-author --before 2018-12-31 --after 2020-01-01aby uzyskać raport za rok 2019. Jako przykład. A jeśli miałbym źle napisać, nazwa git sugeruje poprawną pisownię.

Możesz dostosować _get_repo_slugsub, aby zawierał tylko ostatnią część, remote.origin.urlponieważ moje repozytoria są zapisywane jako, project/repoa twoje mogą nie być.

joehep
źródło