Skip to content

Instantly share code, notes, and snippets.

@aaronhipple
Created February 3, 2017 18:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aaronhipple/fc830929364624799793e72865e5fe01 to your computer and use it in GitHub Desktop.
Save aaronhipple/fc830929364624799793e72865e5fe01 to your computer and use it in GitHub Desktop.
Rebase Scenario
#!/bin/bash
# Generate a rebase practice scenario with a dirty commit.
# Don't run this in a real git repo, obviously!
git init
echo 'Initial data' > my-file.txt
git add my-file.txt
git commit -m 'Initial commit'
for (( i = 1; i <= 10; i++ )); do
echo "Good commit ${i}" >> my-file.txt
git add my-file.txt
git commit -m "Good commit ${i}"
done
git checkout -b 'new-branch'
for (( i = 11; i <= 15; i++ )); do
echo "New commit ${i}" >> my-file.txt
git add my-file.txt
git commit -m "New commit ${i}"
done
git checkout master
git checkout -b dev
echo "Dirty commit" > dirty-file.txt
git add dirty-file.txt
git commit -m "Dirty commit"
git checkout new-branch
git merge dev -m 'Merge dev -- this introduces a dirty commit!'
for (( i = 16; i <= 20; i++ )); do
echo "New commit ${i}" >> my-file.txt
git add my-file.txt
git commit -m "New commit ${i}"
done

A Quick Rebase Scenario

I was working on some changes for TRT, back and forth. On completing the changes and submitting a pull request, I realized that I'd accidentally pulled commits from 'dev' into my feature branch (totally my bad, still learning the workflows here!)

I had about 20 commits on my feature branch, and there were about 30 commits from dev in the mix, most from before I'd branched from master. It may have been safe to merge those into master, but I don't personally feel comfortable making that call, and I was at the end of my day -- I just wanted to get my PR submitted.

Rebase to the Rescue

I considered trying to cherry pick commits onto a clean branch -- but with 20 or so commits to work with and a few peppered in, that seemed tedious. Instead I opted to use rebase to play the new commits back cleanly onto master. Rebase offers an 'interactive' mode that makes it really easy to choose which commits to include and which to drop.

Some initial setup

You may be familiar with the view offered by git rebase -i ....

pick 26278bd New commit 1
pick 577398a New commit 2
pick 1ba508d New commit 3
pick 2b15141 New commit 4
pick 65df0de New commit 5

# Rebase 3b16333..65df0de onto 3b16333 (5 commands)
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

This works nicely, but in my case I wanted to see the committer's name next to each commit so I could more easily see which commits to include. That's easy to do by modifying your ~/.gitconfig like so:

[rebase]
    instructionFormat = %s [%an]

Or equivalently in your terminal

git config --global rebase.instructionFormat '%s [%an]'

Now...

pick 26278bd New commit 1 [Aaron Hipple]
pick 577398a New commit 2 [Aaron Hipple]
pick 1ba508d New commit 3 [Aaron Hipple]
pick 2b15141 New commit 4 [Aaron Hipple]
pick 65df0de New commit 5 [Aaron Hipple]

# Rebase 3b16333..65df0de onto 3b16333 (5 commands)
# ...

Getting The Right Commits in the Right Place

Now to actually run our rebase. The idea is to take a range of commits and replay them onto the master branch, using the interactive option to allow us to drop certain commits along the way. What I did was determine the last commit hash from my branch using git log and then

git rebase -i <clean starting commit> <the last commit from our branch>
git rebase -i master 65c10a7

This results in an interactive rebase screen like this:

pick d7c5437 New commit 1 [Aaron Hipple]
pick 681a602 New commit 2 [Aaron Hipple]
pick d8d4b6e New commit 3 [Aaron Hipple]
pick bea7792 New commit 4 [Aaron Hipple]
pick be55ec2 New commit 5 [Aaron Hipple]
pick 9eebf2d Dirty commit [Aaron Hipple]
pick ca0f456 New commit 6 [Aaron Hipple]
pick b46558e New commit 7 [Aaron Hipple]
pick 409657c New commit 8 [Aaron Hipple]
pick bc3cf17 New commit 9 [Aaron Hipple]
pick e41d34b New commit 10 [Aaron Hipple]

# Rebase 24f4a73..e41d34b onto 24f4a73 (11 commands)
# ...

On this screen we can then just remove our dirty commit (or instruct it to 'drop' instead of 'pick') and then save our changes. This lands us in a detached head state, from which we can quickly save a new, clean branch, and push that. Easy peasy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment