I pushed to trunk from the feature branch and now everything is messed up! What do I do?

I had created a local branch to update the shaders for the "brick" scenario in glcompbench:

$ cd trunk
$ bzr pull
$ cd ..
$ bzr branch trunk brick

My updates to the brick shaders took just long enough that another feature branch was merged into trunk in the meantime. In its own helpful way, bzr told me I was missing files and that I should use merge to update:

$ cd brick
$ bzr merge ../trunk
$ bzr commit

So far, so good :) . At this point I merged in the changes from trunk, which is correct, although not strictly necessary (option 2 above: what to do about trunk changes during feature development). This left me with just my shader changes (data/brick.{vert,frag}), which I then pushed to trunk:

$ bzr push lp:glcompbench

This was where things went wrong :( . I pushed to trunk from within the feature branch, thinking with the best of intentions that I really had a trunk branch plus a small set of changes. No information was lost, but the mainline of the feature branch became the mainline of the upstream trunk (lp:glcompbench). If you could use 'bzr viz' on this version of trunk you would see how the brick branch is now the trunk mainline (the "main line" on the left) and the original mainline commits became part of a branch of the new mainline:

Merge trunk
 |     \
 |      2011.08
 |      |
 |      ...
 |      |
brick   ...
 |     /
2011.07

This is possible in a DVCS because all branches are equal; any branch can be the mainline. In each feature branch, the mainline consists of the commits for that feature.

To avoid this you should always push to upstream trunk (e.g., lp:glcompbench) from the local copy of trunk. In this case I should have merged the new brick changes back into trunk:

$ cd trunk
$ bzr merge ../brick
$ bzr commit

This would have ended up looking like:

Merge feature branch
 |     \
 |     Merge trunk
 |    / |
2011.08 |
 |      |
 ...   brick
 |     /
2011.07

As I said before, merging from trunk into brick was not really necessary (it just makes it easier to handle conflicts for complicated long-standing feature branches if done once in a while). If I had just merged the brick branch into trunk without syncing with trunk first, I would have:

Merge feature branch
 |     \
2011.08 |
 |      |
 ...   brick
 |     /
2011.07

So, at this point, all of the files are fine, but the revision history doesn't reflect what we want to see, so here's the remedy:

1. Recover previous branch tip and push it to launchpad:

$ bzr branch lp:glcompbench broken-trunk
$ bzr branch -r tag:2011.08 broken-trunk trunk
$ cd trunk
$ bzr push --overwrite lp:glcompbench

2. Now apply the feature branch changes (data/brick.*) correctly to trunk:

$ bzr branch -r <XX> broken-trunk brick

where <XX> is the revision introducing the changes.

Now brick contains the original feature branch. From here there are many options. We went with rebasing because it's cleaner for single commits:

$ cd brick
$ bzr rebase ../trunk

Rebase the shader fixes (the <XX> commit) over the new trunk:

$ cd ../trunk
$ bzr pull ../brick

Just pull in the extra commit (no merging):

$ bzr push lp:glcompbench

This leads to this:

brick
 |
2011.08
 |
 ...
 |
2011.07

Of course, this whole process changed the history of lp:glcompbench, so any branches that were branched from the "broken" version of it (e.g. another branch I had to update the "Program" object for managing shader programs) are now broken. In this case, I reapplied the patch to a fresh branch and pushed it back to launchpad.

Resources/HowTo/BZR-FeatureBranches/BZR-MergeUnravelling (last modified 2011-09-14 16:08:38)