This page is primarily intended for developers of Mercurial.


This page appears to contain material that is no longer relevant. Please help improve this page by updating its content.


This page is here to keep track of design choice and discussion which took place while implementing the phase concept.

/!\ For user level details see Phases

/!\ For more generic details about plan to mutate historycan be found here: MutableHG page

1. Abstract

The phase concept aims to introduce a clear distinction between the part of the history that can't be rewritten and the part of history that you can safely rewrite.

A basic usage of phases will track changesets on which you have full control because they exist only in your repository, and will prevent you to rewrite the other changesets that have been shared.

A more advanced usage of phases will allow to exchange a part of the history and yet keep it mutable. The actual rewritting of the existing changesets is covered by MutableHG. This will ease the setup of test or review processes.

The phase concept also adds a layer to help people to control what changesets they exchange with others.

phases of changeset when no data are available:


Phase name and property name:

* * this wiki page * constraint got property name here:

No trash phase:

3. Changeset phases

A 'changeset phases' is an indicator that tells us how a changeset is manipulated and communicated. The details of each phase is described below, here we describe the properties they have in common.

Like bookmarks, phases are not stored in history and thus are not permanent and leave no audit trail.

First, no changeset can be in two phases at once. Phases are ordered, so they can be considered from lowest to highest. The default, lowest phase is 'public' - this is the normal phase of existing changesets. A child changeset can not be in a lower phase than its parents.

The proposed phases are:

public < draft < secret

These phases share a hierarchy of traits:









These names are subject to change. See the Naming section.

3.1. Usage

The current phase of changesets is displayed in the log with a 'phase:' header. The public phase is not displayed at all.

There are corresponding revset predicates for each phase as well.

Most phase change will be made automatically by standard mercurial command. Read each phase section for details.

phases are manipulated via the 'hg phase' command, ie 'hg phase -p x' to mark x as public. Each phase puts its own constraints on how it can be manipulated.

3.2. Implementation

Contexts provide a phase() method that returns the current phase as an integer. Contexts provide a phasestr() method that returns the current phase as a string.

public phase is stored as 0, draft phase is stored 1, secret phase is stored 2

4. Public changesets

Public changesets are changesets that are considered permanently immutable. This matches the history model presented by legacy Mercurial: changesets cannot be changed or removed without using history editing features from extensions. The name reflect the fact that once a changeset is published, it becomes very difficult to remove it from distributed history.

4.1. Usage

Changeset are automatically set in the public phase when they are known to exist outside the repository. This behaviour can be altered using the publish option describe in the draft changeset section.

Changeset can be manually moved to the public phase via 'hg phase --public X'.

Tagged changeset should probably be public

4.2. implementation

4.3. Legacy clients


5. draft changesets

draft changesets are changesets that the user is permitted to use history-modifying operations like rebase or mq on. They may not be tagged. They may also be thought of as 'unpublished'.

Various operations such as pushing to other repo will move changesets into the public phase. Changesets cannot be moved from public to draft without a forcing operation.

A phase.publish option will allow exchange of changeset without moving them in the public phase.

The phase of changesets are communicated between compatible servers and clients.

This should generally be engineered such that users don't have to give any additional thought to draft vs public in their day-to-day usage.

5.1. Usage

Changesets must be in a draft phase when they are created. So new commits will start in a draft phase by default.

5.2. Implementation

The set of draft changesets is stored as a list of draft roots. All descendants of these roots are draft (or in a higher phase). This set is known as the 'draft barrier' and defines the 'draft set'. This barrier is intended only to advance.

The draft barrier is communicated via the pushkey protocol to servers that support it. The client is responsible for advancing the barrier on both the client and server sides. On each operation, the client reduces the phase set on both sides to the intersection of the sets on the client and server. That is, if a changeset is frozen on either side, it becomes frozen on both sides.

By default servers are configured as 'publishing servers'. Legacy servers are publishing servers by default. These are recognized by not having the 'phase' pushkey namespace. When pushing to a publishing server, all pushed changesets are moved into the public phase on the client. Similarly, all changesets pulled from a publishing server are treated as public

See this conversation for details:

5.3. Legacy clients

See above for pushing to legacy clients. Legacy clients are allowed to pull draft changesets, though a round trip will make them public.

6. secret changesets

secret changesets are changesets that are not visible to remote clients. This is useful to mark work private and to avoid inadvertently publishing changesets.

6.1. Usage

secret changesets (and their descendents) can be marked with 'hg phase --secret'.

Some extension as mq may automatically set changeset as secret.

6.2. Implementation

Like draft changesets, secret changesets are implemented via a local barrier set. This set is used to filter changesets from remote clients for push/pull/hgweb.

6.3. Legacy clients

Legacy clients cannot see secret changesets so will not pull them. New clients will not push secret changesets by default

7. More phase

No more phase are planned,

One argument that such functionality is better implemented by hooks, dedicated extensions or tool external to the dvcs itself. In particular, Suggested usages often try to register property (e.g., "passed QA" or "production ready") on a specific node and not just a whole consisted part of the dag. Just because node X passes QA doesn't imply that its ancestor did - in fact, it is almost certainly not the case

