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