Phases
Contents
1. Introduction
Phases improve safety of history rewriting and provide control over changesets exchanged among different repositories (read more). Phases are intended to transparently "just work" for most users (read more). Phases are a part of the core Mercurial client code, enabled in any new client without preventing older clients from working on a repository (read more). Advanced users may decide to handle phases manually to provide finer control (read more).
Like bookmarks, phases are not stored in history; as such, they're not permanent and leave no audit trail.
Phases are introduced in Mercurial 2.1. See the built-in help for details.
If you are new to phases you may want to read Introduction To Mercurial Phases first.
2. Available Phases
Phases are used to:
- Prevent accidentally rewriting part of the history expected to be immutable
- Prevent immature changesets from being exchanged by mistake
To achieve this, three phases share a hierarchy of traits:
|
immutable |
shared |
public |
x |
x |
draft |
|
x |
secret |
|
|
The public phase holds changesets that have been exchanged publicly. Changesets in the public phase are expected to remain in your repository history and are said to be immutable. History rewriting extensions will refuse to delete these immutable changesets. Every changeset you push or pull from or to a public server is put in the public phase.
The draft phase holds changesets that are not yet considered a part of the repository's permanent history. You can safely rewrite them. New commits are in the draft phase by default.
The secret phase holds changesets that you do not want to exchange with other repositories. Secret changesets are hidden from remote peers and will not be included in push operations. Manual operations or extensions may move a changeset into the secret phase.
Phases split the history in a coherent set of changesets. Every changeset in a phase has an ancestor in a phase compatible with its phase. Compatible means an changeset's ancestors must at least have the same traits as the changeset itself, e.g.: a shared changeset always has shared ancestors and an immutable changeset always has immutable ancestors.
In other words the phase of a changeset is always equal to or higher than the phase of its ancestors, according to the following order:
public < draft < secret
A changeset is not expected to automatically move from a lower phase to an higher phase (eg: from public to draft) but automatic movement from draft to public can happen on transferring changesets between clones. Secret changesets have to be moved explicitly (except for bundles specified with the --base option).
3. Phase Movements
Phase movements are automatic and transparent; most users don't have to care much about them. The base rule is very simple:
"Any changesets seen in a remote repository are public"
On standard exchange commands, phases of changesets on both sides are compared. If phases on both sides are not equal, the lowest phase is chosen, e.g.: a changeset known as draft locally but public remotely is set to public locally, because public < draft in the phase hierarchy.
This update happens during standard exchange commands:
pull: remote phase data are used to update the phase data on the local repo. As pull is read-only, it does not change changeset's phases on the remote
push: remote phase data are used to update the phase data on the local repo, then local phase data are pushed to the remote repo
The real behavior is a bit more complicated than changesets on a remote repository are seen as public, but this is true for simple repository setups. If you need finer-grained behavior, consult the section on publishing repositories.
New changesets committed locally are in the draft phase, but some extensions (like mq) may create secret changesets and handle the move from secret to draft in some other way.
Consult the #upgrade_Notes section to check how phases will move the first time a new version of Mercurial touches an existing repository.
4. Command line interface
Phases are intended to be transparent for most users. People should not need to manually handle them and won't generally run into any behavior changes except to prevent common mistakes. Advanced users may want finer control over phase changes; this section describes how to change phases manually.
4.1. Core Command
The phase concept introduces a single new command: phase. This command will allow users to see and change phases of changesets.
$ hg phase -r 8183::8186 8183: public 8184: public 8185: secret 8186: secret
$ hg phase -v --draft 8185 phase change for 1 changesets $ hg phase -r 8183::8186 8183: public 8184: public 8185: draft 8186: secret
See the command documentation for details.
The hg log command displays changeset phases when --debug is used.
All commands related to changeset exchange will ignore secret changesets, including:
- push
- pull
- incoming
- outgoing
- bundle
- clone
A warning will be displayed when outgoing operation (outgoing, push, bundle) fails to push anything in the face of unsynchronized secret changesets.
no changes to push but 7 secret changesets
To see the changesets that are secret, use hg log -r "secret()". (The same searching can be done for public and draft statuses by searching for public() and draft() respectively)
Note that when using the --base option of bundle, secret changeset are included.
4.2. Impact on extension(s)
Extensions that rewrite history (like MQ, rebase, collapse or histedit) will refuse to work on immutable changesets. When applying any of these extensions to a public changeset, an error will be thrown:
abort: revision 8184 is not mutable
4.3. Impact on cloning
If you have any secret changesets in your repository, then a local clone will be forced to use the relatively slow pull protocol, instead of the faster methods of making file copies or HardlinkedClones.
5. Publishing Repository
By default, any changeset exchanged over the wire protocol is set to public. Advanced users may want some other behavior; the publishing repository concept is designed for this purpose.
5.0.1. What is a "publishing repository"?
Setting a repository as "publishing" alters its behavior **when used as a server**: all changesets in the repository are **seen** as public changesets by clients.
So, pushing to a "publishing" repository is the most common way to make changesets public: pushed changesets are seen as public on the remote side and marked as such on local side.
A repository is "publishing" by default. To make a repository non-publishing, add these lines to its hgrc configuration:
[phases] publish = False
Note: the "publish" property has no effects for local operations.
5.0.2. Old repository are publishing
Phase is the first step of a series of features aimed at better handling mutable history within Mercurial. Old clients do not support this feature and are unable to keep track of phase data. The safest solution is to consider as public any changeset going through an old client.
Moreover, most hosting solutions will not support phases from the beginning. Having old clients seen as public repositories will not change their usage: public repositories where you push *immutable* public changesets *shared* with others.
5.0.3. Why is "publishing" the default?
We discussed above that any changeset from a non-phase aware repository should be seen as public. This means that in the following scenario, X is pulled as public::
~/A$ cd ../B ~/B$ new-hg pull ../A # let's pretend A is served by old-hg ~/B$ new-hg log -r tip summary: X phase: public
We want to keep this behavior while creating/serving the A repository with new-hg, although committing with any new-hg creates a draft changeset. To stay backward compatible, the pull must see the new commit as public. Non-publishing servers will advertise them as draft. Having publishing repository be the default is thus necessary to ensure this backward compatibility.
This default value can also be expressed with the following sentence: "By default, without any configuration, everything you exchange with the outside is immutable".
5.0.4. Why allow draft changeset in publishing repository
Note: The publish option is aimed at controlling the behavior of server. Changeset in any state on a publishing server will always be seen as public by other client. "Passive" repository which are only used as server for pull and push operation are not "affected" by this section.
As in the choice for default, the main reason to allow draft changeset in publishing server is backward compatibility. With an old client, the following scenario is valid::
~/A$ old-hg init ~/A$ echo 'babar' > jungle ~/A$ old-hg commit -mA 'X' ~/A$ old-hg qimport -r . # or any other mutable operation on X
If the default is publishing and new commits in such repository are "public" The following operation will be denied as X will be an immutable public changeset. However as other clients see X as public, any pull//push (or event pull//pull) will mark X as public in repo A.
6. Upgrade Notes
The important points to remember are:
- repositories with phase data can still be accessed by old client(s)
- the new client will add phase data to any repository it touches
- if everything you plan to mutate is handled by MQ you don't have to care about anything
6.1. Backward Compatibility
Phase data are stored in a new file and do not alter any part of the existing Mercurial repository format. This means that a new client can safely write phase related data without preventing an old client to work with the repository. This allows new client to store and handle phase related logic on **all repositories**.
6.2. Adding phase data to an old repo
There are a lot of repositories out there with plenty of changesets but lacking any phase data. When looking at such a repository, a new client will take the safe road and decide everything is 'public'. Some extensions register logic to tune this choice; for example, mq will set every changeset under its control as secret in this situation.
You can set all changesets not pushed to a repository in the draft phase again using:
hg phase --force --draft "outgoing() and public()"
6.3. Touching a phased repo with an old client
Beware that any old client won't be able to move phases when touching a repo.
- An old client can mutate immutable changesets
- An old client will push secret changesets
- An old client will commit new changesets in the phase of their parent
- An old client will add changesets in the phase of their parent
(If you were looking for the developer oriented page: PhasesDevel)