Differences between revisions 33 and 35 (spanning 2 versions)
Revision 33 as of 2015-10-23 22:03:55
Size: 20877
Comment: merge destination
Revision 35 as of 2015-10-24 08:40:00
Size: 20782
Comment:
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
Line 6: Line 5:
Line 12: Line 10:
Line 20: Line 17:
Line 24: Line 20:
Line 40: Line 35:

Changeset with topic are ''only aspiring'' to be part of the named branch, but
not fully in that branch yet. When you have branch `foo` name `foo` is resolved
to the heads of `foo` with no topic.
Changeset with topic are ''only aspiring'' to be part of the named branch, but not fully in that branch yet. When you have branch `foo` name `foo` is resolved to the heads of `foo` with no topic.
Line 60: Line 52:
Line 80: Line 71:
Line 98: Line 88:
Line 105: Line 94:
Line 108: Line 96:
Line 110: Line 97:

{{{#!dot
digraph {
  rankdir=LR
  node [shape=box,style=filled]
  A -> B -> C;
  B -> X -> Y;
  A [label="A\n(b: foo)\n(wdir)",fillcolor="lightpink"];
  B [label="B\n(b: foo)"];
  C [label="C\n(b: foo)"];
  X [label="X\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
  Y [label="Y\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
}
}}}

{{{#!dot
digraph {
  rankdir=LR
  node [shape=box,style=filled]
  A -> B -> C;
  B -> X -> Y;
  A [label="A\n(b: foo)\n(wdir)",color="red",fillcolor="lightgrey"];
  B [label="B\n(b: foo)"];
  C [label="C\n(b: foo)"];
  X [label="X\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
  Y [label="Y\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
}
}}}
Line 128: Line 112:
* Currently active branch: `foo`
* Currently active topic: ø
* Running `hg update` bring you on the head of `foo` branch (untopiced-head).
* Currently active branch: `foo` * Currently active topic: ø * Running `hg update` bring you on the head of `foo` branch (untopiced-head).
Line 145: Line 127:
Line 149: Line 130:
Line 163: Line 143:

Line 182: Line 160:
Line 184: Line 161:

`W` was on topic `bar`, but it is now public so `bar` does not apply to `W`. However, the
working copy is still having an active topic.
`W` was on topic `bar`, but it is now public so `bar` does not apply to `W`. However, the working copy is still having an active topic.
Line 201: Line 176:

* Currently active branch: `foo`
* Currently active topic: `bar`
* Running `hg update` bring you on the head of `bar` topic.
* Currently active branch: `foo` * Currently active topic: `bar` * Running `hg update` bring you on the head of `bar` topic.
Line 219: Line 191:

Classical case for getting in that situation is to `hg up bar` that bring you on
`W` with `bar` active, then pull that bring you `B`, `C`, `X`, `Y` and turn `W`
public.

The intent here is to work on topic `bar` and `hg update` should make
you up-to-date in the `bar` context.
Classical case for getting in that situation is to `hg up bar` that bring you on `W` with `bar` active, then pull that bring you `B`, `C`, `X`, `Y` and turn `W` public.

The intent here is to work on topic `bar` and `hg update` should make you up-to-date in the `bar` context.
Line 230: Line 198:

`W` was on topic `bar`, but it is now public so `bar` does not apply to `W`
anymore. However, the working copy is still having an active topic.
`W` was on topic `bar`, but it is now public so `bar` does not apply to `W` anymore. However, the working copy is still having an active topic.
Line 244: Line 210:

* Currently active branch: `foo`
* Currently active topic: `bar`
* Running `hg update` bring you on the head of `foo` branch.
* The `bar`is de-activated
* Currently active branch: `foo` * Currently active topic: `bar` * Running `hg update` bring you on the head of `foo` branch. * The `bar`is de-activated
Line 260: Line 222:

Classical case for getting in that situation is to be working on topic `bar`
then `bar` got 'accepted' and get public. As you former `bar` is not the new
head of `foo`, people merge/rebase and publish more changesets on top of that.

In that case inferring that you topic is "done" and being up to date mean "bring
me to the latest of my branch seems to make sense.

A possible issue, `hg update; hg pull` can provide different from `hg pull; hg
update`, in the case where `hg update` is bringing changeset on the `bar` topic
again.

'''Alternatively''' we could not update, pointing at a command to disable the
topic.
Classical case for getting in that situation is to be working on topic `bar` then `bar` got 'accepted' and get public. As you former `bar` is not the new head of `foo`, people merge/rebase and publish more changesets on top of that.

In that case inferring that you topic is "done" and being up to date mean "bring me to the latest of my branch seems to make sense.

A possible issue, `hg update; hg pull` can provide different from `hg pull; hg update`, in the case where `hg update` is bringing changeset on the `bar` topic again.

'''Alternatively''' we could not update, pointing at a command to disable the topic.
Line 277: Line 231:
Line 281: Line 234:

{{{#!dot
digraph {
  rankdir=LR
  node [shape=box,style=filled]
  A -> B -> C;
  A -> X -> Y;
  A [label="A\n(b: foo)"];
  B [label="B\n(b: foo)"];
  C [label="C\n(b: foo)"];
  X [label="X\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
  Y [label="Y\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
}
}}}
{{{#!dot
digraph {
  rankdir=LR
  node [shape=box,style=filled]
  A -> B -> C;
  A -> X -> Y;
  A [label="A\n(b: foo)"];
  B [label="B\n(b: foo)"];
  C [label="C\n(b: foo)"];
  X [label="X\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
  Y [label="Y\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
}
}}}
Line 298: Line 249:
Pushing the following to a publishing server would make `X` and `Y` public,
fading their `bar` topic making them plain member of branch `foo`, creating a
new head.
Pushing the following to a publishing server would make `X` and `Y` public, fading their `bar` topic making them plain member of branch `foo`, creating a new head.
Line 303: Line 252:
Line 322: Line 270:

push is accepted, all `X`, `Y`, `Z` changeset become public and new single
head of branch `foo` in `Z`.
push is accepted, all `X`, `Y`, `Z` changeset become public and new single head of branch `foo` in `Z`.
Line 327: Line 273:
Line 340: Line 285:
Line 353: Line 297:
Line 359: Line 302:
Line 372: Line 314:
Line 376: Line 317:
Line 387: Line 327:
Line 391: Line 330:
Line 393: Line 331:
Line 406: Line 343:
Line 412: Line 348:

{{{#!dot
digraph {
  rankdir=LR
  node [shape=box,style=filled]
  A -> B -> C;
  A -> X -> Y;
  A [label="A\n(b: foo)"];
  B [label="B\n(b: foo)"];
  C [label="C\n(b: foo)"];
  X [label="X\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
  Y [label="Y\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
}
}}}
{{{#!dot
digraph {
  rankdir=LR
  node [shape=box,style=filled]
  A -> B -> C;
  A -> X -> Y;
  A [label="A\n(b: foo)"];
  B [label="B\n(b: foo)"];
  C [label="C\n(b: foo)"];
  X [label="X\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
  Y [label="Y\n(b: foo)\n(t: bar)",fillcolor="lightblue"];
}
}}}
Line 432: Line 366:
Line 449: Line 382:
Line 456: Line 388:
Line 469: Line 400:

Line 486: Line 415:
Line 493: Line 421:
Line 506: Line 433:
Line 510: Line 436:
Line 523: Line 448:

Line 540: Line 463:
Line 544: Line 466:
Line 557: Line 478:

Line 570: Line 489:
Line 572: Line 490:
Line 575: Line 494:
Line 586: Line 504:
Line 593: Line 510:
Line 597: Line 513:
Line 610: Line 525:
Line 628: Line 542:
Line 630: Line 543:
Line 645: Line 557:
Line 649: Line 560:
Line 662: Line 572:
Line 666: Line 575:
Line 680: Line 588:
Line 699: Line 606:
Line 701: Line 607:
Line 716: Line 621:
Line 720: Line 624:
Line 722: Line 625:
Line 724: Line 626:
Line 726: Line 627:
Line 728: Line 628:
Line 734: Line 633:
  * A benefit of archiving them is that users can query for topics, eg you could say `hg log -r topic(issue123)` which would help   * A benefit of archiving them is that users can query for topics, eg you could say `hg log -r topic(issue123)` which would help
Line 737: Line 636:
  * 'default//feature-foo' would be a topic 'feature-foo' tracking the 'default' branch.
   * 'stable//issue4700' would be a topic 'issue4700' tracking branch stable.
   * '@//feature-bar' would be a topic 'feature-bar' tracking bookmark '@' ?
  * 'stable//issue4689//issue4700' would be a topic 'issue4700' tracking the topic 'stable//issue4689'. When topic 'issue4686' face away (because published), the tracking fallback to 'stable'.
  * 'default//feature-foo' would be a topic 'feature-foo' tracking the 'default' branch.
  * 'stable//issue4700' would be a topic 'issue4700' tracking branch stable.
  * '@//feature-bar' would be a topic 'feature-bar' tracking bookmark '@' ?
  * 'stable//issue4689//issue4700' would be a topic 'issue4700' tracking the topic 'stable//issue4689'. When topic 'issue4686' face away (because published), the tracking fallback to 'stable'.
Line 747: Line 646:
  * that is, it'd be legal to have one head per topic on a non-publishing server.   * that is, it'd be legal to have one head per topic on a non-publishing server.
Line 750: Line 649:
  * Augie doesn't feel great about this option just because of UI complexity.   * Augie doesn't feel great about this option just because of UI complexity.
Line 753: Line 652:
  * One of the major complaints about evolve from veteran mq users is that their patches no longer have explicit names. Topics provide a potential way to name patches again.   * One of the major complaints about evolve from veteran mq users is that their patches no longer have explicit names. Topics provide a potential way to name patches again.
Line 756: Line 655:
Line 759: Line 657:
Code is available at [[http://hg.durin42.com/hg-topic-experiment/]]. Code is available at http://hg.durin42.com/hg-topic-experiment/.
Line 762: Line 660:
Line 767: Line 664:
Line 769: Line 665:
Line 775: Line 670:
Line 777: Line 671:
 * bambams' (on freenode) proposal that excludes phases: https://bpaste.net/show/107d9bb1be4c
Line 779: Line 674:
CategoryDeveloper and CategoryNewFeatures  CategoryDeveloper and CategoryNewFeatures

Note:

This page is primarily intended for developers of Mercurial.

Topic Plan

A (speculative) plan for topic branching that would work more seamlessly with common Mercurial workflows. Still very early prototype stage. Everything is subject to change.

1. Problem Statement

The Mercurial community has been struggling for years to define a nice way to handle 'topic' branches (sometimes also called 'feature' branches), especially when it comes to sharing them with other people (mainly for code review or other collaboration.)

Bookmarks are a clone of git's refs, which seems to work more poorly in Mercurial than they do in Git, in part because the synchronization parts of bookmarks aren't really done. Adding the remaining bits of git's refs to Mercurial has been controversial, and may represent enough of a behavior change that it's infeasible.

Named branches are visible forever in the revision history, which makes them unsuitable for feature branch work as the feature branch names rapidly pollute the output of things like hg branches.

2. Current Target

This describe the target semantic and behavior for topics. Of course some adjustement can be built.

2.1. General semantics

TL;DR; topic are an extra "light-branch" data relevant to draft changesets.

  • Topic is a name explicitly attached to changesets,

  • This Topic data is primarily meant to categorize draft changeset and fade out when things become public,

  • Changeset have both a topic and a 'branch'. The topic allow to gather related in progress work, while the branch data refer to the long terms line of development.

  • Behaviors focus on ensuring any name have a single head.

  • Behaviors related to named branches behave mostly as if the draft-with topic are not on the branch (yet).
  • Behaviors within a topic are similar (with minor sensible difference) to named branches one within the topic.

2.2. General effect on named branch

Changeset with topic are only aspiring to be part of the named branch, but not fully in that branch yet. When you have branch foo name foo is resolved to the heads of foo with no topic.

There is a couple of examples:

Multiple heads are not longer ambiguous,

  • The name foo resolve to C,

  • The name bar resolve to Y,


Partial data does not have radical change on the definition,

  • The name foo resolve to B,

  • The name var resolve to Y,


Usual traversal rules apply:

  • The name foo resolve to B,

  • The name var resolve to X,

2.3. Behavior for update

(This implies change in hg update behavior (but are not super relevant))

2.3.1. Case 1: active branch

Currently active: branch foo', no active topic. Running hg update bring you on the head of foo.

* Currently active branch: foo * Currently active topic: ø * Running hg update bring you on the head of foo branch (untopiced-head).


2.3.2. Case 2: active topic

  • Currently active branch foo

  • Currently active topic: bar

  • Running hg update bring you on the head of bar topic.

2.3.3. Case 3: active topic, lagging behind

W was on topic bar, but it is now public so bar does not apply to W. However, the working copy is still having an active topic.

* Currently active branch: foo * Currently active topic: bar * Running hg update bring you on the head of bar topic.

Classical case for getting in that situation is to hg up bar that bring you on W with bar active, then pull that bring you B, C, X, Y and turn W public.

The intent here is to work on topic bar and hg update should make you up-to-date in the bar context.

Alternatively we could requires more data from the user.

2.3.4. Case 4: active topic, topic is closed

W was on topic bar, but it is now public so bar does not apply to W anymore. However, the working copy is still having an active topic.

* Currently active branch: foo * Currently active topic: bar * Running hg update bring you on the head of foo branch. * The baris de-activated

Classical case for getting in that situation is to be working on topic bar then bar got 'accepted' and get public. As you former bar is not the new head of foo, people merge/rebase and publish more changesets on top of that.

In that case inferring that you topic is "done" and being up to date mean "bring me to the latest of my branch seems to make sense.

A possible issue, hg update; hg pull can provide different from hg pull; hg update, in the case where hg update is bringing changeset on the bar topic again.

Alternatively we could not update, pointing at a command to disable the topic.

2.4. Behavior for push to publishing (default)

Mercurial will keep enforcing a single head for each name:

2.5. Case A: pushing multiple topological branch

  • push is rejected as creating a new head on branch foo (Y)

Pushing the following to a publishing server would make X and Y public, fading their bar topic making them plain member of branch foo, creating a new head.

2.6. Case B: pushing a merged topic

User can then merge/rebase

push is accepted, all X, Y, Z changeset become public and new single head of branch foo in Z.

2.7. Case C: linear push but remote unsynched heads

Local:

Remote:

Push fail as Y creating a new head on branch foo.

User have to pull and merge as usual.

2.8. Case D: linear push while in sync

Local:

  • Push succeed and fade topic bar out.

2.8.1. case E: new named branch

  • Push fails, creating new named branch bli

2.9. Behavior for push to non-publishing repo

2.9.1. New topic

Local:

  • Push succeed
  • branch foo head is A

  • topic bar head is Y

2.9.2. New topic on a topological branch (1)

  • Push succeed
  • branch foo head is C

  • topic bar head is Y

2.9.3. New topic on a topological branch (2)

  • Push succeed
  • branch foo head is C

  • topic boo head is J

  • topic bar head is Y

2.9.4. New topic on a topological branch (3)

Local:

Remote:

  • Push succeed
  • branch foo head is C

  • topic boo head is J

  • topic bar head is Y

2.9.5. New head on topic

  • push fail, new head on topic bar

2.9.6. Conflicting unrelated topic

Local:

Remote:

  • push fail, new head on topic bar

2.9.7. New head on branch

Local:

Remote:

  • push fail, creating new head B on branch foo

fail, creating new head B on branch foo

2.9.8. New topic on new branch

  • Push succeed
  • branch foo head is A

  • branch bli head is ø (does exist yet),

  • topic bar head is Y

2.10. Behavior for merge

(We won't discuss hg rebase, assuming it wil behave the same in 3.7)

2.10.1. Two heads on topic

  • hg merge pick Z as destination

2.10.2. Topic has three heads

  • Merge abort, ambiguous destination.

2.10.3. Topic has one head and include branch head

Local:

  • Nothing to merge

2.10.4. Topic has one head and is behind compared to branch head

  • hg merge pick the branch head as destination (as the topic is alread linear)

2.10.5. Topic has one head, branch has multiple heads

  • hg merge abort, ambiguous destination

2.11. Stacked diffs workflow

2.12. User Transition

3. Pro and Cons

4. Other questions

5. Open ideas

This is a list of idea that emerged while brainstorming. This served as base for the current things.

  • Topic could be a property attached to each changeset (grouping them by similar topic)
  • Topic could fade away when changesets become public (either archived or plain dropped)
    • A benefit of archiving them is that users can query for topics, eg you could say hg log -r topic(issue123) which would help

  • Tracking could be achieved through the naming scheme. eg:
    • 'default//feature-foo' would be a topic 'feature-foo' tracking the 'default' branch.
    • 'stable//issue4700' would be a topic 'issue4700' tracking branch stable.
    • '@//feature-bar' would be a topic 'feature-bar' tracking bookmark '@' ?
    • 'stable//issue4689//issue4700' would be a topic 'issue4700' tracking the topic 'stable//issue4689'. When topic 'issue4686' face away (because published), the tracking fallback to 'stable'.
  • Topics could be non contiguous (mpm idea) feature-foo -> fix-bar -> feature-foo. Allowing a streamlined work that is automatically split apart after that.

  • Topics could be hierarchical 'issue4700.test' 'issue4700.preparation', activation//reference could be done at any level 'issue4700' or 'issue4700' (this could help handle branching/different approach)
  • pushing a new head on a new topic to a non-publishing server would be allowed.

    • that is, it'd be legal to have one head per topic on a non-publishing server.
  • A changeset could maybe have multiple topic.
    • Augie doesn't feel great about this option just because of UI complexity.
  • Users can name patches (in a sense) without mq
    • One of the major complaints about evolve from veteran mq users is that their patches no longer have explicit names. Topics provide a potential way to name patches again.

6. Current Implementation

Assign topics to non-public changesets. A topic is like a named branch, in that it is a label stored in a changeset's extra, but that topics just disappear when the change moves to public phase (the data still exists, it's just not shown.)

Code is available at http://hg.durin42.com/hg-topic-experiment/.

6.0.1. Non-Goals

  • Topics are not suitable for long term branches. We have named branches for that (and possibly also bookmarks, depending on workflow.)
  • Topics are not suitable for tracking a moving point in public history. This seems to be a perfect fit for bookmarks.

6.1. Open Questions

  • Right now we use changeset extra for storing the topic. That might lead to bonus divergence problems. They might be easily fixed, but should we avoid that?
  • Should changesets be allowed multiple topics?
  • How permissive should we be on topic names?

7. See also


CategoryDeveloper and CategoryNewFeatures

TopicPlan (last edited 2021-10-08 14:11:59 by GeorgesRacinet)