This page is primarily intended for Mercurial's developers.
This page is no longer relevant but is kept for historical purposes.
This feature have been implemented in Mercurial 2.1 as Phases
For general information about plan for history mutation in Mercurial, see MutableHG
A system for safely allowing mutable history.
- Original LiquidHg Notes
- Mercurial 1.9 Sprint Notes
The first objective of Liquid-HG is to have a clear distinctions between the part of the history can be altered and the one which can't. (To keep it simple: the private part may be edited while the public can't). This imply two points
- Deny edition of non-liquid (frozen) changeset,
- Simple and logical transition from liquid to frozen state (freezing). This transition should be transparent for the user.
Garbage collection of "abandoned" changeset
For now, all extensions that alter history destroy old changeset by rewriting part of revlog. Most of them save a backout of the stripped part of the revlog in a bundle located at ".hg/strip-backup/"
Semantical Change tracking (as mq do with patches) and versionning (as versionned mq)
mq currently allow to keep a single identifier (patch name) for the multiple changeset that qrefresh create.
Versionned mq allow to restore state of the liquid part you changeset and share it with other people.
History edition extension compatibility
mq (any other extension) keep information about changeset outside the revlog. The force other history editing extension to either: Implement mq compatibility itself, Be incompatible with mq handled changeset, corrupt mq repo.
Sharing volatile part of the history
Sharing the liquid part of the history for review, preview is a complicated issue but an interesting problem.
Ideas and discussion
Original LiquidHg Notes
Liquid HG is a plan to extend Mercurial to more fully support the volatility of MQ, rebase, feature branches, etc., while at the same time maintaining the benefits of immutable history.
The idea is to mark a boundary between history that is permanently fixed and history that is mutable. This boundary will be transmitted between clients via push and pull and will be transparently advanced by certain operations (eg tagging). It can never move backwards.
Changesets above the boundary are considered "liquid" and can be affected by operations like MQ, rebase, etc., and may be subject to garbage collection if they are unreferenced for long periods. Changesets below the boundary ("frozen") are guaranteed not to disappear and are not subject to garbage collection.
The liquid boundary is stored as a list of root nodes for liquid branches. All descendants of these nodes are in the liquid set. This list is shared between clients via the pushkey method proposed for sharing bookmarks.
As the liquid list can be optionally empty, no "requires" entry is needed.
Creating the boundary
All changesets are "frozen" until an appropriate command like qnew creates a boundary. Creating a bookmark is another possibility - new changesets on bookmarks may be considered liquid by default.
Moving the boundary
Generally speaking, the boundary should move naturally in the course of normal operations without any special user intervention. The usual methods will be operations like tagging a changeset, merging on to a branch with a non-liquid head, and qfinishing an mq patch.
Another important way to "freeze" a changeset is to "publish" it to a server with "liquidity" turned off, for instance to the "canonical" project server. Everyone pulling from that server will have their liquid boundary moved to the published boundary.
If bookmarks are enabled, changesets that are not frozen and are not referenced by bookmarks may be garbage-collected.
Old clients obviously won't support pushkey on the liquid list, so they're easy to identify.
New clients with old repo: the liquid list will be effectively empty, so all changesets will be frozen by default
Old client with new repo: doesn't see or update the liquid boundary, mostly works
Pulling from an old client: no effect
Pull by old client: no effect, old client may encounter confusion by seeing removed or obsolete liquid changesets
Push by old client: no effect, new heads will be considered liquid
Pushing to an old client: this is considered 'publishing' and advances the boundary.
Changes to Commands
heads: needs a way to list liquid vs frozen, and identify/filter heads that have bookmark references
log: introduce a marker for liquid csets
push: may require -f or similar to freeze changesets when publishing
Mercurial 1.9 Sprint Notes
- Liquid boundary: The set of changesets which defines permanently published revisions. These changesets and their ancestors are allfrozen (see below).
- Deathset: A specific list of revisions, all descended from a single ancestor revision (its "root"), which are no longer desired and marked as dead. The set is stored as the complete enumeration of dead changeset node SHAs, in order to allow clients to identify dead nodes without having the entire history DAG locally.
- Frozen Changeset: A changeset that is older than the liquid boundary. These changesets are immutable and are guaranteed to be in the repository forever (barring manual user intervention such as a strip operation,) even in the presence of archiving or deletingactions.
- Archived Changeset: A frozen (?) changeset "killed" with a deathset in a "Archival Changeset" (see below) and not kept "alive" by being the ancestor of any live head. Archived changesets will not be propagated to new clones or pulled by clients missing them without an explicit request, either with a --archived flag to pull/clone or explicit mention by revision. They will also be obscured from history browsing operations unless their visibility is specifically requested.
- Archival Changeset: A special changeset with a deathset in the extra field created as a child of the root of the deathset. Marks the nodes in the deathset as "archived". Is considered "archived" itself. Will be propagated only to clients that already have the root of the deathset and the root is not being kept alive by another head.
- Liquid Changeset: A changeset which is not an ancestor of any changeset in the liquid boundary set. These revisions are to be considered temporary until frozen by moving the liquid boundary.
- Evaporated Changeset: A liquid changeset that has been marked as nolonger desired via a pushkey mechanism of defining deathsets.
Behavior of Liquid Boundary
The liquid boundary will always advance in the normal case, requiring direct manual intervention on a server if it must move backwards (much as a strip requires manual intervention and communication today.) Servers which are canonical sources of revision history (for example, http://selenic.com/hg for Mercurial) will either not run the liquid extension, or will run it with liquid.publish=True. When pushing to such a server, the client will advance the liquid boundary to include the heads pushed, and in the case of the latter the server will as well. When pulling from such a server, everything discovered as being present on the server advances the local liquid boundary.
Deathsets enumerate the exact changesets they eradicate. If a changeset included in a deathset has a descendant not contained in a deathset, the deathset has no effect on that changeset (we say it's "kept alive" by that descendant).
Pushkey-based deathsets have no effect on frozen changesets, even if that leaves a new head open. Pushkey-based deathsets may be safely garbage collected if they refer entirely to frozen changesets (i.e. changesets behind the liquid boundary), and they may be reduced in size if part of the deathset falls behind the liquid boundary. All pushkey-based deathsets are pulled at any given time, but only those which would evaporate otherwise-liquid changesets are pushed to the server.
Changeset-extra-based deathsets (that is, deathsets in archival changesets) are only propagated if the immediate ancestor of the deathset-holding changeset (always the root of the deathset) is already on the destination server.
Dead liquid changesets are only pushed or pulled if explicitly requested. Dead frozen changesets are only propagated if they are required as a dependency of a killing changeset.