module Hoe::Git

def changelog_section code

def changelog_section code
  name = {
    :major   => "major enhancement",
    :minor   => "minor enhancement",
    :bug     => "bug fix",
    :unknown => "unknown",
  }[code]
  changes = $changes[code]
  count = changes.size
  name += "s" if count > 1
  name.sub!(/fixs/, 'fixes')
  return if count < 1
  puts "* #{count} #{name}:"
  puts
  changes.sort.each do |line|
    puts "  * #{line}"
  end
  puts
end

def define_git_tasks #:nodoc:

:nodoc:
def define_git_tasks #:nodoc:
  return unless File.exist? ".git"
  desc "Print the current changelog."
  task "git:changelog" do
    tag   = ENV["FROM"] || git_tags.last
    range = [tag, "HEAD"].compact.join ".."
    cmd   = "git log #{range} '--format=tformat:%B|||%aN|||%aE|||'"
    now   = Time.new.strftime "%Y-%m-%d"
    changes = `#{cmd}`.split(/\|\|\|/).each_slice(3).map do |msg, author, email|
      msg.split(/\n/).reject { |s| s.empty? }
    end
    changes = changes.flatten
    next if changes.empty?
    $changes = Hash.new { |h,k| h[k] = [] }
    codes = {
      "!" => :major,
      "+" => :minor,
      "*" => :minor,
      "-" => :bug,
      "?" => :unknown,
    }
    codes_re = Regexp.escape codes.keys.join
    changes.each do |change|
      if change =~ /^\s*([#{codes_re}])\s*(.*)/ then
        code, line = codes[$1], $2
      else
        code, line = codes["?"], change.chomp
      end
      $changes[code] << line
    end
    puts "=== #{ENV['VERSION'] || 'NEXT'} / #{now}"
    puts
    changelog_section :major
    changelog_section :minor
    changelog_section :bug
    changelog_section :unknown
    puts
  end
  desc "Update the manifest with Git's file list. Use Hoe's excludes."
  task "git:manifest" do
    with_config do |config, _|
      files = `git ls-files`.split "\n"
      files.reject! { |f| f =~ config["exclude"] }
      File.open "Manifest.txt", "w" do |f|
        f.puts files.sort.join("\n")
      end
    end
  end
  desc "Create and push a TAG " +
       "(default #{git_release_tag_prefix}#{version})."
  task "git:tag" do
    tag = ENV["TAG"]
    ver = ENV["VERSION"] || version
    pre = ENV["PRERELEASE"] || ENV["PRE"]
    ver += ".#{pre}" if pre
    tag ||= "#{git_release_tag_prefix}#{ver}"
    git_tag_and_push tag
  end
  task "git:tags" do
    p git_tags
  end
  task :release_sanity do
    unless `git status` =~ /^nothing to commit/
      abort "Won't release: Dirty index or untracked files present!"
    end
  end
  task :release_to => "git:tag"
end

def git_svn?

def git_svn?
  File.exist? ".git/svn"
end

def git_tag_and_push tag

def git_tag_and_push tag
  msg = "Tagging #{tag}."
  if git_svn?
    sh "git svn tag #{tag} -m '#{msg}'"
  else
    flags = ' -s' unless `git config --get user.signingkey`.empty?
    sh "git tag#{flags} -f #{tag} -m '#{msg}'"
    git_remotes.each { |remote| sh "git push -f #{remote} tag #{tag}" }
  end
end

def git_tags

def git_tags
  if git_svn?
    source = `git config svn-remote.svn.tags`.strip
    unless source =~ %r{refs/remotes/(.*)/\*$}
      abort "Can't discover git-svn tag scheme from #{source}"
    end
    prefix = $1
    `git branch -r`.split("\n").
      collect { |t| t.strip }.
      select  { |t| t =~ %r{^#{prefix}/#{git_release_tag_prefix}} }
  else
    flags  = "--date-order --simplify-by-decoration --pretty=format:%H"
    hashes = `git log #{flags}`.split(/\n/).reverse
    names  = `git name-rev --tags #{hashes.join " "}`.split(/\n/)
    names  = names.map { |s| s[/tags\/(v.+)/, 1] }.compact
    names  = names.map { |s| s.sub(/\^0$/, '') }
    names.select { |t| t =~ %r{^#{git_release_tag_prefix}} }
  end
end

def initialize_git #:nodoc:

:nodoc:
def initialize_git #:nodoc:
  self.git_release_tag_prefix = "v"
  self.git_remotes            = %w(origin)
end