Changesets Evolution - development page.
This page is intended for developer
For a user perspective see ChangesetEvolution.
Contents
- Contributing
- Roadmap
- Overall concept
- Related Concept
- In progress Features and discussion
- See Also
1. Contributing
The simplest way to help is to grab one of: list of easy bug
There is also a multiple well defined topic that where idea exist but needs an implementation
- Rebase could make more use of obsolescence marker:
- detection that part of the rebase set is already in the destination
- warning about divergence creation.
Obsolescence markers exchange (no really we have idea waiting to be implemented)
- Prototype for a command bringing changeset back to life.
There is more complicated part that requires attention too.
Better storage//loading//cache for markers (depends on marker exchange)
Solving the problem of Topic Branch
- Handling moving changesets around with the "plan" concept,
in memory merge (helps a lot of troubles resolution throught evolve)
- Wide transaction capability
- Continue/stop/abort for all command including evolve
- In transaction content non exchangeable.
- Making evolve capable of solving all troubles that user can encounter
- Computing UI message about troubles from event who happened during the transaction.
2. Roadmap
See CEDRoadMap
3. Overall concept
See CEDConcept
4. Related Concept
Other concept not directly involved in Changeset Evolution and closely related for technical or user experience reason.
AtomicRepositoryLayoutPlan: To reduce race condition around new evolve
- metadata
WideTransactionPlan: To help ability to have "pure" abort/rollback/undo,
FeatureBranchesStruggle: To help organise all your developpements branches,
TopicPlan: A possible solution to the above,
RevsetOperatorPlan: To offer proper way to travel through the
- evolution-history,
PlanHistoryRewritePlan: Factorise and empower all the common history rewriting operation,
ExperimentalExtensionsPlan: To move the evolve extension into core,
CommitCustodyConcept: Top level way to follow and sign draft changeset.
5. In progress Features and discussion
5.1. Using Obsolescence Marker during Rebase
There is two big issues with rebasing a set containing obsolescence changeset:
- It is easy to create divergence
- You get a lot of conflict when rebasing an obsolete stack on it's successors version.
To enable the current implementation set the config experimental.rebaseskipobsolete to true.
Current progress:
rebase can skip obsolete changeset when rebased on successors,
same logic as above, handling prune,
same logic as above, handling split,
ability to detect divergence creation and bail out,
ability to rebase set with obsolescence inside the set (rebasing both precursors and successors) without creating divergence,
official config to control these two behavior (either in one or two config)
config on by default.
5.2. `hg evolve --list`
This is also tracked in https://bz.mercurial-scm.org/show_bug.cgi?id=4845
Evolution can compute a lot of information out of the obsolescence graph. It already use it to compute troubles and automatically solve them. Yet this information is not easily accessible to the user.
We would like to gain a basic version of hg evolve --list that would provide all available information on current troubles.
5.2.1. Output Proposal
- CHANGESET
- - TROUBLE: REASON - TROUBLE: REASON
- - TROUBLE: REASON
More concrete example:
5633576f2df1: unbundle: cleanly abort on unknown ...
- unstable: parent 485eb2a02a2b is obsolete bumped: precursors ec2662b9629d is public divergent: precursors 4a8e21578e14 have another succcessor 84dcc37b1272 divergent: precursors 4a8e21578e14 have another succcessor 2a3010ba6f52
We want to ensure this is implemented through the 'formatter' API so we gain structured output easily "eg -Tjson"
5.2.2. Command Line API
We probably want
--rev to allow select revision to display
--unstable/--bumped/divergent to restrict the trouble we display.
5.2.3. Details on Each Trouble Cases
Here is some idea for data we could provide in each cases. This provide an handy list of case in the process.
5.2.3.1. Unstable from Obsolete Parent
- reason: unstability comes from parent being obsolete
- Source: What is this obsolete parent,
- Evolution target: What the evolution will turn the parent into (beware of pruned changeset)
- We should mention problematic split,
5.2.3.2. Unstable from Unstable Parent
- reason: unstability comes from parent being unstable
- Source: What is this unstable parent,
- parent needs to be evolved before that one.
5.2.3.3. Unstable Merge
- If both parent have obsolescence/troubles, we must issue two entries
5.2.3.4. Bumped Changeset
- Point at the public changeset
- Mention if we need a rebase or not
- ADVANCE: Is there a commit message change
5.2.3.5. Divergent Changeset
- Point at the other divergent changeset
- Point at the "greater common evolution ancestors" of divergence
- Says if this GCEA is known locally
- Point "greater locally known common evolution ancestors" in addition
- otherwise.
- mention if we need a rebase or not,
- The "divergent with a split" case…
5.2.3.6. Divergent with the other side public
This is actually one of the most common divergence scenario. We probably need a dedicated name for this.
- We should mention that the other side is public/immutable.
5.2.3.7. Visible Obsolete Changeset
Should list them with a mention of why they are still visible?
5.3. obsmarker support in `hg strip`
We would like to be able to strip obsolescence marker related to a changeset when stripping it.
We want:
- (some) way to disable it ("nice way" can come later),
- the obsmarker to be in the backup bundle,
- A mode where we strip all markers between the stripped one and first known
- precursors (default?)
- A mode where we strip all precursors too (and associated markers) (beware of
- untargeted descendant of precursors, cf drophack).
The "obsmarker in the backup bundle" will requires some work because there is no other place storing obsmarkers in an on disk bundle. However, all infra should be in place as we already exchange obsmarker over the wire through a bundle.
In the same go, you can consider adding obsmarker support to hg bundle through the "variants" part of the type.
5.4. `hg evolve --continue/--abort/--drop`
Currently evolve implement a super basic state format that does not allow a proper "multi step" behavior.
5.4.1. About `--continue`
Current issues
- does not remember the currently evolved set (just finish the current one)
does not work for --bumped or --divergent
We need to update the format on disk to allow these.
5.4.2. About `--abort`
This does not exists but it would be useful.
- We need to know the evolved set,
- We must also record and remove obsmarker we created in the process.
5.4.3. About `--drop`
Currently, user can "abandon" his evolve at any time. leaving half of the thing evolved and the other one still unstable. That is a pretty cool feature we want a way to keep. Adding a flag could be a way. openning the way to similar feature in other multi step command.
Name is not frozen, this is the first time we mention it.
Maybe we just want hg up -C to be that, but it seems a bit too easy to run by mistake.
5.5. Tagging draft changeset
Currently tagging draft a changeset may lead to issues when the tagged changeset is rewritten and its hash change, making the tag invalid.
Here is a small (draftish) list of ideas to handle the question, the first one "rewriting the tag file when we evolve/rewrite the tagging changeset" is probably the way to go.
Mailing list discussion about this: https://www.mercurial-scm.org/pipermail/evolve-testers/2016-May/000178.html
5.5.1. rewriting the tag file when we evolve/rewrite the tagging changeset
When the tagging changeset is evolved (changed from a descendant of the old tagged, to the new version), we could rewrite the content of the tag file.
Pro:
- Does "the right thing"™
- Works even if the tagged changeset is pulled -after- the rewrite.
Cons:
- Only work when the tagging changeset is a descendant of the tagged,
- Does not handle the local case.
5.5.2. Warn when pushing a draft tag
The idea is that pushing a tagged changeset as draft increase the risk of rewrite, so warning could help having user think about turning them public.
Pro:
- easy to do,
- can be reverted when we need something better,
- prevent the error to pass silently,
Cons:
- Does not actually solve the problem,
- Does not handle the local case.
5.5.3. Warn when rewriting a draft tag
Pro:
- easy to do,
- can be reverted when we need something better,
- prevent the error to pass silently,
Cons:
- Does not actually solve the problem,
5.5.4. Turning the changeset public on tagging
Pro:
- Prevent any tag rewrite issue,
- easy to do,
- handle both local and remote case,
Cons:
- loose the ability to fix tagging error before publishing,
- break backward compatibility,
- hard to revert if we get something better,
5.5.5. Turn the tagged changesets public on push
Pro:
- Turn changeset public at the time it become more complicated,
Cons:
- loose the ability to fix tagging error before publishing,
- break backward compatibility for non-publishing repo,
- hard to revert if we get something better,
- do not handle the local case.
5.5.6. refuse to tag draft
Pro:
- Get rid of the issue (almost) entirely,
- handle local and remote case,
Cons:
- massive backward compatibility breakage, sensible user script will break,
- hard to revert if we get something better
5.6. Obsolescence Marker Discovery
See dedicated page: ObsolescenceMarkersExchange
5.7. amend/uncommit/recommit/etc
A recurring discussion is "What should be 'base' for the 'amend' operation" (or other related command).
The complexity of the situation comes from the fact we have 3 states of interest
wc(): The working copy content (on disk file)
p1(): The working copy parent content (commit content, affected by commands)
p1()~1: The working copy parent (commit content).
Various core command already allow to interact with these layers.
* The commit command move content from wc() to p1() (creating a new p1()) in the process), * The commit --amend is just a variation of that moving content from wc() to p1() (reusing the existing one). * The revert command move content from p1() to wc() * The revert --rev command move content from any commit content to wc()
Evolve have various commands in this area too.
* The amend command mimic commit --amend, moving content from wc() to p1() * The uncommit command move content from p1()~1 to p1().
A common question is to change the default of amend to use p1()~1 as base. Picking change from wc() when selected and resetting to p1()~1 for excluded content. "Just changing the default" is not a solution here. We have multiple possibilities (because we have 3 states to select from) and if we change from one to the other, some people will just start asking for the other one instead.CategoryNewFeatures
However, having an easy way to select the mode would be nice, gather all usecase into a single command would be preferable. Building a good UI for that might allow us to remove the dedicated uncommit command and provide a recommit capabilities that people have been asking for.
5.8. Record types of operation
Storing more information about what the type of rewrite in obsolescence markers would be useful. The idea is to leverage the bitfield in the markers to record various kind of actions.
5.8.1. Recorded actions
- did the "diff" changed, (eg: amend)
- did the parent changed, (eg: rebase, but not plain evolve)
- did the commit message got edited,
- did the changeset metadata got rewritten.
And probably these extra data too:
- is the changeset purely identical to another (rewind),
- is the rewrite part of a fold.
The various edition command will be responsible for adding useful information:
a hg amend that update the diff will create a markers recording the "content changes",
a hg amend -e that only update he commit message will record the "message change",
a hg rebase will record the "parent change",
a hg amend that update the message, the content and user will record these three things (content, message and meta changes).
hg histedit would record the right operation according the the action.
5.8.2. Advantages
Compact: Since we use the (existing) bitfield, the storage cost is free (or minimal if we need to adds an extra Byte of bitfield). Accessing the data is also very cheap:
Compoundable: We'll often needs to combine multiple markers to reports changesets between two revisions (eg: we do not have the intermediate revision locally). the bit approach makes it trivial to compound the information. We can display display the same final information when the same result if obtained from different path (eg: from two markers [content change; message change] or from one markers [content change | message change]).
UI Abstraction: since we record the "effect" information, we abstract the command names. This makes use more agile about the actual UI. User can use their own "hg myownrewritetool" command and still record information useful for the other users.
5.8.3. Usage
Having that data will help providing information about the obsolescence history. Example:
@ ac28e3 durham / First commit | | o d4afe7 durham | | Second commit | | | x 8e9a5d (Amended as ac28e3) durham |/ First commit |
6. See Also
entry points: list of easy bug
contributing: list of all issues
User: ChangesetEvolution
Planning: Road Map
Core Concept: Concept and important sub-issue
Interface: Generic user interface discussion
Vabulary: How should we name things
Exchange: Information about obsolescence markers exchange and discovery
Rebase: Behavior with "hg rebase"