Last active
December 15, 2021 15:46
-
-
Save burke/6ed882e52c68771be1e2c13326fe8159 to your computer and use it in GitHub Desktop.
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
# fragment added to ~/.zshrc | |
zle-gasdf() { gasdf; zle .beginning-of-line; zle .kill-line; zle .accept-line } | |
zle -N zle-gasdf | |
bindkey 'å' zle-gasdf # Alt-A Canadian English |
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
#!ruby --disable-gems | |
autoload(:FileUtils, 'fileutils') | |
autoload(:Open3, 'open3') | |
class GASDF | |
def initialize(toplevel) | |
@m = Mutex.new | |
@cv = ConditionVariable.new | |
@ticker = 0 | |
@toplevel = toplevel | |
@status = { | |
commit: false, | |
fetch: false, | |
rebase: false, | |
push: false, | |
} | |
render(first: true) | |
end | |
TICKS = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'.each_char.to_a | |
def run | |
threads = @status | |
.keys | |
.map { |k| Thread.new { send(k) } } | |
threads << Thread.new do | |
loop do | |
@m.synchronize { @cv.wait(@m) } | |
render | |
break if done? | |
end | |
end | |
# don't wait on this one, just let it get torn down on exit | |
Thread.new do | |
loop do | |
@ticker += 1 | |
sleep(0.1) | |
@m.synchronize { @cv.broadcast } | |
end | |
end | |
threads.map(&:join) | |
end | |
private | |
def done? | |
@status.values.all? { |v| v == :done } | |
end | |
def commit | |
out = git!('status', '--porcelain') | |
return if out.empty? | |
git!('add', '.') | |
git!('commit', '-q', '-m', '[automatic commit]') | |
ensure | |
complete(:commit) | |
end | |
def fetch | |
wait(:fetch, :commit) | |
git!('fetch', '-q', 'origin', branch) | |
ensure | |
complete(:fetch) | |
end | |
def rebase | |
wait(:rebase, :fetch) | |
git!('rebase', '-X', 'theirs', '-q', 'FETCH_HEAD') | |
ensure | |
complete(:rebase) | |
end | |
def push | |
wait(:push, :commit) | |
_, _, stat = git('push', '-q') | |
return if stat.success? | |
stall(:push) | |
wait(:push, :rebase) | |
git!('push', '-q') | |
ensure | |
complete(:push) | |
end | |
def branch | |
git!('rev-parse', '--abbrev-ref', 'HEAD').chomp | |
end | |
def stall(x) | |
@m.synchronize do | |
@status[x] = :stall | |
render | |
@cv.broadcast | |
end | |
end | |
def complete(x) | |
@m.synchronize do | |
@status[x] = :done | |
render | |
@cv.broadcast | |
end | |
end | |
def wait(from, x) | |
@status[from] = :wait | |
loop do | |
@m.synchronize do | |
@cv.wait(@m) | |
return if @status[x] == :done | |
end | |
end | |
ensure | |
@status[from] = false | |
end | |
def git(*args) | |
Open3.capture3('git', '-C', @toplevel, *args) | |
end | |
def git!(*args) | |
out, err, stat = git(*args) | |
unless stat.success? | |
abort("git #{args.first} failed: #{err}") | |
end | |
out | |
end | |
def render(first: false) | |
o = +'' | |
tick = TICKS[@ticker % TICKS.size] | |
[:commit, :fetch, :rebase, :push].each do |f| | |
color = case @status[f] | |
when false then "\x1b[1;33m#{tick}" | |
when :done then "\x1b[1;32m✓" | |
when :stall then "\x1b[1;31m#{tick}" | |
when :wait then "\x1b[1;35m⧖" | |
else "\x1b[1;31m#{tick}" | |
end | |
o += color + f.to_s[0].upcase | |
end | |
if first | |
print(o + "\x1b[0m") | |
else | |
print("\x1b[8D" + o + "\x1b[0m") | |
end | |
end | |
end | |
toplevel = File.expand_path(Dir.pwd) | |
loop do | |
toplevel = Dir.pwd | |
break if Dir.exist?(File.join(toplevel, '.git')) | |
toplevel = File.dirname(toplevel) | |
abort('not in a git repo') if toplevel == '/' | |
end | |
case ARGV.shift | |
when '-e' | |
FileUtils.touch(File.join(toplevel, '.git', 'gasdf')) | |
puts('enabled') | |
when nil | |
unless File.exist?(File.join(toplevel, '.git', 'gasdf')) | |
abort('run with -e to enable') | |
end | |
GASDF.new(toplevel).run | |
else | |
abort('invalid usage') | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment