ivan ivan - 10 days ago 6
Git Question

Git: break a large+messy feature branch into smaller branches

I have a feature branch that has grown and grown, with lots of twists and turns as the feature evolved. I'd like to break some separately-releasable chunks off into their own branches.

The commits aren't clean enough to build such branches by selecting by commit (i.e. some commits touch multiple files, only some of which I want to select). I'm a deft hand at interactive-rebasing, hunk-editing, etc. but I wonder if there's a better way to do this?

My initial thought is to create a branch that's a copy of the original feature branch, squash all it's commits into 1, reset to the previous commit (leaving the full uncommitted, unstaged feature branch in my working tree), then selectively add and commit the pieces I want.

$ git co -b partial-feature
$ git rebase -i HEAD~170


In the editor:

pick 234e78fa Begin writing feature
f 7844c437 Add more stuff
f 3523437 And even more
...


Then,

$ git reset HEAD~
$ git add first_good_change.rb
$ git commit -m 'Add a good change'
$ git add second_good_change.rb
$ git commit -m 'Add another good change'
...


Is there a smarter way?

Answer

That's basically quite fine, but it can be a bit simpler.

My initial thought is to create a branch that's a copy of the original feature branch, squash all it's commits into 1, reset to the previous commit

That's equivalent to a soft reset to the start of the branch. That's a single command, instead of a rebase followed by a reset.

git checkout -b partial-feature
git reset start

Where start is the SHA1 where you want the branch to start from. This is decided different from HEAD~170, because you don't need to calculate 170, just simply review git log and find the suitable SHA1.

After this, continue as you did:

git add first_good_change.rb
git commit -m 'Add a good change'
git add second_good_change.rb
git commit -m 'Add another good change'

And so on. At any point, if you want, you can continue on another feature branch, to unleash to reviewers changes little by little, making their job easier, for example:

git checkout -b partial-feature-round-2
git add more_good_change.rb
git commit -m 'Add a good change'
git add even_more_good_change.rb
git commit -m 'Add another good change'

And so on. Just make sure to not post partial-feature-round-2 up for review before partial-feature is merged.

After everything of the original branch is committed, the content of the project will be identical to the original branch, just the history will look different. It's perfectly normal to slice and dice your history like this.

It's not always possible to split a branch to smaller branches this way, for example when the changes are too inter-related that it's difficult to separate to chunks that still work. That is, when you post partial-feature for code review, it had better compile and fully work, and this might be difficult to achieve if the changes are too complicated.

Last tip: while building a partial branch for a multi-staged code review, an easy way to verify that the partial feature still works and in a code-reviewable state, is to stash the remaining changes, and compile and run tests. If all good, the branch is ready for code review, you can create a pull request, and then continue to the next stage, popping the stash and adding more commits.