module GitCommands
  class ShellError < RuntimeError; end
 
  @logging = ENV['LOGGING'] != "false"
 
  def self.run cmd, *expected_exitstatuses
    puts "+ #{cmd}" if @logging
    output = `#{cmd} 2>&1`
    puts output.gsub(/^/, "- ") if @logging
    expected_exitstatuses << 0 if expected_exitstatuses.empty?
    raise ShellError.new("ERROR: '#{cmd}' failed with exit status #{$?.exitstatus}") unless
      [expected_exitstatuses].flatten.include?( $?.exitstatus )
    output
  end
 
  def self.current_branch
    run("git branch --no-color | grep '*' | cut -d ' ' -f 2").chomp
  end
 
  def self.remote_branch_exists?(branch)
    ! run("git branch -r --no-color | grep '#{branch}'").blank?
  end
 
  def self.ensure_clean_working_directory!
    return if run("git status", 0, 1).match(/working directory clean/)
    raise "Must have clean working directory"
  end
 
  def self.diff_staging
    puts run("git diff HEAD origin/staging")
  end
 
  def self.diff_production
    puts run("git diff origin/staging origin/production")
  end

  def self.push(src_branch, dst_branch)
    raise "origin/#{dst_branch} branch does not exist" unless remote_branch_exists?("origin/#{dst_branch}")
    ensure_clean_working_directory!
    begin
      run "git fetch"
      run "git push -f origin #{src_branch}:#{dst_branch}"
    rescue
      puts "Pushing #{src_branch} to origin/#{dst_branch} failed."
      raise
    end
  end
 
  def self.push_staging
    push(current_branch, "staging")
  end
 
  def self.push_production
    push("origin/staging", "production")
  end
 
  def self.branch_production(branch)
    raise "You must specify a branch name." if branch.blank?
    ensure_clean_working_directory!
    run "git fetch"
    run "git branch -f #{branch} origin/production"
    run "git checkout #{branch}"
  end
 
  def self.pull_template
    ensure_clean_working_directory!
    run "git pull git://github.com/thoughtbot/suspenders.git master"
  end
end
 
namespace :git do
  namespace :push do
    desc "Reset origin's staging branch to be the current branch."
    task :staging do
      GitCommands.push_staging
    end
 
    desc "Reset origin's production branch to origin's staging branch."
    task :production do
      GitCommands.push_production
    end
  end
 
  namespace :diff do
    desc "Show the difference between current branch and origin/staging."
    task :staging do
      GitCommands.diff_staging
    end
 
    desc "Show the difference between origin/staging and origin/production."
    task :production do
      GitCommands.diff_production
    end
  end
 
  namespace :pull do
    desc "Pull updates from suspenders, the thoughtbot rails template."
    task :suspenders do
      GitCommands.pull_template
    end
  end
 
  namespace :branch do
    desc "Branch origin/production into BRANCH locally."
    task :production do
      GitCommands.branch_production(branch)
    end
  end
end