class GitTools::Branches::Cleaner

Constants

DEFAULT_REMOTE
MASTER_BRANCH

Attributes

master_branch[R]
protected_branches[R]
remote[R]

Public Class Methods

merged_threshold() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 37
def self.merged_threshold
  @@age_threshold_for_deleting_remote_branches_in_master
end
merged_threshold_in_days=(days) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 41
def self.merged_threshold_in_days=(days)
  @@age_threshold_for_deleting_remote_branches_in_master = days * Time::SECONDS_IN_DAY
end
new(remote = nil, master_branch = nil, protected_branches = nil) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 55
def initialize(remote = nil, master_branch = nil, protected_branches = nil)
  @remote = remote
  @protected_branches = protected_branches || Branches.default_keep_list
  @master_branch = master_branch || get_remote_head || MASTER_BRANCH

  git_remote_prune # Prune before collecting branch names.

  @branches = branches

  if @master_branch.empty?
    raise "Master branch was not set or determined."
  else
    puts "Master branch is #{@master_branch}" if $VERBOSE
  end

  puts "Merged branch threshold is #{@@age_threshold_for_deleting_remote_branches_in_master/Time::SECONDS_IN_DAY} days." if $VERBOSE
  puts "Unmerged branch threshold is #{@@age_threshold_for_deleting_any_unmerged_branches/Time::SECONDS_IN_DAY} days." if $VERBOSE
end
unmerged_threshold() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 45
def self.unmerged_threshold
  @@age_threshold_for_deleting_any_unmerged_branches
end
unmerged_threshold_in_days=(days) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 49
def self.unmerged_threshold_in_days=(days)
  @@age_threshold_for_deleting_any_unmerged_branches = days * Time::SECONDS_IN_DAY
end
with_local(master_branch = nil, protected_branches = nil) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 29
def self.with_local(master_branch = nil, protected_branches = nil)
  self.new(nil, master_branch, protected_branches)
end
with_origin(protected_branches = nil) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 33
def self.with_origin(protected_branches = nil)
  self.new(DEFAULT_REMOTE, nil, protected_branches)
end

Public Instance Methods

local?() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 74
def local?
  @remote.nil?
end
remote?() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 78
def remote?
  !local?
end
run!() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 82
def run!
  puts "Skipping prompts" if $VERBOSE && ActionExecutor.skip_prompted
  (@branches - protected_branches - [master_branch] ).each do |branch|
    branch = Branch.new(branch, remote)
    containing_branches = contained_branches(branch.normalized_name) - [branch.name]

    if protected_branch?(branch.name)
      puts "#{label_for_remote} branch [#{branch}] is on keep list ( #{kbs.join(" , ")} )" if $VERBOSE
    elsif in_master_branch?(containing_branches)
      if delete_branch_merged_to_master_without_confirmation?(branch.age)
        message = "Removing #{label_for_remote.downcase} branch [#{branch}] since it is in #{master_branch}. [#{branch.age.relative}]"
        branch.remove!(message)
      else
        message = "#{label_for_remote} branch [#{branch}] is in #{master_branch} and could be deleted [#{branch.age.relative}]"
        branch.confirm_remove(message, "Delete #{branch}?")
      end
    elsif delete_unmerged_branch?(branch.age)
      branch_list = containing_branches.empty? ? '' : "Branch has been merged into:\n  #{containing_branches.join("\n  ")}"
      message = "#{label_for_remote} branch [#{branch}] is not on #{master_branch}, but old [#{branch.age.relative}]. #{branch_list}"
      branch.confirm_remove(message, "Delete old unmerged branch: #{branch} ?")
    else
      puts "Ignoring unmerged #{label_for_remote.downcase} branch [#{branch}]" if $VERBOSE
    end

    if defined?($signal_handler)
      break if $signal_handler.interrupted?
    end
  end

  git_remote_prune
rescue StandardError => e
  puts e.message
  puts e.backtrace if $DEBUG
end

Private Instance Methods

branches() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 129
def branches
  clean_branches_result(`git branch #{git_argument_for_remote}`)
end
clean_branches_result(branches) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 142
def clean_branches_result(branches)
  bs = branches.to_s.split(/\n/).map { |b| b.strip.sub(/^\s*\*\s*/, '') }

  if local?
    bs
  else
    # technically split out remote branch name (may not be origin) and return as list
    bs.delete_if { |b| b =~ /HEAD/ }
    bs.find_all { |b| /^#{remote}\// =~ b }.map { |b| b.sub(/^#{remote}\//, '') }
  end
end
contained_branches(branch) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 154
def contained_branches(branch)
  # git's --contains param seems to cause this stderr out:
  #   error: branch 'origin/HEAD' does not point at a commit
  # piping that to stderr and cleaning out.
  clean_branches_result(`git branch #{git_argument_for_remote} --contains #{branch} 2>&1`)
end
delete_branch_merged_to_master_without_confirmation?(time) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 169
def delete_branch_merged_to_master_without_confirmation?(time)
  if local?
    true
  else
    (Time.now - time) > self.class.merged_threshold
  end
end
delete_unmerged_branch?(time) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 177
def delete_unmerged_branch?(time)
  (Time.now - time) > self.class.unmerged_threshold
end
get_remote_head() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 137
def get_remote_head
  head = (`git branch -r | grep /HEAD`).sub(/\w+\/HEAD -> \w+\//, '').strip
  head.empty? ? nil : head
end
git_argument_for_remote() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 133
def git_argument_for_remote
  local? ? '' : ' -r'
end
git_remote_prune() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 119
def git_remote_prune
  if remote?
    `git remote prune #{remote}`
  end
end
in_master_branch?(branch) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 161
def in_master_branch?(branch)
  branch.include?(master_branch)
end
label_for_remote() click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 125
def label_for_remote
  local? ? "Local" : "Remote"
end
protected_branch?(branch) click to toggle source
# File lib/git_tools/branches/cleaner.rb, line 165
def protected_branch?(branch)
  protected_branches.include?(branch)
end