Monday, March 28, 2011

Mercurial Mq: Modify a changeset

Mercurial is an awesome distributed version control system.  If you work on a project that cares about clean changesets, you may run into the need to modify a changeset after you have committed it.  This generally isn't allowed, and it's a potentially dangerous thing to do.

It's dangerous because changing history modifies the identity of the repository.  So if you pushed the changeset, then modified it, your repository will no longer be compatible with the remote one.  And that would be bad.

But if you haven't shared the changeset yet, you CAN modify it.  All you need is to enable the mq extension.  You can use this extension to turn an existing changeset into a patch, and then you can modify that patch.  When you're done, you can finish the patch, turning it back into a finalized changeset.  You can also edit more than one changeset this way by importing them, then popping them off the queue one at a time.

Here's an example:
hg qimport -r 123 -r 124
hg qpop

# make changes to files

hg qrefresh
hg qpush

# make changes to files

hg qrefresh
hg qfinish -a

If you want to update the changeset comment, you can do that by editing the respective .diff file in the .hg\patches directory.  For example, .\hg\patches\123.diff.  Just open that sucker up, modify the comments on top and save it when you're done.  Couldn't be simpler!

Monday, March 21, 2011

Mercurial Mq

Mercurial is an awesome distributed version control system, abbreviated "hg."  Hg's command approach emphasizes small well named commands to make it easier to learn and understand.  It also comes "safe" and "easy" out of the box, but allows you to simply turn on built-in extensions to gain all kinds of shoot-yourself-in-the-foot power.

One of those extensions is called Mq.  This allows you to manage a set of patches in a queue.  You may be asking yourself, "What the hell does that mean?!"  Instead of describing how it works, I'll describe when and how you'd use it.

Suppose you are developing a new feature.  You're changing code here, changing code there, adding tests here and there, and so on.  But suddenly you notice some code that could use improving.  But that code doesn't have anything to do w/ the feature you are working on.  What do you do?!

You could just change it.  But if you do, you will have two unrelated changes in the same changeset.  This isn't the end of the world...  But it's not good either.  For one thing, you might introduce a bug.  And now when someone is trying to track down that bug, it will be really non-intuitive that your changeset may have introduced that bug.  Basically, tangling changes is just bad.  It means you're moving too fast, doing too many things at once.  So let's not just change it.

You could hope you'll remember about it for later.  Or you could write it down and hope you'll see your note and come back to it.  But, come on, that ain't gonna happen.

OR you could use the mq extension.  mq will allow you to take all the changes you've made on your feature and put them in a patch in a queue.  Then create a new patch in the queue, and improve the unrelated code.  Now go back to the first patch and keep working.  When you're all done, "finish" all the patches in the queue and they turn into permanent changesets!  ta da!

Here's what that little story would look like, assuming it's the first time you've ever used mq in this repo:
# work on your feature, notice code that needs to be improved...

hg init --mq #only needed first time you use mq
hg qnew -m "my new feature" new-feature #automatically includes all working dir changes in the patch
hg qnew -m "improved some code" improve-code

# improve the code...

hg qrefresh #adds working dir changes to current patch
hg qpop #takes improve-code changes out of working dir, drops you back to just the new-feature changes

# finish your feature...

hg qrefresh #adds working dir changes to new-feature patch
hg qfinish -a #converts all patches into permanent changesets
This might look like a lot to you at first glance, but it's not. And in practice its surprisingly simple.

You move between patches with hg qpop and qpush.  qpop takes a patch out of your working directory and drops to the previous patch in the queue.  qpush does the opposite, adding the changes of the next patch in the queue back into your working directory.  You can have any number of patches, one after the other, in your queue.  And, if you need to for some reason, you can reorder patches.  Check out the MqExtension page for more.  And after you enable it, "hg help mq" makes it very easy to learn all the various commands.

Very useful feature, and addictive once you get used to it!

Monday, March 14, 2011

Mercurial Branches

One of the best things about a distributed version control system, like Mercurial, is how easy it is for many people to collaborate and share code and changes with each other.

In hg there are many ways of sharing changes. You can export changesets w/ the export command and send them to people. You can bundle up a bunch of changesets together with the bundle command. Or you can share your repository and other developers and can pull directly from you.

