Search code examples
mercurialdvcsbitbucket

How to enforce a commit policy in Mercurial DVCS


For a small software development I would like changes to follow a defined process, whereby an integration branch would contain only "complete" changes. The idea stems from a post on my blog about getting useful history logs from Mercurial. However this isn't just about generating logs, but about a structured way of working.

In summary the idea is that a repository would have an "integration" branch which would not be directly developed on, instead any work would be carried out on another branch, and when complete would be merged into the integration branch - below is a rough example, with commit comments in braces:

  O   {Implemented new feature X}
  |\
  | O {...}
  | |
  | O {...}
  |/
  O   {Fixed bug 002}
  |\
  | O {...}
  |/
  O   {added tag "Release 1.0"}
  |
  O   {Fixed bug 001}
  |\
  | O {...}
  |/
  O  -- integration branch
 /
O  -- default branch

The "integration" branch would only allow being merged-to and tagged.

This is similar to how we do development at work (on a server-based non-DVCS system), where you make changes on your work branch, and when complete (and reviewed, tested, ...) you merge those changes into an integration branch, for formal test and release.

Anyway, my question is - how would I enforce the policy of making changes only on a work branch, while on the integration branch we only allow merging or tagging?

My initial thought is to add a hook on pre-commit (not precommit or pretxncommit, as I believe these get fired when you, for example, create a tag). The hook would check that, if you were on the integration branch, there are two parents, meaning this is the result of a merge, and fail if that wasn't the case.

However as I understand it, the hooks are not copied when you clone a repository. As this would be a project-specific setting it shouldn't be set at the user-level. So how would I stop a user from cloning the repository (thus losing the hooks), making changes directly on the integration branch, and then pushing?

hg clone main_repo
hg update integration_branch
(make changes without starting a new branch)
hg commit -m "I made some changes"
hg push

Also, how could I enforce this when using a system such as Bitbucket?

Alternatively, is this not the correct approach to a workflow when using a DVCS?


Solution

  • I think you said you want a "structured way of working", and so I wonder if what you are looking for instead, is code lieutenants. That means that the door is locked from the inside, and only the lieutenant opens it, and code hits the central repo, only when the lieutenant pulls it in. Code that has been through your approval process is pulled into a central or authoritative repository, which is a "structured way of working".

    By talking about denying write to a branch only in a repo, instead of denying write to the entire central repo, It sounds to me almost like you're asking how can you take away the DVCS's #1 great attribute, which (a) that there is not just one copy, and that each copy can have its own read/write access rules, one of those copies is the central one if you like it to be so, and (b) that commits are separate from inflicting them on anyone else. A commit is a local working copy action. It is not until these commits hit the authoritative repository, either the central one, or the one managed by your code lietenant, that you have even made a real change, under this controlled process, using DVCS.

    Or are you thinking that users shouldn't even commit into their own working DVCS local instances, without making branches?

    In short, you could say, each local machine's working copy IS A BRANCH, an invisible branch, not inflicted on anyone, or even named, until it is polled by the lieutenant who will do the code review, and integration test the entire changeset, which is functionally equivalent to what you might have called a "feature branch" in a CVCS. Those invisible branches are all writeable, and the central repo (not a branch, separate REPO) is read only by virtue of how it is set up; users can sync from it, but not push to it, except the lieutenant who pulls new changes into it. Fundamentally stable, but still everybody can get work done.