Git Blame Commit Statistics


198

ฉันจะ "ตำหนิ" ตำหนิ (หรือฟังก์ชันที่เหมาะสมยิ่งขึ้นและ / หรือใช้ร่วมกับคำสั่งเชลล์) เพื่อให้สถิติเกี่ยวกับจำนวนบรรทัด (รหัส) ในพื้นที่เก็บข้อมูลที่มาจากผู้ส่งแต่ละคนได้อย่างไร

ตัวอย่างผลลัพธ์:

Committer 1: 8046 Lines
Committer 2: 4378 Lines

11
ควรมีคำสั่งในตัวสำหรับการ ... มีคำสั่งสำหรับกรณีการใช้งานทั่วไปที่น้อยกว่ามาก
Ciro Santilli 郝海东冠状病六四事件法轮功

@CiroSantilli แต่มันง่ายที่จะเพิ่ม shellscript ที่ invocable จาก git
อเล็กซ์


1
นี้เป็นที่น่ากลัวมากทีเดียวcode.google.com/p/gitinspectorโดยเฉพาะอย่างยิ่งถ้าคุณได้รับมอบหมายให้คะแนนโดยทีมงานของนักเรียน (โครงการขนาดใหญ่ไม่จำเป็นต้องใช้ ... มันช้าเพราะโทษแต่ละไฟล์ของแต่ละบุคคล)
sehe

คำตอบ:


166

ปรับปรุง

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

ฉันอัพเดตบางสิ่งระหว่างทาง

เพื่อความสะดวกคุณสามารถใส่คำสั่งนี้ลงในคำสั่งของตนเองได้

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

เก็บที่นี่ในเส้นทางของคุณหรือปรับเปลี่ยนเส้นทางของคุณและใช้มันเหมือน

  • 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

คำตอบเดิม

ในขณะที่คำตอบที่ยอมรับทำงานได้ช้ามาก

$ 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

เกือบจะทันที

หากต้องการรับรายการไฟล์ที่ถูกติดตามในปัจจุบันคุณสามารถใช้

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

วิธีนี้หลีกเลี่ยงการโทรfileเพื่อกำหนดประเภทไฟล์และใช้ grep เพื่อจับคู่ส่วนขยายที่ต้องการด้วยเหตุผลด้านประสิทธิภาพ หากควรรวมไฟล์ทั้งหมดให้ลบออกจากบรรทัด

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

หากไฟล์สามารถมีช่องว่างซึ่งไม่ดีสำหรับเชลล์คุณสามารถใช้:

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

ให้รายการของไฟล์ (ผ่านไปป์) หนึ่งสามารถใช้ xargs เพื่อเรียกคำสั่งและกระจายข้อโต้แย้ง คำสั่งที่อนุญาตให้ประมวลผลหลายไฟล์ได้รับการ-n1ยอมรับ ในกรณีนี้เราเรียกgit blame --line-porcelainและทุกการโทรเราใช้อาร์กิวเมนต์ 1 ตัว

xargs -n1 git blame --line-porcelain

จากนั้นเรากรองผลลัพธ์สำหรับการเกิดขึ้นของ "ผู้เขียน" เรียงลำดับรายการและนับบรรทัดที่ซ้ำกันโดย:

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

บันทึก

คำตอบอื่น ๆ จะกรองบรรทัดที่มีช่องว่างเท่านั้น

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