That last method where people can pull from you is the easiest, fastest, and generally the best. But it can be a real pain to make sure everyone you want can see your repo. This is where named branches can be useful.

In Mercurial you can "branch" by creating clones in "path space." This is the generally recommended way to go. But you can also create branches within the same repository without cloning a new repository. The benefit of this is you can push and pull those branches through a central repository or any other shared repository. This makes is much easier to share changes with people! It's also more efficient, both from a network traffic and disk space perspective.

For example, you could start work on a new feature in a named branch and make a fair amount of progress and decide you want some feedback from the team. You share your changes, and the team can then commit new changes and share them back to you.

If we were using clones instead of named branches, your feature changes would be in a cloned repository. If your team has a central repo, you wouldn't be able to push your clone there because you're not ready to "finalize" your work yet. So instead, you would have to host your clone so your team could pull from it. Then each team member would have to host THEIR cloned version of your clone so you could pull back their feedback.

With named branches you can leverage the shared repos you already have setup, in this case a central repo. You just create a branch, work on it, and push it to the central repo. Your team pulls from the central repo, makes updates to the branch, and pushes them back.

Here's what this would look like:
hg branch new-feature

# do some work and commit some changes...

hg push -b new-feature --new-branch #push to central repo
Note the --new-branch switch. If you don't include this switch hg will abort with a warning that the push will add new branches to the remote repo. This warning prevents you from accidentally sharing branches that haven't been shared yet. Also note that you don't have to include "-b new-feature". Hg will push all changesets in the repository by default.

Your teammates would then do this:
hg pull
hg up new-feature

# review and add feedback commits...

hg push
If the default branch has had commits added since new-feature was branched, the pull command here will print a message telling you that 1 head has been added to the repository. This head is on the new branch. If you run hg merge at this point it will abort telling you that branch 'default' only has one head. If you want to actually merge branches, you have to explicitly give it the branch name, as we'll see next.

When you're all done with the feature you can merge it back to the default branch, and close your feature branch. That looks like this:
hg up default
hg merge new-feature
hg ci -m "merge"
hg up new-feature
hg ci --close-branch -m "close" #closes the branch
hg up default

If you don't close the branch it will remain listed in the hg branches command.

And that's all there is to named branches! Just hg branch to create them, hg update to move between them, hg merge to merge them back together again, and hg commit to work on them. Easy, fast, and efficient!

Monday, March 7, 2011

Craft over Art

I'm slowly working through Apprenticeship Patterns.  Kind of a dry book, but it does have a few interesting concepts.  One I particularly enjoyed was the "Craft over Art" pattern.  The chapter opens with a quote from Richard Stallman: "I would describe programming as a craft, which is a kind of art, but not a fine art.  Craft means making useful objects with perhaps decorative touches.  Fine art means making things purely for their beauty."

It goes on with "As a craftsman you are primarily building something that serves the needs of others, not indulging in artistic expression.  After all, there's no such thing as a starving craftsman.  As our friend Laurent Bossavit put it: 'For a craftsman to starve is a failure; he's supposed to earn a living at his craft.'...  If your desire to do beautiful work forces you out of professional software development and away from building useful things for real people, then you have left the craft."

"Part of the process of maturation encompassed by this pattern is developing the ability to sacrifice beauty in favor of utility if and when it becomes necessary."

I found this to be an incredibly accurate and valuable discussion.  One of the most difficult balancing acts in programming is that between building what your customer needs, and building what you wish they needed, or even just what you want to build.  Sometimes its hard to tell the difference between the two.  Other times, you know the difference, but it pisses you off!

There is another balancing act that comes up a lot: that between the quality you WANT to build and the quality your user wants.  This goes both ways, but generally we tend to want to build at a higher quality than our users think they want.  "When using this pattern you will have to balance your customer's desire for the immediate resolution of their problem with the internal standards that make you a craftsman."  This is especially important for people influenced by the craftsmanship movement.  Sometimes craftsmanship comes across as a pursuit for perfection over a pursuit for utility.

For me, this Craft over Art pattern was a good reminder to stay focused on delivering high quality utility for my users.