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!
--- 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,
--- 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."""
--- 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):
--- 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)
--- /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
+
--- 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
-------------------