# Requires Ruby's JSON gem, or any other gem with compatible interface.
# The JSON output, thus, is necessarily valid.
# Supports nested hashes (just one level of nesting).
# Tries to validate all ...date and ..time fields using Ruby time.
# The script removes all linefeeds=newlines from all the fields.
# Sets the date format to rfc2282 as it is easy to parse in Ruby.
# You can also use the git_log function programmatically to receive a Ruby hash.
# Git log reference: http://git-scm.com/docs/git-log
#!/usr/bin/env ruby
require 'json'
require 'date'
require 'time'
path = ARGV[0] ? ARGV[0] : Dir.pwd
def git_log(repo)
begin
Dir.chdir(repo)
rescue StandardError => e
puts 'fatal: ' + e.message
Kernel::exit(129)
end
cmd = %(git --no-pager log --all --no-merges --ignore-missing --shortstat --date=rfc --pretty=format:'§§±±`±±§§commit_hash§±`±§%H§±`±§tree_hash§±`±§%T§±`±§parent_hash§±`±§%P§±`±§author§±`±§name§±``±§%an§±``±§mailmap_name§±``±§%aN§±``±§email§±``±§%ae§±``±§mailmap_email§±``±§%aE§±``±§date§±``±§%ad§±`±§committer§±`±§name§±``±§%cn§±``±§mailmap_name§±``±§%cN§±``±§email§±``±§%ce§±``±§mailmap_email§±``±§%cE§±``±§date§±``±§%cd§±`±§subject§±`±§%§±`±§ref_name§±`±§%d§±`±§notes§±`±§%N§§±±``±±§§')
pop = IO.popen(cmd) {|out| out.read.gsub(/\n/, '')}
Kernel::exit(129) if pop.empty?
pop.gsub!('§§±±`±±§§',"\n")
lpops = pop.lines[1..-1]
regi = Regexp.new('^ ([0-9]*) files? changed(, ([0-9]*) insertions?\(\+\))?(, ([0-9]*) deletions?\(-\))?')
regal = Regexp.new('§±``±§')
a = Array.new
lpops.each do |l|
log,change=l.split('§§±±``±±§§')
change=regi.match(change)
h = {files_changed: change[1].to_i, insertions: change[3].to_i, deletions: change[5].to_i}
l1s=log.split('§±`±§')
(0...l1s.length).step(2).each do |i|
if regal.match(l1s[i+1])
l2s = l1s[i+1].split('§±``±§')
h[l1s[i]] = Hash.new
(0...l2s.length).step(2).each do |j|
h[l1s[i]][l2s[j]] = ['date','time'].include?(l2s[j]) ? Time.rfc2822(l2s[j+1]) : l2s[j+1]
end
else
h[l1s[i]]=l1s[i+1]
end
end
a.insert(0, h)
end
a
end
puts JSON::pretty_generate(git_log(path))