##// END OF EJS Templates
annotate: do not construct attr.s object per line while computing history...
Yuya Nishihara -
r37082:434e520a default
parent child Browse files
Show More
@@ -369,6 +369,15 b' class annotateline(object):'
369 # Whether this annotation was the result of a skip-annotate.
369 # Whether this annotation was the result of a skip-annotate.
370 skip = attr.ib(default=False)
370 skip = attr.ib(default=False)
371
371
372 @attr.s(slots=True, frozen=True)
373 class _annotatedfile(object):
374 # list indexed by lineno - 1
375 fctxs = attr.ib()
376 linenos = attr.ib()
377 skips = attr.ib()
378 # full file content
379 text = attr.ib()
380
372 def _countlines(text):
381 def _countlines(text):
373 if text.endswith("\n"):
382 if text.endswith("\n"):
374 return text.count("\n")
383 return text.count("\n")
@@ -385,7 +394,7 b' def _annotatepair(parents, childfctx, ch'
385
394
386 See test-annotate.py for unit tests.
395 See test-annotate.py for unit tests.
387 '''
396 '''
388 pblocks = [(parent, mdiff.allblocks(parent[1], child[1], opts=diffopts))
397 pblocks = [(parent, mdiff.allblocks(parent.text, child.text, opts=diffopts))
389 for parent in parents]
398 for parent in parents]
390
399
391 if skipchild:
400 if skipchild:
@@ -398,7 +407,9 b' def _annotatepair(parents, childfctx, ch'
398 # Changed blocks ('!') or blocks made only of blank lines ('~')
407 # Changed blocks ('!') or blocks made only of blank lines ('~')
399 # belong to the child.
408 # belong to the child.
400 if t == '=':
409 if t == '=':
401 child[0][b1:b2] = parent[0][a1:a2]
410 child.fctxs[b1:b2] = parent.fctxs[a1:a2]
411 child.linenos[b1:b2] = parent.linenos[a1:a2]
412 child.skips[b1:b2] = parent.skips[a1:a2]
402
413
403 if skipchild:
414 if skipchild:
404 # Now try and match up anything that couldn't be matched,
415 # Now try and match up anything that couldn't be matched,
@@ -419,9 +430,11 b' def _annotatepair(parents, childfctx, ch'
419 for (a1, a2, b1, b2), _t in blocks:
430 for (a1, a2, b1, b2), _t in blocks:
420 if a2 - a1 >= b2 - b1:
431 if a2 - a1 >= b2 - b1:
421 for bk in xrange(b1, b2):
432 for bk in xrange(b1, b2):
422 if child[0][bk].fctx == childfctx:
433 if child.fctxs[bk] == childfctx:
423 ak = min(a1 + (bk - b1), a2 - 1)
434 ak = min(a1 + (bk - b1), a2 - 1)
424 child[0][bk] = attr.evolve(parent[0][ak], skip=True)
435 child.fctxs[bk] = parent.fctxs[ak]
436 child.linenos[bk] = parent.linenos[ak]
437 child.skips[bk] = True
425 else:
438 else:
426 remaining[idx][1].append((a1, a2, b1, b2))
439 remaining[idx][1].append((a1, a2, b1, b2))
427
440
@@ -430,9 +443,11 b' def _annotatepair(parents, childfctx, ch'
430 for parent, blocks in remaining:
443 for parent, blocks in remaining:
431 for a1, a2, b1, b2 in blocks:
444 for a1, a2, b1, b2 in blocks:
432 for bk in xrange(b1, b2):
445 for bk in xrange(b1, b2):
433 if child[0][bk].fctx == childfctx:
446 if child.fctxs[bk] == childfctx:
434 ak = min(a1 + (bk - b1), a2 - 1)
447 ak = min(a1 + (bk - b1), a2 - 1)
435 child[0][bk] = attr.evolve(parent[0][ak], skip=True)
448 child.fctxs[bk] = parent.fctxs[ak]
449 child.linenos[bk] = parent.linenos[ak]
450 child.skips[bk] = True
436 return child
451 return child
437
452
438 def annotate(base, parents, linenumber=False, skiprevs=None, diffopts=None):
453 def annotate(base, parents, linenumber=False, skiprevs=None, diffopts=None):
@@ -443,11 +458,13 b' def annotate(base, parents, linenumber=F'
443
458
444 if linenumber:
459 if linenumber:
445 def decorate(text, fctx):
460 def decorate(text, fctx):
446 return ([annotateline(fctx=fctx, lineno=i)
461 n = _countlines(text)
447 for i in xrange(1, _countlines(text) + 1)], text)
462 linenos = pycompat.rangelist(1, n + 1)
463 return _annotatedfile([fctx] * n, linenos, [False] * n, text)
448 else:
464 else:
449 def decorate(text, fctx):
465 def decorate(text, fctx):
450 return ([annotateline(fctx=fctx)] * _countlines(text), text)
466 n = _countlines(text)
467 return _annotatedfile([fctx] * n, [False] * n, [False] * n, text)
451
468
452 # This algorithm would prefer to be recursive, but Python is a
469 # This algorithm would prefer to be recursive, but Python is a
453 # bit recursion-hostile. Instead we do an iterative
470 # bit recursion-hostile. Instead we do an iterative
@@ -501,8 +518,10 b' def annotate(base, parents, linenumber=F'
501 hist[f] = curr
518 hist[f] = curr
502 del pcache[f]
519 del pcache[f]
503
520
504 lineattrs, text = hist[base]
521 a = hist[base]
505 return pycompat.ziplist(lineattrs, mdiff.splitnewlines(text))
522 return [(annotateline(fctx, lineno, skip), line)
523 for fctx, lineno, skip, line
524 in zip(a.fctxs, a.linenos, a.skips, mdiff.splitnewlines(a.text))]
506
525
507 def toposort(revs, parentsfunc, firstbranch=()):
526 def toposort(revs, parentsfunc, firstbranch=()):
508 """Yield revisions from heads to roots one (topo) branch at a time.
527 """Yield revisions from heads to roots one (topo) branch at a time.
@@ -71,6 +71,9 b' if ispy3:'
71 def maplist(*args):
71 def maplist(*args):
72 return list(map(*args))
72 return list(map(*args))
73
73
74 def rangelist(*args):
75 return list(range(*args))
76
74 def ziplist(*args):
77 def ziplist(*args):
75 return list(zip(*args))
78 return list(zip(*args))
76
79
@@ -348,6 +351,7 b' else:'
348 bytesio = cStringIO.StringIO
351 bytesio = cStringIO.StringIO
349 stringio = bytesio
352 stringio = bytesio
350 maplist = map
353 maplist = map
354 rangelist = range
351 ziplist = zip
355 ziplist = zip
352 rawinput = raw_input
356 rawinput = raw_input
353 getargspec = inspect.getargspec
357 getargspec = inspect.getargspec
@@ -5,12 +5,18 b' import unittest'
5
5
6 from mercurial import (
6 from mercurial import (
7 mdiff,
7 mdiff,
8 pycompat,
8 )
9 )
9 from mercurial.dagop import (
10 from mercurial.dagop import (
10 annotateline,
11 annotateline,
12 _annotatedfile,
11 _annotatepair,
13 _annotatepair,
12 )
14 )
13
15
16 def tr(a):
17 return [annotateline(fctx, lineno, skip)
18 for fctx, lineno, skip in zip(a.fctxs, a.linenos, a.skips)]
19
14 class AnnotateTests(unittest.TestCase):
20 class AnnotateTests(unittest.TestCase):
15 """Unit tests for annotate code."""
21 """Unit tests for annotate code."""
16
22
@@ -26,16 +32,16 b' class AnnotateTests(unittest.TestCase):'
26 diffopts = mdiff.diffopts()
32 diffopts = mdiff.diffopts()
27
33
28 def decorate(text, fctx):
34 def decorate(text, fctx):
29 return ([annotateline(fctx=fctx, lineno=i)
35 n = text.count(b'\n')
30 for i in range(1, text.count(b'\n') + 1)],
36 linenos = pycompat.rangelist(1, n + 1)
31 text)
37 return _annotatedfile([fctx] * n, linenos, [False] * n, text)
32
38
33 # Basic usage
39 # Basic usage
34
40
35 oldann = decorate(olddata, oldfctx)
41 oldann = decorate(olddata, oldfctx)
36 p1ann = decorate(p1data, p1fctx)
42 p1ann = decorate(p1data, p1fctx)
37 p1ann = _annotatepair([oldann], p1fctx, p1ann, False, diffopts)
43 p1ann = _annotatepair([oldann], p1fctx, p1ann, False, diffopts)
38 self.assertEqual(p1ann[0], [
44 self.assertEqual(tr(p1ann), [
39 annotateline(b'old', 1),
45 annotateline(b'old', 1),
40 annotateline(b'old', 2),
46 annotateline(b'old', 2),
41 annotateline(b'p1', 3),
47 annotateline(b'p1', 3),
@@ -43,7 +49,7 b' class AnnotateTests(unittest.TestCase):'
43
49
44 p2ann = decorate(p2data, p2fctx)
50 p2ann = decorate(p2data, p2fctx)
45 p2ann = _annotatepair([oldann], p2fctx, p2ann, False, diffopts)
51 p2ann = _annotatepair([oldann], p2fctx, p2ann, False, diffopts)
46 self.assertEqual(p2ann[0], [
52 self.assertEqual(tr(p2ann), [
47 annotateline(b'old', 1),
53 annotateline(b'old', 1),
48 annotateline(b'p2', 2),
54 annotateline(b'p2', 2),
49 annotateline(b'p2', 3),
55 annotateline(b'p2', 3),
@@ -54,7 +60,7 b' class AnnotateTests(unittest.TestCase):'
54 childann = decorate(childdata, childfctx)
60 childann = decorate(childdata, childfctx)
55 childann = _annotatepair([p1ann, p2ann], childfctx, childann, False,
61 childann = _annotatepair([p1ann, p2ann], childfctx, childann, False,
56 diffopts)
62 diffopts)
57 self.assertEqual(childann[0], [
63 self.assertEqual(tr(childann), [
58 annotateline(b'old', 1),
64 annotateline(b'old', 1),
59 annotateline(b'c', 2),
65 annotateline(b'c', 2),
60 annotateline(b'p2', 2),
66 annotateline(b'p2', 2),
@@ -65,7 +71,7 b' class AnnotateTests(unittest.TestCase):'
65 childann = decorate(childdata, childfctx)
71 childann = decorate(childdata, childfctx)
66 childann = _annotatepair([p2ann, p1ann], childfctx, childann, False,
72 childann = _annotatepair([p2ann, p1ann], childfctx, childann, False,
67 diffopts)
73 diffopts)
68 self.assertEqual(childann[0], [
74 self.assertEqual(tr(childann), [
69 annotateline(b'old', 1),
75 annotateline(b'old', 1),
70 annotateline(b'c', 2),
76 annotateline(b'c', 2),
71 annotateline(b'p1', 3),
77 annotateline(b'p1', 3),
@@ -78,7 +84,7 b' class AnnotateTests(unittest.TestCase):'
78 childann = decorate(childdata, childfctx)
84 childann = decorate(childdata, childfctx)
79 childann = _annotatepair([p1ann, p2ann], childfctx, childann, True,
85 childann = _annotatepair([p1ann, p2ann], childfctx, childann, True,
80 diffopts)
86 diffopts)
81 self.assertEqual(childann[0], [
87 self.assertEqual(tr(childann), [
82 annotateline(b'old', 1),
88 annotateline(b'old', 1),
83 annotateline(b'old', 2, True),
89 annotateline(b'old', 2, True),
84 # note that this line was carried over from earlier so it is *not*
90 # note that this line was carried over from earlier so it is *not*
@@ -91,7 +97,7 b' class AnnotateTests(unittest.TestCase):'
91 childann = decorate(childdata, childfctx)
97 childann = decorate(childdata, childfctx)
92 childann = _annotatepair([p2ann, p1ann], childfctx, childann, True,
98 childann = _annotatepair([p2ann, p1ann], childfctx, childann, True,
93 diffopts)
99 diffopts)
94 self.assertEqual(childann[0], [
100 self.assertEqual(tr(childann), [
95 annotateline(b'old', 1),
101 annotateline(b'old', 1),
96 annotateline(b'old', 2, True),
102 annotateline(b'old', 2, True),
97 annotateline(b'p1', 3),
103 annotateline(b'p1', 3),
General Comments 0
You need to be logged in to leave comments. Login now