Mercurial > evolve
view doc/obs-concept.rst @ 149:03f314e32058
import some doc
author | Pierre-Yves David <pierre-yves.david@logilab.fr> |
---|---|
date | Tue, 20 Mar 2012 11:02:11 +0100 |
parents | |
children |
line wrap: on
line source
========================= Obsolete concept ========================= Obsolete marker is a powerful concept that allow mercurial to safely handle history rewriting operations. It is a new type of relation between Mercurial changesets that track the result of history rewriting operations. This concept is simple to define and provides a very solid base to: - Very fast history rewriting operations, - auditable and reversible history rewritting process, - clean final history, - share and collaborate on mutable part of the history, - gracefully handle history rewriting conflict, - allows various history rewriting UI to collaborate with a underlying common API. ----------------------------------------------------- Basic concept ----------------------------------------------------- Every history rewriting operation stores the information that old rewritten changesets has newer version available in a set of changeset. This simple rules allows to express any possible history rewriting operation: .. figure:: ./figures/example-1-update.png *Updating* a changeset Create one obsolete marker: ``([A'] obsolete A)`` .. figure:: ./figures/example-2-split.png *Splitting* a changeset in multiple one Create one obsolete marker ``([B1, B2] obsolete B)]`` .. figure:: ./figures/example-3-merge.png *Merging* multiple changeset in a single one Create two obsolete markers ``([C] obsolete A), ([C] obsolete B)`` .. figure:: ./figures/example-4-reorder.png *Moving* changeset around Reordering those two changesets need two obsolete markers: ``([A'] obsolete A), ([B'] obsolete B)`` .. figure:: ./figures/example-5-delete.png *Removing* a changeset: One obselete marker ``([] obsolete B)`` To conclude, a single obsolete marker express a relation from **0..n** new changesets to **1** old changeset. ----------------------------------------------------- Basic Usage ----------------------------------------------------- Obsolete markers create a perpendicular history: **a versionned version of the changeset graph**. This means that we can have the same feature we have for versioned files but applied to changeset: First: we can display a **coherent view** of the history graph with only a single version of your changeset are displayed by the UI. Second, because obsolete changeset content are still **available**. You can * **browse** the content of your obsolete commit, * **compare** newer and older version of a changeset, * **restore** content of previously obsolete changeset. Finally, obsolete marker can be **exchanged between repositories**. You are able to share the result on your history rewriting operation with other and **collaborate on mutable part of the history**. Conflicting history rewriting operation can be detected and **resolved** as easily as conflicting changes on file. ----------------------------------------------------- Detecting and solving tricky situation ----------------------------------------------------- History rewriting can lead to complex situation. Obsolete marker introduce a simple representation this complex reality. But people using complex workflow will one day or another you have to face the intrinsics complexity of some situation. This section describe possible situations, define precise set of changesets involved in such situation and explains how error case can we automatically resolved using available information. obsolete changesets ```````````````````` Old changesets left behind by obsolete operation are said **obsolete**. With current version of mercurial, this *obsolete* part is stripped from the repository before the end of every rewritting operation. .. figure:: ./figures/error-obsolete.png Rebasing `B` and `C` on `A` (as `B'`, `C'`) This rebase operation added two obsolete markers from new changesets to old changesets. These Two old changesets are now part of the *obsolete* part of the history. In most case the obsolete set will be fully hidden to both UI and discovery so user do not have to care about them unless he wants to audit history rewriting operation. Unstable changesets ``````````````````` While exploring obsolete marker possibility a bit further you way end up with *obsolete* changeset with *non-obsolete* children. There is two common ways to achieve this: * Pull a changeset based of an old version of a changeset [#]_. * Use a partial rewriting operation. For example amend on a changeset with childrens. *Non-obsolete* changeset based on *obsolete* one are said **unstable** .. figure:: ./figures/error-unstable.png Amend `A` into `A'` leaving `B` behind. In this situation we can not consider `B` as *obsolete*. But we have all necessary data to detect `B` as an *unstable* branch of the history because its parent `A` is *obsolete*. In addition, we have enough data to automatically resolve this instability: we know that the new version of `B` parent (`A`) is `A'`, We can deduce that we should rebase `B` on `A'` to get a stable history again. Proper warning should be issued when part of the history become unstable. UI will be able to use the obsolete marker to automatically suggest resolution to the user of even carry them out for him. XXX details automatic resolution for * movement * handling deletion * handling split on multiple head .. [#] For this to happen one needs to explicitly enable exchange of draft changeset. See phase help for details. The two part of the obsolete set `````````````````````````````````````` The previous section show that it could be two kinds of *obsolete* changeset: * *obsolete* changeset with no or *obsolete* only descendants, said **extinct**. * *obsolete* changeset with *unstable* descendants, said **suspended**. .. figure:: ./figures/error-extinct.png Amend `A` and `C` leaving `B` behind. In this example we have two *obsolete* changesets: `C` with no *unstable* children is *extinct*. `A` with *unstable* descendant (`B`) is *suspended*. `B` is *unstable* as before. Because nothing outside the obsolete set default on *extinct* changesets, they can be safely hidden in the UI and even garbage collected. *Suspended* changeset have to stay visible and available until they unstable descendant are rewritten in stable version. Conflicting rewriting `````````````````````` If people start to concurrently edit the same part of the history they will likely meet conflicting situation when a changeset have been rewritten in two different versions. .. figure:: ./figures/error-conflicting.png Conflicting rewriting of `A` into `A'` and `A''` This kind of conflict is easy to detect with obsolete marker because an obsolete changeset have more than one new version. It may be seen as the multiple heads case Mercurial warn you about on pull. It is resolved the same way by a merge of A' and A'' that will keep the same parent than `A'` and `A''` with two obsolete markers pointing to both `A` and `A'` .. warning:: TODO: Add a schema of the resolution. (merge A' and A'' with A as ancestor and graft the result of A^) Allowing multiple new changesets to obsolete a single one allow to distinct a splitted changeset from history rewriting conflict. Reliable history `````````````````````` Obsolete marker really help to smooth rewriting operation process. However they do not change the fact that **you should only rewrite the mutable part of the history**. The phase concept enforce this rules by explicitly defining a public immutable set of changeset. Rewriting operation refuse to work on public changeset, but they is still some corner case where changesets rewritten in the past are made public. Special rules apply for obsolete marker pointing to public changeset * Public changesets are excluded from the obsolete set (public changeset are never hidden or candidate to garbage collection) * *newer* version of public changeset are said **latecomer** and highlighted as error case. Solving such error is easy. Because we know what changeset a *latecomer* try to rewrite, we can easily compute a smaller changeset containing only the change from the old *public* to the new *latecomer*. .. warning:: add a schema