I have been a big supporter of Subversion for a long time. I have used it off and on for about 3 years now and have never really had very many troubles while using it. It has always suited my needs and actually allowed me to really learn what version control is, why it’s useful and how to use it. Until recently, I have been very satisfied with its capabilities.
A Distributed Problem
When I created the MyEPICS 2.0 application, I had a vision to make a general framework which can be used outside the context of a service learning management program. Well, fast forward about two years later and I feel I have a stellar framework. I have one tiny problem though, which is that I need to make modifications inside the framework’s directory structure, without changing the main subversion repository. In other words, I have a project that I want to base upon the MyEPICS Framework, but I don’t want to use the main Subversion repository for its version control because not everything I do is going to back into the framework.
An example directory structure is below:
/
-/ME
--Core.php
--...
-/Modules
--/<--- Here is where I want to change things
Purely Subversion Option
If I were to want to do something like this, I would have to check out each Module directory individually from my own repository, and manually do each svn command in each module directory to get up to speed. Not to mention, if I want to update it server side, I must update each directory individually.
On the other hand, I could just set up a new repository for my own work, and any changes outside of the Modules directory I could manually generate a diff, and apply it to the main repository and commit, and vice versa for getting changes from the main repository. It's tedious, but can work.
Mercurial managed
With using a mercurial managed repository, I can check out the repository to some directory, and make a mercurial repository on top of that. From there, I can hg clone the repository to do any of my own work. I can play around with the modules directory, do all of my own changes, without effecting the main Subversion repository. Any time I have a change to add to the main Subversion repository, I just svn commit them. If I have a change to pull from the main Subversion repository, I just
svn update
hg commit
And I have an updated repository! The only downside is that for some reason, any clone of my main mercurial repository doesn't allow me to do my Subversion commands, such as updating and committing. There are a few directories that Mercurial doesn't see in the .svn directory, which I will have to find out why at a later date.
Making the Switch
No Central Repository
Probably the biggest difference is that there is no "central" repository to work with. This means, that any changes you create can be isolated to your environment. You can totally fork an entire project and set up your own version control, and sync up with it whenever you want w/o them really knowing. In fact, that's the entire problem that I've had working with Subversion... I couldn't find a way to do this. Now, trying to argue that there isn't a "main" repository and you'll just be wasting energy. In every project, there has to be some "official" repository which contains the "official" code. In the Linux kernel, that's Linus' repository. For other projects, they may have an "official" repository which is delegated by a select group of people.
Ignoring Files
Mercurial uses a file named .hgignore to tell it to ignore files, as opposed to Subversion using a property on the file, namely svn:ignore. In this file, you can either add single files, or regular expressions. The man page on hgignore is helpful in this case.
Meta Repositories, Tagging, and Branching OH MY!
In the case of Subversion, it is recommended that each repository contain all the tags and branches in it's repository layout, and even allows for multiple repositories contained within one large repository, thus making a meta-repository.
In Mercurial, there is no repository layout. Your repository is just your project itself, so there is no trunk, branches or tag directory. In subversion, if you wanted to branch or tag, you just copy a directory in the repository to the branches or tags and work from there. In Mercurial if you wish to make a branch, you simply hg clone hg branch (thanks luke!) it. When you want your changes to be seen, you push it back or wait for someone to pull from you. If you want to tag something, it's accomplished via hg tag.
Helpful Hint
While I was reading the mercurial red-bean book, I came across such an excellent note, I'm going to repeat it verbatim:
Note: If you’re new to Mercurial, you should keep in mind a common “error”, which is to use the “hg pull” command without any options. By default, the “hg pull” command does not update the working directory, so you’ll bring new changesets into your repository, but the working directory will stay synced at the same changeset as before the pull. If you make some changes and commit afterwards, you’ll thus create a new head, because your working directory isn’t synced to whatever the current tip is.
I put the word “error” in quotes because all that you need to do to rectify this situation is “hg merge”, then “hg commit”. In other words, this almost never has negative consequences; it just surprises people. I’ll discuss other ways to avoid this behaviour, and why Mercurial behaves in this initially surprising way, later on.
Conclusion
After working with Mercurial for a few weeks, I have a little bit better of an understanding of how it works, and enjoy using much better than I did when working on the Rubix Cube project. For now, this will allow me to have my own version control while keeping in sync with the main Subversion repository.