Submitter | Boris Feld |
---|---|
Date | Nov. 20, 2017, 5:15 p.m. |
Message ID | <2d706154205c1fbff7ad.1511198122@FB> |
Download | mbox | patch |
Permalink | /patch/25667/ |
State | Accepted |
Headers | show |
Comments
On Mon, Nov 20, 2017 at 06:15:22PM +0100, Boris Feld wrote: > # HG changeset patch > # User Boris Feld <boris.feld@octobus.net> > # Date 1510800762 -3600 > # Thu Nov 16 03:52:42 2017 +0100 > # Node ID 2d706154205c1fbff7ad5baea90697c7848ad61c > # Parent 5312b5738172718b868c1ecfd28ada8d4d7e85de > # EXP-Topic single-heads > # Available At https://bitbucket.org/octobus/mercurial-devel/ > # hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 2d706154205c > server: introduce a 'experimental.single-head-per-branch' option Convince me this should be done like this rather than as an example hook? We've wanted example hooks in contrib/example-hooks for years, maybe this would be a good start.
On Mon, 2017-11-20 at 16:14 -0500, Augie Fackler wrote: > On Mon, Nov 20, 2017 at 06:15:22PM +0100, Boris Feld wrote: > > # HG changeset patch > > # User Boris Feld <boris.feld@octobus.net> > > # Date 1510800762 -3600 > > # Thu Nov 16 03:52:42 2017 +0100 > > # Node ID 2d706154205c1fbff7ad5baea90697c7848ad61c > > # Parent 5312b5738172718b868c1ecfd28ada8d4d7e85de > > # EXP-Topic single-heads > > # Available At https://bitbucket.org/octobus/mercurial-devel/ > > # hg pull https://bitbucket.org/octobus/mercurial-deve > > l/ -r 2d706154205c > > server: introduce a 'experimental.single-head-per-branch' option > > Convince me this should be done like this rather than as an example > hook? We've wanted example hooks in contrib/example-hooks for years, > maybe this would be a good start. We discussed the possibility to use a hook with Gregory, but found several limitations which explains why we went with this approach instead: - The performance impact. Having the code in Python meant it has access to more data in a more efficient way. - We don't really want to promote the Python hook API much because it uses the internal API. - No cross-compatibility issues like we could have with shell scripts on Windows. And not all Mercurial installations on Windows have access to a Python interpreter. - Named branch policy might really fit as standard hooks. However, we can think of multiple "mode" that would not really fit well as "hooks (see later in the email for details). Gregory seemed ready to take this series except for UI considerations, so we updated the series to put the config in experimental so we could land it while we bikeshed about how to activate it and what is the best form it should take. This series is a start of an experimental travel to tune the behavior of Mercurial in order to better support specific workflows. This series starts with enforcing a single head but it's only a part of a bigger plan. Here are the areas we already thought about that would receive such tuning options: named-branches: * current behavior * enforcing single heads * refuse any named branch (but default) bookmarks: * current, * forbid any bookmarks, * request all heads to have a bookmark, subrepositories: * allow all, * allow hg only * allow none phases: (getting into topic territory) * publish all, * publish draft without topic, * refuse draft without topic, * publish none, These settings have non-binary values, we are thinking about something like: [repo-mode] named-branch = single-head Or: [repo-mode] name-branch = disallow We could try to "force" the value in the hooks section, but it would feels awkward to us: [hooks] internals.name-branch.single-head = yes internals.name-branch.refuse = no We want to add similar tunings to other areas. Each area will likely have several modes. From our understanding of internal hooks, they would be either enabled or disabled. This difference let us We think that having a dedicated section to that will be clearer. We are not sure about the name yet, maybe 'constraints' or 'workflow'. They should probably come with a requirement to prevent older clients to break things. The example above is to start a standalone discussion. We prefer to have this series gets into core (under the experimental section) in the meantime.
On Tue, Nov 21, 2017 at 08:13:57PM +0100, Boris Feld wrote: > On Mon, 2017-11-20 at 16:14 -0500, Augie Fackler wrote: > > On Mon, Nov 20, 2017 at 06:15:22PM +0100, Boris Feld wrote: > > > # HG changeset patch > > > # User Boris Feld <boris.feld@octobus.net> > > > # Date 1510800762 -3600 > > > # Thu Nov 16 03:52:42 2017 +0100 > > > # Node ID 2d706154205c1fbff7ad5baea90697c7848ad61c > > > # Parent 5312b5738172718b868c1ecfd28ada8d4d7e85de > > > # EXP-Topic single-heads > > > # Available At https://bitbucket.org/octobus/mercurial-devel/ > > > # hg pull https://bitbucket.org/octobus/mercurial-deve > > > l/ -r 2d706154205c > > > server: introduce a 'experimental.single-head-per-branch' option > > > > Convince me this should be done like this rather than as an example > > hook? We've wanted example hooks in contrib/example-hooks for years, > > maybe this would be a good start. > > We discussed the possibility to use a hook with Gregory, but found > several limitations which explains why we went with this approach > instead: > > - The performance impact. Having the code in Python meant it has > access to more data in a more efficient way. > - We don't really want to promote the Python hook API much because > it uses the internal API. > - No cross-compatibility issues like we could have with shell > scripts on Windows. And not all Mercurial installations on Windows have > access to a Python interpreter. > - Named branch policy might really fit as standard hooks. However, > we can think of multiple "mode" that would not really fit well as > "hooks (see later in the email for details). > > Gregory seemed ready to take this series except for UI considerations, > so we updated the series to put the config in experimental so we could > land it while we bikeshed about how to activate it and what is the best > form it should take. Works for me. Since it's experimental, we can always drop it. I'd still _very much_ like to see some sample hooks, and "only one head in the repo" seems like a great starter hook for a contrib/example-hooks/... collection. Might be a good way to earn some brownie points.
Patch
diff --git a/mercurial/configitems.py b/mercurial/configitems.py --- a/mercurial/configitems.py +++ b/mercurial/configitems.py @@ -442,6 +442,9 @@ coreconfigitem('experimental', 'rebase.m coreconfigitem('experimental', 'revlogv2', default=None, ) +coreconfigitem('experimental', 'single-head-per-branch', + default=False, +) coreconfigitem('experimental', 'spacemovesdown', default=False, ) diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1244,6 +1244,8 @@ class localrepository(object): # gating. tracktags(tr2) repo = reporef() + if repo.ui.configbool('experimental', 'single-head-per-branch'): + scmutil.enforcesinglehead(repo, tr2, desc) if hook.hashook(repo.ui, 'pretxnclose-bookmark'): for name, (old, new) in sorted(tr.changes['bookmarks'].items()): args = tr.hookargs.copy() diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -1277,3 +1277,18 @@ def nodesummaries(repo, nodes, maxnumnod return ' '.join(short(h) for h in nodes) first = ' '.join(short(h) for h in nodes[:maxnumnodes]) return _("%s and %s others") % (first, len(nodes) - maxnumnodes) + +def enforcesinglehead(repo, tr, desc): + """check that no named branch has multiple heads""" + if desc in ('strip', 'repair'): + # skip the logic during strip + return + visible = repo.filtered('visible') + # possible improvement: we could restrict the check to affected branch + for name, heads in visible.branchmap().iteritems(): + if len(heads) > 1: + msg = _('rejecting multiple heads on branch "%s"') + msg %= name + hint = _('%d heads: %s') + hint %= (len(heads), nodesummaries(repo, heads)) + raise error.Abort(msg, hint=hint) diff --git a/tests/test-single-head.t b/tests/test-single-head.t new file mode 100644 --- /dev/null +++ b/tests/test-single-head.t @@ -0,0 +1,203 @@ +===================== +Test workflow options +===================== + + $ . "$TESTDIR/testlib/obsmarker-common.sh" + +Test single head enforcing - Setup +============================================= + + $ cat << EOF >> $HGRCPATH + > [experimental] + > evolution = all + > EOF + $ hg init single-head-server + $ cd single-head-server + $ cat <<EOF >> .hg/hgrc + > [phases] + > publish = no + > [experimental] + > single-head-per-branch = yes + > 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 (glob) + 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 (glob) + 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 (glob) + 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: rejecting multiple heads on branch "default" + (2 heads: 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 (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 2 changesets with 2 changes to 2 files + +Test single head enforcing - after rewrite +------------------------------------------ + + $ mkcommit c_dF0 + $ hg push + pushing to $TESTTMP/single-head-server (glob) + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + $ hg commit --amend -m c_dF1 + $ hg push + pushing to $TESTTMP/single-head-server (glob) + 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 + +Check it does to interfer with strip +------------------------------------ + +setup + + $ hg branch branch_A --force + marked working directory as branch branch_A + $ mkcommit c_aG0 + created new head + $ hg update 'desc("c_dF1")' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ mkcommit c_dH0 + $ hg update 'desc("c_aG0")' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg merge + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ mkcommit c_aI0 + $ hg log -G + @ changeset: 10:49003e504178 + |\ branch: branch_A + | | tag: tip + | | parent: 8:a33fb808fb4b + | | parent: 3:840af1c6bc88 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: c_aI0 + | | + | | o changeset: 9:fe47ea669cea + | | | parent: 7:99a2dc242c5d + | | | user: test + | | | date: Thu Jan 01 00:00:00 1970 +0000 + | | | summary: c_dH0 + | | | + | o | changeset: 8:a33fb808fb4b + | |/ branch: branch_A + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: c_aG0 + | | + | o changeset: 7:99a2dc242c5d + | | parent: 5:6ed1df20edb1 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: c_dF1 + | | + | o changeset: 5:6ed1df20edb1 + | |\ parent: 4:9bf953aa81f6 + | | | parent: 2:286d02a6e2a2 + | | | user: test + | | | date: Thu Jan 01 00:00:00 1970 +0000 + | | | summary: c_dE0 + | | | + | | o changeset: 4:9bf953aa81f6 + | | | parent: 1:134bc3852ad2 + | | | user: test + | | | date: Thu Jan 01 00:00:00 1970 +0000 + | | | summary: c_dD0 + | | | + o | | changeset: 3:840af1c6bc88 + | | | branch: branch_A + | | | parent: 0:ea207398892e + | | | user: test + | | | date: Thu Jan 01 00:00:00 1970 +0000 + | | | summary: c_aC0 + | | | + | o | changeset: 2:286d02a6e2a2 + | |/ user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: c_dB0 + | | + | o changeset: 1:134bc3852ad2 + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c_dA0 + | + o changeset: 0:ea207398892e + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: ROOT + + +actual stripping + + $ hg strip --config extensions.strip= --rev 'desc("c_dH0")' + saved backup bundle to $TESTTMP/client/.hg/strip-backup/fe47ea669cea-a41bf5a9-backup.hg (glob) +