# HG changeset patch # User Anton Shestakov # Date 1710439938 10800 # Node ID 3076dba01b86f25f7868f0518f35c3b955ecfec9 # Parent 50c0f1e2dfb67b2789ddfba5ac633ca614a31925# Parent cd22d260d8ccdbe6f8c55b103c8d9656014a157b branching: merge with stable diff -r 50c0f1e2dfb6 -r 3076dba01b86 hgext3rd/topic/__init__.py --- a/hgext3rd/topic/__init__.py Sun Mar 03 15:38:03 2024 -0300 +++ b/hgext3rd/topic/__init__.py Thu Mar 14 15:12:18 2024 -0300 @@ -179,6 +179,7 @@ hg, localrepo, lock as lockmod, + logcmdutil, merge, namespaces, node, @@ -700,31 +701,23 @@ self._topic_namespaces = namespaces return namespaces - @property - def currenttns(self): - tns = self.vfs.tryread(b'topic-namespace') + def wlock(self, wait=True): + wlock = super(topicrepo, self).wlock(wait=wait) # we should definitely drop this at some point, but it depends on # our own release schedule, not core's, so here's hg 1.0 # hg <= 1.0 (cfa08c88a5c4) - if tns == b'none': + if wlock is not None and wlock.held: try: - with self.wlock(wait=False): - try: - # we make sure the file contains what we expect - if self.vfs.read(b'topic-namespace') == b'none': - repo.vfs.unlinkpath(b'topic-namespace') - except IOError as err: - if err.errno != errno.ENOENT: - raise - except error.LockError: - # if we cannot acquire wdir lock, then we shouldn't do - # anything at all, since it'd be unsafe to modify wdir - pass - elif tns == b'': - # technically, if user creates an empty file, it should be - # handled differently than non-existing file, but the - # distinction is probably not that important - tns = b'none' + if self.vfs.read(b'topic-namespace') == b'none': + repo.vfs.unlinkpath(b'topic-namespace') + except IOError as err: + if err.errno != errno.ENOENT: + raise + return wlock + + @property + def currenttns(self): + tns = self.vfs.tryread(b'topic-namespace') or b'none' return encoding.tolocal(tns) @util.propertycache @@ -1843,6 +1836,68 @@ for tns in repo.topic_namespaces: ui.write(b'%s\n' % (tns,)) +@command(b'debug-default-topic-namespace', [ + (b'', b'none', True, b'find changesets with topic-namespace=none'), + (b'', b'default', False, b'find changesets with topic-namespace=default'), + (b'', b'clear', False, b'remove topic namespace from commit extras'), + ] + commands.formatteropts) +def debugdefaulttns(ui, repo, **opts): + """list changesets with the default topic namespace in commit extras""" + opts = pycompat.byteskwargs(opts) + condition = [] + if opts[b'none']: + condition += [b'extra("topic-namespace", "none")'] + if opts[b'default']: + condition += [b'extra("topic-namespace", "default")'] + if not condition: + condition = [b'none()'] + revs = repo.revs(b'not public() and not obsolete() and (%lr)', condition) + if opts[b'clear']: + with repo.wlock(), repo.lock(), repo.transaction(b'debug-default-topic-namespace'): + for rev in revs: + _clear_tns_extras(ui, repo, rev) + return + displayer = logcmdutil.changesetdisplayer(ui, repo, opts) + logcmdutil.displayrevs(ui, repo, revs, displayer, None) + +def _clear_tns_extras(ui, repo, rev): + ctx = repo[rev] + + if len(ctx.parents()) > 1: + # ctx.files() isn't reliable for merges, so fall back to the + # slower repo.status() method + st = ctx.p1().status(ctx) + files = set(st.modified) | set(st.added) | set(st.removed) + else: + files = set(ctx.files()) + + def filectxfn(repo, unused, path): + try: + return ctx[path] + except error.ManifestLookupError: + return None + + extra = ctx.extra().copy() + del extra[b'topic-namespace'] + + p1 = ctx.p1().node() + p2 = ctx.p2().node() + mc = context.memctx(repo, + (p1, p2), + ctx.description(), + files, + filectxfn, + user=ctx.user(), + date=ctx.date(), + extra=extra) + + overrides = {(b'phases', b'new-commit'): ctx.phase()} + with repo.ui.configoverride(overrides, b'debug-default-topic-namespace'): + newnode = repo.commitctx(mc) + + replacements = {(ctx.node(),): (newnode,)} + scmutil.cleanupnodes(repo, replacements, b'debug-default-topic-namespace') + @command(b'debug-parse-fqbn', commands.formatteropts, _(b'FQBN'), optionalrepo=True) def debugparsefqbn(ui, repo, fqbn, **opts): """parse branch//namespace/topic string into its components""" diff -r 50c0f1e2dfb6 -r 3076dba01b86 tests/test-namespaces.t --- a/tests/test-namespaces.t Sun Mar 03 15:38:03 2024 -0300 +++ b/tests/test-namespaces.t Thu Mar 14 15:12:18 2024 -0300 @@ -286,6 +286,60 @@ abort: topic namespace 'nonsense' does not exist [10] +Debug command related to the default/empty topic namespace + + $ hg debug-topic-namespace --clear + + $ echo none > none + $ hg ci -qAm 'tns=none' \ + > --config extensions.topic=! \ + > --config extensions.commitextras= \ + > --extra topic-namespace=none + + + $ echo default > default + $ hg ci -qAm 'tns=default' \ + > --config extensions.topic=! \ + > --config extensions.commitextras= \ + > --extra topic-namespace=default + + $ hg debug-default-topic-namespace \ + > --debug \ + > | grep extra + extra: branch=stable + extra: topic-namespace=none + + $ hg debug-default-topic-namespace \ + > --no-none \ + > --default \ + > --debug \ + > | grep extra + extra: branch=stable + extra: topic-namespace=default + + $ hg debug-default-topic-namespace \ + > --default \ + > -T '{rev}:{node|short} {join(extras, " ")}\n' + 4:29a2d0acd473 branch=stable topic-namespace=none + 5:16d6061fce0c branch=stable topic-namespace=default + + $ hg debug-default-topic-namespace --none --default --clear + 1 new orphan changesets + + $ hg debug-default-topic-namespace --none --default + + $ hg evolve --config extensions.evolve= --any + update:[7] tns=default + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + working directory is now at 68ef84f4b0a2 + move:[7] tns=default + atop:[6] tns=none + working directory is now at ddeaa72064d4 + + $ hg debug-default-topic-namespace --none --default + + $ hg verify --quiet + Parsing $ hg debugparsefqbn foo/bar//user26/feature -T '[{branch}] <{topic_namespace}> ({topic})\n'