tags:

views:

544

answers:

2

I don't use Mercurial, but i'd like to start, so i'm reading about it. The only SCM system i've used extensively is CVS. Most of what i've read about Mercurial makes sense, and sounds good. But i'm alternately shocked and perplexed by that way it does tags.

A tag is just a nickname for a changeset (and by 'changeset' we really mean the state resulting from the changeset). Cool. The mapping from tags to changeset IDs is stored in the .hgtags file. Also cool. The .hgtags file is versioned.

What?

This has lots of counterintuitive consequences. For instance, if i commit a changeset which i then want to tag (say, the code which will form release 1.0), i have to commit again after tagging, to put the updated tag file into the repository. And if i then update to that tagged changeset at some later date, the working copy won't contain any knowledge of that tag. If i do some work, thus founding a new branch (say, for bugfixes, heading towards 1.1), that branch won't have any knowledge of the tag from which it grew. Unless i copy it over manually, that is.

And as development continues both on the original trunk and my new branch, with tags being created to mark important changesets (the 2.0 release on the trunk, the 1.1 and 1.2 bugfix releases on the branch), both branches will proceed in ignorance of the other branch's tags. So, if i finish working on one branch, and want to switch to some particular changeset on another (say, i finish the 1.2 bugfix release, but now have to start on the 2.1 bugfix, based on 2.0), i am now stuffed. My current changeset doesn't know about 2.0!

What can i do?

  • I can ask someone who's working on the 2.x branch to read out the actual changeset ID for 2.0, and use that explicitly, but this is appalling.
  • I could name my branches, as well as using tags, so that i could hop across to the head of the 2.x branch, thus learning about the new tags, and then skip back to the 2.0 tag. Assuming that branches, unlike tags, are universally visible - is that the case? Even if it is, this seems clunky.
  • I could maintain a single global hgtags file outside the repository, and use a couple of hooks to pull in a copy on an update, overwriting the local copy, and to copy back any changes on a commit. I'm not sure how this would work in a multi-user environment, where developers are pushing changes to a shared repository; i might need a separate repository just for the hgtags file.
  • I could use local tags, which stand outside the versioning mechanism, and so avoid the whole problem. As with shared global tags, i would have to put in place a mechanism to synchronise the localtags file between developers.

None of these solutions seem totally great. What should i do?

An assumption here is that i'm managing branches using named branches in a single repository, rather than repository-per-branch. Would the situation be any better if i did the latter?

+8  A: 

Versioning the .hgtags file allows you to

  • edit tags and see who edited them (and why if they left a proper commit message)
  • transfer tags between repositories using the normal mechanisms

However, there are some confusion going on here.

  • You write that

    [...] And if i then update to that tagged changeset at some later date, the working copy won't contain any knowledge of that tag. [...]

    That is wrong, tags are collected from the .hgtags files found in all heads. That means that you can update to an old tag (hg update 0.1) and still see all your tags (hg tags).

  • You ask if branches are universally visible. Yes they are -- the names of named branches can be used in any context where you need to specify a changeset, as can tags.

Make sure you understand what named branches are before you begin using them. They are actually not necessary for making a bugfix branch. You can instead choose to simply go back (hg update 1.0) and fix the bug and then commit. That will create a new head, which your line of development towards 1.1 (this gives you multiple heads). So you don't have to create a named branch to add a new branch of development to your repository.

Having multiple heads is completely equivalent to having multiple clones. You can even convert back and forth: you can use

% hg clone -r X-head repo repo-X

to untangle the X-head changeset and its ancestors from the other changesets in repo. And you can combine multiple clones by simply pulling all changesets into one big clone.

Named branches are similar, yet different. They allow you to embed a named in each changeset. So you can have several changesets in your history with the name "foo". When you do hg update foo you will end up at the tip-most of these changesets. In this way, named branches function as a kind of floating tag.

If you are uncomfortable with the idea of a permanent label for your changesets, you can instead try the bookmarks extension. That will also give you "floating tags" which you can use for updating, but they wont be permanently part of the history since they aren't versioned.

I hope this helps a bit.

Martin Geisler
Aha! Martin, thanks for clearing up where the set of visible tags comes from - my problem evaporates completely.I take your point about named branches, ie that you don't have to give a branch a name for it to exist. My gut feeling is that unnamed branches would lead to confusion (having committed the bugfix changeset based on 1.0, if you then update to something else, how do you get back to the bugfix line - don't you have to know the changeset ID?), but of course i have yet to try this in practice.
Tom Anderson
You're right, you do have to know the changeset ID. "hg heads" can tell you it, and the bookmarks extension can give them a name. The easiest way it to use several clones like we do it in the Mercurial project itself. There we have hg and hg-stable clones and bugfixes go into hg-stable. We could also have used named branches, I guess we don't because named branches were added long after the hg/hg-stable scheme was used.
Martin Geisler
A: 

The choice of method for tagging definitely has some odd side effects, but they're explained well in this wiki. It's also suggested that you clone an entire repo and then update to the point of interest to avoid the case where you will have created a repo that does not contain the tag that was used to create it.

Dave Johansen