web-dev-qa-db-ja.com

Gitリポジトリの特定の作者によって変更された総行数を数える方法は?

Gitリポジトリの特定の作者によって変更された行をカウントするために呼び出すことができるコマンドはありますか? Githubが彼らの影響グラフのためにこれをするようにコミットの数を数える方法がなければならないことを私は知っています。

399
Gav

次のコマンドの出力は、合計を合計するためにスクリプトに送るのがかなり簡単なはずです。

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

これは現在のHEAD上のすべてのコミットの統計を与えます。他のブランチで統計を合計したい場合は、それらをgit logへの引数として提供する必要があります。

スクリプトに渡すために、 "oneline"フォーマットでさえ削除することは空のログフォーマットで行うことができます、そしてJakubNarębskiによってコメントされているように、--numstatは別の選択肢です。行ごとの統計ではなくファイルごとの統計を生成しますが、解析がさらに簡単です。

git log --author="<authorname>" --pretty=tformat: --numstat
278
CB Bailey

これは作者に関するいくつかの統計を与えます。必要に応じて修正してください。

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

Mac OSXでAwkを使う:

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

編集(2017)

Githubには滑らかに見え、依存関係としてbashを使用する新しいパッケージがあります(Linuxでテスト済み)。スクリプトよりも直接使用に適しています。

それは git-quick-stats(githubリンク)です

git-quick-statsをフォルダにコピーし、そのフォルダをpathに追加します。

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

使用法:

git-quick-stats

enter image description here

541
Alex

誰かが自分のコードベースですべてのユーザーの統計情報を見たい場合は、私の同僚2人が最近この恐ろしいワンライナーを思い付きました。

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

(私たちのリポジトリを締めくくるのに数分かかる、それには約10-15kのコミットがある。)

179
Dan

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

コミットや変更されたファイルの数を含め、すべての作者の数を一度に取得するための素晴らしいツールです。

Sudo apt-get install Ruby-dev
Sudo gem install git_fame
cd /path/to/gitdir && git fame

https://github.com/casperdcl/git-fame にPythonバージョンもあります(@fraczで言及)

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

出力例:

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

しかし、注意してください。コメントでJaredが述べたように、非常に大きなリポジトリでそれをすることは数時間かかるでしょう。 Gitデータを大量に処理する必要があることを考えると、それが改善されるかどうかはわかりません。

現在コードベースに含まれている行の数が最も多い人を確認するには、次のような方法が便利です。

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

他の答えは主にコミットで変更された行に焦点を当てていますが、コミットが生き残らず上書きされた場合、それらは単に解約されたのかもしれません。上記の呪文は、一度に1つだけではなく、すべてのコミッターを行でソートすることもできます。 git blame(-C -M)にいくつかのオプションを追加して、ファイルの移動やファイル間の行の移動を考慮に入れたより良い数値を得ることができますが、コマンドを実行すると実行時間が長くなります。

また、すべてのコミッターのすべてのコミットで変更された行を探している場合は、次の小さなスクリプトが役立ちます。

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

102
mmrobins

与えられたブランチ上の与えられた作者(または全ての作者)によるコミットの数を数えるには git-shortlog を使います。特にその--numbered--summaryオプションを見てください。 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
87
Jakub Narębski

AlexのGerty3000の答えを見て、私はワンライナーを短くする:

基本的に、git log numstatとnotを使って、ファイルの数を追跡します かわった。

Mac OS X上のGitバージョン2.1.0

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

例:

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

シェルワンライナーを使った AaronM からの 答え は良いのですが、実際には、さらにもう1つのバグがあります。ユーザー名と日付の間にスペースの量が異なると、スペースによってユーザー名が破損することがあります。破損したユーザー名はユーザーカウントのために複数の行を与えるでしょう、そしてあなたはそれらをあなた自身で合計しなければなりません。

この小さな変更で私の問題は解決しました。

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

名前の後から日付までのすべての空白を消費する\ sの後の+に注意してください。

実はこの答えを他の誰かを助けることと同じくらい私自身の記憶のために追加しています。

  • 編集2019-01-23電子メールで集計するために--show-emailgit blame -wを追加しました。これは、異なるコンピュータで異なるNamename__の形式を使用する人がいます。同じGit。
25
Erik Zivkovic

これは、すべての作者のための統計を生成する短いワンライナーです。 https://stackoverflow.com/a/20414465/1102119 でのDanの解決策よりもはるかに高速です(私のものは時間の複雑さ O(N) ではなく O(NM) ここで、Nはコミット数、Mは作者数です。

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
22
kccqzy

@mmrobins @AaronM @ErikZ @JamesMishraは、共通の問題を抱えている亜種を提供しました。それらは、同じ行のリポジトリからの行の内容を含む、スクリプトの使用を意図していない情報の混合を生成してから、混乱を正規表現で照合する。

これは、いくつかの行が有効なUTF-8テキストではないとき、そしていくつかの行が正規表現に一致するときにも問題になります(これはここで起こりました)。

これはこれらの問題を抱えていない修正された行です。データを別々の行にきれいに出力するようにgitに要求します。これにより、欲しいものを確実にフィルタリングすることが容易になります。

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

作者のメール、コミッターなど、他の文字列をgrepすることができます。

おそらく最初にexport LC_ALL=Cbashと仮定)を実行してバイトレベルの処理を強制します(これはUTF-8ベースのロケールからのgrepを非常に速くするためにも起こります)。

21

解決策は途中でRubyで与えられました、ここでデフォルトでもう少し利用可能なPerlが作者によって現在の行のためにPerlを使用する代替案です。

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

Charles Baileyの答え に加えて、コマンドに-Cパラメータを追加することをお勧めします。それ以外の場合、ファイルの内容が変更されていなくても、ファイルの名前変更は追加と削除(ファイルに行が含まれているのと同じくらいの数)としてカウントされます。

git log --oneline --shortstatコマンドを使用したときに、私のプロジェクトの1つからたくさんのファイルが移動される コミット です。

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

そしてここで、ファイルのコピーを検出し名前を変更するgit log --oneline --shortstat -Cコマンドを使用した同じコミットがあります。

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

私の意見では、ファイルの名前を変更する方が最初からファイルを書くよりもはるかに小さい操作であるため、後者は人がプロジェクトにどれほどの影響を与えたかについてのより現実的な見解を与えます。

13
Esko Luontola

これは、指定されたログクエリーに対するユーザーごとの影響を軽減する簡単なRubyスクリプトです。

例えば、 rubinius の場合、

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

スクリプト:

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

あなたはwhodidを使用することができます( https://www.npmjs.com/package/whodid

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

そして

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

または単に入力する

$ whodid

そして、あなたはこのような結果を見ることができます

Contribution state
=====================================================
 score  | author
-----------------------------------------------------
 3059   | someguy <[email protected]>
 585    | somelady <[email protected]>
 212    | niceguy <[email protected]>
 173    | coolguy <[email protected]>
=====================================================
9
victor.cheval

これは最善の方法であり、それはまたあなたにすべてのユーザーによるコミットの総数の明確な絵を与えます

git shortlog -s -n
8
edrich13

私は上記の短い答えの修正を提供しました、しかしそれは私のニーズには十分ではありませんでした。コミットされた行と最終的なコードの行の両方を分類できるようにする必要がありました。私はまたファイルによる内訳を望んでいました。このコードは再帰的なものではありません、それは単一のディレクトリの結果を返すだけですが、誰かがさらに行きたがっているならそれは良いスタートです。ファイルにコピーして貼り付けて実行可能にするか、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};
}
5
AaronM

以下を使用してログをファイルに保存します。

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

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

あなたの出力は以下のようになります。

225        files changed
6751       insertions
1379       deletions
1
Amen Ayach

このスクリプトはここでそれを行います。それをauthorship.shに入れて、chmod + xにすれば、すべて完了です。

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

質問は特定の作者に関する情報を求めましたが、答えの多くは変更されたコードの行に基づいて作者のランク付けされたリストを返す解決策でした。

これが私が探していたものでしたが、既存の解決策は全く完璧ではありませんでした。グーグルを通してこの質問を見つけるかもしれない人々の利益のために、私は彼らにいくつかの改良を加えて、そして私が以下に表示するシェルスクリプトにそれらを作りました。注釈付きのもの(私はこれからも維持します)は私のGithubで 見つけることができます

PerlとRubyのどちらにもnoの依存関係はありません。さらに、空白、名前の変更、および行の移動が行の変更数で考慮されます。これをファイルに入れてGitリポジトリを最初のパラメータとして渡します。

#!/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
0
James Mishra

これまで私が確認した中で最高のツールはgitinspectorです。それは、ユーザごとに、週ごとなどに設定されたレポートを提供します。npmで以下のようにインストールすることができます。

npm install -g gitinspector

詳細を見るためのリンク

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

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

https://github.com/ejwa/gitinspector

コマンド例は

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

あなたは欲しい Git blame

いくつかの、よく、統計を印刷するための--show-statsオプションがあります。

0
gbjbaanb