# HG changeset patch # User Pierre-Yves David # Date 1457806767 0 # Node ID 0504e76bfbd90ca254792b4353c3686043c800d3 # Parent d49f75eab6a3cfcf76826f1440157e8f0d90212b push: allow pushing new topic to non-publishing server by default This improves and fix the behavior change introduced by the new "topicmap". * Topics are properly ignored when pushing to a publishing server, * pushing new topics is allowed without --force a non-publishing server, * Pushing extra heads on a topic requires --force. Create of new head on a branch by phase movement is not properly detected for now. We'll improve that part in a later changesets. There is more awful monkey patching going on. We'll have to refactor core to get rid of them. diff -r d49f75eab6a3 -r 0504e76bfbd9 src/topic/__init__.py --- a/src/topic/__init__.py Sat Mar 12 15:36:17 2016 +0000 +++ b/src/topic/__init__.py Sat Mar 12 18:19:27 2016 +0000 @@ -13,11 +13,14 @@ import functools from mercurial.i18n import _ +from mercurial import branchmap from mercurial import cmdutil from mercurial import commands from mercurial import context +from mercurial import discovery as discoverymod from mercurial import error from mercurial import extensions +from mercurial import localrepo from mercurial import lock from mercurial import merge from mercurial import namespaces @@ -26,12 +29,13 @@ from mercurial import patch from mercurial import phases from mercurial import util -from mercurial import branchmap +from mercurial import wireproto from . import constants from . import revset as topicrevset from . import destination from . import topicmap +from . import discovery cmdtable = {} command = cmdutil.command(cmdtable) @@ -58,6 +62,8 @@ def reposetup(ui, repo): orig = repo.__class__ + if not isinstance(repo, localrepo.localrepository): + return # this can be a peer in the ssh case (puzzling) class topicrepo(repo.__class__): def commit(self, *args, **kwargs): backup = self.ui.backupconfig('ui', 'allowemptycommit') @@ -117,6 +123,17 @@ if '_topiccaches' in vars(self.unfiltered()): self.unfiltered()._topiccaches.clear() + def peer(self): + peer = super(topicrepo, self).peer() + if getattr(peer, '_repo', None) is not None: # localpeer + class topicpeer(peer.__class__): + def branchmap(self): + usetopic = not self._repo.publishing() + return self._repo.branchmap(topic=usetopic) + peer.__class__ = topicpeer + return peer + + repo.__class__ = topicrepo if util.safehasattr(repo, 'names'): repo.names.addnamespace(namespaces.namespace( @@ -276,6 +293,8 @@ extensions.wrapfunction(cmdutil, 'buildcommittext', committextwrap) extensions.wrapfunction(merge, 'update', mergeupdatewrap) +extensions.wrapfunction(discoverymod, '_headssummary', discovery._headssummary) +extensions.wrapfunction(wireproto, 'branchmap', discovery.wireprotobranchmap) topicrevset.modsetup() cmdutil.summaryhooks.add('topic', summaryhook) diff -r d49f75eab6a3 -r 0504e76bfbd9 src/topic/discovery.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/topic/discovery.py Sat Mar 12 18:19:27 2016 +0000 @@ -0,0 +1,51 @@ +from mercurial import branchmap +from . import topicmap + +def _headssummary(orig, repo, remote, outgoing): + publishing = ('phases' not in remote.listkeys('namespaces') + or bool(remote.listkeys('phases').get('publishing', False))) + if publishing: + return orig(repo, remote, outgoing) + oldgetitem = repo.__getitem__ + oldrepo = repo.__class__ + oldbranchcache = branchmap.branchcache + oldfilename = branchmap._filename + try: + class repocls(repo.__class__): + def __getitem__(self, key): + ctx = super(repocls, self).__getitem__(key) + oldbranch = ctx.branch + def branch(): + branch = oldbranch() + topic = ctx.topic() + if topic: + branch = "%s:%s" % (branch, topic) + return branch + ctx.branch = branch + return ctx + repo.__class__ = repocls + branchmap.branchcache = topicmap.topiccache + branchmap._filename = topicmap._filename + summary = orig(repo, remote, outgoing) + for key, value in summary.iteritems(): + if ':' in key: # This is a topic + if value[0] is None and value[1]: + summary[key] = ([value[1].pop(0)], ) + value[1:] + return summary + finally: + repo.__class__ = oldrepo + branchmap.branchcache = oldbranchcache + branchmap._filename = oldfilename + +def wireprotobranchmap(orig, repo, proto): + oldrepo = repo.__class__ + print repo + try: + class repocls(repo.__class__): + def branchmap(self): + usetopic = not self.publishing() + return super(repocls, self).branchmap(topic=usetopic) + repo.__class__ = repocls + return orig(repo, proto) + finally: + repo.__class__ = oldrepo diff -r d49f75eab6a3 -r 0504e76bfbd9 tests/test-topic-push.t --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-topic-push.t Sat Mar 12 18:19:27 2016 +0000 @@ -0,0 +1,330 @@ + $ . "$TESTDIR/testlib" + + $ cat << EOF >> $HGRCPATH + > [ui] + > logtemplate = {rev} {branch} {get(namespaces, "topics")} {phase} {desc|firstline}\n + > [ui] + > ssh =python "$RUNTESTDIR/dummyssh" + > EOF + + $ hg init main + $ hg init draft + $ cat << EOF >> draft/.hg/hgrc + > [phases] + > publish=False + > EOF + $ hg clone main client + updating to branch default + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cat << EOF >> client/.hg/hgrc + > [paths] + > draft=../draft + > EOF + + +Testing core behavior to make sure we did not break anything +============================================================ + +Pushing a first changeset + + $ cd client + $ echo aaa > aaa + $ hg add aaa + $ hg commit -m 'CA' + $ hg outgoing -G + comparing with $TESTTMP/main + searching for changes + @ 0 default draft CA + + $ hg push + pushing to $TESTTMP/main + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + +Pushing two heads + + $ echo aaa > bbb + $ hg add bbb + $ hg commit -m 'CB' + $ echo aaa > ccc + $ hg up 'desc(CA)' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg add ccc + $ hg commit -m 'CC' + created new head + $ hg outgoing -G + comparing with $TESTTMP/main + searching for changes + @ 2 default draft CC + + o 1 default draft CB + + $ hg push + pushing to $TESTTMP/main + searching for changes + abort: push creates new remote head 9fe81b7f425d! + (merge or see "hg help push" for details about pushing new heads) + [255] + $ hg outgoing -r 'desc(CB)' -G + comparing with $TESTTMP/main + searching for changes + o 1 default draft CB + + $ hg push -r 'desc(CB)' + pushing to $TESTTMP/main + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + +Pushing a new branch + + $ hg branch mountain + marked working directory as branch mountain + (branches are permanent and global, did you want a bookmark?) + $ hg commit --amend + $ hg outgoing -G + comparing with $TESTTMP/main + searching for changes + @ 4 mountain draft CC + + $ hg push + pushing to $TESTTMP/main + searching for changes + abort: push creates new remote branches: mountain! + (use 'hg push --new-branch' to create new remote branches) + [255] + $ hg push --new-branch + pushing to $TESTTMP/main + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + 2 new obsolescence markers + +Including on non-publishing + + $ hg push --new-branch draft + pushing to $TESTTMP/draft + searching for changes + adding changesets + adding manifests + adding file changes + added 3 changesets with 3 changes to 3 files (+1 heads) + 2 new obsolescence markers + +Testing topic behavior +====================== + +Local peer tests +---------------- + + $ hg up -r 'desc(CA)' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg topic babar + $ echo aaa > ddd + $ hg add ddd + $ hg commit -m 'CD' + created new head + $ hg log -G # keep track of phase because I saw some strange bug during developement + @ 5 default babar draft CD + | + | o 4 mountain public CC + |/ + | o 1 default public CB + |/ + o 0 default public CA + + +Pushing a new topic to a non publishing server should not be seen as a new head + + $ hg push draft + pushing to $TESTTMP/draft + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files (+1 heads) + $ hg log -G + @ 5 default babar draft CD + | + | o 4 mountain public CC + |/ + | o 1 default public CB + |/ + o 0 default public CA + + +Pushing a new topic to a publishing server should be seen as a new head + + $ hg push + pushing to $TESTTMP/main + searching for changes + abort: push creates new remote head 67f579af159d! + (merge or see "hg help push" for details about pushing new heads) + [255] + $ hg log -G + @ 5 default babar draft CD + | + | o 4 mountain public CC + |/ + | o 1 default public CB + |/ + o 0 default public CA + + +wireprotocol tests +------------------ + + $ hg up -r 'desc(CA)' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg topic celeste + $ echo aaa > eee + $ hg add eee + $ hg commit -m 'CE' + created new head + $ hg log -G # keep track of phase because I saw some strange bug during developement + @ 6 default celeste draft CE + | + | o 5 default babar draft CD + |/ + | o 4 mountain public CC + |/ + | o 1 default public CB + |/ + o 0 default public CA + + +Pushing a new topic to a non publishing server should not be seen as a new head + + $ hg push ssh://user@dummy/draft + pushing to ssh://user@dummy/draft + searching for changes + remote: adding changesets + remote: adding manifests + remote: adding file changes + remote: added 1 changesets with 1 changes to 1 files (+1 heads) + $ hg log -G + @ 6 default celeste draft CE + | + | o 5 default babar draft CD + |/ + | o 4 mountain public CC + |/ + | o 1 default public CB + |/ + o 0 default public CA + + +Pushing a new topic to a publishing server should be seen as a new head + + $ hg push ssh://user@dummy/main + pushing to ssh://user@dummy/main + searching for changes + abort: push creates new remote head 67f579af159d! + (merge or see "hg help push" for details about pushing new heads) + [255] + $ hg log -G + @ 6 default celeste draft CE + | + | o 5 default babar draft CD + |/ + | o 4 mountain public CC + |/ + | o 1 default public CB + |/ + o 0 default public CA + + +Check that we reject multiple head on the same topic +---------------------------------------------------- + + $ hg up 'desc(CB)' + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg topic babar + $ echo aaa > fff + $ hg add fff + $ hg commit -m 'CF' + $ hg log -G + @ 7 default babar draft CF + | + | o 6 default celeste draft CE + | | + | | o 5 default babar draft CD + | |/ + | | o 4 mountain public CC + | |/ + o | 1 default public CB + |/ + o 0 default public CA + + + $ hg push draft + pushing to $TESTTMP/draft + searching for changes + abort: push creates new remote head f0bc62a661be on branch 'default:babar'! + (merge or see "hg help push" for details about pushing new heads) + [255] + +Multiple head on a branch merged in a topic changesets +------------------------------------------------------------------------ + + + $ hg up 'desc(CA)' + 0 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ echo aaa > ggg + $ hg add ggg + $ hg commit -m 'CG' + created new head + $ hg up 'desc(CF)' + switching to topic babar + 2 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg merge 'desc(CG)' + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ hg commit -m 'CM' + $ hg log -G + @ 9 default babar draft CM + |\ + | o 8 default draft CG + | | + o | 7 default babar draft CF + | | + | | o 6 default celeste draft CE + | |/ + | | o 5 default babar draft CD + | |/ + | | o 4 mountain public CC + | |/ + o | 1 default public CB + |/ + o 0 default public CA + + +Reject when pushing to draft + + $ hg push draft -r . + pushing to $TESTTMP/draft + searching for changes + abort: push creates new remote head 4937c4cad39e! + (merge or see "hg help push" for details about pushing new heads) + [255] + + +Reject when pushing to publishing + + $ hg push -r . + pushing to $TESTTMP/main + searching for changes + adding changesets + adding manifests + adding file changes + added 3 changesets with 2 changes to 2 files + + $ cd .. +