##// END OF EJS Templates
discovery: enforce filtering into revlogbaseddag._internalizeall...
Pierre-Yves David -
r20224:34d4a037 default
parent child Browse files
Show More
@@ -1,277 +1,279 b''
1 # dagutil.py - dag utilities for mercurial
1 # dagutil.py - dag utilities for mercurial
2 #
2 #
3 # Copyright 2010 Benoit Boissinot <bboissin@gmail.com>
3 # Copyright 2010 Benoit Boissinot <bboissin@gmail.com>
4 # and Peter Arrenbrecht <peter@arrenbrecht.ch>
4 # and Peter Arrenbrecht <peter@arrenbrecht.ch>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from node import nullrev
9 from node import nullrev
10 from i18n import _
10 from i18n import _
11
11
12
12
13 class basedag(object):
13 class basedag(object):
14 '''generic interface for DAGs
14 '''generic interface for DAGs
15
15
16 terms:
16 terms:
17 "ix" (short for index) identifies a nodes internally,
17 "ix" (short for index) identifies a nodes internally,
18 "id" identifies one externally.
18 "id" identifies one externally.
19
19
20 All params are ixs unless explicitly suffixed otherwise.
20 All params are ixs unless explicitly suffixed otherwise.
21 Pluralized params are lists or sets.
21 Pluralized params are lists or sets.
22 '''
22 '''
23
23
24 def __init__(self):
24 def __init__(self):
25 self._inverse = None
25 self._inverse = None
26
26
27 def nodeset(self):
27 def nodeset(self):
28 '''set of all node idxs'''
28 '''set of all node idxs'''
29 raise NotImplementedError
29 raise NotImplementedError
30
30
31 def heads(self):
31 def heads(self):
32 '''list of head ixs'''
32 '''list of head ixs'''
33 raise NotImplementedError
33 raise NotImplementedError
34
34
35 def parents(self, ix):
35 def parents(self, ix):
36 '''list of parents ixs of ix'''
36 '''list of parents ixs of ix'''
37 raise NotImplementedError
37 raise NotImplementedError
38
38
39 def inverse(self):
39 def inverse(self):
40 '''inverse DAG, where parents becomes children, etc.'''
40 '''inverse DAG, where parents becomes children, etc.'''
41 raise NotImplementedError
41 raise NotImplementedError
42
42
43 def ancestorset(self, starts, stops=None):
43 def ancestorset(self, starts, stops=None):
44 '''
44 '''
45 set of all ancestors of starts (incl), but stop walk at stops (excl)
45 set of all ancestors of starts (incl), but stop walk at stops (excl)
46 '''
46 '''
47 raise NotImplementedError
47 raise NotImplementedError
48
48
49 def descendantset(self, starts, stops=None):
49 def descendantset(self, starts, stops=None):
50 '''
50 '''
51 set of all descendants of starts (incl), but stop walk at stops (excl)
51 set of all descendants of starts (incl), but stop walk at stops (excl)
52 '''
52 '''
53 return self.inverse().ancestorset(starts, stops)
53 return self.inverse().ancestorset(starts, stops)
54
54
55 def headsetofconnecteds(self, ixs):
55 def headsetofconnecteds(self, ixs):
56 '''
56 '''
57 subset of connected list of ixs so that no node has a descendant in it
57 subset of connected list of ixs so that no node has a descendant in it
58
58
59 By "connected list" we mean that if an ancestor and a descendant are in
59 By "connected list" we mean that if an ancestor and a descendant are in
60 the list, then so is at least one path connecting them.
60 the list, then so is at least one path connecting them.
61 '''
61 '''
62 raise NotImplementedError
62 raise NotImplementedError
63
63
64 def externalize(self, ix):
64 def externalize(self, ix):
65 '''return a list of (or set if given a set) of node ids'''
65 '''return a list of (or set if given a set) of node ids'''
66 return self._externalize(ix)
66 return self._externalize(ix)
67
67
68 def externalizeall(self, ixs):
68 def externalizeall(self, ixs):
69 '''return a list of (or set if given a set) of node ids'''
69 '''return a list of (or set if given a set) of node ids'''
70 ids = self._externalizeall(ixs)
70 ids = self._externalizeall(ixs)
71 if isinstance(ixs, set):
71 if isinstance(ixs, set):
72 return set(ids)
72 return set(ids)
73 return list(ids)
73 return list(ids)
74
74
75 def internalize(self, id):
75 def internalize(self, id):
76 '''return a list of (or set if given a set) of node ixs'''
76 '''return a list of (or set if given a set) of node ixs'''
77 return self._internalize(id)
77 return self._internalize(id)
78
78
79 def internalizeall(self, ids, filterunknown=False):
79 def internalizeall(self, ids, filterunknown=False):
80 '''return a list of (or set if given a set) of node ids'''
80 '''return a list of (or set if given a set) of node ids'''
81 ixs = self._internalizeall(ids, filterunknown)
81 ixs = self._internalizeall(ids, filterunknown)
82 if isinstance(ids, set):
82 if isinstance(ids, set):
83 return set(ixs)
83 return set(ixs)
84 return list(ixs)
84 return list(ixs)
85
85
86
86
87 class genericdag(basedag):
87 class genericdag(basedag):
88 '''generic implementations for DAGs'''
88 '''generic implementations for DAGs'''
89
89
90 def ancestorset(self, starts, stops=None):
90 def ancestorset(self, starts, stops=None):
91 stops = stops and set(stops) or set()
91 stops = stops and set(stops) or set()
92 seen = set()
92 seen = set()
93 pending = list(starts)
93 pending = list(starts)
94 while pending:
94 while pending:
95 n = pending.pop()
95 n = pending.pop()
96 if n not in seen and n not in stops:
96 if n not in seen and n not in stops:
97 seen.add(n)
97 seen.add(n)
98 pending.extend(self.parents(n))
98 pending.extend(self.parents(n))
99 return seen
99 return seen
100
100
101 def headsetofconnecteds(self, ixs):
101 def headsetofconnecteds(self, ixs):
102 hds = set(ixs)
102 hds = set(ixs)
103 if not hds:
103 if not hds:
104 return hds
104 return hds
105 for n in ixs:
105 for n in ixs:
106 for p in self.parents(n):
106 for p in self.parents(n):
107 hds.discard(p)
107 hds.discard(p)
108 assert hds
108 assert hds
109 return hds
109 return hds
110
110
111
111
112 class revlogbaseddag(basedag):
112 class revlogbaseddag(basedag):
113 '''generic dag interface to a revlog'''
113 '''generic dag interface to a revlog'''
114
114
115 def __init__(self, revlog, nodeset):
115 def __init__(self, revlog, nodeset):
116 basedag.__init__(self)
116 basedag.__init__(self)
117 self._revlog = revlog
117 self._revlog = revlog
118 self._heads = None
118 self._heads = None
119 self._nodeset = nodeset
119 self._nodeset = nodeset
120
120
121 def nodeset(self):
121 def nodeset(self):
122 return self._nodeset
122 return self._nodeset
123
123
124 def heads(self):
124 def heads(self):
125 if self._heads is None:
125 if self._heads is None:
126 self._heads = self._getheads()
126 self._heads = self._getheads()
127 return self._heads
127 return self._heads
128
128
129 def _externalize(self, ix):
129 def _externalize(self, ix):
130 return self._revlog.index[ix][7]
130 return self._revlog.index[ix][7]
131 def _externalizeall(self, ixs):
131 def _externalizeall(self, ixs):
132 idx = self._revlog.index
132 idx = self._revlog.index
133 return [idx[i][7] for i in ixs]
133 return [idx[i][7] for i in ixs]
134
134
135 def _internalize(self, id):
135 def _internalize(self, id):
136 ix = self._revlog.rev(id)
136 ix = self._revlog.rev(id)
137 if ix == nullrev:
137 if ix == nullrev:
138 raise LookupError(id, self._revlog.indexfile, _('nullid'))
138 raise LookupError(id, self._revlog.indexfile, _('nullid'))
139 return ix
139 return ix
140 def _internalizeall(self, ids, filterunknown):
140 def _internalizeall(self, ids, filterunknown):
141 rl = self._revlog
141 rl = self._revlog
142 if filterunknown:
142 if filterunknown:
143 return [r for r in map(rl.nodemap.get, ids)
143 return [r for r in map(rl.nodemap.get, ids)
144 if r is not None and r != nullrev]
144 if (r is not None
145 and r != nullrev
146 and r not in rl.filteredrevs)]
145 return map(self._internalize, ids)
147 return map(self._internalize, ids)
146
148
147
149
148 class revlogdag(revlogbaseddag):
150 class revlogdag(revlogbaseddag):
149 '''dag interface to a revlog'''
151 '''dag interface to a revlog'''
150
152
151 def __init__(self, revlog):
153 def __init__(self, revlog):
152 revlogbaseddag.__init__(self, revlog, set(revlog))
154 revlogbaseddag.__init__(self, revlog, set(revlog))
153
155
154 def _getheads(self):
156 def _getheads(self):
155 return [r for r in self._revlog.headrevs() if r != nullrev]
157 return [r for r in self._revlog.headrevs() if r != nullrev]
156
158
157 def parents(self, ix):
159 def parents(self, ix):
158 rlog = self._revlog
160 rlog = self._revlog
159 idx = rlog.index
161 idx = rlog.index
160 revdata = idx[ix]
162 revdata = idx[ix]
161 prev = revdata[5]
163 prev = revdata[5]
162 if prev != nullrev:
164 if prev != nullrev:
163 prev2 = revdata[6]
165 prev2 = revdata[6]
164 if prev2 == nullrev:
166 if prev2 == nullrev:
165 return [prev]
167 return [prev]
166 return [prev, prev2]
168 return [prev, prev2]
167 prev2 = revdata[6]
169 prev2 = revdata[6]
168 if prev2 != nullrev:
170 if prev2 != nullrev:
169 return [prev2]
171 return [prev2]
170 return []
172 return []
171
173
172 def inverse(self):
174 def inverse(self):
173 if self._inverse is None:
175 if self._inverse is None:
174 self._inverse = inverserevlogdag(self)
176 self._inverse = inverserevlogdag(self)
175 return self._inverse
177 return self._inverse
176
178
177 def ancestorset(self, starts, stops=None):
179 def ancestorset(self, starts, stops=None):
178 rlog = self._revlog
180 rlog = self._revlog
179 idx = rlog.index
181 idx = rlog.index
180 stops = stops and set(stops) or set()
182 stops = stops and set(stops) or set()
181 seen = set()
183 seen = set()
182 pending = list(starts)
184 pending = list(starts)
183 while pending:
185 while pending:
184 rev = pending.pop()
186 rev = pending.pop()
185 if rev not in seen and rev not in stops:
187 if rev not in seen and rev not in stops:
186 seen.add(rev)
188 seen.add(rev)
187 revdata = idx[rev]
189 revdata = idx[rev]
188 for i in [5, 6]:
190 for i in [5, 6]:
189 prev = revdata[i]
191 prev = revdata[i]
190 if prev != nullrev:
192 if prev != nullrev:
191 pending.append(prev)
193 pending.append(prev)
192 return seen
194 return seen
193
195
194 def headsetofconnecteds(self, ixs):
196 def headsetofconnecteds(self, ixs):
195 if not ixs:
197 if not ixs:
196 return set()
198 return set()
197 rlog = self._revlog
199 rlog = self._revlog
198 idx = rlog.index
200 idx = rlog.index
199 headrevs = set(ixs)
201 headrevs = set(ixs)
200 for rev in ixs:
202 for rev in ixs:
201 revdata = idx[rev]
203 revdata = idx[rev]
202 for i in [5, 6]:
204 for i in [5, 6]:
203 prev = revdata[i]
205 prev = revdata[i]
204 if prev != nullrev:
206 if prev != nullrev:
205 headrevs.discard(prev)
207 headrevs.discard(prev)
206 assert headrevs
208 assert headrevs
207 return headrevs
209 return headrevs
208
210
209 def linearize(self, ixs):
211 def linearize(self, ixs):
210 '''linearize and topologically sort a list of revisions
212 '''linearize and topologically sort a list of revisions
211
213
212 The linearization process tries to create long runs of revs where
214 The linearization process tries to create long runs of revs where
213 a child rev comes immediately after its first parent. This is done by
215 a child rev comes immediately after its first parent. This is done by
214 visiting the heads of the given revs in inverse topological order,
216 visiting the heads of the given revs in inverse topological order,
215 and for each visited rev, visiting its second parent, then its first
217 and for each visited rev, visiting its second parent, then its first
216 parent, then adding the rev itself to the output list.
218 parent, then adding the rev itself to the output list.
217 '''
219 '''
218 sorted = []
220 sorted = []
219 visit = list(self.headsetofconnecteds(ixs))
221 visit = list(self.headsetofconnecteds(ixs))
220 visit.sort(reverse=True)
222 visit.sort(reverse=True)
221 finished = set()
223 finished = set()
222
224
223 while visit:
225 while visit:
224 cur = visit.pop()
226 cur = visit.pop()
225 if cur < 0:
227 if cur < 0:
226 cur = -cur - 1
228 cur = -cur - 1
227 if cur not in finished:
229 if cur not in finished:
228 sorted.append(cur)
230 sorted.append(cur)
229 finished.add(cur)
231 finished.add(cur)
230 else:
232 else:
231 visit.append(-cur - 1)
233 visit.append(-cur - 1)
232 visit += [p for p in self.parents(cur)
234 visit += [p for p in self.parents(cur)
233 if p in ixs and p not in finished]
235 if p in ixs and p not in finished]
234 assert len(sorted) == len(ixs)
236 assert len(sorted) == len(ixs)
235 return sorted
237 return sorted
236
238
237
239
238 class inverserevlogdag(revlogbaseddag, genericdag):
240 class inverserevlogdag(revlogbaseddag, genericdag):
239 '''inverse of an existing revlog dag; see revlogdag.inverse()'''
241 '''inverse of an existing revlog dag; see revlogdag.inverse()'''
240
242
241 def __init__(self, orig):
243 def __init__(self, orig):
242 revlogbaseddag.__init__(self, orig._revlog, orig._nodeset)
244 revlogbaseddag.__init__(self, orig._revlog, orig._nodeset)
243 self._orig = orig
245 self._orig = orig
244 self._children = {}
246 self._children = {}
245 self._roots = []
247 self._roots = []
246 self._walkfrom = len(self._revlog) - 1
248 self._walkfrom = len(self._revlog) - 1
247
249
248 def _walkto(self, walkto):
250 def _walkto(self, walkto):
249 rev = self._walkfrom
251 rev = self._walkfrom
250 cs = self._children
252 cs = self._children
251 roots = self._roots
253 roots = self._roots
252 idx = self._revlog.index
254 idx = self._revlog.index
253 while rev >= walkto:
255 while rev >= walkto:
254 data = idx[rev]
256 data = idx[rev]
255 isroot = True
257 isroot = True
256 for prev in [data[5], data[6]]: # parent revs
258 for prev in [data[5], data[6]]: # parent revs
257 if prev != nullrev:
259 if prev != nullrev:
258 cs.setdefault(prev, []).append(rev)
260 cs.setdefault(prev, []).append(rev)
259 isroot = False
261 isroot = False
260 if isroot:
262 if isroot:
261 roots.append(rev)
263 roots.append(rev)
262 rev -= 1
264 rev -= 1
263 self._walkfrom = rev
265 self._walkfrom = rev
264
266
265 def _getheads(self):
267 def _getheads(self):
266 self._walkto(nullrev)
268 self._walkto(nullrev)
267 return self._roots
269 return self._roots
268
270
269 def parents(self, ix):
271 def parents(self, ix):
270 if ix is None:
272 if ix is None:
271 return []
273 return []
272 if ix <= self._walkfrom:
274 if ix <= self._walkfrom:
273 self._walkto(ix)
275 self._walkto(ix)
274 return self._children.get(ix, [])
276 return self._children.get(ix, [])
275
277
276 def inverse(self):
278 def inverse(self):
277 return self._orig
279 return self._orig
General Comments 0
You need to be logged in to leave comments. Login now