Comment compter le nombre total de lignes modifiées par un auteur spécifique dans un référentiel Git?

458

Existe-t-il une commande que je peux invoquer qui comptera les lignes modifiées par un auteur spécifique dans un référentiel Git? Je sais qu'il doit y avoir des moyens de compter le nombre de validations comme le fait Github pour leur graphique d'impact.

Gav
la source
1
Vous pourriez considérer un outil célèbre qui recueille des statistiques pour le développement du noyau Linux, par exemple, le référentiel est ici git://git.lwn.net/gitdm.git.
0andriy

Réponses:

310

La sortie de la commande suivante devrait être raisonnablement facile à envoyer au script pour additionner les totaux:

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

Cela donne des statistiques pour tous les commits sur la HEAD actuelle. Si vous souhaitez ajouter des statistiques dans d'autres branches, vous devrez les fournir comme arguments git log.

Pour passer à un script, la suppression même du format "en ligne" peut être effectuée avec un format de journal vide, et comme l'a commenté Jakub Narębski, --numstatest une autre alternative. Il génère des statistiques par fichier plutôt que par ligne, mais est encore plus facile à analyser.

git log --author="<authorname>" --pretty=tformat: --numstat
CB Bailey
la source
2
Modification de ma réponse acceptée car cela donne le résultat de la manière que j'attendais et sera plus utile aux autres visiteurs qui cherchent à y parvenir.
Gav
14
Vous pouvez utiliser à la --numstatplace de --shortstatsi vous souhaitez ajouter des statistiques un peu plus facilement.
Jakub Narębski
8
Vous voudrez peut-être également ajouter "--no-merges".
yoyo
9
désolé pour ces questions, mais que me disent les chiffres? Il y a deux rangées et je n'ai aucune idée de ce qu'ils me disent. Lignes hachées et ajoutées?
Informatic0re
2
@ Informatic0re git help logme dit que les premières lignes sont ajoutées, les secondes supprimées.
ThomasH
599

Cela donne quelques statistiques sur l'auteur, modifiez au besoin.

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

Utilisation d'Awk sur 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)

Il y a un nouveau paquet sur github qui a l'air lisse et utilise bash comme dépendances (testé sur linux). Il est plus adapté à une utilisation directe qu'à des scripts.

C'est git-quick-stats (lien github) .

Copiez git-quick-statsdans un dossier et ajoutez le dossier au chemin.

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

Usage:

git-quick-stats

entrez la description de l'image ici

Alex
la source
18
Merci pour cette ravissante long-liner! Cet endroit awk a écouvillonné le deck de tout le monde (précis, rapide, pas de sortie extra bizarre). Pas étonnant, étant donné que c'est le genre de chose pour laquelle awk a été conçu ... Dommage que vous ayez été si en retard à la fête.
zxq9
4
@ zxq9: Je n'étais même pas à stackoverflow lorsque la question a été posée et j'ai été inspiré par les réponses ici. espérons que je vais lentement dépasser tout le monde ici car les gens en ont toujours besoin.
Alex
9
Cela fonctionne très bien, mais j'ai dû changer gawkpour awkle faire fonctionner dans le terminal OSX
Zach Lysobey
1
@samthebest, car le déplacement du fichier ne reflète pas des statistiques correctes. Les lignes ne sont pas modifiées. À Alex: Je parle de Git. Btw, voir mon commentaire à la question d'origine.
0andriy
2
Si l'url ne fonctionne pas pour vous, essayez ceci:git clone https://github.com/arzzen/git-quick-stats.git
Nicolas
226

Au cas où quelqu'un voudrait voir les statistiques de chaque utilisateur dans sa base de code, quelques-uns de mes collègues ont récemment proposé cet horrible one-liner:

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

(Prend quelques minutes pour parcourir notre repo, qui a environ 10-15k commits.)

Dan
la source
12
C'est génial! michael,: 6057 files changed, 854902 insertions(+), 26973 deletions(-), 827929 net
Michael J.Calkins
1
@EugenKonkov dans le code, il est défini comme des insertions - des suppressions.
Dan
13
c'est la seule commande qui donne le résultat total pour un référentiel et s'exécute sans aucun plugin.
Ömer Faruk Almalı
1
Je reçois un groupe d'utilisateurs répertoriés ensemble, presque toutes les combinaisons possibles de développeurs reviennent. bizarrerie de ma part?
Damon
2
@BenSewards vous pouvez utiliser Bash sur Windows en utilisant le sous-système Windows pour Linux, plus d'informations ici
mjsr
152

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

est un bel outil pour obtenir le nombre de tous les auteurs à la fois, y compris le nombre de fichiers de validation et de fichiers modifiés:

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

Il existe également une version Python sur https://github.com/casperdcl/git-fame (mentionnée par @fracz):

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

Exemple de sortie:

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

Mais attention: comme mentionné par Jared dans le commentaire, le faire sur un très grand référentiel prendra des heures. Je ne sais pas si cela pourrait être amélioré, étant donné qu'il doit traiter autant de données Git.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
1
C'est génial mais tellement lent
Jared Burrows
1
A bien fonctionné sur le macbook mi-2015 et le projet Android de taille moyenne (127k LoC '). Quelques minutes.
maxweber
2
@Vincent pour cent de toal loc / commits / files pour l'utilisateur actuel.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
1
Modifier la branche, le délai d'expiration et exclure un dossier:git fame --branch=dev --timeout=-1 --exclude=Pods/*
jonmecer
1
@AlexanderMills Je suppose que c'est parce que vous ne pouvez pas compter de manière significative les lignes sur les blobs
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功
103

J'ai trouvé ce qui suit utile pour voir qui avait le plus de lignes actuellement dans la base de code:

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

Les autres réponses se sont principalement concentrées sur les lignes modifiées dans les commits, mais si les commits ne survivent pas et sont remplacés, ils peuvent simplement avoir été désabonnés. L'incantation ci-dessus vous permet également de trier tous les committers par lignes au lieu d'un seul à la fois. Vous pouvez ajouter quelques options à git blame (-C -M) pour obtenir de meilleurs nombres qui prennent en compte le mouvement de fichier et le mouvement de ligne entre les fichiers, mais la commande peut s'exécuter beaucoup plus longtemps si vous le faites.

De plus, si vous recherchez des lignes modifiées dans toutes les validations pour tous les validateurs, le petit script suivant est utile:

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

mmrobins
la source
31
J'étais sur le point de donner un +1, mais j'ai réalisé que la solution dépend du rubis ... :(
mac
3
Vous pouvez le modifier pour ne pas utiliser ruby ​​assez facilement car j'utilise simplement ruby ​​pour la substitution de chaîne. Vous pouvez utiliser perl, sed, python, etc.
mmrobins
21
ne fonctionne pas pour moi: -e: 1: dans `<main> ': séquence d'octets invalide en UTF-8 (ArgumentError)
Michał Dębski
1
/^.*\((.*?)\s[\d]{4}/devrait être /^.*?\((.*?)\s[\d]{4}/d'empêcher la correspondance des parenthèses dans la source en tant qu'auteur.
Timothy Gu
1
mmm mes exécutions ont montré beaucoup d'utilisateurs qui n'existent même pas, en raison d'une mauvaise analyse. Je pense que ce n'est pas une réponse fiable.
mjsr
92

Pour compter le nombre de validations d'un auteur donné (ou de tous les auteurs) sur une branche donnée, vous pouvez utiliser git-shortlog ; voir en particulier ses options --numberedet ses --summaryoptions, par exemple lorsqu'il est exécuté sur le dépôt 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
la source
2
Notez que v1.6.4c'est ici dans cet exemple pour rendre la sortie déterministe: ce sera la même peu importe quand avez-vous cloné et / ou récupéré à partir du référentiel git.
Jakub Narębski
dont v1.6.4me donne:fatal: ambiguous argument 'v1.6.4': unknown revision or path not in the working tree.
Vlad l'Impala
5
Ah, non, j'ai raté "lors de l'exécution sur le dépôt git". Pour être juste, la plupart des gens n'exécuteront pas cette commande sur le dépôt git. Par une assez grande marge, en fait.
Vlad l'Impala
4
git shortlog -sneou, si vous préférez ne pas inclure les fusionsgit shortlog -sne --no-merges
Mark Swardstrom
1
@Swards: -sest --summary, -nest --numberedet [nouveau] -eest --emaild'afficher les e-mails des auteurs (et de compter séparément le même auteur avec une adresse e-mail différente, en tenant compte des .mailmapcorrections). Bon appel --no-merges.
Jakub Narębski
75

Après avoir regardé la réponse d' Alex et de Gerty3000 , j'ai essayé de raccourcir le one-liner:

Fondamentalement, l'utilisation de git log numstat et le non- suivi du nombre de fichiers ont changé.

Git version 2.1.0 sur 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

Exemple:

Jared Burrows   added lines: 6826, removed lines: 2825, total lines: 4001
Jared Burrows
la source
Je ne peux pas en faire un alias :-(
brat
33

La réponse d' AaronM en utilisant le shell one-liner est bonne, mais en fait, il y a encore un autre bug, où les espaces corrompent les noms d'utilisateur s'il y a différentes quantités d'espaces blancs entre le nom d'utilisateur et la date. Les noms d'utilisateurs corrompus donneront plusieurs lignes pour le nombre d'utilisateurs et vous devez les résumer vous-même.

Ce petit changement a résolu le problème pour moi:

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

Notez le + après \ s qui consommera tous les espaces blancs du nom à la date.

En fait, ajouter cette réponse autant pour ma propre mémoire que pour aider quelqu'un d'autre, car c'est au moins la deuxième fois que je google sur le sujet :)

  • Modifier 2019-01-23 Ajouté --show-emailà git blame -wagréger sur le courrier électronique à la place, car certaines personnes utilisent des Nameformats différents sur différents ordinateurs, et parfois deux personnes portant le même nom travaillent dans le même git.
Erik Zivkovic
la source
Cette réponse en utilisant Perl semblait un peu meilleure que celle à base de rubis. Ruby s'étouffa sur des lignes qui n'étaient pas du texte UTF-8 réel, perl ne se plaignit pas. Mais Perl a-t-il fait la bonne chose? Je ne sais pas.
Stéphane Gourichon
Les sous-modules en résultent unsupported file typemais sinon cela semble fonctionner correctement même avec eux (il les saute).
Vladimír Čunát
24

Voici une courte ligne unique qui produit des statistiques pour tous les auteurs. C'est beaucoup plus rapide que la solution de Dan ci-dessus sur https://stackoverflow.com/a/20414465/1102119 (la mienne a la complexité temporelle O (N) au lieu de O (NM) où N est le nombre de validations et M le nombre d'auteurs ).

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
la source
4
Bien mais que signifie la sortie?
Gary Willoughby
Vous devriez ajouter --no-show-signature, sinon les personnes qui signent pgp leurs commits ne seront pas comptées.
Philihp Busby
2
ins [a] - del [a], ins [a], del [a], a, donc si j'ai raison insertion-suppression, insertion, suppression, nom
MrKekson
Comment puis-je ajouter cette commande à ma configuration git pour pouvoir l'appeler avec "git count-lines"?
takanuva15
Qu'à cela ne tienne, j'ai tout compris: 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". (Notez que je suis sous Windows; vous devrez peut-être utiliser différents types de citations)
takanuva15
21

@mmrobins @AaronM @ErikZ @JamesMishra a fourni des variantes qui ont toutes un problème en commun: ils demandent à git de produire un mélange d'informations non destinées à la consommation de scripts, y compris le contenu de la ligne du référentiel sur la même ligne, puis associent le désordre avec une expression rationnelle .

C'est un problème lorsque certaines lignes ne sont pas du texte UTF-8 valide, et aussi lorsque certaines lignes correspondent à l'expression rationnelle (cela s'est produit ici).

Voici une ligne modifiée qui n'a pas ces problèmes. Il demande à git de sortir les données proprement sur des lignes séparées, ce qui facilite le filtrage robuste de ce que nous voulons:

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

Vous pouvez grep pour d'autres chaînes, comme author-mail, committer, etc.

Peut-être faites d'abord export LC_ALL=C(en supposant bash) pour forcer le traitement au niveau des octets (cela se produit également pour accélérer considérablement grep à partir des paramètres régionaux basés sur UTF-8).

Stéphane Gourichon
la source
Belle ligne là-bas, très cool, que vous pouvez facilement mélanger, mais cela ne fait pas ce que l'affiche originale demandait, fournissez un décompte par auteur de git. Bien sûr, vous pouvez l'exécuter et faire un wc-l, etc., mais vous devrez ensuite répéter pour chaque auteur du référentiel.
AaronM
1
@AaronM Je ne comprends pas vos critiques. Cette ligne AFAIK produit les mêmes statistiques que la vôtre, mais en plus robuste. Donc, si ma réponse "ne fait pas ce que l'affiche originale demandait, fournissez un décompte par auteur de git", alors la vôtre encore plus. Veuillez m'éclairer.
Stéphane Gourichon
désolé d'avoir mal lu, je pensais que la commande devait être modifiée pour chaque nom d'auteur différent. Votre commentaire sur grep pour d'autres cordes m'a conduit là-bas mais c'était mon malentendu.
AaronM
C'est tellement génial. Merci!
Tek
16

Une solution a été donnée avec ruby ​​au milieu, perl étant un peu plus disponible par défaut voici une alternative utilisant perl pour les lignes actuelles par auteur.

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
la source
5
La regex mise à jour ne fait pas de différence significative, et elle est cassée car vous n'avez pas échappé au premier paren. Cependant, je peux voir certains cas où mon précédent pourrait trouver des bits dans la ligne de code sur lesquels se verrouiller. Cela fonctionnerait de manière plus fiable: 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
merci d'avoir essayé de créer une expression rationnelle plus fiable. Voir ma réponse pour une variante plus robuste stackoverflow.com/a/36090245/1429390
Stéphane Gourichon
13

En plus de la réponse de Charles Bailey , vous voudrez peut-être ajouter le-C paramètre aux commandes. Sinon, les renommages de fichiers comptent comme de nombreux ajouts et suppressions (autant que le fichier comporte des lignes), même si le contenu du fichier n'a pas été modifié.

Pour illustrer, voici un commit avec beaucoup de fichiers déplacés depuis l'un de mes projets, lors de l'utilisation de la git log --oneline --shortstatcommande:

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

Et voici le même commit en utilisant la git log --oneline --shortstat -Ccommande qui détecte les copies de fichiers et les renomme:

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

À mon avis, ce dernier donne une vision plus réaliste de l'impact qu'une personne a eu sur le projet, car renommer un fichier est une opération beaucoup plus petite que l'écrire à partir de zéro.

Esko Luontola
la source
2
Quand j'exécute "git log --oneline --shortstat", je n'obtiens pas votre résultat. J'ai une liste de commit avec le nombre d'éditions mais pas le nombre total. Comment puis-je obtenir le nombre total de lignes éditées dans tous les dépôts git?
Mehdi
12

vous pouvez utiliser whodid ( https://www.npmjs.com/package/whodid )

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

et

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

ou tapez simplement

$ whodid

alors vous pouvez voir un résultat comme celui-ci

Contribution state
=====================================================
 score  | author
-----------------------------------------------------
 3059   | someguy <[email protected]>
 585    | somelady <[email protected]>
 212    | niceguy <[email protected]>
 173    | coolguy <[email protected]>
=====================================================
victor.cheval
la source
Que signifie «score»?
user11171
@Volte npm i n'est qu'un raccourci pour l'installation de npm
Michiel
Oui, je suis au courant. Mon -gdevait venir avant le nom du paquet, le macOS. J'essaye simplement d'aider.
Volte
11

Voici un script ruby ​​rapide qui corrige l'impact par utilisateur contre une requête de journal donnée.

Par exemple, pour rubinius :

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

le scénario:

#!/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
la source
2
Ce script est génial, mais exclut les auteurs qui n'ont que des validations sur une seule ligne! Pour corriger, changez comme suit: changes = / (\ d +) insertion. * (\ D +) suppression / .match (ligne)
Larry Gritz
9

c'est la meilleure façon et cela vous donne également une image claire du nombre total de commits par tous les utilisateurs

git shortlog -s -n
edrich13
la source
2
Utile, mais c'est le nombre de
validations et
5

J'ai fourni une modification d'une réponse courte ci-dessus, mais ce n'était pas suffisant pour mes besoins. J'avais besoin de pouvoir catégoriser les lignes validées et les lignes dans le code final. Je voulais aussi une ventilation par fichier. Ce code n'est pas récurrent, il ne renverra que les résultats pour un seul répertoire, mais c'est un bon début si quelqu'un veut aller plus loin. Copiez et collez dans un fichier et exécutez-le ou exécutez-le avec 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 = <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
la source
Je reçois cette erreur: division illégale par zéro à la ligne 71 de x.pl.
Vivek Jha
Résolution de la division illégale par zéro à la ligne 71. Pensez que cela se produit s'il n'y a pas de modifications, mais il y a quelque temps, j'ai écrit ceci.
AaronM
2

Pour les utilisateurs de Windows, vous pouvez utiliser le script de commandes suivant qui compte les lignes ajoutées / supprimées pour l'auteur spécifié

@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
la source
2

Voici un super repo qui vous facilite la vie

git-quick-stats

Sur un Mac avec brew installé

brew install git-quick-stats

Courir

git-quick-stats

Choisissez simplement l'option que vous souhaitez dans cette liste en tapant le numéro indiqué et en appuyant sur Entrée.

 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
la source
1

Ce script ici le fera. Mettez-le dans authorhip.sh, chmod + x it, et vous êtes prêt.

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

la source
1
non ça ne va PAS!, vous avez posté ça ailleurs, ça génère des erreurs sur macs et linux, vous savez, le type d'ordinateurs sur lequel git a été fait!
Pizzaiola Gorgonzola
1

Enregistrez vos journaux dans un fichier en utilisant:

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

Pour les amateurs de 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')

Vos sorties seraient comme:

225        files changed
6751       insertions
1379       deletions
Amen Ayach
la source
0

Vous voulez blâmer Git .

Il y a une option --show-stats pour imprimer des statistiques.

gbjbaanb
la source
J'ai essayé blame, mais cela n'a pas vraiment donné les statistiques dont je pensais que l'OP aurait besoin?
CB Bailey
Merci, cela m'a aussi aidé avec .mailmap!
Gav
0

La question posée des informations sur une spécifique auteur , mais la plupart des réponses étaient des solutions qui renvoyaient des listes classées d'auteurs en fonction de leurs lignes de code modifiées.

C'est ce que je cherchais, mais les solutions existantes n'étaient pas tout à fait parfaites. Dans l'intérêt des personnes susceptibles de trouver cette question via Google, j'ai apporté quelques améliorations à celles-ci et en ai fait un script shell, que j'affiche ci-dessous. Un annoté (que je continuerai à maintenir) peut être trouvé sur mon Github .

Il n'y a pas de dépendances sur Perl ou Ruby. De plus, les espaces blancs, les renommages et les mouvements de ligne sont pris en compte dans le nombre de changements de ligne. Mettez-le simplement dans un fichier et passez votre référentiel Git comme premier paramètre.

#!/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
la source
0

Le meilleur outil que j'ai identifié jusqu'à présent est gitinspector. Il donne le rapport défini par utilisateur, par semaine, etc. Vous pouvez installer comme ci-dessous avec npm

npm install -g gitinspector

Les liens pour obtenir plus de détails

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

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

https://github.com/ejwa/gitinspector

des exemples de commandes sont

gitinspector -lmrTw 
gitinspector --since=1-1-2017 etc
Ravikiran Reddy Kotapati
la source
0

J'ai écrit ce script Perl pour accomplir cette tâche.

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

Je l'ai nommé git-line-changes-by-authoret mis /usr/local/bin. Parce qu'il est enregistré sur mon chemin, je peux lancer la commande git line-changes-by-author --before 2018-12-31 --after 2020-01-01pour obtenir le rapport pour l'année 2019. Par exemple. Et si je devais mal orthographier le nom git suggérera l'orthographe appropriée.

Vous souhaiterez peut-être ajuster le _get_repo_slugsous - marin pour n'inclure que la dernière partie duremote.origin.url fichier car mes référentiels sont enregistrés sous project/repoet le vôtre pourrait ne pas l'être.

joehep
la source