Differences between revisions 17 and 18
Revision 17 as of 2011-11-07 18:04:52
Size: 21393
Comment:
Revision 18 as of 2011-11-23 13:28:28
Size: 20434
Editor: vaf26-2-82-244-108-134
Comment: proof-read and fixes
Deletions are marked like this. Additions are marked like this.
Line 139: Line 139:
Line 141: Line 140:
Line 144: Line 142:
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 'state' name is somewhat problematic because it's rather overloaded already and conflicts with hg stat. Thus 'phase' is maybe a better choice.
Line 149: Line 147:
 * not have conflicting initials (liquid vs local)   * not have conflicting initials (liquid vs local)
Line 170: Line 168:

Line 173: Line 169:

I expect this usecase To be a good mirror of how we would like to use phase at
l
ogilab or for mercurial development. There is three kind of people working
with three kind of repo.
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.
Line 179: Line 172:

This scenario distinct three kind of people:

:Developer: are part of coherent development force that know each other write
            code and create changeset. They are not allowed to validate their
            changeset.

:Admin: are a smaller part of the same coherent development force but their are
        allowed to validate changeset.

:External: are people external to the team. There are not allowed to validate
           their changeset they write



''Note: This is a team with a few people allowed to validate changeset. The
opposite ratio can apply too. With only a few people (eg interns) not
allowed to validate they changeset''



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.''
Line 203: Line 183:

There is Three (kind of) repositories:

:Public: repository is the reference repository of 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: repository is a developement 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 are repo created and controlled by people:external for
           building contribution. The core team have no control on them but may
           pull contribution from them.
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.
Line 221: Line 192:
Line 226: Line 196:
 * phase:Public refer to the 0 phase where changeset are immutable

 * ctx:X refer a changeset "X"
 * phase:Public refer to the 0 phase in which a changeset are immutable

 * ctx:X refer to a changeset "X"
Line 231: Line 201:

We need a way to make sure no changeset set in the phase:Public by
something else than being pushed into repo:Public.
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.
Line 237: Line 205:
 * 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
 * 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
Line 245: Line 212:
   set in public phase by something else than pulling from repo:Public or
  
repo:Devel
  . set in public phase by something else than pulling from repo:Public or repo:Devel
