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