Git Blame Commit Statistics

198

Jak mogę „obwinić” winę (lub jakąś lepiej dopasowaną funkcję i / lub w połączeniu z poleceniami powłoki), aby podać mi statystyczną liczbę wierszy (kodu) aktualnie znajdujących się w repozytorium pochodzących z każdego podmiotu odpowiedzialnego?

Przykładowe dane wyjściowe:

Committer 1: 8046 Lines
Committer 2: 4378 Lines
Erik Aigner
źródło
11
Naprawdę powinno być do tego wbudowane polecenie ... istnieją polecenia dla znacznie mniej powszechnych przypadków użycia.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
@CiroSantilli, ale łatwo jest dodać skrypt powłoki, który można wywołać z git.
Alex
możliwy duplikat Jak liczyć całkowitą liczbę wierszy zmienionych przez określonego autora w repozytorium Git? ponieważ można go łatwo sprowadzić do tego: po prostu
obejrzyj
1
jest to dość niesamowite code.google.com/p/gitinspector zwłaszcza jeśli Klasyfikację zadań przez zespoły uczniów (duże projekty nie muszą stosować ... jest powolny, ponieważ obwinia każdy pojedynczy plik)
sehe

Odpowiedzi:

166

Aktualizacja

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

Po drodze zaktualizowałem kilka rzeczy.

Dla wygody możesz również umieścić to w swoim własnym poleceniu:

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

przechowuj to gdzieś na swojej ścieżce lub zmodyfikuj swoją ścieżkę i używaj jej jak

  • 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

Oryginalna odpowiedź

Chociaż zaakceptowana odpowiedź spełnia swoje zadanie, jest bardzo powolna.

$ 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

jest prawie natychmiastowy.

Aby uzyskać listę aktualnie śledzonych plików, możesz użyć

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

To rozwiązanie pozwala uniknąć wywoływania w filecelu ustalenia rodzaju pliku i wykorzystuje grep do dopasowania pożądanego rozszerzenia ze względu na wydajność. Jeśli wszystkie pliki powinny zostać uwzględnione, po prostu usuń to z wiersza.

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

jeśli pliki mogą zawierać spacje, które są szkodliwe dla powłok, możesz użyć:

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

Podaj listę plików (przez potok), za pomocą xargs można wywołać polecenie i rozdzielić argumenty. Polecenia, które umożliwiają przetwarzanie wielu plików, pozwalają ominąć -n1. W tym przypadku dzwonimy git blame --line-porcelaini do każdego połączenia używamy dokładnie 1 argumentu.

xargs -n1 git blame --line-porcelain

Następnie filtrujemy dane wyjściowe pod kątem wystąpienia „autora”, sortujemy listę i liczymy zduplikowane linie według:

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

Uwaga

Inne odpowiedzi faktycznie odfiltrowują wiersze zawierające tylko białe znaki.

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

Powyższe polecenie spowoduje wydrukowanie autorów wierszy zawierających co najmniej jeden znak spacji. Możesz także użyć dopasowania, \w*[^\w#]które wyklucza również wiersze, w których pierwszy znak inny niż biały nie jest #(komentarz w wielu językach skryptowych).

Alex
źródło
2
@nilbus: nie możesz. echo "a\nb\nc"|xargs -n1 cmdrozwinie się docmd a; cmd b; cmd d
Alex
2
--line-porcelana już nie działa (git 1.7.5.4) zamiast tego użyj --porcelain
isoiphone
4
Użytkownicy OSX, spróbuj wykonać następujące czynności (nadal nie działa na plikach z nową linią w nazwie):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
Jeśli chcesz po prostu wszystko pod bieżącą ścieżką, na dowolną głębokość, użyj „./” jako filtru ścieżki (w miejscu, gdzie odpowiadający umieści „ / .c”).
Ben Dilts
2
Może użyj „winić -w”, aby uzyskać lepszą własność kodu, gdy kod został tylko ponownie sformatowany stackoverflow.com/questions/4112410/…
sleeplessnerd
124

Napisałem klejnot zwany git-fame, który może się przydać.

Instalacja i użytkowanie:

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

Wynik:

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
źródło
5
+1 w końcu 1, który działa i wygląda na to, że daje sensowne liczby, reszta wiersza poleceń albo nie działa na OSX z powodu niekompatybilności utils, albo daje małe liczby na moim repozytorium. To jest na OSX i ruby ​​1.9.3 (napar)
Karthik T
9
Nie bądź głupi, @tcaswell. Wskazanie czegoś przydatnego nie jest spamem, nawet jeśli zdarzyło się, że to właśnie on coś napisał.
Wayne
5
Odpowiadając na własne pytanie: git fame --exclude = ścieżki / do / plików, ścieżki / do / innych / plików
Maciej Swic
2
@Adam: Nadal masz z tym problemy? Działa dla mnie bardzo dobrze w systemie OS X 10.9.5.
Sam Dutton,
2
W przypadku każdego repo, które jest większe niż kilka, czas potrzebny na wykonanie tego klejnotu jest astronomiczny
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

Wyjaśnienie krok po kroku:

Wyświetl wszystkie pliki pod kontrolą wersji

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

Przycinaj listę tylko do plików tekstowych

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

Git obwinia wszystkie pliki tekstowe, ignorując zmiany białych znaków

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

Wyciągnij nazwiska autorów

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

Posortuj listę autorów i niech uniq policzy liczbę kolejno powtarzających się wierszy

|sort|uniq -c

Przykładowe dane wyjściowe:

   1334 Maneater
   1924 Another guy
  37195 Brian Ruby
   1482 Anna Lambda
zerowe
źródło
1
Wygląda na to, że mam inną sedwersję, moja nie rozumie -rflagi i ma problemy z regexem (narzeka na niezrównoważone pareny, nawet gdy usuwam nadwyżkę ().
Erik Aigner,
7
Nieważne, sudo brew install gnu-sedrozwiązałem to. Działa jak marzenie!
Erik Aigner,
5
Lub port install gseddla użytkowników MacPorts.
Gavin Brock
Zrobiłem sudo brew install gnu-sed(co zadziałało), ale wciąż dostaję błędy, których sed nie rozpoznaje -r. :(
Adam Tuttle
1
Na OSX po zainstalowaniu gsed przez Macports uruchomiłem to polecenie, aby działało (zastąpiono sed przez 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 summarydostarczone przez git-extras pakietu jest dokładnie to, czego potrzebują. Zapoznaj się z dokumentacją git-dodatków - git-podsumowanie :

git summary --line

Daje wynik, który wygląda następująco:

project  : TestProject
lines    : 13397
authors  :
8927 John Doe            66.6%
4447 Jane Smith          33.2%
  23 Not Committed Yet   0.2%
adius
źródło
1
Fajnie, ale nie wydaje się obsługiwać filtru ścieżki ani przynajmniej argumentu podkatalogu. Byłoby milej.
spinkus
1
Ładne i czyste rozwiązanie. Odpowiedź Alexa przyniosła bardzo małe liczby linii z jakiegoś powodu. To po prostu wyszło z pudełka. Zajęło to około 30 sekund dla ~ 200 000 linii rozłożonych na kilkaset plików.
fgblomqvist
6

Rozwiązanie Erika było niesamowite, ale miałem pewne problemy ze znakami diakrytycznymi (pomimo że moje LC_*zmienne środowiskowe są pozornie ustawione poprawnie) i szum przeciekał przez linie kodu, które faktycznie miały w nich daty. Moje sed-fu jest kiepskie, więc skończyłem z tym fragmentem Frankensteina z rubinem, ale działa dla mnie bezbłędnie na 200 000+ LOC i sortuje wyniki:

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

Zauważ też, że gsedzamiast sedtego instaluje się binarny homebrew, pozostawiając system bez zmian.

gtd
źródło
4

git shortlog -sn

Spowoduje to wyświetlenie listy zatwierdzeń według autora.

moinudin
źródło
17
Zwraca liczbę zatwierdzeń na autora, a nie liczbę wierszy.
v64
Bardzo pomocny w określeniu głównych współtwórców projektu / katalogu / pliku
Ares
4

Oto główny fragment odpowiedzi @Alex, który faktycznie wykonuje operację agregowania linii winy. Obciąłem go, aby działał na jednym pliku, a nie na zestawie plików.

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

Publikuję to tutaj, ponieważ często wracam do tej odpowiedzi i ponownie czytam post i ponownie analizuję przykłady, aby wyodrębnić część, którą cenię. Nie jest też wystarczająco ogólny dla mojego przypadku użycia; jego zakres dotyczy całego projektu C.


Lubię wyświetlać statystyki dla każdego pliku, uzyskiwane za pomocą foriteratora bash zamiast, xargsponieważ uważam, że xargs jest mniej czytelny i trudny w użyciu / zapamiętywaniu, zalety / wady xargs vs dla powinny być omówione gdzie indziej.

Oto praktyczny fragment, który wyświetli wyniki dla każdego pliku osobno:

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

I przetestowałem, uruchamianie tego polecenia w powłoce bash jest bezpieczne ctrl + c, jeśli chcesz umieścić to w skrypcie bash, być może będziesz potrzebować Trap na SIGINT i SIGTERM, jeśli chcesz, aby użytkownik mógł przerwać twoją pętlę for.

ThorSummoner
źródło
1
git blame -w -M -C -C --line-porcelain path/to/file.txt | grep -I '^author ' | sort | uniq -ic | sort -nrZnaleziono drobną korektę git blame tutaj, która dokładniej przedstawia statystyki, których szukałem. W szczególności opcja -M i -C-C (celowo są to dwa litery C). -M wykrywa ruchy w pliku, a -C -C wykrywa skopiowane linie z innych plików. Zobacz dokument tutaj . Dla kompletności, -w ignoruje spacje.
John Lee,
1

Mam to rozwiązanie, które zlicza obwiniane linie we wszystkich plikach tekstowych (z wyjątkiem plików binarnych, nawet wersjonowanych):

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
źródło
1

Działa to w dowolnym katalogu struktury źródłowej repozytorium, na wypadek, gdybyś chciał sprawdzić określony moduł źródłowy.

find . -name '*.c' | xargs -n1 git blame --line-porcelain | grep "^author "|sort|uniq -c|sort -nr
Martin G.
źródło
0

I przyjął górną odpowiedź do 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

Jest to opcja, czy uruchomić git blamez -wprzełącznikiem, dodałem go, ponieważ ignoruje whitespace zmian.

Wydajność na moim komputerze była korzystna dla Powershell (~ 50s w porównaniu do ~ 65s dla tego samego repo), chociaż rozwiązanie Bash działało pod WSL2

Matt M.
źródło
-1

Stworzyłem własny skrypt, który jest kombinacją @nilbus i @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
źródło
Dla mnie twoja sprawa enter code herepowodowała problemy ... czy to działa poprawnie?
Menios,
-1

Funkcja Bash, której celem jest uruchomienie jednego pliku źródłowego w systemie 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
źródło