Mercurial (as of 2.1) Now Has Phases

In my last post I pondered (ranted?) on the reluctance of Mercurial people to edit repository history, especially as compared to git people. Today I noticed that Mercurial 2.1 has been released, and it has a new feature that makes editing your local repository much safer, phases (more detail here).

To me this looks like a really cool idea. Phases explicitly mark changesets as either public (have been pushed or pulled, and should not be edited) or draft (still local to your repository and can be freely edited). Changesets are given a phase automatically behind the scenes (though you can override this, of course), and history editing tools respect phases so that now it is even harder to shoot yourself in the foot when editing your repository history with tools like rebase or mq.

There is also one more phase, secret, that can be enabled for use with patches in your Mercurial Queues. Secret changesets will not be pushed, pulled, or cloned (or even show up in the output of incoming or outgoing). That's a nice touch too.

I wonder if git will ever pick this idea up? Git users seem to already be pretty comfortable with history editing procedures, but I have also read (old?) debates about when it is a good idea and when it is definitely a bad idea to use rebase with git repositories. It seems like phases could clear up confusion in the git world too.


Ryan said…
In git, phases would probably be implicit, with any change upstream of a remote ref being considered public.
Bryan said…
I'm not sure exactly what you mean by "implicit." I hope I didn't make it sound like phases in mercurial are something you have to manage yourself. From the mercurial wiki:


Phase movements are automatic and transparent; most users don't have to care much about them. The base rule is very simple:

"Any changesets seen in a remote repository are public"


Of course git would be the same.
Ryan said…
What I mean by implicit is similar to how membership of commits in a branch are determined in git vs mercurial. In hg, the branch is explicity written into the commit (changeset?). In git, a branch is a pointer to a commit, and that branch is considered to contain the pointed-to commit and all of that commit's ancestors, even though none of those commits explicitly reference the branch in any way.

If the Git developers were to implement a similar feature for treating public and private commits differently, I think Git would probably not explicitly keep track of the status of each commit, but rather regard any commit as public if it is pointed to by a remote ref or is an ancestor of such a commit.
Bryan said…
Ryan, I understand what you meant by implicit now, thanks for the explanation. I do need to clarify what you said about mercurial branches though.

In mercurial, you can do an hg update to any revision in the history and then create a new commit as a child of that revision. If the parent of this new commit was not already a head in the directed acyclic graph, you now have a new branch. If that's all you do then the branch does not have a name, but it won't ever be garbage collected or anything like that (you can always find it by running hg heads).

If you want to use git-style (implicit, as you say) branching, you can now use the hg bookmark command to create a bookmark for this branch, which is a named reference or pointer just like git branches are. Of course you can create the bookmark first too if that's more intuitive.

You can also use the original style of mercurial branches that you mention, where before committing you run the hg branch command to create a branch name, and then all commits along that branch in the DAG will have a branch name associated with them.

This is getting a little bit out of date (bookmarks have evolved to be even more like git branches), but I think it's still the best description of mercurial branches out there:
Bryan said…
sorry, here's a link to the beginning of the blog entry:

Popular posts from this blog

SystemVerilog Fork Disable "Gotchas"

Git Rebase Explained

'git revert' Is Not Equivalent To 'svn revert'