# HG changeset patch # User Pierre-Yves David # Date 1498610757 -7200 # Node ID b933a8068c17ec6949266f329909a8a62f52b064 # Parent 1d2c66dc4ee3f2311e90329e584760c9759d5edb topic: add some initial support for using stack on named branch Stack is a useful command that can make sense in other context. The current support is hacky and awful, but we have something! diff -r 1d2c66dc4ee3 -r b933a8068c17 README --- a/README Wed Jun 28 01:58:09 2017 +0200 +++ b/README Wed Jun 28 02:45:57 2017 +0200 @@ -129,6 +129,7 @@ - stack: also show the unstable status for the current changeset (issue5553) - stack: properly abort when and unknown topic is requested, + - stack: add basic and raw support for named branches - topic: changing topic on revs no longer adds extra instability (issue5441) - topic: topics: rename '--change' flag to '--rev' flag, - topic: multiple large performance improvements, diff -r 1d2c66dc4ee3 -r b933a8068c17 hgext3rd/topic/__init__.py --- a/hgext3rd/topic/__init__.py Wed Jun 28 01:58:09 2017 +0200 +++ b/hgext3rd/topic/__init__.py Wed Jun 28 02:45:57 2017 +0200 @@ -122,19 +122,29 @@ context.basectx.topic = _contexttopic topicrev = re.compile(r'^t\d+$') +branchrev = re.compile(r'^b\d+$') def _namemap(repo, name): + revs = None if topicrev.match(name): idx = int(name[1:]) - topic = repo.currenttopic - if not topic: + ttype = 'topic' + tname = topic = repo.currenttopic + if not tname: raise error.Abort(_('cannot resolve "%s": no active topic') % name) revs = list(stack.getstack(repo, topic=topic)) + elif branchrev.match(name): + ttype = 'branch' + idx = int(name[1:]) + tname = branch = repo[None].branch() + revs = list(stack.getstack(repo, branch=branch)) + + if revs is not None: try: r = revs[idx - 1] except IndexError: - msg = _('cannot resolve "%s": topic "%s" has only %d changesets') - raise error.Abort(msg % (name, topic, len(revs))) + msg = _('cannot resolve "%s": %s "%s" has only %d changesets') + raise error.Abort(msg % (name, ttype, tname, len(revs))) return [repo[r].node()] if name not in repo.topics: return [] @@ -282,7 +292,7 @@ topic = repo.currenttopic if not topic: raise error.Abort(_('no active topic to list')) - return stack.showstack(ui, repo, topic, opts) + return stack.showstack(ui, repo, topic=topic, opts=opts) if rev: if not obsolete.isenabled(repo, obsolete.createmarkersopt): @@ -309,10 +319,13 @@ List the current topic by default.""" if not topic: + topic = None + branch = None + if topic is None and repo.currenttopic: topic = repo.currenttopic - if not topic: - raise error.Abort(_('no active topic to list')) - return stack.showstack(ui, repo, topic=topic, opts=opts) + if topic is None: + branch = repo[None].branch() + return stack.showstack(ui, repo, branch=branch, topic=topic, opts=opts) def _changecurrenttopic(repo, newtopic): """changes the current topic.""" diff -r 1d2c66dc4ee3 -r b933a8068c17 hgext3rd/topic/revset.py --- a/hgext3rd/topic/revset.py Wed Jun 28 01:58:09 2017 +0200 +++ b/hgext3rd/topic/revset.py Wed Jun 28 02:45:57 2017 +0200 @@ -75,7 +75,13 @@ if not topic: raise error.Abort(_('no active topic to list')) # ordering hack, boo - return revset.baseset(stack.getstack(repo, topic=topic)) & subset + topic = None + branch = None + if not topic and repo.currenttopic: + topic = repo.currenttopic + if not topic: + branch = repo[None].branch() + return revset.baseset(stack.getstack(repo, branch=branch, topic=topic)) & subset def modsetup(ui): diff -r 1d2c66dc4ee3 -r b933a8068c17 hgext3rd/topic/stack.py --- a/hgext3rd/topic/stack.py Wed Jun 28 01:58:09 2017 +0200 +++ b/hgext3rd/topic/stack.py Wed Jun 28 02:45:57 2017 +0200 @@ -10,9 +10,16 @@ ) from .evolvebits import builddependencies, _orderrevs, _singlesuccessor -def getstack(repo, topic=None): +def getstack(repo, branch=None, topic=None): # XXX need sorting - trevs = repo.revs("topic(%s) - obsolete()", topic) + if topic is not None and branch is not None: + raise error.ProgrammingError('both branch and topic specified (not defined yet)') + elif topic is not None: + trevs = repo.revs("topic(%s) - obsolete()", topic) + elif branch is not None: + trevs = repo.revs("branch(%s) - obsolete()", branch) + else: + raise error.ProgrammingError('neither branch and topic specified (not defined yet)') return _orderrevs(repo, trevs) def labelsgen(prefix, labelssuffix): @@ -21,12 +28,22 @@ """ return ' '.join(prefix % suffix for suffix in labelssuffix) -def showstack(ui, repo, topic=None, opts=None): +def showstack(ui, repo, branch=None, topic=None, opts=None): if opts is None: opts = {} - if topic not in repo.topics: - raise error.Abort(_('cannot resolve "%s": no such topic found') % topic) + if topic is not None and branch is not None: + msg = 'both branch and topic specified [%s]{%s}(not defined yet)' + msg %= (branch, topic) + raise error.ProgrammingError(msg) + elif topic is not None: + prefix = 't' + if topic not in repo.topics: + raise error.Abort(_('cannot resolve "%s": no such topic found') % topic) + elif branch is not None: + prefix = 'b' + else: + raise error.ProgrammingError('neither branch and topic specified (not defined yet)') fm = ui.formatter('topicstack', opts) prev = None @@ -37,7 +54,7 @@ if topic == repo.currenttopic: label = 'topic.active' - data = stackdata(repo, topic=topic) + data = stackdata(repo, branch=branch, topic=topic) fm.plain(_('### topic: %s') % ui.label(topic, label), label='topic.stack.summary.topic') @@ -58,7 +75,7 @@ fm.plain('%d behind' % data['behindcount'], label='topic.stack.summary.behindcount') fm.plain('\n') - for idx, r in enumerate(getstack(repo, topic=topic), 1): + for idx, r in enumerate(getstack(repo, branch=branch, topic=topic), 1): ctx = repo[r] p1 = ctx.p1() if p1.obsolete(): @@ -100,7 +117,7 @@ if idx is None: fm.plain(' ') else: - fm.write('topic.stack.index', 't%d', idx, + fm.write('topic.stack.index', '%s%%d' % prefix, idx, label='topic.stack.index ' + labelsgen('topic.stack.index.%s', states)) fm.write('topic.stack.state.symbol', '%s', symbol, label='topic.stack.state ' + labelsgen('topic.stack.state.%s', states)) @@ -113,7 +130,7 @@ fm.plain('\n') fm.end() -def stackdata(repo, topic=None): +def stackdata(repo, branch=None, topic=None): """get various data about a stack :changesetcount: number of non-obsolete changesets in the stack @@ -122,7 +139,7 @@ :behindcount: number of changeset on rebase destination """ data = {} - revs = repo.revs("topic(%s) - obsolete()", topic) + revs = getstack(repo, branch, topic) data['changesetcount'] = len(revs) data['troubledcount'] = len([r for r in revs if repo[r].troubled()]) deps, rdeps = builddependencies(repo, revs) diff -r 1d2c66dc4ee3 -r b933a8068c17 tests/test-stack-branch.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-stack-branch.t Wed Jun 28 02:45:57 2017 +0200 @@ -0,0 +1,257 @@ + + $ . "$TESTDIR/testlib/topic_setup.sh" + +Initial setup + + $ cat << EOF >> $HGRCPATH + > [ui] + > logtemplate = {rev} {branch} \{{get(namespaces, "topics")}} {phase} {desc|firstline}\n + > [experimental] + > evolution=createmarkers,exchange,allowunstable + > EOF + + $ hg init main + $ cd main + $ hg branch other + marked working directory as branch other + (branches are permanent and global, did you want a bookmark?) + $ echo aaa > aaa + $ hg add aaa + $ hg commit -m c_a + $ echo aaa > bbb + $ hg add bbb + $ hg commit -m c_b + $ hg branch foo + marked working directory as branch foo + $ echo aaa > ccc + $ hg add ccc + $ hg commit -m c_c + $ echo aaa > ddd + $ hg add ddd + $ hg commit -m c_d + $ echo aaa > eee + $ hg add eee + $ hg commit -m c_e + $ echo aaa > fff + $ hg add fff + $ hg commit -m c_f + $ hg log -G + @ 5 foo {} draft c_f + | + o 4 foo {} draft c_e + | + o 3 foo {} draft c_d + | + o 2 foo {} draft c_c + | + o 1 other {} draft c_b + | + o 0 other {} draft c_a + + +Check that topic without any parent does not crash --list +--------------------------------------------------------- + + $ hg up other + 0 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ hg stack + ### topic: None + ### branch: other + b2@ c_b (current) + b1: c_a + $ hg phase --public 'branch("other")' + $ hg up foo + 4 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Simple test +----------- + +'hg stack' list all changeset in the topic + + $ hg branch + foo + $ hg stack + ### topic: None + ### branch: foo + b4@ c_f (current) + b3: c_e + b2: c_d + b1: c_c + ^ c_b + +Test "t#" reference +------------------- + + $ hg up b2 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg up foo + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg up b42 + abort: cannot resolve "b42": branch "foo" has only 4 changesets + [255] + $ hg up b2 + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg summary + parent: 3:f61adbacd17a + c_d + branch: foo + commit: (clean) + update: 2 new changesets (update) + phases: 4 draft + +Case with some of the branch unstable +------------------------------------ + + $ echo bbb > ddd + $ hg commit --amend + $ hg log -G + @ 7 foo {} draft c_d + | + | o 5 foo {} draft c_f + | | + | o 4 foo {} draft c_e + | | + | x 3 foo {} draft c_d + |/ + o 2 foo {} draft c_c + | + o 1 other {} public c_b + | + o 0 other {} public c_a + + $ hg stack + ### topic: None + ### branch: foo + b4$ c_f (unstable) + b3$ c_e (unstable) + b2@ c_d (current) + b1: c_c + ^ c_b + $ hg up b3 + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg stack + ### topic: None + ### branch: foo + b4$ c_f (unstable) + b3$ c_e (current unstable) + b2: c_d + b1: c_c + ^ c_b + $ hg up b2 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + +Also test the revset: + + $ hg log -r 'stack()' + abort: no active topic to list + [255] + +Case with multiple heads on the topic +------------------------------------- + +Make things linear again + + $ hg rebase -s 'desc(c_e)' -d 'desc(c_d) - obsolete()' + rebasing 4:4f2a69f6d380 "c_e" + rebasing 5:913c298d8b0a "c_f" + $ hg log -G + o 9 foo {} draft c_f + | + o 8 foo {} draft c_e + | + @ 7 foo {} draft c_d + | + o 2 foo {} draft c_c + | + o 1 other {} public c_b + | + o 0 other {} public c_a + + +Create the second branch + + $ hg up 'desc(c_d)' + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ echo aaa > ggg + $ hg add ggg + $ hg commit -m c_g + created new head + $ echo aaa > hhh + $ hg add hhh + $ hg commit -m c_h + $ hg log -G + @ 11 foo {} draft c_h + | + o 10 foo {} draft c_g + | + | o 9 foo {} draft c_f + | | + | o 8 foo {} draft c_e + |/ + o 7 foo {} draft c_d + | + o 2 foo {} draft c_c + | + o 1 other {} public c_b + | + o 0 other {} public c_a + + +Test output + + $ hg stack + ### topic: None (2 heads) + ### branch: foo + b6: c_f + b5: c_e + b2^ c_d (base) + b4@ c_h (current) + b3: c_g + b2: c_d + b1: c_c + ^ c_b + +Case with multiple heads on the topic with unstability involved +--------------------------------------------------------------- + +We amend the message to make sure the display base pick the right changeset + + $ hg up 'desc(c_d)' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo ccc > ddd + $ hg commit --amend -m 'c_D' + $ hg rebase -d . -s 'desc(c_g)' + rebasing 10:2ebb6e48ab8a "c_g" + rebasing 11:634f38e27a1d "c_h" + $ hg log -G + o 15 foo {} draft c_h + | + o 14 foo {} draft c_g + | + @ 13 foo {} draft c_D + | + | o 9 foo {} draft c_f + | | + | o 8 foo {} draft c_e + | | + | x 7 foo {} draft c_d + |/ + o 2 foo {} draft c_c + | + o 1 other {} public c_b + | + o 0 other {} public c_a + + + $ hg stack + ### topic: None (2 heads) + ### branch: foo + b6$ c_f (unstable) + b5$ c_e (unstable) + b2^ c_D (base) + b4: c_h + b3: c_g + b2@ c_D (current) + b1: c_c + ^ c_b + diff -r 1d2c66dc4ee3 -r b933a8068c17 tests/test-topic-stack.t --- a/tests/test-topic-stack.t Wed Jun 28 01:58:09 2017 +0200 +++ b/tests/test-topic-stack.t Wed Jun 28 02:45:57 2017 +0200 @@ -129,8 +129,14 @@ $ hg topic --clear $ hg stack - abort: no active topic to list - [255] + ### topic: None + ### branch: default + b6@ c_f (current) + b5: c_e + b4: c_d + b3: c_c + b2: c_b + b1: c_a Test "t#" reference -------------------