Created
November 4, 2020 19:37
-
-
Save locofocos/7c7374d872145cc80c9ff9da29b6e78f to your computer and use it in GitHub Desktop.
Untangles a few git branches into a linear history by rebasing.
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
# Untangles a few git branches into a linear history by rebasing. | |
# | |
# Setup: | |
# | |
# In your .gitconfig file, create an alias like so (first line is Windows, 2nd line unix): | |
# | |
# [alias] | |
# untangle = !ruby /c/DEV/git_untangle.rb | |
# untangle = !ruby ~/git_untangle.rb | |
# | |
# | |
# Usage: | |
# | |
# Find the branch names that need to be untangled. | |
# Sort them into the final intended order (like "A B C", where A is the earliest branch and C is the latest branch) | |
# Run- | |
# git untangle A B C | |
# | |
# One assumption made by this script: You're the only person changing | |
# the states of these branches. So if someone else rebases a branch | |
# which you've branched off of, this script may or may not help you, | |
# probably depending on whether or not their commits changed anything | |
# (i.e. whether it's an easy case or hard case, https://git-scm.com/docs/git-rebase#_recovering_from_upstream_rebase) | |
# You could, however, have a great time if you assign a single person to | |
# run this script, rebase all the branches in one go, then have everyone else | |
# reset their branches to the copy from origin. | |
### A command to run at every step of the rebase for verification (or nil if you don't want to): | |
#command_line_verify = 'bundle exec rspec' | |
#command_line_verify = 'mvn compile test-compile checkstyle:check' | |
#command_line_verify = 'mvn clean install -DskipTests -DskipITs -Djacoco.skip=true -Dcheckstyle.skip=true' | |
command_line_verify = nil | |
### True if you want to interactively rebase. Feel free to use this to squash commits. | |
# interactive = true | |
interactive = false | |
# Grab the branch names | |
branch_names = ARGV | |
puts 'Branches:' | |
puts branch_names | |
puts | |
# Grab the original commit hash for each branch | |
original_branch_hashes = branch_names.map { |branch| `git rev-parse #{branch}`.strip! } | |
exit unless $?.success? # exit if the command failed | |
puts 'Original commit hashes:' | |
puts original_branch_hashes | |
puts | |
# Rebase each branch based off the original commit hash of the previous branch | |
index = 1 # the first branch doesn't need to be rebased | |
while index < branch_names.size do | |
this_branch = branch_names[index] | |
prev_branch = branch_names[index-1] | |
prev_branch_orig_hash = original_branch_hashes[index-1] | |
command = "git checkout #{this_branch}" | |
puts command | |
`#{command}` | |
command = "git merge-base #{prev_branch_orig_hash} #{this_branch}" | |
puts command | |
mergebase = `#{command}` | |
command = "git rebase " | |
command += "-i " if interactive | |
command += "-x \"#{command_line_verify}\"" if command_line_verify | |
command += " --onto #{prev_branch} #{mergebase}" | |
puts command | |
success = system(command) | |
puts | |
unless success | |
puts 'Open another terminal, fix the issue, finish the rebase, then return here and press enter to continue...' | |
STDIN.gets | |
end | |
index+= 1 | |
end | |
puts 'Done! Make sure the graph structure looks good, then force push each of the branches: (copy/paste if you dare)' | |
puts | |
branch_names.each do |branch_name| | |
unless branch_name == 'master' | |
puts "git checkout #{branch_name}" | |
puts "git push --force-with-lease origin #{branch_name}" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment