Created
January 28, 2022 17:50
-
-
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.
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 | |
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