phases: make outgoing object and discovery aware of exclusion
The outgoing object gains an "excluded" members holding all changesets which
were excluded because there where secret.
The core discovery code now remove secret changeset from discovery by default.
This means that any command relying on discovery will exclude secret changeset.
Most notable one are outgoing and bundle. (But bundle with and explicit
``--base`` still allow to bundle outgoing changeset.
--- a/mercurial/discovery.py Mon Jan 09 03:47:16 2012 +0100
+++ b/mercurial/discovery.py Wed Jan 11 00:27:46 2012 +0100
@@ -7,7 +7,7 @@
from node import nullid, short
from i18n import _
-import util, setdiscovery, treediscovery
+import util, setdiscovery, treediscovery, phases
def findcommonincoming(repo, remote, heads=None, force=False):
"""Return a tuple (common, anyincoming, heads) used to identify the common
@@ -54,6 +54,7 @@
missing is a list of all nodes present in local but not in remote.
common is a list of all nodes shared between the two repos.
+ excluded is the list of missing changeset that shouldn't be sent remotely.
missingheads is the list of heads of missing.
commonheads is the list of heads of common.
@@ -66,6 +67,7 @@
self._revlog = revlog
self._common = None
self._missing = None
+ self.excluded = []
def _computecommonmissing(self):
sets = self._revlog.findcommonmissing(self.commonheads,
@@ -94,8 +96,41 @@
If commoninc is given, it must the the result of a prior call to
findcommonincoming(repo, other, force) to avoid recomputing it here.'''
- common, _any, _hds = commoninc or findcommonincoming(repo, other, force=force)
- return outgoing(repo.changelog, common, onlyheads or repo.heads())
+ # declare an empty outgoing object to be filled later
+ og = outgoing(repo.changelog, None, None)
+
+ # get common set if not provided
+ if commoninc is None:
+ commoninc = findcommonincoming(repo, other, force=force)
+ og.commonheads, _any, _hds = commoninc
+
+ # compute outgoing
+ if not repo._phaseroots[phases.secret]:
+ og.missingheads = onlyheads or repo.heads()
+ elif onlyheads is None:
+ # use visible heads as it should be cached
+ og.missingheads = phases.visibleheads(repo)
+ og.excluded = [ctx.node() for ctx in repo.set('secret()')]
+ else:
+ # compute common, missing and exclude secret stuff
+ sets = repo.changelog.findcommonmissing(og.commonheads, onlyheads)
+ og._common, allmissing = sets
+ og._missing = missing = []
+ og._excluded = excluded = []
+ for node in allmissing:
+ if repo[node].phase() >= phases.secret:
+ excluded.append(node)
+ else:
+ missing.append(node)
+ if excluded:
+ # update missing heads
+ rset = repo.set('heads(%ln)', missing)
+ missingheads = [ctx.node() for ctx in rset]
+ else:
+ missingheads = onlyheads
+ og.missingheads = missingheads
+
+ return og
def prepush(repo, remote, force, revs, newbranch):
'''Analyze the local and remote repositories and determine which
@@ -121,29 +156,17 @@
_common, inc, remoteheads = commoninc
cl = repo.changelog
- alloutg = outgoing.missing
+ outg = outgoing.missing
common = outgoing.commonheads
- outg = []
- secret = []
- for o in alloutg:
- if repo[o].phase() >= 2:
- secret.append(o)
- else:
- outg.append(o)
if not outg:
- if secret:
+ if outgoing.excluded:
repo.ui.status(_("no changes to push but %i secret changesets\n")
- % len(secret))
+ % len(outgoing.excluded))
else:
repo.ui.status(_("no changes found\n"))
return None, 1, common
- if secret:
- # recompute target revs
- revs = [ctx.node() for ctx in repo.set('heads(::(%ld))',
- map(repo.changelog.rev, outg))]
-
if not force and remoteheads != [nullid]:
if remote.capable('branchmap'):
# Check for each named branch if we're creating new remote heads.
@@ -241,7 +264,8 @@
if unsynced:
repo.ui.warn(_("note: unsynced remote changes!\n"))
- if revs is None:
+ if revs is None and not outgoing.excluded:
+ # push everything,
# use the fast path, no race possible on push
cg = repo._changegroup(outg, 'push')
else:
--- a/tests/test-check-code-hg.t Mon Jan 09 03:47:16 2012 +0100
+++ b/tests/test-check-code-hg.t Wed Jan 11 00:27:46 2012 +0100
@@ -351,9 +351,6 @@
> If onlyheads is given, only nodes ancestral to nodes in onlyheads (inclusive)
warning: line over 80 characters
mercurial/discovery.py:0:
- > common, _any, _hds = commoninc or findcommonincoming(repo, other, force=force)
- warning: line over 80 characters
- mercurial/discovery.py:0:
> def findcommonoutgoing(repo, other, onlyheads=None, force=False, commoninc=None):
warning: line over 80 characters
mercurial/dispatch.py:0:
--- a/tests/test-phases.t Mon Jan 09 03:47:16 2012 +0100
+++ b/tests/test-phases.t Wed Jan 11 00:27:46 2012 +0100
@@ -95,6 +95,23 @@
> [phases]
> publish=False
> EOF
+ $ hg outgoing ../push-dest --template='{rev} {phase} {desc|firstline}\n'
+ comparing with ../push-dest
+ searching for changes
+ 0 public A
+ 1 public B
+ 2 draft C
+ 3 draft D
+ 6 draft B'
+ $ hg outgoing -r default ../push-dest --template='{rev} {phase} {desc|firstline}\n'
+ comparing with ../push-dest
+ searching for changes
+ 0 public A
+ 1 public B
+ 2 draft C
+ 3 draft D
+ 6 draft B'
+
$ hg push ../push-dest -f # force because we push multiple heads
pushing to ../push-dest
searching for changes
@@ -140,6 +157,13 @@
0 0 A
$ cd ..
+But secret can still be bundled explicitly
+
+ $ cd initialrepo
+ $ hg bundle --base '4^' -r 'children(4)' ../secret-bundle.hg
+ 4 changesets found
+ $ cd ..
+
Test revset
$ cd initialrepo