In particular no trash phase:

The big issue with using phase mechanism to mark changeset as dead is that phase define set of node which are consistent regarding rules that are problematic for the dead semantic. A golden semantic of phase are """all descendant of a changeset in phase X have the same property that this changeset""". We **can not** alter this:

Having a trash phase means that a Y changeset children of X may be seen dead "by mistake" just because X was marked as dead. This is very problematic if X is added **after** the data than X is dead was created.

Small local example bellow:

This example highlight that phase are not very well suited to store "dead" information.

We may have dedicated logic that detect new changesets is added on top of trashed one and handle this situation. But phase for dead still have two major issue!

1) It prevents any sane synchronisation of this trash phase with the outside. As phase only store un-versionned data, you have no way to know if the trash phase information were created before or after a changeset was committed. This means that you can't decide if the trash data really applies to changeset when you transfers it.

2) Adding non-trashed changeset on top on trashed changeset means moving the boundary and loosing the information that the parents trashed changeset are "not welcome in the final history".

To conclusion about trash phase:

8. Naming

/!\ Until these names are officially nailed down, please use the names above for discussion of the concepts.

The 'state' name was somewhat problematic because it's rather overloaded already and conflicts with hg stat. Thus 'phase' was picked instead.

The 'frozen/liquid/local/dead' name set is also not ideal. The ideal set of phase names will:

Current proposals:

Discarded proposals:

From a UI perspective, transitions between phases are done either implicitly or by setting a phase. So there should be no 'freeze' or 'publish' verb in the UI.

9. Controlling and Hooking on phase movement

9.1. Basic use case for control

I expect this use case to be a good mirror of how we would like to use phase at Logilab or for mercurial development. There is three kind of people working with three kind of repo.

9.1.1. People

This scenario includes three kind of people :

:Developer: are part of a team that write code and create changesets. They are not allowed to validate their own changesets.

:Admin: are part of the same team but are allowed to validate changeset.

:External: are not part of the team and not allowed to validate their changesets either.

Note: In this team, only a few people are allowed to validate changesets, but it could be different, for example with everyone but the interns allowed to validate their changesets.

9.1.2. Repositories

This scenario includes three kind of repositories:

:Public: is the repository used as a reference for the project. The phases.publish option is set to True. Only people:admin people are allowed to push to it. For the mercurial project it is the repository available at

:Devel: is a development repository used by the team. The phases.publish option is set to False. Both people:developer and people:admin can push to it but only people:admin are expected to move the phase boundary to mark changeset as validated and immutable.

:External: repositories created and controlled by people:external where they work on their contributions. The core development team can only pull contribution from these repositories.

9.1.3. Vocabulary

9.1.4. Expectation

We need a way to make sure no changeset can be set in the phase:Public by something else than being pushed into repo:Public.

Below are some variants:

9.1.5. Examples Example A Example B Example C example D example E

9.2. Basic use case for hook

MQ: When mq managed changeset move from secret to ready (or public) mq will want to detect it and either:

QA: When changeset are made public QA bot may trigger.

SYNC: When changeset are made public in a devel repo, automatic sync with public repo may be wished.

9.3. Hook change

The way to controll this should be through hooks. Bellow is proposal

9.3.1. existing hook change

The following existing hooks familly might receive and additional argument about phase:

They would receive a new argument $IN_PHASE. This argument hold a generic information about the maximum phase the changeset may take.

This would affect the following case:

9.3.2. Introducing new hook

I can see two new possible famillies of hook:

movephase: triggered when we move phase boundary locally.

pushphase: triggered when we send phase data to remote

Stuff that move phase:

The more details we can get about phase movement is

9.4. Solving usecase with Hook

9.4.1. Control usecase

repo:Devel set a premovephase hook that deny phase move for user outside people:Admin. This deny A.1, B.2, D.3, E.3

people:Admin set a movephase hook that display warning when movephase is called on something else than:

people:Developer set a movephase hook that display flashy warning when movephase is called on something else than:

people:Developer set a pushphase hook that display flashy warning in all case:





MUST be allowed



MUST be allowed



COULD be allowed

denied (because D.3 and E.3)


COULD be denied

flashy warning it's not too late


MUST be denied



COULD be denied

flashy warning it's not too late


COULD be denied

flashy warning it's not too late


MUST be denied



COULD be denied

flashy warning it's not too late



flashy warning it's not too late



flashy warning contact the other dev


MUST be denied



No control

No Control


DENY phase move

flashy warning fixable locally


MUST be denied


9.5. other case

MQ: can use a prexnmovephase hook to deny pull over mq patches.

MQ: can use a movephase hook to qfinish mqpatch made public.

QA: can use movephase hook to trigger build bot.

SYNC: can use movephase hook to trigger build bot.

10. Implementation status

10.1. Done

10.2. Remaining task

10.2.1. Current discussion

10.2.2. code cleanup

10.2.3. Know bug

10.2.4. Performance

10.2.5. Usability

10.2.6. Impact on other command and extension

10.2.7. Stronger secret phase


PhasesDevel (last edited 2012-10-25 21:24:24 by mpm)