##// END OF EJS Templates
revlog: choose a consistent ancestor when there's a tie...
Bryan O'Sullivan -
r18987:3605d4e7 default
parent child Browse files
Show More
@@ -1,431 +1,386
1 # ancestor.py - generic DAG ancestor algorithm for mercurial
1 # ancestor.py - generic DAG ancestor algorithm for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 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 import error, heapq, util
8 import heapq, util
9 from node import nullrev
9 from node import nullrev
10
10
11 def ancestors(pfunc, *orignodes):
11 def ancestors(pfunc, *orignodes):
12 """
12 """
13 Returns the common ancestors of a and b that are furthest from a
13 Returns the common ancestors of a and b that are furthest from a
14 root (as measured by longest path).
14 root (as measured by longest path).
15
15
16 pfunc must return a list of parent vertices for a given vertex.
16 pfunc must return a list of parent vertices for a given vertex.
17 """
17 """
18 if not isinstance(orignodes, set):
18 if not isinstance(orignodes, set):
19 orignodes = set(orignodes)
19 orignodes = set(orignodes)
20 if nullrev in orignodes:
20 if nullrev in orignodes:
21 return set()
21 return set()
22 if len(orignodes) <= 1:
22 if len(orignodes) <= 1:
23 return orignodes
23 return orignodes
24
24
25 def candidates(nodes):
25 def candidates(nodes):
26 allseen = (1 << len(nodes)) - 1
26 allseen = (1 << len(nodes)) - 1
27 seen = [0] * (max(nodes) + 1)
27 seen = [0] * (max(nodes) + 1)
28 for i, n in enumerate(nodes):
28 for i, n in enumerate(nodes):
29 seen[n] = 1 << i
29 seen[n] = 1 << i
30 poison = 1 << (i + 1)
30 poison = 1 << (i + 1)
31
31
32 gca = set()
32 gca = set()
33 interesting = left = len(nodes)
33 interesting = left = len(nodes)
34 nv = len(seen) - 1
34 nv = len(seen) - 1
35 while nv >= 0 and interesting:
35 while nv >= 0 and interesting:
36 v = nv
36 v = nv
37 nv -= 1
37 nv -= 1
38 if not seen[v]:
38 if not seen[v]:
39 continue
39 continue
40 sv = seen[v]
40 sv = seen[v]
41 if sv < poison:
41 if sv < poison:
42 interesting -= 1
42 interesting -= 1
43 if sv == allseen:
43 if sv == allseen:
44 gca.add(v)
44 gca.add(v)
45 sv |= poison
45 sv |= poison
46 if v in nodes:
46 if v in nodes:
47 left -= 1
47 left -= 1
48 if left <= 1:
48 if left <= 1:
49 # history is linear
49 # history is linear
50 return set([v])
50 return set([v])
51 if sv < poison:
51 if sv < poison:
52 for p in pfunc(v):
52 for p in pfunc(v):
53 sp = seen[p]
53 sp = seen[p]
54 if p == nullrev:
54 if p == nullrev:
55 continue
55 continue
56 if sp == 0:
56 if sp == 0:
57 seen[p] = sv
57 seen[p] = sv
58 interesting += 1
58 interesting += 1
59 elif sp != sv:
59 elif sp != sv:
60 seen[p] |= sv
60 seen[p] |= sv
61 else:
61 else:
62 for p in pfunc(v):
62 for p in pfunc(v):
63 if p == nullrev:
63 if p == nullrev:
64 continue
64 continue
65 sp = seen[p]
65 sp = seen[p]
66 if sp and sp < poison:
66 if sp and sp < poison:
67 interesting -= 1
67 interesting -= 1
68 seen[p] = sv
68 seen[p] = sv
69 return gca
69 return gca
70
70
71 def deepest(nodes):
71 def deepest(nodes):
72 interesting = {}
72 interesting = {}
73 count = max(nodes) + 1
73 count = max(nodes) + 1
74 depth = [0] * count
74 depth = [0] * count
75 seen = [0] * count
75 seen = [0] * count
76 mapping = []
76 mapping = []
77 for (i, n) in enumerate(sorted(nodes)):
77 for (i, n) in enumerate(sorted(nodes)):
78 depth[n] = 1
78 depth[n] = 1
79 b = 1 << i
79 b = 1 << i
80 seen[n] = b
80 seen[n] = b
81 interesting[b] = 1
81 interesting[b] = 1
82 mapping.append((b, n))
82 mapping.append((b, n))
83 nv = count - 1
83 nv = count - 1
84 while nv >= 0 and len(interesting) > 1:
84 while nv >= 0 and len(interesting) > 1:
85 v = nv
85 v = nv
86 nv -= 1
86 nv -= 1
87 dv = depth[v]
87 dv = depth[v]
88 if dv == 0:
88 if dv == 0:
89 continue
89 continue
90 sv = seen[v]
90 sv = seen[v]
91 for p in pfunc(v):
91 for p in pfunc(v):
92 if p == nullrev:
92 if p == nullrev:
93 continue
93 continue
94 dp = depth[p]
94 dp = depth[p]
95 nsp = sp = seen[p]
95 nsp = sp = seen[p]
96 if dp <= dv:
96 if dp <= dv:
97 depth[p] = dv + 1
97 depth[p] = dv + 1
98 if sp != sv:
98 if sp != sv:
99 interesting[sv] += 1
99 interesting[sv] += 1
100 nsp = seen[p] = sv
100 nsp = seen[p] = sv
101 if sp:
101 if sp:
102 interesting[sp] -= 1
102 interesting[sp] -= 1
103 if interesting[sp] == 0:
103 if interesting[sp] == 0:
104 del interesting[sp]
104 del interesting[sp]
105 elif dv == dp - 1:
105 elif dv == dp - 1:
106 nsp = sp | sv
106 nsp = sp | sv
107 if nsp == sp:
107 if nsp == sp:
108 continue
108 continue
109 seen[p] = nsp
109 seen[p] = nsp
110 interesting.setdefault(nsp, 0)
110 interesting.setdefault(nsp, 0)
111 interesting[nsp] += 1
111 interesting[nsp] += 1
112 interesting[sp] -= 1
112 interesting[sp] -= 1
113 if interesting[sp] == 0:
113 if interesting[sp] == 0:
114 del interesting[sp]
114 del interesting[sp]
115 interesting[sv] -= 1
115 interesting[sv] -= 1
116 if interesting[sv] == 0:
116 if interesting[sv] == 0:
117 del interesting[sv]
117 del interesting[sv]
118
118
119 if len(interesting) != 1:
119 if len(interesting) != 1:
120 return []
120 return []
121
121
122 k = 0
122 k = 0
123 for i in interesting:
123 for i in interesting:
124 k |= i
124 k |= i
125 return set(n for (i, n) in mapping if k & i)
125 return set(n for (i, n) in mapping if k & i)
126
126
127 gca = candidates(orignodes)
127 gca = candidates(orignodes)
128
128
129 if len(gca) <= 1:
129 if len(gca) <= 1:
130 return gca
130 return gca
131 return deepest(gca)
131 return deepest(gca)
132
132
133 def genericancestor(a, b, pfunc):
133 def genericancestor(a, b, pfunc):
134 """
134 """
135 Returns the common ancestor of a and b that is furthest from a
135 Returns the common ancestor of a and b that is furthest from a
136 root (as measured by longest path) or None if no ancestor is
136 root (as measured by longest path) or None if no ancestor is
137 found. If there are multiple common ancestors at the same
137 found. If there are multiple common ancestors at the same
138 distance, the first one found is returned.
138 distance, the first one found is returned.
139
139
140 pfunc must return a list of parent vertices for a given vertex
140 pfunc must return a list of parent vertices for a given vertex
141 """
141 """
142
142
143 if a == b:
143 if a == b:
144 return a
144 return a
145
145
146 a, b = sorted([a, b])
146 a, b = sorted([a, b])
147
147
148 # find depth from root of all ancestors
148 # find depth from root of all ancestors
149 # depth is stored as a negative for heapq
149 # depth is stored as a negative for heapq
150 parentcache = {}
150 parentcache = {}
151 visit = [a, b]
151 visit = [a, b]
152 depth = {}
152 depth = {}
153 while visit:
153 while visit:
154 vertex = visit[-1]
154 vertex = visit[-1]
155 pl = [p for p in pfunc(vertex) if p != nullrev]
155 pl = [p for p in pfunc(vertex) if p != nullrev]
156 parentcache[vertex] = pl
156 parentcache[vertex] = pl
157 if not pl:
157 if not pl:
158 depth[vertex] = 0
158 depth[vertex] = 0
159 visit.pop()
159 visit.pop()
160 else:
160 else:
161 for p in pl:
161 for p in pl:
162 if p == a or p == b: # did we find a or b as a parent?
162 if p == a or p == b: # did we find a or b as a parent?
163 return p # we're done
163 return p # we're done
164 if p not in depth:
164 if p not in depth:
165 visit.append(p)
165 visit.append(p)
166 if visit[-1] == vertex:
166 if visit[-1] == vertex:
167 # -(maximum distance of parents + 1)
167 # -(maximum distance of parents + 1)
168 depth[vertex] = min([depth[p] for p in pl]) - 1
168 depth[vertex] = min([depth[p] for p in pl]) - 1
169 visit.pop()
169 visit.pop()
170
170
171 # traverse ancestors in order of decreasing distance from root
171 # traverse ancestors in order of decreasing distance from root
172 def ancestors(vertex):
172 def ancestors(vertex):
173 h = [(depth[vertex], vertex)]
173 h = [(depth[vertex], vertex)]
174 seen = set()
174 seen = set()
175 while h:
175 while h:
176 d, n = heapq.heappop(h)
176 d, n = heapq.heappop(h)
177 if n not in seen:
177 if n not in seen:
178 seen.add(n)
178 seen.add(n)
179 yield (d, n)
179 yield (d, n)
180 for p in parentcache[n]:
180 for p in parentcache[n]:
181 heapq.heappush(h, (depth[p], p))
181 heapq.heappush(h, (depth[p], p))
182
182
183 def generations(vertex):
183 def generations(vertex):
184 sg, s = None, set()
184 sg, s = None, set()
185 for g, v in ancestors(vertex):
185 for g, v in ancestors(vertex):
186 if g != sg:
186 if g != sg:
187 if sg:
187 if sg:
188 yield sg, s
188 yield sg, s
189 sg, s = g, set((v,))
189 sg, s = g, set((v,))
190 else:
190 else:
191 s.add(v)
191 s.add(v)
192 yield sg, s
192 yield sg, s
193
193
194 x = generations(a)
194 x = generations(a)
195 y = generations(b)
195 y = generations(b)
196 gx = x.next()
196 gx = x.next()
197 gy = y.next()
197 gy = y.next()
198
198
199 # increment each ancestor list until it is closer to root than
199 # increment each ancestor list until it is closer to root than
200 # the other, or they match
200 # the other, or they match
201 try:
201 try:
202 while True:
202 while True:
203 if gx[0] == gy[0]:
203 if gx[0] == gy[0]:
204 for v in gx[1]:
204 for v in gx[1]:
205 if v in gy[1]:
205 if v in gy[1]:
206 return v
206 return v
207 gy = y.next()
207 gy = y.next()
208 gx = x.next()
208 gx = x.next()
209 elif gx[0] > gy[0]:
209 elif gx[0] > gy[0]:
210 gy = y.next()
210 gy = y.next()
211 else:
211 else:
212 gx = x.next()
212 gx = x.next()
213 except StopIteration:
213 except StopIteration:
214 return None
214 return None
215
215
216 def finddepths(nodes, pfunc):
217 visit = list(nodes)
218 rootpl = [nullrev, nullrev]
219 depth = {}
220 while visit:
221 vertex = visit[-1]
222 pl = pfunc(vertex)
223 if not pl or pl == rootpl:
224 depth[vertex] = 0
225 visit.pop()
226 else:
227 for p in pl:
228 if p != nullrev and p not in depth:
229 visit.append(p)
230 if visit[-1] == vertex:
231 dp = [depth[p] for p in pl if p != nullrev]
232 if dp:
233 depth[vertex] = max(dp) + 1
234 else:
235 depth[vertex] = 0
236 visit.pop()
237 return depth
238
239 def ancestor(a, b, pfunc):
240 xs = ancestors(pfunc, a, b)
241 y = genericancestor(a, b, pfunc)
242 if y == -1:
243 y = None
244 if not xs:
245 if y is None:
246 return None
247 print xs, y
248 raise error.RepoError('ancestors disagree on whether a gca exists')
249 elif y is None:
250 print xs, y
251 raise error.RepoError('ancestors disagree on whether a gca exists')
252 if y in xs:
253 return y
254 xds = finddepths(xs, pfunc)
255 xds = [ds[x] for x in xs]
256 yd = finddepths([y], pfunc)[y]
257 if len([xd != yd for xd in xds]) > 0:
258 raise error.RepoError('ancestor depths do not match')
259 return xs.pop()
260
261 def missingancestors(revs, bases, pfunc):
216 def missingancestors(revs, bases, pfunc):
262 """Return all the ancestors of revs that are not ancestors of bases.
217 """Return all the ancestors of revs that are not ancestors of bases.
263
218
264 This may include elements from revs.
219 This may include elements from revs.
265
220
266 Equivalent to the revset (::revs - ::bases). Revs are returned in
221 Equivalent to the revset (::revs - ::bases). Revs are returned in
267 revision number order, which is a topological order.
222 revision number order, which is a topological order.
268
223
269 revs and bases should both be iterables. pfunc must return a list of
224 revs and bases should both be iterables. pfunc must return a list of
270 parent revs for a given revs.
225 parent revs for a given revs.
271 """
226 """
272
227
273 revsvisit = set(revs)
228 revsvisit = set(revs)
274 basesvisit = set(bases)
229 basesvisit = set(bases)
275 if not revsvisit:
230 if not revsvisit:
276 return []
231 return []
277 if not basesvisit:
232 if not basesvisit:
278 basesvisit.add(nullrev)
233 basesvisit.add(nullrev)
279 start = max(max(revsvisit), max(basesvisit))
234 start = max(max(revsvisit), max(basesvisit))
280 bothvisit = revsvisit.intersection(basesvisit)
235 bothvisit = revsvisit.intersection(basesvisit)
281 revsvisit.difference_update(bothvisit)
236 revsvisit.difference_update(bothvisit)
282 basesvisit.difference_update(bothvisit)
237 basesvisit.difference_update(bothvisit)
283 # At this point, we hold the invariants that:
238 # At this point, we hold the invariants that:
284 # - revsvisit is the set of nodes we know are an ancestor of at least one
239 # - revsvisit is the set of nodes we know are an ancestor of at least one
285 # of the nodes in revs
240 # of the nodes in revs
286 # - basesvisit is the same for bases
241 # - basesvisit is the same for bases
287 # - bothvisit is the set of nodes we know are ancestors of at least one of
242 # - bothvisit is the set of nodes we know are ancestors of at least one of
288 # the nodes in revs and one of the nodes in bases
243 # the nodes in revs and one of the nodes in bases
289 # - a node may be in none or one, but not more, of revsvisit, basesvisit
244 # - a node may be in none or one, but not more, of revsvisit, basesvisit
290 # and bothvisit at any given time
245 # and bothvisit at any given time
291 # Now we walk down in reverse topo order, adding parents of nodes already
246 # Now we walk down in reverse topo order, adding parents of nodes already
292 # visited to the sets while maintaining the invariants. When a node is
247 # visited to the sets while maintaining the invariants. When a node is
293 # found in both revsvisit and basesvisit, it is removed from them and
248 # found in both revsvisit and basesvisit, it is removed from them and
294 # added to bothvisit instead. When revsvisit becomes empty, there are no
249 # added to bothvisit instead. When revsvisit becomes empty, there are no
295 # more ancestors of revs that aren't also ancestors of bases, so exit.
250 # more ancestors of revs that aren't also ancestors of bases, so exit.
296
251
297 missing = []
252 missing = []
298 for curr in xrange(start, nullrev, -1):
253 for curr in xrange(start, nullrev, -1):
299 if not revsvisit:
254 if not revsvisit:
300 break
255 break
301
256
302 if curr in bothvisit:
257 if curr in bothvisit:
303 bothvisit.remove(curr)
258 bothvisit.remove(curr)
304 # curr's parents might have made it into revsvisit or basesvisit
259 # curr's parents might have made it into revsvisit or basesvisit
305 # through another path
260 # through another path
306 for p in pfunc(curr):
261 for p in pfunc(curr):
307 revsvisit.discard(p)
262 revsvisit.discard(p)
308 basesvisit.discard(p)
263 basesvisit.discard(p)
309 bothvisit.add(p)
264 bothvisit.add(p)
310 continue
265 continue
311
266
312 # curr will never be in both revsvisit and basesvisit, since if it
267 # curr will never be in both revsvisit and basesvisit, since if it
313 # were it'd have been pushed to bothvisit
268 # were it'd have been pushed to bothvisit
314 if curr in revsvisit:
269 if curr in revsvisit:
315 missing.append(curr)
270 missing.append(curr)
316 thisvisit = revsvisit
271 thisvisit = revsvisit
317 othervisit = basesvisit
272 othervisit = basesvisit
318 elif curr in basesvisit:
273 elif curr in basesvisit:
319 thisvisit = basesvisit
274 thisvisit = basesvisit
320 othervisit = revsvisit
275 othervisit = revsvisit
321 else:
276 else:
322 # not an ancestor of revs or bases: ignore
277 # not an ancestor of revs or bases: ignore
323 continue
278 continue
324
279
325 thisvisit.remove(curr)
280 thisvisit.remove(curr)
326 for p in pfunc(curr):
281 for p in pfunc(curr):
327 if p == nullrev:
282 if p == nullrev:
328 pass
283 pass
329 elif p in othervisit or p in bothvisit:
284 elif p in othervisit or p in bothvisit:
330 # p is implicitly in thisvisit. This means p is or should be
285 # p is implicitly in thisvisit. This means p is or should be
331 # in bothvisit
286 # in bothvisit
332 revsvisit.discard(p)
287 revsvisit.discard(p)
333 basesvisit.discard(p)
288 basesvisit.discard(p)
334 bothvisit.add(p)
289 bothvisit.add(p)
335 else:
290 else:
336 # visit later
291 # visit later
337 thisvisit.add(p)
292 thisvisit.add(p)
338
293
339 missing.reverse()
294 missing.reverse()
340 return missing
295 return missing
341
296
342 class lazyancestors(object):
297 class lazyancestors(object):
343 def __init__(self, cl, revs, stoprev=0, inclusive=False):
298 def __init__(self, cl, revs, stoprev=0, inclusive=False):
344 """Create a new object generating ancestors for the given revs. Does
299 """Create a new object generating ancestors for the given revs. Does
345 not generate revs lower than stoprev.
300 not generate revs lower than stoprev.
346
301
347 This is computed lazily starting from revs. The object supports
302 This is computed lazily starting from revs. The object supports
348 iteration and membership.
303 iteration and membership.
349
304
350 cl should be a changelog and revs should be an iterable. inclusive is
305 cl should be a changelog and revs should be an iterable. inclusive is
351 a boolean that indicates whether revs should be included. Revs lower
306 a boolean that indicates whether revs should be included. Revs lower
352 than stoprev will not be generated.
307 than stoprev will not be generated.
353
308
354 Result does not include the null revision."""
309 Result does not include the null revision."""
355 self._parentrevs = cl.parentrevs
310 self._parentrevs = cl.parentrevs
356 self._initrevs = revs
311 self._initrevs = revs
357 self._stoprev = stoprev
312 self._stoprev = stoprev
358 self._inclusive = inclusive
313 self._inclusive = inclusive
359
314
360 # Initialize data structures for __contains__.
315 # Initialize data structures for __contains__.
361 # For __contains__, we use a heap rather than a deque because
316 # For __contains__, we use a heap rather than a deque because
362 # (a) it minimizes the number of parentrevs calls made
317 # (a) it minimizes the number of parentrevs calls made
363 # (b) it makes the loop termination condition obvious
318 # (b) it makes the loop termination condition obvious
364 # Python's heap is a min-heap. Multiply all values by -1 to convert it
319 # Python's heap is a min-heap. Multiply all values by -1 to convert it
365 # into a max-heap.
320 # into a max-heap.
366 self._containsvisit = [-rev for rev in revs]
321 self._containsvisit = [-rev for rev in revs]
367 heapq.heapify(self._containsvisit)
322 heapq.heapify(self._containsvisit)
368 if inclusive:
323 if inclusive:
369 self._containsseen = set(revs)
324 self._containsseen = set(revs)
370 else:
325 else:
371 self._containsseen = set()
326 self._containsseen = set()
372
327
373 def __iter__(self):
328 def __iter__(self):
374 """Generate the ancestors of _initrevs in reverse topological order.
329 """Generate the ancestors of _initrevs in reverse topological order.
375
330
376 If inclusive is False, yield a sequence of revision numbers starting
331 If inclusive is False, yield a sequence of revision numbers starting
377 with the parents of each revision in revs, i.e., each revision is *not*
332 with the parents of each revision in revs, i.e., each revision is *not*
378 considered an ancestor of itself. Results are in breadth-first order:
333 considered an ancestor of itself. Results are in breadth-first order:
379 parents of each rev in revs, then parents of those, etc.
334 parents of each rev in revs, then parents of those, etc.
380
335
381 If inclusive is True, yield all the revs first (ignoring stoprev),
336 If inclusive is True, yield all the revs first (ignoring stoprev),
382 then yield all the ancestors of revs as when inclusive is False.
337 then yield all the ancestors of revs as when inclusive is False.
383 If an element in revs is an ancestor of a different rev it is not
338 If an element in revs is an ancestor of a different rev it is not
384 yielded again."""
339 yielded again."""
385 seen = set()
340 seen = set()
386 revs = self._initrevs
341 revs = self._initrevs
387 if self._inclusive:
342 if self._inclusive:
388 for rev in revs:
343 for rev in revs:
389 yield rev
344 yield rev
390 seen.update(revs)
345 seen.update(revs)
391
346
392 parentrevs = self._parentrevs
347 parentrevs = self._parentrevs
393 stoprev = self._stoprev
348 stoprev = self._stoprev
394 visit = util.deque(revs)
349 visit = util.deque(revs)
395
350
396 while visit:
351 while visit:
397 for parent in parentrevs(visit.popleft()):
352 for parent in parentrevs(visit.popleft()):
398 if parent >= stoprev and parent not in seen:
353 if parent >= stoprev and parent not in seen:
399 visit.append(parent)
354 visit.append(parent)
400 seen.add(parent)
355 seen.add(parent)
401 yield parent
356 yield parent
402
357
403 def __contains__(self, target):
358 def __contains__(self, target):
404 """Test whether target is an ancestor of self._initrevs."""
359 """Test whether target is an ancestor of self._initrevs."""
405 # Trying to do both __iter__ and __contains__ using the same visit
360 # Trying to do both __iter__ and __contains__ using the same visit
406 # heap and seen set is complex enough that it slows down both. Keep
361 # heap and seen set is complex enough that it slows down both. Keep
407 # them separate.
362 # them separate.
408 seen = self._containsseen
363 seen = self._containsseen
409 if target in seen:
364 if target in seen:
410 return True
365 return True
411
366
412 parentrevs = self._parentrevs
367 parentrevs = self._parentrevs
413 visit = self._containsvisit
368 visit = self._containsvisit
414 stoprev = self._stoprev
369 stoprev = self._stoprev
415 heappop = heapq.heappop
370 heappop = heapq.heappop
416 heappush = heapq.heappush
371 heappush = heapq.heappush
417
372
418 targetseen = False
373 targetseen = False
419
374
420 while visit and -visit[0] > target and not targetseen:
375 while visit and -visit[0] > target and not targetseen:
421 for parent in parentrevs(-heappop(visit)):
376 for parent in parentrevs(-heappop(visit)):
422 if parent < stoprev or parent in seen:
377 if parent < stoprev or parent in seen:
423 continue
378 continue
424 # We need to make sure we push all parents into the heap so
379 # We need to make sure we push all parents into the heap so
425 # that we leave it in a consistent state for future calls.
380 # that we leave it in a consistent state for future calls.
426 heappush(visit, -parent)
381 heappush(visit, -parent)
427 seen.add(parent)
382 seen.add(parent)
428 if parent == target:
383 if parent == target:
429 targetseen = True
384 targetseen = True
430
385
431 return targetseen
386 return targetseen
@@ -1,1342 +1,1337
1 # revlog.py - storage back-end for mercurial
1 # revlog.py - storage back-end for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 """Storage back-end for Mercurial.
8 """Storage back-end for Mercurial.
9
9
10 This provides efficient delta storage with O(1) retrieve and append
10 This provides efficient delta storage with O(1) retrieve and append
11 and O(changes) merge between branches.
11 and O(changes) merge between branches.
12 """
12 """
13
13
14 # import stuff from node for others to import from revlog
14 # import stuff from node for others to import from revlog
15 from node import bin, hex, nullid, nullrev
15 from node import bin, hex, nullid, nullrev
16 from i18n import _
16 from i18n import _
17 import ancestor, mdiff, parsers, error, util, dagutil
17 import ancestor, mdiff, parsers, error, util, dagutil
18 import struct, zlib, errno
18 import struct, zlib, errno
19
19
20 _pack = struct.pack
20 _pack = struct.pack
21 _unpack = struct.unpack
21 _unpack = struct.unpack
22 _compress = zlib.compress
22 _compress = zlib.compress
23 _decompress = zlib.decompress
23 _decompress = zlib.decompress
24 _sha = util.sha1
24 _sha = util.sha1
25
25
26 # revlog header flags
26 # revlog header flags
27 REVLOGV0 = 0
27 REVLOGV0 = 0
28 REVLOGNG = 1
28 REVLOGNG = 1
29 REVLOGNGINLINEDATA = (1 << 16)
29 REVLOGNGINLINEDATA = (1 << 16)
30 REVLOGGENERALDELTA = (1 << 17)
30 REVLOGGENERALDELTA = (1 << 17)
31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
31 REVLOG_DEFAULT_FLAGS = REVLOGNGINLINEDATA
32 REVLOG_DEFAULT_FORMAT = REVLOGNG
32 REVLOG_DEFAULT_FORMAT = REVLOGNG
33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
33 REVLOG_DEFAULT_VERSION = REVLOG_DEFAULT_FORMAT | REVLOG_DEFAULT_FLAGS
34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGGENERALDELTA
34 REVLOGNG_FLAGS = REVLOGNGINLINEDATA | REVLOGGENERALDELTA
35
35
36 # revlog index flags
36 # revlog index flags
37 REVIDX_KNOWN_FLAGS = 0
37 REVIDX_KNOWN_FLAGS = 0
38
38
39 # max size of revlog with inline data
39 # max size of revlog with inline data
40 _maxinline = 131072
40 _maxinline = 131072
41 _chunksize = 1048576
41 _chunksize = 1048576
42
42
43 RevlogError = error.RevlogError
43 RevlogError = error.RevlogError
44 LookupError = error.LookupError
44 LookupError = error.LookupError
45
45
46 def getoffset(q):
46 def getoffset(q):
47 return int(q >> 16)
47 return int(q >> 16)
48
48
49 def gettype(q):
49 def gettype(q):
50 return int(q & 0xFFFF)
50 return int(q & 0xFFFF)
51
51
52 def offset_type(offset, type):
52 def offset_type(offset, type):
53 return long(long(offset) << 16 | type)
53 return long(long(offset) << 16 | type)
54
54
55 nullhash = _sha(nullid)
55 nullhash = _sha(nullid)
56
56
57 def hash(text, p1, p2):
57 def hash(text, p1, p2):
58 """generate a hash from the given text and its parent hashes
58 """generate a hash from the given text and its parent hashes
59
59
60 This hash combines both the current file contents and its history
60 This hash combines both the current file contents and its history
61 in a manner that makes it easy to distinguish nodes with the same
61 in a manner that makes it easy to distinguish nodes with the same
62 content in the revision graph.
62 content in the revision graph.
63 """
63 """
64 # As of now, if one of the parent node is null, p2 is null
64 # As of now, if one of the parent node is null, p2 is null
65 if p2 == nullid:
65 if p2 == nullid:
66 # deep copy of a hash is faster than creating one
66 # deep copy of a hash is faster than creating one
67 s = nullhash.copy()
67 s = nullhash.copy()
68 s.update(p1)
68 s.update(p1)
69 else:
69 else:
70 # none of the parent nodes are nullid
70 # none of the parent nodes are nullid
71 l = [p1, p2]
71 l = [p1, p2]
72 l.sort()
72 l.sort()
73 s = _sha(l[0])
73 s = _sha(l[0])
74 s.update(l[1])
74 s.update(l[1])
75 s.update(text)
75 s.update(text)
76 return s.digest()
76 return s.digest()
77
77
78 def decompress(bin):
78 def decompress(bin):
79 """ decompress the given input """
79 """ decompress the given input """
80 if not bin:
80 if not bin:
81 return bin
81 return bin
82 t = bin[0]
82 t = bin[0]
83 if t == '\0':
83 if t == '\0':
84 return bin
84 return bin
85 if t == 'x':
85 if t == 'x':
86 try:
86 try:
87 return _decompress(bin)
87 return _decompress(bin)
88 except zlib.error, e:
88 except zlib.error, e:
89 raise RevlogError(_("revlog decompress error: %s") % str(e))
89 raise RevlogError(_("revlog decompress error: %s") % str(e))
90 if t == 'u':
90 if t == 'u':
91 return bin[1:]
91 return bin[1:]
92 raise RevlogError(_("unknown compression type %r") % t)
92 raise RevlogError(_("unknown compression type %r") % t)
93
93
94 # index v0:
94 # index v0:
95 # 4 bytes: offset
95 # 4 bytes: offset
96 # 4 bytes: compressed length
96 # 4 bytes: compressed length
97 # 4 bytes: base rev
97 # 4 bytes: base rev
98 # 4 bytes: link rev
98 # 4 bytes: link rev
99 # 32 bytes: parent 1 nodeid
99 # 32 bytes: parent 1 nodeid
100 # 32 bytes: parent 2 nodeid
100 # 32 bytes: parent 2 nodeid
101 # 32 bytes: nodeid
101 # 32 bytes: nodeid
102 indexformatv0 = ">4l20s20s20s"
102 indexformatv0 = ">4l20s20s20s"
103 v0shaoffset = 56
103 v0shaoffset = 56
104
104
105 class revlogoldio(object):
105 class revlogoldio(object):
106 def __init__(self):
106 def __init__(self):
107 self.size = struct.calcsize(indexformatv0)
107 self.size = struct.calcsize(indexformatv0)
108
108
109 def parseindex(self, data, inline):
109 def parseindex(self, data, inline):
110 s = self.size
110 s = self.size
111 index = []
111 index = []
112 nodemap = {nullid: nullrev}
112 nodemap = {nullid: nullrev}
113 n = off = 0
113 n = off = 0
114 l = len(data)
114 l = len(data)
115 while off + s <= l:
115 while off + s <= l:
116 cur = data[off:off + s]
116 cur = data[off:off + s]
117 off += s
117 off += s
118 e = _unpack(indexformatv0, cur)
118 e = _unpack(indexformatv0, cur)
119 # transform to revlogv1 format
119 # transform to revlogv1 format
120 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
120 e2 = (offset_type(e[0], 0), e[1], -1, e[2], e[3],
121 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
121 nodemap.get(e[4], nullrev), nodemap.get(e[5], nullrev), e[6])
122 index.append(e2)
122 index.append(e2)
123 nodemap[e[6]] = n
123 nodemap[e[6]] = n
124 n += 1
124 n += 1
125
125
126 # add the magic null revision at -1
126 # add the magic null revision at -1
127 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
127 index.append((0, 0, 0, -1, -1, -1, -1, nullid))
128
128
129 return index, nodemap, None
129 return index, nodemap, None
130
130
131 def packentry(self, entry, node, version, rev):
131 def packentry(self, entry, node, version, rev):
132 if gettype(entry[0]):
132 if gettype(entry[0]):
133 raise RevlogError(_("index entry flags need RevlogNG"))
133 raise RevlogError(_("index entry flags need RevlogNG"))
134 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
134 e2 = (getoffset(entry[0]), entry[1], entry[3], entry[4],
135 node(entry[5]), node(entry[6]), entry[7])
135 node(entry[5]), node(entry[6]), entry[7])
136 return _pack(indexformatv0, *e2)
136 return _pack(indexformatv0, *e2)
137
137
138 # index ng:
138 # index ng:
139 # 6 bytes: offset
139 # 6 bytes: offset
140 # 2 bytes: flags
140 # 2 bytes: flags
141 # 4 bytes: compressed length
141 # 4 bytes: compressed length
142 # 4 bytes: uncompressed length
142 # 4 bytes: uncompressed length
143 # 4 bytes: base rev
143 # 4 bytes: base rev
144 # 4 bytes: link rev
144 # 4 bytes: link rev
145 # 4 bytes: parent 1 rev
145 # 4 bytes: parent 1 rev
146 # 4 bytes: parent 2 rev
146 # 4 bytes: parent 2 rev
147 # 32 bytes: nodeid
147 # 32 bytes: nodeid
148 indexformatng = ">Qiiiiii20s12x"
148 indexformatng = ">Qiiiiii20s12x"
149 ngshaoffset = 32
149 ngshaoffset = 32
150 versionformat = ">I"
150 versionformat = ">I"
151
151
152 class revlogio(object):
152 class revlogio(object):
153 def __init__(self):
153 def __init__(self):
154 self.size = struct.calcsize(indexformatng)
154 self.size = struct.calcsize(indexformatng)
155
155
156 def parseindex(self, data, inline):
156 def parseindex(self, data, inline):
157 # call the C implementation to parse the index data
157 # call the C implementation to parse the index data
158 index, cache = parsers.parse_index2(data, inline)
158 index, cache = parsers.parse_index2(data, inline)
159 return index, getattr(index, 'nodemap', None), cache
159 return index, getattr(index, 'nodemap', None), cache
160
160
161 def packentry(self, entry, node, version, rev):
161 def packentry(self, entry, node, version, rev):
162 p = _pack(indexformatng, *entry)
162 p = _pack(indexformatng, *entry)
163 if rev == 0:
163 if rev == 0:
164 p = _pack(versionformat, version) + p[4:]
164 p = _pack(versionformat, version) + p[4:]
165 return p
165 return p
166
166
167 class revlog(object):
167 class revlog(object):
168 """
168 """
169 the underlying revision storage object
169 the underlying revision storage object
170
170
171 A revlog consists of two parts, an index and the revision data.
171 A revlog consists of two parts, an index and the revision data.
172
172
173 The index is a file with a fixed record size containing
173 The index is a file with a fixed record size containing
174 information on each revision, including its nodeid (hash), the
174 information on each revision, including its nodeid (hash), the
175 nodeids of its parents, the position and offset of its data within
175 nodeids of its parents, the position and offset of its data within
176 the data file, and the revision it's based on. Finally, each entry
176 the data file, and the revision it's based on. Finally, each entry
177 contains a linkrev entry that can serve as a pointer to external
177 contains a linkrev entry that can serve as a pointer to external
178 data.
178 data.
179
179
180 The revision data itself is a linear collection of data chunks.
180 The revision data itself is a linear collection of data chunks.
181 Each chunk represents a revision and is usually represented as a
181 Each chunk represents a revision and is usually represented as a
182 delta against the previous chunk. To bound lookup time, runs of
182 delta against the previous chunk. To bound lookup time, runs of
183 deltas are limited to about 2 times the length of the original
183 deltas are limited to about 2 times the length of the original
184 version data. This makes retrieval of a version proportional to
184 version data. This makes retrieval of a version proportional to
185 its size, or O(1) relative to the number of revisions.
185 its size, or O(1) relative to the number of revisions.
186
186
187 Both pieces of the revlog are written to in an append-only
187 Both pieces of the revlog are written to in an append-only
188 fashion, which means we never need to rewrite a file to insert or
188 fashion, which means we never need to rewrite a file to insert or
189 remove data, and can use some simple techniques to avoid the need
189 remove data, and can use some simple techniques to avoid the need
190 for locking while reading.
190 for locking while reading.
191 """
191 """
192 def __init__(self, opener, indexfile):
192 def __init__(self, opener, indexfile):
193 """
193 """
194 create a revlog object
194 create a revlog object
195
195
196 opener is a function that abstracts the file opening operation
196 opener is a function that abstracts the file opening operation
197 and can be used to implement COW semantics or the like.
197 and can be used to implement COW semantics or the like.
198 """
198 """
199 self.indexfile = indexfile
199 self.indexfile = indexfile
200 self.datafile = indexfile[:-2] + ".d"
200 self.datafile = indexfile[:-2] + ".d"
201 self.opener = opener
201 self.opener = opener
202 self._cache = None
202 self._cache = None
203 self._basecache = (0, 0)
203 self._basecache = (0, 0)
204 self._chunkcache = (0, '')
204 self._chunkcache = (0, '')
205 self.index = []
205 self.index = []
206 self._pcache = {}
206 self._pcache = {}
207 self._nodecache = {nullid: nullrev}
207 self._nodecache = {nullid: nullrev}
208 self._nodepos = None
208 self._nodepos = None
209
209
210 v = REVLOG_DEFAULT_VERSION
210 v = REVLOG_DEFAULT_VERSION
211 opts = getattr(opener, 'options', None)
211 opts = getattr(opener, 'options', None)
212 if opts is not None:
212 if opts is not None:
213 if 'revlogv1' in opts:
213 if 'revlogv1' in opts:
214 if 'generaldelta' in opts:
214 if 'generaldelta' in opts:
215 v |= REVLOGGENERALDELTA
215 v |= REVLOGGENERALDELTA
216 else:
216 else:
217 v = 0
217 v = 0
218
218
219 i = ''
219 i = ''
220 self._initempty = True
220 self._initempty = True
221 try:
221 try:
222 f = self.opener(self.indexfile)
222 f = self.opener(self.indexfile)
223 i = f.read()
223 i = f.read()
224 f.close()
224 f.close()
225 if len(i) > 0:
225 if len(i) > 0:
226 v = struct.unpack(versionformat, i[:4])[0]
226 v = struct.unpack(versionformat, i[:4])[0]
227 self._initempty = False
227 self._initempty = False
228 except IOError, inst:
228 except IOError, inst:
229 if inst.errno != errno.ENOENT:
229 if inst.errno != errno.ENOENT:
230 raise
230 raise
231
231
232 self.version = v
232 self.version = v
233 self._inline = v & REVLOGNGINLINEDATA
233 self._inline = v & REVLOGNGINLINEDATA
234 self._generaldelta = v & REVLOGGENERALDELTA
234 self._generaldelta = v & REVLOGGENERALDELTA
235 flags = v & ~0xFFFF
235 flags = v & ~0xFFFF
236 fmt = v & 0xFFFF
236 fmt = v & 0xFFFF
237 if fmt == REVLOGV0 and flags:
237 if fmt == REVLOGV0 and flags:
238 raise RevlogError(_("index %s unknown flags %#04x for format v0")
238 raise RevlogError(_("index %s unknown flags %#04x for format v0")
239 % (self.indexfile, flags >> 16))
239 % (self.indexfile, flags >> 16))
240 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
240 elif fmt == REVLOGNG and flags & ~REVLOGNG_FLAGS:
241 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
241 raise RevlogError(_("index %s unknown flags %#04x for revlogng")
242 % (self.indexfile, flags >> 16))
242 % (self.indexfile, flags >> 16))
243 elif fmt > REVLOGNG:
243 elif fmt > REVLOGNG:
244 raise RevlogError(_("index %s unknown format %d")
244 raise RevlogError(_("index %s unknown format %d")
245 % (self.indexfile, fmt))
245 % (self.indexfile, fmt))
246
246
247 self._io = revlogio()
247 self._io = revlogio()
248 if self.version == REVLOGV0:
248 if self.version == REVLOGV0:
249 self._io = revlogoldio()
249 self._io = revlogoldio()
250 try:
250 try:
251 d = self._io.parseindex(i, self._inline)
251 d = self._io.parseindex(i, self._inline)
252 except (ValueError, IndexError):
252 except (ValueError, IndexError):
253 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
253 raise RevlogError(_("index %s is corrupted") % (self.indexfile))
254 self.index, nodemap, self._chunkcache = d
254 self.index, nodemap, self._chunkcache = d
255 if nodemap is not None:
255 if nodemap is not None:
256 self.nodemap = self._nodecache = nodemap
256 self.nodemap = self._nodecache = nodemap
257 if not self._chunkcache:
257 if not self._chunkcache:
258 self._chunkclear()
258 self._chunkclear()
259
259
260 def tip(self):
260 def tip(self):
261 return self.node(len(self.index) - 2)
261 return self.node(len(self.index) - 2)
262 def __len__(self):
262 def __len__(self):
263 return len(self.index) - 1
263 return len(self.index) - 1
264 def __iter__(self):
264 def __iter__(self):
265 return iter(xrange(len(self)))
265 return iter(xrange(len(self)))
266 def revs(self, start=0, stop=None):
266 def revs(self, start=0, stop=None):
267 """iterate over all rev in this revlog (from start to stop)"""
267 """iterate over all rev in this revlog (from start to stop)"""
268 step = 1
268 step = 1
269 if stop is not None:
269 if stop is not None:
270 if start > stop:
270 if start > stop:
271 step = -1
271 step = -1
272 stop += step
272 stop += step
273 else:
273 else:
274 stop = len(self)
274 stop = len(self)
275 return xrange(start, stop, step)
275 return xrange(start, stop, step)
276
276
277 @util.propertycache
277 @util.propertycache
278 def nodemap(self):
278 def nodemap(self):
279 self.rev(self.node(0))
279 self.rev(self.node(0))
280 return self._nodecache
280 return self._nodecache
281
281
282 def hasnode(self, node):
282 def hasnode(self, node):
283 try:
283 try:
284 self.rev(node)
284 self.rev(node)
285 return True
285 return True
286 except KeyError:
286 except KeyError:
287 return False
287 return False
288
288
289 def clearcaches(self):
289 def clearcaches(self):
290 try:
290 try:
291 self._nodecache.clearcaches()
291 self._nodecache.clearcaches()
292 except AttributeError:
292 except AttributeError:
293 self._nodecache = {nullid: nullrev}
293 self._nodecache = {nullid: nullrev}
294 self._nodepos = None
294 self._nodepos = None
295
295
296 def rev(self, node):
296 def rev(self, node):
297 try:
297 try:
298 return self._nodecache[node]
298 return self._nodecache[node]
299 except RevlogError:
299 except RevlogError:
300 # parsers.c radix tree lookup failed
300 # parsers.c radix tree lookup failed
301 raise LookupError(node, self.indexfile, _('no node'))
301 raise LookupError(node, self.indexfile, _('no node'))
302 except KeyError:
302 except KeyError:
303 # pure python cache lookup failed
303 # pure python cache lookup failed
304 n = self._nodecache
304 n = self._nodecache
305 i = self.index
305 i = self.index
306 p = self._nodepos
306 p = self._nodepos
307 if p is None:
307 if p is None:
308 p = len(i) - 2
308 p = len(i) - 2
309 for r in xrange(p, -1, -1):
309 for r in xrange(p, -1, -1):
310 v = i[r][7]
310 v = i[r][7]
311 n[v] = r
311 n[v] = r
312 if v == node:
312 if v == node:
313 self._nodepos = r - 1
313 self._nodepos = r - 1
314 return r
314 return r
315 raise LookupError(node, self.indexfile, _('no node'))
315 raise LookupError(node, self.indexfile, _('no node'))
316
316
317 def node(self, rev):
317 def node(self, rev):
318 return self.index[rev][7]
318 return self.index[rev][7]
319 def linkrev(self, rev):
319 def linkrev(self, rev):
320 return self.index[rev][4]
320 return self.index[rev][4]
321 def parents(self, node):
321 def parents(self, node):
322 i = self.index
322 i = self.index
323 d = i[self.rev(node)]
323 d = i[self.rev(node)]
324 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
324 return i[d[5]][7], i[d[6]][7] # map revisions to nodes inline
325 def parentrevs(self, rev):
325 def parentrevs(self, rev):
326 return self.index[rev][5:7]
326 return self.index[rev][5:7]
327 def start(self, rev):
327 def start(self, rev):
328 return int(self.index[rev][0] >> 16)
328 return int(self.index[rev][0] >> 16)
329 def end(self, rev):
329 def end(self, rev):
330 return self.start(rev) + self.length(rev)
330 return self.start(rev) + self.length(rev)
331 def length(self, rev):
331 def length(self, rev):
332 return self.index[rev][1]
332 return self.index[rev][1]
333 def chainbase(self, rev):
333 def chainbase(self, rev):
334 index = self.index
334 index = self.index
335 base = index[rev][3]
335 base = index[rev][3]
336 while base != rev:
336 while base != rev:
337 rev = base
337 rev = base
338 base = index[rev][3]
338 base = index[rev][3]
339 return base
339 return base
340 def flags(self, rev):
340 def flags(self, rev):
341 return self.index[rev][0] & 0xFFFF
341 return self.index[rev][0] & 0xFFFF
342 def rawsize(self, rev):
342 def rawsize(self, rev):
343 """return the length of the uncompressed text for a given revision"""
343 """return the length of the uncompressed text for a given revision"""
344 l = self.index[rev][2]
344 l = self.index[rev][2]
345 if l >= 0:
345 if l >= 0:
346 return l
346 return l
347
347
348 t = self.revision(self.node(rev))
348 t = self.revision(self.node(rev))
349 return len(t)
349 return len(t)
350 size = rawsize
350 size = rawsize
351
351
352 def ancestors(self, revs, stoprev=0, inclusive=False):
352 def ancestors(self, revs, stoprev=0, inclusive=False):
353 """Generate the ancestors of 'revs' in reverse topological order.
353 """Generate the ancestors of 'revs' in reverse topological order.
354 Does not generate revs lower than stoprev.
354 Does not generate revs lower than stoprev.
355
355
356 See the documentation for ancestor.lazyancestors for more details."""
356 See the documentation for ancestor.lazyancestors for more details."""
357
357
358 return ancestor.lazyancestors(self, revs, stoprev=stoprev,
358 return ancestor.lazyancestors(self, revs, stoprev=stoprev,
359 inclusive=inclusive)
359 inclusive=inclusive)
360
360
361 def descendants(self, revs):
361 def descendants(self, revs):
362 """Generate the descendants of 'revs' in revision order.
362 """Generate the descendants of 'revs' in revision order.
363
363
364 Yield a sequence of revision numbers starting with a child of
364 Yield a sequence of revision numbers starting with a child of
365 some rev in revs, i.e., each revision is *not* considered a
365 some rev in revs, i.e., each revision is *not* considered a
366 descendant of itself. Results are ordered by revision number (a
366 descendant of itself. Results are ordered by revision number (a
367 topological sort)."""
367 topological sort)."""
368 first = min(revs)
368 first = min(revs)
369 if first == nullrev:
369 if first == nullrev:
370 for i in self:
370 for i in self:
371 yield i
371 yield i
372 return
372 return
373
373
374 seen = set(revs)
374 seen = set(revs)
375 for i in self.revs(start=first + 1):
375 for i in self.revs(start=first + 1):
376 for x in self.parentrevs(i):
376 for x in self.parentrevs(i):
377 if x != nullrev and x in seen:
377 if x != nullrev and x in seen:
378 seen.add(i)
378 seen.add(i)
379 yield i
379 yield i
380 break
380 break
381
381
382 def findcommonmissing(self, common=None, heads=None):
382 def findcommonmissing(self, common=None, heads=None):
383 """Return a tuple of the ancestors of common and the ancestors of heads
383 """Return a tuple of the ancestors of common and the ancestors of heads
384 that are not ancestors of common. In revset terminology, we return the
384 that are not ancestors of common. In revset terminology, we return the
385 tuple:
385 tuple:
386
386
387 ::common, (::heads) - (::common)
387 ::common, (::heads) - (::common)
388
388
389 The list is sorted by revision number, meaning it is
389 The list is sorted by revision number, meaning it is
390 topologically sorted.
390 topologically sorted.
391
391
392 'heads' and 'common' are both lists of node IDs. If heads is
392 'heads' and 'common' are both lists of node IDs. If heads is
393 not supplied, uses all of the revlog's heads. If common is not
393 not supplied, uses all of the revlog's heads. If common is not
394 supplied, uses nullid."""
394 supplied, uses nullid."""
395 if common is None:
395 if common is None:
396 common = [nullid]
396 common = [nullid]
397 if heads is None:
397 if heads is None:
398 heads = self.heads()
398 heads = self.heads()
399
399
400 common = [self.rev(n) for n in common]
400 common = [self.rev(n) for n in common]
401 heads = [self.rev(n) for n in heads]
401 heads = [self.rev(n) for n in heads]
402
402
403 # we want the ancestors, but inclusive
403 # we want the ancestors, but inclusive
404 has = set(self.ancestors(common))
404 has = set(self.ancestors(common))
405 has.add(nullrev)
405 has.add(nullrev)
406 has.update(common)
406 has.update(common)
407
407
408 # take all ancestors from heads that aren't in has
408 # take all ancestors from heads that aren't in has
409 missing = set()
409 missing = set()
410 visit = util.deque(r for r in heads if r not in has)
410 visit = util.deque(r for r in heads if r not in has)
411 while visit:
411 while visit:
412 r = visit.popleft()
412 r = visit.popleft()
413 if r in missing:
413 if r in missing:
414 continue
414 continue
415 else:
415 else:
416 missing.add(r)
416 missing.add(r)
417 for p in self.parentrevs(r):
417 for p in self.parentrevs(r):
418 if p not in has:
418 if p not in has:
419 visit.append(p)
419 visit.append(p)
420 missing = list(missing)
420 missing = list(missing)
421 missing.sort()
421 missing.sort()
422 return has, [self.node(r) for r in missing]
422 return has, [self.node(r) for r in missing]
423
423
424 def findmissingrevs(self, common=None, heads=None):
424 def findmissingrevs(self, common=None, heads=None):
425 """Return the revision numbers of the ancestors of heads that
425 """Return the revision numbers of the ancestors of heads that
426 are not ancestors of common.
426 are not ancestors of common.
427
427
428 More specifically, return a list of revision numbers corresponding to
428 More specifically, return a list of revision numbers corresponding to
429 nodes N such that every N satisfies the following constraints:
429 nodes N such that every N satisfies the following constraints:
430
430
431 1. N is an ancestor of some node in 'heads'
431 1. N is an ancestor of some node in 'heads'
432 2. N is not an ancestor of any node in 'common'
432 2. N is not an ancestor of any node in 'common'
433
433
434 The list is sorted by revision number, meaning it is
434 The list is sorted by revision number, meaning it is
435 topologically sorted.
435 topologically sorted.
436
436
437 'heads' and 'common' are both lists of revision numbers. If heads is
437 'heads' and 'common' are both lists of revision numbers. If heads is
438 not supplied, uses all of the revlog's heads. If common is not
438 not supplied, uses all of the revlog's heads. If common is not
439 supplied, uses nullid."""
439 supplied, uses nullid."""
440 if common is None:
440 if common is None:
441 common = [nullrev]
441 common = [nullrev]
442 if heads is None:
442 if heads is None:
443 heads = self.headrevs()
443 heads = self.headrevs()
444
444
445 return ancestor.missingancestors(heads, common, self.parentrevs)
445 return ancestor.missingancestors(heads, common, self.parentrevs)
446
446
447 def findmissing(self, common=None, heads=None):
447 def findmissing(self, common=None, heads=None):
448 """Return the ancestors of heads that are not ancestors of common.
448 """Return the ancestors of heads that are not ancestors of common.
449
449
450 More specifically, return a list of nodes N such that every N
450 More specifically, return a list of nodes N such that every N
451 satisfies the following constraints:
451 satisfies the following constraints:
452
452
453 1. N is an ancestor of some node in 'heads'
453 1. N is an ancestor of some node in 'heads'
454 2. N is not an ancestor of any node in 'common'
454 2. N is not an ancestor of any node in 'common'
455
455
456 The list is sorted by revision number, meaning it is
456 The list is sorted by revision number, meaning it is
457 topologically sorted.
457 topologically sorted.
458
458
459 'heads' and 'common' are both lists of node IDs. If heads is
459 'heads' and 'common' are both lists of node IDs. If heads is
460 not supplied, uses all of the revlog's heads. If common is not
460 not supplied, uses all of the revlog's heads. If common is not
461 supplied, uses nullid."""
461 supplied, uses nullid."""
462 if common is None:
462 if common is None:
463 common = [nullid]
463 common = [nullid]
464 if heads is None:
464 if heads is None:
465 heads = self.heads()
465 heads = self.heads()
466
466
467 common = [self.rev(n) for n in common]
467 common = [self.rev(n) for n in common]
468 heads = [self.rev(n) for n in heads]
468 heads = [self.rev(n) for n in heads]
469
469
470 return [self.node(r) for r in
470 return [self.node(r) for r in
471 ancestor.missingancestors(heads, common, self.parentrevs)]
471 ancestor.missingancestors(heads, common, self.parentrevs)]
472
472
473 def nodesbetween(self, roots=None, heads=None):
473 def nodesbetween(self, roots=None, heads=None):
474 """Return a topological path from 'roots' to 'heads'.
474 """Return a topological path from 'roots' to 'heads'.
475
475
476 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
476 Return a tuple (nodes, outroots, outheads) where 'nodes' is a
477 topologically sorted list of all nodes N that satisfy both of
477 topologically sorted list of all nodes N that satisfy both of
478 these constraints:
478 these constraints:
479
479
480 1. N is a descendant of some node in 'roots'
480 1. N is a descendant of some node in 'roots'
481 2. N is an ancestor of some node in 'heads'
481 2. N is an ancestor of some node in 'heads'
482
482
483 Every node is considered to be both a descendant and an ancestor
483 Every node is considered to be both a descendant and an ancestor
484 of itself, so every reachable node in 'roots' and 'heads' will be
484 of itself, so every reachable node in 'roots' and 'heads' will be
485 included in 'nodes'.
485 included in 'nodes'.
486
486
487 'outroots' is the list of reachable nodes in 'roots', i.e., the
487 'outroots' is the list of reachable nodes in 'roots', i.e., the
488 subset of 'roots' that is returned in 'nodes'. Likewise,
488 subset of 'roots' that is returned in 'nodes'. Likewise,
489 'outheads' is the subset of 'heads' that is also in 'nodes'.
489 'outheads' is the subset of 'heads' that is also in 'nodes'.
490
490
491 'roots' and 'heads' are both lists of node IDs. If 'roots' is
491 'roots' and 'heads' are both lists of node IDs. If 'roots' is
492 unspecified, uses nullid as the only root. If 'heads' is
492 unspecified, uses nullid as the only root. If 'heads' is
493 unspecified, uses list of all of the revlog's heads."""
493 unspecified, uses list of all of the revlog's heads."""
494 nonodes = ([], [], [])
494 nonodes = ([], [], [])
495 if roots is not None:
495 if roots is not None:
496 roots = list(roots)
496 roots = list(roots)
497 if not roots:
497 if not roots:
498 return nonodes
498 return nonodes
499 lowestrev = min([self.rev(n) for n in roots])
499 lowestrev = min([self.rev(n) for n in roots])
500 else:
500 else:
501 roots = [nullid] # Everybody's a descendant of nullid
501 roots = [nullid] # Everybody's a descendant of nullid
502 lowestrev = nullrev
502 lowestrev = nullrev
503 if (lowestrev == nullrev) and (heads is None):
503 if (lowestrev == nullrev) and (heads is None):
504 # We want _all_ the nodes!
504 # We want _all_ the nodes!
505 return ([self.node(r) for r in self], [nullid], list(self.heads()))
505 return ([self.node(r) for r in self], [nullid], list(self.heads()))
506 if heads is None:
506 if heads is None:
507 # All nodes are ancestors, so the latest ancestor is the last
507 # All nodes are ancestors, so the latest ancestor is the last
508 # node.
508 # node.
509 highestrev = len(self) - 1
509 highestrev = len(self) - 1
510 # Set ancestors to None to signal that every node is an ancestor.
510 # Set ancestors to None to signal that every node is an ancestor.
511 ancestors = None
511 ancestors = None
512 # Set heads to an empty dictionary for later discovery of heads
512 # Set heads to an empty dictionary for later discovery of heads
513 heads = {}
513 heads = {}
514 else:
514 else:
515 heads = list(heads)
515 heads = list(heads)
516 if not heads:
516 if not heads:
517 return nonodes
517 return nonodes
518 ancestors = set()
518 ancestors = set()
519 # Turn heads into a dictionary so we can remove 'fake' heads.
519 # Turn heads into a dictionary so we can remove 'fake' heads.
520 # Also, later we will be using it to filter out the heads we can't
520 # Also, later we will be using it to filter out the heads we can't
521 # find from roots.
521 # find from roots.
522 heads = dict.fromkeys(heads, False)
522 heads = dict.fromkeys(heads, False)
523 # Start at the top and keep marking parents until we're done.
523 # Start at the top and keep marking parents until we're done.
524 nodestotag = set(heads)
524 nodestotag = set(heads)
525 # Remember where the top was so we can use it as a limit later.
525 # Remember where the top was so we can use it as a limit later.
526 highestrev = max([self.rev(n) for n in nodestotag])
526 highestrev = max([self.rev(n) for n in nodestotag])
527 while nodestotag:
527 while nodestotag:
528 # grab a node to tag
528 # grab a node to tag
529 n = nodestotag.pop()
529 n = nodestotag.pop()
530 # Never tag nullid
530 # Never tag nullid
531 if n == nullid:
531 if n == nullid:
532 continue
532 continue
533 # A node's revision number represents its place in a
533 # A node's revision number represents its place in a
534 # topologically sorted list of nodes.
534 # topologically sorted list of nodes.
535 r = self.rev(n)
535 r = self.rev(n)
536 if r >= lowestrev:
536 if r >= lowestrev:
537 if n not in ancestors:
537 if n not in ancestors:
538 # If we are possibly a descendant of one of the roots
538 # If we are possibly a descendant of one of the roots
539 # and we haven't already been marked as an ancestor
539 # and we haven't already been marked as an ancestor
540 ancestors.add(n) # Mark as ancestor
540 ancestors.add(n) # Mark as ancestor
541 # Add non-nullid parents to list of nodes to tag.
541 # Add non-nullid parents to list of nodes to tag.
542 nodestotag.update([p for p in self.parents(n) if
542 nodestotag.update([p for p in self.parents(n) if
543 p != nullid])
543 p != nullid])
544 elif n in heads: # We've seen it before, is it a fake head?
544 elif n in heads: # We've seen it before, is it a fake head?
545 # So it is, real heads should not be the ancestors of
545 # So it is, real heads should not be the ancestors of
546 # any other heads.
546 # any other heads.
547 heads.pop(n)
547 heads.pop(n)
548 if not ancestors:
548 if not ancestors:
549 return nonodes
549 return nonodes
550 # Now that we have our set of ancestors, we want to remove any
550 # Now that we have our set of ancestors, we want to remove any
551 # roots that are not ancestors.
551 # roots that are not ancestors.
552
552
553 # If one of the roots was nullid, everything is included anyway.
553 # If one of the roots was nullid, everything is included anyway.
554 if lowestrev > nullrev:
554 if lowestrev > nullrev:
555 # But, since we weren't, let's recompute the lowest rev to not
555 # But, since we weren't, let's recompute the lowest rev to not
556 # include roots that aren't ancestors.
556 # include roots that aren't ancestors.
557
557
558 # Filter out roots that aren't ancestors of heads
558 # Filter out roots that aren't ancestors of heads
559 roots = [n for n in roots if n in ancestors]
559 roots = [n for n in roots if n in ancestors]
560 # Recompute the lowest revision
560 # Recompute the lowest revision
561 if roots:
561 if roots:
562 lowestrev = min([self.rev(n) for n in roots])
562 lowestrev = min([self.rev(n) for n in roots])
563 else:
563 else:
564 # No more roots? Return empty list
564 # No more roots? Return empty list
565 return nonodes
565 return nonodes
566 else:
566 else:
567 # We are descending from nullid, and don't need to care about
567 # We are descending from nullid, and don't need to care about
568 # any other roots.
568 # any other roots.
569 lowestrev = nullrev
569 lowestrev = nullrev
570 roots = [nullid]
570 roots = [nullid]
571 # Transform our roots list into a set.
571 # Transform our roots list into a set.
572 descendants = set(roots)
572 descendants = set(roots)
573 # Also, keep the original roots so we can filter out roots that aren't
573 # Also, keep the original roots so we can filter out roots that aren't
574 # 'real' roots (i.e. are descended from other roots).
574 # 'real' roots (i.e. are descended from other roots).
575 roots = descendants.copy()
575 roots = descendants.copy()
576 # Our topologically sorted list of output nodes.
576 # Our topologically sorted list of output nodes.
577 orderedout = []
577 orderedout = []
578 # Don't start at nullid since we don't want nullid in our output list,
578 # Don't start at nullid since we don't want nullid in our output list,
579 # and if nullid shows up in descendants, empty parents will look like
579 # and if nullid shows up in descendants, empty parents will look like
580 # they're descendants.
580 # they're descendants.
581 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
581 for r in self.revs(start=max(lowestrev, 0), stop=highestrev + 1):
582 n = self.node(r)
582 n = self.node(r)
583 isdescendant = False
583 isdescendant = False
584 if lowestrev == nullrev: # Everybody is a descendant of nullid
584 if lowestrev == nullrev: # Everybody is a descendant of nullid
585 isdescendant = True
585 isdescendant = True
586 elif n in descendants:
586 elif n in descendants:
587 # n is already a descendant
587 # n is already a descendant
588 isdescendant = True
588 isdescendant = True
589 # This check only needs to be done here because all the roots
589 # This check only needs to be done here because all the roots
590 # will start being marked is descendants before the loop.
590 # will start being marked is descendants before the loop.
591 if n in roots:
591 if n in roots:
592 # If n was a root, check if it's a 'real' root.
592 # If n was a root, check if it's a 'real' root.
593 p = tuple(self.parents(n))
593 p = tuple(self.parents(n))
594 # If any of its parents are descendants, it's not a root.
594 # If any of its parents are descendants, it's not a root.
595 if (p[0] in descendants) or (p[1] in descendants):
595 if (p[0] in descendants) or (p[1] in descendants):
596 roots.remove(n)
596 roots.remove(n)
597 else:
597 else:
598 p = tuple(self.parents(n))
598 p = tuple(self.parents(n))
599 # A node is a descendant if either of its parents are
599 # A node is a descendant if either of its parents are
600 # descendants. (We seeded the dependents list with the roots
600 # descendants. (We seeded the dependents list with the roots
601 # up there, remember?)
601 # up there, remember?)
602 if (p[0] in descendants) or (p[1] in descendants):
602 if (p[0] in descendants) or (p[1] in descendants):
603 descendants.add(n)
603 descendants.add(n)
604 isdescendant = True
604 isdescendant = True
605 if isdescendant and ((ancestors is None) or (n in ancestors)):
605 if isdescendant and ((ancestors is None) or (n in ancestors)):
606 # Only include nodes that are both descendants and ancestors.
606 # Only include nodes that are both descendants and ancestors.
607 orderedout.append(n)
607 orderedout.append(n)
608 if (ancestors is not None) and (n in heads):
608 if (ancestors is not None) and (n in heads):
609 # We're trying to figure out which heads are reachable
609 # We're trying to figure out which heads are reachable
610 # from roots.
610 # from roots.
611 # Mark this head as having been reached
611 # Mark this head as having been reached
612 heads[n] = True
612 heads[n] = True
613 elif ancestors is None:
613 elif ancestors is None:
614 # Otherwise, we're trying to discover the heads.
614 # Otherwise, we're trying to discover the heads.
615 # Assume this is a head because if it isn't, the next step
615 # Assume this is a head because if it isn't, the next step
616 # will eventually remove it.
616 # will eventually remove it.
617 heads[n] = True
617 heads[n] = True
618 # But, obviously its parents aren't.
618 # But, obviously its parents aren't.
619 for p in self.parents(n):
619 for p in self.parents(n):
620 heads.pop(p, None)
620 heads.pop(p, None)
621 heads = [n for n, flag in heads.iteritems() if flag]
621 heads = [n for n, flag in heads.iteritems() if flag]
622 roots = list(roots)
622 roots = list(roots)
623 assert orderedout
623 assert orderedout
624 assert roots
624 assert roots
625 assert heads
625 assert heads
626 return (orderedout, roots, heads)
626 return (orderedout, roots, heads)
627
627
628 def headrevs(self):
628 def headrevs(self):
629 try:
629 try:
630 return self.index.headrevs()
630 return self.index.headrevs()
631 except AttributeError:
631 except AttributeError:
632 return self._headrevs()
632 return self._headrevs()
633
633
634 def _headrevs(self):
634 def _headrevs(self):
635 count = len(self)
635 count = len(self)
636 if not count:
636 if not count:
637 return [nullrev]
637 return [nullrev]
638 # we won't iter over filtered rev so nobody is a head at start
638 # we won't iter over filtered rev so nobody is a head at start
639 ishead = [0] * (count + 1)
639 ishead = [0] * (count + 1)
640 index = self.index
640 index = self.index
641 for r in self:
641 for r in self:
642 ishead[r] = 1 # I may be an head
642 ishead[r] = 1 # I may be an head
643 e = index[r]
643 e = index[r]
644 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
644 ishead[e[5]] = ishead[e[6]] = 0 # my parent are not
645 return [r for r, val in enumerate(ishead) if val]
645 return [r for r, val in enumerate(ishead) if val]
646
646
647 def heads(self, start=None, stop=None):
647 def heads(self, start=None, stop=None):
648 """return the list of all nodes that have no children
648 """return the list of all nodes that have no children
649
649
650 if start is specified, only heads that are descendants of
650 if start is specified, only heads that are descendants of
651 start will be returned
651 start will be returned
652 if stop is specified, it will consider all the revs from stop
652 if stop is specified, it will consider all the revs from stop
653 as if they had no children
653 as if they had no children
654 """
654 """
655 if start is None and stop is None:
655 if start is None and stop is None:
656 if not len(self):
656 if not len(self):
657 return [nullid]
657 return [nullid]
658 return [self.node(r) for r in self.headrevs()]
658 return [self.node(r) for r in self.headrevs()]
659
659
660 if start is None:
660 if start is None:
661 start = nullid
661 start = nullid
662 if stop is None:
662 if stop is None:
663 stop = []
663 stop = []
664 stoprevs = set([self.rev(n) for n in stop])
664 stoprevs = set([self.rev(n) for n in stop])
665 startrev = self.rev(start)
665 startrev = self.rev(start)
666 reachable = set((startrev,))
666 reachable = set((startrev,))
667 heads = set((startrev,))
667 heads = set((startrev,))
668
668
669 parentrevs = self.parentrevs
669 parentrevs = self.parentrevs
670 for r in self.revs(start=startrev + 1):
670 for r in self.revs(start=startrev + 1):
671 for p in parentrevs(r):
671 for p in parentrevs(r):
672 if p in reachable:
672 if p in reachable:
673 if r not in stoprevs:
673 if r not in stoprevs:
674 reachable.add(r)
674 reachable.add(r)
675 heads.add(r)
675 heads.add(r)
676 if p in heads and p not in stoprevs:
676 if p in heads and p not in stoprevs:
677 heads.remove(p)
677 heads.remove(p)
678
678
679 return [self.node(r) for r in heads]
679 return [self.node(r) for r in heads]
680
680
681 def children(self, node):
681 def children(self, node):
682 """find the children of a given node"""
682 """find the children of a given node"""
683 c = []
683 c = []
684 p = self.rev(node)
684 p = self.rev(node)
685 for r in self.revs(start=p + 1):
685 for r in self.revs(start=p + 1):
686 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
686 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
687 if prevs:
687 if prevs:
688 for pr in prevs:
688 for pr in prevs:
689 if pr == p:
689 if pr == p:
690 c.append(self.node(r))
690 c.append(self.node(r))
691 elif p == nullrev:
691 elif p == nullrev:
692 c.append(self.node(r))
692 c.append(self.node(r))
693 return c
693 return c
694
694
695 def descendant(self, start, end):
695 def descendant(self, start, end):
696 if start == nullrev:
696 if start == nullrev:
697 return True
697 return True
698 for i in self.descendants([start]):
698 for i in self.descendants([start]):
699 if i == end:
699 if i == end:
700 return True
700 return True
701 elif i > end:
701 elif i > end:
702 break
702 break
703 return False
703 return False
704
704
705 def ancestor(self, a, b):
705 def ancestor(self, a, b):
706 """calculate the least common ancestor of nodes a and b"""
706 """calculate the least common ancestor of nodes a and b"""
707
707
708 # fast path, check if it is a descendant
709 a, b = self.rev(a), self.rev(b)
708 a, b = self.rev(a), self.rev(b)
710 start, end = sorted((a, b))
709 ancs = ancestor.ancestors(self.parentrevs, a, b)
711 if self.descendant(start, end):
710 if ancs:
712 return self.node(start)
711 # choose a consistent winner when there's a tie
713
712 return min(map(self.node, ancs))
714 c = ancestor.ancestor(a, b, self.parentrevs)
713 return nullid
715 if c is None:
716 return nullid
717
718 return self.node(c)
719
714
720 def _match(self, id):
715 def _match(self, id):
721 if isinstance(id, int):
716 if isinstance(id, int):
722 # rev
717 # rev
723 return self.node(id)
718 return self.node(id)
724 if len(id) == 20:
719 if len(id) == 20:
725 # possibly a binary node
720 # possibly a binary node
726 # odds of a binary node being all hex in ASCII are 1 in 10**25
721 # odds of a binary node being all hex in ASCII are 1 in 10**25
727 try:
722 try:
728 node = id
723 node = id
729 self.rev(node) # quick search the index
724 self.rev(node) # quick search the index
730 return node
725 return node
731 except LookupError:
726 except LookupError:
732 pass # may be partial hex id
727 pass # may be partial hex id
733 try:
728 try:
734 # str(rev)
729 # str(rev)
735 rev = int(id)
730 rev = int(id)
736 if str(rev) != id:
731 if str(rev) != id:
737 raise ValueError
732 raise ValueError
738 if rev < 0:
733 if rev < 0:
739 rev = len(self) + rev
734 rev = len(self) + rev
740 if rev < 0 or rev >= len(self):
735 if rev < 0 or rev >= len(self):
741 raise ValueError
736 raise ValueError
742 return self.node(rev)
737 return self.node(rev)
743 except (ValueError, OverflowError):
738 except (ValueError, OverflowError):
744 pass
739 pass
745 if len(id) == 40:
740 if len(id) == 40:
746 try:
741 try:
747 # a full hex nodeid?
742 # a full hex nodeid?
748 node = bin(id)
743 node = bin(id)
749 self.rev(node)
744 self.rev(node)
750 return node
745 return node
751 except (TypeError, LookupError):
746 except (TypeError, LookupError):
752 pass
747 pass
753
748
754 def _partialmatch(self, id):
749 def _partialmatch(self, id):
755 try:
750 try:
756 return self.index.partialmatch(id)
751 return self.index.partialmatch(id)
757 except RevlogError:
752 except RevlogError:
758 # parsers.c radix tree lookup gave multiple matches
753 # parsers.c radix tree lookup gave multiple matches
759 raise LookupError(id, self.indexfile, _("ambiguous identifier"))
754 raise LookupError(id, self.indexfile, _("ambiguous identifier"))
760 except (AttributeError, ValueError):
755 except (AttributeError, ValueError):
761 # we are pure python, or key was too short to search radix tree
756 # we are pure python, or key was too short to search radix tree
762 pass
757 pass
763
758
764 if id in self._pcache:
759 if id in self._pcache:
765 return self._pcache[id]
760 return self._pcache[id]
766
761
767 if len(id) < 40:
762 if len(id) < 40:
768 try:
763 try:
769 # hex(node)[:...]
764 # hex(node)[:...]
770 l = len(id) // 2 # grab an even number of digits
765 l = len(id) // 2 # grab an even number of digits
771 prefix = bin(id[:l * 2])
766 prefix = bin(id[:l * 2])
772 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
767 nl = [e[7] for e in self.index if e[7].startswith(prefix)]
773 nl = [n for n in nl if hex(n).startswith(id)]
768 nl = [n for n in nl if hex(n).startswith(id)]
774 if len(nl) > 0:
769 if len(nl) > 0:
775 if len(nl) == 1:
770 if len(nl) == 1:
776 self._pcache[id] = nl[0]
771 self._pcache[id] = nl[0]
777 return nl[0]
772 return nl[0]
778 raise LookupError(id, self.indexfile,
773 raise LookupError(id, self.indexfile,
779 _('ambiguous identifier'))
774 _('ambiguous identifier'))
780 return None
775 return None
781 except TypeError:
776 except TypeError:
782 pass
777 pass
783
778
784 def lookup(self, id):
779 def lookup(self, id):
785 """locate a node based on:
780 """locate a node based on:
786 - revision number or str(revision number)
781 - revision number or str(revision number)
787 - nodeid or subset of hex nodeid
782 - nodeid or subset of hex nodeid
788 """
783 """
789 n = self._match(id)
784 n = self._match(id)
790 if n is not None:
785 if n is not None:
791 return n
786 return n
792 n = self._partialmatch(id)
787 n = self._partialmatch(id)
793 if n:
788 if n:
794 return n
789 return n
795
790
796 raise LookupError(id, self.indexfile, _('no match found'))
791 raise LookupError(id, self.indexfile, _('no match found'))
797
792
798 def cmp(self, node, text):
793 def cmp(self, node, text):
799 """compare text with a given file revision
794 """compare text with a given file revision
800
795
801 returns True if text is different than what is stored.
796 returns True if text is different than what is stored.
802 """
797 """
803 p1, p2 = self.parents(node)
798 p1, p2 = self.parents(node)
804 return hash(text, p1, p2) != node
799 return hash(text, p1, p2) != node
805
800
806 def _addchunk(self, offset, data):
801 def _addchunk(self, offset, data):
807 o, d = self._chunkcache
802 o, d = self._chunkcache
808 # try to add to existing cache
803 # try to add to existing cache
809 if o + len(d) == offset and len(d) + len(data) < _chunksize:
804 if o + len(d) == offset and len(d) + len(data) < _chunksize:
810 self._chunkcache = o, d + data
805 self._chunkcache = o, d + data
811 else:
806 else:
812 self._chunkcache = offset, data
807 self._chunkcache = offset, data
813
808
814 def _loadchunk(self, offset, length):
809 def _loadchunk(self, offset, length):
815 if self._inline:
810 if self._inline:
816 df = self.opener(self.indexfile)
811 df = self.opener(self.indexfile)
817 else:
812 else:
818 df = self.opener(self.datafile)
813 df = self.opener(self.datafile)
819
814
820 readahead = max(65536, length)
815 readahead = max(65536, length)
821 df.seek(offset)
816 df.seek(offset)
822 d = df.read(readahead)
817 d = df.read(readahead)
823 df.close()
818 df.close()
824 self._addchunk(offset, d)
819 self._addchunk(offset, d)
825 if readahead > length:
820 if readahead > length:
826 return util.buffer(d, 0, length)
821 return util.buffer(d, 0, length)
827 return d
822 return d
828
823
829 def _getchunk(self, offset, length):
824 def _getchunk(self, offset, length):
830 o, d = self._chunkcache
825 o, d = self._chunkcache
831 l = len(d)
826 l = len(d)
832
827
833 # is it in the cache?
828 # is it in the cache?
834 cachestart = offset - o
829 cachestart = offset - o
835 cacheend = cachestart + length
830 cacheend = cachestart + length
836 if cachestart >= 0 and cacheend <= l:
831 if cachestart >= 0 and cacheend <= l:
837 if cachestart == 0 and cacheend == l:
832 if cachestart == 0 and cacheend == l:
838 return d # avoid a copy
833 return d # avoid a copy
839 return util.buffer(d, cachestart, cacheend - cachestart)
834 return util.buffer(d, cachestart, cacheend - cachestart)
840
835
841 return self._loadchunk(offset, length)
836 return self._loadchunk(offset, length)
842
837
843 def _chunkraw(self, startrev, endrev):
838 def _chunkraw(self, startrev, endrev):
844 start = self.start(startrev)
839 start = self.start(startrev)
845 length = self.end(endrev) - start
840 length = self.end(endrev) - start
846 if self._inline:
841 if self._inline:
847 start += (startrev + 1) * self._io.size
842 start += (startrev + 1) * self._io.size
848 return self._getchunk(start, length)
843 return self._getchunk(start, length)
849
844
850 def _chunk(self, rev):
845 def _chunk(self, rev):
851 return decompress(self._chunkraw(rev, rev))
846 return decompress(self._chunkraw(rev, rev))
852
847
853 def _chunkbase(self, rev):
848 def _chunkbase(self, rev):
854 return self._chunk(rev)
849 return self._chunk(rev)
855
850
856 def _chunkclear(self):
851 def _chunkclear(self):
857 self._chunkcache = (0, '')
852 self._chunkcache = (0, '')
858
853
859 def deltaparent(self, rev):
854 def deltaparent(self, rev):
860 """return deltaparent of the given revision"""
855 """return deltaparent of the given revision"""
861 base = self.index[rev][3]
856 base = self.index[rev][3]
862 if base == rev:
857 if base == rev:
863 return nullrev
858 return nullrev
864 elif self._generaldelta:
859 elif self._generaldelta:
865 return base
860 return base
866 else:
861 else:
867 return rev - 1
862 return rev - 1
868
863
869 def revdiff(self, rev1, rev2):
864 def revdiff(self, rev1, rev2):
870 """return or calculate a delta between two revisions"""
865 """return or calculate a delta between two revisions"""
871 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
866 if rev1 != nullrev and self.deltaparent(rev2) == rev1:
872 return str(self._chunk(rev2))
867 return str(self._chunk(rev2))
873
868
874 return mdiff.textdiff(self.revision(rev1),
869 return mdiff.textdiff(self.revision(rev1),
875 self.revision(rev2))
870 self.revision(rev2))
876
871
877 def revision(self, nodeorrev):
872 def revision(self, nodeorrev):
878 """return an uncompressed revision of a given node or revision
873 """return an uncompressed revision of a given node or revision
879 number.
874 number.
880 """
875 """
881 if isinstance(nodeorrev, int):
876 if isinstance(nodeorrev, int):
882 rev = nodeorrev
877 rev = nodeorrev
883 node = self.node(rev)
878 node = self.node(rev)
884 else:
879 else:
885 node = nodeorrev
880 node = nodeorrev
886 rev = None
881 rev = None
887
882
888 cachedrev = None
883 cachedrev = None
889 if node == nullid:
884 if node == nullid:
890 return ""
885 return ""
891 if self._cache:
886 if self._cache:
892 if self._cache[0] == node:
887 if self._cache[0] == node:
893 return self._cache[2]
888 return self._cache[2]
894 cachedrev = self._cache[1]
889 cachedrev = self._cache[1]
895
890
896 # look up what we need to read
891 # look up what we need to read
897 text = None
892 text = None
898 if rev is None:
893 if rev is None:
899 rev = self.rev(node)
894 rev = self.rev(node)
900
895
901 # check rev flags
896 # check rev flags
902 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
897 if self.flags(rev) & ~REVIDX_KNOWN_FLAGS:
903 raise RevlogError(_('incompatible revision flag %x') %
898 raise RevlogError(_('incompatible revision flag %x') %
904 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
899 (self.flags(rev) & ~REVIDX_KNOWN_FLAGS))
905
900
906 # build delta chain
901 # build delta chain
907 chain = []
902 chain = []
908 index = self.index # for performance
903 index = self.index # for performance
909 generaldelta = self._generaldelta
904 generaldelta = self._generaldelta
910 iterrev = rev
905 iterrev = rev
911 e = index[iterrev]
906 e = index[iterrev]
912 while iterrev != e[3] and iterrev != cachedrev:
907 while iterrev != e[3] and iterrev != cachedrev:
913 chain.append(iterrev)
908 chain.append(iterrev)
914 if generaldelta:
909 if generaldelta:
915 iterrev = e[3]
910 iterrev = e[3]
916 else:
911 else:
917 iterrev -= 1
912 iterrev -= 1
918 e = index[iterrev]
913 e = index[iterrev]
919 chain.reverse()
914 chain.reverse()
920 base = iterrev
915 base = iterrev
921
916
922 if iterrev == cachedrev:
917 if iterrev == cachedrev:
923 # cache hit
918 # cache hit
924 text = self._cache[2]
919 text = self._cache[2]
925
920
926 # drop cache to save memory
921 # drop cache to save memory
927 self._cache = None
922 self._cache = None
928
923
929 self._chunkraw(base, rev)
924 self._chunkraw(base, rev)
930 if text is None:
925 if text is None:
931 text = str(self._chunkbase(base))
926 text = str(self._chunkbase(base))
932
927
933 bins = [self._chunk(r) for r in chain]
928 bins = [self._chunk(r) for r in chain]
934 text = mdiff.patches(text, bins)
929 text = mdiff.patches(text, bins)
935
930
936 text = self._checkhash(text, node, rev)
931 text = self._checkhash(text, node, rev)
937
932
938 self._cache = (node, rev, text)
933 self._cache = (node, rev, text)
939 return text
934 return text
940
935
941 def _checkhash(self, text, node, rev):
936 def _checkhash(self, text, node, rev):
942 p1, p2 = self.parents(node)
937 p1, p2 = self.parents(node)
943 if node != hash(text, p1, p2):
938 if node != hash(text, p1, p2):
944 raise RevlogError(_("integrity check failed on %s:%d")
939 raise RevlogError(_("integrity check failed on %s:%d")
945 % (self.indexfile, rev))
940 % (self.indexfile, rev))
946 return text
941 return text
947
942
948 def checkinlinesize(self, tr, fp=None):
943 def checkinlinesize(self, tr, fp=None):
949 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
944 if not self._inline or (self.start(-2) + self.length(-2)) < _maxinline:
950 return
945 return
951
946
952 trinfo = tr.find(self.indexfile)
947 trinfo = tr.find(self.indexfile)
953 if trinfo is None:
948 if trinfo is None:
954 raise RevlogError(_("%s not found in the transaction")
949 raise RevlogError(_("%s not found in the transaction")
955 % self.indexfile)
950 % self.indexfile)
956
951
957 trindex = trinfo[2]
952 trindex = trinfo[2]
958 dataoff = self.start(trindex)
953 dataoff = self.start(trindex)
959
954
960 tr.add(self.datafile, dataoff)
955 tr.add(self.datafile, dataoff)
961
956
962 if fp:
957 if fp:
963 fp.flush()
958 fp.flush()
964 fp.close()
959 fp.close()
965
960
966 df = self.opener(self.datafile, 'w')
961 df = self.opener(self.datafile, 'w')
967 try:
962 try:
968 for r in self:
963 for r in self:
969 df.write(self._chunkraw(r, r))
964 df.write(self._chunkraw(r, r))
970 finally:
965 finally:
971 df.close()
966 df.close()
972
967
973 fp = self.opener(self.indexfile, 'w', atomictemp=True)
968 fp = self.opener(self.indexfile, 'w', atomictemp=True)
974 self.version &= ~(REVLOGNGINLINEDATA)
969 self.version &= ~(REVLOGNGINLINEDATA)
975 self._inline = False
970 self._inline = False
976 for i in self:
971 for i in self:
977 e = self._io.packentry(self.index[i], self.node, self.version, i)
972 e = self._io.packentry(self.index[i], self.node, self.version, i)
978 fp.write(e)
973 fp.write(e)
979
974
980 # if we don't call close, the temp file will never replace the
975 # if we don't call close, the temp file will never replace the
981 # real index
976 # real index
982 fp.close()
977 fp.close()
983
978
984 tr.replace(self.indexfile, trindex * self._io.size)
979 tr.replace(self.indexfile, trindex * self._io.size)
985 self._chunkclear()
980 self._chunkclear()
986
981
987 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
982 def addrevision(self, text, transaction, link, p1, p2, cachedelta=None):
988 """add a revision to the log
983 """add a revision to the log
989
984
990 text - the revision data to add
985 text - the revision data to add
991 transaction - the transaction object used for rollback
986 transaction - the transaction object used for rollback
992 link - the linkrev data to add
987 link - the linkrev data to add
993 p1, p2 - the parent nodeids of the revision
988 p1, p2 - the parent nodeids of the revision
994 cachedelta - an optional precomputed delta
989 cachedelta - an optional precomputed delta
995 """
990 """
996 node = hash(text, p1, p2)
991 node = hash(text, p1, p2)
997 if node in self.nodemap:
992 if node in self.nodemap:
998 return node
993 return node
999
994
1000 dfh = None
995 dfh = None
1001 if not self._inline:
996 if not self._inline:
1002 dfh = self.opener(self.datafile, "a")
997 dfh = self.opener(self.datafile, "a")
1003 ifh = self.opener(self.indexfile, "a+")
998 ifh = self.opener(self.indexfile, "a+")
1004 try:
999 try:
1005 return self._addrevision(node, text, transaction, link, p1, p2,
1000 return self._addrevision(node, text, transaction, link, p1, p2,
1006 cachedelta, ifh, dfh)
1001 cachedelta, ifh, dfh)
1007 finally:
1002 finally:
1008 if dfh:
1003 if dfh:
1009 dfh.close()
1004 dfh.close()
1010 ifh.close()
1005 ifh.close()
1011
1006
1012 def compress(self, text):
1007 def compress(self, text):
1013 """ generate a possibly-compressed representation of text """
1008 """ generate a possibly-compressed representation of text """
1014 if not text:
1009 if not text:
1015 return ("", text)
1010 return ("", text)
1016 l = len(text)
1011 l = len(text)
1017 bin = None
1012 bin = None
1018 if l < 44:
1013 if l < 44:
1019 pass
1014 pass
1020 elif l > 1000000:
1015 elif l > 1000000:
1021 # zlib makes an internal copy, thus doubling memory usage for
1016 # zlib makes an internal copy, thus doubling memory usage for
1022 # large files, so lets do this in pieces
1017 # large files, so lets do this in pieces
1023 z = zlib.compressobj()
1018 z = zlib.compressobj()
1024 p = []
1019 p = []
1025 pos = 0
1020 pos = 0
1026 while pos < l:
1021 while pos < l:
1027 pos2 = pos + 2**20
1022 pos2 = pos + 2**20
1028 p.append(z.compress(text[pos:pos2]))
1023 p.append(z.compress(text[pos:pos2]))
1029 pos = pos2
1024 pos = pos2
1030 p.append(z.flush())
1025 p.append(z.flush())
1031 if sum(map(len, p)) < l:
1026 if sum(map(len, p)) < l:
1032 bin = "".join(p)
1027 bin = "".join(p)
1033 else:
1028 else:
1034 bin = _compress(text)
1029 bin = _compress(text)
1035 if bin is None or len(bin) > l:
1030 if bin is None or len(bin) > l:
1036 if text[0] == '\0':
1031 if text[0] == '\0':
1037 return ("", text)
1032 return ("", text)
1038 return ('u', text)
1033 return ('u', text)
1039 return ("", bin)
1034 return ("", bin)
1040
1035
1041 def _addrevision(self, node, text, transaction, link, p1, p2,
1036 def _addrevision(self, node, text, transaction, link, p1, p2,
1042 cachedelta, ifh, dfh):
1037 cachedelta, ifh, dfh):
1043 """internal function to add revisions to the log
1038 """internal function to add revisions to the log
1044
1039
1045 see addrevision for argument descriptions.
1040 see addrevision for argument descriptions.
1046 invariants:
1041 invariants:
1047 - text is optional (can be None); if not set, cachedelta must be set.
1042 - text is optional (can be None); if not set, cachedelta must be set.
1048 if both are set, they must correspond to each other.
1043 if both are set, they must correspond to each other.
1049 """
1044 """
1050 btext = [text]
1045 btext = [text]
1051 def buildtext():
1046 def buildtext():
1052 if btext[0] is not None:
1047 if btext[0] is not None:
1053 return btext[0]
1048 return btext[0]
1054 # flush any pending writes here so we can read it in revision
1049 # flush any pending writes here so we can read it in revision
1055 if dfh:
1050 if dfh:
1056 dfh.flush()
1051 dfh.flush()
1057 ifh.flush()
1052 ifh.flush()
1058 basetext = self.revision(self.node(cachedelta[0]))
1053 basetext = self.revision(self.node(cachedelta[0]))
1059 btext[0] = mdiff.patch(basetext, cachedelta[1])
1054 btext[0] = mdiff.patch(basetext, cachedelta[1])
1060 chk = hash(btext[0], p1, p2)
1055 chk = hash(btext[0], p1, p2)
1061 if chk != node:
1056 if chk != node:
1062 raise RevlogError(_("consistency error in delta"))
1057 raise RevlogError(_("consistency error in delta"))
1063 return btext[0]
1058 return btext[0]
1064
1059
1065 def builddelta(rev):
1060 def builddelta(rev):
1066 # can we use the cached delta?
1061 # can we use the cached delta?
1067 if cachedelta and cachedelta[0] == rev:
1062 if cachedelta and cachedelta[0] == rev:
1068 delta = cachedelta[1]
1063 delta = cachedelta[1]
1069 else:
1064 else:
1070 t = buildtext()
1065 t = buildtext()
1071 ptext = self.revision(self.node(rev))
1066 ptext = self.revision(self.node(rev))
1072 delta = mdiff.textdiff(ptext, t)
1067 delta = mdiff.textdiff(ptext, t)
1073 data = self.compress(delta)
1068 data = self.compress(delta)
1074 l = len(data[1]) + len(data[0])
1069 l = len(data[1]) + len(data[0])
1075 if basecache[0] == rev:
1070 if basecache[0] == rev:
1076 chainbase = basecache[1]
1071 chainbase = basecache[1]
1077 else:
1072 else:
1078 chainbase = self.chainbase(rev)
1073 chainbase = self.chainbase(rev)
1079 dist = l + offset - self.start(chainbase)
1074 dist = l + offset - self.start(chainbase)
1080 if self._generaldelta:
1075 if self._generaldelta:
1081 base = rev
1076 base = rev
1082 else:
1077 else:
1083 base = chainbase
1078 base = chainbase
1084 return dist, l, data, base, chainbase
1079 return dist, l, data, base, chainbase
1085
1080
1086 curr = len(self)
1081 curr = len(self)
1087 prev = curr - 1
1082 prev = curr - 1
1088 base = chainbase = curr
1083 base = chainbase = curr
1089 offset = self.end(prev)
1084 offset = self.end(prev)
1090 flags = 0
1085 flags = 0
1091 d = None
1086 d = None
1092 basecache = self._basecache
1087 basecache = self._basecache
1093 p1r, p2r = self.rev(p1), self.rev(p2)
1088 p1r, p2r = self.rev(p1), self.rev(p2)
1094
1089
1095 # should we try to build a delta?
1090 # should we try to build a delta?
1096 if prev != nullrev:
1091 if prev != nullrev:
1097 if self._generaldelta:
1092 if self._generaldelta:
1098 if p1r >= basecache[1]:
1093 if p1r >= basecache[1]:
1099 d = builddelta(p1r)
1094 d = builddelta(p1r)
1100 elif p2r >= basecache[1]:
1095 elif p2r >= basecache[1]:
1101 d = builddelta(p2r)
1096 d = builddelta(p2r)
1102 else:
1097 else:
1103 d = builddelta(prev)
1098 d = builddelta(prev)
1104 else:
1099 else:
1105 d = builddelta(prev)
1100 d = builddelta(prev)
1106 dist, l, data, base, chainbase = d
1101 dist, l, data, base, chainbase = d
1107
1102
1108 # full versions are inserted when the needed deltas
1103 # full versions are inserted when the needed deltas
1109 # become comparable to the uncompressed text
1104 # become comparable to the uncompressed text
1110 if text is None:
1105 if text is None:
1111 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1106 textlen = mdiff.patchedsize(self.rawsize(cachedelta[0]),
1112 cachedelta[1])
1107 cachedelta[1])
1113 else:
1108 else:
1114 textlen = len(text)
1109 textlen = len(text)
1115 if d is None or dist > textlen * 2:
1110 if d is None or dist > textlen * 2:
1116 text = buildtext()
1111 text = buildtext()
1117 data = self.compress(text)
1112 data = self.compress(text)
1118 l = len(data[1]) + len(data[0])
1113 l = len(data[1]) + len(data[0])
1119 base = chainbase = curr
1114 base = chainbase = curr
1120
1115
1121 e = (offset_type(offset, flags), l, textlen,
1116 e = (offset_type(offset, flags), l, textlen,
1122 base, link, p1r, p2r, node)
1117 base, link, p1r, p2r, node)
1123 self.index.insert(-1, e)
1118 self.index.insert(-1, e)
1124 self.nodemap[node] = curr
1119 self.nodemap[node] = curr
1125
1120
1126 entry = self._io.packentry(e, self.node, self.version, curr)
1121 entry = self._io.packentry(e, self.node, self.version, curr)
1127 if not self._inline:
1122 if not self._inline:
1128 transaction.add(self.datafile, offset)
1123 transaction.add(self.datafile, offset)
1129 transaction.add(self.indexfile, curr * len(entry))
1124 transaction.add(self.indexfile, curr * len(entry))
1130 if data[0]:
1125 if data[0]:
1131 dfh.write(data[0])
1126 dfh.write(data[0])
1132 dfh.write(data[1])
1127 dfh.write(data[1])
1133 dfh.flush()
1128 dfh.flush()
1134 ifh.write(entry)
1129 ifh.write(entry)
1135 else:
1130 else:
1136 offset += curr * self._io.size
1131 offset += curr * self._io.size
1137 transaction.add(self.indexfile, offset, curr)
1132 transaction.add(self.indexfile, offset, curr)
1138 ifh.write(entry)
1133 ifh.write(entry)
1139 ifh.write(data[0])
1134 ifh.write(data[0])
1140 ifh.write(data[1])
1135 ifh.write(data[1])
1141 self.checkinlinesize(transaction, ifh)
1136 self.checkinlinesize(transaction, ifh)
1142
1137
1143 if type(text) == str: # only accept immutable objects
1138 if type(text) == str: # only accept immutable objects
1144 self._cache = (node, curr, text)
1139 self._cache = (node, curr, text)
1145 self._basecache = (curr, chainbase)
1140 self._basecache = (curr, chainbase)
1146 return node
1141 return node
1147
1142
1148 def group(self, nodelist, bundler, reorder=None):
1143 def group(self, nodelist, bundler, reorder=None):
1149 """Calculate a delta group, yielding a sequence of changegroup chunks
1144 """Calculate a delta group, yielding a sequence of changegroup chunks
1150 (strings).
1145 (strings).
1151
1146
1152 Given a list of changeset revs, return a set of deltas and
1147 Given a list of changeset revs, return a set of deltas and
1153 metadata corresponding to nodes. The first delta is
1148 metadata corresponding to nodes. The first delta is
1154 first parent(nodelist[0]) -> nodelist[0], the receiver is
1149 first parent(nodelist[0]) -> nodelist[0], the receiver is
1155 guaranteed to have this parent as it has all history before
1150 guaranteed to have this parent as it has all history before
1156 these changesets. In the case firstparent is nullrev the
1151 these changesets. In the case firstparent is nullrev the
1157 changegroup starts with a full revision.
1152 changegroup starts with a full revision.
1158 """
1153 """
1159
1154
1160 # if we don't have any revisions touched by these changesets, bail
1155 # if we don't have any revisions touched by these changesets, bail
1161 if len(nodelist) == 0:
1156 if len(nodelist) == 0:
1162 yield bundler.close()
1157 yield bundler.close()
1163 return
1158 return
1164
1159
1165 # for generaldelta revlogs, we linearize the revs; this will both be
1160 # for generaldelta revlogs, we linearize the revs; this will both be
1166 # much quicker and generate a much smaller bundle
1161 # much quicker and generate a much smaller bundle
1167 if (self._generaldelta and reorder is not False) or reorder:
1162 if (self._generaldelta and reorder is not False) or reorder:
1168 dag = dagutil.revlogdag(self)
1163 dag = dagutil.revlogdag(self)
1169 revs = set(self.rev(n) for n in nodelist)
1164 revs = set(self.rev(n) for n in nodelist)
1170 revs = dag.linearize(revs)
1165 revs = dag.linearize(revs)
1171 else:
1166 else:
1172 revs = sorted([self.rev(n) for n in nodelist])
1167 revs = sorted([self.rev(n) for n in nodelist])
1173
1168
1174 # add the parent of the first rev
1169 # add the parent of the first rev
1175 p = self.parentrevs(revs[0])[0]
1170 p = self.parentrevs(revs[0])[0]
1176 revs.insert(0, p)
1171 revs.insert(0, p)
1177
1172
1178 # build deltas
1173 # build deltas
1179 for r in xrange(len(revs) - 1):
1174 for r in xrange(len(revs) - 1):
1180 prev, curr = revs[r], revs[r + 1]
1175 prev, curr = revs[r], revs[r + 1]
1181 for c in bundler.revchunk(self, curr, prev):
1176 for c in bundler.revchunk(self, curr, prev):
1182 yield c
1177 yield c
1183
1178
1184 yield bundler.close()
1179 yield bundler.close()
1185
1180
1186 def addgroup(self, bundle, linkmapper, transaction):
1181 def addgroup(self, bundle, linkmapper, transaction):
1187 """
1182 """
1188 add a delta group
1183 add a delta group
1189
1184
1190 given a set of deltas, add them to the revision log. the
1185 given a set of deltas, add them to the revision log. the
1191 first delta is against its parent, which should be in our
1186 first delta is against its parent, which should be in our
1192 log, the rest are against the previous delta.
1187 log, the rest are against the previous delta.
1193 """
1188 """
1194
1189
1195 # track the base of the current delta log
1190 # track the base of the current delta log
1196 content = []
1191 content = []
1197 node = None
1192 node = None
1198
1193
1199 r = len(self)
1194 r = len(self)
1200 end = 0
1195 end = 0
1201 if r:
1196 if r:
1202 end = self.end(r - 1)
1197 end = self.end(r - 1)
1203 ifh = self.opener(self.indexfile, "a+")
1198 ifh = self.opener(self.indexfile, "a+")
1204 isize = r * self._io.size
1199 isize = r * self._io.size
1205 if self._inline:
1200 if self._inline:
1206 transaction.add(self.indexfile, end + isize, r)
1201 transaction.add(self.indexfile, end + isize, r)
1207 dfh = None
1202 dfh = None
1208 else:
1203 else:
1209 transaction.add(self.indexfile, isize, r)
1204 transaction.add(self.indexfile, isize, r)
1210 transaction.add(self.datafile, end)
1205 transaction.add(self.datafile, end)
1211 dfh = self.opener(self.datafile, "a")
1206 dfh = self.opener(self.datafile, "a")
1212
1207
1213 try:
1208 try:
1214 # loop through our set of deltas
1209 # loop through our set of deltas
1215 chain = None
1210 chain = None
1216 while True:
1211 while True:
1217 chunkdata = bundle.deltachunk(chain)
1212 chunkdata = bundle.deltachunk(chain)
1218 if not chunkdata:
1213 if not chunkdata:
1219 break
1214 break
1220 node = chunkdata['node']
1215 node = chunkdata['node']
1221 p1 = chunkdata['p1']
1216 p1 = chunkdata['p1']
1222 p2 = chunkdata['p2']
1217 p2 = chunkdata['p2']
1223 cs = chunkdata['cs']
1218 cs = chunkdata['cs']
1224 deltabase = chunkdata['deltabase']
1219 deltabase = chunkdata['deltabase']
1225 delta = chunkdata['delta']
1220 delta = chunkdata['delta']
1226
1221
1227 content.append(node)
1222 content.append(node)
1228
1223
1229 link = linkmapper(cs)
1224 link = linkmapper(cs)
1230 if node in self.nodemap:
1225 if node in self.nodemap:
1231 # this can happen if two branches make the same change
1226 # this can happen if two branches make the same change
1232 chain = node
1227 chain = node
1233 continue
1228 continue
1234
1229
1235 for p in (p1, p2):
1230 for p in (p1, p2):
1236 if p not in self.nodemap:
1231 if p not in self.nodemap:
1237 raise LookupError(p, self.indexfile,
1232 raise LookupError(p, self.indexfile,
1238 _('unknown parent'))
1233 _('unknown parent'))
1239
1234
1240 if deltabase not in self.nodemap:
1235 if deltabase not in self.nodemap:
1241 raise LookupError(deltabase, self.indexfile,
1236 raise LookupError(deltabase, self.indexfile,
1242 _('unknown delta base'))
1237 _('unknown delta base'))
1243
1238
1244 baserev = self.rev(deltabase)
1239 baserev = self.rev(deltabase)
1245 chain = self._addrevision(node, None, transaction, link,
1240 chain = self._addrevision(node, None, transaction, link,
1246 p1, p2, (baserev, delta), ifh, dfh)
1241 p1, p2, (baserev, delta), ifh, dfh)
1247 if not dfh and not self._inline:
1242 if not dfh and not self._inline:
1248 # addrevision switched from inline to conventional
1243 # addrevision switched from inline to conventional
1249 # reopen the index
1244 # reopen the index
1250 ifh.close()
1245 ifh.close()
1251 dfh = self.opener(self.datafile, "a")
1246 dfh = self.opener(self.datafile, "a")
1252 ifh = self.opener(self.indexfile, "a")
1247 ifh = self.opener(self.indexfile, "a")
1253 finally:
1248 finally:
1254 if dfh:
1249 if dfh:
1255 dfh.close()
1250 dfh.close()
1256 ifh.close()
1251 ifh.close()
1257
1252
1258 return content
1253 return content
1259
1254
1260 def strip(self, minlink, transaction):
1255 def strip(self, minlink, transaction):
1261 """truncate the revlog on the first revision with a linkrev >= minlink
1256 """truncate the revlog on the first revision with a linkrev >= minlink
1262
1257
1263 This function is called when we're stripping revision minlink and
1258 This function is called when we're stripping revision minlink and
1264 its descendants from the repository.
1259 its descendants from the repository.
1265
1260
1266 We have to remove all revisions with linkrev >= minlink, because
1261 We have to remove all revisions with linkrev >= minlink, because
1267 the equivalent changelog revisions will be renumbered after the
1262 the equivalent changelog revisions will be renumbered after the
1268 strip.
1263 strip.
1269
1264
1270 So we truncate the revlog on the first of these revisions, and
1265 So we truncate the revlog on the first of these revisions, and
1271 trust that the caller has saved the revisions that shouldn't be
1266 trust that the caller has saved the revisions that shouldn't be
1272 removed and that it'll re-add them after this truncation.
1267 removed and that it'll re-add them after this truncation.
1273 """
1268 """
1274 if len(self) == 0:
1269 if len(self) == 0:
1275 return
1270 return
1276
1271
1277 for rev in self:
1272 for rev in self:
1278 if self.index[rev][4] >= minlink:
1273 if self.index[rev][4] >= minlink:
1279 break
1274 break
1280 else:
1275 else:
1281 return
1276 return
1282
1277
1283 # first truncate the files on disk
1278 # first truncate the files on disk
1284 end = self.start(rev)
1279 end = self.start(rev)
1285 if not self._inline:
1280 if not self._inline:
1286 transaction.add(self.datafile, end)
1281 transaction.add(self.datafile, end)
1287 end = rev * self._io.size
1282 end = rev * self._io.size
1288 else:
1283 else:
1289 end += rev * self._io.size
1284 end += rev * self._io.size
1290
1285
1291 transaction.add(self.indexfile, end)
1286 transaction.add(self.indexfile, end)
1292
1287
1293 # then reset internal state in memory to forget those revisions
1288 # then reset internal state in memory to forget those revisions
1294 self._cache = None
1289 self._cache = None
1295 self._chunkclear()
1290 self._chunkclear()
1296 for x in xrange(rev, len(self)):
1291 for x in xrange(rev, len(self)):
1297 del self.nodemap[self.node(x)]
1292 del self.nodemap[self.node(x)]
1298
1293
1299 del self.index[rev:-1]
1294 del self.index[rev:-1]
1300
1295
1301 def checksize(self):
1296 def checksize(self):
1302 expected = 0
1297 expected = 0
1303 if len(self):
1298 if len(self):
1304 expected = max(0, self.end(len(self) - 1))
1299 expected = max(0, self.end(len(self) - 1))
1305
1300
1306 try:
1301 try:
1307 f = self.opener(self.datafile)
1302 f = self.opener(self.datafile)
1308 f.seek(0, 2)
1303 f.seek(0, 2)
1309 actual = f.tell()
1304 actual = f.tell()
1310 f.close()
1305 f.close()
1311 dd = actual - expected
1306 dd = actual - expected
1312 except IOError, inst:
1307 except IOError, inst:
1313 if inst.errno != errno.ENOENT:
1308 if inst.errno != errno.ENOENT:
1314 raise
1309 raise
1315 dd = 0
1310 dd = 0
1316
1311
1317 try:
1312 try:
1318 f = self.opener(self.indexfile)
1313 f = self.opener(self.indexfile)
1319 f.seek(0, 2)
1314 f.seek(0, 2)
1320 actual = f.tell()
1315 actual = f.tell()
1321 f.close()
1316 f.close()
1322 s = self._io.size
1317 s = self._io.size
1323 i = max(0, actual // s)
1318 i = max(0, actual // s)
1324 di = actual - (i * s)
1319 di = actual - (i * s)
1325 if self._inline:
1320 if self._inline:
1326 databytes = 0
1321 databytes = 0
1327 for r in self:
1322 for r in self:
1328 databytes += max(0, self.length(r))
1323 databytes += max(0, self.length(r))
1329 dd = 0
1324 dd = 0
1330 di = actual - len(self) * s - databytes
1325 di = actual - len(self) * s - databytes
1331 except IOError, inst:
1326 except IOError, inst:
1332 if inst.errno != errno.ENOENT:
1327 if inst.errno != errno.ENOENT:
1333 raise
1328 raise
1334 di = 0
1329 di = 0
1335
1330
1336 return (dd, di)
1331 return (dd, di)
1337
1332
1338 def files(self):
1333 def files(self):
1339 res = [self.indexfile]
1334 res = [self.indexfile]
1340 if not self._inline:
1335 if not self._inline:
1341 res.append(self.datafile)
1336 res.append(self.datafile)
1342 return res
1337 return res
General Comments 0
You need to be logged in to leave comments. Login now