149 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable file
		
	
	
	
	
| #!/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()
 |