Show More
@@ -592,3 +592,196 b' def show_changeset(ui, repo, opts, buffe' | |||
|
592 | 592 | return t |
|
593 | 593 | return changeset_printer(ui, repo, patch, br, buffered) |
|
594 | 594 | |
|
595 | def walkchangerevs(ui, repo, pats, change, opts): | |
|
596 | '''Iterate over files and the revs they changed in. | |
|
597 | ||
|
598 | Callers most commonly need to iterate backwards over the history | |
|
599 | it is interested in. Doing so has awful (quadratic-looking) | |
|
600 | performance, so we use iterators in a "windowed" way. | |
|
601 | ||
|
602 | We walk a window of revisions in the desired order. Within the | |
|
603 | window, we first walk forwards to gather data, then in the desired | |
|
604 | order (usually backwards) to display it. | |
|
605 | ||
|
606 | This function returns an (iterator, matchfn) tuple. The iterator | |
|
607 | yields 3-tuples. They will be of one of the following forms: | |
|
608 | ||
|
609 | "window", incrementing, lastrev: stepping through a window, | |
|
610 | positive if walking forwards through revs, last rev in the | |
|
611 | sequence iterated over - use to reset state for the current window | |
|
612 | ||
|
613 | "add", rev, fns: out-of-order traversal of the given file names | |
|
614 | fns, which changed during revision rev - use to gather data for | |
|
615 | possible display | |
|
616 | ||
|
617 | "iter", rev, None: in-order traversal of the revs earlier iterated | |
|
618 | over with "add" - use to display data''' | |
|
619 | ||
|
620 | def increasing_windows(start, end, windowsize=8, sizelimit=512): | |
|
621 | if start < end: | |
|
622 | while start < end: | |
|
623 | yield start, min(windowsize, end-start) | |
|
624 | start += windowsize | |
|
625 | if windowsize < sizelimit: | |
|
626 | windowsize *= 2 | |
|
627 | else: | |
|
628 | while start > end: | |
|
629 | yield start, min(windowsize, start-end-1) | |
|
630 | start -= windowsize | |
|
631 | if windowsize < sizelimit: | |
|
632 | windowsize *= 2 | |
|
633 | ||
|
634 | files, matchfn, anypats = matchpats(repo, pats, opts) | |
|
635 | follow = opts.get('follow') or opts.get('follow_first') | |
|
636 | ||
|
637 | if repo.changelog.count() == 0: | |
|
638 | return [], matchfn | |
|
639 | ||
|
640 | if follow: | |
|
641 | defrange = '%s:0' % repo.changectx().rev() | |
|
642 | else: | |
|
643 | defrange = 'tip:0' | |
|
644 | revs = revrange(ui, repo, opts['rev'] or [defrange]) | |
|
645 | wanted = {} | |
|
646 | slowpath = anypats | |
|
647 | fncache = {} | |
|
648 | ||
|
649 | if not slowpath and not files: | |
|
650 | # No files, no patterns. Display all revs. | |
|
651 | wanted = dict.fromkeys(revs) | |
|
652 | copies = [] | |
|
653 | if not slowpath: | |
|
654 | # Only files, no patterns. Check the history of each file. | |
|
655 | def filerevgen(filelog, node): | |
|
656 | cl_count = repo.changelog.count() | |
|
657 | if node is None: | |
|
658 | last = filelog.count() - 1 | |
|
659 | else: | |
|
660 | last = filelog.rev(node) | |
|
661 | for i, window in increasing_windows(last, nullrev): | |
|
662 | revs = [] | |
|
663 | for j in xrange(i - window, i + 1): | |
|
664 | n = filelog.node(j) | |
|
665 | revs.append((filelog.linkrev(n), | |
|
666 | follow and filelog.renamed(n))) | |
|
667 | revs.reverse() | |
|
668 | for rev in revs: | |
|
669 | # only yield rev for which we have the changelog, it can | |
|
670 | # happen while doing "hg log" during a pull or commit | |
|
671 | if rev[0] < cl_count: | |
|
672 | yield rev | |
|
673 | def iterfiles(): | |
|
674 | for filename in files: | |
|
675 | yield filename, None | |
|
676 | for filename_node in copies: | |
|
677 | yield filename_node | |
|
678 | minrev, maxrev = min(revs), max(revs) | |
|
679 | for file_, node in iterfiles(): | |
|
680 | filelog = repo.file(file_) | |
|
681 | # A zero count may be a directory or deleted file, so | |
|
682 | # try to find matching entries on the slow path. | |
|
683 | if filelog.count() == 0: | |
|
684 | slowpath = True | |
|
685 | break | |
|
686 | for rev, copied in filerevgen(filelog, node): | |
|
687 | if rev <= maxrev: | |
|
688 | if rev < minrev: | |
|
689 | break | |
|
690 | fncache.setdefault(rev, []) | |
|
691 | fncache[rev].append(file_) | |
|
692 | wanted[rev] = 1 | |
|
693 | if follow and copied: | |
|
694 | copies.append(copied) | |
|
695 | if slowpath: | |
|
696 | if follow: | |
|
697 | raise util.Abort(_('can only follow copies/renames for explicit ' | |
|
698 | 'file names')) | |
|
699 | ||
|
700 | # The slow path checks files modified in every changeset. | |
|
701 | def changerevgen(): | |
|
702 | for i, window in increasing_windows(repo.changelog.count()-1, | |
|
703 | nullrev): | |
|
704 | for j in xrange(i - window, i + 1): | |
|
705 | yield j, change(j)[3] | |
|
706 | ||
|
707 | for rev, changefiles in changerevgen(): | |
|
708 | matches = filter(matchfn, changefiles) | |
|
709 | if matches: | |
|
710 | fncache[rev] = matches | |
|
711 | wanted[rev] = 1 | |
|
712 | ||
|
713 | class followfilter: | |
|
714 | def __init__(self, onlyfirst=False): | |
|
715 | self.startrev = nullrev | |
|
716 | self.roots = [] | |
|
717 | self.onlyfirst = onlyfirst | |
|
718 | ||
|
719 | def match(self, rev): | |
|
720 | def realparents(rev): | |
|
721 | if self.onlyfirst: | |
|
722 | return repo.changelog.parentrevs(rev)[0:1] | |
|
723 | else: | |
|
724 | return filter(lambda x: x != nullrev, | |
|
725 | repo.changelog.parentrevs(rev)) | |
|
726 | ||
|
727 | if self.startrev == nullrev: | |
|
728 | self.startrev = rev | |
|
729 | return True | |
|
730 | ||
|
731 | if rev > self.startrev: | |
|
732 | # forward: all descendants | |
|
733 | if not self.roots: | |
|
734 | self.roots.append(self.startrev) | |
|
735 | for parent in realparents(rev): | |
|
736 | if parent in self.roots: | |
|
737 | self.roots.append(rev) | |
|
738 | return True | |
|
739 | else: | |
|
740 | # backwards: all parents | |
|
741 | if not self.roots: | |
|
742 | self.roots.extend(realparents(self.startrev)) | |
|
743 | if rev in self.roots: | |
|
744 | self.roots.remove(rev) | |
|
745 | self.roots.extend(realparents(rev)) | |
|
746 | return True | |
|
747 | ||
|
748 | return False | |
|
749 | ||
|
750 | # it might be worthwhile to do this in the iterator if the rev range | |
|
751 | # is descending and the prune args are all within that range | |
|
752 | for rev in opts.get('prune', ()): | |
|
753 | rev = repo.changelog.rev(repo.lookup(rev)) | |
|
754 | ff = followfilter() | |
|
755 | stop = min(revs[0], revs[-1]) | |
|
756 | for x in xrange(rev, stop-1, -1): | |
|
757 | if ff.match(x) and x in wanted: | |
|
758 | del wanted[x] | |
|
759 | ||
|
760 | def iterate(): | |
|
761 | if follow and not files: | |
|
762 | ff = followfilter(onlyfirst=opts.get('follow_first')) | |
|
763 | def want(rev): | |
|
764 | if ff.match(rev) and rev in wanted: | |
|
765 | return True | |
|
766 | return False | |
|
767 | else: | |
|
768 | def want(rev): | |
|
769 | return rev in wanted | |
|
770 | ||
|
771 | for i, window in increasing_windows(0, len(revs)): | |
|
772 | yield 'window', revs[0] < revs[-1], revs[-1] | |
|
773 | nrevs = [rev for rev in revs[i:i+window] if want(rev)] | |
|
774 | srevs = list(nrevs) | |
|
775 | srevs.sort() | |
|
776 | for rev in srevs: | |
|
777 | fns = fncache.get(rev) | |
|
778 | if not fns: | |
|
779 | def fns_generator(): | |
|
780 | for f in change(rev)[3]: | |
|
781 | if matchfn(f): | |
|
782 | yield f | |
|
783 | fns = fns_generator() | |
|
784 | yield 'add', rev, fns | |
|
785 | for rev in nrevs: | |
|
786 | yield 'iter', rev, None | |
|
787 | return iterate(), matchfn |
@@ -49,200 +49,6 b' def logmessage(opts):' | |||
|
49 | 49 | (logfile, inst.strerror)) |
|
50 | 50 | return message |
|
51 | 51 | |
|
52 | def walkchangerevs(ui, repo, pats, change, opts): | |
|
53 | '''Iterate over files and the revs they changed in. | |
|
54 | ||
|
55 | Callers most commonly need to iterate backwards over the history | |
|
56 | it is interested in. Doing so has awful (quadratic-looking) | |
|
57 | performance, so we use iterators in a "windowed" way. | |
|
58 | ||
|
59 | We walk a window of revisions in the desired order. Within the | |
|
60 | window, we first walk forwards to gather data, then in the desired | |
|
61 | order (usually backwards) to display it. | |
|
62 | ||
|
63 | This function returns an (iterator, matchfn) tuple. The iterator | |
|
64 | yields 3-tuples. They will be of one of the following forms: | |
|
65 | ||
|
66 | "window", incrementing, lastrev: stepping through a window, | |
|
67 | positive if walking forwards through revs, last rev in the | |
|
68 | sequence iterated over - use to reset state for the current window | |
|
69 | ||
|
70 | "add", rev, fns: out-of-order traversal of the given file names | |
|
71 | fns, which changed during revision rev - use to gather data for | |
|
72 | possible display | |
|
73 | ||
|
74 | "iter", rev, None: in-order traversal of the revs earlier iterated | |
|
75 | over with "add" - use to display data''' | |
|
76 | ||
|
77 | def increasing_windows(start, end, windowsize=8, sizelimit=512): | |
|
78 | if start < end: | |
|
79 | while start < end: | |
|
80 | yield start, min(windowsize, end-start) | |
|
81 | start += windowsize | |
|
82 | if windowsize < sizelimit: | |
|
83 | windowsize *= 2 | |
|
84 | else: | |
|
85 | while start > end: | |
|
86 | yield start, min(windowsize, start-end-1) | |
|
87 | start -= windowsize | |
|
88 | if windowsize < sizelimit: | |
|
89 | windowsize *= 2 | |
|
90 | ||
|
91 | files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) | |
|
92 | follow = opts.get('follow') or opts.get('follow_first') | |
|
93 | ||
|
94 | if repo.changelog.count() == 0: | |
|
95 | return [], matchfn | |
|
96 | ||
|
97 | if follow: | |
|
98 | defrange = '%s:0' % repo.changectx().rev() | |
|
99 | else: | |
|
100 | defrange = 'tip:0' | |
|
101 | revs = cmdutil.revrange(ui, repo, opts['rev'] or [defrange]) | |
|
102 | wanted = {} | |
|
103 | slowpath = anypats | |
|
104 | fncache = {} | |
|
105 | ||
|
106 | if not slowpath and not files: | |
|
107 | # No files, no patterns. Display all revs. | |
|
108 | wanted = dict.fromkeys(revs) | |
|
109 | copies = [] | |
|
110 | if not slowpath: | |
|
111 | # Only files, no patterns. Check the history of each file. | |
|
112 | def filerevgen(filelog, node): | |
|
113 | cl_count = repo.changelog.count() | |
|
114 | if node is None: | |
|
115 | last = filelog.count() - 1 | |
|
116 | else: | |
|
117 | last = filelog.rev(node) | |
|
118 | for i, window in increasing_windows(last, nullrev): | |
|
119 | revs = [] | |
|
120 | for j in xrange(i - window, i + 1): | |
|
121 | n = filelog.node(j) | |
|
122 | revs.append((filelog.linkrev(n), | |
|
123 | follow and filelog.renamed(n))) | |
|
124 | revs.reverse() | |
|
125 | for rev in revs: | |
|
126 | # only yield rev for which we have the changelog, it can | |
|
127 | # happen while doing "hg log" during a pull or commit | |
|
128 | if rev[0] < cl_count: | |
|
129 | yield rev | |
|
130 | def iterfiles(): | |
|
131 | for filename in files: | |
|
132 | yield filename, None | |
|
133 | for filename_node in copies: | |
|
134 | yield filename_node | |
|
135 | minrev, maxrev = min(revs), max(revs) | |
|
136 | for file_, node in iterfiles(): | |
|
137 | filelog = repo.file(file_) | |
|
138 | # A zero count may be a directory or deleted file, so | |
|
139 | # try to find matching entries on the slow path. | |
|
140 | if filelog.count() == 0: | |
|
141 | slowpath = True | |
|
142 | break | |
|
143 | for rev, copied in filerevgen(filelog, node): | |
|
144 | if rev <= maxrev: | |
|
145 | if rev < minrev: | |
|
146 | break | |
|
147 | fncache.setdefault(rev, []) | |
|
148 | fncache[rev].append(file_) | |
|
149 | wanted[rev] = 1 | |
|
150 | if follow and copied: | |
|
151 | copies.append(copied) | |
|
152 | if slowpath: | |
|
153 | if follow: | |
|
154 | raise util.Abort(_('can only follow copies/renames for explicit ' | |
|
155 | 'file names')) | |
|
156 | ||
|
157 | # The slow path checks files modified in every changeset. | |
|
158 | def changerevgen(): | |
|
159 | for i, window in increasing_windows(repo.changelog.count()-1, | |
|
160 | nullrev): | |
|
161 | for j in xrange(i - window, i + 1): | |
|
162 | yield j, change(j)[3] | |
|
163 | ||
|
164 | for rev, changefiles in changerevgen(): | |
|
165 | matches = filter(matchfn, changefiles) | |
|
166 | if matches: | |
|
167 | fncache[rev] = matches | |
|
168 | wanted[rev] = 1 | |
|
169 | ||
|
170 | class followfilter: | |
|
171 | def __init__(self, onlyfirst=False): | |
|
172 | self.startrev = nullrev | |
|
173 | self.roots = [] | |
|
174 | self.onlyfirst = onlyfirst | |
|
175 | ||
|
176 | def match(self, rev): | |
|
177 | def realparents(rev): | |
|
178 | if self.onlyfirst: | |
|
179 | return repo.changelog.parentrevs(rev)[0:1] | |
|
180 | else: | |
|
181 | return filter(lambda x: x != nullrev, | |
|
182 | repo.changelog.parentrevs(rev)) | |
|
183 | ||
|
184 | if self.startrev == nullrev: | |
|
185 | self.startrev = rev | |
|
186 | return True | |
|
187 | ||
|
188 | if rev > self.startrev: | |
|
189 | # forward: all descendants | |
|
190 | if not self.roots: | |
|
191 | self.roots.append(self.startrev) | |
|
192 | for parent in realparents(rev): | |
|
193 | if parent in self.roots: | |
|
194 | self.roots.append(rev) | |
|
195 | return True | |
|
196 | else: | |
|
197 | # backwards: all parents | |
|
198 | if not self.roots: | |
|
199 | self.roots.extend(realparents(self.startrev)) | |
|
200 | if rev in self.roots: | |
|
201 | self.roots.remove(rev) | |
|
202 | self.roots.extend(realparents(rev)) | |
|
203 | return True | |
|
204 | ||
|
205 | return False | |
|
206 | ||
|
207 | # it might be worthwhile to do this in the iterator if the rev range | |
|
208 | # is descending and the prune args are all within that range | |
|
209 | for rev in opts.get('prune', ()): | |
|
210 | rev = repo.changelog.rev(repo.lookup(rev)) | |
|
211 | ff = followfilter() | |
|
212 | stop = min(revs[0], revs[-1]) | |
|
213 | for x in xrange(rev, stop-1, -1): | |
|
214 | if ff.match(x) and x in wanted: | |
|
215 | del wanted[x] | |
|
216 | ||
|
217 | def iterate(): | |
|
218 | if follow and not files: | |
|
219 | ff = followfilter(onlyfirst=opts.get('follow_first')) | |
|
220 | def want(rev): | |
|
221 | if ff.match(rev) and rev in wanted: | |
|
222 | return True | |
|
223 | return False | |
|
224 | else: | |
|
225 | def want(rev): | |
|
226 | return rev in wanted | |
|
227 | ||
|
228 | for i, window in increasing_windows(0, len(revs)): | |
|
229 | yield 'window', revs[0] < revs[-1], revs[-1] | |
|
230 | nrevs = [rev for rev in revs[i:i+window] if want(rev)] | |
|
231 | srevs = list(nrevs) | |
|
232 | srevs.sort() | |
|
233 | for rev in srevs: | |
|
234 | fns = fncache.get(rev) | |
|
235 | if not fns: | |
|
236 | def fns_generator(): | |
|
237 | for f in change(rev)[3]: | |
|
238 | if matchfn(f): | |
|
239 | yield f | |
|
240 | fns = fns_generator() | |
|
241 | yield 'add', rev, fns | |
|
242 | for rev in nrevs: | |
|
243 | yield 'iter', rev, None | |
|
244 | return iterate(), matchfn | |
|
245 | ||
|
246 | 52 | def write_bundle(cg, filename=None, compress=True): |
|
247 | 53 | """Write a bundle file and return its filename. |
|
248 | 54 | |
@@ -1352,7 +1158,7 b' def grep(ui, repo, pattern, *pats, **opt' | |||
|
1352 | 1158 | if opts['all']: |
|
1353 | 1159 | cols.append(change) |
|
1354 | 1160 | if opts['user']: |
|
1355 |
cols.append(ui.shortuser(get |
|
|
1161 | cols.append(ui.shortuser(get(r)[1])) | |
|
1356 | 1162 | if opts['files_with_matches']: |
|
1357 | 1163 | c = (fn, r) |
|
1358 | 1164 | if c in filerevmatches: |
@@ -1366,8 +1172,8 b' def grep(ui, repo, pattern, *pats, **opt' | |||
|
1366 | 1172 | |
|
1367 | 1173 | fstate = {} |
|
1368 | 1174 | skip = {} |
|
1369 |
get |
|
|
1370 |
changeiter, matchfn = walkchangerevs(ui, repo, pats, get |
|
|
1175 | get = util.cachefunc(lambda r:repo.changectx(r).changeset()) | |
|
1176 | changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) | |
|
1371 | 1177 | count = 0 |
|
1372 | 1178 | incrementing = False |
|
1373 | 1179 | follow = opts.get('follow') |
@@ -1664,8 +1470,8 b' def log(ui, repo, *pats, **opts):' | |||
|
1664 | 1470 | files and full commit message is shown. |
|
1665 | 1471 | """ |
|
1666 | 1472 | |
|
1667 |
get |
|
|
1668 |
changeiter, matchfn = walkchangerevs(ui, repo, pats, get |
|
|
1473 | get = util.cachefunc(lambda r:repo.changectx(r).changeset()) | |
|
1474 | changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) | |
|
1669 | 1475 | |
|
1670 | 1476 | if opts['limit']: |
|
1671 | 1477 | try: |
@@ -1725,7 +1531,7 b' def log(ui, repo, *pats, **opts):' | |||
|
1725 | 1531 | continue |
|
1726 | 1532 | |
|
1727 | 1533 | if opts['keyword']: |
|
1728 |
changes = get |
|
|
1534 | changes = get(rev) | |
|
1729 | 1535 | miss = 0 |
|
1730 | 1536 | for k in [kw.lower() for kw in opts['keyword']]: |
|
1731 | 1537 | if not (k in changes[1].lower() or |
@@ -1738,8 +1544,8 b' def log(ui, repo, *pats, **opts):' | |||
|
1738 | 1544 | |
|
1739 | 1545 | copies = [] |
|
1740 | 1546 | if opts.get('copies') and rev: |
|
1741 |
mf = get |
|
|
1742 |
for fn in get |
|
|
1547 | mf = get(rev)[0] | |
|
1548 | for fn in get(rev)[3]: | |
|
1743 | 1549 | rename = getrenamed(fn, rev, mf) |
|
1744 | 1550 | if rename: |
|
1745 | 1551 | copies.append((fn, rename[0])) |
General Comments 0
You need to be logged in to leave comments.
Login now