#!/usr/bin/env ruby # Copyright (C) 2010 Gregoire Lejeune # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA require 'rubygems' require 'getoptlong' require 'graphviz' class Git2Gv def initialize( xGVPath, xOutFile, xOutFormat, bNothugly ) @xGVPath, @xOutFile, @xOutFormat, @bNothugly = xGVPath, xOutFile, xOutFormat.to_sym, bNothugly end def run git = GraphViz.new( :G, :path => @xGVPath ) git.node[:shape => "record"] branches.each do |branch| git.add_node( branch, :style => "filled", :fillcolor => :lightgrey ) end commits.each do |commit| git.add_node( commit[:short], :style => "filled", :fillcolor => :lightblue, :label => "{ #{commit[:long]} |{ #{commit[:comment].gsub(/\{/, "\\{").gsub(/\}/, "\\}").gsub(/\|/, "\\|").gsub(//, "\\>")}}| { #{commit[:commiter]} | #{commit[:date]} } }" ) end refs.each do |from, to| git.add_edge( from, to ) end relations.each do |from, to| git.add_edge( to, from, :dir => "back" ) end git.output( @xOutFormat => @xOutFile, :nothugly => @bNothugly ) end # private def cmd(c, &blk) `#{c}`.split($/).map(&blk || proc {|a| a }) end def commits @commits ||= begin data = [] cmd('git log --pretty=format:"%h - %H - %s - %cn - %cd" --date=short').each do |commit| x = commit.split( " - " ) data << { :short => x[0], :long => x[1], :comment => x[2], :commiter => x[3], :date => x[4] } end data end end def relations @rels ||= cmd("git log --pretty=format:\"%h %p\"") { |l| c, *parents = l.split parents.map {|p| [p, c] } }.flatten(1) end def branches @branches ||= cmd("git branch") {|b| b[2..-1] } end def refs branches.inject({}) {|h,b| h.tap { h[b] = `git log -1 #{b} --pretty=format:"%h"` } } end def current_branch cmd("git branch").select {|l| l =~ /^\*/ }.first.strip[2..-1] end end def usage puts "usage: git2gv [-Tformat] [-ofile] [-h] [-V]" puts "-T, --output-format format Output format (default: png)" puts " --nothugly Use nothugly if SVG output" puts "-o, --output-file file Output file (default: STDOUT)" puts "-p, --path Graphviz path" puts "-V, --version Show version" puts "-h, --help Show this usage message" end def version puts "Git2GraphViz v#{Constants::RGV_VERSION}, (c)2010 Gregoire Lejeune " puts "" puts "This program is free software; you can redistribute it and/or modify" puts "it under the terms of the GNU General Public License as published by" puts "the Free Software Foundation; either version 2 of the License, or" puts "(at your option) any later version." puts "" puts "This program is distributed in the hope that it will be useful," puts "but WITHOUT ANY WARRANTY; without even the implied warranty of" puts "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" puts "GNU General Public License for more details." puts "" puts "You should have received a copy of the GNU General Public License" puts "along with this program; if not, write to the Free Software" puts "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA" end oOpt = GetoptLong.new( ['--output-format', '-T', GetoptLong::REQUIRED_ARGUMENT], ['--output-file', '-o', GetoptLong::REQUIRED_ARGUMENT], ['--path', '-p', GetoptLong::REQUIRED_ARGUMENT], ['--nothugly', GetoptLong::NO_ARGUMENT], ['--help', '-h', GetoptLong::NO_ARGUMENT], ['--version', '-V', GetoptLong::NO_ARGUMENT] ) xOutFormat = "png" xOutFile = nil xGVPath = "" bNothugly = false begin oOpt.each_option do |xOpt, xValue| case xOpt when '--output-format' xOutFormat = xValue when '--output-file' xOutFile = xValue when '--path' xGVPath = xValue when '--nothugly' bNothugly = true when '--help' usage( ) exit when '--version' version( ) exit end end rescue GetoptLong::InvalidOption => e usage( ) exit end xFile = ARGV[0] Git2Gv.new( xGVPath, xOutFile, xOutFormat, bNothugly ).run