Skip to content

Instantly share code, notes, and snippets.

@DimaSamodurov
Last active September 17, 2024 14:43
Show Gist options
  • Save DimaSamodurov/0e29828342c233102c5b26a2bea057e9 to your computer and use it in GitHub Desktop.
Save DimaSamodurov/0e29828342c233102c5b26a2bea057e9 to your computer and use it in GitHub Desktop.
List nested branches down to the master branch based on PR sequence (using GitHub command line)
module Colors
RED = 31
GREEN = 32
YELLOW = 33
BLUE = 34
end
class String
def colorize(color_code)
"\e[#{color_code}m#{self}\e[0m"
end
def red
colorize(Colors::RED)
end
def green
colorize(Colors::GREEN)
end
def yellow
colorize(Colors::YELLOW)
end
def blue
colorize(Colors::BLUE)
end
end
#!/usr/bin/env ruby
require_relative 'colorize'
require 'json'
# Script simplifies applying changes to the sequence of PRs.
# Scenario: You implement a feature in a long running branch but split by smaller, easy to read PRs: prA, prB, prC etc.
# so that we have sequence of branches: master > branchA, branchB, branchC etc.
# sometime(e.g. for testing), you need to synchronize updates from lower branches through entire sequence to the top.
# One strategy would be to merge all these branches into combined branch, another strategy - to merge branches bottom up:
# master to branchA, branchA to branchB, branchB to branchC etc.
# Current scrips helps running commands of this strategy.
#
# To use run:
#
# list-prs.rb <top-branch>
#
# As a result, sequence of branches will be saved to tmp/branch-sequence.txt, you can review it
#
# Then run merging in interactive mode:
#
# merge-branches.rb tmp/branch-sequence.txt
#
# Enjoy.
def get_pr_nesting(branch, base)
nesting = []
loop do
print '.'
output = `gh pr view --json baseRefName,headRefName,url,title,statusCheckRollup #{branch}`.strip
break if output.empty?
json = JSON.parse output
nesting << json
branch = json.fetch("baseRefName")
print("\n") && break if branch == base
end
nesting
end
def is_green?(pr)
pr.fetch('statusCheckRollup').select{|check| check['__typename'] == 'StatusContext'}.
all? { |check| check['state'] == 'SUCCESS' }
end
###
branch = ARGV[0] || `git rev-parse --abbrev-ref HEAD`.strip
prs = get_pr_nesting(branch, ENV['base'] || 'master')
prs.reverse.each_with_index do |pr, i|
color = is_green?(pr) ? Colors::GREEN : Colors::RED
puts "#{(i+1).to_s.ljust(4).colorize(color)} #{pr['url']} #{pr['headRefName']}"
end
prs = (prs.map{|pr| pr['headRefName']} + ['master']).reverse
File.write 'tmp/branch-sequence.txt', prs.join("\n")
puts 'The list of PRs saved to "tmp/branch-sequence.txt" file'
#!/usr/bin/env ruby
require_relative 'colorize'
require 'io/console'
# This script helps merging sequence of branches
# stored in file specified as first argument (default tmp/branch-sequence.txt)
Input = Struct.new(:input) do
def continue?
["\r"].include? input
end
def skip?
[' '].include? input
end
end
def prompt(title,
message = "Press #{'Enter'.blue} to continue, #{'Space'.blue} to skip, or #{'any input'.blue} to cancel.")
puts title if title
puts message if message
Input.new(STDIN.getch).tap do |input|
next if input.continue?
puts 'Skipped.' and next if input.skip?
puts 'Cancelled.'
exit
end
end
prs = File.readlines(ARGV[0] || 'tmp/branch-sequence.txt').map(&:chomp)
puts 'Merging nested branches in order from top to bottom:'
puts prs.join("\n")
puts
prs.each_cons(2) do |a, b|
puts '------------------------------------------'
puts "Updating #{b.green}"
puts `git switch #{b}` # that will display branch status
status = `git status`.chomp
if status.include? 'Your branch is behind'
input = prompt 'Going to pull'
puts `git pull` if input.continue?
end
input = prompt "Merging #{a.green} into #{b.green}"
next puts 'Skipped' if input.skip?
# unlike ``, `system` command runs in interactive mode, opening editor with merge message if any
system "git merge #{a}" if input.continue?
puts status = `git status`.chomp
if status.include? 'branch is ahead of'
input = prompt 'Do you want to push?'
next puts 'Skipped' if input.skip?
puts `git push` if input.continue?
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment