Mercurial > public > mercurial-scm > hg
comparison mercurial/commands.py @ 3650:731e739b8659
move walkchangerevs to cmdutils
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Wed, 15 Nov 2006 15:51:58 -0600 |
parents | e50891e461e4 |
children | 6389205291c6 |
comparison
equal
deleted
inserted
replaced
3649:e50891e461e4 | 3650:731e739b8659 |
---|---|
46 message = open(logfile).read() | 46 message = open(logfile).read() |
47 except IOError, inst: | 47 except IOError, inst: |
48 raise util.Abort(_("can't read commit message '%s': %s") % | 48 raise util.Abort(_("can't read commit message '%s': %s") % |
49 (logfile, inst.strerror)) | 49 (logfile, inst.strerror)) |
50 return message | 50 return message |
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 | 51 |
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 |
249 Existing files will not be overwritten. | 55 Existing files will not be overwritten. |
1350 if opts['line_number']: | 1156 if opts['line_number']: |
1351 cols.append(str(l.linenum)) | 1157 cols.append(str(l.linenum)) |
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(getchange(r)[1])) | 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: |
1359 continue | 1165 continue |
1360 filerevmatches[c] = 1 | 1166 filerevmatches[c] = 1 |
1364 counts[change] += 1 | 1170 counts[change] += 1 |
1365 return counts['+'], counts['-'] | 1171 return counts['+'], counts['-'] |
1366 | 1172 |
1367 fstate = {} | 1173 fstate = {} |
1368 skip = {} | 1174 skip = {} |
1369 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset()) | 1175 get = util.cachefunc(lambda r:repo.changectx(r).changeset()) |
1370 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts) | 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') |
1374 for st, rev, fns in changeiter: | 1180 for st, rev, fns in changeiter: |
1375 if st == 'window': | 1181 if st == 'window': |
1662 non-trivial parents, user, date and time, and a summary for each | 1468 non-trivial parents, user, date and time, and a summary for each |
1663 commit. When the -v/--verbose switch is used, the list of changed | 1469 commit. When the -v/--verbose switch is used, the list of changed |
1664 files and full commit message is shown. | 1470 files and full commit message is shown. |
1665 """ | 1471 """ |
1666 | 1472 |
1667 getchange = util.cachefunc(lambda r:repo.changectx(r).changeset()) | 1473 get = util.cachefunc(lambda r:repo.changectx(r).changeset()) |
1668 changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts) | 1474 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts) |
1669 | 1475 |
1670 if opts['limit']: | 1476 if opts['limit']: |
1671 try: | 1477 try: |
1672 limit = int(opts['limit']) | 1478 limit = int(opts['limit']) |
1673 except ValueError: | 1479 except ValueError: |
1723 continue | 1529 continue |
1724 if opts['only_merges'] and len(parents) != 2: | 1530 if opts['only_merges'] and len(parents) != 2: |
1725 continue | 1531 continue |
1726 | 1532 |
1727 if opts['keyword']: | 1533 if opts['keyword']: |
1728 changes = getchange(rev) | 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 |
1732 k in changes[4].lower() or | 1538 k in changes[4].lower() or |
1733 k in " ".join(changes[3][:20]).lower()): | 1539 k in " ".join(changes[3][:20]).lower()): |
1736 if miss: | 1542 if miss: |
1737 continue | 1543 continue |
1738 | 1544 |
1739 copies = [] | 1545 copies = [] |
1740 if opts.get('copies') and rev: | 1546 if opts.get('copies') and rev: |
1741 mf = getchange(rev)[0] | 1547 mf = get(rev)[0] |
1742 for fn in getchange(rev)[3]: | 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])) |
1746 displayer.show(rev, changenode, copies=copies) | 1552 displayer.show(rev, changenode, copies=copies) |
1747 elif st == 'iter': | 1553 elif st == 'iter': |