# HG changeset patch # User Pierre-Yves David # Date 1509387854 -3600 # Node ID f286eefbd20d0c3ff5b8ff99fd4d2133fe70f231 # Parent 31493a1b0e3986cb02fa3d65c55a2d3bdee1e4aa topic: add an option to enforce a single head per name in a repository diff -r 31493a1b0e39 -r f286eefbd20d CHANGELOG --- a/CHANGELOG Sun Oct 15 00:07:21 2017 +0530 +++ b/CHANGELOG Mon Oct 30 19:24:14 2017 +0100 @@ -10,6 +10,11 @@ * evolve: rename '--contentdivergent' flag to '--content-divergent' * evolve: rename '--phasedivergent' flag to '--phase-divergent' +topic + + * add an experimental flag to enforce one head per name policy, + (off by default, see 'hg help -e topic' for details) + 6.8.0 -- 2017-10-23 ------------------- diff -r 31493a1b0e39 -r f286eefbd20d hgext3rd/topic/__init__.py --- a/hgext3rd/topic/__init__.py Sun Oct 15 00:07:21 2017 +0530 +++ b/hgext3rd/topic/__init__.py Mon Oct 30 19:24:14 2017 +0100 @@ -63,6 +63,15 @@ topic-mode = enforce-all # abort the commit (even for merge) topic-mode = random # use a randomized generated topic (except for merge) topic-mode = random-all # use a randomized generated topic (even for merge) + +Single head enforcing +===================== + +The extensions come with an option to enforce that there is only one heads for +each name in the repository at any time. + + [experimental] + enforce-single-head = yes """ from __future__ import absolute_import @@ -99,6 +108,7 @@ from . import ( compat, constants, + flow, revset as topicrevset, destination, stack, @@ -152,6 +162,9 @@ configitem('experimental', 'enforce-topic', default=False, ) + configitem('experimental', 'enforce-single-head', + default=False, + ) configitem('experimental', 'topic-mode', default=None, ) @@ -368,6 +381,16 @@ if desc in ('strip', 'repair') or ctr is not None: return tr + if repo.ui.configbool('experimental', 'enforce-single-head'): + reporef = weakref.ref(self) + origvalidator = tr.validator + + def validator(tr2): + repo = reporef() + flow.enforcesinglehead(repo, tr2) + origvalidator(tr2) + tr.validator = validator + # real transaction start ct = self.currenttopic if not ct: diff -r 31493a1b0e39 -r f286eefbd20d hgext3rd/topic/flow.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hgext3rd/topic/flow.py Mon Oct 30 19:24:14 2017 +0100 @@ -0,0 +1,15 @@ +from __future__ import absolute_import + +from mercurial import ( + error, + node, +) + +from mercurial.i18n import _ + +def enforcesinglehead(repo, tr): + for name, heads in repo.filtered('visible').branchmap().iteritems(): + if len(heads) > 1: + hexs = [node.short(n) for n in heads] + raise error.Abort(_('%d heads on "%s"') % (len(heads), name), + hint=(', '.join(hexs))) diff -r 31493a1b0e39 -r f286eefbd20d tests/test-topic-flow-single-head.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-topic-flow-single-head.t Mon Oct 30 19:24:14 2017 +0100 @@ -0,0 +1,189 @@ +===================== +Test workflow options +===================== + + $ . "$TESTDIR/testlib/topic_setup.sh" + $ . "$TESTDIR/testlib/common.sh" + +Test single head enforcing - Setup +============================================= + + $ hg init single-head-server + $ cd single-head-server + $ cat <> .hg/hgrc + > [phases] + > publish = no + > [experimental] + > enforce-single-head = yes + > evolution = all + > EOF + $ mkcommit ROOT + $ mkcommit c_dA0 + $ cd .. + + $ hg clone single-head-server client + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + +Test single head enforcing - with branch only +--------------------------------------------- + + $ cd client + +continuing the current defaultbranch + + $ mkcommit c_dB0 + $ hg push + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + +creating a new branch + + $ hg up 'desc("ROOT")' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ hg branch branch_A + marked working directory as branch branch_A + (branches are permanent and global, did you want a bookmark?) + $ mkcommit c_aC0 + $ hg push --new-branch + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + +Create a new head on the default branch + + $ hg up 'desc("c_dA0")' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit c_dD0 + created new head + $ hg push -f + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + transaction abort! + rollback completed + abort: 2 heads on "default" + (286d02a6e2a2, 9bf953aa81f6) + [255] + +remerge them + + $ hg merge + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ mkcommit c_dE0 + $ hg push + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + +Test single head enforcing - with topic +--------------------------------------- + +pushing a new topic + + $ hg topic foo + marked working directory as topic: foo + $ mkcommit c_dF0 + active topic 'foo' grew its first changeset + $ hg push + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + +pushing a new topo branch (with a topic) + + $ hg up 'desc("c_dD0")' + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ hg topic bar + marked working directory as topic: bar + $ mkcommit c_dG0 + active topic 'bar' grew its first changeset + $ hg push + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + +detect multiple heads on the topic + + $ mkcommit c_dH0 + $ hg push + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + $ hg up 'desc("c_dG0")' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit c_dI0 + $ hg push -f + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + transaction abort! + rollback completed + abort: 2 heads on "default:bar" + (5194f5dcd542, 48a01453c1c5) + [255] + +merge works fine + + $ hg merge + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ mkcommit c_dJ0 + $ hg push + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + +Test single head enforcing - by phase move +------------------------------------------ + + $ hg -R ../single-head-server phase --public 'desc("c_dJ0")' + abort: 2 heads on "default" + (6ed1df20edb1, 678bca4de98c) + [255] + +Test single head enforcing - after rewrite +------------------------------------------ + + $ hg up foo + switching to topic foo + 3 files updated, 0 files merged, 4 files removed, 0 files unresolved + $ hg commit --amend -m c_dF1 + $ hg push + pushing to $TESTTMP/single-head-server + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 0 changes to 1 files (+1 heads) + 1 new obsolescence markers + obsoleted 1 changesets