# HG changeset patch # User Pierre-Yves David # Date 1457796977 0 # Node ID d49f75eab6a3cfcf76826f1440157e8f0d90212b # Parent 8a53f99d906177656434de1d835f2ed9b10884ae topic: take topic in account for all branch head computation This changeset introduce a "topicmap" that is tracking not just the head of all branches, but the heads of all branch+topic pair. Including the head of the part of the branch without any topic. In practice this means that BRANCHNAME now resolve to the tipmost part for the branch without topic and impact various other logic like head checking during push and default destination for update and merge (these aspect will need adjustment in later changesets). The on-the-fly-temporary-monkey-patching process is pretty horrible, but allow to move forward without waiting on having core patched. We use 'branch:topic' as the branchmap key, this is a small and easy hack that help use a lot for (future) support of heads discovery/checking and on disc cache. I'm not sure it is worthwhile to improve this until an implementation into core. Note that this changeset change the branchmap in all cases, including during exchange, see next changeset for improved behavior. We also currently have the on-disk cache disabled because the core branchmap is lacking phase information in its cache key. This will get done in a later changesets diff -r 8a53f99d9061 -r d49f75eab6a3 src/topic/__init__.py --- a/src/topic/__init__.py Mon Mar 14 20:18:09 2016 -0400 +++ b/src/topic/__init__.py Sat Mar 12 15:36:17 2016 +0000 @@ -26,10 +26,12 @@ from mercurial import patch from mercurial import phases from mercurial import util +from mercurial import branchmap from . import constants from . import revset as topicrevset from . import destination +from . import topicmap cmdtable = {} command = cmdutil.command(cmdtable) @@ -92,6 +94,29 @@ def currenttopic(self): return self.vfs.tryread('topic') + def branchmap(self, topic=True): + if not topic: + super(topicrepo, self).branchmap() + oldbranchcache = branchmap.branchcache + oldfilename = branchmap._filename + oldcaches = getattr(self, '_branchcaches', {}) + try: + branchmap.branchcache = topicmap.topiccache + branchmap._filename = topicmap._filename + self._branchcaches = getattr(self, '_topiccaches', {}) + branchmap.updatecache(self) + self._topiccaches = self._branchcaches + return self._topiccaches[self.filtername] + finally: + self._branchcaches = oldcaches + branchmap.branchcache = oldbranchcache + branchmap._filename = oldfilename + + def invalidatecaches(self): + super(topicrepo, self).invalidatecaches() + if '_topiccaches' in vars(self.unfiltered()): + self.unfiltered()._topiccaches.clear() + repo.__class__ = topicrepo if util.safehasattr(repo, 'names'): repo.names.addnamespace(namespaces.namespace( diff -r 8a53f99d9061 -r d49f75eab6a3 src/topic/topicmap.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/topic/topicmap.py Sat Mar 12 15:36:17 2016 +0000 @@ -0,0 +1,61 @@ +from mercurial import branchmap + +def _filename(repo): + """name of a branchcache file for a given repo or repoview""" + filename = "cache/topicmap" + if repo.filtername: + filename = '%s-%s' % (filename, repo.filtername) + return filename + +oldbranchcache = branchmap.branchcache + +class topiccache(oldbranchcache): + + def __init__(self, *args, **kwargs): + otherbranchcache = branchmap.branchcache + try: + # super() call may fail otherwise + branchmap.branchcache = oldbranchcache + return super(topiccache, self).__init__(*args, **kwargs) + finally: + branchmap.branchcache = otherbranchcache + + def branchtip(self, branch, topic=''): + '''Return the tipmost open head on branch head, otherwise return the + tipmost closed head on branch. + Raise KeyError for unknown branch.''' + if topic: + branch = '%s:%s' % (branch, topic) + return super(topiccache, self).branchtip(branch) + + def branchheads(self, branch, closed=False, topic=''): + if topic: + branch = '%s:%s' % (branch, topic) + return super(topiccache, self).branchheads(branch, closed=closed) + + def write(self, repo): + # The cache key needs to take phase root in account because change to + # what is public affect topics. We can't write on disk until we have this. + return + + def update(self, repo, revgen): + """Given a branchhead cache, self, that may have extra nodes or be + missing heads, and a generator of nodes that are strictly a superset of + heads missing, this function updates self to be correct. + """ + oldgetbranchinfo = repo.revbranchcache().branchinfo + try: + def branchinfo(r): + info = oldgetbranchinfo(r) + topic = '' + ctx = repo[r] + if ctx.mutable(): + topic = ctx.topic() + branch = info[0] + if topic: + branch = '%s:%s' % (branch, topic) + return (branch, info[1]) + repo.revbranchcache().branchinfo = branchinfo + return super(topiccache, self).update(repo, revgen) + finally: + repo.revbranchcache().branchinfo = oldgetbranchinfo diff -r 8a53f99d9061 -r d49f75eab6a3 tests/test-topic-dest.t --- a/tests/test-topic-dest.t Mon Mar 14 20:18:09 2016 -0400 +++ b/tests/test-topic-dest.t Sat Mar 12 15:36:17 2016 +0000 @@ -39,6 +39,8 @@ $ hg log -r 'ngtip(.)' 3 () c_delta + $ hg log -r 'default' + 3 () c_delta multiple heads with topic @@ -64,6 +66,8 @@ $ hg log -r 'ngtip(.)' 3 () c_delta + $ hg log -r 'default' + 3 () c_delta one of the head is a valid tip @@ -72,7 +76,6 @@ $ echo epsilon >> epsilon $ hg add epsilon $ hg ci -m "c_epsilon" - created new head $ hg log -G @ 6 () c_epsilon | @@ -90,12 +93,16 @@ $ hg log -r 'ngtip(.)' 6 () c_epsilon + $ hg log -r 'default' + 6 () c_epsilon merge destination ================= $ hg up 'ngtip(default)' 0 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg up default + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo zeta >> zeta $ hg add zeta $ hg ci -m "c_zeta" diff -r 8a53f99d9061 -r d49f75eab6a3 tests/test-topic.t --- a/tests/test-topic.t Mon Mar 14 20:18:09 2016 -0400 +++ b/tests/test-topic.t Sat Mar 12 15:36:17 2016 +0000 @@ -75,7 +75,6 @@ narf $ echo >> fran work >> beta $ hg ci -m 'start on fran' - created new head $ hg co narf switching to topic narf 2 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -86,6 +85,7 @@ narf $ echo 'narf!!!' >> alpha $ hg ci -m 'narf!' + created new head $ hg log -G @ changeset: 6:7c34953036d6 | tag: tip @@ -287,6 +287,7 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ echo answer >> alpha $ hg ci -m 'Narf is like `zort` or `poit`!' + created new head $ hg merge narf merging alpha warning: conflicts while merging alpha! (edit, then use 'hg resolve --mark') @@ -298,12 +299,16 @@ (no more unresolved files) $ hg topic narf $ hg ci -m 'Finish narf' + created new head $ hg topics fran * narf query - $ hg debugnamecomplete + $ hg debugnamecomplete # branch:topic here is a buggy side effect default + default:fran + default:narf + default:query fran narf query @@ -381,7 +386,7 @@ adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) - (run 'hg heads' to see heads, 'hg merge' to merge) + (run 'hg heads' to see heads) $ hg topics fran * query @@ -513,6 +518,7 @@ $ hg topics --clear $ echo fran? >> beta $ hg ci -m 'fran?' + created new head $ hg log -Gr 'draft()' @ changeset: 10:4073470c35e1 | tag: tip