In Mercurial, branches (created with the hg branch inside the same repository) are currently irreversible. After the introduction, you can only remove them from the branch namespace by rewriting the story. Therefore, I would create only real branches, if either the project life cycle is low (only a few function branches), or the branches are universal enough to remain relevant for many years (for example, "stable", "bugfix", etc.). As far as I know, branches (like everything else) are created locally without your control, so if someone decides to use a branch, this branch will also appear in your main repository after pull / push.
The effect is that - in your structure - after pulling from development to the stable, you will also get feature1 and feature2 branches merged into stable, being completely useless. Pulling stable into development, because you fixed bugs on the stable version, will also merge the error branch into development. You can try this by creating a stable repository, clone it for development, split it up to feature1 in development, and put the development in stable (make some changes between these steps): the branch name will appear with an inactive header in a stable state, although you merged it:
stable $ hg branches
default 4: 1c3cd0d1a523
feature2 3: 82879465c5f3
feature1 2: 5d7480426f21 (inactive)
bugs 1: 116860d2d85e (inactive)
I remember that Git is able to delete branches, but Mercurial has some development to catch up with this topic; I am not sure if this is still relevant, so please correct me if I am wrong.
[Modify] According to Trimming Dead Branches in the Mercurial wiki, there are 4 ways to “remove” a branch. The only way to really delete the branch name, and not just close (deactivate) by replacing the repository with a cleared history. [/ Edit]
From what I have heard and seen, cloning is often used instead of branches when using Mercurial. I remember that I had the same thoughts as you when I switched from SVN to Mercurial last year. The usual approach is different from centralized version control because branching can occur at any time without central control over it, so clones are the preferred way to branch out so as not to pollute the branch namespace and separate events and flexibility (you will always get full repository, if you pull / clone, including all branches, and if you just want to branch out to check something, you will need to choose a unique branch name that will be permanent, and thus displayed to everyone in your project). Although this approach seems like a waste of disk space, and you need to keep track of where your branches are located (usually inside some project folder on your user account and inside your IDE), this seems like a much more flexible and practical way to handle the branch. (This reads harder than when you actually use it yourself.)
Having a number of small projects using Mercurial, this is what has worked for our company so far (just a few active developers for each project):
On server:
projectname-main
development is pushed and pulled out of there; it is an “authoritative” development branch to synchronize team repositories.
projectname-stable
if the version is released / deployed, it means that -main is pressed; only patches are made in -stable and then reverted to -main: following the recommendations of the Mercurial Guide from Bryan O'Sullivan , patches for versions that are considered more stable (e.g. previous releases) can usually be returned to the development branch, but changes to development branches may contain newer, erratic functions that should not be pulled into the service branch unless a release (or similar) occurs.
Locally on developer machines:
projectname-main is cloned once, processed and synchronized using pull (+ merge) and regularly returns to the server.
If we need the "branch" function, we clone -main (locally or from the server) and call the cloning "name-name-description of the object". For backup or centralized sharing, we also create a clone on the server and click there. If the function is ready for -main, we pull -featuredescription into our local -main, merge the changes, pull -main from the server, merge again and click-return to the server. If changes occur while we are working on -featuredescription, we can easily extract from -main and merge the changes (without pushing back to -main yet, because we do not want the function to be there yet).
The disadvantage compared to real industries is that you cannot easily find out where the changes came from after they are merged with one of their parents. But this is not a problem for us (commit messages were useful enough or splitting feature branches was not interesting as soon as they were merged with the parent repositories).
Thinking about large projects, I came up with the following scheme, which should work (but not yet been used), similar to centralized version control. It relies on limited write access to "authoritative" server-side repositories, so only privileged developers (the project manager) are allowed to merge and click there. In addition, there is a CI server as a gatekeeper to another -main-testing repository, which is a subset of -main containing only approved CI changes with a slight delay.
On server:
projectname-main
development; only a few people have write access, the changes that need to be given to them must be pulled by them; they control which branches of objects are merged.
projectname-main-tested
development; no one should write here if something went wrong, because verified means that the continuous integration system managed to create -main and clicked this revision on -main-testing, so the code here is checked and should not interrupt compilation or tests
projectname-stable
projectname-stable-tested
the same strategy for a stable "industry"; as before, we push -main to -stable on releases and work on fixes, -stable-verified, approved by CI
And of course, there should be several repositories somewhere where teams / developers can actually push their daily work as long as -main is now only for authoritarian changes (of course, they can work completely locally, but they have to synchronize where -Never if they don't like to check with each other using the hg service or set up their own servers or take care of the backup).
Another option to reduce commits and repositories / branches is to use the mq extension to work with the fix queue. However, I find using clones or branches easier, at least for small projects.