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