Skip to content

Instantly share code, notes, and snippets.

@lboulard
Last active March 8, 2026 16:23
Show Gist options
  • Select an option

  • Save lboulard/ba11adf2c65b9a34a206f664b8609347 to your computer and use it in GitHub Desktop.

Select an option

Save lboulard/ba11adf2c65b9a34a206f664b8609347 to your computer and use it in GitHub Desktop.
Parse output of git clone and show nicer progress output
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'open3'
require 'fileutils'
require 'uri'
repo = ARGV[0]
raise 'missing repo argument' unless repo
name = ARGV[1] || File.basename(URI(repo).path).delete_suffix('.git')
FileUtils.rm_rf name
# Requires '--progress', when not in terminal '--quiet' is enforced
args = ['git', 'clone', '--progress', repo, name]
def stream_cmd(*args, &block)
Open3.popen3(*args) do |_stdin, stdout, stderr, thread|
readers = [stdout, stderr]
until readers.empty?
begin
# Block until at least one pipe is readable (timeout optional)
ready, = IO.select(readers, nil, nil, 5)
next unless ready # timeout hit, loop again
ready.each do |io|
line = io.gets # read one line from whichever pipe is ready
if line
block.call(io == stdout ? :stdout : :stderr, line)
else
readers.delete(io) # EOF on this pipe, stop watching it
end
end
rescue StandardError
begin
Process.kill('TERM', thread.pid)
rescue StandardError => e
warn "failed to kill process #{thread.pid}:\n#{e}"
end
raise
end
end
thread.value.exitstatus
end
end
EIGHTHS = [' ', '▏', '▎', '▍', '▌', '▋', '▊', '▉', '█'].freeze
def smooth_bar(current, total, width: 20)
pct = current.to_f / total
full_cells = pct * width
filled = full_cells.floor
return '█' * filled if filled == width
remainder = full_cells - filled # fractional part 0..1
tip = EIGHTHS[(remainder * 8).floor]
"\e[1;93m\e[1;44m#{'█' * filled}#{tip}#{' ' * (width - filled - 1)}\e[0m"
end
newline = false
begin
progress = nil
percent = 0
exit_code = stream_cmd(*args) do |stream, line|
case stream
when :stdout then puts "OUT | #{line.chomp}"
when :stderr
case line
in %r{^(?<prefix>(?<remote>remote: )?[\w\s]+):\s+(?<progress>\d+)%\s\((?<current>\d+)/(?<total>\d+)\)(?<suffix>.*)?}
current = Regexp.last_match(:current).to_i
total = Regexp.last_match(:total).to_i
bar = smooth_bar(current, total)
percent = Regexp.last_match(:progress).to_i
prefix = Regexp.last_match(:prefix)
suffix = Regexp.last_match(:suffix).chomp
progress = "▕#{bar}▏#{percent.to_s.rjust 5}% | #{prefix} (#{current}/#{total}) #{suffix}\e[K"
in /^remote:\s(.*)/
event = 'REMOTE'
else
event = 'INFO'
end
end
$stdout.write " #{event.ljust 8}▕ #{line.chomp}\e[K\n" if event
if progress
event = 'PROGRESS'
$stdout.write " #{event.ljust 8}▕#{progress}\e[K\r"
progress = nil if percent >= 100
newline = true
end
ensure
$stdout.flush
end
ensure
$stdout.write "\n" if newline
$stdout.write "--\n"
$stdout.flush
end
puts "Exited with: #{exit_code}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment