Skip to content

Instantly share code, notes, and snippets.

@willhbr
Created February 8, 2026 10:01
Show Gist options
  • Select an option

  • Save willhbr/c7e459c95f52c3e6bea8096c36f10bb3 to your computer and use it in GitHub Desktop.

Select an option

Save willhbr/c7e459c95f52c3e6bea8096c36f10bb3 to your computer and use it in GitHub Desktop.
require "json"
require "set"
require "tempfile"
def get_chain
output = `jj log -r 'trunk()..@' --no-graph -T '"[" ++ json(description.first_line()) ++ "," ++ json(change_id) ++ "]\n"'`
if $? != 0
raise "jj failed"
end
output.each_line.map do |line|
desc, change = JSON.parse(line)
[desc, change]
end.to_a
end
def formatted(chain)
lines = chain.map do |item|
desc, change = item
if desc.empty?
desc = 'no description'
end
"#{change} # #{desc}"
end.join("\n")
"JJ: newest\n#{lines}\nJJ: oldest"
end
def parse_order(contents, previous)
ids = Set.new(previous)
changes = []
contents.each_line do |line|
m = line.match(/^(\w+) #/)
unless m
puts "invalid line: #{line}"
next
end
id = m[1]
unless ids.include? id
puts "wrong id: #{id}"
next
end
changes << id
end
changes
end
def histedit(old_order, new_order)
system 'jj', 'parallelize', old_order.join('|')
prev = 'trunk()'
new_order.reverse_each do |change|
system 'jj', 'rebase', '-s', change, '-o', prev
prev = change
end
end
chain = get_chain
change_order = chain.map { |c| c[1] }
Tempfile.create ['jj-histedit-', '.jjdescription'] do |file|
fmt = formatted(chain)
File.write(file, fmt)
system 'vim', file.path
altered = File.read(file.path)
if altered == fmt
puts "no changes"
exit 0
end
new_order = parse_order(altered, change_order)
histedit(change_order, new_order)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment