log0
log0

Reputation: 10917

Git contributors of each file

I want to list every contributors for each file in the repository.

Here is currently what I do:

find . | xargs -L 1 git blame -f | cut -d' ' -f 2-4 | sort | uniq

This is very slow. Is there a better solution ?

Upvotes: 8

Views: 2963

Answers (5)

jthill
jthill

Reputation: 60255

Don't use --stat if you don't need the stats, why ask it to rerun all the diffs, then scrape all the results off? Just use --name-only.

git log --all --pretty=%x09%cN --name-only |  awk -F$'\t' '
        NF==2   { name=$2 }
        NF==1   { contribs[ $0 ][ name ] = 1 }
        END     {
                n = asorti(contribs,sorted)
                for ( i=0 ; ++i < n ; ) {
                        file = sorted[i]
                        print file
                        for ( name in contribs[file] ) print "\t"name
                }
        }
'

Upvotes: 0

Meghana Randad
Meghana Randad

Reputation: 450

git log --pretty=format:"%cn" <filename> | sort | uniq -c

You can also do more with git log, Ex: committers to each file after certain date (Ex: after 2018-10-1): git log --after="2018-10-1" --pretty=format:"%cn" <filename> | sort | uniq -c

Ref: https://www.atlassian.com/git/tutorials/git-log

Upvotes: 0

igor
igor

Reputation: 2100

I would write a small script that analyzes the output of git log --stat --pretty=format:'%cN'; something along the lines of:

#!/usr/bin/env perl

my %file;
my $contributor = q();

while (<>) {
    chomp;
    if (/^\S/) {
        $contributor = $_;
    }
    elsif (/^\s*(.*?)\s*\|\s*\d+\s*[+-]+/) {
        $file{$1}{$contributor} = 1;
    }
}

for my $filename (sort keys %file) {
    print "$filename:\n";
    for my $contributor (sort keys %{$file{$filename}}) {
        print "  * $contributor\n";
    }
}

(Written just quickly; does not cover cases like binary files.)

If you stored this script, e.g., as ~/git-contrib.pl, you could call it with:

git log --stat=1000,1000 --pretty=format:'%cN' | perl ~/git-contrib.pl

Advantage: call git only once, which implies that it is reasonably fast. Disadvantage: it’s a separate script.

Upvotes: 6

CharlesB
CharlesB

Reputation: 90286

Taking ДМИТРИЙ's answer as a base, I'd say the following :

git ls-tree -r --name-only master ./ | while read file ; do
    echo "=== $file"
    git log --follow --pretty=format:%an -- $file | sort | uniq
done

Enhancement is that it follows file's rename in its history, and behaves correctly if files contain spaces (| while read file)

Upvotes: 8

tldr:

for file in `git ls-tree -r --name-only master ./`; do
    echo $file
    git shortlog -s -- $file | sed -e 's/^\s*[0-9]*\s*//'
done
  1. You can get all tracked files in repository with git ls-tree. Find is really bad choice.

    For example, get list of tracked file in branch master in current dir (./):

    git ls-tree -r --name-only master ./
    
  2. You can get list of file editors with get shortlog (git blame is overkill):

    git shortlog -s -- $file
    

So, for each file from ls-tree response you should call shortlog and modify its output however you want.

Upvotes: 3

Related Questions