Size: 23661
Comment:
|
Size: 7441
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 3: | Line 3: |
/!\ This page is intended for developers /!\ For more generic details check [[MutableHG]] page |
|
Line 7: | Line 5: |
== 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. == related discussion == State of changeset when no data are available: * http://selenic.com/pipermail/mercurial-devel/2012-January/036745.html Phase name and property name: * http://selenic.com/pipermail/mercurial-devel/2011-December/036632.html * this wiki page * constraint got property name here: http://selenic.com/pipermail/mercurial-devel/2012-January/036760.html == 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. |
== Introduction == The phase concept improves safety of history rewriting and control over changesets exchanged ([[#Available_Phases|read more]]). This phase concept aims to "just works" in a transparent manner for any user ([[#Phase_Movements|read more]]). It is part of Core and always enabled in any new client but doesn't prevent older client to work on a repository ([[#Upgrade_Notes|read more]]). Advanced user may decides to handle phase manually ([[#Publishing_Repository|read more]]). |
Line 33: | Line 10: |
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. | This concept is introduced in Mercurial 2.1. |
Line 35: | Line 12: |
The proposed phases are: | == Available Phases == The phase concept allow to: |
Line 37: | Line 15: |
public < draft < secret | * Prevent accidental rewriting part of the history expected to be immutable. * Keep immature changeset to be exchanged by mistake. |
Line 39: | Line 18: |
These phases share a hierarchy of traits: | To achieve this, are three phases sharing a hierarchy of traits: |
Line 41: | Line 20: |
||public ||x||x|| ||draft || ||x|| |
||public ||x ||x || ||draft || ||x || |
Line 48: | Line 27: |
These names are subject to change. See the Naming section. | * '''The public phases''' holds changeset announced publicly in. They are expect to |
Line 50: | Line 29: |
=== Usage === The current phase of changesets is displayed in the log with a 'phase:' header. The public phase is not displayed at all. |
. always exists in your history and are said ''immutable''. History rewriting extension will refuse to delete such '''immutable''' changeset. Every changeset your push or pull from a public server are set in this phase. |
Line 53: | Line 31: |
There are corresponding revset predicates for each phase as well. | * '''The draft phase''' holds changesets that was not expect marked as part of |
Line 55: | Line 33: |
Most phase change will be made automatically by standard mercurial command. Read each phase section for details. | . the permanent history. You can safely rewrite them. New commit are draft by default. |
Line 57: | Line 35: |
States 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. | * '''The secret phase''' holds changeset that you do not want to exchange with |
Line 59: | Line 37: |
=== 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. |
. other repository. Secret changeset are hidden to remote peer and won't be included in push operation. Manual operation or Extension may turn changeset secret. |
Line 62: | Line 39: |
public phase is stored as 0, draft phase is stored 1, secret phase is stored 2 | Phases split the history in coherent set of changeset. Every changeset in a phase have ancestor in a phase ''compatible'' with its phase. ''Compatible'' means an changeset ancestors have at least the same traits that the children changeset. eg: A ''shared'' changeset alway have ''shared'' ancestor and an ''immutable'' changeset always have ''immutable'' ancestors. |
Line 64: | Line 41: |
== 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. |
In other word the phase of a changeset is alway equal of higher that the phase of it's descendant. According to the following order: |
Line 67: | Line 43: |
=== 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. |
. public < draft < secret |
Line 70: | Line 45: |
* changeset pulled are public * changeset pushed are public * changeset unbundled are public |
A changeset is not expected to automatically move from a lower phase to an higher phase (eg: from ''public'' to ''draft'') but automatic '' '' |
Line 74: | Line 47: |
Changeset can be manually moved to the public phase via 'hg phase --public X'. | == Phase Movements == Phase movement are automatic and transparent and most user don't have to care much about them. The base rule is very simple: '' '' |
Line 76: | Line 50: |
Tagged changeset should probably be public | . “''Any changesets on a remote repository is seen a public''” '' '' |
Line 78: | Line 52: |
=== implementation === === Legacy clients === N/A |
On standard exchange commands, the phase of changesets on both side are compared. If differ the lowest phase is choosed. (eg: a changeset known as ''draft'' locally but '''public''' remotely is set public localy. Because public < draft) '' '' |
Line 82: | Line 54: |
== 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'. |
This update happen during standard exchange commands: '' '' |
Line 85: | Line 56: |
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. | * '''pull''': remote phase data are used to update the phase data on '' '' |
Line 87: | Line 58: |
A phase.publish option will allow exchange of changeset without moving them in the public phase. | . the local repo. As pull is read only, it does not change changeset's phase on the remote '' '' |
Line 89: | Line 60: |
* Changeset adding changeset to a phase.publish=True server are set public. This is the default. | * '''push''': remote phase data are used to the phase data on the local repo and '' '' |
Line 91: | Line 62: |
* Changeset adding changeset to a phase.publish=False server do not alter their phase. This allows people to collaborate on work in progress before it becomes finalized. | . then local phase data are pushed to the local repo. '' '' |
Line 93: | Line 64: |
The phase of changesets are communicated between compatible servers and clients. | The real behavior is a it's a bit more complicated than '' changesets on a remote repository is seen a public'', but this is true for repository keeping default . If you need a finer behavior, consult the ''[[#Publishing_Repository|publishing repository]]'' section. '' '' |
Line 95: | Line 66: |
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. | New changeset committed locally are ''draft'', but some extension like ''mq'' may create ''secret'' and handle the move from ''secret'' to ''draft'' automatically '' '' |
Line 97: | Line 68: |
=== Usage === Changesets must be in a draft phase when they are created. So new commits will start in a draft phase by default. |
Consult the [[#upgrade_Notes]] section to check how phase will move the first time a new version of Mercurial touch and existing repository. '' '' |
Line 100: | Line 70: |
=== 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. |
== command line interface == === core command === === impact on extension === == Publishing Repository == By default any changeset exchanged over the wire are set public. Advanced user may want a finer behavior. The Publishing repository concept is designed for this purpose. '' '' |
Line 103: | Line 76: |
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. | ==== What is a "publishing repository"? ==== Setting a repository as "publishing" alter its behavior **when used as a server**: all changesets are **seen** as public changesets by clients. '' '' |
Line 105: | Line 79: |
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 | 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. '' '' |
Line 107: | Line 81: |
See this conversation for details: http://selenic.com/pipermail/mercurial-devel/2011-December/036279.html | Note: the "publishing" property have no effects for local operations. '' '' |
Line 109: | Line 83: |
=== 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. |
==== Old repository are publishing ==== Phase is the first step of a series of features aiming at handling mutable history within mercurial. Old client do not support such feature and are unable to hold phase data. The safest solution is to consider as public any changeset going through an old client. '' '' |
Line 112: | Line 86: |
== 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. |
Moreover, most hosting solution will not support phase 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. '' '' |
Line 115: | Line 88: |
=== Usage === secret changesets (and their descendents) can be marked with 'hg phase --secret'. |
==== 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:: '' '' |
Line 118: | Line 91: |
Some extension as mq may automatically set changeset as secret. | {{{ }}} 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 server will advertise them as draft. Having publishing repository the default is thus necessary to ensure this backward compatibility. '' '' |
Line 120: | Line 95: |
=== 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. |
This default value can also be expressed with the following sentence: "By default, without any configuration, everything you exchange with the outside is immutable.". This behaviour seems sane. '' '' |
Line 123: | Line 97: |
=== Legacy clients === Legacy clients cannot see secret changesets so will not pull them. New clients will not push secret changesets by default |
==== 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. '' '' |
Line 126: | Line 100: |
== More phase == | 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:: '' '' |
Line 128: | Line 102: |
No more phase are planned, | {{{ ~/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 }}} '' '' |
Line 130: | Line 110: |
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 | 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. '' '' |
Line 132: | Line 112: |
Allowing enforcement of public changeset only repository through config is probably something to do. This could be done with another "strict" option or a third value config for phase related option (mode=public, publishing(default), mutable) '' '' | |
Line 133: | Line 114: |
One argument about other usage for phase is 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. | == Upgrade Notes == ---- '' '' '' '' |
Line 135: | Line 118: |
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: (1) We created phase for this exact semantic and we need it for all other usage. (2) The way phase are store and exchanged rely on this [1]. 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: $ hg pull Getting changeset "X" You look "X" and decide it's a silly changeset $ hg trash "X" Getting patch-y.diff from your mailbox patch-y.diff is a nice bugfix and you want it by chance, patch-y.diff have been created above "X" $ hg import --exact patch-y.diff As "Y" is a descendant of "X", "Y" is trashed too! 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: * You can not safely exchange data about "dead" changeset through phase. Loosing most of point of having this data. * Safely using phase locally mean loosing the "dead" information whenever there is a conflict. == Naming == /!\ Until these names are officially nailed down, please use the names above for discussion of the concepts. The 'state' name is somewhat problematic because it's rather overloaded already and conflicts with hg stat. Thus 'phase' is maybe a better choice. The 'frozen/liquid/local/dead' name set is also not ideal. The ideal set of state names will: * all be on an obvious continuum or theme * not have conflicting initials (liquid vs local) (why?) * not conflict with common options (frozen vs force) * not insist too much on a particular workflow (review) * imply that changesets states can be moved only in one direction (frozen vs public) * shouldn't conflict with the attributes of states (mutable, hidden) * have fairly obvious semantics Current proposals: * public < draft < secret < ? (public implicates publishing: push or pull) * finished < draft < private (a draft gets finished) * final < liquid < private (a liquid reaches a final state) Discarded proposals: * public < local < secret < ? (local is a bit misleading, doesn't imply mutability) * public < local < private (initials clash) * published < review or ready < draft < trash (review or ready is a bit too much workflow) * published < mutable < private < discarded (mutable isn't thematic, conflicts with attributes) * public < draft < private < deprecated (name clash) * public < liquid < private < deprecated (liquid strongly implies mutability and unfinishedness) (but name clash) From a UI perspective, transitions between states are done either implicitly or by setting a state. So there should be no 'freeze' or 'publish' verb in the UI. == Controlling and Hooking on phase movement == === 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. ==== 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.'' ==== 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 http://selenic.com/hg :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. ==== Vocabulary ==== * people:XXX refer to a mercurial user in the group XXX described above. * repo:XXX refer to a mercurial repositoty in the group XXX described above. * phase:Public refer to the 0 phase in which a changeset are immutable * ctx:X refer to a changeset "X" ==== 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: * We don't expect changeset of repo:Devel to in the public phase if they are not in repo:Public. * Phase movement on repo:Devel should be only accepted is triggered by * pull from repo:Public * push from people:admin * Changeset in a repo owned by people:devel of people:external should not be . set in public phase by something else than pulling from repo:Public or repo:Devel ==== Examples ==== ===== Example A ===== . (1) A people:Admin push ctx:A to repo:Public (2) A people:Developer pull ctx:A from repo:Public. The phase:Public boundary . move to ctx:A (3) The same people:Developer push ctx:A to repo:Devel. The public:Phase boundary move . to ctx:A * A.1 MUST be allowed * A.2 MUST be allowed * A.3 COULD be allowed ===== Example B ===== . (1) A people:Developer make ctx:B public locally. (2) The same people:Developer push ctx:B to Dev. Public phase boundary move . to B * B.1 COULD be denied. But we can't ensure it is. * B.2 MUST be denied ===== Example C ===== . (1) A people:Developer set ctx:C as phase:Public locally (2) A people:Admin pull from people:Developer which have publish=False. . phase:Public boundary move to ctx:C (2) The same people:Admin push to repo:Devel. The phase:Public . boundary move to ctx:C * C.1 COULD be denied. But we can't ensure it is * C.2 and C.3 are more about people:Admin making a mistake. But if the phase . movement is silent enough it won't be hard to make such mistake. ===== example D ===== . (1) A people:Developer set ctx:D as phase:Public locally (2) Another people:Developer pull from the first one. The phase:Public . boundary move to ctx:D (3) The second people:Developer push to Dev. The phase:Public boundary . move to ctx:D * D.1 COULD be denied. But we can't ensure it is. * D.2 CAN NOT be denied if we what to stay a ''Decentralized'' VCS (make it . clear by having a case where dev exchange valid public changeset) * D.3 SHOULD be denied (this conflict with A.1) ===== example E ===== . (1) A people:External push ctx:E to it's repo:External repo using old . mercurial version. (2) A people:Developer pull ctx:E from repo:External. The phase:Public . boundary move to ctx:E (3) The same people:Developer push ctx:E to repo:Devel for sharing. The . phase:Public boundary move to ctx:E. * E.1 Is not something we have control on. * E.2 SHOULD not deny the pull but COULD deny the phase movement (at least warn about it) * E.3 MUST be denied === 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: * qfinish those patches * Abort the transaction 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. === Hook change === The way to controll this should be through hooks. Bellow is proposal ==== existing hook change ==== The following existing hooks familly might receive and additional argument about phase: * changegroup * commit * incoming * outgoing 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: * hg unbundle into publish == True * '''changegroup''' IN_PHASE=0 * '''incoming''' IN_PHASE=0 * hg unbundle into publish == False * '''changegroup''' IN_PHASE=1 * '''incoming''' IN_PHASE=1 (I'm not sur bundle should be seens as public or not…) * hg pull into publish == True from publish == True * '''changegroup''' IN_PHASE=0 * '''incoming''' IN_PHASE=0 * '''remote outgoing''' IN_PHASE=0 * hg pull into publish == True from publish == False * '''changegroup''' IN_PHASE=0 * '''incoming''' IN_PHASE=1 * '''remote outgoing''' IN_PHASE=1 * hg pull into publish == False from publish == True * '''changegroup''' IN_PHASE=1 * '''incoming''' IN_PHASE=0 * '''remote outgoing''' IN_PHASE=0 * hg pull into publish == False from publish == False * '''changegroup''' IN_PHASE=1 * '''incoming''' IN_PHASE=1 * '''remote outgoing''' IN_PHASE=1 * hg push from publish == True to publish == True * '''remote changegroup''' IN_PHASE=0 * '''outgoing''' IN_PHASE=0 * '''remote incoming''' IN_PHASE=0 (how should it know about it ?) * hg push from publish == True to publish == False * '''remote changegroup''' IN_PHASE=1 * '''outgoing''' IN_PHASE=0 * '''remote incoming''' IN_PHASE=0 (how should it know about it ?) * hg push from publish == False to publish == True * '''remote changegroup''' IN_PHASE=0 * '''outgoing''' IN_PHASE=0 * '''remote incoming''' IN_PHASE=1 (how should it know about it ?) * hg push from publish == False to publish == False * '''remote changegroup''' IN_PHASE=1 * '''outgoing''' IN_PHASE=1 * '''remote incoming''' IN_PHASE=1 (how should it know about it ?) * hg commit * '''commit''' IN_PHASE=1 * hg commit --phase=secret * '''commit''' IN_PHASE=2 * hg commit --phase=public * '''commit''' IN_PHASE=0 ==== Introducing new hook ==== I can see two new possible famillies of hook: movephase: triggered when we move phase boundary locally. * reason (commit, pull, push, remotepush, addchangegroup, unbundle, other (eg manual)) * source * publishing_source (true or false) * user ? * boundaries (how to serialise this for shell…) pushphase: triggered when we send phase data to remote * source (aka remote) * user ? * publishing_source (true or false) * boundaries (how to serialise this for shell…) Stuff that move phase: * hg commit * hg pull * hg push * hg unbundle The more details we can get about phase movement is * hg commit: * premovephase with precommit * prexnmovephase with prexncommit * movephase with commit * hg pull * (Do we need something before discovery ?) * '''premovephase''' . before we pull changegroup (move computed using remote phase data) ? (phase did not moved yet) * '''prexnmovephase''' after we pull changegroup (move computed using remote phase data). . (phase did moved) * '''movephase''' after changegroup have been added (or nothing to pull) . (phase did moved) * hg push * (Do we need something before discovery ?) * '''premovephase''' before we push changegroup (move computed using remote phase data ?) * '''prepushphase''' before we push changegroup (move computed using remote phase data ?) * '''prexnmovephase''' after we pushed changegroup but before we push phases to remote * '''prexnpushphase''' after we pushed changegroup but before push phases to remote * '''pushphase''' after changegroup have been pushed (or nothing to push) and phase pushed * '''movephase''' after changegroup have been added (or nothing to pull) and phase pushed === Solving usecase with Hook === ==== 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: ''''' * '''''local operation ''''' * '''''pull from repo:Public ''''' * '''''push to repo:Public ''''' '''''people:Developer set a ''movephase'' hook that display flashy warning when movephase is called on something else than: ''''' * '''''pull from repo:Public ''''' '''''people:Developer set a ''pushphase'' hook that display flashy warning in all case: ||case ||requirement ||behavior || ||A.1 ||MUST be allowed ||works || ||A.2 ||MUST be allowed ||works || ||A.3 ||COULD be allowed ||denied (because D.3 and E.3) '' '' || ||B.1 ||COULD be denied ||flashy warning it's not too late || ||B.2 ||MUST be denied ||denied || ||C.1 ||COULD be denied ||flashy warning it's not too late || ||C.2 ||COULD be denied ||flashy warning it's not too late || ||C.3 ||MUST be denied ||allowed || ||D.1 ||COULD be denied ||flashy warning it's not too late || ||D.2 ||Wrong ||flashy warning it's not too late || ||D.2' ||Wrong ||flashy warning contact the other dev || ||D.3 ||MUST be denied ||denied || ||E.1 ||No control ||No Control || ||E.2 ||DENY phase move ||flashy warning fixable locally || ||E.3 ||MUST be denied ||denied || ''''' === 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. ''''' == Implementation status == === Done === * '''Basic phase mechanism'''. phase summary can be move and are properly save in mercurial transaction. * '''Public and draft phases'''. Changeset are committed as draft. Changeset are set as public on exchange operation. * phase.publish option to control if echanged changeset are set in the public phase. * Exchange phase boundary exchange === Implementation in progress === * secret phase === To be implemented === * display phase data in log + template support * phase command to view and alter changeset phase * revset support * clone support * '''Public changeset are immutable''', rebase and mq refuse to delete them. * Hide secret changeset in hgweb graft. * have ui.status about phase movement. * Hide secret changeset from outgoing. === Still in discussion === * Hooks on Phase movement * phases number: 0,1,2 or 0,10,20 keep room for future addition ? ---- ''''' CategoryDeveloper ''''' |
(If you were looking to the developer oriented page: PhaseDevel) '' '' |
Phases
Contents
1. Introduction
The phase concept improves safety of history rewriting and control over changesets exchanged (read more). This phase concept aims to "just works" in a transparent manner for any user (read more). It is part of Core and always enabled in any new client but doesn't prevent older client to work on a repository (read more). Advanced user may decides to handle phase manually (read more).
Like bookmarks, phases are not stored in history and thus are not permanent and leave no audit trail.
This concept is introduced in Mercurial 2.1.
2. Available Phases
The phase concept allow to:
- Prevent accidental rewriting part of the history expected to be immutable.
- Keep immature changeset to be exchanged by mistake.
To achieve this, are three phases sharing a hierarchy of traits:
|
immutable |
shared |
public |
x |
x |
draft |
|
x |
secret |
|
|
* The public phases holds changeset announced publicly in. They are expect to
always exists in your history and are said immutable. History rewriting extension will refuse to delete such immutable changeset. Every changeset your push or pull from a public server are set in this phase.
* The draft phase holds changesets that was not expect marked as part of
- the permanent history. You can safely rewrite them. New commit are draft by default.
* The secret phase holds changeset that you do not want to exchange with
- other repository. Secret changeset are hidden to remote peer and won't be included in push operation. Manual operation or Extension may turn changeset secret.
Phases split the history in coherent set of changeset. Every changeset in a phase have ancestor in a phase compatible with its phase. Compatible means an changeset ancestors have at least the same traits that the children changeset. eg: A shared changeset alway have shared ancestor and an immutable changeset always have immutable ancestors.
In other word the phase of a changeset is alway equal of higher that the phase of it's descendant. 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
3. Phase Movements
Phase movement are automatic and transparent and most user don't have to care much about them. The base rule is very simple:
“Any changesets on a remote repository is seen a public”
On standard exchange commands, the phase of changesets on both side are compared. If differ the lowest phase is choosed. (eg: a changeset known as draft locally but public remotely is set public localy. Because public < draft)
This update happen 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 phase on the remote
* push: remote phase data are used to the phase data on the local repo and
then local phase data are pushed to the local repo.
The real behavior is a it's a bit more complicated than changesets on a remote repository is seen a public, but this is true for repository keeping default . If you need a finer behavior, consult the publishing repository section.
New changeset committed locally are draft, but some extension like mq may create secret and handle the move from secret to draft automatically
Consult the #upgrade_Notes section to check how phase will move the first time a new version of Mercurial touch and existing repository.
4. command line interface
4.1. core command
4.2. impact on extension
5. Publishing Repository
By default any changeset exchanged over the wire are set public. Advanced user may want a finer behavior. The Publishing repository concept is designed for this purpose.
5.0.1. What is a "publishing repository"?
Setting a repository as "publishing" alter its behavior **when used as a server**: all changesets 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.
Note: the "publishing" property have no effects for local operations.
5.0.2. Old repository are publishing
Phase is the first step of a series of features aiming at handling mutable history within mercurial. Old client do not support such feature and are unable to hold phase data. The safest solution is to consider as public any changeset going through an old client.
Moreover, most hosting solution will not support phase 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::
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 server will advertise them as draft. Having publishing repository 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.". This behaviour seems sane.
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. Allowing enforcement of public changeset only repository through config is probably something to do. This could be done with another "strict" option or a third value config for phase related option (mode=public, publishing(default), mutable)
(If you were looking to the developer oriented page: PhaseDevel) 6. Upgrade Notes