##// END OF EJS Templates
treediscovery: switch to command executor interface...
Gregory Szorc -
r37652:0ed11f93 default
parent child Browse files
Show More
@@ -1,159 +1,172 b''
1 1 # discovery.py - protocol changeset discovery functions
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import collections
11 11
12 12 from .i18n import _
13 13 from .node import (
14 14 nullid,
15 15 short,
16 16 )
17 17 from . import (
18 18 error,
19 19 )
20 20
21 21 def findcommonincoming(repo, remote, heads=None, force=False):
22 22 """Return a tuple (common, fetch, heads) used to identify the common
23 23 subset of nodes between repo and remote.
24 24
25 25 "common" is a list of (at least) the heads of the common subset.
26 26 "fetch" is a list of roots of the nodes that would be incoming, to be
27 27 supplied to changegroupsubset.
28 28 "heads" is either the supplied heads, or else the remote's heads.
29 29 """
30 30
31 31 knownnode = repo.changelog.hasnode
32 32 search = []
33 33 fetch = set()
34 34 seen = set()
35 35 seenbranch = set()
36 36 base = set()
37 37
38 38 if not heads:
39 heads = remote.heads()
39 with remote.commandexecutor() as e:
40 heads = e.callcommand('heads', {}).result()
40 41
41 42 if repo.changelog.tip() == nullid:
42 43 base.add(nullid)
43 44 if heads != [nullid]:
44 45 return [nullid], [nullid], list(heads)
45 46 return [nullid], [], heads
46 47
47 48 # assume we're closer to the tip than the root
48 49 # and start by examining the heads
49 50 repo.ui.status(_("searching for changes\n"))
50 51
51 52 unknown = []
52 53 for h in heads:
53 54 if not knownnode(h):
54 55 unknown.append(h)
55 56 else:
56 57 base.add(h)
57 58
58 59 if not unknown:
59 60 return list(base), [], list(heads)
60 61
61 62 req = set(unknown)
62 63 reqcnt = 0
63 64
64 65 # search through remote branches
65 66 # a 'branch' here is a linear segment of history, with four parts:
66 67 # head, root, first parent, second parent
67 68 # (a branch always has two parents (or none) by definition)
68 unknown = collections.deque(remote.branches(unknown))
69 with remote.commandexecutor() as e:
70 branches = e.callcommand('branches', {'nodes': unknown}).result()
71
72 unknown = collections.deque(branches)
69 73 while unknown:
70 74 r = []
71 75 while unknown:
72 76 n = unknown.popleft()
73 77 if n[0] in seen:
74 78 continue
75 79
76 80 repo.ui.debug("examining %s:%s\n"
77 81 % (short(n[0]), short(n[1])))
78 82 if n[0] == nullid: # found the end of the branch
79 83 pass
80 84 elif n in seenbranch:
81 85 repo.ui.debug("branch already found\n")
82 86 continue
83 87 elif n[1] and knownnode(n[1]): # do we know the base?
84 88 repo.ui.debug("found incomplete branch %s:%s\n"
85 89 % (short(n[0]), short(n[1])))
86 90 search.append(n[0:2]) # schedule branch range for scanning
87 91 seenbranch.add(n)
88 92 else:
89 93 if n[1] not in seen and n[1] not in fetch:
90 94 if knownnode(n[2]) and knownnode(n[3]):
91 95 repo.ui.debug("found new changeset %s\n" %
92 96 short(n[1]))
93 97 fetch.add(n[1]) # earliest unknown
94 98 for p in n[2:4]:
95 99 if knownnode(p):
96 100 base.add(p) # latest known
97 101
98 102 for p in n[2:4]:
99 103 if p not in req and not knownnode(p):
100 104 r.append(p)
101 105 req.add(p)
102 106 seen.add(n[0])
103 107
104 108 if r:
105 109 reqcnt += 1
106 110 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
107 111 repo.ui.debug("request %d: %s\n" %
108 112 (reqcnt, " ".join(map(short, r))))
109 113 for p in xrange(0, len(r), 10):
110 for b in remote.branches(r[p:p + 10]):
114 with remote.commandexecutor() as e:
115 branches = e.callcommand('branches', {
116 'nodes': r[p:p + 10],
117 }).result()
118
119 for b in branches:
111 120 repo.ui.debug("received %s:%s\n" %
112 121 (short(b[0]), short(b[1])))
113 122 unknown.append(b)
114 123
115 124 # do binary search on the branches we found
116 125 while search:
117 126 newsearch = []
118 127 reqcnt += 1
119 128 repo.ui.progress(_('searching'), reqcnt, unit=_('queries'))
120 for n, l in zip(search, remote.between(search)):
129
130 with remote.commandexecutor() as e:
131 between = e.callcommand('between', {'pairs': search}).result()
132
133 for n, l in zip(search, between):
121 134 l.append(n[1])
122 135 p = n[0]
123 136 f = 1
124 137 for i in l:
125 138 repo.ui.debug("narrowing %d:%d %s\n" % (f, len(l), short(i)))
126 139 if knownnode(i):
127 140 if f <= 2:
128 141 repo.ui.debug("found new branch changeset %s\n" %
129 142 short(p))
130 143 fetch.add(p)
131 144 base.add(i)
132 145 else:
133 146 repo.ui.debug("narrowed branch search to %s:%s\n"
134 147 % (short(p), short(i)))
135 148 newsearch.append((p, i))
136 149 break
137 150 p, f = i, f * 2
138 151 search = newsearch
139 152
140 153 # sanity check our fetch list
141 154 for f in fetch:
142 155 if knownnode(f):
143 156 raise error.RepoError(_("already have changeset ")
144 157 + short(f[:4]))
145 158
146 159 base = list(base)
147 160 if base == [nullid]:
148 161 if force:
149 162 repo.ui.warn(_("warning: repository is unrelated\n"))
150 163 else:
151 164 raise error.Abort(_("repository is unrelated"))
152 165
153 166 repo.ui.debug("found new changesets starting at " +
154 167 " ".join([short(f) for f in fetch]) + "\n")
155 168
156 169 repo.ui.progress(_('searching'), None)
157 170 repo.ui.debug("%d total queries\n" % reqcnt)
158 171
159 172 return base, list(fetch), heads
General Comments 0
You need to be logged in to leave comments. Login now