Skip to content

Instantly share code, notes, and snippets.

@andynu
Created January 28, 2022 17:50
Show Gist options
  • Save andynu/82c086bcd2c8187f18ff0aa965b29440 to your computer and use it in GitHub Desktop.
Save andynu/82c086bcd2c8187f18ff0aa965b29440 to your computer and use it in GitHub Desktop.
git-stat-all command, lists an overall status of all the git repositories under the current directory.
#!/usr/bin/env ruby
require 'active_support/all'
require 'pp'
require 'rainbow'
require 'terminal-table'
$show_all = ARGV.include? '--all'
$undeployed = ARGV.include? '--undeployed'
BRANCH_CLEAN_IGNORE = [
].freeze
IGNORE_DIRS = %w[archive tmp].freeze
if ARGV.include? '--color'
Rainbow.enabled = true # force it (for watch --color)
end
Ref = Struct.new(:sha, :name) do
def <=>(other)
sha <=> other.sha
end
def ==(other)
other.class == self.class && other.sha == self.sha
end
end
def refs_to_undeployed(target_sha, refs_str)
refs = refs_str.lines.map{|refline| Ref.new(*refline.split(' ')) }
production_refs = refs.select{|ref| ref.name.in?(['refs/tags/example-deploment-tag', 'refs/tags/example-deployment-tag2']) }
target_ref = Ref.new(target_sha, 'target')
return nil if production_refs.blank? # Never deployed
!(target_ref.in?(production_refs)) # Deployment matches production or not.
end
def load_statuses
statuses = []
Dir['*'].sort.each do |dir|
next unless File.directory? dir
next unless File.directory?(File.join(dir, '.git'))
next if dir.in? IGNORE_DIRS
#next unless dir == 'git-svn-bin'
#puts '-'*80
#p dir
status = `cd #{dir}; git status`
refs = `cd #{dir}; git show-ref`
current_sha = `cd #{dir}; git show | head -n 1`.strip.split(/\s/).last
undeployed = refs_to_undeployed(current_sha, refs)
#puts status
status =~ /On branch ([^\s]+)/
branch = $1
unpushed = (status =~ /Your branch is ahead of '(.*)' by (\d+) commit/)
unpushed_origin = $1
unpushed_count = $2.to_i rescue nil
unpulled = (status =~ /Your branch is behind '(.*)' by (\d+) commit/)
unpulled_origin = $1
unpulled_count = $2.to_i rescue nil
diverged = (status =~ /Your branch and '(.*)' have diverged,/)
if diverged
status =~ /and have (\d+) and (\d+) different commits each, respectively/
unpushed = true
unpushed_count = $1.to_i rescue nil
unpulled = true
unpulled_count = $2.to_i rescue nil
end
info = {
app: dir,
branch: branch,
local_changes: ! !!(status =~ /nothing to commit/),
untracked: !!(status =~ /Untracked/),
unstaged: !!(status =~ /Changes not staged/),
unpushed: !!unpushed,
unpushed_count: unpushed_count,
unpushed_origin: unpushed_origin,
unpulled: !!unpulled,
unpulled_count: unpulled_count,
unpulled_origin: unpulled_origin,
undeployed: undeployed,
status: status,
}
statuses << info
end
return statuses
end
def colorize_branch(app, branch)
clr = case branch
when 'dev'
:blue
when 'master'
:green
else
if app.in?(BRANCH_CLEAN_IGNORE)
:yellow
else
:red
end
end
Rainbow(branch).color(clr)
end
def colorize_deployment(undeployed)
case undeployed
when nil
Rainbow('none').color(:darkslategray)
when false
Rainbow('deployed').color(:green)
else
Rainbow('undeployed').color(:red)
end
end
def colorize_conditional(cond, good_msg='GOOD', bad_msg='BAD')
clr = cond ? :green : :red
msg = cond ? good_msg : bad_msg
Rainbow(msg).color(clr)
end
def colorize_assert(value, msg=nil)
msg ||= value ? 'OK' : 'BAD'
clr = value ? :green : :red
Rainbow(msg).color(clr)
end
def colorize_refute(value, msg=nil)
msg ||= value ? 'BAD' : 'OK'
clr = value ? :red : :green
Rainbow(msg).color(clr)
end
statuses = load_statuses()
def branch_clean(project, branch)
return true if project.in?(BRANCH_CLEAN_IGNORE)
branch.nil? || branch.in?(['master', 'dev'])
end
tainted = statuses.select{|info|
$show_all ||
info[:local_changes] ||
info[:unstaged] ||
info[:untracked] ||
info[:unpushed] ||
($undeployed && info[:undeployed]) ||
!branch_clean(info[:app], info[:branch])
}
table = Terminal::Table.new
table << %w[app local_changes unstaged untracked branch unpushed unmerged undeployed]
tainted.each do |info|
table << [
info[:app],
colorize_conditional(!info[:local_changes]),
colorize_refute(info[:unstaged]),
colorize_refute(info[:untracked]),
colorize_branch(info[:app], info[:branch]),
colorize_refute(info[:unpushed], "ahead #{info[:unpushed_count]}"),
colorize_refute(info[:unpulled], "behind #{info[:unpulled_count]}"),
colorize_deployment(info[:undeployed])
]
end
puts table
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment