2010年12月17日星期五

Mercurial workflows: Use REBASE to replace MERGE

Mercurial workflows: mainline workflow



The simplified workflow

In the simplified workflow, it is nearly identical to the normal centralized
workflows (except most operations are local).  When I want to start work
for the day, I’ll:
  • work work work
  • hg commit –Am “Working on some stuff”
  • work work work
  • hg ci –Am “Working on some more stuff”
  • work work work
  • hg ci –Am “Finished my stuff”
At this point, I’ve finished some logical set of work, and I’d like to push my work upstream.  My local repository now looks like:
image
Unlike the previous workflow, my “master” bookmark moves along, instead of always pointing at the latest pulled commit.  It’s still important that this bookmark sticks around, as we’ll see soon.  Now that I want to push, I want to first pull down incoming commits.  Let’s suppose that someone else also made some commits on another repository and already pushed.  The server repository shows this:
image
Note that we don’t see our bookmarks here, as by default bookmarks don’t get pushed upstream.  When we pull from upstream, we’ll get the commit from the “otherdude” developer.  So, I’ll:
  • hg pull --rebase
Instead a “pull/merge/update” workflow, which generates noisy merge commits, I’ll rebase my three commits against the upstream changes.  Rebase simply replays my three commits against the incoming tip.  That would mean that I expect to see that the parent of “Working on some stuff” to be the “otherdude” commit instead of the “Finishing work on a feature” commit.  After the pull and rebase, my local repository is now:
image
This is what we expected, our commits that originally came after the “Finishing work on a feature” commit got moved AFTER the “otherdude” commit.  This produces a nice clean timeline that makes localizing bugs and merging changes a lot easier.  With a regular pull/merge workflow, you’re merging all 3 commits at once.  With a rebase, I merge one commit at a time, making the potential merges much smaller.  Each merge also modifies each commit, instead of one gigantic merge commit with all changes coming in at once.
Anyway, I’ve pull upstream changes, so now I’m ready to push:
  • hg push –b master
I only want to push that mainline branch, “master”, just like my previous workflows.  By pushing only my “master” branch, I can transfer back and forth between my simplified, mainline workflow and topic branch workflow very easily, with neither conflicting with the other.  In Git, only the current branch is ever pushed by default, but in Hg, it’s the opposite, so I have to a bit more explicit.

Comparing rebase and merge

Just to show what a merge looks like in comparison, let’s say “otherdude” doesn’t rebase before he commits his additional work.  He has some more commits:
image
Now he wants to push these two commits up.  However, the other user already pushed rebased commits, so now the server looks like:
image
Instead of doing a rebase on pull, he does a regular pull and update.  Because other commits have gone in, he’ll need to do a merge:
  • hg pull –u
  • hg merge
  • hg ci –Am “Stupid merge commit”
He tries to pull and update, but since there were commits there, two heads get created and he needs to merge in his changes.  This causes an extra merge AND commit step, and now uglies up the history:
image
So the silly thing about this is the stupid merge commit contains ALL changes from the other two outgoing commits, yet all three commits get pushed!  This also becomes really ugly over time, especially when you have overlapping commits and merges:
image
I’m not sure human beings are meant to comprehend this picture, so I’ll take the rebased workflow with its clean, linear history any day of the week.  With the simplified workflow, rebasing is actually simpler than the pull/merge/commit workflow.  Rebase is good, whether we work in topic branches or not.



source: mercurial-workflows-mainline-workflow

1 則留言:

  1. Use "pull --rebase" seems to be more neat than "pull/merge/update" approach, if the intermediate commits are not completed and make no sense for others to see the details of those changesets.

    回覆刪除