topics: update current topic to the topic of newly rebased commit (issue5551)
The rebase code passes branchmerge equals to True while updating to the rebased
commit. We need to make sure topic is preserved even after rebase and hence we
need to update the topic even when branchmerge argument is set to True. But
there is a twist in the tale, merge also uses this part of code and we allow to
update topic when brancmerge is True, in merge cases the topic after merge will
the topic of the destination commit, not the topic of working directory parent.
So we need the function to have information about whether a rebase is going on,
and we do it by wrapping the rebase command and storing some value in the
config. This is a bit hacky but works for now. This patch fixes issue related to
loosing of topic while rebase.
Thanks to Boris Feld for the rigourous tests.
.. Copyright 2014 Greg Ward <greg@gerg.ca>----------------Evolve: Concepts----------------Getting the most out of software requires an accurate understanding ofthe concepts underlying it. For example, you cannot use Mercurial toits full potential without understanding the DAG (directed acyclicgraph) of changesets and the meaning of parent/child relationshipsbetween nodes in that graph. Mercurial with changeset evolution addssome additional concepts to the graph of changesets. Understandingthose concepts will make you an informed and empowered user of``evolve``...note:: This document contains math! If you have a pathological fear of set theory and the associated notation, you might be better off just reading the `user guide`_. But if you appreciate the theoretical rigour underlying core Mercurial, you will be happy to know that it continues right into changeset evolution...note:: This document is incomplete! (The formatting of the math isn't quite right yet, and the diagrams are missing for malformatted.)This document follows standard set theory notation:: x ∈ A: x is a member of A A ∪ B: union of A and B: { x | x ∈ A or x ∈ B } A ∖ B: set difference: { x | x ∈ A and x ∉ B } A ⊇ B: superset: if x ∈ B, then x ∈ A.._`user guide`: user-guide.htmlPhases------First, every changeset in a Mercurial repository (since 2.3) has a*phase*. Phases are independent of ``evolve`` and they affectMercurial usage with or without changeset evolution. However, theywere implemented in order to support evolution, and are a criticalfoundation of ``evolve``.Phases are strictly ordered: secret > draft > publicChangesets generally only move from a higher phase to a lower phase.Typically, changesets start life in *draft* phase, and move to*public* phase when they are pushed to a public repository. (You canset the default phase of new commits in Mercurial configuration.)The purpose of phases is to prevent modifying published history.``evolve`` will therefore only let you rewrite changesets in one ofthe two *mutable* phases (secret or draft).Run ``hg help phases`` for more information on phases.Obsolete changesets-------------------*Obsolescence* is they key concept at the heart of changesetevolution. Everything else in this document depends on understandingobsolescence. So: what does it mean for a changeset to be obsolete?In implementation terms, there is an *obsolescence marker* associatedwith changesets: every changeset is either obsolete or not.The simplest way that a changeset becomes obsolete is by *pruning* it.The ``hg prune`` command simply marks the specified changesetsobsolete, as long as they are mutable.More commonly, a changeset *A* becomes obsolete by *amending* it.Amendment creates a new changeset *A'* that replaces *A*, which is nowobsolete. *A'* is the successor of *A*, and *A* the predecessor of *A'*: [diagram: A and A' with pred/succ edge]The predecessor/successor relationship forms an additional*obsolescence graph* overlaid on top of the traditional DAG formed bychangesets and their parent/child relationships. In fact, theobsolescence graph is second-order version control. Where thetraditional parent/child DAG tracks changes to your source code, theobsolescence graph tracks changes to your changesets. It tracks theevolution of your changesets.(If you prefer a calculus metaphor to set theory, it might help tothink of the traditional parent/child DAG as the first derivative ofyour source code, and the obsolescence DAG as the second derivative.)Troubled changesets (unstable, bumped, divergent)-------------------------------------------------Evolving history can introduce problems that need to be solved. Forexample, if you prune a changeset *P* but not its descendants, thosedescendants are now on thin ice. To push a changeset to anotherrepository *R*, all of its ancestors must be present in *R* or pushedat the same time. But Mercurial does not push obsolete changesets like*P*, so it cannot push the descendants of *P*. Any non-obsoletechangeset that is a descendant of an obsolete changeset is said to be*unstable*. [diagram: obsolete cset with non-obsolete descendant]Another sort of trouble occurs when two developers, Alice and Bob,collaborate via a shared non-publishing repository. (This is howdevelopers can safely `share mutable history`_.) Say Alice and Bobboth start the day with changeset *C* in *draft* phase. If Alicepushes *C* to their public repository, then it is now published andtherefore immutable. But Bob is working from a desert island andcannot pull this change in *C*'s phase. For Bob, *C* is still in draftphase and therefore mutable. So Bob amends *C*, which marks itobsolete and replaces it with *C'*. When he is back online and pullsfrom the public repository, Mercurial learns that *C* is public, whichmeans it cannot be obsolete. We say that *C'* is *bumped*, since it isthe successor of a public changeset..._`share mutable history`: sharing.html(Incidentally, the terminology here comes from airline overbooking: iftwo people have bought tickets for the same seat on a plane and theyboth show up at the airport, only one of them gets on the plane. Thepassenger who is left behind in the airport terminal has been"bumped".)The third sort of trouble is when Alice and Bob both amend the samechangeset *C* to have different successors. When this happens, thesuccessors are both called *divergent* (unless one of them is inpublic phase; only mutable changesets are divergent).The collective term for unstable, bumped, and divergent changeset is*troubled*:: troubled = unstable ∪ bumped ∪ divergentIt is possible for a changeset to be in any of the troubled categoriesat the same time: it might be unstable and divergent, or bumped anddivergent, or whatever. [diagram: Venn diagram of troubled changesets, showing overlap]The presence of troubled changesets indicates the need to run ``hgevolve``.Hidden (and visible) changesets-------------------------------Some obsolete changesets are *hidden*: deliberately suppressed byMercurial and usually not visible through the UI. (As of Mercurial2.9, there are still some commands that inadvertently reveal hiddenchangesets; these are bugs and will be fixed in due course.)All hidden changesets are obsolete, and all obsolete changesets arepart of your repository. Mathematically speaking:: repo ⊇ obsolete ⊇ hiddenOr, putting it visually: [diagram: Venn diagram showing nested strict subsets]However, the presence of obsolete but not hidden changesets should betemporary. The desired end state for any history mutation operation isthat all obsolete changesets are hidden, i.e.: repo ⊇ obsolete, obsolete = hiddenVisually: [diagram: Venn diagram showing obsolete = hidden, subset of repo]Why is this changeset visible?------------------------------Any changeset which is not hidden is *visible*. That is, :: visible = repo ∖ hidden(Recall that ∖ means set difference: *visible* is the set ofchangesets that are in *repo* but not in *hidden*.)After amending or pruning a changeset, you might expect it to behidden. It doesn't always work out that way. The precise rules are:: hideable = obsolete blockers = bookmarks ∪ parents(workingcopy) ∪ localtags hidden = hideable ∖ ancestors((repo ∖ hideable) ∪ blockers)This will probably be clearer with a worked example. First, here's arepository with some obsolete changesets, some troubled changesets,one bookmark, a working copy, and some hidden changesets:: x-x / -o-o-o-o \ x-x-oHere's the computation required to determine which changesets arehidden:: repo = { 0, 1, 2, 3, 4, 5, 6, 7, 8 } hideable = obsolete = { 2, 4, 5, 8 } blockers = { 6 } ∪ { 4 } ∪ {} blockers = { 4, 6 } hidden = hideable ∖ ancestors((repo ∖ { 2, 4, 5, 8 }) ∪ { 4, 6 }) hidden = hideable ∖ ancestors({ 0, 1, 3, 6, 7 } ∪ { 4, 6 }) hidden = hideable ∖ ancestors({ 0, 1, 3, 4, 6, 7 }) hidden = { 2, 4, 5, 8 } ∖ { 0, 1, 2, 3, 4, 5, 6, 7 } hidden = { 8 }