Recently, I've been working using the Git Flow approach. While implementing a feature, I found some code that I had written poorly before. There's that great quote by Robert C. Martin,
Always leave code cleaner than you found it.
So I decided to follow his advice and make the changes. But I was silly and didn't change my branch. Now my feature had all the new stuff... and all my fixes to stuff that wasn't really related. The problem was that I didn't work on the feature and the fixes linearly. There were interwoven commits (even though they were almost completely independent - apart from the package installations). So instead of creating a pull request with my feature and a bunch of fixes, I decided I wanted to break up the branch into multiple branches and create a pull request for each. But what is the easiest way to do this?
Before we jump into the (easiest) way that I found, let me show you a little demo. I have a project that has two
.js files (
second.js). Imagine that these are completely unrelated,
I then decided to start up a new feature and add a new file called... you guessed it...
But while I was working on it, I realised a couple of flaws in
second.js and made changes to them both. I then continued to add code to
third.js to finish up the feature.
Mistakes were made, but how do we fix them?
There are a few ways to fix this. The two that I'll point out are
git rebase and
git cherry. We could use
git rebase to remove some of the commits and keep others and we could use
git cherry to create new branches (off
develop) and pull certain commits in. Both are good options, but in this tutorial I'm going to run through using
git rebase. I'd like to give willoller some credit for his answer in stackoverflow, this is pretty much a visual representation of what he said.
I'd also like to give credit to Dan Gitschooldude for the
git cherry video which was super insightful and you should check out if you haven't used this before.
So first we need to copy the current branch, in my case
feature/new-amazing-idea into multiple branches (1 backup and then the number of branches we'd like to end on). For me that's 3 branches in total. First I'll rename this branch,
git branch -m feature/backup.
Now create all of the branches you'd like by the end of the modifications,
Sweet! We're ready to go, let's start with the
feature/new-amazing-idea branch. First we need to check it out,
git checkout feature/new-amazing-idea.
We're going to use
git rebase which means that we're going to rewrite the history of this branch. The first step is to specify how many commits to look back during this rewrite process. In my case, it's 8 commits between the
develop. So I started up an interactive rebase by running
git rebase -i HEAD~8. I use
vim as my editor so the output may look a bit different to yours but this is what I see,
So now we can see all of the commits that we're editing and the action that should be applied to each commit on the far left. I then changed "pick" to "drop" for all of the commits that did not form a part of my new feature.
When you're done, save the file and close it.
In my case, I had a merge conflict :/... This is because
package.json was updated in multiple commits. I removed the conflict and committed the changes.
Once I'd fixed the conflicts, I ran
git rebase --continue to see if there were anymore conflicts that needed to be resolved. In my case, there weren't anymore,
Sweet so that's the feature branch done! The log now looks as follows,
Now I'm going to extract the data for the second feature I had going on here. It's pretty similar to the previous section. First check it out,
git checkout feature/secondjs-async-workflow and then run rebase,
git rebase -i HEAD~8.
That was a success :)
Finally, I need to set up the
fix/firstjs-defaults branch. This is how my rebase output looks,
And again, it was a successful rebase :)
Final Git Log
So now we have all of our branches with the correct commits,
I think it's time to delete the backup branch :O Run
git branch -D feature/backup and check out the logs,
The merge conflict commit isn't ideal, but I'd much rather have that than a branch that has multiple features that kills my code reviewer. PS. You may have noticed that I spelt
moment-timezone incorrectly in my commit. The
git rebase command lets you modify your commit messages as well using the
Now my commit message doesn't have any typos!
We're now ready to create 3 pull requests and our code reviewers will have a much easier time reviewing the code, since there are fewer lines and they all share the same responsibility. I hope this helps in future projects when you get lost like me and decide to make changes before switching branches :P