Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

git

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?

like image 803
ivan Avatar asked Nov 29 '16 18:11

ivan


2 Answers

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.

like image 154
janos Avatar answered Nov 19 '22 21:11

janos


Your way is pretty darn good and is pretty much the exact approach I'd go with for a fair number of these cases. If instead of committing file-by-file, though, you want to commit changes across multiple files simultaneously, you might be better served by keeping one window open for git add -i and a second to git commit interactive changes.


The only alternative I would suggest, which I've used in the past when rewrites and git adds got particularly nasty, would be copying the final files out of the repo, roll back the branch (including the working tree), and then selectively copy stuff over and git commit -am stuff in. Something like:

# starting from branch with messy commit histories
cp file1 file2 file3 [...] ~/final_versions
git reset --hard <good_commit>
vim -p file1 file2 file3 [...] ~/final_versions/*

and meanwhile, in another window, git commit -am.

like image 30
Pockets Avatar answered Nov 19 '22 21:11

Pockets