This page is primarily intended for Mercurial's developers.
Mercurial Hidden Commits Proposal
Main proponents: DurhamGoode
Goal: To define a unified user experience for hiding commits, and concrete steps to make it happen in core.
For this document, ignore all current changesets hiding user experience done by obsmarkers. Differences with obsmarker based hiding are covered below.
High Level Approach
We can ship a user experience where changesets are hidden only when users perform an explicitly destructive action, and can be unhidden trivially and without hard-to-undo consequences. In today’s terms, we would hide changesets during amend, rebase, histedit, shelve, and strip (or some new command with the goal of hiding changesets). Basically, anywhere that currently uses repair.strip() can instead hide changesets.
- Normal hg log-like commands would not see the hidden changesets (just like it is today).
Any hg command that specifies an explicit hidden changeset hash could access the changeset: (hg log -r HIDDEN_HASH).
- At no point would information about what is hidden in a repo cross the wire protocol.
- Unbundle and pull would unhide incoming changesets (to be backwards compatible)
- (New Command) hg hide/unhide - equivalent to hg strip, except it just hides and recovers changesets
hg journal (an extension that already exists in core) is used to find old hashes. A changeset could be unhidden via:
- hg checkout HASH
- hg bookmark -r HASH NAME
- hg unhide -r HASH
Interaction With Obsmarkers
Obsmarkers would have no effect on hiddenness. Obsmarker information would be conveyed to a user via other means (ex: log/smartlog) outside the scope of this document. hg hide would have an option to hide changesets that obsmarkers unambiguously indicate are no longer necessary.
Steps To Make It Happen (by the 4.3 release)
- Implement new hidden storage
- Transaction controlled bitmap or root-based storage (unrelated to phases) or range-based
- Convert amend, rebase, histedit, and shelve to also write to the hidden store (in addition to the obstore).
Make pull/unbundle unhide changesets.
- Make explicitly specified hashes visible in the repo hidden-filter layer.
- Replace current repo hidden-filter with one that just looks at the new storage.
Enable checkout/bookmark/tag/etc to unhide changesets
Add new hide/unhide commands
Benefits Of This Approach
- Only one new (intuitive) concept: hide/unhide
- Incremental changes: can be behind config knobs initially (obsmarker hiding vs new storage hiding; stripping vs not stripping)
- Does not change most user visible behavior (rebase still makes the old changesets disappear; you still can’t amend in the middle of a stack or anything crazy; pulling still makes changesets appear)
Downsides Of This Approach
- This approach to hiding, where the user has complete control over hiding and unhiding changesets, prevents strategies that want to enforce a stricter style of hiding. For instance, changeset evolution currently enforces that history only moves forward, and allowing unhiding and using an old changeset breaks that enforcement.
This proposal means that old versions of changesets are not automatically hidden during pull. This can have negative user experience effects on mutable-history collaboration since the user must perform an extra step (hg hide --obsolete) to hide dead changesets. This extra explicitness may be helpful in informing users of what changes have happened and may prevent accidental hiding of changesets the user did not wish to be hidden but prevents this hiding from being automatic.
Notes On Differences With Obsmarker Based Hiding
Reminder: Obsmarker hiding can be turned on today in core with the "experimental.evolution=all" configuration.
Obsmarker based hiding today works as follows. A changeset is hidden if:
- It has an obsmarker pointing away from it to a successor changeset.
- It is not public
- It doesn’t have a bookmark or tag pointing to it
- It doesn’t have the working copy pointing to it
- All its descendants are hidden
This pattern has several consequences:
- Downloading new obsmarkers can cause changesets to disappear, perhaps surprisingly or arguably without sufficiently alerting the user.
- Permanently unhiding a changeset is impossible without inventing a layer on top of obsmarkers.
- Computing what is hidden requires read-time computation or caching to process the rules above.
The proposed solution differs in several ways:
- Changesets are not hidden when new obsmarkers are downloaded. This removes some of the benefit of obsmarkers, but drastically simplifies the understanding of when changesets are hidden, since changesets are only hidden when users do explicit actions to hide them. This makes it easier to explain to users, and gives users more confidence that their source control system won’t surprise them.
- By not using obsmarkers to determine hidden, we can unhide changesets trivially. The obsmarker can still exist to indicate the evolution of a changeset over time, but that data can be limited to the use cases where it actually matters (i.e. auto rebase) and can avoid having other effects, like on hiding.
Effect On Enabling Hidden Changesets In Core
- By not bundling hidden changesets with obsmarker behavior, we can enable hidden changesets more quickly and without bundling further new concepts for users to understand. This will let us get feedback on how users interact with hidden changesets, which can help influence the user experience of auto rebase.