[PATCH 3 of 6] checkheads: simplify the structure build by preprocessing
pierre-yves.david at logilab.fr
pierre-yves.david at logilab.fr
Tue Jul 17 11:49:34 CDT 2012
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at logilab.fr>
# Date 1342535442 -7200
# Node ID 2c5cd8a23acd23b9b001ee0e9fe4c11269d4eaac
# Parent 23c7f8cc9cea4c7784577815296fe51ed018542f
checkheads: simplify the structure build by preprocessing
All useful data are now gathered in a single dictionnary. `branchmapsummary` is
renamed to `headssummary` and its return value is greatly simplified.
diff --git a/mercurial/discovery.py b/mercurial/discovery.py
--- a/mercurial/discovery.py
+++ b/mercurial/discovery.py
@@ -147,52 +147,64 @@ def findcommonoutgoing(repo, other, only
commonheads = set(og.commonheads)
og.missingheads = [h for h in og.missingheads if h not in commonheads]
return og
-def _branchmapsummary(repo, remote, outgoing):
+def _headssummary(repo, remote, outgoing):
"""compute a summary of branch and heads status before and after push
- - oldmap: {'branch': [heads]} mapping for remote
- - newmap: {'branch': [heads]} mapping for local
- - unsynced: set of branch that have unsynced remote changes
- - branches: set of all common branch pushed
- - newbranches: list of plain new pushed branch
+ return {'branch': ([remoteheads], [newheads], [unsyncedheads])} mapping
+
+ - branch: the branch name
+ - remoteheads: the list of remote heads known locally
+ None is the branch is new
+ - newheads: the new remote heads (known locally) with outgoing pushed
+ - unsyncedheads: the list of remote heads unknown locally.
"""
cl = repo.changelog
-
+ headssum = {}
# A. Create set of branches involved in the push.
branches = set(repo[n].branch() for n in outgoing.missing)
remotemap = remote.branchmap()
newbranches = branches - set(remotemap)
branches.difference_update(newbranches)
- # B. 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]
+ # A. register remote heads
+ remotebranches = set()
+ for branch, heads in remote.branchmap().iteritems():
+ remotebranches.add(branch)
+ known = []
+ unsynced = []
+ for h in heads:
+ if h in cl.nodemap:
+ known.append(h)
+ else:
+ unsynced.append(h)
+ headssum[branch] = (known, list(known), unsynced)
+ # B. add new branch data
+ missingctx = list(repo[n] for n in outgoing.missing)
+ touchedbranches = set()
+ for ctx in missingctx:
+ branch = ctx.branch()
+ touchedbranches.add(branch)
+ if branch not in headssum:
+ headssum[branch] = (None, [], [])
- 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)
+ # C drop data about untouched branches:
+ for branch in remotebranches - touchedbranches:
+ del headssum[branch]
- # C. Update newmap with outgoing changes.
+ # D. 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)
- return oldmap, newmap, unsynced, branches, newbranches
+ newmap = dict((branch, heads[1]) for branch, heads in headssum.iteritems()
+ if heads[0] is not None)
+ repo._updatebranchcache(newmap, missingctx)
+ for branch, newheads in newmap.iteritems():
+ headssum[branch][1][:] = newheads
+ return headssum
-def _oldbranchmapsummary(repo, remoteheads, outgoing, inc=False):
+def _oldheadssummary(repo, remoteheads, outgoing, inc=False):
"""Compute branchmapsummary for repo without branchmap support"""
cl = repo.changelog
# 1-4b. old servers: Check for new topological heads.
# Construct {old,new}map with branch = None (topological branch).
@@ -202,15 +214,13 @@ def _oldbranchmapsummary(repo, remotehea
# - an element of oldheads
# - another element of outgoing.missing
# - nullrev
# This explains why the new head are very simple to compute.
r = repo.set('heads(%ln + %ln)', oldheads, outgoing.missing)
- branches = set([None])
- newmap = {None: list(c.node() for c in r)}
- oldmap = {None: oldheads}
- unsynced = inc and branches or set()
- return oldmap, newmap, unsynced, branches, set()
+ newheads = list(c.node() for c in r)
+ unsynced = inc and set([None]) or set()
+ return {None: (oldheads, newheads, unsynced)}
def checkheads(repo, remote, outgoing, remoteheads, newbranch=False, inc=False):
"""Check that a push won't add any outgoing head
raise Abort error and display ui message as needed.
@@ -224,14 +234,15 @@ def checkheads(repo, remote, outgoing, r
if remoteheads == [nullid]:
# remote is empty, nothing to check.
return
if remote.capable('branchmap'):
- bms = _branchmapsummary(repo, remote, outgoing)
+ headssum = _headssummary(repo, remote, outgoing)
else:
- bms = _oldbranchmapsummary(repo, remoteheads, outgoing, inc)
- oldmap, newmap, unsynced, branches, newbranches = bms
+ headssum = _oldheadssummary(repo, remoteheads, outgoing, inc)
+ newbranches = [branch for branch, heads in headssum.iteritems()
+ if heads[0] is None]
# 1. Check for new branches on the remote.
if newbranches and not newbranch: # new branch requires --new-branch
branchnames = ', '.join(sorted(newbranches))
raise util.Abort(_("push creates new remote branches: %s!")
% branchnames,
@@ -242,34 +253,41 @@ def checkheads(repo, remote, outgoing, r
# If there are more heads after the push than before, a suitable
# error message, depending on unsynced status, is displayed.
error = None
localbookmarks = repo._bookmarks
- for branch in branches:
- newhs = set(newmap[branch])
- oldhs = set(oldmap[branch])
+ unsynced = False
+ for branch, heads in headssum.iteritems():
+ if heads[0] is None:
+ # Maybe we should abort if we push more that one head
+ # for new branches ?
+ continue
+ if heads[2]:
+ unsynced = True
+ oldhs = set(heads[0])
+ newhs = set(heads[1])
dhs = None
if len(newhs) > len(oldhs):
- # strip updates to existing remote heads from the new heads list
remotebookmarks = remote.listkeys('bookmarks')
bookmarkedheads = set()
for bm in localbookmarks:
rnode = remotebookmarks.get(bm)
if rnode and rnode in repo:
lctx, rctx = repo[bm], repo[rnode]
if rctx == lctx.ancestor(rctx):
bookmarkedheads.add(lctx.node())
+ # strip updates to existing remote heads from the new heads list
dhs = list(newhs - bookmarkedheads - oldhs)
if dhs:
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:
+ if heads[2]: # 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")
More information about the Mercurial-devel
mailing list