|
#!/usr/bin/env ruby |
|
# Usage: |
|
# git clog # prints |
|
# git clog -w # writes |
|
|
|
module Clog |
|
extend self |
|
|
|
# Returns the latest tag (assumed to be the current version). |
|
def version |
|
v = `git tag | tail -n 1`.strip |
|
v.empty? ? nil : v |
|
end |
|
|
|
# Returns the history file of the repo. |
|
def history_file |
|
Dir['*HISTORY*'].first || Dir['*CHANGE*'].first |
|
end |
|
|
|
# Returns a list of git tags. |
|
def tags |
|
`git tag`.strip.split("\n") |
|
end |
|
|
|
# Returns the first parent commit SHA1. |
|
def tail |
|
`git rev-list HEAD | tail -n 1`.strip |
|
end |
|
|
|
# List of possible ranges |
|
def all_ranges |
|
arr = Array.new |
|
['HEAD', tags.reverse, tail].flatten.each_cons(2) { |to, from| arr << [to, from] } |
|
arr |
|
end |
|
|
|
# Returns a list of changesets for the given tags. |
|
# @return Array of hashes. |
|
def changesets(range=:all) |
|
case range |
|
when :latest |
|
[ { name: 'HEAD', changes: categories(changes(version, 'HEAD')) } ] |
|
when :all |
|
all_ranges.map do |(to, from)| |
|
{ name: to, changes: categories(changes(from, to)) } |
|
end |
|
end |
|
end |
|
|
|
# Returns an array of commit messages. |
|
def changes(from=version, to=nil) |
|
log = `git log --pretty="format:%s" #{from||tail}..#{to}` |
|
log.strip.split("\n").sort |
|
end |
|
|
|
# From a list of commit messages -> categories |
|
def categories(list=changes) |
|
cats = Hash.new { |h, k| h[k] = Array.new } |
|
|
|
list.each { |line| |
|
case line |
|
when /^Merge branch/i then nil |
|
when /^Add(?:ed)? (.*)$/i then cats['Added'] << $1 |
|
when /^Fix(?:ed)? (.*)$/i then cats['Fixed'] << $1 |
|
when /^Change(?:d)? (.*)$/i then cats['Changed'] << $1 |
|
when /^(?:Remove|Delete)(?:d)? (.*)$/i then cats['Removed'] << $1 |
|
when /^\+ (.*)$/i then cats['Added'] << $1 |
|
when /^\! (.*)$/ then cats['Fixed'] << $1 |
|
when /^\- (.*)$/i then cats['Removed'] << $1 |
|
when /^\~ (.*)$/i then cats['Changed'] << $1 |
|
when /^\. (.*)$/i then cats['Misc'] << $1 |
|
else cats['Changed'] << line |
|
end |
|
} |
|
|
|
cats |
|
end |
|
|
|
def log(sets) |
|
sets.map do |set| |
|
# Heading name |
|
h = set[:name] |
|
h = "Latest version - #{Time.now.strftime('%b %d, %Y')}" if h == 'HEAD' |
|
|
|
h + "\n" + '-'*h.size + "\n\n" + |
|
set[:changes].map { |category, list| |
|
"### #{category}:\n" + |
|
list.map { |item| " * #{item}" }.join("\n") |
|
}.join("\n\n") |
|
end.join("\n\n") |
|
end |
|
|
|
def log_old |
|
h = "Since #{version || 'the beginning'} - #{Time.now.strftime('%b %d, %Y')}" |
|
"#{h}\n#{'-'*h.size}\n\n" + |
|
categories.map { |category, list| |
|
"### #{category}:\n" + |
|
list.map { |item| " * #{item}" }.join("\n") |
|
}.join("\n\n") |
|
end |
|
end |
|
|
|
options = { |
|
:all => (ARGV.delete('-a') || ARGV.delete('--all')) |
|
} |
|
|
|
if ARGV == ['--help'] || ARGV == ['-h'] |
|
puts "Usage: git clog [-a] [-w]" |
|
puts |
|
puts " -a, --all: See the changelog since time immemorial" |
|
puts " -w, --write: Write to a history file" |
|
exit |
|
end |
|
|
|
unless File.directory?('.git') |
|
puts "Try this command inside a repo's root path." |
|
exit |
|
end |
|
|
|
sets = if options[:all] |
|
Clog.changesets(:all) |
|
else |
|
Clog.changesets(:latest) |
|
end |
|
|
|
if ARGV == [] |
|
puts Clog.log(sets) |
|
$stderr << "\nUse 'git clog -w' to write.\n" |
|
|
|
elsif ARGV == ['-w'] || ARGV == ['--write'] |
|
# Create the history file |
|
system 'touch HISTORY.md' if Clog.history_file.nil? |
|
|
|
# Concat |
|
history = File.open(Clog.history_file) { |f| f.read } |
|
output = Clog.log(sets) |
|
output += "\n\n#{'-'*80}\n\n" + history unless history.empty? |
|
|
|
# Write |
|
File.open(Clog.history_file, 'w') { |f| f.write output } |
|
|
|
if ENV['EDITOR'] |
|
exec "#{ENV['EDITOR']} #{Clog.history_file}" |
|
else |
|
puts "Done writing to #{Clog.history_file}." |
|
end |
|
end |