Skip to content

Instantly share code, notes, and snippets.

@mislav
Created September 11, 2008 20:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mislav/10301 to your computer and use it in GitHub Desktop.
Save mislav/10301 to your computer and use it in GitHub Desktop.
Generates an adds/removes-based scoreboard for a git project
## Usage:
$ ruby git-scoreboard.rb
You can also specify a range of commits:
$ ruby git-scoreboard.rb v1.2..HEAD
## Outputs:
Most adds:
1. Mislav Marohnic 36430
2. Steve Jobs 4329
3. Bill Gates 2144
Most deletes:
1. Bill Gates 12736
2. Mislav Marohnic 926
3. Steve Jobs 903
## Note:
This script ignores these files:
* extensions: sql, xml, yml
* everything under "vendor/" or "log/"
* Prototype and script.aculo.us
#!/usr/bin/env ruby
## Scoreboard for git projects with multiple people committing.
## This is a better version of: git log --pretty=format:%an | sort | uniq -ci | sort -r | head -n30
scoreboard = {}
author = commit_id = changes_per_commit = nil
commits = []
# TODO: ignore file moves :(
ignore_mode = false
$ignore_pattern = %r!
(?:
\.(?: sql | xml | ya?ml ) | # ignored file extensions
/javascripts/(?: builder|controls|effects|scriptaculous|prototype )\.js |
(?: ^
public/dispatch\.\w{2,4} |
public/\d{3}.html |
config/boot.rb
)
)$ |
\b(?: vendor | log )/ # otherwise the winner would be the one who freezes rails
!x
log = `git log --no-merges -p --pretty=format:"##%an|%h" #{ARGV.join(' ')}`
log.each_line do |line|
if line =~ %r{^diff .+ a/(.+) b/(.+)\n$}
## start of a file
path = $1
ignore_mode = path =~ $ignore_pattern
puts "ignored: " + path if ignore_mode and $VERBOSE
else
case line[0,2]
when '##'
## start of a commit
# record last commit info:
commits << [commit_id, changes_per_commit] if commit_id
# initialize metadata:
author, commit_id = line[2..-2].split('|')
scoreboard[author] ||= [0, 0]
changes_per_commit = 0
when '+ ', '- '
## the main stuff!
unless ignore_mode
scoreboard[author][line[0,1] == '+' ? 0 : 1] += 1
changes_per_commit += 1
end
end
end
end
everyone = scoreboard.to_a
# names with multibyte chars screw this up, but whatever:
longest_name = scoreboard.keys.compact.sort_by { |name| name.length }.last.length
def output(entries, index, width)
entries.reverse.each_with_index do |entry, i|
puts sprintf(' %2d. %s %d', i + 1, entry[0].ljust(width), entry[1][index])
break if i == 29
end
end
puts "Most adds:"
output(everyone.sort_by { |entry| entry[1][0] }, 0, longest_name)
puts "\nMost deletes:"
output(everyone.sort_by { |entry| entry[1][1] }, 1, longest_name)
if $VERBOSE
largest = commits.sort_by { |c| c[1] }[-4..-1].reverse.map { |c| c[0] }
puts "\nLargest commits: #{largest.join(', ')}"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment