Created
October 8, 2015 18:32
-
-
Save oggy/e86aab056cc42092a97d to your computer and use it in GitHub Desktop.
Command to delete old remote branches
This file contains 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 | |
require 'time' | |
class App | |
CUTOFF_DAYS = 30 | |
def initialize(args) | |
args.size <= 1 or | |
abort "USAGE: #$0 [remote]" | |
@remote = args.first || 'origin' | |
end | |
def run | |
branches = stagnant_branches | |
if confirm(branches) | |
delete_branches(branches) | |
else | |
abort 'Aborted.' | |
end | |
end | |
private | |
def stagnant_branches | |
cutoff = Time.now - CUTOFF_DAYS*24*60*60 | |
all_branches.select { |b| b.last_updated_at.nil? || b.last_updated_at < cutoff } | |
end | |
def all_branches | |
`git branch --no-color -r`.lines.map do |line| | |
line =~ /\A..#@remote\/(\S+)/ or | |
next | |
Branch.new(@remote, $1) | |
end.compact.sort_by { |b| b.last_updated_at || Time.at(0) }.reverse | |
end | |
def confirm(branches) | |
branches.each do |branch| | |
puts " #{branch.info}" | |
end | |
$>.sync = true | |
loop do | |
$>.print "Delete these branches? [y/n] " | |
case $<.gets.strip | |
when /\Ay(?:es)?\z/i | |
return true | |
when /\Ano?\z/i | |
return false | |
end | |
end | |
end | |
def delete_branches(branches) | |
puts "Deleting:" | |
branches.each do |branch| | |
puts " #{branch.name}: #{branch.sha}" | |
end | |
refspecs = branches.map { |b| ":#{b.name}" } | |
#sh "git push #@remote #{refspecs.join(' ')}" | |
end | |
def sh(command) | |
puts "++ #{command}" | |
system command | |
end | |
class Branch | |
def initialize(remote, name) | |
@remote = remote | |
@name = name | |
end | |
attr_reader :remote, :name | |
def last_updated_at | |
@last_updated_at ||= find_last_reflog_date || find_last_commit_date | |
end | |
def info | |
if num_commits > 0 | |
"#{name} - #{age}, #{num_commits} commits by #{authors.join(', ')}" | |
else | |
"#{name} - #{age}, no commits" | |
end | |
end | |
def sha | |
`git log -n 1 --format=%H #@remote/#@name`.chomp | |
end | |
private | |
def age | |
if last_updated_at | |
seconds_ago = Time.now - last_updated_at | |
days_ago = (seconds_ago / 24 / 60 / 60).round | |
"#{days_ago} days old" | |
else | |
"age unknown" | |
end | |
end | |
def num_commits | |
@num_commits ||= commits.size | |
end | |
def authors | |
@authors ||= commits.map { |sha, author| author.split(/&/).map(&:strip) }.flatten.uniq.sort | |
end | |
def commits | |
@commits ||= `git log --format=%H:%an #@remote/master..#@remote/#@name`.lines.map do |line| | |
line.chomp.split(/:/) | |
end | |
end | |
def find_last_reflog_date | |
output = `git log -g -n 1 --date=local --pretty=%gd #@remote/#@name` | |
time_string = output[/(?<=@\{)(.*)(?>\})/] or | |
return nil | |
DateTime.parse(time_string).to_time | |
end | |
def find_last_commit_date | |
output = `git log -n 1 --format=%cd #@remote/#@name`.chomp | |
DateTime.parse(output).to_time | |
end | |
end | |
end | |
App.new(ARGV).run |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment