[PATCH STABLE] branchmap: server should not advertise secret changeset in branchmap (Issue3303)

pierre-yves.david at logilab.fr pierre-yves.david at logilab.fr
Tue Apr 24 13:21:09 CDT 2012


# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at logilab.fr>
# Date 1335277964 -7200
# Branch stable
# Node ID cfaeb9318f834d8d8b9fbb8eb6085c09de234391
# Parent  cbf2ea2f5ca169d22e0729cb71a21b808574b90e
branchmap: server should not advertise secret changeset in branchmap (Issue3303)

Discovery now use an overlay above branchmap to prune invisible "secret"
changeset from branchmap.

To minimise impact on the code during the code freeze, this is achieve by
recomputing non-secret heads on the fly when any secret changeset exists. This
is a computation heavy approach similar to the one used for visible heads. But
few sever should contains secret changeset anyway.  See comment in code for more
robust approach.

On local repo the wrapper is applied explicitly while the wire-protocol take
care of wrapping branchmap call in a transparent way. This could be unified by
the Peter Arrenbrecht and Sune Foldager proposal of a `peer` object.

An inappropriate `(+i heads)` may still appear when pushing new changes on a
repository with secret changeset. (see Issue3394 for details)

diff --git a/mercurial/discovery.py b/mercurial/discovery.py
--- a/mercurial/discovery.py
+++ b/mercurial/discovery.py
@@ -153,7 +153,10 @@
         branches = set(repo[n].branch() for n in outgoing.missing)
 
         # 2. Check for new branches on the remote.
-        remotemap = remote.branchmap()
+        if remote.local():
+            remotemap = phases.visiblebranchmap(remote)
+        else:
+            remotemap = remote.branchmap()
         newbranches = branches - set(remotemap)
         if newbranches and not newbranch: # new branch requires --new-branch
             branchnames = ', '.join(sorted(newbranches))
diff --git a/mercurial/phases.py b/mercurial/phases.py
--- a/mercurial/phases.py
+++ b/mercurial/phases.py
@@ -263,6 +263,28 @@
         vheads = repo.heads()
     return vheads
 
+def visiblebranchmap(repo):
+    """return a branchmap for the visible set"""
+    # XXX Recomputing this data on the fly is very slow.  We should build a
+    # XXX cached version while computin the standard branchmap version.
+    sroots = repo._phaseroots[secret]
+    if sroots:
+        vbranchmap = {}
+        for branch, nodes in  repo.branchmap().iteritems():
+            # search for secret heads.
+            for n in nodes:
+                if repo[n].phase() >= secret:
+                    nodes = None
+                    break
+            # if secreat heads where found we must compute them again
+            if nodes is None:
+                s = repo.set('heads(branch(%s) - secret())', branch)
+                nodes = [c.node() for c in s]
+            vbranchmap[branch] = nodes
+    else:
+        vbranchmap = repo.branchmap()
+    return vbranchmap
+
 def analyzeremotephases(repo, subset, roots):
     """Compute phases heads and root in a subset of node from root dict
 
diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -396,7 +396,7 @@
     return "".join(r)
 
 def branchmap(repo, proto):
-    branchmap = repo.branchmap()
+    branchmap = phases.visiblebranchmap(repo)
     heads = []
     for branch, nodes in branchmap.iteritems():
         branchname = urllib.quote(encoding.fromlocal(branch))
diff --git a/tests/test-phases-exchange.t b/tests/test-phases-exchange.t
--- a/tests/test-phases-exchange.t
+++ b/tests/test-phases-exchange.t
@@ -833,12 +833,20 @@
   o  0 public a-A - 054250a37db4
   
 
-pushing a locally public and draft changesets remotly secret should make them appear on the remote side
+pushing a locally public and draft changesets remotly secret should make them
+appear on the remote side.
+
 
   $ hg -R ../mu phase --secret --force 967b449fbc94
   $ hg push -r 435b5d83910c ../mu
   pushing to ../mu
   searching for changes
+  abort: push creates new remote head 435b5d83910c!
+  (did you forget to merge? use push -f to force)
+  [255]
+  $ hg push -fr 435b5d83910c ../mu # because the push will create new visible head
+  pushing to ../mu
+  searching for changes
   adding changesets
   adding manifests
   adding file changes
diff --git a/tests/test-phases.t b/tests/test-phases.t
--- a/tests/test-phases.t
+++ b/tests/test-phases.t
@@ -135,6 +135,39 @@
   2 1 C
   1 0 B
   0 0 A
+
+(Issue3303)
+Check that remote secret changeset are ignore when checking creation of remote heads
+
+We add a secret head into the push destination.  This secreat head shadow a
+visible shared between the initial repo and the push destination.
+
+  $ hg up -q 4 # B'
+  $ mkcommit Z --config phases.new-commit=secret
+  $ hg phase .
+  5: secret
+
+# We now try to push a new public changeset that descend from the common public
+# head shadowed by the remote secret head.
+
+  $ cd ../initialrepo
+  $ hg up -q 6 #B'
+  $ mkcommit I
+  created new head
+  $ hg push ../push-dest
+  pushing to ../push-dest
+  searching for changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 1 changes to 1 files (+1 heads)
+
+:note: The "(+1 heads)" is wrong as we do not had any visible head
+
+
+Restore condition prior extra insertion.
+  $ hg -q --config extensions.mq= strip .
+  $ hg up -q 7
   $ cd ..
 
 Test secret changeset are not pull


More information about the Mercurial-devel mailing list