Skip to content

Instantly share code, notes, and snippets.

@hauspie
Created April 16, 2012 09:20
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hauspie/2397222 to your computer and use it in GitHub Desktop.
Save hauspie/2397222 to your computer and use it in GitHub Desktop.
Extract git history to tikz picture
#!/usr/bin/env ruby
# A small ruby script to extract a git history to a tikz picture
# Author: Michael Hauspie <Michael.Hauspie@lifl.fr>
#
# Not clean code, not always working well, but does its job in most of the cases I needed :)
# A commit object
class Commit
attr_accessor :hash
attr_accessor :children
attr_accessor :parents
attr_accessor :message
attr_reader :node_pos
# Construct a commit from a line
# A line is commit_hash [child1_hash ... childN_hash] message
def initialize()
@hash = nil
@children = Hash.new()
@parents = Hash.new()
@message = ""
@node_pos = 0
@message_pos = 0
end
def build(line)
# Parse each word to match a hash or the commmit message
pos=0
line.split(" ").each do |word|
if word =~ /[a-f0-9]{7}/ && @message == "" # match a short sha-1 commit tag
if ! @hash
@hash = word
else
@parents[word] = nil
end
elsif word == '*' # Position of the node
@node_pos = pos
elsif word =~ /[^|\/\\]/
@message_pos = pos
@message << " #{word}"
end
pos = pos + 1
end
@message.delete!("!")
@message.lstrip!()
if !@hash
return false
end
return true
end
# sets a child object
def set_parent(c)
@parents[c.hash] = c
c.children[@hash] = self
end
def export_to_tikz(ypos)
puts "\\node[commit] (#{@hash}) at (#{0.5*@node_pos},#{ypos}) {};"
puts "\\node[right,xshift=#{@message_pos*0.5}] (label_#{@hash}) at (#{@hash}.east) {\\verb!#{@hash} #{@message}!};"
@children.each_value do |child|
puts "\\draw[->] (#{@hash}) -- (#{child.hash});"
end
end
def to_s()
"#{@hash}: #{@message} #{@node_pos} #{@message_pos}"
end
end
class Branch
attr_accessor :name
attr_accessor :hash
attr_accessor :commit
def initialize(line)
words = line.split(" ")
@name = words[0]
@hash = words[1]
@commit = nil
end
end
# A repository, which is a collection of commit objects and branches
class Repository
def initialize()
@commits = Hash.new()
@branches = Array.new()
end
def add_commit(commit)
@commits[commit.hash] = commit
end
def add_branch(branch)
if ! @commits.has_key?(branch.hash)
return false
end
c = @commits.fetch(branch.hash)
branch.commit = c
@branches << branch
return true
end
# This iterates other commits and resolves its parents
def resolve_parents()
@commits.each_value do |commit|
commit.parents.keys.each do |parent_hash|
c = @commits.fetch(parent_hash)
commit.set_parent(c) # Link the commit object to its parent
end
end
end
def export_to_tikz
puts "\\begin{tikzpicture}"
ypos=0
ystep=-0.5
@commits.each_value do |commit|
commit.export_to_tikz(ypos)
ypos = ypos + ystep
end
@branches.each do |branch|
puts "\\node[branch,right,xshift=.1] (#{branch.name}) at (label_#{branch.hash}.east) {\\texttt{#{branch.name}}};"
end
puts "\\end{tikzpicture}"
end
end
r = Repository.new()
# Extract commits
`git log --graph --branches --oneline --parents`.lines().each do |line|
c = Commit.new()
if c.build(line)
r.add_commit(c)
end
end
r.resolve_parents()
# Extract branches
`git branch -av | cut -b 3-`.lines().each do |line|
r.add_branch(Branch.new(line))
end
r.export_to_tikz()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment