Statistiques de Git Blame Commit


198

Comment puis-je «abuser» du blâme (ou d'une fonction mieux adaptée, et / ou en conjonction avec des commandes shell) pour me donner une statistique de la quantité de lignes (de code) actuellement dans le référentiel provenant de chaque committer?

Exemple de sortie:

Committer 1: 8046 Lines
Committer 2: 4378 Lines

11
Il devrait vraiment y avoir une commande intégrée pour cela ... il y a des commandes pour des cas d'utilisation beaucoup moins courants.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

@CiroSantilli mais il est facile d'ajouter un shellscript invocable depuis git.
Alex

doublon possible de Comment compter le nombre total de lignes modifiées par un auteur spécifique dans un référentiel Git? car il peut être facilement réduit à celui-là: il suffit de
parcourir

1
cela est assez impressionnant code.google.com/p/gitinspector surtout si vous classement des missions par des équipes d'étudiants (grands projets pourront ne pas soumettre ... il est lent , car il accuse chaque fichier)
sehe

Réponses:


166

Mettre à jour

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

J'ai mis à jour certaines choses en cours de route.

Pour plus de commodité, vous pouvez également mettre ceci dans sa propre commande:

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

stocker quelque part sur votre chemin ou modifier votre chemin et l'utiliser comme

  • 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

Réponse originale

Bien que la réponse acceptée fasse l'affaire, elle est très lente.

$ 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

est presque instantané.

Pour obtenir une liste des fichiers actuellement suivis, vous pouvez utiliser

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

Cette solution évite d'appeler filepour déterminer le type de fichier et utilise grep pour faire correspondre l'extension souhaitée pour des raisons de performances. Si tous les fichiers doivent être inclus, supprimez-les simplement de la ligne.

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

si les fichiers peuvent contenir des espaces, ce qui est mauvais pour les shells, vous pouvez utiliser:

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

Donnez une liste de fichiers (via un canal), vous pouvez utiliser xargs pour appeler une commande et distribuer les arguments. Les commandes qui permettent de traiter plusieurs fichiers obmettent le -n1. Dans ce cas, nous appelons git blame --line-porcelainet pour chaque appel, nous utilisons exactement 1 argument.

xargs -n1 git blame --line-porcelain

Nous filtrons ensuite la sortie pour les occurrences de "auteur" trions la liste et comptons les lignes en double par:

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

Remarque

D'autres réponses filtrent en fait les lignes qui ne contiennent que des espaces blancs.

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

La commande ci-dessus imprimera les auteurs de lignes contenant au moins un caractère non blanc. Vous pouvez également utiliser match \w*[^\w#]qui exclura également les lignes où le premier caractère non blanc n'est pas un #(commentaire dans de nombreux langages de script).


2
@nilbus: vous ne pouvez pas. echo "a\nb\nc"|xargs -n1 cmdsera étendu àcmd a; cmd b; cmd d
Alex

2
--line-porcelain ne semble plus fonctionner (git 1.7.5.4) à la place, utilisez --porcelain
isoiphone

4
Utilisateurs d'OSX, essayez ce qui suit (ne fonctionne toujours pas sur les fichiers avec des retours à la ligne en leur nom):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
Si vous voulez juste que tout soit sous le chemin actuel, à n'importe quelle profondeur, utilisez "./" comme filtre de chemin (où le répondeur a mis " / .c").
Ben Dilts

2
Utilisez peut-être "blame -w" pour obtenir une meilleure propriété du code lorsque le code a été uniquement reformaté stackoverflow.com/questions/4112410/…
sleeplessnerd

124

J'ai écrit un joyau appelé git-fame qui pourrait être utile.

Installation et utilisation:

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

Production:

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 enfin 1 qui fonctionne et qui semble donner des nombres sensibles, les autres en ligne de commande ne fonctionnent pas sur OSX en raison de l'incompatibilité des utilitaires ou donnent des numéros minuscules sur mon dépôt. C'est sur OSX et ruby ​​1.9.3 (brew)
Karthik T

9
Ne sois pas stupide, @tcaswell. Ce n'est pas du spam que de pointer quelque chose d'utile, même si c'est vous qui avez écrit ce quelque chose.
Wayne

5
Répondre à ma propre question: git fame --exclude = chemins / vers / fichiers, chemins / vers / autres / fichiers
Maciej Swic

2
@Adam: Avez-vous toujours des problèmes avec cela? Fonctionne très bien pour moi sur OS X 10.9.5.
Sam Dutton du

2
Pour tout dépôt supérieur à quelques engagements, le temps nécessaire à ce bijou est astronomique
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

Explication étape par étape:

Lister tous les fichiers sous contrôle de version

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

Taillez la liste uniquement aux fichiers texte

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

Git blâme tous les fichiers texte, ignorant les changements d'espaces

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

Retirez les noms des auteurs

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

Triez la liste des auteurs et demandez à uniq de compter le nombre de lignes répétitives consécutives

|sort|uniq -c

Exemple de sortie:

   1334 Maneater
   1924 Another guy
  37195 Brian Ruby
   1482 Anna Lambda

1
Il semble que j'ai une sedversion différente , la mienne ne comprend pas le -rdrapeau et a des problèmes avec l'expression régulière (se plaint de parens déséquilibrés, même lorsque j'enlève le surplus ().
Erik Aigner

7
Peu importe, sudo brew install gnu-sedje l' ai résolu. Fonctionne comme un charme!
Erik Aigner

5
Ou port install gsedpour les utilisateurs de MacPorts.
Gavin Brock

J'ai fait un sudo brew install gnu-sed(qui a fonctionné) mais je reçois toujours des erreurs que sed ne reconnaît pas -r. :(
Adam Tuttle

1
Sur OSX après l'installation de gsed via macports, j'ai exécuté cette commande pour le faire fonctionner (remplacé sed par 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 summaryfourni par le paquet git-extras est exactement ce dont vous avez besoin. Consultez la documentation sur git-extras - git-summary :

git summary --line

Donne une sortie qui ressemble à ceci:

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

1
Bien, mais ne semble pas prendre en charge un filtre de chemin, ou au moins un argument de sous-répertoire. Ce serait mieux.
spinkus

1
Solution agréable et propre. @ La réponse d'Alex a donné un très petit nombre de lignes pour une raison quelconque. Cela a juste fonctionné hors de la boîte. A pris quelque chose comme 30 secondes pour ~ 200k lignes réparties sur quelques centaines de fichiers.
fgblomqvist

6

La solution d'Erik était géniale, mais j'ai eu quelques problèmes avec les signes diacritiques (malgré le fait que mes LC_*variables d'environnement soient définies ostensiblement correctement) et le bruit qui fuyait sur les lignes de code qui contenaient en fait des dates. Mon sed-fu est pauvre, donc je me suis retrouvé avec cet extrait de frankenstein avec du rubis, mais cela fonctionne parfaitement pour moi sur 200 000+ LOC, et il trie les résultats:

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

Notez également gsedau lieu de sedparce que c'est l'installation homebrew binaire, laissant le système sed intact.


4

git shortlog -sn

Cela affichera une liste de commits par auteur.


17
Cela renvoie le nombre de validations par auteur, pas le nombre de lignes.
v64

Très utile pour déterminer les principaux contributeurs à un projet / répertoire / fichier
Ares

4

Voici l'extrait principal de la réponse de @Alex qui fait en fait l'opération d'agrégation des lignes de blâme. Je l'ai réduit pour fonctionner sur un seul fichier plutôt que sur un ensemble de fichiers.

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

Je poste ceci ici parce que je reviens souvent à cette réponse et que je relis le message et que je résume les exemples pour extraire la partie que j'apprécie. Il n'est pas non plus assez générique pour mon cas d'utilisation; sa portée est pour un projet C entier.


J'aime répertorier les statistiques par fichier, obtenues via un foritérateur bash au lieu de xargstrouver des xargs moins lisibles et difficiles à utiliser / à mémoriser, les avantages / inconvénients de xargs vs pour devraient être discutés ailleurs.

Voici un extrait pratique qui affichera les résultats pour chaque fichier individuellement:

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

Et j'ai testé, exécuter cette stright dans un shell bash est sûr ctrl + c, si vous avez besoin de mettre cela dans un script bash, vous devrez peut-être intercepter sur SIGINT et SIGTERM si vous voulez que l'utilisateur puisse rompre votre boucle for.


1
git blame -w -M -C -C --line-porcelain path/to/file.txt | grep -I '^author ' | sort | uniq -ic | sort -nrJ'ai trouvé un léger ajustement à l' git blame ici qui décrit plus précisément les statistiques que je cherchais. Plus précisément, l'option -M et -C -C (ce sont deux C à des fins). -M détecte les déplacements dans le fichier et -C -C détecte les lignes copiées à partir d'autres fichiers. Voir le doc ici . Par souci d'exhaustivité, -w ignore les espaces blancs.
John Lee


1

J'ai cette solution qui compte les lignes blâmées dans tous les fichiers texte (à l'exclusion des fichiers binaires, même ceux versionnés):

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

Cela fonctionne dans n'importe quel répertoire de la structure source du dépôt, au cas où vous voudriez inspecter un certain module source.

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

0

J'ai adopté la meilleure réponse à 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

Il est facultatif de savoir si vous exécutez git blameavec le -wcommutateur, je l'ai ajouté car il ignore les changements d'espaces.

Les performances sur ma machine étaient en faveur de Powershell (~ 50s vs ~ 65s pour le même repo), bien que la solution Bash fonctionnait sous WSL2


-1

J'ai créé mon propre script qui est une combinaison de @nilbus et @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

Pour moi, votre truc enter code herecausait des problèmes .... est-ce que cela fonctionne correctement?
Menios

-1

Fonction Bash qui cible un seul fichier source exécuté sur 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
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.