Show More
@@ -592,3 +592,196 b' def show_changeset(ui, repo, opts, buffe' | |||||
592 | return t |
|
592 | return t | |
593 | return changeset_printer(ui, repo, patch, br, buffered) |
|
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 | (logfile, inst.strerror)) |
|
49 | (logfile, inst.strerror)) | |
50 | return message |
|
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 | def write_bundle(cg, filename=None, compress=True): |
|
52 | def write_bundle(cg, filename=None, compress=True): | |
247 | """Write a bundle file and return its filename. |
|
53 | """Write a bundle file and return its filename. | |
248 |
|
54 | |||
@@ -1352,7 +1158,7 b' def grep(ui, repo, pattern, *pats, **opt' | |||||
1352 | if opts['all']: |
|
1158 | if opts['all']: | |
1353 | cols.append(change) |
|
1159 | cols.append(change) | |
1354 | if opts['user']: |
|
1160 | if opts['user']: | |
1355 |
cols.append(ui.shortuser(get |
|
1161 | cols.append(ui.shortuser(get(r)[1])) | |
1356 | if opts['files_with_matches']: |
|
1162 | if opts['files_with_matches']: | |
1357 | c = (fn, r) |
|
1163 | c = (fn, r) | |
1358 | if c in filerevmatches: |
|
1164 | if c in filerevmatches: | |
@@ -1366,8 +1172,8 b' def grep(ui, repo, pattern, *pats, **opt' | |||||
1366 |
|
1172 | |||
1367 | fstate = {} |
|
1173 | fstate = {} | |
1368 | skip = {} |
|
1174 | skip = {} | |
1369 |
get |
|
1175 | get = util.cachefunc(lambda r:repo.changectx(r).changeset()) | |
1370 |
changeiter, matchfn = walkchangerevs(ui, repo, pats, get |
|
1176 | changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) | |
1371 | count = 0 |
|
1177 | count = 0 | |
1372 | incrementing = False |
|
1178 | incrementing = False | |
1373 | follow = opts.get('follow') |
|
1179 | follow = opts.get('follow') | |
@@ -1664,8 +1470,8 b' def log(ui, repo, *pats, **opts):' | |||||
1664 | files and full commit message is shown. |
|
1470 | files and full commit message is shown. | |
1665 | """ |
|
1471 | """ | |
1666 |
|
1472 | |||
1667 |
get |
|
1473 | get = util.cachefunc(lambda r:repo.changectx(r).changeset()) | |
1668 |
changeiter, matchfn = walkchangerevs(ui, repo, pats, get |
|
1474 | changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) | |
1669 |
|
1475 | |||
1670 | if opts['limit']: |
|
1476 | if opts['limit']: | |
1671 | try: |
|
1477 | try: | |
@@ -1725,7 +1531,7 b' def log(ui, repo, *pats, **opts):' | |||||
1725 | continue |
|
1531 | continue | |
1726 |
|
1532 | |||
1727 | if opts['keyword']: |
|
1533 | if opts['keyword']: | |
1728 |
changes = get |
|
1534 | changes = get(rev) | |
1729 | miss = 0 |
|
1535 | miss = 0 | |
1730 | for k in [kw.lower() for kw in opts['keyword']]: |
|
1536 | for k in [kw.lower() for kw in opts['keyword']]: | |
1731 | if not (k in changes[1].lower() or |
|
1537 | if not (k in changes[1].lower() or | |
@@ -1738,8 +1544,8 b' def log(ui, repo, *pats, **opts):' | |||||
1738 |
|
1544 | |||
1739 | copies = [] |
|
1545 | copies = [] | |
1740 | if opts.get('copies') and rev: |
|
1546 | if opts.get('copies') and rev: | |
1741 |
mf = get |
|
1547 | mf = get(rev)[0] | |
1742 |
for fn in get |
|
1548 | for fn in get(rev)[3]: | |
1743 | rename = getrenamed(fn, rev, mf) |
|
1549 | rename = getrenamed(fn, rev, mf) | |
1744 | if rename: |
|
1550 | if rename: | |
1745 | copies.append((fn, rename[0])) |
|
1551 | copies.append((fn, rename[0])) |
General Comments 0
You need to be logged in to leave comments.
Login now