-
-
Save midwire/888397ee45af8d534c24999a83490137 to your computer and use it in GitHub Desktop.
Determine orphaned branches that have been merged into another branch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# | |
# Get a list of merged branches | |
# | |
# You need to checkout the target remote branch before running. In other words, | |
# if your target branch is 'master', you have to have it checked out before you | |
# run this script, otherwise you will get an error like: `fatal: malformed | |
# object name master`. Git needs to have the branch checked out and pulled in | |
# order to find the branches that have/have-not been merged into it. | |
# | |
# To create a list of branch-names only, without date info | |
# | |
# git-merged-branches.rb --branch production --exclude-days 180 | cut -f1 -d'|' | |
# | |
# For help run: git-merged-branches.rb --help | |
require 'trollop' | |
require 'fileutils' | |
require 'colored' | |
require 'pry' | |
class GitMergedBranches | |
attr_accessor :branch_count, :options, :elapsed, :excluded_branches, :matched_count | |
include FileUtils | |
class BranchEntry | |
attr_accessor :name, :last_commit_date, :relative_last_commit | |
def initialize(name, last_commit_date, relative_last_commit) | |
self.name = name | |
self.last_commit_date = last_commit_date | |
self.relative_last_commit = relative_last_commit | |
end | |
def <=>(other) | |
self.last_commit_date <=> other.last_commit_date | |
end | |
def to_s | |
"#{name} | #{last_commit_date} | #{relative_last_commit}" | |
end | |
end | |
class << self | |
def collect_args(*_args) | |
opts = Trollop.options do | |
opt( | |
:branch, | |
'Base branch - list branches merged into this branch. Uses current branch if not specified.', | |
type: :string, short: 'b', required: false | |
) | |
opt( | |
:exclude_days, | |
'Exclude branches that have no commits within this many days', | |
type: :integer, short: 'x', required: false, default: 0 | |
) | |
opt( | |
:color, | |
'Use colored output', | |
type: :boolean, short: 'c', required: false, default: true | |
) | |
end | |
# Set branch to current if not given on command line | |
opts[:branch] ||= `git rev-parse --abbrev-ref HEAD`.chomp | |
opts | |
end | |
def run | |
start_time = Time.now | |
opts = collect_args(ARGV) | |
instance = GitMergedBranches.new(opts) | |
instance.process | |
instance.elapsed = Time.now - start_time | |
instance.report_summary | |
end | |
end | |
def initialize(opts) | |
self.branch_count = 0 | |
self.excluded_branches = [] | |
self.options = opts | |
end | |
def color(string, clr) | |
if options[:color] | |
puts(string.send(clr)) | |
else | |
puts(string) | |
end | |
end | |
def report_summary | |
puts | |
color(">>> Processed #{branch_count} branches in [#{elapsed}] seconds", :red) | |
color(">>> Branches with NO commits in the last #{options[:exclude_days]} days: [#{matched_count}]", :red) | |
color(">>> Branches with commits in the last #{options[:exclude_days]} days: [#{excluded_branches.count}]", :red) | |
end | |
def process | |
self.matched_count = matched_branches.count | |
color(">>> #{matched_count} remote branches with no commits in the last #{options[:exclude_days]} days:", :green) | |
puts | |
puts matched_branches.sort | |
end | |
def merged_branches | |
@branches ||= begin | |
current_origin = nil | |
cmd = "git branch -r --merged #{options[:branch]}" | |
merged_list = `#{cmd}`.split("\n").collect(&:strip) | |
fail "Error running: #{cmd}. See output above ^^^" unless $?.exitstatus == 0 | |
# find and delete the HEAD pointer and current branch origin | |
head_pointer = merged_list.grep(/ -> /).first | |
current_origin = head_pointer.split(/ -> /).last if head_pointer | |
merged_list.delete_if { |elem| [head_pointer, current_origin].include?(elem) } | |
end | |
end | |
def matched_branches | |
today = Date.today | |
@sorted ||= merged_branches.map do |branch| | |
self.branch_count += 1 | |
date_strings = `git show --format="%ci|%cr" #{branch} | head -n 1`.chomp.split('|') | |
last_commit_date = Date.strptime(date_strings.first, '%Y-%m-%d') | |
days_old = (today - last_commit_date).to_i | |
if recent_branch?(days_old) | |
excluded_branches << branch | |
nil | |
else | |
BranchEntry.new(branch, last_commit_date, date_strings.last) | |
end | |
end.compact | |
end | |
# Determine if a branch has any commits within the last options[:exclude_days] days | |
def recent_branch?(branch_age) | |
branch_age.to_i <= options[:exclude_days].to_i | |
end | |
end | |
GitMergedBranches.run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment