changeset 6376:5c8196a550b6

topic: make hg stack work for branches with double slashes in them Other commands are likely affected as well. Things to note: we're using FQBN-formatted branch instead of full branch//namespace/topic in the workingctx.dirty() check because otherwise, if you had no topic active and were trying to update to a topic, wdir would be considered to be dirty and update would abort (same with unset topic namespace and trying to update to a changeset with topic namespace set). With just a bare branch, this doesn't happen, because you can't deactivate a branch. This is caught by test-topic.t.
author Anton Shestakov <av6@dwimlabs.net>
date Thu, 15 Dec 2022 15:44:39 +0400
parents fcbca44dd0df
children 1f88043c21c7
files hgext3rd/topic/__init__.py hgext3rd/topic/topicmap.py tests/test-stack-branch.t
diffstat 3 files changed, 111 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/hgext3rd/topic/__init__.py	Mon Jan 30 19:19:52 2023 +0400
+++ b/hgext3rd/topic/__init__.py	Thu Dec 15 15:44:39 2022 +0400
@@ -410,6 +410,26 @@
     with discovery.override_context_branch(repo) as repo:
         return orig(ui, repo, *args, **kwargs)
 
+def wrapwctxbranch(orig, self):
+    branch = orig(self)
+    return common.formatfqbn(branch=branch)
+
+def wrapwctxdirty(orig, self, missing=False, merge=True, branch=True):
+    """check whether a working directory is modified"""
+    # check subrepos first
+    for s in sorted(self.substate):
+        if self.sub(s).dirty(missing=missing):
+            return True
+    # check current working dir
+    return (
+        (merge and self.p2())
+        or (branch and self.branch() != common.formatfqbn(branch=self.p1().branch()))
+        or self.modified()
+        or self.added()
+        or self.removed()
+        or (missing and self.deleted())
+    )
+
 def uisetup(ui):
     destination.modsetup(ui)
     discovery.modsetup(ui)
@@ -458,6 +478,10 @@
 
     # Wrap workingctx extra to return the topic name
     extensions.wrapfunction(context.workingctx, '__init__', wrapinit)
+    # Wrap workingctx.branch() to return branch name in the "//" format
+    extensions.wrapfunction(context.workingctx, 'branch', wrapwctxbranch)
+    # Wrap workingctx.dirty() to check branch//namespace/topic
+    extensions.wrapfunction(context.workingctx, 'dirty', wrapwctxdirty)
     # Wrap changelog.add to drop empty topic
     extensions.wrapfunction(changelog.changelog, 'add', wrapadd)
     # Make exchange._checkpublish handle experimental.topic.publish-bare-branch
--- a/hgext3rd/topic/topicmap.py	Mon Jan 30 19:19:52 2023 +0400
+++ b/hgext3rd/topic/topicmap.py	Thu Dec 15 15:44:39 2022 +0400
@@ -14,6 +14,7 @@
 from . import (
     common,
     compat,
+    discovery,
 )
 
 basefilter = set([b'base', b'immutable'])
@@ -98,10 +99,12 @@
 def commitstatus(orig, repo, node, branch, bheads=None, tip=None, opts=None):
     # wrap commit status use the topic branch heads
     ctx = repo[node]
-    if ctx.topic() and ctx.branch() == branch:
+    ctxbranch = common.formatfqbn(branch=ctx.branch())
+    if ctx.topic() and ctxbranch == branch:
         bheads = repo.branchheads(b"%s:%s" % (branch, ctx.topic()))
 
-    ret = orig(repo, node, branch, bheads=bheads, tip=tip, opts=opts)
+    with discovery.override_context_branch(repo) as repo:
+        ret = orig(repo, node, branch, bheads=bheads, tip=tip, opts=opts)
 
     # logic copy-pasted from cmdutil.commitstatus()
     if opts is None:
@@ -111,7 +114,8 @@
     parents = ctx.parents()
 
     if (not opts.get(b'amend') and bheads and node not in bheads and not any(
-        p.node() in bheads and p.branch() == branch for p in parents
+        p.node() in bheads and common.formatfqbn(branch=p.branch()) == branch
+        for p in parents
     )):
         repo.ui.status(_(b"(consider using topic for lightweight branches."
                          b" See 'hg help topic')\n"))
--- a/tests/test-stack-branch.t	Mon Jan 30 19:19:52 2023 +0400
+++ b/tests/test-stack-branch.t	Thu Dec 15 15:44:39 2022 +0400
@@ -12,8 +12,8 @@
 
   $ hg init main
   $ cd main
-  $ hg branch other
-  marked working directory as branch other
+  $ hg branch double//slash
+  marked working directory as branch double//slash
   (branches are permanent and global, did you want a bookmark?)
   $ echo aaa > aaa
   $ hg add aaa
@@ -21,8 +21,8 @@
   $ echo aaa > bbb
   $ hg add bbb
   $ hg commit -m c_b
-  $ hg branch foo
-  marked working directory as branch foo
+  $ hg branch foo//bar
+  marked working directory as branch foo//bar
   $ echo aaa > ccc
   $ hg add ccc
   $ hg commit -m c_c
@@ -36,30 +36,30 @@
   $ hg add fff
   $ hg commit -m c_f
   $ hg log -G
-  @  5 foo {} draft c_f
+  @  5 foo//bar {} draft c_f
   |
-  o  4 foo {} draft c_e
+  o  4 foo//bar {} draft c_e
   |
-  o  3 foo {} draft c_d
+  o  3 foo//bar {} draft c_d
   |
-  o  2 foo {} draft c_c
+  o  2 foo//bar {} draft c_c
   |
-  o  1 other {} draft c_b
+  o  1 double//slash {} draft c_b
   |
-  o  0 other {} draft c_a
+  o  0 double//slash {} draft c_a
   
 
 Check that topic without any parent does not crash --list
 ---------------------------------------------------------
 
-  $ hg up other
+  $ hg up double//slash//
   0 files updated, 0 files merged, 4 files removed, 0 files unresolved
   $ hg stack
-  ### target: other (branch)
+  ### target: double//slash (branch)
   s2@ c_b (current)
   s1: c_a
-  $ hg phase --public 'branch("other")'
-  $ hg up foo
+  $ hg phase --public 'branch("double//slash//")'
+  $ hg up foo//bar//
   4 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
 Simple test
@@ -68,38 +68,38 @@
 'hg stack' list all changeset in the topic
 
   $ hg branch
-  foo
+  foo//bar
   $ hg stack
-  ### target: foo (branch)
+  ### target: foo//bar (branch)
   s4@ c_f (current)
   s3: c_e
   s2: c_d
   s1: c_c
   s0^ c_b (base)
   $ hg stack -v
-  ### target: foo (branch)
-  s4(913c298d8b0a)@ c_f (current)
-  s3(4f2a69f6d380): c_e
-  s2(f61adbacd17a): c_d
-  s1(3e9313bc4b71): c_c
-  s0(4a04f1104a27)^ c_b (base)
+  ### target: foo//bar (branch)
+  s4(18b3ff044de9)@ c_f (current)
+  s3(b1913e064ca1): c_e
+  s2(8fad7e98adf6): c_d
+  s1(da14ac95d156): c_c
+  s0(2450a061c0f0)^ c_b (base)
 
 Test "t#" reference
 -------------------
 
   $ hg up s2
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
-  $ hg up foo
+  $ hg up foo//bar//
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up s42
-  abort: cannot resolve "s42": branch "foo" has only 4 non-public changesets
+  abort: cannot resolve "s42": branch "foo//bar//" has only 4 non-public changesets
   [255]
   $ hg up s2
   0 files updated, 0 files merged, 2 files removed, 0 files unresolved
   $ hg summary
-  parent: 3:f61adbacd17a 
+  parent: 3:8fad7e98adf6 
    c_d
-  branch: foo
+  branch: foo//bar//
   commit: (clean)
   update: 2 new changesets (update)
   phases: 4 draft
@@ -111,22 +111,22 @@
   $ hg commit --amend
   2 new orphan changesets
   $ hg log -G
-  @  6 foo {} draft c_d
+  @  6 foo//bar {} draft c_d
   |
-  | *  5 foo {} draft c_f
+  | *  5 foo//bar {} draft c_f
   | |
-  | *  4 foo {} draft c_e
+  | *  4 foo//bar {} draft c_e
   | |
-  | x  3 foo {} draft c_d
+  | x  3 foo//bar {} draft c_d
   |/
-  o  2 foo {} draft c_c
+  o  2 foo//bar {} draft c_c
   |
-  o  1 other {} public c_b
+  o  1 double//slash {} public c_b
   |
-  o  0 other {} public c_a
+  o  0 double//slash {} public c_a
   
   $ hg stack
-  ### target: foo (branch)
+  ### target: foo//bar (branch)
   s4$ c_f (orphan)
   s3$ c_e (orphan)
   s2@ c_d (current)
@@ -135,7 +135,7 @@
   $ hg up s3
   2 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg stack
-  ### target: foo (branch)
+  ### target: foo//bar (branch)
   s4$ c_f (orphan)
   s3@ c_e (current orphan)
   s2: c_d
@@ -147,10 +147,10 @@
 Also test the revset:
 
   $ hg log -r 'stack()'
-  2 foo {} draft c_c
-  6 foo {} draft c_d
-  4 foo {} draft c_e
-  5 foo {} draft c_f
+  2 foo//bar {} draft c_c
+  6 foo//bar {} draft c_d
+  4 foo//bar {} draft c_e
+  5 foo//bar {} draft c_f
 
 Case with multiple heads on the topic
 -------------------------------------
@@ -158,20 +158,20 @@
 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"
+  rebasing 4:b1913e064ca1 "c_e"
+  rebasing 5:18b3ff044de9 "c_f"
   $ hg log -G
-  o  8 foo {} draft c_f
+  o  8 foo//bar {} draft c_f
   |
-  o  7 foo {} draft c_e
+  o  7 foo//bar {} draft c_e
   |
-  @  6 foo {} draft c_d
+  @  6 foo//bar {} draft c_d
   |
-  o  2 foo {} draft c_c
+  o  2 foo//bar {} draft c_c
   |
-  o  1 other {} public c_b
+  o  1 double//slash {} public c_b
   |
-  o  0 other {} public c_a
+  o  0 double//slash {} public c_a
   
 
 Create the second branch
@@ -187,27 +187,27 @@
   $ hg add hhh
   $ hg commit -m c_h
   $ hg log -G
-  @  10 foo {} draft c_h
+  @  10 foo//bar {} draft c_h
   |
-  o  9 foo {} draft c_g
+  o  9 foo//bar {} draft c_g
   |
-  | o  8 foo {} draft c_f
+  | o  8 foo//bar {} draft c_f
   | |
-  | o  7 foo {} draft c_e
+  | o  7 foo//bar {} draft c_e
   |/
-  o  6 foo {} draft c_d
+  o  6 foo//bar {} draft c_d
   |
-  o  2 foo {} draft c_c
+  o  2 foo//bar {} draft c_c
   |
-  o  1 other {} public c_b
+  o  1 double//slash {} public c_b
   |
-  o  0 other {} public c_a
+  o  0 double//slash {} public c_a
   
 
 Test output
 
   $ hg stack
-  ### target: foo (branch) (2 heads)
+  ### target: foo//bar (branch) (2 heads)
   s6@ c_h (current)
   s5: c_g
   s2^ c_d (base)
@@ -228,30 +228,30 @@
   $ hg commit --amend -m 'c_D' 
   4 new orphan changesets
   $ hg rebase -d . -s 'desc(c_g)'
-  rebasing 9:2ebb6e48ab8a "c_g"
-  rebasing 10:634f38e27a1d "c_h"
+  rebasing 9:8c1819a4441f "c_g"
+  rebasing 10:e255b784f0e9 "c_h"
   $ hg log -G
-  o  13 foo {} draft c_h
+  o  13 foo//bar {} draft c_h
   |
-  o  12 foo {} draft c_g
+  o  12 foo//bar {} draft c_g
   |
-  @  11 foo {} draft c_D
+  @  11 foo//bar {} draft c_D
   |
-  | *  8 foo {} draft c_f
+  | *  8 foo//bar {} draft c_f
   | |
-  | *  7 foo {} draft c_e
+  | *  7 foo//bar {} draft c_e
   | |
-  | x  6 foo {} draft c_d
+  | x  6 foo//bar {} draft c_d
   |/
-  o  2 foo {} draft c_c
+  o  2 foo//bar {} draft c_c
   |
-  o  1 other {} public c_b
+  o  1 double//slash {} public c_b
   |
-  o  0 other {} public c_a
+  o  0 double//slash {} public c_a
   
 
   $ hg stack
-  ### target: foo (branch) (2 heads)
+  ### target: foo//bar (branch) (2 heads)
   s6: c_h
   s5: c_g
   s2^ c_D (base current)
@@ -265,27 +265,27 @@
 ----------------------------------------------------------
 
   $ hg log --graph
-  o  13 foo {} draft c_h
+  o  13 foo//bar {} draft c_h
   |
-  o  12 foo {} draft c_g
+  o  12 foo//bar {} draft c_g
   |
-  @  11 foo {} draft c_D
+  @  11 foo//bar {} draft c_D
   |
-  | *  8 foo {} draft c_f
+  | *  8 foo//bar {} draft c_f
   | |
-  | *  7 foo {} draft c_e
+  | *  7 foo//bar {} draft c_e
   | |
-  | x  6 foo {} draft c_d
+  | x  6 foo//bar {} draft c_d
   |/
-  o  2 foo {} draft c_c
+  o  2 foo//bar {} draft c_c
   |
-  o  1 other {} public c_b
+  o  1 double//slash {} public c_b
   |
-  o  0 other {} public c_a
+  o  0 double//slash {} public c_a
   
 
   $ hg stack
-  ### target: foo (branch) (2 heads)
+  ### target: foo//bar (branch) (2 heads)
   s6: c_h
   s5: c_g
   s2^ c_D (base current)
@@ -296,7 +296,7 @@
   s0^ c_b (base)
   $ hg phase --public s1
   $ hg stack
-  ### target: foo (branch) (2 heads)
+  ### target: foo//bar (branch) (2 heads)
   s5: c_h
   s4: c_g
   s1^ c_D (base current)
@@ -311,7 +311,7 @@
   $ hg topic --rev s4::s5 sometopic
   changed topic on 2 changesets to "sometopic"
   $ hg stack
-  ### target: foo (branch)
+  ### target: foo//bar (branch)
   s3$ c_f (orphan)
   s2$ c_e (orphan)
   s1@ c_D (current)