Skip to content

Instantly share code, notes, and snippets.

@lgs
Forked from Sutto/git_commit_graph.rb
Created March 3, 2009 22:17
Show Gist options
  • Save lgs/73563 to your computer and use it in GitHub Desktop.
Save lgs/73563 to your computer and use it in GitHub Desktop.
require 'gruff'
class GitCommitGraph
Author = Struct.new(:name, :email)
Commit = Struct.new(:hash, :author, :time, :insertions, :deletions, :files_changed)
attr_accessor :time_spacing, :built, :repository, :since, :name, :commit_hash
attr_accessor :scale_type, :width, :height, :graph, :graph_type
def initialize(name, repository_location, since = Time.at(0), commit_hash = nil, scale_type = :normal, time_spacing = 600)
self.since = since
self.repository = repository_location
self.name = name
self.commit_hash = commit_hash
self.scale_type = scale_type.to_sym
self.time_spacing = time_spacing
self.built = false
self.graph_type = :discrete
self.width = 800
self.height = 450
end
def save(file)
discrete, cumulative = build!
g = Gruff::Line.new("#{self.width}x#{self.height}")
source = (self.graph_type == :cumulative ? cumulative : discrete)
g.data "Insertions", source[:added], "#24762A"
g.data "Deletions", source[:deleted], "#760F00"
g.title = "Commits for #{self.name} - #{self.graph_type != :cumulative ? "Per Period" : "Cumulative"}"
g.font = "/Users/sutto/Library/Fonts/nevis.ttf"
self.graph = g
self.graph.write(file)
end
def extract_commits
log_text = Dir.chdir(self.repository) { %x[git log --shortstat --no-merges --pretty="format:%H,%an,%ae,%ad" #{self.commit_hash || "HEAD"}] }.to_s
commit_sets = []
commit_raw = log_text.split("\n\n")
commit_raw.each do |commit_set|
parts = commit_set.split("\n")
authors_raw = parts[0..-2]
details_raw = parts.last
hash, time, author = nil, nil, nil
authors_raw.map do |line|
hash, name, email, c_time = line.split(",")
time = Time.parse(c_time)
author = Author.new(name, email)
end
files, added, deleted = /^(\d+) files changed\, (\d+) insertions\(\+\)\, (\d+) deletions\(\-\)$/i.match(details_raw.strip).to_a[1..-1]
commit_sets << Commit.new(hash, author, time, added.to_i, deleted.to_i, files.to_i)
end
commit_sets = commit_sets.reject { |co| co.time < self.since }
return commit_sets
end
def build!
cs = extract_commits
grouped = cs.group_by { |c| ((c.time - self.since).to_f / self.time_spacing).ceil }
max = grouped.keys.max # Get The max.
cumulative = {:added => [], :deleted => []}
discrete = {:added => [], :deleted => []}
running_added, running_deleted = 0, 0
m = grouped.keys.min
m.upto(max) do |t|
commits = grouped[t] || []
added = commits.map(&:insertions).sum
deleted = commits.map(&:deletions).sum
cumulative[:added][t] = running_added + added
cumulative[:deleted][t] = running_deleted + deleted
discrete[:added][t] = added
discrete[:deleted][t] = deleted
running_added, running_deleted = cumulative[:added][t], cumulative[:deleted][t]
end
if scale_type == :log
discrete.each do |k, v|
discrete[k] = v.compact.map { |f| f <= 0 ? 0 : Math.log(f) }
end
cumulative.each do |k, v|
cumulative[k] = v.compact.map { |f| f <= 0 ? 0 : Math.log(f) }
end
end
return discrete, cumulative
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment