Skip to content

Instantly share code, notes, and snippets.

@djmitche
Last active September 19, 2016 15:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djmitche/861025ac5324a8333049100378e58c6d to your computer and use it in GitHub Desktop.
Save djmitche/861025ac5324a8333049100378e58c6d to your computer and use it in GitHub Desktop.
Mercurial and Mozreview

Mercurial

Conceptual

Not much like the Github / Git-flow model. "Branches" are implemented as repositories, and we have a few types of repositories:

  • try -- pushes here parse "try syntax" which defines the jobs you want to run; used for testing
  • integration -- this is the entry into mozilla-central; patches here are tested but will be backed out by sheriffs if they cause failures (mozilla-inbound and autoland)
  • mozilla-central -- the "trunk", latest known-good, branch (nightly is built from here)
  • mozilla-aurora, -beta, etc. -- release branches
  • pine, alder, ash, etc. -- project branches / twigs

These all share the same version-control history, with different "leaves"; sheriffs regularly merge things from integraiton branches into central, and from central into release branches.

Note that there are older techniques for using Mercurial that revolve around mq (which is basically a version of the old patches-and-cvs way the kernel was developed). Avoid.

Setup

Get the most recent version of Mercurial, not just the most recent your OS has available. Every bug in Mercurial is fixed in the X.Y.Z release that will be pushed tomorrow afteronon. If you're OK with it, I would recommend sudo pip install mercurial on your development system.

Clone https://hg.mozilla.org/mozilla-central to get the source. Clone it somewhere with a lot of space. Run ./mach mercurial-setup and, for now at least, accept all of its defaults. This will install a bunch of extensions!

My extensions:

[extensions]
graphlog =
#prompt = /home/dustin/lib/python/prompt.py
color =
pager =
rebase =
purge =
histedit =
record =
shelve =
progress =
mq =
blackbox =
firefoxtree = ~/.mozbuild/version-control-tools/hgext/firefoxtree
reviewboard = /home/dustin/.mozbuild/version-control-tools/hgext/reviewboard/client.py
bzexport = /home/dustin/.mozbuild/version-control-tools/hgext/bzexport
bzpost = /home/dustin/.mozbuild/version-control-tools/hgext/bzpost
bundleclone = /home/dustin/.mozbuild/version-control-tools/hgext/bundleclone
mqext = /home/dustin/.mozbuild/version-control-tools/hgext/mqext
qimportbz = /home/dustin/.mozbuild/version-control-tools/hgext/qimportbz
push-to-try = /home/dustin/.mozbuild/version-control-tools/hgext/push-to-try
qbackout = /home/dustin/.mozbuild/version-control-tools/hgext/qbackout

# https://bitbucket.org/facebook/hg-experimental/raw/be7a54df7b36f7e9ac93ae05b9ff7621e32c089c/fbhistedit.py
fbhistedit = /home/dustin/.mozbuild/fbhistedit.py

# temporary until this is a full hg feature..
evolve = /home/dustin/.mozbuild/evolve/hgext/evolve.py

Bookmark-Based Development

Generally you will want to work on top of central, as it is most likely to be working.

hg pull central
hg up central

Now create a bookmark for your work. I generally use a bug number. If you need more bookmarks, file more bugs. Bugs are cheap.

hg bookmark bug1234567

you can see your current bookmark with hg bookmark.

Write your patch. Build with ./mach build. I don't know much more than that about building the browser!

Don't forget that Mercurial works on the svn model: changes to files which are already in the repository will be committed without any kind of hg add, but you will need to hg add new files. Run hg status to see what's up with your repository, noting that it's very slow for this large respository!

To commit, hg commit. The commit message format is:

Bug 1234567: <imperative voice summary>; r?<ircnick>

<longer description of the change>

You can update the most recent changeset with hg commit --amend.

Microcommits

More, smaller changesets are better. If you can break a change down into some independent but still internally self-consistent changes, do so. Especially if different people might review those different changesets. For exmaple, if your new implementation requires refactoring an existing piece of code to be more flexible, make that refactor a changeset of its own, followed by a changeset including your new implementation.

But managing multiple changesets for a single bug starts to get complicated. A bit of Mercurial terminology: changesets have "phases", and the interesting phases here are "draft", "public", and "obsolete". A public changeset has been pushed to a public repository and can't be changed anymore. A draft changeset has not been pushed anywhere interesting and can be edited. A obsolete changeset has been discarded in favor of an updated version -- for exmaple, hg commit --amend will mark the old changeset as obsolete.

So you will need a command to show the draft changesets on the current bookmark. You can use this one, which defines hg wip.

[revsetalias]
wip = reverse((ancestors(.) and not public()) or parents(ancestors(.) and not public()))

[templates]
wip = '{label("log.branch", ifeq(branch, "default", "", branch))} {label("changeset.{phase}", rev)} {label("grep.user", author|user)}{label("log.tag", if(tags," {tags}"))} {label("log.bookmark", if(bookmarks," {bookmarks}"))}\n{label(ifcontains(rev, revset('parents()'), 'desc.here', 'desc.nothere'),desc|firstline)}'

[alias]
wip = log --graph --rev=wip --template=wip

This will show you all draft changesets as well as the public changeset on which they are based.

So you have several draft changesets, say "refactor frobnification", "frobnicate foozles", and "consolidate foozle definitions", and you find an error in the first. You can fix this in two ways.

The first method is to make a single changeset to fix the error, and laterl "roll up" that changeset into the "refactor frobnification" commit. I generally name such roll-up commits starting with "R":

vi testing/frobnification.py
hg commit -m "R refactor frobnification" testing/frobnification.py

Then later, run hg histedit. This tool shows all of the draft changesets (just like hg wip, but in reverse order) and lets you reorder and set actions for them, as described in the comments at the bottom. Find the "R" commit, set its action to "r", and move it so it follows immediately after the "refactor frobnification" commit. When the histedit completes, hg wip will show only the three changesets, and hg diff -c .^^ will show your fix merged into the frobnification refactor.

I often pile up a few "R" commits before histediting. This is easiest when you find an error when you're in the middle of something else, and have a bunch of changed files in your working copy.

The second method is to use hg histedit directly to edit that commit. This requires that your working copy be clean. Find the commit in the list, and change its action to "e". When you run the histedit, hg will stop with all of the files from that changeset marked as un-committed. You can make the fix, test all you want, then run hg histedit --continue which will prompt you to confirm the commit message.

If you have the fbhistedit extension installed, I recommend using "f" instead of "e", as it leaves the existing changeset committed and stops with a clean working copy. You can then fix the changeset with hg commit --amend or, if you decide the fix won't work or accidentally break something else, just clean out your working copy and start over.

Pushing to try

OK, so you've got a nice sequence of microcommits, and your local testing shows them to be correct, but you need to test it on another platform.

The try server enables this. You'll need ti figure out what you want to run -- http://trychooser.pub.build.mozilla.org is probably the easiest way -- and then use hg push-to-try -m "try: ..." to push it. This will automatically add an empty commit containing this syntax (and any uncommitted files in your working copy), push it to try, and then revert the empty commmit (restoring your files). It shows a link to treeherder where you can monitor the progress of your jobs.

Pushing to Mozreview

Once you're happy with the patchset, it's time to get review. Double-check that you've flagged all of your commits with r?<ircnic> for the appropriate reviewer. If you don't know who's appropriate, ask someone -- generally two or three degrees of connection are sufficient to find the correct reviewer.

Push your changes with:

hg push -r . review

This will show you a URL for each of the changesets, plus a final "review summary" link. Hit "n" to skip publishing the review request right away, and click that last link. That will take you to your (unpublished) review request, where you can give it your one once-over first to catch any silly mistakes.

Once you're happy with it, publish the review request. Mozreview will make a bunch of updates to the bug, and flag your reviewers there. You can generally expect a review within 24-48 hours, and it's legit to ask nicely if you've gotten silence for longer than that.

Mozreview

Mozreview has several different "kinds" of pages that all look alike. There are "summary" (patchset, all changesets together) and "review request" (changeset) views, and for each there is a view showing comments and updates, and a view showing the diff.

From the summary, you can push changes to try (the effect is the same as hg push-to-try, but the result is linked from the review request so your reviewers can look too). After your reviews are all in, you can also land your changes here.

The diff views show a typical colored diff, and the little floating "+" symbols on the left let you add line comments. Due to some weird beliefs of the upstream maintainers of reviewboard, however, you usually won't see these inlined into the diff like you do in github.

When you add a new line comment, there is a checkbox to "create an issue'. This pretty much means "please address this", and unchecking the box is a way to make a general comment ("I like how you structured this").

A reviewer gives each review request (so, each changeset) a response, which can include a comment and "r?", "r-", "r+", or "" (clearing the flag). There's still some disagreement as to how these are used, but here's what I think:

  • r+ means "I'm happy to see this landed as-is, or I trust you to make the simple changes I've suggested"
  • r- means "This needs changes, and I'd like to review again before it lands." Notably, it does not mean "you suck" or "you are totally unqualified to write a patch so go get a new job". I try to include a positive comment with my r-'s.
  • r? means "I'm still reviewing this" (for example, maybe I've asked a clarifying question)
  • clearing the review request means I don't think I'm qualified to review this

When a all changesets in a review summary are marked r+ and all issues are either fixed or dropped, mozreview will let you (anyone, really) land the changes. It's OK to land someone else's change if there's a rush, as long as you're willing to keep an eye on it and work with the sheriffs if it causes a problem.

If you get r-'s, you can go back and redraft your patchset -- a lot of histediting is involved here! While you're histediting, be careful with the "MozReview ID" lines in the comments -- those are what tie a changeset to its review request even when the hg revision ID changes. If you merge two changesets together, think carefully about which MozReview ID you keep.

To update the request, just push to review like you did initially. There is currently a bug in mozreview which means that reviewers who have left their review status at "r?" or, I think, cleared the review will not be notified of the new request, so you may need to ping them.

Landing

As mentioned above, landing a commit is easy with mozreview. You can also land a commit from the command line. First, make sure to turn all r?<ircnick> to r=<ircnick>, and that you are accurately representing those reviews. Then

hg pull inbound
hg rebase -d inbound

This pulls the latest commit from inbound and rebases your work on top of it.

hg outgoing -r . inbound

This will show all of the commits that will be landed, just to double-check

hg push -r . inbound

This pushes the commits. If your changes are rejected because there are new commits on inbound, go back to hg pull inbound and try again, typing faster this time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment