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