mercurial/discovery.py
changeset 15932 4154338f0bc0
parent 15893 eb6867b98223
child 15951 bd84fc0b5f64
--- a/mercurial/discovery.py	Thu Jan 19 11:30:37 2012 +0100
+++ b/mercurial/discovery.py	Thu Jan 19 15:50:55 2012 +0100
@@ -132,146 +132,108 @@
 
     return og
 
-def prepush(repo, remote, force, revs, newbranch):
-    '''Analyze the local and remote repositories and determine which
-    changesets need to be pushed to the remote. Return value depends
-    on circumstances:
-
-    If we are not going to push anything, return a tuple (None, 1,
-    common) The third element "common" is the list of heads of the
-    common set between local and remote.
+def checkheads(repo, remote, outgoing, remoteheads, newbranch=False):
+    """Check that a push won't add any outgoing head
 
-    Otherwise, return a tuple (changegroup, remoteheads, futureheads),
-    where changegroup is a readable file-like object whose read()
-    returns successive changegroup chunks ready to be sent over the
-    wire, remoteheads is the list of remote heads and futureheads is
-    the list of heads of the common set between local and remote to
-    be after push completion.
-    '''
-    commoninc = findcommonincoming(repo, remote, force=force)
-    outgoing = findcommonoutgoing(repo, remote, onlyheads=revs,
-                                      commoninc=commoninc, force=force)
-    _common, inc, remoteheads = commoninc
+    raise Abort error and display ui message as needed.
+    """
+    if remoteheads == [nullid]:
+        # remote is empty, nothing to check.
+        return
 
     cl = repo.changelog
-    outg = outgoing.missing
-    common = outgoing.commonheads
+    if remote.capable('branchmap'):
+        # Check for each named branch if we're creating new remote heads.
+        # To be a remote head after push, node must be either:
+        # - unknown locally
+        # - a local outgoing head descended from update
+        # - a remote head that's known locally and not
+        #   ancestral to an outgoing head
 
-    if not outg:
-        if outgoing.excluded:
-            repo.ui.status(_("no changes to push but %i secret changesets\n")
-                           % len(outgoing.excluded))
-        else:
-            repo.ui.status(_("no changes found\n"))
-        return None, 1, common
+        # 1. Create set of branches involved in the push.
+        branches = set(repo[n].branch() for n in outgoing.missing)
 
-    if not force and remoteheads != [nullid]:
-        if remote.capable('branchmap'):
-            # Check for each named branch if we're creating new remote heads.
-            # To be a remote head after push, node must be either:
-            # - unknown locally
-            # - a local outgoing head descended from update
-            # - a remote head that's known locally and not
-            #   ancestral to an outgoing head
-
-            # 1. Create set of branches involved in the push.
-            branches = set(repo[n].branch() for n in outg)
+        # 2. Check for new branches on the remote.
+        remotemap = remote.branchmap()
+        newbranches = branches - set(remotemap)
+        if newbranches and not newbranch: # new branch requires --new-branch
+            branchnames = ', '.join(sorted(newbranches))
+            raise util.Abort(_("push creates new remote branches: %s!")
+                               % branchnames,
+                             hint=_("use 'hg push --new-branch' to create"
+                                    " new remote branches"))
+        branches.difference_update(newbranches)
 
-            # 2. Check for new branches on the remote.
-            remotemap = remote.branchmap()
-            newbranches = branches - set(remotemap)
-            if newbranches and not newbranch: # new branch requires --new-branch
-                branchnames = ', '.join(sorted(newbranches))
-                raise util.Abort(_("push creates new remote branches: %s!")
-                                   % branchnames,
-                                 hint=_("use 'hg push --new-branch' to create"
-                                        " new remote branches"))
-            branches.difference_update(newbranches)
+        # 3. Construct the initial oldmap and newmap dicts.
+        # They contain information about the remote heads before and
+        # after the push, respectively.
+        # Heads not found locally are not included in either dict,
+        # since they won't be affected by the push.
+        # unsynced contains all branches with incoming changesets.
+        oldmap = {}
+        newmap = {}
+        unsynced = set()
+        for branch in branches:
+            remotebrheads = remotemap[branch]
+            prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
+            oldmap[branch] = prunedbrheads
+            newmap[branch] = list(prunedbrheads)
+            if len(remotebrheads) > len(prunedbrheads):
+                unsynced.add(branch)
 
-            # 3. Construct the initial oldmap and newmap dicts.
-            # They contain information about the remote heads before and
-            # after the push, respectively.
-            # Heads not found locally are not included in either dict,
-            # since they won't be affected by the push.
-            # unsynced contains all branches with incoming changesets.
-            oldmap = {}
-            newmap = {}
-            unsynced = set()
-            for branch in branches:
-                remotebrheads = remotemap[branch]
-                prunedbrheads = [h for h in remotebrheads if h in cl.nodemap]
-                oldmap[branch] = prunedbrheads
-                newmap[branch] = list(prunedbrheads)
-                if len(remotebrheads) > len(prunedbrheads):
-                    unsynced.add(branch)
-
-            # 4. Update newmap with outgoing changes.
-            # This will possibly add new heads and remove existing ones.
-            ctxgen = (repo[n] for n in outg)
-            repo._updatebranchcache(newmap, ctxgen)
+        # 4. Update newmap with outgoing changes.
+        # This will possibly add new heads and remove existing ones.
+        ctxgen = (repo[n] for n in outgoing.missing)
+        repo._updatebranchcache(newmap, ctxgen)
 
-        else:
-            # 1-4b. old servers: Check for new topological heads.
-            # Construct {old,new}map with branch = None (topological branch).
-            # (code based on _updatebranchcache)
-            oldheads = set(h for h in remoteheads if h in cl.nodemap)
-            newheads = oldheads.union(outg)
-            if len(newheads) > 1:
-                for latest in reversed(outg):
-                    if latest not in newheads:
-                        continue
-                    minhrev = min(cl.rev(h) for h in newheads)
-                    reachable = cl.reachable(latest, cl.node(minhrev))
-                    reachable.remove(latest)
-                    newheads.difference_update(reachable)
-            branches = set([None])
-            newmap = {None: newheads}
-            oldmap = {None: oldheads}
-            unsynced = inc and branches or set()
+    else:
+        # 1-4b. old servers: Check for new topological heads.
+        # Construct {old,new}map with branch = None (topological branch).
+        # (code based on _updatebranchcache)
+        oldheads = set(h for h in remoteheads if h in cl.nodemap)
+        newheads = oldheads.union(outg)
+        if len(newheads) > 1:
+            for latest in reversed(outg):
+                if latest not in newheads:
+                    continue
+                minhrev = min(cl.rev(h) for h in newheads)
+                reachable = cl.reachable(latest, cl.node(minhrev))
+                reachable.remove(latest)
+                newheads.difference_update(reachable)
+        branches = set([None])
+        newmap = {None: newheads}
+        oldmap = {None: oldheads}
+        unsynced = inc and branches or set()
 
-        # 5. Check for new heads.
-        # If there are more heads after the push than before, a suitable
-        # error message, depending on unsynced status, is displayed.
-        error = None
-        for branch in branches:
-            newhs = set(newmap[branch])
-            oldhs = set(oldmap[branch])
-            if len(newhs) > len(oldhs):
-                dhs = list(newhs - oldhs)
-                if error is None:
-                    if branch not in ('default', None):
-                        error = _("push creates new remote head %s "
-                                  "on branch '%s'!") % (short(dhs[0]), branch)
-                    else:
-                        error = _("push creates new remote head %s!"
-                                  ) % short(dhs[0])
-                    if branch in unsynced:
-                        hint = _("you should pull and merge or "
-                                 "use push -f to force")
-                    else:
-                        hint = _("did you forget to merge? "
-                                 "use push -f to force")
-                if branch is not None:
-                    repo.ui.note(_("new remote heads on branch '%s'\n") % branch)
-                for h in dhs:
-                    repo.ui.note(_("new remote head %s\n") % short(h))
-        if error:
-            raise util.Abort(error, hint=hint)
+    # 5. Check for new heads.
+    # If there are more heads after the push than before, a suitable
+    # error message, depending on unsynced status, is displayed.
+    error = None
+    for branch in branches:
+        newhs = set(newmap[branch])
+        oldhs = set(oldmap[branch])
+        if len(newhs) > len(oldhs):
+            dhs = list(newhs - oldhs)
+            if error is None:
+                if branch not in ('default', None):
+                    error = _("push creates new remote head %s "
+                              "on branch '%s'!") % (short(dhs[0]), branch)
+                else:
+                    error = _("push creates new remote head %s!"
+                              ) % short(dhs[0])
+                if branch in unsynced:
+                    hint = _("you should pull and merge or "
+                             "use push -f to force")
+                else:
+                    hint = _("did you forget to merge? "
+                             "use push -f to force")
+            if branch is not None:
+                repo.ui.note(_("new remote heads on branch '%s'\n") % branch)
+            for h in dhs:
+                repo.ui.note(_("new remote head %s\n") % short(h))
+    if error:
+        raise util.Abort(error, hint=hint)
 
-        # 6. Check for unsynced changes on involved branches.
-        if unsynced:
-            repo.ui.warn(_("note: unsynced remote changes!\n"))
-
-    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:
-        cg = repo.getlocalbundle('push', outgoing)
-    # no need to compute outg ancestor. All node in outg have either:
-    # - parents in outg
-    # - parents in common
-    # - nullid parent
-    rset = repo.set('heads(%ln + %ln)', common, outg)
-    futureheads = [ctx.node() for ctx in rset]
-    return cg, remoteheads, futureheads
+    # 6. Check for unsynced changes on involved branches.
+    if unsynced:
+        repo.ui.warn(_("note: unsynced remote changes!\n"))