คำสั่งด้านบนจะพิมพ์ผู้แต่งบรรทัดที่มีอักขระที่ไม่ใช่ช่องว่างอย่างน้อยหนึ่งตัว นอกจากนี้คุณยังสามารถใช้การจับคู่\w*[^\w#]ซึ่งจะยกเว้นบรรทัดที่อักขระที่ไม่ใช่ช่องว่างตัวแรกไม่ใช่#(แสดงความคิดเห็นในหลายภาษาสคริปต์)


2
@nilbus: คุณทำไม่ได้ echo "a\nb\nc"|xargs -n1 cmdจะขยายเป็นcmd a; cmd b; cmd d
Alex

2
- line-porcelain ดูเหมือนจะไม่ทำงานอีกต่อไป (git 1.7.5.4) แทนที่จะใช้ --porcelain
isoiphone

4
ผู้ใช้ OSX ลองใช้สิ่งต่อไปนี้ (ยังไม่สามารถใช้งานไฟล์ที่มีการขึ้นบรรทัดใหม่ในชื่อของพวกเขา):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
หากคุณต้องการทุกสิ่งภายใต้เส้นทางปัจจุบันไปที่ความลึกใด ๆ ให้ใช้ "./" เป็นตัวกรองเส้นทาง (ที่ผู้ตอบให้ใส่ " / .c")
เบ็นดิลต์

2
อาจจะใช้คำว่า "โทษ -w" เพื่อให้ได้เป็นเจ้าของรหัสที่ดีขึ้นเมื่อได้รับรหัสการจัดรูปแบบเท่านั้นstackoverflow.com/questions/4112410/...
sleeplessnerd

124

ฉันเขียนพลอยชื่อgit-fameซึ่งอาจมีประโยชน์

การติดตั้งและการใช้งาน:

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

เอาท์พุท:

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 ในที่สุด 1 ที่ใช้งานได้และดูเหมือนว่าจะให้ตัวเลขที่สมเหตุสมผลส่วนที่เหลือของบรรทัดคำสั่งไม่สามารถใช้กับ OSX ได้เนื่องจากการใช้งานร่วมกันไม่ได้หรือให้ตัวเลขเล็ก ๆ บน repo ของฉัน นี่คือบน OSX และทับทิม 1.9.3 (ชง)
Karthik T

9
อย่าโง่ @tcaswell ไม่ใช่สแปมที่จะชี้ไปยังสิ่งที่มีประโยชน์แม้ว่าคุณจะเป็นคนเขียนสิ่งนั้น
Wayne

5
ตอบคำถามของฉันเอง: git fame --exclude = path / to / files, path / to / other / files
Maciej Swic

2
@ Adam: คุณยังคงมีปัญหากับสิ่งนี้หรือไม่? ทำงานได้ดีมากสำหรับฉันใน OS X 10.9.5
Sam Dutton

2
สำหรับ repo ใด ๆ ที่ใหญ่กว่าไม่กี่ครั้งที่อัญมณีนี้ต้องทำมันเป็นงานทางดาราศาสตร์
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

คำอธิบายทีละขั้นตอน:

แสดงรายการไฟล์ทั้งหมดภายใต้การควบคุมเวอร์ชัน

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

ตัดรายการลงเหลือเฉพาะไฟล์ข้อความ

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

Git โทษไฟล์ข้อความทั้งหมดโดยไม่สนใจการเปลี่ยนแปลงของช่องว่าง

|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/ +$//'

เรียงลำดับรายชื่อผู้แต่งและให้ uniq นับจำนวนบรรทัดที่ซ้ำกันอย่างต่อเนื่อง

|sort|uniq -c

ตัวอย่างผลลัพธ์:

   1334 Maneater
   1924 Another guy
  37195 Brian Ruby
   1482 Anna Lambda

1
ดูเหมือนว่าฉันมีsedรุ่นที่แตกต่างกันฉันไม่เข้าใจการ-rตั้งค่าสถานะและมีปัญหากับ regex (บ่นเกี่ยวกับ parens ที่ไม่สมดุลแม้ว่าฉันจะเอาส่วนเกินออก()
Erik Aigner

7
ไม่เป็นไรsudo brew install gnu-sedแก้ไขมัน ทำงานเหมือนจับใจ!
Erik Aigner

5
หรือport install gsedสำหรับผู้ใช้ MacPort
Gavin Brock

ฉันทำsudo brew install gnu-sed(ซึ่งใช้งานได้) แต่ฉันยังคงได้รับข้อผิดพลาดที่ไม่รู้จัก -r :(
Adam Tuttle

1
บน OSX หลังจากติดตั้ง gsed ผ่าน macports ฉันใช้คำสั่งนี้เพื่อให้มันทำงาน (แทนที่ sed ด้วย 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 summaryให้โดยแพ็คเกจgit-extrasเป็นสิ่งที่คุณต้องการ ชำระเงินเอกสารที่git-extras - git-summary :

git summary --line

ให้เอาต์พุตที่มีลักษณะดังนี้:

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

1
ดี แต่ดูเหมือนจะไม่รองรับตัวกรองเส้นทางหรืออย่างน้อยก็มีอาร์กิวเมนต์ไดเรกทอรีย่อย จะดีกว่า
spinkus

1
ทางออกที่ดีและสะอาด @ คำตอบของอเล็กซ์ให้ผลนับบรรทัดน้อยมากด้วยเหตุผลบางอย่าง นี่เพิ่งออกมาจากกล่อง ใช้เวลาประมาณ 30 วินาทีเพื่อให้บรรทัด ~ ~ ~ ~ ~ ~ ~ ~ 200k เส้นกระจายไปสองสามร้อยไฟล์
fgblomqvist

6

วิธีแก้ปัญหาของ Erik นั้นยอดเยี่ยม แต่ฉันมีปัญหาเกี่ยวกับการกำกับเสียง (แม้ว่าLC_*ตัวแปรสภาพแวดล้อมของฉันจะถูกตั้งค่าอย่างถูกต้องอย่างเห็นได้ชัด) และเสียงดังทะลุผ่านโค้ดที่มีวันที่จริง sed-fu ของฉันไม่ดีดังนั้นฉันจึงลงเอยด้วยตัวอย่าง frankenstein ที่มีทับทิมอยู่ในนั้น แต่มันใช้ได้ผลกับฉันอย่างไม่มีที่ติบน 200,000+ LOC และมันเรียงลำดับผลลัพธ์:

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

โปรดทราบด้วยว่าgsedแทนที่จะเป็นsedเพราะนั่นคือการติดตั้งไบนารีของ Homebrew ทำให้ระบบไม่เสียหาย


4

git shortlog -sn

นี่จะแสดงรายการการผูกพันต่อผู้แต่ง


17
ส่งคืนจำนวนการคอมมิตต่อผู้เขียนไม่ใช่จำนวนบรรทัด
v64

มีประโยชน์มากในการพิจารณาผู้มีส่วนร่วมหลักในโครงการ / ไดเรกทอรี / ไฟล์
Ares

4

นี่คือตัวอย่างข้อมูลหลักจากคำตอบของ @Alex ที่จะทำการรวมบรรทัดตำหนิ ฉันตัดมันเพื่อทำงานกับไฟล์เดียวมากกว่าชุดไฟล์

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

ฉันโพสต์ที่นี่เพราะฉันกลับมาที่คำตอบนี้บ่อยครั้งและอ่านโพสต์ซ้ำอีกครั้งและแยกย่อยตัวอย่างเพื่อแยกส่วนที่ฉันให้ความสำคัญกับการเก็บภาษี และมันก็ไม่ธรรมดาสำหรับกรณีการใช้งานของฉัน ขอบเขตสำหรับโครงการ C ทั้งหมด


ฉันชอบที่จะแสดงรายการสถิติต่อไฟล์, ประสบความสำเร็จด้วยการใช้ bash foriterator แทนxargsเพราะฉันพบว่า xargs อ่านได้น้อยลงและใช้งานยาก / จำได้ง่ายข้อดี / ข้อเสียของxargs vsควรได้รับจากที่อื่น

นี่คือตัวอย่างข้อมูลจริงที่จะแสดงผลลัพธ์สำหรับแต่ละไฟล์:

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

และฉันทดสอบการใช้ stright นี้ใน bash shell นั้นปลอดภัย ctrl + c ถ้าคุณต้องการใส่มันไว้ในสคริปต์ bash คุณอาจต้องTrap กับ SIGINT และ SIGTERMถ้าคุณต้องการให้ผู้ใช้สามารถทำลายลูปของคุณได้


1
git blame -w -M -C -C --line-porcelain path/to/file.txt | grep -I '^author ' | sort | uniq -ic | sort -nrพบว่ามีการปรับแต่งเล็กน้อยที่git blame นี่เพื่อแสดงสถิติที่ฉันกำลังมองหาให้แม่นยำยิ่งขึ้น โดยเฉพาะอย่างยิ่งตัวเลือก -M และ -C -C (สิ่งเหล่านี้คือจุดประสงค์สองข้อของ C) -M ตรวจจับการเคลื่อนไหวภายในไฟล์และ -C -C ตรวจจับบรรทัดที่คัดลอกจากไฟล์อื่น ดูเอกสารที่นี่ เพื่อความสมบูรณ์ -w จะละเว้นช่องว่าง
John Lee


1

ฉันมีวิธีแก้ปัญหานี้ที่นับบรรทัดตำหนิในไฟล์ข้อความทั้งหมด (ไม่รวมไฟล์ไบนารี่แม้กระทั่งไฟล์ที่มีเวอร์ชัน):

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

สิ่งนี้ใช้ได้ในไดเรกทอรีใด ๆ ของโครงสร้างซอร์สของ repo ในกรณีที่คุณต้องการตรวจสอบโมดูลซอร์สที่แน่นอน

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

0

ฉันยอมรับคำตอบยอดนิยมของ 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

เป็นทางเลือกว่าคุณจะรันgit blameด้วย-wสวิตช์หรือไม่ฉันเพิ่มมันเพราะไม่สนใจการเปลี่ยนแปลงของช่องว่าง

ประสิทธิภาพในเครื่องของฉันเป็นที่นิยมของ Powershell (~ 50s vs ~ 65s สำหรับ repo เดียวกัน) แม้ว่า Bash solution จะทำงานภายใต้WSL2


-1

สร้างสคริปต์ของตัวเองซึ่งเป็นการรวมกันของ @nilbus และ @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

สำหรับฉันแล้วสิ่งที่คุณenter code hereกำลังก่อให้เกิดปัญหา .... มันทำงานถูกต้องหรือไม่?
Menios

-1

ฟังก์ชัน Bash ที่กำหนดเป้าหมายไฟล์ต้นฉบับไฟล์เดียวที่ทำงานบน 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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.