mercurial/discovery.py
changeset 14164 cb98fed52495
parent 14073 72c84f24b420
child 14184 4ab6e2d597cc
--- a/mercurial/discovery.py	Mon May 02 19:21:30 2011 +0200
+++ b/mercurial/discovery.py	Mon May 02 19:21:30 2011 +0200
@@ -7,7 +7,7 @@
 
 from node import nullid, short
 from i18n import _
-import util, error
+import util, error, setdiscovery, treediscovery
 
 def findcommonincoming(repo, remote, heads=None, force=False):
     """Return a tuple (common, anyincoming, heads) used to identify the common
@@ -20,145 +20,28 @@
       changegroupsubset. No code except for pull should be relying on this fact
       any longer.
     "heads" is either the supplied heads, or else the remote's heads.
+
+    If you pass heads and they are all known locally, the reponse lists justs
+    these heads in "common" and in "heads".
     """
 
-    m = repo.changelog.nodemap
-    search = []
-    fetch = set()
-    seen = set()
-    seenbranch = set()
-    base = set()
-
-    if not heads:
-        heads = remote.heads()
-
-    if repo.changelog.tip() == nullid:
-        base.add(nullid)
-        if heads != [nullid]:
-            return [nullid], [nullid], list(heads)
-        return [nullid], [], []
-
-    # assume we're closer to the tip than the root
-    # and start by examining the heads
-    repo.ui.status(_("searching for changes\n"))
-
-    if remote.capable('getbundle'):
-        myheads = repo.heads()
-        known = remote.known(myheads)
-        if util.all(known):
-            hasincoming = set(heads).difference(set(myheads)) and True
-            return myheads, hasincoming, heads
-
-    unknown = []
-    for h in heads:
-        if h not in m:
-            unknown.append(h)
-        else:
-            base.add(h)
-
-    heads = unknown
-    if not unknown:
-        return list(base), [], []
-
-    req = set(unknown)
-    reqcnt = 0
-
-    # search through remote branches
-    # a 'branch' here is a linear segment of history, with four parts:
-    # head, root, first parent, second parent
-    # (a branch always has two parents (or none) by definition)
-    unknown = remote.branches(unknown)
-    while unknown:
-        r = []
-        while unknown:
-            n = unknown.pop(0)
-            if n[0] in seen:
-                continue
+    if not remote.capable('getbundle'):
+        return treediscovery.findcommonincoming(repo, remote, heads, force)
 
-            repo.ui.debug("examining %s:%s\n"
-                          % (short(n[0]), short(n[1])))
-            if n[0] == nullid: # found the end of the branch
-                pass
-            elif n in seenbranch:
-                repo.ui.debug("branch already found\n")
-                continue
-            elif n[1] and n[1] in m: # do we know the base?
-                repo.ui.debug("found incomplete branch %s:%s\n"
-                              % (short(n[0]), short(n[1])))
-                search.append(n[0:2]) # schedule branch range for scanning
-                seenbranch.add(n)
-            else:
-                if n[1] not in seen and n[1] not in fetch:
-                    if n[2] in m and n[3] in m:
-                        repo.ui.debug("found new changeset %s\n" %
-                                      short(n[1]))
-                        fetch.add(n[1]) # earliest unknown
-                    for p in n[2:4]:
-                        if p in m:
-                            base.add(p) # latest known
-
-                for p in n[2:4]:
-                    if p not in req and p not in m:
-                        r.append(p)
-                        req.add(p)
-            seen.add(n[0])
-
-        if r:
-            reqcnt += 1
-            repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
-            repo.ui.debug("request %d: %s\n" %
-                        (reqcnt, " ".join(map(short, r))))
-            for p in xrange(0, len(r), 10):
-                for b in remote.branches(r[p:p + 10]):
-                    repo.ui.debug("received %s:%s\n" %
-                                  (short(b[0]), short(b[1])))
-                    unknown.append(b)
+    if heads:
+        allknown = True
+        nm = repo.changelog.nodemap
+        for h in heads:
+            if nm.get(h) is None:
+                allknown = False
+                break
+        if allknown:
+            return (heads, False, heads)
 
-    # do binary search on the branches we found
-    while search:
-        newsearch = []
-        reqcnt += 1
-        repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
-        for n, l in zip(search, remote.between(search)):
-            l.append(n[1])
-            p = n[0]
-            f = 1
-            for i in l:
-                repo.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
-                if i in m:
-                    if f <= 2:
-                        repo.ui.debug("found new branch changeset %s\n" %
-                                          short(p))
-                        fetch.add(p)
-                        base.add(i)
-                    else:
-                        repo.ui.debug("narrowed branch search to %s:%s\n"
-                                      % (short(p), short(i)))
-                        newsearch.append((p, i))
-                    break
-                p, f = i, f * 2
-            search = newsearch
-
-    # sanity check our fetch list
-    for f in fetch:
-        if f in m:
-            raise error.RepoError(_("already have changeset ")
-                                  + short(f[:4]))
-
-    base = list(base)
-    if base == [nullid]:
-        if force:
-            repo.ui.warn(_("warning: repository is unrelated\n"))
-        else:
-            raise util.Abort(_("repository is unrelated"))
-
-    repo.ui.debug("found new changesets starting at " +
-                 " ".join([short(f) for f in fetch]) + "\n")
-
-    repo.ui.progress(_('searching'), None)
-    repo.ui.debug("%d total queries\n" % reqcnt)
-
-    return base, list(fetch), heads
+    res = setdiscovery.findcommonheads(repo.ui, repo, remote,
+                                       abortwhenunrelated=not force)
+    common, anyinc, srvheads = res
+    return (list(common), anyinc, heads or list(srvheads))
 
 def prepush(repo, remote, force, revs, newbranch):
     '''Analyze the local and remote repositories and determine which
@@ -174,9 +57,7 @@
     changegroup is a readable file-like object whose read() returns
     successive changegroup chunks ready to be sent over the wire and
     remoteheads is the list of remote heads.'''
-    remoteheads = remote.heads()
-    common, inc, _rheads = findcommonincoming(repo, remote, heads=remoteheads,
-                                              force=force)
+    common, inc, remoteheads = findcommonincoming(repo, remote, force=force)
 
     cl = repo.changelog
     outg = cl.findmissing(common, revs)