Line 249: Line 215:
Line 251: Line 216:

   
(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
 . (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
Line 262: Line 225:
Line 264: Line 226:

   (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
 . (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
Line 273: Line 233:

   
(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
 . (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
Line 285: Line 241:
              movement is silent enough it won't be hard to make such mistake.   . movement is silent enough it won't be hard to make such mistake.
Line 288: Line 244:

   
(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
 . (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
Line 300: Line 252:
   clear by having a case where dev exchange valid public changeset)   . clear by having a case where dev exchange valid public changeset)
Line 304: Line 256:
Line 306: Line 257:

   
(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.
 . (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.
Line 322: Line 270:
Line 324: Line 271:

MQ: When mq managed changeset move from secret to ready (or public) mq will want to
   
detect it and either:
MQ: When mq managed changeset move from secret to ready (or public) mq will want to detect it and either:
Line 334: Line 279:
SYNC: When changeset are made public in a devel repo, automatic sync with public repo
     
may be wished.
SYNC: When changeset are made public in a devel repo, automatic sync with public repo may be wished.
Line 339: Line 282:
Line 343: Line 285:
Line 351: Line 292:
They would receive a new argument $IN_PHASE. This argument hold a generic
information about the maximum phase the changeset may take.
They would receive a new argument $IN_PHASE. This argument hold a generic information about the maximum phase the changeset may take.
Line 357: Line 297:

 
* '''changegroup''' IN_PHASE=0
   * '''incoming''' IN_PHASE=0
  * '''changegroup''' IN_PHASE=0
  * '''incoming''' IN_PHASE=0
Line 362: Line 301:

 
* '''changegroup''' IN_PHASE=1
  * '''incoming''' IN_PHASE=1 (I'm not sur bundle should be seens as public or not…)
  * '''changegroup''' IN_PHASE=1
  * '''incoming''' IN_PHASE=1 (I'm not sur bundle should be seens as public or not…)
Line 367: Line 305:

 
* '''changegroup''' IN_PHASE=0
   * '''incoming''' IN_PHASE=0
   * '''remote outgoing''' IN_PHASE=0
  * '''changegroup''' IN_PHASE=0
  * '''incoming''' IN_PHASE=0
  * '''remote outgoing''' IN_PHASE=0
Line 373: Line 310:

 
* '''changegroup''' IN_PHASE=0
   * '''incoming''' IN_PHASE=1
   * '''remote outgoing''' IN_PHASE=1
  * '''changegroup''' IN_PHASE=0
  * '''incoming''' IN_PHASE=1
  * '''remote outgoing''' IN_PHASE=1
Line 379: Line 315:

 
* '''changegroup''' IN_PHASE=1
   * '''incoming''' IN_PHASE=0
   * '''remote outgoing''' IN_PHASE=0
  * '''changegroup''' IN_PHASE=1
  * '''incoming''' IN_PHASE=0
  * '''remote outgoing''' IN_PHASE=0
Line 385: Line 320:


 
* '''changegroup''' IN_PHASE=1
   * '''incoming''' IN_PHASE=1
   * '''remote outgoing''' IN_PHASE=1
  * '''changegroup''' IN_PHASE=1
  * '''incoming''' IN_PHASE=1
  * '''remote outgoing''' IN_PHASE=1
Line 392: Line 325:

 
* '''remote changegroup''' IN_PHASE=0
   * '''outgoing''' IN_PHASE=0
  * '''remote incoming''' IN_PHASE=0 (how should it know about it ?)
  * '''remote changegroup''' IN_PHASE=0
  * '''outgoing''' IN_PHASE=0
  * '''remote incoming''' IN_PHASE=0 (how should it know about it ?)
Line 398: Line 330:

 
* '''remote changegroup''' IN_PHASE=1
   * '''outgoing''' IN_PHASE=0
  * '''remote incoming''' IN_PHASE=0 (how should it know about it ?)
  * '''remote changegroup''' IN_PHASE=1
  * '''outgoing''' IN_PHASE=0
  * '''remote incoming''' IN_PHASE=0 (how should it know about it ?)
Line 404: Line 335:

 
* '''remote changegroup''' IN_PHASE=0
   * '''outgoing''' IN_PHASE=0
  * '''remote incoming''' IN_PHASE=1 (how should it know about it ?)
  * '''remote changegroup''' IN_PHASE=0
  * '''outgoing''' IN_PHASE=0
  * '''remote incoming''' IN_PHASE=1 (how should it know about it ?)
Line 410: Line 340:

 
* '''remote changegroup''' IN_PHASE=1
   * '''outgoing''' IN_PHASE=1
  * '''remote incoming''' IN_PHASE=1 (how should it know about it ?)
  * '''remote changegroup''' IN_PHASE=1
  * '''outgoing''' IN_PHASE=1
  * '''remote incoming''' IN_PHASE=1 (how should it know about it ?)
Line 416: Line 345:

 
* '''commit''' IN_PHASE=1
  * '''commit''' IN_PHASE=1
Line 420: Line 348:

 
* '''commit''' IN_PHASE=2
  * '''commit''' IN_PHASE=2
Line 424: Line 351:

 
* '''commit''' IN_PHASE=0
  * '''commit''' IN_PHASE=0
Line 429: Line 354:

Line 442: Line 365:
Line 447: Line 371:
Line 455: Line 378:
Line 459: Line 381:

 
* premovephase with precommit
  * prexnmovephase with prexncommit
   * movephase with commit
  * premovephase with precommit
  * prexnmovephase with prexncommit
  * movephase with commit
Line 466: Line 386:

 
* (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)
  * (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)
Line 478: Line 395:

 
* (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
  * (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
Line 495: Line 411:
Line 497: Line 412:

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 ||
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 ||


'''''
Line 536: Line 446:

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.

'''''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. '''''
Line 548: Line 455:
CategoryDeveloper ''''' CategoryDeveloper '''''

States Plan

/!\ This page is intended for developers

A set of proposed Mercurial features to cleanly and safely handle mutable history.

1. Introduction

This page aims to define features connected with the 'liquid hg' discussion in enough detail that they can be fine-tuned and implemented.

They are intentionally presented in a proposed order of implementation.

2. Hidden changesets

Various features will require changesets to be hidden by default. So it makes sense to implement a centralized method of dealing with this property.

Usage:

Hidden changesets can be shown with the --hidden flag, eg 'hg log --hidden' will show all changesets.

Implementation:

The changelog object contains a _hidden set (using revision numbers) that log and other commands consult. To hide a changeset, add it to the set at startup. Contexts are given a .hidden() predicate that consults the set.

3. Changeset states

A 'changeset state' is an indicator that tells us how a changeset is manipulated and communicated. The details of each state is described below, here we describe their shared properties.

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

First, no changeset can be in two states at once. States are also ordered, so they can be considered from lowest to highest. The default, lowest state is 'frozen' - this is the normal state of existing changesets. No child changeset can be in a lower state than its parents.

The proposed states are:

frozen < liquid < local < dead

These states share a hierarchy of traits:

mutable

unshared

hidden

GC-able

frozen

liquid

x

local

x

x

dead

x

x

x

x

These names are subject to change. Some of them have initials that collide with each other and with options like force and they don't have an obvious progression.

3.1. Usage

States are manipulated via the 'hg state' command, ie 'hg state -f x' to mark x as frozen. Each state puts its own constraints on how it can be manipulated. The current state of changesets is displayed in the log with a 'state:' header. The frozen state is not displayed at all.

There are corresponding revset predicates for each state as well.

3.2. Implementation

Contexts provide a state() method that returns the current state as a string. It may be useful to represent the frozen state with the empty string.

4. Frozen changesets

Frozen 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. This state can be thought of as 'published': once a changeset is published, it becomes very difficult to remove it from distributed history.

4.1. Usage

Changeset are moved to the frozen state via 'hg state --frozen X'.

4.2. Implementation

N/A

4.3. Legacy clients

N/A

5. Liquid changesets

Liquid 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 certain public servers will move changesets into the frozen state. Changesets cannot be moved from frozen to liquid without a forcing operation.

The liquidity of changesets can be communicated between compatible servers and clients. This allows people to collaborate on work in progress before it becomes finalized.

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

5.1. Usage

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

5.2. Implementation

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

The liquid 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 liquid 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.

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

5.3. Legacy clients

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

6. Local changesets

Local 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

Local changesets (and their descendents) can be marked with 'hg state --local'.

6.2. Implementation

Like liquid changesets, local changesets are implemented via a local barrier set. This set is used to filter changesets from remote clients for push/pull/hgweb. This is probably best implemented via a _local set on changelog.

6.3. Legacy clients

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

7. Dead changesets

Dead changesets are changesets that are hidden and are eligible for garbage collection.

Dead changesets themselves are never pushed or pulled between clients (they are a subset of local) but deadness of changesets can be communicated between clients.

If a client has a dead changeset X and happens to pull a remote changeset Y that is a descendant of it, changeset X becomes liquid or frozen as appropriate.

Garbage collection consists of stripping all dead changesets. When garbage collection occurs is currently undefined.

7.1. Usage

Commits (and their descendents) can be manually marked dead with 'hg state --dead'. Some operations like strip, qpop, and rebase may choose to mark changesets dead rather than actually stripping them.

7.2. Implementation

The dead set is stored as a complete list of all revisions. This allows dead status to be propagated after garbage collection occurs. Dead status is communicated via pushkey.

7.3. Legacy clients

Like local changesets, old clients cannot see dead changesets.

8. Abandoned changesets

Abandoned changesets are changesets that have been marked as 'no longer relevant'. Like dead changesets, they are hidden, but abandoned changesets are implemented via markers in history so they are not part of the states concept.

Abandoned changesets are not pushed or pulled (see the implementation of local) but are not subject to garbage collection.

Abandoned changesets can be unabandoned by committing new descendants.

8.1. Usage

To mark a branch as abandoned, use 'hg commit --abandon' to abandon the working directory and its descendants.

8.2. Implementation

The abandon commit contains an explicit list of abandoned changesets.

8.3. Legacy clients

Old clients see abandoned changesets and their marker commits as normal changesets.

9. Obsolete changesets

Obsolete changesets are part of an advanced concept (see ChangesetEvolution) used to automatically resolve and combine refactoring operations between collaborators using liquid changesets. Like dead changessets, these are also hidden, but obsolete markers are implemented as pointers from X' (new changeset) to X (obsolete changeset) so are again not part of the state concept.

10. 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)
  • 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 < ?

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)

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.

11. Controlling and Hooking on phase movement

11.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.

11.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.

11.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 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.

11.1.3. 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"

11.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:

  • 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

11.1.5. Examples

11.1.5.1. 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

11.1.5.2. 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

11.1.5.3. 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.

11.1.5.4. 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)

11.1.5.5. 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

11.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:

  • 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.

11.3. Hook change

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

11.3.1. 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

11.3.2. 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===

11.3.3. 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

11.4. 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.


CategoryDeveloper

StatesPlan (last edited 2012-01-12 00:40:09 by Pierre-YvesDavid)