Created
December 13, 2016 21:47
-
-
Save jamesarosen/7dab76495c2fbbe28baa18509f07bbee to your computer and use it in GitHub Desktop.
Generate a histogram of line changes (insertions + deletions) from a GitHub repo, bucketed by week
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'time' | |
class Commit | |
DATE = /^Date: +(\w{3} \w{3} \d{1,2}.+)$/ | |
MERGE = /^Merge: +([a-z0-9]+) ([a-z0-9]+)$/ | |
CHANGES = /^ *\d+ files? changed(?:, (\d+) insertions?...)?(?:, (\d+) deletions?...)? *$/ | |
attr_reader :sha | |
def self.sha_for(pointer) | |
`git rev-list -n 1 #{pointer}`.strip | |
end | |
def initialize(pointer) | |
@sha = self.class.sha_for pointer | |
@stat = `git show #{@sha} --stat` | |
end | |
def year_week | |
@year_week ||= begin | |
match = DATE.match(@stat) | |
throw "No Date info in #{@stat}" unless match | |
Time.parse(match[1]).strftime('%G-%V') | |
end | |
end | |
def parent | |
@parent ||= begin | |
pointer = is_merge? ? MERGE.match(@stat)[1] : "#{@sha}~1" | |
Commit.new(pointer) | |
end | |
end | |
def change_count | |
@change_count ||= begin | |
match = CHANGES.match(@stat) | |
raise "No change info in #{@stat}" unless match | |
(match[1] || 0).to_i + (match[2] || 0).to_i | |
end | |
end | |
def same_sha?(other) | |
if other.is_a?(Commit) | |
sha == other.sha | |
else | |
sha == self.class.sha_for(other) | |
end | |
end | |
def is_merge? | |
@is_merge ||= MERGE =~ @stat | |
end | |
def inspect | |
"<Commit sha:#{sha.slice(0, 8)} bucket:#{year_week} lines:#{change_count}>" | |
end | |
end | |
class Histogram | |
def initialize | |
@buckets ||= {} | |
end | |
def <<(commit) | |
@buckets[commit.year_week] ||= 0 | |
@buckets[commit.year_week] += commit.change_count | |
end | |
def print | |
@buckets.keys.sort.reverse.each do |week| | |
puts "#{week} #{@buckets[week]}" | |
end | |
end | |
end | |
def each_commit(start_pointer, end_pointer, &block) | |
current = Commit.new(start_pointer) | |
while !current.same_sha?(end_pointer) | |
yield current | |
current = current.parent | |
end | |
end | |
def run | |
if ARGV.length != 2 | |
puts 'USAGE: ruby git-changes-histogram.rb head head~1000' | |
end | |
histogram = Histogram.new | |
each_commit ARGV[0], ARGV[1] do |commit| | |
histogram << commit | |
end | |
histogram.print | |
end | |
run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment