Skip to content

Instantly share code, notes, and snippets.

@scotje
Created November 13, 2015 23:31
Show Gist options
  • Save scotje/58257c6f7815cb0c0575 to your computer and use it in GitHub Desktop.
Save scotje/58257c6f7815cb0c0575 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'rubygems'
require 'fileutils'
require 'word_salad'
def decorate_log_with_branch(log, branch_name, root_sha, origin=[])
cursor = root_sha
log[root_sha][:branch] = branch_name
while prev = log[cursor][:parents][0] do
# Break if we are following a branch and we reach the common ancestor.
break if origin.include?(prev)
log[prev][:branch] = branch_name
cursor = prev
end
end
def random_branch_name
candidate = 2.words.join("_")
while @_names_used.include?(candidate)
candidate = 2.words.join("_")
end
@_names_used << candidate
return candidate
end
@_names_used = []
repo = ARGV[0]
mainline_branch = ARGV[1]
replay_depth = ARGV[2]
# R.I.P. memory
puts "Parsing git-log..."
commits_array = `/usr/bin/env GIT_DIR=#{repo} git log --branches --format="%H%x09%P%x09%s"`.lines.collect do |log_line|
sha, parents, subject = log_line.split("\t")
[sha, {parents: parents.split(" "), subject: subject}]
end
log = Hash[commits_array]
puts "Finding main branch history..."
mainline_head = `/usr/bin/env GIT_DIR=#{repo} git rev-parse #{mainline_branch}`.strip
decorate_log_with_branch(log, mainline_branch, mainline_head)
mainline_history = log.map { |sha, commit| sha if commit[:branch] == mainline_branch }.compact!
puts "Finding unmerged branches..."
head_refs_array = `/usr/bin/env GIT_DIR=#{repo} git show-ref --heads`.lines.collect do |ref_line|
sha, branch = ref_line.split(" ")
branch.gsub!(/refs\/heads\//, '')
@_names_used << branch
[branch, sha]
end
head_refs = Hash[head_refs_array]
head_refs.delete(mainline_branch) # We already walked mainline branch
head_refs.each do |branch, sha|
decorate_log_with_branch(log, branch, sha, mainline_history)
end
if depth = replay_depth.to_i
puts "Trimming to last #{depth} commits..."
trimmed_log = log.take(depth)
else
trimmed_log = log
end
puts "Finding merged branches..."
trimmed_log.each do |sha, commit|
if mainline_history.include?(sha) && commit[:parents].size == 2
new_branch = random_branch_name
log[sha][:ends_branch] = new_branch
decorate_log_with_branch(log, new_branch, commit[:parents][1], mainline_history)
end
end
puts "Pruning orphan commits..."
trimmed_log.reject! { |sha, commit| !commit[:branch] }
puts "Replaying commits in original order, send USR1 to PID #{Process.pid} to advance..."
trimmed_log.reverse_each do |sha, commit|
@next = false
Signal.trap("USR1") { @next = true }
sleep(0.5) until @next
log_line = " #{commit[:branch]} -> #{sha}"
File.open("#{repo}/refs/heads/#{commit[:branch]}", "w") do |f|
f.puts(sha)
end
if commit[:ends_branch] && File.exists?("#{repo}/refs/heads/#{commit[:ends_branch]}")
FileUtils.rm("#{repo}/refs/heads/#{commit[:ends_branch]}", force: true)
log_line << " (Deleted branch #{commit[:ends_branch]})"
end
puts log_line
end
puts "Done!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment