Collapsing changesets: Draft guide for Dedalus workflows

38 views
Skip to first unread message

Jeffrey S. Oishi

unread,
Sep 30, 2015, 12:29:48 PM9/30/15
to dedal...@googlegroups.com
Hi All,

Here's a bit of a work in progress draft taken from a document I'm working on that will form a "Contribution Guide" for Dedalus. Eventually, in the not at all distant future, I hope this will end up in the documentation. However, as of right now, it's a disjointed mess of notes sitting on my laptop. Below is the relevant section on rebasing changes; attached are the two scripts it references.

Please let me know if you have any questions

In short, we would like a cleanish history, but we are not interested in going crazy rewriting history to preserve a perfectly linear commit log (some projects do this). We just want to minimize noise in the commit log (e.g. "fixed typo!"; "oops, nope it was right the first time"; "no wait..."). I'm pretty guilty of these kinds of commits, and I don't necessarily see them as inherently bad. However, when proposing new features to the core of the code, we should make sure everything is as easy to debug as possible. That said, here is the first cut at explaining rebasing and phases in hg.

* Readying your work for Pull Requests

There are (at least) two basic ways you can use hg. You can use it as a "work log", in which you make many many commits, some of which do not work, some of which will be entirely reverted, one of which (hopefully the last!) is a correct, bug-free, new feature. The other main way is to only commit when the feature you're working on passes all tests.

If you (like me) use the "work log" approach, when it comes time to issue a pull request to have your completed new feature accepted into Dedalus, we ask that you clean up the changelog a bit. This is especially true if you have a complete rewrite of the feature during the pull request peer review process.

There is a simple way to "collapse" many changesets into a single one that implements only the final version of the code you want to have pulled into Dedalus. However, there are a few subtleties that we have to address. The first thing we need to do is understand "phases" in hg.

** hg phases

Internal to hg are three phases, which are ordered as follows:

public < draft < secret

hen changes are shared (by push/pull), the phase marker is lowered to the lowest common setting, typically, this is "public".

In a typical Dedalus workflow, one develops changesets on some local clone of the repo. These will begin as "draft" changesets. However, if you push those changesets to your fork of Dedalus on Bitbucket, they become public.

** Rebasing

For the purposes of future debugging, we would like the master Dedalus repository history to be as clean as possible. In order to maintain this cleanliness, it is good practice to "collapse" many of the small, unimportant, incremental changesets into a few important changesets. You can use the rebase tool in hg to do this.

The simplest way to do this is if all the changesets you've made are still draft: that is, you haven't pushed to your bitbucket fork of Dedalus. Let's say you've just added changesets D and E, where D is an incremental change you don't want to keep (there can be arbitrary numbers of changesets between C and E; I've just used one to keep it simple). If everything is local, and $ID_X is the changeset hash of change X, you can simply do

$ hg rebase -r $ID_D::$ID_E -d $ID_C --collapse

This will destructively rewrite the history of your local working copy so that changeset D never happened. The commit log that rebase generates by default will keep all of the log messages from the previous commits and append them to the final log message. You can give rebase -m "message here." of whatever message you'd like, just as with commit.

However, let's say you've already pushed changeset D and E to bitbucket. Now, D's phase has been changed to public, even on your local copy. You can't just destructively rewrite history. So, instead, you'll do the same thing, but use the --keep option to rebase:

hg rebase -r $ID_D::$ID_E -d $ID_C --collapse --keep

If you forget --keep, you'll get an error message like this:
 abort: can't rebase immutable changeset <$ID_D>

What this will do is create a new head with the collapsed changeset, while keeping the old, messy history. To push this to bitbucket, you'll have to --force

$ hg push --force

because it will create a new remote head on your bitbucket fork. You can just update the pull request to this head, and then the final changes will be one single commit.

In order to demonstrate this, attached are two simple bash scripts you can run that will create a sample repository, a fake "bitbucket" clone that lives right next to it in the parent directory, and then runs all of the above workflows. rebase_example.sh gives the typical Dedalus workflow when you have already pushed your commits to your fork on bitbucket. rebase_nokeep.sh gives the simpler case you may want to use going forward, where you eliminate any incremental changesets BEFORE you push to bitbucket. In real life, you'll need to use a combination of both of these workflows, since pull request review will likely lead to additional changes, some quite large.


rebase_example.sh
rebase_nokeep.sh

Ben Brown

unread,
Sep 30, 2015, 12:43:27 PM9/30/15
to dedal...@googlegroups.com
Jeff,
     Thank you for writing up this excellent guide!

Pull request reviews and revisions will require the public rebasing, correct?

What's the best way to close out old heads (e.g., the old messy history) after a PR is complete?  Is there a way to prune them out of the visible head list to reduce confusion?  I have some memory that there is within mercurial, but I'm unclear how it works with bitbucket.

--Ben

--
You received this message because you are subscribed to the Google Groups "Dedalus Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dedalus-dev...@googlegroups.com.
To post to this group, send email to dedal...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dedalus-dev/CAAOZTN8JNObyL3Ah0nCcZunfe-nsHdpRMRL9Tw1AUY6z9FVBfQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Jeffrey S. Oishi

unread,
Sep 30, 2015, 12:47:06 PM9/30/15
to dedal...@googlegroups.com
Hi Ben,

Yes, there is, but stripping changesets can get very messy if you have multiple copies of your dev fork. I'll write up some instructions for that later.  The reason I don't just tell you some commands is I want to make sure I test everything first. Rewriting history on public changesets is possible, but can lead to massive confusion. As long as that's in your own dev fork, it might not matter much, but I want to come up with some good practices before giving advice.

j

Ben Brown

unread,
Sep 30, 2015, 12:49:33 PM9/30/15
to dedal...@googlegroups.com
Jeff,


On Wed, Sep 30, 2015 at 10:46 AM, Jeffrey S. Oishi <jso...@gmail.com> wrote:
..., it might not matter much, but I want to come up with some good practices before giving advice.


Seems very wise.  Really appreciate your effort on sorting this all out and in helping the rest of us learn!

--Ben

 
Reply all
Reply to author
Forward
0 new messages