Skip to content

Instantly share code, notes, and snippets.

@midwire
Last active August 31, 2021 16:00
Show Gist options
  • Select an option

  • Save midwire/0b4ece1d6e8a548dfc1f081a5b5cd67c to your computer and use it in GitHub Desktop.

Select an option

Save midwire/0b4ece1d6e8a548dfc1f081a5b5cd67c to your computer and use it in GitHub Desktop.

Revisions

  1. midwire revised this gist Aug 16, 2016. No changes.
  2. midwire revised this gist Aug 16, 2016. No changes.
  3. midwire created this gist Aug 16, 2016.
    142 changes: 142 additions & 0 deletions git-merged-branches.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,142 @@
    #!/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 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'

    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
    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