/!\ This page is primarily intended for Mercurial's developers.

Internal Changeset Plan

Status: Project

Main proponents: Pierre-YvesDavid

/!\ This is a speculative project and does not represent any firm decisions on future behavior.

Introduce a mechanism to track and hide changeset created as side effect of internal operation

1. Goal

Operation like amend, histedit, shelve created temporary internal commit. We are looking for some official way to record and hide them once they are no longer necessary.

2. Problem Space

2.1. Possible approach

Since introducing a new "internal" concept will requires a new repository requirement. We have multiple options at hands.

  1. dedicated 'internal' phases,
  2. ad-hoc mechanism (let us says bytemap),
  3. key in changeset extra,

(If someone think about something else, let me know)

2.2. Internal Changesets Properties

  1. They are irrelevant to the users, B. Nothing will be based on them, C. Once done with their purpose, we should not need them again, D. They never leave the repository, E. They never stop being internal changesets,

2.3. Life Cycle of Internal Changesets

2.3.1. amend

  1. transaction is open,
  2. a temporary commit is created,
  3. <temp> is used to build the result of the amend,

  4. obsmarkers are created (between old and new),
  5. <tmp> is "archived" (currently obsmarkers),

  6. transaction is committed.

The commit is created and "archived" in the same transaction. It is never visible to anyone.

Actually, we could fully remove this changeset with appropriate in-memory-ctx capabilities.

2.3.2. histedit

Scenario:

   pick <initial-second>
   pick <initial-first>
   roll <initial-fourth> # the important part is the fold
   pick <initial-third>

(Assuming single transaction for simplicity)

   1) transaction is open,
   2) <initial-second> is rebased on base
     (possible conflict and associated transaction open/close)
      -> creates <final-first>,
   3) <initial-first> is rebased on the <final-first>
     (possible conflict and associated transaction open/close)
      -> creates <temporary-second>,
   4) <initial-fourth> is rebased on the <temporary-second> (no commit),
     (possible conflict and associated transaction open/close)
   5) changes are folded in <temporary-second> as <final-second>
   6) <initial-third> is rebased on the <final-second>
     (possible conflict and associated transaction open/close)
      -> creates <final-third>,
   7) transaction is committed.

Possible rebase conflict during (4) will expose <temporary-second> to the user as:

The user needs to see that temporary commit during the merge conflict resolution.

2.3.3. shelve

shelving:

   1) transaction is open,
   2) shelved change are committed,
   3) commit is recorded as a shelve-changeset,
   4) apply hiding on this shelve,
   5) transaction is committed

unshelving (I might be wrong, I've not followed everything):

   1) transaction is open
   2) uncommitted changes are put in a temporary commit
   *) access the shelve-changeset (somehow),
   3) hg rebase -r <shelve> -d <tmp>
   <) possible conflict resolution that requires transaction commit
   >) possible transaction reopen (if conflict)
   =) rebase complete
   4) grab the result of the shelve and restore working copy parent
   5) hide <tmp> and <rebased>
   6) close transaction

Note: during possible rebase conflict, the "shelve" is currently visible to the user. Can we change this ?

* At minimum, it needs to be at least visible to the "hg resolve command". This is easy to achieve in all cases.

* Having it visible seems valuable for the user experience.

2.3.4. conclusion regarding life cycle

Even if many internal never needs to be visible out of a transaction that creates them. There seems to be valid cases were the internal changeset is exposed to the user to help with merge conflict (histedit and shelve).

It is worth noting that while the changeset need to be visible, they seems to always be working copy parent (directly or through a merge). So the current mechanism to unhide could simply work to deal with this life cycle.

In general however, once an internal changeset has been hidden, we won't need it again. There is a small exception with <shelved> changeset. More on that below.

2.4. Additional note about shelve

The shelve extensions use a full call to the "rebase" command to merge the shelved change on the destination. It could directly use the core merge mechanism to perform this graft (not using the rebase extension). This would allow to skip the temporary commit currently created by rebase.

In addition, if I'm not mistaken, core-merge is able to merge with a dirty working copy. So it might be possible to directly trigger that "graft" without the temporary local commit. hg resolve will still use and display proper information. And the pre-merge content will be available for restoration on --abort.

That would lift the need of temporary internal changeset during unshelve.From there, the only internal changeset involved in shelve would be the shelved changeset itself.

In addition Since we keep track of shelved changeset, it will be easy to feed them to the hiding logic as long as they are official shelve. However, when the shelve is deleted, we want to be able to hide <shelved> changeset and the internal-changeset concept is handy.

The idea of using a mechanism dedicated to shelve for hiding active shelve allow to lift the exception created by shelves in regard with the life-cycle of internal-changeset. This can also become useful if people starts exchanging shelve between repository (urg) as won't make another exception to the internal space.

3. Analysis of available option

Lets dive deeper in the various option we have:

3.1. Phases

We could use new dedicated phase(s) to keep track of internal changesets.

3.1.1. advantages

3.1.2. disadvantages

3.1.3. other

3.1.4. summary

Phases seems like a good option, most of the usual drawback of phases regarding 'hiding' are neutralized by "internal-changeset" property and it fits well in the concept to separate 'internal' from the other changesets. The main reservation would be around the change implied to the phases concept.

3.2. ad-hoc solution

We could build a dedicated solution to track internal changeset and their life-cycle (eg: bit maps, root tracking)

3.2.1. advantages

3.2.2. disadvantages

3.2.3. other

3.2.4. summary

This seems a possible way to implement internal changeset. It might implies quite some work.

3.3. changeset data

We could use a special key in extra (eg: '_internal') to track internal changesets.

3.3.1. advantages

3.3.2. disadvantages

3.3.3. other

3.3.4. summary

Despite a couple of interesting property, using extra for 'internal' will not be very adequate for the task at hand. I would requires extra performance work and a secondary concept to handle the life cycle.

=== Extra thought about life cycle ===

Their is a couple of way to handle the internal-life-cycle while tracking a single 'internal' "state".

4. Conclusion

4.1. short version

At the current stage of my reflexion, my personal choice will be:

4.2. rational

I'm going for phase-space because 'public/draft/secret' do not make sense for internal changeset anyway. Making it some explicit with an 'internal' phase seem a good move.

In addition, we already have all the UI and concept around phase. So introducing a new one will not add complexity to our UI.

We go for a single phases since a all lyfe-cycle/visibility concerns can apparently be taken care of the fact working copy parent are visible.

We could rely on a generic local hiding mechanism but having a dedicated phases increase insulation. Such insulation reduce the chance of a user touching internal visibility by mistake and help filtering them out of the UI. Implementation is not more complex since we can already feed the visibility code from multiple sources.

I keep the 'extra' key idea to make sure we'll never collide with 'real-space'.

4.2.1. implementation idea

5. Roadmap

<discussion stage>

6. See Also


CategoryDeveloper CategoryNewFeatures

InternalsPlan (last edited 2017-04-03 10:06:44 by Pierre-YvesDavid)