--- a/CHANGELOG Sat Mar 21 15:04:18 2020 +0100
+++ b/CHANGELOG Tue Apr 07 19:33:40 2020 +0200
@@ -6,13 +6,17 @@
* compat: clean up old compatibility code
-9.3.0 - in progress
+9.3.1 - in progress
-------------------
* obsexchange: avoid sending too large request to http server
* obsdiscovery: server no longer aborts with a 500 error if client sends a
request without obscommon
* evolve: improved behavior when evolving above the result of a split
+ * topic: fix auto-publish=abort with server that auto-publishes bare branches
+ * evolve: checking for new head on push is no longer confused by mixed
+ branches(or topics)
+ * single-heads: ignore obsolete section when enforcing one head per branch
9.3.0 -- 2020-03-04
-------------------
--- a/hgext3rd/evolve/__init__.py Sat Mar 21 15:04:18 2020 +0100
+++ b/hgext3rd/evolve/__init__.py Tue Apr 07 19:33:40 2020 +0200
@@ -290,6 +290,7 @@
state,
evolvecmd,
exthelper,
+ headchecking,
metadata,
obscache,
obsexchange,
@@ -344,6 +345,7 @@
eh.merge(compat.eh)
eh.merge(cmdrewrite.eh)
eh.merge(rewind.eh)
+eh.merge(headchecking.eh)
uisetup = eh.finaluisetup
extsetup = eh.finalextsetup
reposetup = eh.finalreposetup
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hgext3rd/evolve/headchecking.py Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,189 @@
+from __future__ import absolute_import
+
+import functools
+
+from mercurial import (
+ discovery,
+ error,
+ extensions,
+ phases,
+ scmutil,
+ util,
+)
+
+from mercurial.i18n import _
+
+
+from . import (
+ compat,
+ exthelper,
+)
+
+eh = exthelper.exthelper()
+
+
+@eh.uisetup
+def uisetup(ui):
+ extensions.wrapfunction(discovery, '_postprocessobsolete', _postprocessobsolete)
+ extensions.wrapfunction(scmutil, 'enforcesinglehead', enforcesinglehead)
+
+def branchinfo(pushop, repo, node):
+ return repo[node].branch()
+
+# taken from 7d5455b988ec + branchinfo abstraction.
+def _postprocessobsolete(orig, pushop, futurecommon, candidate_newhs):
+ """post process the list of new heads with obsolescence information
+
+ Exists as a sub-function to contain the complexity and allow extensions to
+ experiment with smarter logic.
+
+ Returns (newheads, discarded_heads) tuple
+ """
+ pushingmarkerfor = discovery.pushingmarkerfor
+ # known issue
+ #
+ # * We "silently" skip processing on all changeset unknown locally
+ #
+ # * if <nh> is public on the remote, it won't be affected by obsolete
+ # marker and a new is created
+
+ # define various utilities and containers
+ repo = pushop.repo
+ unfi = repo.unfiltered()
+ torev = compat.getgetrev(unfi.changelog)
+ public = phases.public
+ getphase = unfi._phasecache.phase
+ ispublic = lambda r: getphase(unfi, r) == public
+ ispushed = lambda n: torev(n) in futurecommon
+ hasoutmarker = functools.partial(pushingmarkerfor, unfi.obsstore, ispushed)
+ successorsmarkers = unfi.obsstore.successors
+ newhs = set() # final set of new heads
+ discarded = set() # new head of fully replaced branch
+
+ localcandidate = set() # candidate heads known locally
+ unknownheads = set() # candidate heads unknown locally
+ for h in candidate_newhs:
+ if h in unfi:
+ localcandidate.add(h)
+ else:
+ if successorsmarkers.get(h) is not None:
+ msg = (
+ b'checkheads: remote head unknown locally has'
+ b' local marker: %s\n'
+ )
+ repo.ui.debug(msg % hex(h))
+ unknownheads.add(h)
+
+ # fast path the simple case
+ if len(localcandidate) == 1:
+ return unknownheads | set(candidate_newhs), set()
+
+ # actually process branch replacement
+ while localcandidate:
+ nh = localcandidate.pop()
+ current_branch = branchinfo(pushop, unfi, nh)
+ # run this check early to skip the evaluation of the whole branch
+ if ispublic(torev(nh)) or not unfi[nh].obsolete():
+ newhs.add(nh)
+ continue
+
+ # Get all revs/nodes on the branch exclusive to this head
+ # (already filtered heads are "ignored"))
+ branchrevs = unfi.revs(
+ b'only(%n, (%ln+%ln))', nh, localcandidate, newhs
+ )
+
+ branchnodes = []
+ for r in branchrevs:
+ ctx = unfi[r]
+ if ctx.branch() == current_branch:
+ branchnodes.append(ctx.node())
+
+ # The branch won't be hidden on the remote if
+ # * any part of it is public,
+ # * any part of it is considered part of the result by previous logic,
+ # * if we have no markers to push to obsolete it.
+ if (
+ any(ispublic(torev(n)) for n in branchnodes)
+ or (any(torev(n) in futurecommon and not unfi[n].obsolete() for n in branchnodes))
+ # XXX `hasoutmarker` does not guarantee the changeset to be
+ # obsolete, nor obsoleted by the push.
+ or any(not hasoutmarker(n) for n in branchnodes)
+ ):
+ newhs.add(nh)
+ else:
+ # note: there is a corner case if there is a merge in the branch.
+ # we might end up with -more- heads. However, these heads are not
+ # "added" by the push, but more by the "removal" on the remote so I
+ # think is a okay to ignore them,
+ discarded.add(nh)
+ newhs |= unknownheads
+ return newhs, discarded
+
+def _get_branch_name(ctx):
+ # make it easy for extension with the branch logic there
+ branch = ctx.branch()
+ if util.safehasattr(ctx, 'topic'):
+ topic = ctx.topic()
+ if topic:
+ branch = "%s:%s" % (branch, topic)
+ return branch
+
+def _filter_obsolete_heads(repo, heads):
+ """filter heads to return non-obsolete ones
+
+ Given a list of heads (on the same named branch) return a new list of heads
+ where the obsolete part have been skimmed out.
+ """
+ new_heads = []
+ old_heads = heads[:]
+ while old_heads:
+ rh = old_heads.pop()
+ ctx = repo[rh]
+ current_name = _get_branch_name(ctx)
+ # run this check early to skip the evaluation of the whole branch
+ if not ctx.obsolete():
+ new_heads.append(rh)
+ continue
+
+ # Get all revs/nodes on the branch exclusive to this head
+ # (already filtered heads are "ignored"))
+ sections_revs = repo.revs(
+ b'only(%d, (%ld+%ld))', rh, old_heads, new_heads,
+ )
+ keep_revs = []
+ for r in sections_revs:
+ ctx = repo[r]
+ if ctx.obsolete():
+ continue
+ if _get_branch_name(ctx) != current_name:
+ continue
+ keep_revs.append(r)
+ for h in repo.revs(b'heads(%ld and (::%ld))', sections_revs, keep_revs):
+ new_heads.append(h)
+ new_heads.sort()
+ return new_heads
+
+def enforcesinglehead(orig, repo, tr, desc, accountclosed=False):
+ """check that no named branch has multiple heads"""
+ nodesummaries = scmutil.nodesummaries
+ if desc in (b'strip', b'repair'):
+ # skip the logic during strip
+ return
+ visible = repo.filtered(b'visible')
+ # possible improvement: we could restrict the check to affected branch
+ bm = visible.branchmap()
+ cl = repo.changelog
+ to_rev = cl.rev
+ to_node = cl.node
+ for name in bm:
+ all_heads = bm.branchheads(name, closed=accountclosed)
+ all_heads = [to_rev(n) for n in all_heads]
+ heads = _filter_obsolete_heads(repo, all_heads)
+ heads = [to_node(r) for r in heads]
+ if len(heads) > 1:
+ msg = _(b'rejecting multiple heads on branch "%s"')
+ msg %= name
+ hint = _(b'%d heads: %s')
+ hint %= (len(heads), nodesummaries(repo, heads))
+ raise error.Abort(msg, hint=hint)
--- a/hgext3rd/evolve/stablerangecache.py Sat Mar 21 15:04:18 2020 +0100
+++ b/hgext3rd/evolve/stablerangecache.py Tue Apr 07 19:33:40 2020 +0200
@@ -48,7 +48,7 @@
storage backend for that cache neither.
This computation will finish in a finite amount of time, even for repositories
-with millions of revision and many merges. However it might take multiple tens
+with millions of revisions and many merges. However it might take multiple tens
of minutes to complete in such case.
In the future, better implementation of the algorithm in a more appropriate
--- a/hgext3rd/topic/__init__.py Sat Mar 21 15:04:18 2020 +0100
+++ b/hgext3rd/topic/__init__.py Tue Apr 07 19:33:40 2020 +0200
@@ -140,6 +140,7 @@
commands,
context,
error,
+ exchange,
extensions,
hg,
localrepo,
@@ -372,6 +373,8 @@
extensions.wrapfunction(context.workingctx, '__init__', wrapinit)
# Wrap changelog.add to drop empty topic
extensions.wrapfunction(changelog.changelog, 'add', wrapadd)
+ # Make exchange._checkpublish handle experimental.topic.publish-bare-branch
+ extensions.wrapfunction(exchange, '_checkpublish', flow.replacecheckpublish)
server.setupserver(ui)
@@ -393,6 +396,9 @@
def _restrictcapabilities(self, caps):
caps = super(topicrepo, self)._restrictcapabilities(caps)
caps.add(b'topics')
+ if self.ui.configbool(b'experimental',
+ b'topic.publish-bare-branch'):
+ caps.add(b'ext-topics-publish=auto')
return caps
def commit(self, *args, **kwargs):
--- a/hgext3rd/topic/discovery.py Sat Mar 21 15:04:18 2020 +0100
+++ b/hgext3rd/topic/discovery.py Tue Apr 07 19:33:40 2020 +0200
@@ -133,6 +133,46 @@
repo.__class__ = oldrepo
+def _get_branch_name(ctx):
+ # make it easy for extension with the branch logic there
+ return ctx.branch()
+
+
+def _filter_obsolete_heads(repo, heads):
+ """filter heads to return non-obsolete ones
+
+ Given a list of heads (on the same named branch) return a new list of heads
+ where the obsolete part have been skimmed out.
+ """
+ new_heads = []
+ old_heads = heads[:]
+ while old_heads:
+ rh = old_heads.pop()
+ ctx = repo[rh]
+ current_name = _get_branch_name(ctx)
+ # run this check early to skip the evaluation of the whole branch
+ if not ctx.obsolete():
+ new_heads.append(rh)
+ continue
+
+ # Get all revs/nodes on the branch exclusive to this head
+ # (already filtered heads are "ignored"))
+ sections_revs = repo.revs(
+ b'only(%d, (%ld+%ld))', rh, old_heads, new_heads,
+ )
+ keep_revs = []
+ for r in sections_revs:
+ ctx = repo[r]
+ if ctx.obsolete():
+ continue
+ if _get_branch_name(ctx) != current_name:
+ continue
+ keep_revs.append(r)
+ for h in repo.revs(b'heads(%ld and (::%ld))', sections_revs, keep_revs):
+ new_heads.append(h)
+ new_heads.sort()
+ return new_heads
+
# Discovery have deficiency around phases, branch can get new heads with pure
# phases change. This happened with a changeset was allowed to be pushed
# because it had a topic, but it later become public and create a new branch
@@ -144,7 +184,9 @@
for b in repo.branchmap().iterbranches():
if b':' in b[0]:
continue
- data[b[0]] = len(b[1])
+ oldheads = [repo[n].rev() for n in b[1]]
+ newheads = _filter_obsolete_heads(repo, oldheads)
+ data[b[0]] = len(newheads)
return data
def handlecheckheads(orig, op, inpart):
--- a/hgext3rd/topic/flow.py Sat Mar 21 15:04:18 2020 +0100
+++ b/hgext3rd/topic/flow.py Tue Apr 07 19:33:40 2020 +0200
@@ -118,3 +118,57 @@
extendpushoperation)
extensions.wrapfunction(exchange, '_pushdiscoveryphase', wrapphasediscovery)
exchange.pushdiscoverymapping[b'phase'] = exchange._pushdiscoveryphase
+
+def replacecheckpublish(orig, pushop):
+ listkeys = exchange.listkeys
+ repo = pushop.repo
+ ui = repo.ui
+ behavior = ui.config(b'experimental', b'auto-publish')
+ if pushop.publish or behavior not in (b'warn', b'confirm', b'abort'):
+ return
+
+ # possible modes are:
+ #
+ # none -> nothing is published on push
+ # all -> everything is published on push
+ # auto -> only changeset without topic are published on push
+ #
+ # Unknown mode is assumed "all" for safety.
+ #
+ # TODO: do a wider brain storming about mode names.
+
+ mode = b'all'
+ remotephases = listkeys(pushop.remote, b'phases')
+ if not remotephases.get(b'publishing', False):
+ mode = b'none'
+ for c in pushop.remote.capabilities():
+ if c.startswith(b'ext-topics-publish'):
+ mode = c.split(b'=', 1)[1]
+ break
+ if mode == b'none':
+ return
+
+ if pushop.revs is None:
+ published = repo.filtered(b'served').revs(b'not public()')
+ else:
+ published = repo.revs(b'::%ln - public()', pushop.revs)
+ if mode == b'auto':
+ published = repo.revs(b'%ld::(%ld - topic())', published, published)
+ if published:
+ if behavior == b'warn':
+ ui.warn(
+ _(b'%i changesets about to be published\n') % len(published)
+ )
+ elif behavior == b'confirm':
+ if ui.promptchoice(
+ _(b'push and publish %i changesets (yn)?$$ &Yes $$ &No')
+ % len(published)
+ ):
+ raise error.Abort(_(b'user quit'))
+ elif behavior == b'abort':
+ msg = _(b'push would publish %i changesets') % len(published)
+ hint = _(
+ b"use --publish or adjust 'experimental.auto-publish'"
+ b" config"
+ )
+ raise error.Abort(msg, hint=hint)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-mixed-branch-topic-G1.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,92 @@
+====================================
+Testing head checking code: Case E-1
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category F: case involving changeset on multiple topic
+TestCase 1: moving a branch to another location
+
+.. old-state:
+..
+.. * 1-changeset on branch default
+.. * 1-changeset on topic Z (above Y)
+..
+.. new-state:
+..
+.. * 1-changeset on branch default
+.. * 1-changeset on topic Z (rebased away from A0)
+..
+.. expected-result:
+..
+.. * push allowed
+..
+.. graph-summary:
+..
+.. B ø⇠◔ B' topic Z
+.. | |
+.. A ◔ | branch default
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic Z
+ marked working directory as topic: Z
+ $ mkcommit B0
+ active topic 'Z' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg topic Z
+ marked working directory as topic: Z
+ $ mkcommit B1
+ $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 845eeb768064 (draft)[Z]: B1
+ |
+ | x 35d2f30a8ba4 (draft)[Z]: B0
+ | |
+ | o 8aaa48160adc (draft): A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-mixed-branch-topic-G2.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,108 @@
+====================================
+Testing head checking code: Case E-2
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category F: case involving changeset on multiple branch
+TestCase 2: moving interleaved branch away from each other
+
+.. old-state:
+..
+.. * 2-changeset on branch default
+.. * 1-changeset on topic Z (between the two other)
+..
+.. new-state:
+..
+.. * 2-changeset on branch default, aligned
+.. * 1-changeset on topic Z (at the same location)
+..
+.. expected-result:
+..
+.. * push allowed
+..
+.. graph-summary:
+..
+.. C ø⇠◔ C' branch default
+.. | |
+.. B ◔ | topic Z
+.. | |
+.. A ø⇠◔ A' branch default
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic Z
+ marked working directory as topic: Z
+ $ mkcommit B0
+ active topic 'Z' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg topic --clear
+ $ mkcommit C0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ mkcommit A1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ mkcommit C1
+ $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 2 new orphan changesets
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 0c76bc104656 (draft): C1
+ |
+ o f6082bc4ffef (draft): A1
+ |
+ | x 44759c6d327d (draft): C0
+ | |
+ | * 35d2f30a8ba4 (draft)[Z]: B0
+ | |
+ | x 8aaa48160adc (draft): A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("C1")'
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ 2 new obsolescence markers
+ obsoleted 2 changesets
+ 1 new orphan changesets
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-mixed-branch-topic-G3.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,97 @@
+====================================
+Testing head checking code: Case E-3
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category E: case involving changeset on multiple branch
+TestCase 8: moving only part of the interleaved branch away, creating 2 heads
+
+.. old-state:
+..
+.. * 2-changeset on branch default
+.. * 1-changeset on topic Z (between the two other)
+..
+.. new-state:
+..
+.. * 2-changeset on branch default, on untouched, the other moved
+.. * 1-changeset on topic Z (at the same location)
+..
+.. expected-result:
+..
+.. * push rejected
+..
+.. graph-summary:
+..
+.. C ø⇠◔ C' branch default
+.. | |
+.. B ◔ | topic Z
+.. | |
+.. A ◔ | branch default
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic Z
+ marked working directory as topic: Z
+ $ mkcommit B0
+ active topic 'Z' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg topic --clear
+ $ mkcommit C0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ mkcommit C1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ dc44c53142f0 (draft): C1
+ |
+ | x 44759c6d327d (draft): C0
+ | |
+ | o 35d2f30a8ba4 (draft)[Z]: B0
+ | |
+ | o 8aaa48160adc (draft): A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("C1")'
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ abort: push creates new remote head dc44c53142f0!
+ (merge or see 'hg help push' for details about pushing new heads)
+ [255]
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-multi-topics-F1.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,98 @@
+====================================
+Testing head checking code: Case E-1
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category F: case involving changeset on multiple topic
+TestCase 1: moving a branch to another location
+
+.. old-state:
+..
+.. * 1-changeset on topic Y
+.. * 1-changeset on topic Z (above Y)
+..
+.. new-state:
+..
+.. * 1-changeset on topic Y
+.. * 1-changeset on topic Z (rebased away from A0)
+..
+.. expected-result:
+..
+.. * push allowed
+..
+.. graph-summary:
+..
+.. B ø⇠◔ B' topic Z
+.. | |
+.. A ◔ | topic Y
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic -r . Y
+ switching to topic Y
+ changed topic on 1 changesets to "Y"
+ $ hg topic Z
+ $ mkcommit B0
+ active topic 'Z' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 1 changes to 2 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg up 0
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg topic Z
+ marked working directory as topic: Z
+ $ mkcommit B1
+ $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 845eeb768064 (draft)[Z]: B1
+ |
+ | x e1494106e1ca (draft)[Z]: B0
+ | |
+ | o f5cd873e2965 (draft)[Y]: A0
+ |/
+ | x 8aaa48160adc (draft): A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-multi-topics-F2.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,112 @@
+====================================
+Testing head checking code: Case E-2
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category F: case involving changeset on multiple branch
+TestCase 2: moving interleaved branch away from each other
+
+.. old-state:
+..
+.. * 2-changeset on topic Y
+.. * 1-changeset on topic Z (between the two other)
+..
+.. new-state:
+..
+.. * 2-changeset on topic Y, aligned
+.. * 1-changeset on topic Z (at the same location)
+..
+.. expected-result:
+..
+.. * push allowed
+..
+.. graph-summary:
+..
+.. C ø⇠◔ C' topic Y
+.. | |
+.. B ◔ | topic Z
+.. | |
+.. A ø⇠◔ A' topic Y
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic -r . Y
+ switching to topic Y
+ changed topic on 1 changesets to "Y"
+ $ hg strip --config extensions.strip= --hidden 'hidden()' # clean old A0
+ saved backup bundle to $TESTTMP/E1/client/.hg/strip-backup/8aaa48160adc-19166392-backup.hg
+ $ hg topic Z
+ $ mkcommit B0
+ active topic 'Z' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg topic Y
+ $ mkcommit C0
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 2 changes to 3 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg up 0
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ hg topic Y
+ marked working directory as topic: Y
+ $ mkcommit A1
+ $ mkcommit C1
+ $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 2 new orphan changesets
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 0e26ba57d799 (draft)[Y]: C1
+ |
+ o fb4a34222909 (draft)[Y]: A1
+ |
+ | x 345721b128e8 (draft)[Y]: C0
+ | |
+ | * e1494106e1ca (draft)[Z]: B0
+ | |
+ | x f5cd873e2965 (draft)[Y]: A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("C1")'
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ 2 new obsolescence markers
+ obsoleted 2 changesets
+ 1 new orphan changesets
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-multi-topics-F3.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,101 @@
+====================================
+Testing head checking code: Case E-3
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category E: case involving changeset on multiple branch
+TestCase 8: moving only part of the interleaved branch away, creating 2 heads
+
+.. old-state:
+..
+.. * 2-changeset on topic Y
+.. * 1-changeset on topic Z (between the two other)
+..
+.. new-state:
+..
+.. * 2-changeset on topic Y, on untouched, the other moved
+.. * 1-changeset on topic Z (at the same location)
+..
+.. expected-result:
+..
+.. * push rejected
+..
+.. graph-summary:
+..
+.. C ø⇠◔ C' topic Y
+.. | |
+.. B ◔ | topic Z
+.. | |
+.. A ◔ | topic Y
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic -r . Y
+ switching to topic Y
+ changed topic on 1 changesets to "Y"
+ $ hg strip --config extensions.strip= --hidden 'hidden()' # clean old A0
+ saved backup bundle to $TESTTMP/E1/client/.hg/strip-backup/8aaa48160adc-19166392-backup.hg
+ $ hg topic Z
+ $ mkcommit B0
+ active topic 'Z' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg topic Y
+ $ mkcommit C0
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 2 changes to 3 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg up 0
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ hg topic Y
+ marked working directory as topic: Y
+ $ mkcommit C1
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 57530ca5eb24 (draft)[Y]: C1
+ |
+ | x 345721b128e8 (draft)[Y]: C0
+ | |
+ | o e1494106e1ca (draft)[Z]: B0
+ | |
+ | o f5cd873e2965 (draft)[Y]: A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("C1")'
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ abort: push creates new remote head 57530ca5eb24 on branch 'default:Y'!
+ (merge or see 'hg help push' for details about pushing new heads)
+ [255]
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-multibranches-E1.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,93 @@
+====================================
+Testing head checking code: Case E-1
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category E: case involving changeset on multiple branch
+TestCase 8: moving a branch to another location
+
+.. old-state:
+..
+.. * 1-changeset on branch default
+.. * 1-changeset on branch Z (above default)
+..
+.. new-state:
+..
+.. * 1-changeset on branch default
+.. * 1-changeset on branch Z (rebased away from A0)
+..
+.. expected-result:
+..
+.. * push allowed
+..
+.. graph-summary:
+..
+.. B ø⇠◔ B'
+.. | |
+.. A ◔ |
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg branch Z
+ marked working directory as branch Z
+ (branches are permanent and global, did you want a bookmark?)
+ $ mkcommit B0
+ $ hg push --new-branch
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg branch --force Z
+ marked working directory as branch Z
+ $ mkcommit B1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ c98b855401e7 (draft): B1
+ |
+ | x 93e5c1321ece (draft): B0
+ | |
+ | o 8aaa48160adc (draft): A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-multibranches-E2.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,108 @@
+====================================
+Testing head checking code: Case E-2
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category E: case involving changeset on multiple branch
+TestCase 8: moving interleaved branch away from each other
+
+.. old-state:
+..
+.. * 2-changeset on branch default
+.. * 1-changeset on branch Z (between the two other)
+..
+.. new-state:
+..
+.. * 2-changeset on branch default, aligned
+.. * 1-changeset on branch Z (at the same location)
+..
+.. expected-result:
+..
+.. * push allowed
+..
+.. graph-summary:
+..
+.. C ø⇠◔ C'
+.. | |
+.. B ◔ |
+.. | |
+.. A ø⇠◔ A'
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg branch Z
+ marked working directory as branch Z
+ (branches are permanent and global, did you want a bookmark?)
+ $ mkcommit B0
+ $ hg branch default --force
+ marked working directory as branch default
+ $ mkcommit C0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg push --new-branch
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ mkcommit A1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ mkcommit C1
+ $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 2 new orphan changesets
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 0c76bc104656 (draft): C1
+ |
+ o f6082bc4ffef (draft): A1
+ |
+ | x afc55ba2ce61 (draft): C0
+ | |
+ | * 93e5c1321ece (draft): B0
+ | |
+ | x 8aaa48160adc (draft): A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("C1")'
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ 2 new obsolescence markers
+ obsoleted 2 changesets
+ 1 new orphan changesets
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-push-checkheads-multibranches-E3.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,97 @@
+====================================
+Testing head checking code: Case E-3
+====================================
+
+Mercurial checks for the introduction of new heads on push. Evolution comes
+into play to detect if existing branches on the server are being replaced by
+some of the new one we push.
+
+This case is part of a series of tests checking this behavior.
+
+Category E: case involving changeset on multiple branch
+TestCase 8: moving only part of the interleaved branch away, creating 2 heads
+
+.. old-state:
+..
+.. * 2-changeset on branch default
+.. * 1-changeset on branch Z (between the two other)
+..
+.. new-state:
+..
+.. * 2-changeset on branch default, on untouched, the other moved
+.. * 1-changeset on branch Z (at the same location)
+..
+.. expected-result:
+..
+.. * push rejected
+..
+.. graph-summary:
+..
+.. C ø⇠◔ C'
+.. | |
+.. B ◔ |
+.. | |
+.. A ◔ |
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir E1
+ $ cd E1
+ $ setuprepos
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg branch Z
+ marked working directory as branch Z
+ (branches are permanent and global, did you want a bookmark?)
+ $ mkcommit B0
+ $ hg branch default --force
+ marked working directory as branch default
+ $ mkcommit C0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg push --new-branch
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ mkcommit C1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ dc44c53142f0 (draft): C1
+ |
+ | x afc55ba2ce61 (draft): C0
+ | |
+ | o 93e5c1321ece (draft): B0
+ | |
+ | o 8aaa48160adc (draft): A0
+ |/
+ o 1e4be0697311 (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("C1")'
+ pushing to $TESTTMP/E1/server
+ searching for changes
+ abort: push creates new remote head dc44c53142f0!
+ (merge or see 'hg help push' for details about pushing new heads)
+ [255]
+
+ $ cd ../..
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-named-branch-A1.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,108 @@
+=========================================
+Testing single head enforcement: Case A-1
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category A: Involving obsolescence
+TestCase 1: A fully obsolete branch kept visible by another one.
+
+.. old-state:
+..
+.. * 2 changeset changeset on branch default
+.. * 2 changeset changeset on branch Z on top of them.
+..
+.. new-state:
+..
+.. * 2 changeset changeset on branch Z at the same location
+.. * 2 changeset changeset on branch default superceeding the other ones
+..
+.. expected-result:
+..
+.. * only one head detected
+..
+.. graph-summary:
+..
+.. D ● (branch Z)
+.. |
+.. C ● (branch Z)
+.. |
+.. B ø⇠◔ B'
+.. | |
+.. A ø⇠◔ A'
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir A1
+ $ cd A1
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ mkcommit B0
+ $ hg branch Z
+ marked working directory as branch Z
+ (branches are permanent and global, did you want a bookmark?)
+ $ mkcommit C0
+ $ mkcommit D0
+ $ hg push --new-branch
+ pushing to $TESTTMP/A1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ mkcommit A1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ mkcommit B1
+ $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 3 new orphan changesets
+ $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 262c8c798096 [default] (draft): B1
+ |
+ o f6082bc4ffef [default] (draft): A1
+ |
+ | * cdf1dbb37a67 [Z] (draft): D0
+ | |
+ | * 3213e3e16c67 [Z] (draft): C0
+ | |
+ | x d73caddc5533 [default] (draft): B0
+ | |
+ | x 8aaa48160adc [default] (draft): A0
+ |/
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("B1")'
+ pushing to $TESTTMP/A1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ 2 new obsolescence markers
+ obsoleted 2 changesets
+ 2 new orphan changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-named-branch-A2.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,107 @@
+
+=========================================
+Testing single head enforcement: Case A-2
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category A: Involving obsolescence
+TestCase 2: A branch is split in two, effectively creating two heads
+
+.. old-state:
+..
+.. * 2 changeset changeset on branch default
+.. * 2 changeset changeset on branch Z on top of them.
+..
+.. new-state:
+..
+.. * 2 changeset changeset on branch Z at the same location
+.. * 1 changeset changeset on branch default unchanged
+.. * 1 changeset changeset on branch default superceeding the other ones
+..
+.. expected-result:
+..
+.. * two heads detected
+..
+.. graph-summary:
+..
+.. D ● (branch Z)
+.. |
+.. C ● (branch Z)
+.. |
+.. B ø⇠◔ B'
+.. | |
+.. A ● |
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir A2
+ $ cd A2
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ mkcommit B0
+ $ hg branch Z
+ marked working directory as branch Z
+ (branches are permanent and global, did you want a bookmark?)
+ $ mkcommit C0
+ $ mkcommit D0
+ $ hg push --new-branch
+ pushing to $TESTTMP/A2/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ mkcommit B1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 2 new orphan changesets
+ $ hg log -G --hidden
+ @ 25c56d33e4c4 [default] (draft): B1
+ |
+ | * cdf1dbb37a67 [Z] (draft): D0
+ | |
+ | * 3213e3e16c67 [Z] (draft): C0
+ | |
+ | x d73caddc5533 [default] (draft): B0
+ | |
+ | o 8aaa48160adc [default] (draft): A0
+ |/
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+(force push to make sure we get the changeset on the remote)
+
+ $ hg push -r 'desc("B1")' --force
+ pushing to $TESTTMP/A2/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ transaction abort!
+ rollback completed
+ abort: rejecting multiple heads on branch "default"
+ (2 heads: 8aaa48160adc 25c56d33e4c4)
+ [255]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-named-branch-A3.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,116 @@
+=========================================
+Testing single head enforcement: Case A-3
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category A: Involving obsolescence
+TestCase 3: Full superceedig of a branch interleaved with another
+
+.. old-state:
+..
+.. * 2 changeset changeset on branch default
+.. * 2 changeset changeset on branch Z interleaved with the other
+..
+.. new-state:
+..
+.. * 2 changeset changeset on branch Z at the same location
+.. * 2 changeset changeset on branch default superceeding the other ones
+..
+.. expected-result:
+..
+.. * only one head detected
+..
+.. graph-summary:
+..
+.. D ● (branch Z)
+.. |
+.. C ø⇠◔ C'
+.. | |
+.. B ● | (branch Z)
+.. | |
+.. A ø⇠◔ A'
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir A3
+ $ cd A3
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg branch Z
+ marked working directory as branch Z
+ (branches are permanent and global, did you want a bookmark?)
+ $ mkcommit B0
+ $ hg branch default --force
+ marked working directory as branch default
+ $ mkcommit C0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg branch Z --force
+ marked working directory as branch Z
+ $ mkcommit D0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg push --new-branch
+ pushing to $TESTTMP/A3/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ mkcommit A1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ mkcommit C1
+ $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 3 new orphan changesets
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 0c76bc104656 [default] (draft): C1
+ |
+ o f6082bc4ffef [default] (draft): A1
+ |
+ | * 78578c4306ce [Z] (draft): D0
+ | |
+ | x afc55ba2ce61 [default] (draft): C0
+ | |
+ | * 93e5c1321ece [Z] (draft): B0
+ | |
+ | x 8aaa48160adc [default] (draft): A0
+ |/
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("C1")'
+ pushing to $TESTTMP/A3/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ 2 new obsolescence markers
+ obsoleted 2 changesets
+ 2 new orphan changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-named-branch-A4.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,113 @@
+=========================================
+Testing single head enforcement: Case A-4
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category A: Involving obsolescence
+TestCase 4: Partial rewrite of a branch to dis-interleave it
+
+.. old-state:
+..
+.. * 2 changeset changeset on branch default
+.. * 2 changeset changeset on branch Z interleaved with the other one
+..
+.. new-state:
+..
+.. * 2 changeset changeset on branch Z at the same location
+.. * 1 changeset on default untouched (the lower one)
+.. * 1 changeset on default moved on the other one
+..
+.. expected-result:
+..
+.. * only one head detected
+..
+.. graph-summary:
+..
+.. D ● (branch Z)
+.. |
+.. C ø⇠◔ C'
+.. | |
+.. B ● | (branch Z)
+.. |/
+.. A ●
+.. |
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir A4
+ $ cd A4
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg branch Z
+ marked working directory as branch Z
+ (branches are permanent and global, did you want a bookmark?)
+ $ mkcommit B0
+ $ hg branch default --force
+ marked working directory as branch default
+ $ mkcommit C0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg branch Z --force
+ marked working directory as branch Z
+ $ mkcommit D0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg push --new-branch
+ pushing to $TESTTMP/A4/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files
+ $ hg up 'desc("A0")'
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ mkcommit C1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 1 new orphan changesets
+ $ hg log -G --hidden
+ @ cfe9ed94fa4a [default] (draft): C1
+ |
+ | * 78578c4306ce [Z] (draft): D0
+ | |
+ | x afc55ba2ce61 [default] (draft): C0
+ | |
+ | o 93e5c1321ece [Z] (draft): B0
+ |/
+ o 8aaa48160adc [default] (draft): A0
+ |
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+(force push to make sure we get the changeset on the remote)
+
+ $ hg push -r 'desc("C1")' --force
+ pushing to $TESTTMP/A4/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 1 new orphan changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-named-branch-A5.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,113 @@
+=========================================
+Testing single head enforcement: Case A-1
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category A: Involving obsolescence
+TestCase 1: obsoleting a merge reveal two heads
+
+.. old-state:
+..
+.. * 3 changeset changeset on branch default (2 on their own branch + 1 merge)
+.. * 1 changeset on branch Z (children of the merge)
+..
+.. new-state:
+..
+.. * 2 changeset changeset on branch default (merge is obsolete) each a head
+.. * 1 changeset on branch Z keeping the merge visible
+..
+.. expected-result:
+..
+.. * 2 heads detected (because we skip the merge).
+..
+.. graph-summary:
+..
+.. D ● (branch Z)
+.. |
+.. C ● (branch Z)
+.. |
+.. M ⊗
+.. |\
+.. A ● ● B
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir A5
+ $ cd A5
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg up 0
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ mkcommit B0
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ hg merge
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m 'M0'
+ $ hg branch Z
+ marked working directory as branch Z
+ (branches are permanent and global, did you want a bookmark?)
+ $ mkcommit C0
+ $ hg push --new-branch
+ pushing to $TESTTMP/A5/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 2 changes to 2 files
+ $ hg up 0
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ mkcommit A1
+ created new head
+ (consider using topic for lightweight branches. See 'hg help topic')
+ $ mkcommit B1
+ $ hg debugobsolete `getid "desc(M0)"` --record-parents
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 1 new orphan changesets
+ $ hg log -G --hidden
+ @ 262c8c798096 [default] (draft): B1
+ |
+ o f6082bc4ffef [default] (draft): A1
+ |
+ | * 61c95483cc12 [Z] (draft): C0
+ | |
+ | x 14d3d4d41d1a [default] (draft): M0
+ | |\
+ +---o 74ff5441d343 [default] (draft): B0
+ | |
+ | o 8aaa48160adc [default] (draft): A0
+ |/
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+(force push to make sure we get the changeset on the remote)
+
+ $ hg push -r 'desc("C0")' --force
+ pushing to $TESTTMP/A5/server
+ searching for changes
+ no changes found
+ transaction abort!
+ rollback completed
+ abort: rejecting multiple heads on branch "default"
+ (2 heads: 8aaa48160adc 74ff5441d343)
+ [255]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-topic-B1.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,114 @@
+=========================================
+Testing single head enforcement: Case A-1
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category B: Involving obsolescence and topic
+TestCase 1: A fully obsolete topic kept visible by another one.
+
+.. old-state:
+..
+.. * 2 changeset changeset on topic X
+.. * 2 changeset changeset on topic Y on top of them.
+..
+.. new-state:
+..
+.. * 2 changeset changeset on topic Y at the same location
+.. * 2 changeset changeset on topic X superceeding the other ones
+..
+.. expected-result:
+..
+.. * only one head detected
+..
+.. graph-summary:
+..
+.. D ● (topic-Y)
+.. |
+.. C ● (topic-Y)
+.. |
+.. B ø⇠◔ B' (topic-X)
+.. | |
+.. A ø⇠◔ A' (topic-X)
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir B1
+ $ cd B1
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic -r . topic-X
+ switching to topic topic-X
+ changed topic on 1 changesets to "topic-X"
+ $ hg strip --config extensions.strip= --hidden 'hidden()' --no-backup # clean old A0
+ $ mkcommit B0
+ $ hg topic topic-Y
+ $ mkcommit C0
+ active topic 'topic-Y' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ mkcommit D0
+ $ hg push --new-branch
+ pushing to $TESTTMP/B1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 4 changesets with 3 changes to 4 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg up 0
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ hg topic topic-X
+ marked working directory as topic: topic-X
+ $ mkcommit A1
+ $ mkcommit B1
+ $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 3 new orphan changesets
+ $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ f4ed6717fb66 [default:topic-X] (draft): B1
+ |
+ o c1340bef453e [default:topic-X] (draft): A1
+ |
+ | * 618812b710f7 [default:topic-Y] (draft): D0
+ | |
+ | * d1ad53773db2 [default:topic-Y] (draft): C0
+ | |
+ | x 1c1f62b56685 [default:topic-X] (draft): B0
+ | |
+ | x 5a47a98cd8e5 [default:topic-X] (draft): A0
+ |/
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("B1")'
+ pushing to $TESTTMP/B1/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ 2 new obsolescence markers
+ obsoleted 2 changesets
+ 2 new orphan changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-topic-B2.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,115 @@
+
+=========================================
+Testing single head enforcement: Case A-2
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category B: Involving obsolescence with topic
+TestCase 2: A branch is split in two, effectively creating two heads
+
+.. old-state:
+..
+.. * 2 changeset changeset on topic X
+.. * 2 changeset changeset on topic Y on top of them.
+..
+.. new-state:
+..
+.. * 2 changeset changeset on topic Y at the same location
+.. * 1 changeset changeset on topic X unchanged
+.. * 1 changeset changeset on topic X superceeding the other ones
+..
+.. expected-result:
+..
+.. * two heads detected
+..
+.. graph-summary:
+..
+.. D ● (topic-Y)
+.. |
+.. C ● (topic-Y)
+.. |
+.. B ø⇠◔ B' (topic-X)
+.. | |
+.. A ● | (topic-X)
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir B2
+ $ cd B2
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic -r . topic-X
+ switching to topic topic-X
+ changed topic on 1 changesets to "topic-X"
+ $ hg strip --config extensions.strip= --hidden 'hidden()' --no-backup # clean old A0
+ $ mkcommit B0
+ $ hg branch Z
+ marked working directory as branch Z
+ $ hg topic topic-Y
+ $ mkcommit C0
+ active topic 'topic-Y' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ mkcommit D0
+ $ hg push --new-branch
+ pushing to $TESTTMP/B2/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 4 changesets with 3 changes to 4 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg up 0
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ hg topic topic-X
+ marked working directory as topic: topic-X
+ $ mkcommit B1
+ $ hg debugobsolete `getid "desc(B0)" ` `getid "desc(B1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 2 new orphan changesets
+ $ hg log -G --hidden
+ @ 5a4735b75167 [default:topic-X] (draft): B1
+ |
+ | * 02490b2dd1c5 [Z:topic-Y] (draft): D0
+ | |
+ | * 447ad8382abc [Z:topic-Y] (draft): C0
+ | |
+ | x 1c1f62b56685 [default:topic-X] (draft): B0
+ | |
+ | o 5a47a98cd8e5 [default:topic-X] (draft): A0
+ |/
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+(force push to make sure we get the changeset on the remote)
+
+ $ hg push -r 'desc("B1")' --force
+ pushing to $TESTTMP/B2/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ transaction abort!
+ rollback completed
+ abort: rejecting multiple heads on branch "default:topic-X"
+ (2 heads: 5a47a98cd8e5 5a4735b75167)
+ [255]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-topic-B3.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,116 @@
+=========================================
+Testing single head enforcement: Case A-3
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category B: Involving obsolescence with topic
+TestCase 3: Full superceedig of a branch interleaved with another
+
+.. old-state:
+..
+.. * 2 changeset changeset on topic Y
+.. * 2 changeset changeset on topic X interleaved with the other
+..
+.. new-state:
+..
+.. * 2 changeset changeset on topic X at the same location
+.. * 2 changeset changeset on topic Y superceeding the other ones
+..
+.. expected-result:
+..
+.. * only one head detected
+..
+.. graph-summary:
+..
+.. D ● (topic-Y)
+.. |
+.. C ø⇠◔ C' (topix-X)
+.. | |
+.. B ● | (topic-Y)
+.. | |
+.. A ø⇠◔ A' (topic-X)
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir B3
+ $ cd B3
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic -r . topic-X
+ switching to topic topic-X
+ changed topic on 1 changesets to "topic-X"
+ $ hg strip --config extensions.strip= --hidden 'hidden()' --no-backup # clean old A0
+ $ hg topic topic-Y
+ $ mkcommit B0
+ active topic 'topic-Y' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg topic topic-X
+ $ mkcommit C0
+ $ hg topic topic-Y
+ $ mkcommit D0
+ $ hg push --new-branch
+ pushing to $TESTTMP/B3/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 4 changesets with 3 changes to 4 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg up 0
+ 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+ $ hg topic topic-X
+ marked working directory as topic: topic-X
+ $ mkcommit A1
+ $ mkcommit C1
+ $ hg debugobsolete `getid "desc(A0)" ` `getid "desc(A1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 3 new orphan changesets
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg log -G --hidden
+ @ 9f6e6381b9aa [default:topic-X] (draft): C1
+ |
+ o c1340bef453e [default:topic-X] (draft): A1
+ |
+ | * 850d57e10bfe [default:topic-Y] (draft): D0
+ | |
+ | x fcdd583577e8 [default:topic-X] (draft): C0
+ | |
+ | * 030eec7a0fe2 [default:topic-Y] (draft): B0
+ | |
+ | x 5a47a98cd8e5 [default:topic-X] (draft): A0
+ |/
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+ $ hg push -r 'desc("C1")'
+ pushing to $TESTTMP/B3/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ 2 new obsolescence markers
+ obsoleted 2 changesets
+ 2 new orphan changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-topic-B4.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,113 @@
+=========================================
+Testing single head enforcement: Case A-4
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category A: Involving obsolescence
+TestCase 4: Partial rewrite of a branch to dis-interleave it
+
+.. old-state:
+..
+.. * 2 changeset changeset on topic X
+.. * 2 changeset changeset on topic Y interleaved with the other one
+..
+.. new-state:
+..
+.. * 2 changeset changeset on topic Y at the same location
+.. * 1 changeset on topic X untouched (the lower one)
+.. * 1 changeset on topic X moved on the other one
+..
+.. expected-result:
+..
+.. * only one head detected
+..
+.. graph-summary:
+..
+.. D ● (topic-Y)
+.. |
+.. C ø⇠◔ C' (topic-X)
+.. | |
+.. B ● | (topic-Y)
+.. |/
+.. A ● (topic-X)
+.. |
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir B4
+ $ cd B4
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic -r . topic-X
+ switching to topic topic-X
+ changed topic on 1 changesets to "topic-X"
+ $ hg strip --config extensions.strip= --hidden 'hidden()' --no-backup # clean old A0
+ $ hg topic topic-Y
+ $ mkcommit B0
+ active topic 'topic-Y' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg topic topic-X
+ $ mkcommit C0
+ $ hg topic topic-Y
+ $ mkcommit D0
+ $ hg push --new-branch
+ pushing to $TESTTMP/B4/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 4 changesets with 3 changes to 4 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg up 'desc("A0")'
+ switching to topic topic-X
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ hg topic topic-X
+ $ mkcommit C1
+ $ hg debugobsolete `getid "desc(C0)" ` `getid "desc(C1)"`
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 1 new orphan changesets
+ $ hg log -G --hidden
+ @ b98a8bd4ca39 [default:topic-X] (draft): C1
+ |
+ | * 850d57e10bfe [default:topic-Y] (draft): D0
+ | |
+ | x fcdd583577e8 [default:topic-X] (draft): C0
+ | |
+ | o 030eec7a0fe2 [default:topic-Y] (draft): B0
+ |/
+ o 5a47a98cd8e5 [default:topic-X] (draft): A0
+ |
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+(force push to make sure we get the changeset on the remote)
+
+ $ hg push -r 'desc("C1")' --force
+ pushing to $TESTTMP/B4/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 1 new orphan changesets
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-single-head-obsolescence-topic-B5.t Tue Apr 07 19:33:40 2020 +0200
@@ -0,0 +1,117 @@
+=========================================
+Testing single head enforcement: Case A-1
+=========================================
+
+A repository is set to only accept a single head per name (typically named
+branch). However, obsolete changesets can make this enforcement more
+complicated, because they can be kept visible by other changeset on other
+branch.
+
+This case is part of a series of tests checking this behavior.
+
+Category A: Involving obsolescence
+TestCase 1: obsoleting a merge reveal two heads
+
+.. old-state:
+..
+.. * 3 changeset changeset on topic X (2 on their own branch + 1 merge)
+.. * 1 changeset on topic Y (children of the merge)
+..
+.. new-state:
+..
+.. * 2 changeset changeset on topic X (merge is obsolete) each a head
+.. * 1 changeset on topic Y keeping the merge visible
+..
+.. expected-result:
+..
+.. * 2 heads detected (because we skip the merge).
+..
+.. graph-summary:
+..
+.. D ● (topic-Y)
+.. |
+.. C ● (topic-Y)
+.. |
+.. M ⊗ (topic-X)
+.. |\
+.. A ● ● B (topic-X)
+.. |/
+.. ●
+
+ $ . $TESTDIR/testlib/topic_setup.sh
+ $ . $TESTDIR/testlib/push-checkheads-util.sh
+
+Test setup
+----------
+
+ $ mkdir B5
+ $ cd B5
+ $ setuprepos single-head
+ creating basic server and client repo
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ cd client
+ $ hg topic -r . topic-X
+ switching to topic topic-X
+ changed topic on 1 changesets to "topic-X"
+ $ hg strip --config extensions.strip= --hidden 'hidden()' --no-backup # clean old A0
+ $ hg up 0
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg topic topic-X
+ marked working directory as topic: topic-X
+ $ mkcommit B0
+ $ hg merge
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -m 'M0'
+ $ hg topic topic-Y
+ $ mkcommit C0
+ active topic 'topic-Y' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg push --new-branch
+ pushing to $TESTTMP/B5/server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 4 changesets with 2 changes to 3 files (+1 heads)
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ $ hg up 0
+ 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+ $ mkcommit A1
+ $ mkcommit B1
+ $ hg debugobsolete `getid "desc(M0)"` --record-parents
+ 1 new obsolescence markers
+ obsoleted 1 changesets
+ 1 new orphan changesets
+ $ hg log -G --hidden
+ @ 262c8c798096 [default] (draft): B1
+ |
+ o f6082bc4ffef [default] (draft): A1
+ |
+ | * 339fd31549ed [default:topic-Y] (draft): C0
+ | |
+ | x 33b3d4185449 [default:topic-X] (draft): M0
+ | |\
+ +---o d3826ff42cf7 [default:topic-X] (draft): B0
+ | |
+ | o 5a47a98cd8e5 [default:topic-X] (draft): A0
+ |/
+ o 1e4be0697311 [default] (public): root
+
+
+Actual testing
+--------------
+
+(force push to make sure we get the changeset on the remote)
+
+ $ hg push -r 'desc("C0")' --force
+ pushing to $TESTTMP/B5/server
+ searching for changes
+ no changes found
+ transaction abort!
+ rollback completed
+ abort: rejecting multiple heads on branch "default:topic-X"
+ (2 heads: 5a47a98cd8e5 d3826ff42cf7)
+ [255]
--- a/tests/test-topic-flow-publish-bare.t Sat Mar 21 15:04:18 2020 +0100
+++ b/tests/test-topic-flow-publish-bare.t Tue Apr 07 19:33:40 2020 +0200
@@ -321,3 +321,43 @@
$ hg phase --public -r 10 --config experimental.topic.allow-publish=no
abort: rejecting publishing of changeset 858be9a8daaf and 1 others
[255]
+
+Checking the option to prevent automatic publishing
+===================================================
+
+ $ hg up branchA
+ 2 files updated, 0 files merged, 5 files removed, 0 files unresolved
+
+Trying to push changeset without topic (would publish them)
+
+ $ mkcommit c_aM0
+ $ hg debugcapabilities $TESTTMP/bare-branch-server | grep topics
+ ext-topics-publish=auto
+ topics
+ $ hg push --config experimental.auto-publish=abort -r .
+ pushing to $TESTTMP/bare-branch-server
+ abort: push would publish 1 changesets
+ (use --publish or adjust 'experimental.auto-publish' config)
+ [255]
+ $ hg push --config experimental.auto-publish=abort -r . --publish
+ pushing to $TESTTMP/bare-branch-server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+
+Pushing a changeset with topic (not publishing, no warning)
+
+ $ hg topic test-push-protection
+ marked working directory as topic: test-push-protection
+ $ mkcommit c_aL0
+ active topic 'test-push-protection' grew its first changeset
+ (see 'hg help topics' for more information)
+ $ hg push --config experimental.auto-publish=abort -r .
+ pushing to $TESTTMP/bare-branch-server
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
--- a/tests/testlib/push-checkheads-util.sh Sat Mar 21 15:04:18 2020 +0100
+++ b/tests/testlib/push-checkheads-util.sh Tue Apr 07 19:33:40 2020 +0200
@@ -5,7 +5,7 @@
cat >> $HGRCPATH <<EOF
[ui]
# simpler log output
-logtemplate ="{node|short} ({phase}): {desc}\n"
+logtemplate ="{node|short} ({phase}){if(topic, "[{topic}]")}: {desc}\n"
[phases]
# non publishing server
@@ -26,4 +26,14 @@
mkcommit A0
cd ..
hg clone server client
+
+ if [ "$1" = "single-head" ]; then
+ echo >> "server/.hg/hgrc" "[experimental]"
+ echo >> "server/.hg/hgrc" "# enforce a single name per branch"
+ echo >> "server/.hg/hgrc" "single-head-per-branch = yes"
+
+ echo >> "client/.hg/hgrc" "[ui]"
+ echo >> "client/.hg/hgrc" "# simpler log output"
+ echo >> "client/.hg/hgrc" 'logtemplate = "{node|short} [{branch}{if(topic, ":{topic}")}] ({phase}): {desc}\\n"'
+ fi
}