##// END OF EJS Templates
use ui buffering in changeset printer...
Matt Mackall -
r3738:cb48cd27 default
parent child Browse files
Show More
@@ -1,787 +1,749 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), 'os sys')
11 demandload(globals(), 'os sys')
12 demandload(globals(), 'mdiff util templater cStringIO patch')
12 demandload(globals(), 'mdiff util templater patch')
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 def revpair(repo, revs):
16 def revpair(repo, revs):
17 '''return pair of nodes, given list of revisions. second item can
17 '''return pair of nodes, given list of revisions. second item can
18 be None, meaning use working dir.'''
18 be None, meaning use working dir.'''
19
19
20 def revfix(repo, val, defval):
20 def revfix(repo, val, defval):
21 if not val and val != 0:
21 if not val and val != 0:
22 val = defval
22 val = defval
23 return repo.lookup(val)
23 return repo.lookup(val)
24
24
25 if not revs:
25 if not revs:
26 return repo.dirstate.parents()[0], None
26 return repo.dirstate.parents()[0], None
27 end = None
27 end = None
28 if len(revs) == 1:
28 if len(revs) == 1:
29 if revrangesep in revs[0]:
29 if revrangesep in revs[0]:
30 start, end = revs[0].split(revrangesep, 1)
30 start, end = revs[0].split(revrangesep, 1)
31 start = revfix(repo, start, 0)
31 start = revfix(repo, start, 0)
32 end = revfix(repo, end, repo.changelog.count() - 1)
32 end = revfix(repo, end, repo.changelog.count() - 1)
33 else:
33 else:
34 start = revfix(repo, revs[0], None)
34 start = revfix(repo, revs[0], None)
35 elif len(revs) == 2:
35 elif len(revs) == 2:
36 if revrangesep in revs[0] or revrangesep in revs[1]:
36 if revrangesep in revs[0] or revrangesep in revs[1]:
37 raise util.Abort(_('too many revisions specified'))
37 raise util.Abort(_('too many revisions specified'))
38 start = revfix(repo, revs[0], None)
38 start = revfix(repo, revs[0], None)
39 end = revfix(repo, revs[1], None)
39 end = revfix(repo, revs[1], None)
40 else:
40 else:
41 raise util.Abort(_('too many revisions specified'))
41 raise util.Abort(_('too many revisions specified'))
42 return start, end
42 return start, end
43
43
44 def revrange(repo, revs):
44 def revrange(repo, revs):
45 """Yield revision as strings from a list of revision specifications."""
45 """Yield revision as strings from a list of revision specifications."""
46
46
47 def revfix(repo, val, defval):
47 def revfix(repo, val, defval):
48 if not val and val != 0 and defval is not None:
48 if not val and val != 0 and defval is not None:
49 return defval
49 return defval
50 return repo.changelog.rev(repo.lookup(val))
50 return repo.changelog.rev(repo.lookup(val))
51
51
52 seen, l = {}, []
52 seen, l = {}, []
53 for spec in revs:
53 for spec in revs:
54 if revrangesep in spec:
54 if revrangesep in spec:
55 start, end = spec.split(revrangesep, 1)
55 start, end = spec.split(revrangesep, 1)
56 start = revfix(repo, start, 0)
56 start = revfix(repo, start, 0)
57 end = revfix(repo, end, repo.changelog.count() - 1)
57 end = revfix(repo, end, repo.changelog.count() - 1)
58 step = start > end and -1 or 1
58 step = start > end and -1 or 1
59 for rev in xrange(start, end+step, step):
59 for rev in xrange(start, end+step, step):
60 if rev in seen:
60 if rev in seen:
61 continue
61 continue
62 seen[rev] = 1
62 seen[rev] = 1
63 l.append(rev)
63 l.append(rev)
64 else:
64 else:
65 rev = revfix(repo, spec, None)
65 rev = revfix(repo, spec, None)
66 if rev in seen:
66 if rev in seen:
67 continue
67 continue
68 seen[rev] = 1
68 seen[rev] = 1
69 l.append(rev)
69 l.append(rev)
70
70
71 return l
71 return l
72
72
73 def make_filename(repo, pat, node,
73 def make_filename(repo, pat, node,
74 total=None, seqno=None, revwidth=None, pathname=None):
74 total=None, seqno=None, revwidth=None, pathname=None):
75 node_expander = {
75 node_expander = {
76 'H': lambda: hex(node),
76 'H': lambda: hex(node),
77 'R': lambda: str(repo.changelog.rev(node)),
77 'R': lambda: str(repo.changelog.rev(node)),
78 'h': lambda: short(node),
78 'h': lambda: short(node),
79 }
79 }
80 expander = {
80 expander = {
81 '%': lambda: '%',
81 '%': lambda: '%',
82 'b': lambda: os.path.basename(repo.root),
82 'b': lambda: os.path.basename(repo.root),
83 }
83 }
84
84
85 try:
85 try:
86 if node:
86 if node:
87 expander.update(node_expander)
87 expander.update(node_expander)
88 if node and revwidth is not None:
88 if node and revwidth is not None:
89 expander['r'] = (lambda:
89 expander['r'] = (lambda:
90 str(repo.changelog.rev(node)).zfill(revwidth))
90 str(repo.changelog.rev(node)).zfill(revwidth))
91 if total is not None:
91 if total is not None:
92 expander['N'] = lambda: str(total)
92 expander['N'] = lambda: str(total)
93 if seqno is not None:
93 if seqno is not None:
94 expander['n'] = lambda: str(seqno)
94 expander['n'] = lambda: str(seqno)
95 if total is not None and seqno is not None:
95 if total is not None and seqno is not None:
96 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
96 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
97 if pathname is not None:
97 if pathname is not None:
98 expander['s'] = lambda: os.path.basename(pathname)
98 expander['s'] = lambda: os.path.basename(pathname)
99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
99 expander['d'] = lambda: os.path.dirname(pathname) or '.'
100 expander['p'] = lambda: pathname
100 expander['p'] = lambda: pathname
101
101
102 newname = []
102 newname = []
103 patlen = len(pat)
103 patlen = len(pat)
104 i = 0
104 i = 0
105 while i < patlen:
105 while i < patlen:
106 c = pat[i]
106 c = pat[i]
107 if c == '%':
107 if c == '%':
108 i += 1
108 i += 1
109 c = pat[i]
109 c = pat[i]
110 c = expander[c]()
110 c = expander[c]()
111 newname.append(c)
111 newname.append(c)
112 i += 1
112 i += 1
113 return ''.join(newname)
113 return ''.join(newname)
114 except KeyError, inst:
114 except KeyError, inst:
115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
115 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
116 inst.args[0])
116 inst.args[0])
117
117
118 def make_file(repo, pat, node=None,
118 def make_file(repo, pat, node=None,
119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
119 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
120 if not pat or pat == '-':
120 if not pat or pat == '-':
121 return 'w' in mode and sys.stdout or sys.stdin
121 return 'w' in mode and sys.stdout or sys.stdin
122 if hasattr(pat, 'write') and 'w' in mode:
122 if hasattr(pat, 'write') and 'w' in mode:
123 return pat
123 return pat
124 if hasattr(pat, 'read') and 'r' in mode:
124 if hasattr(pat, 'read') and 'r' in mode:
125 return pat
125 return pat
126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
126 return open(make_filename(repo, pat, node, total, seqno, revwidth,
127 pathname),
127 pathname),
128 mode)
128 mode)
129
129
130 def matchpats(repo, pats=[], opts={}, head=''):
130 def matchpats(repo, pats=[], opts={}, head=''):
131 cwd = repo.getcwd()
131 cwd = repo.getcwd()
132 if not pats and cwd:
132 if not pats and cwd:
133 opts['include'] = [os.path.join(cwd, i)
133 opts['include'] = [os.path.join(cwd, i)
134 for i in opts.get('include', [])]
134 for i in opts.get('include', [])]
135 opts['exclude'] = [os.path.join(cwd, x)
135 opts['exclude'] = [os.path.join(cwd, x)
136 for x in opts.get('exclude', [])]
136 for x in opts.get('exclude', [])]
137 cwd = ''
137 cwd = ''
138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
138 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
139 opts.get('exclude'), head)
139 opts.get('exclude'), head)
140
140
141 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
141 def walk(repo, pats=[], opts={}, node=None, head='', badmatch=None):
142 files, matchfn, anypats = matchpats(repo, pats, opts, head)
142 files, matchfn, anypats = matchpats(repo, pats, opts, head)
143 exact = dict.fromkeys(files)
143 exact = dict.fromkeys(files)
144 for src, fn in repo.walk(node=node, files=files, match=matchfn,
144 for src, fn in repo.walk(node=node, files=files, match=matchfn,
145 badmatch=badmatch):
145 badmatch=badmatch):
146 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
146 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
147
147
148 def findrenames(repo, added=None, removed=None, threshold=0.5):
148 def findrenames(repo, added=None, removed=None, threshold=0.5):
149 if added is None or removed is None:
149 if added is None or removed is None:
150 added, removed = repo.status()[1:3]
150 added, removed = repo.status()[1:3]
151 changes = repo.changelog.read(repo.dirstate.parents()[0])
151 changes = repo.changelog.read(repo.dirstate.parents()[0])
152 mf = repo.manifest.read(changes[0])
152 mf = repo.manifest.read(changes[0])
153 for a in added:
153 for a in added:
154 aa = repo.wread(a)
154 aa = repo.wread(a)
155 bestscore, bestname = None, None
155 bestscore, bestname = None, None
156 for r in removed:
156 for r in removed:
157 rr = repo.file(r).read(mf[r])
157 rr = repo.file(r).read(mf[r])
158 delta = mdiff.textdiff(aa, rr)
158 delta = mdiff.textdiff(aa, rr)
159 if len(delta) < len(aa):
159 if len(delta) < len(aa):
160 myscore = 1.0 - (float(len(delta)) / len(aa))
160 myscore = 1.0 - (float(len(delta)) / len(aa))
161 if bestscore is None or myscore > bestscore:
161 if bestscore is None or myscore > bestscore:
162 bestscore, bestname = myscore, r
162 bestscore, bestname = myscore, r
163 if bestname and bestscore >= threshold:
163 if bestname and bestscore >= threshold:
164 yield bestname, a, bestscore
164 yield bestname, a, bestscore
165
165
166 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
166 def addremove(repo, pats=[], opts={}, wlock=None, dry_run=None,
167 similarity=None):
167 similarity=None):
168 if dry_run is None:
168 if dry_run is None:
169 dry_run = opts.get('dry_run')
169 dry_run = opts.get('dry_run')
170 if similarity is None:
170 if similarity is None:
171 similarity = float(opts.get('similarity') or 0)
171 similarity = float(opts.get('similarity') or 0)
172 add, remove = [], []
172 add, remove = [], []
173 mapping = {}
173 mapping = {}
174 for src, abs, rel, exact in walk(repo, pats, opts):
174 for src, abs, rel, exact in walk(repo, pats, opts):
175 if src == 'f' and repo.dirstate.state(abs) == '?':
175 if src == 'f' and repo.dirstate.state(abs) == '?':
176 add.append(abs)
176 add.append(abs)
177 mapping[abs] = rel, exact
177 mapping[abs] = rel, exact
178 if repo.ui.verbose or not exact:
178 if repo.ui.verbose or not exact:
179 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
179 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
180 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
180 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
181 remove.append(abs)
181 remove.append(abs)
182 mapping[abs] = rel, exact
182 mapping[abs] = rel, exact
183 if repo.ui.verbose or not exact:
183 if repo.ui.verbose or not exact:
184 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
184 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
185 if not dry_run:
185 if not dry_run:
186 repo.add(add, wlock=wlock)
186 repo.add(add, wlock=wlock)
187 repo.remove(remove, wlock=wlock)
187 repo.remove(remove, wlock=wlock)
188 if similarity > 0:
188 if similarity > 0:
189 for old, new, score in findrenames(repo, add, remove, similarity):
189 for old, new, score in findrenames(repo, add, remove, similarity):
190 oldrel, oldexact = mapping[old]
190 oldrel, oldexact = mapping[old]
191 newrel, newexact = mapping[new]
191 newrel, newexact = mapping[new]
192 if repo.ui.verbose or not oldexact or not newexact:
192 if repo.ui.verbose or not oldexact or not newexact:
193 repo.ui.status(_('recording removal of %s as rename to %s '
193 repo.ui.status(_('recording removal of %s as rename to %s '
194 '(%d%% similar)\n') %
194 '(%d%% similar)\n') %
195 (oldrel, newrel, score * 100))
195 (oldrel, newrel, score * 100))
196 if not dry_run:
196 if not dry_run:
197 repo.copy(old, new, wlock=wlock)
197 repo.copy(old, new, wlock=wlock)
198
198
199 class uibuffer(object):
200 # Implement and delegate some ui protocol. Save hunks of
201 # output for later display in the desired order.
202 def __init__(self, ui):
203 self.ui = ui
204 self.hunk = {}
205 self.header = {}
206 self.quiet = ui.quiet
207 self.verbose = ui.verbose
208 self.debugflag = ui.debugflag
209 self.lastheader = None
210 def note(self, *args):
211 if self.verbose:
212 self.write(*args)
213 def status(self, *args):
214 if not self.quiet:
215 self.write(*args)
216 def debug(self, *args):
217 if self.debugflag:
218 self.write(*args)
219 def write(self, *args):
220 self.hunk.setdefault(self.rev, []).extend(args)
221 def write_header(self, *args):
222 self.header.setdefault(self.rev, []).extend(args)
223 def mark(self, rev):
224 self.rev = rev
225 def flush(self, rev):
226 if rev in self.header:
227 h = "".join(self.header[rev])
228 if h != self.lastheader:
229 self.lastheader = h
230 self.ui.write(h)
231 del self.header[rev]
232 if rev in self.hunk:
233 self.ui.write("".join(self.hunk[rev]))
234 del self.hunk[rev]
235 return 1
236 return 0
237
238 class changeset_printer(object):
199 class changeset_printer(object):
239 '''show changeset information when templating not requested.'''
200 '''show changeset information when templating not requested.'''
240
201
241 def __init__(self, ui, repo, patch, brinfo, buffered):
202 def __init__(self, ui, repo, patch, brinfo, buffered):
242 self.ui = ui
203 self.ui = ui
243 self.repo = repo
204 self.repo = repo
244 self.buffered = buffered
205 self.buffered = buffered
245 self.patch = patch
206 self.patch = patch
246 self.brinfo = brinfo
207 self.brinfo = brinfo
247 if buffered:
208 self.header = {}
248 self.ui = uibuffer(ui)
209 self.hunk = {}
210 self.lastheader = None
249
211
250 def flush(self, rev):
212 def flush(self, rev):
251 return self.ui.flush(rev)
213 if rev in self.header:
214 h = self.header[rev]
215 if h != self.lastheader:
216 self.lastheader = h
217 self.ui.write(h)
218 del self.header[rev]
219 if rev in self.hunk:
220 self.ui.write(self.hunk[rev])
221 del self.hunk[rev]
222 return 1
223 return 0
252
224
253 def show(self, rev=0, changenode=None, copies=None):
225 def show(self, rev=0, changenode=None, copies=None, **props):
226 if self.buffered:
227 self.ui.pushbuffer()
228 self._show(rev, changenode, copies, props)
229 self.hunk[rev] = self.ui.popbuffer()
230 else:
231 self._show(rev, changenode, copies, props)
232
233 def _show(self, rev, changenode, copies, props):
254 '''show a single changeset or file revision'''
234 '''show a single changeset or file revision'''
255 if self.buffered:
256 self.ui.mark(rev)
257 log = self.repo.changelog
235 log = self.repo.changelog
258 if changenode is None:
236 if changenode is None:
259 changenode = log.node(rev)
237 changenode = log.node(rev)
260 elif not rev:
238 elif not rev:
261 rev = log.rev(changenode)
239 rev = log.rev(changenode)
262
240
263 if self.ui.quiet:
241 if self.ui.quiet:
264 self.ui.write("%d:%s\n" % (rev, short(changenode)))
242 self.ui.write("%d:%s\n" % (rev, short(changenode)))
265 return
243 return
266
244
267 changes = log.read(changenode)
245 changes = log.read(changenode)
268 date = util.datestr(changes[2])
246 date = util.datestr(changes[2])
269 extra = changes[5]
247 extra = changes[5]
270 branch = extra.get("branch")
248 branch = extra.get("branch")
271
249
272 hexfunc = self.ui.debugflag and hex or short
250 hexfunc = self.ui.debugflag and hex or short
273
251
274 parents = log.parentrevs(rev)
252 parents = log.parentrevs(rev)
275 if not self.ui.debugflag:
253 if not self.ui.debugflag:
276 if parents[1] == nullrev:
254 if parents[1] == nullrev:
277 if parents[0] >= rev - 1:
255 if parents[0] >= rev - 1:
278 parents = []
256 parents = []
279 else:
257 else:
280 parents = [parents[0]]
258 parents = [parents[0]]
281 parents = [(p, hexfunc(log.node(p))) for p in parents]
259 parents = [(p, hexfunc(log.node(p))) for p in parents]
282
260
283 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
261 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
284
262
285 if branch:
263 if branch:
286 self.ui.write(_("branch: %s\n") % branch)
264 self.ui.write(_("branch: %s\n") % branch)
287 for tag in self.repo.nodetags(changenode):
265 for tag in self.repo.nodetags(changenode):
288 self.ui.write(_("tag: %s\n") % tag)
266 self.ui.write(_("tag: %s\n") % tag)
289 for parent in parents:
267 for parent in parents:
290 self.ui.write(_("parent: %d:%s\n") % parent)
268 self.ui.write(_("parent: %d:%s\n") % parent)
291
269
292 if self.brinfo:
270 if self.brinfo:
293 br = self.repo.branchlookup([changenode])
271 br = self.repo.branchlookup([changenode])
294 if br:
272 if br:
295 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
273 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
296
274
297 if self.ui.debugflag:
275 if self.ui.debugflag:
298 self.ui.write(_("manifest: %d:%s\n") %
276 self.ui.write(_("manifest: %d:%s\n") %
299 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
277 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
300 self.ui.write(_("user: %s\n") % changes[1])
278 self.ui.write(_("user: %s\n") % changes[1])
301 self.ui.write(_("date: %s\n") % date)
279 self.ui.write(_("date: %s\n") % date)
302
280
303 if self.ui.debugflag:
281 if self.ui.debugflag:
304 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
282 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
305 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
283 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
306 files):
284 files):
307 if value:
285 if value:
308 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
286 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
309 elif changes[3] and self.ui.verbose:
287 elif changes[3] and self.ui.verbose:
310 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
288 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
311 if copies and self.ui.verbose:
289 if copies and self.ui.verbose:
312 copies = ['%s (%s)' % c for c in copies]
290 copies = ['%s (%s)' % c for c in copies]
313 self.ui.write(_("copies: %s\n") % ' '.join(copies))
291 self.ui.write(_("copies: %s\n") % ' '.join(copies))
314
292
315 if extra and self.ui.debugflag:
293 if extra and self.ui.debugflag:
316 extraitems = extra.items()
294 extraitems = extra.items()
317 extraitems.sort()
295 extraitems.sort()
318 for key, value in extraitems:
296 for key, value in extraitems:
319 self.ui.write(_("extra: %s=%s\n")
297 self.ui.write(_("extra: %s=%s\n")
320 % (key, value.encode('string_escape')))
298 % (key, value.encode('string_escape')))
321
299
322 description = changes[4].strip()
300 description = changes[4].strip()
323 if description:
301 if description:
324 if self.ui.verbose:
302 if self.ui.verbose:
325 self.ui.write(_("description:\n"))
303 self.ui.write(_("description:\n"))
326 self.ui.write(description)
304 self.ui.write(description)
327 self.ui.write("\n\n")
305 self.ui.write("\n\n")
328 else:
306 else:
329 self.ui.write(_("summary: %s\n") %
307 self.ui.write(_("summary: %s\n") %
330 description.splitlines()[0])
308 description.splitlines()[0])
331 self.ui.write("\n")
309 self.ui.write("\n")
332
310
333 self.showpatch(changenode)
311 self.showpatch(changenode)
334
312
335 def showpatch(self, node):
313 def showpatch(self, node):
336 if self.patch:
314 if self.patch:
337 prev = self.repo.changelog.parents(node)[0]
315 prev = self.repo.changelog.parents(node)[0]
338 patch.diff(self.repo, prev, node, fp=self.ui)
316 patch.diff(self.repo, prev, node, fp=self.ui)
339 self.ui.write("\n")
317 self.ui.write("\n")
340
318
341 class changeset_templater(changeset_printer):
319 class changeset_templater(changeset_printer):
342 '''format changeset information.'''
320 '''format changeset information.'''
343
321
344 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
322 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
345 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
323 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
346 self.t = templater.templater(mapfile, templater.common_filters,
324 self.t = templater.templater(mapfile, templater.common_filters,
347 cache={'parent': '{rev}:{node|short} ',
325 cache={'parent': '{rev}:{node|short} ',
348 'manifest': '{rev}:{node|short}',
326 'manifest': '{rev}:{node|short}',
349 'filecopy': '{name} ({source})'})
327 'filecopy': '{name} ({source})'})
350
328
351 def use_template(self, t):
329 def use_template(self, t):
352 '''set template string to use'''
330 '''set template string to use'''
353 self.t.cache['changeset'] = t
331 self.t.cache['changeset'] = t
354
332
355 def show(self, rev=0, changenode=None, copies=[], **props):
333 def _show(self, rev, changenode, copies, props):
356 '''show a single changeset or file revision'''
334 '''show a single changeset or file revision'''
357 if self.buffered:
358 self.ui.mark(rev)
359 log = self.repo.changelog
335 log = self.repo.changelog
360 if changenode is None:
336 if changenode is None:
361 changenode = log.node(rev)
337 changenode = log.node(rev)
362 elif not rev:
338 elif not rev:
363 rev = log.rev(changenode)
339 rev = log.rev(changenode)
364
340
365 changes = log.read(changenode)
341 changes = log.read(changenode)
366
342
367 def showlist(name, values, plural=None, **args):
343 def showlist(name, values, plural=None, **args):
368 '''expand set of values.
344 '''expand set of values.
369 name is name of key in template map.
345 name is name of key in template map.
370 values is list of strings or dicts.
346 values is list of strings or dicts.
371 plural is plural of name, if not simply name + 's'.
347 plural is plural of name, if not simply name + 's'.
372
348
373 expansion works like this, given name 'foo'.
349 expansion works like this, given name 'foo'.
374
350
375 if values is empty, expand 'no_foos'.
351 if values is empty, expand 'no_foos'.
376
352
377 if 'foo' not in template map, return values as a string,
353 if 'foo' not in template map, return values as a string,
378 joined by space.
354 joined by space.
379
355
380 expand 'start_foos'.
356 expand 'start_foos'.
381
357
382 for each value, expand 'foo'. if 'last_foo' in template
358 for each value, expand 'foo'. if 'last_foo' in template
383 map, expand it instead of 'foo' for last key.
359 map, expand it instead of 'foo' for last key.
384
360
385 expand 'end_foos'.
361 expand 'end_foos'.
386 '''
362 '''
387 if plural: names = plural
363 if plural: names = plural
388 else: names = name + 's'
364 else: names = name + 's'
389 if not values:
365 if not values:
390 noname = 'no_' + names
366 noname = 'no_' + names
391 if noname in self.t:
367 if noname in self.t:
392 yield self.t(noname, **args)
368 yield self.t(noname, **args)
393 return
369 return
394 if name not in self.t:
370 if name not in self.t:
395 if isinstance(values[0], str):
371 if isinstance(values[0], str):
396 yield ' '.join(values)
372 yield ' '.join(values)
397 else:
373 else:
398 for v in values:
374 for v in values:
399 yield dict(v, **args)
375 yield dict(v, **args)
400 return
376 return
401 startname = 'start_' + names
377 startname = 'start_' + names
402 if startname in self.t:
378 if startname in self.t:
403 yield self.t(startname, **args)
379 yield self.t(startname, **args)
404 vargs = args.copy()
380 vargs = args.copy()
405 def one(v, tag=name):
381 def one(v, tag=name):
406 try:
382 try:
407 vargs.update(v)
383 vargs.update(v)
408 except (AttributeError, ValueError):
384 except (AttributeError, ValueError):
409 try:
385 try:
410 for a, b in v:
386 for a, b in v:
411 vargs[a] = b
387 vargs[a] = b
412 except ValueError:
388 except ValueError:
413 vargs[name] = v
389 vargs[name] = v
414 return self.t(tag, **vargs)
390 return self.t(tag, **vargs)
415 lastname = 'last_' + name
391 lastname = 'last_' + name
416 if lastname in self.t:
392 if lastname in self.t:
417 last = values.pop()
393 last = values.pop()
418 else:
394 else:
419 last = None
395 last = None
420 for v in values:
396 for v in values:
421 yield one(v)
397 yield one(v)
422 if last is not None:
398 if last is not None:
423 yield one(last, tag=lastname)
399 yield one(last, tag=lastname)
424 endname = 'end_' + names
400 endname = 'end_' + names
425 if endname in self.t:
401 if endname in self.t:
426 yield self.t(endname, **args)
402 yield self.t(endname, **args)
427
403
428 def showbranches(**args):
404 def showbranches(**args):
429 branch = changes[5].get("branch")
405 branch = changes[5].get("branch")
430 if branch:
406 if branch:
431 return showlist('branch', [branch], plural='branches', **args)
407 return showlist('branch', [branch], plural='branches', **args)
432 # add old style branches if requested
408 # add old style branches if requested
433 if self.brinfo:
409 if self.brinfo:
434 br = self.repo.branchlookup([changenode])
410 br = self.repo.branchlookup([changenode])
435 if changenode in br:
411 if changenode in br:
436 return showlist('branch', br[changenode],
412 return showlist('branch', br[changenode],
437 plural='branches', **args)
413 plural='branches', **args)
438
414
439 def showparents(**args):
415 def showparents(**args):
440 parents = [[('rev', log.rev(p)), ('node', hex(p))]
416 parents = [[('rev', log.rev(p)), ('node', hex(p))]
441 for p in log.parents(changenode)
417 for p in log.parents(changenode)
442 if self.ui.debugflag or p != nullid]
418 if self.ui.debugflag or p != nullid]
443 if (not self.ui.debugflag and len(parents) == 1 and
419 if (not self.ui.debugflag and len(parents) == 1 and
444 parents[0][0][1] == rev - 1):
420 parents[0][0][1] == rev - 1):
445 return
421 return
446 return showlist('parent', parents, **args)
422 return showlist('parent', parents, **args)
447
423
448 def showtags(**args):
424 def showtags(**args):
449 return showlist('tag', self.repo.nodetags(changenode), **args)
425 return showlist('tag', self.repo.nodetags(changenode), **args)
450
426
451 def showextras(**args):
427 def showextras(**args):
452 extras = changes[5].items()
428 extras = changes[5].items()
453 extras.sort()
429 extras.sort()
454 for key, value in extras:
430 for key, value in extras:
455 args = args.copy()
431 args = args.copy()
456 args.update(dict(key=key, value=value))
432 args.update(dict(key=key, value=value))
457 yield self.t('extra', **args)
433 yield self.t('extra', **args)
458
434
459 def showcopies(**args):
435 def showcopies(**args):
460 c = [{'name': x[0], 'source': x[1]} for x in copies]
436 c = [{'name': x[0], 'source': x[1]} for x in copies]
461 return showlist('file_copy', c, plural='file_copies', **args)
437 return showlist('file_copy', c, plural='file_copies', **args)
462
438
463 if self.ui.debugflag:
439 if self.ui.debugflag:
464 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
440 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
465 def showfiles(**args):
441 def showfiles(**args):
466 return showlist('file', files[0], **args)
442 return showlist('file', files[0], **args)
467 def showadds(**args):
443 def showadds(**args):
468 return showlist('file_add', files[1], **args)
444 return showlist('file_add', files[1], **args)
469 def showdels(**args):
445 def showdels(**args):
470 return showlist('file_del', files[2], **args)
446 return showlist('file_del', files[2], **args)
471 def showmanifest(**args):
447 def showmanifest(**args):
472 args = args.copy()
448 args = args.copy()
473 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
449 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
474 node=hex(changes[0])))
450 node=hex(changes[0])))
475 return self.t('manifest', **args)
451 return self.t('manifest', **args)
476 else:
452 else:
477 def showfiles(**args):
453 def showfiles(**args):
478 return showlist('file', changes[3], **args)
454 return showlist('file', changes[3], **args)
479 showadds = ''
455 showadds = ''
480 showdels = ''
456 showdels = ''
481 showmanifest = ''
457 showmanifest = ''
482
458
483 defprops = {
459 defprops = {
484 'author': changes[1],
460 'author': changes[1],
485 'branches': showbranches,
461 'branches': showbranches,
486 'date': changes[2],
462 'date': changes[2],
487 'desc': changes[4],
463 'desc': changes[4],
488 'file_adds': showadds,
464 'file_adds': showadds,
489 'file_dels': showdels,
465 'file_dels': showdels,
490 'files': showfiles,
466 'files': showfiles,
491 'file_copies': showcopies,
467 'file_copies': showcopies,
492 'manifest': showmanifest,
468 'manifest': showmanifest,
493 'node': hex(changenode),
469 'node': hex(changenode),
494 'parents': showparents,
470 'parents': showparents,
495 'rev': rev,
471 'rev': rev,
496 'tags': showtags,
472 'tags': showtags,
497 'extras': showextras,
473 'extras': showextras,
498 }
474 }
499 props = props.copy()
475 props = props.copy()
500 props.update(defprops)
476 props.update(defprops)
501
477
502 try:
478 try:
503 if self.ui.debugflag and 'header_debug' in self.t:
479 if self.ui.debugflag and 'header_debug' in self.t:
504 key = 'header_debug'
480 key = 'header_debug'
505 elif self.ui.quiet and 'header_quiet' in self.t:
481 elif self.ui.quiet and 'header_quiet' in self.t:
506 key = 'header_quiet'
482 key = 'header_quiet'
507 elif self.ui.verbose and 'header_verbose' in self.t:
483 elif self.ui.verbose and 'header_verbose' in self.t:
508 key = 'header_verbose'
484 key = 'header_verbose'
509 elif 'header' in self.t:
485 elif 'header' in self.t:
510 key = 'header'
486 key = 'header'
511 else:
487 else:
512 key = ''
488 key = ''
513 if key:
489 if key:
514 h = templater.stringify(self.t(key, **props))
490 h = templater.stringify(self.t(key, **props))
515 if self.buffered:
491 if self.buffered:
516 self.ui.write_header(h)
492 self.header[rev] = h
517 else:
493 else:
518 self.ui.write(h)
494 self.ui.write(h)
519 if self.ui.debugflag and 'changeset_debug' in self.t:
495 if self.ui.debugflag and 'changeset_debug' in self.t:
520 key = 'changeset_debug'
496 key = 'changeset_debug'
521 elif self.ui.quiet and 'changeset_quiet' in self.t:
497 elif self.ui.quiet and 'changeset_quiet' in self.t:
522 key = 'changeset_quiet'
498 key = 'changeset_quiet'
523 elif self.ui.verbose and 'changeset_verbose' in self.t:
499 elif self.ui.verbose and 'changeset_verbose' in self.t:
524 key = 'changeset_verbose'
500 key = 'changeset_verbose'
525 else:
501 else:
526 key = 'changeset'
502 key = 'changeset'
527 self.ui.write(templater.stringify(self.t(key, **props)))
503 self.ui.write(templater.stringify(self.t(key, **props)))
528 self.showpatch(changenode)
504 self.showpatch(changenode)
529 except KeyError, inst:
505 except KeyError, inst:
530 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
506 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
531 inst.args[0]))
507 inst.args[0]))
532 except SyntaxError, inst:
508 except SyntaxError, inst:
533 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
509 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
534
510
535 class stringio(object):
536 '''wrap cStringIO for use by changeset_templater.'''
537 def __init__(self):
538 self.fp = cStringIO.StringIO()
539
540 def write(self, *args):
541 for a in args:
542 self.fp.write(a)
543
544 write_header = write
545
546 def __getattr__(self, key):
547 return getattr(self.fp, key)
548
549 def show_changeset(ui, repo, opts, buffered=False):
511 def show_changeset(ui, repo, opts, buffered=False):
550 """show one changeset using template or regular display.
512 """show one changeset using template or regular display.
551
513
552 Display format will be the first non-empty hit of:
514 Display format will be the first non-empty hit of:
553 1. option 'template'
515 1. option 'template'
554 2. option 'style'
516 2. option 'style'
555 3. [ui] setting 'logtemplate'
517 3. [ui] setting 'logtemplate'
556 4. [ui] setting 'style'
518 4. [ui] setting 'style'
557 If all of these values are either the unset or the empty string,
519 If all of these values are either the unset or the empty string,
558 regular display via changeset_printer() is done.
520 regular display via changeset_printer() is done.
559 """
521 """
560 # options
522 # options
561 patch = opts.get('patch')
523 patch = opts.get('patch')
562 br = None
524 br = None
563 if opts.get('branches'):
525 if opts.get('branches'):
564 ui.warn(_("the --branches option is deprecated, "
526 ui.warn(_("the --branches option is deprecated, "
565 "please use 'hg branches' instead\n"))
527 "please use 'hg branches' instead\n"))
566 br = True
528 br = True
567 tmpl = opts.get('template')
529 tmpl = opts.get('template')
568 mapfile = None
530 mapfile = None
569 if tmpl:
531 if tmpl:
570 tmpl = templater.parsestring(tmpl, quoted=False)
532 tmpl = templater.parsestring(tmpl, quoted=False)
571 else:
533 else:
572 mapfile = opts.get('style')
534 mapfile = opts.get('style')
573 # ui settings
535 # ui settings
574 if not mapfile:
536 if not mapfile:
575 tmpl = ui.config('ui', 'logtemplate')
537 tmpl = ui.config('ui', 'logtemplate')
576 if tmpl:
538 if tmpl:
577 tmpl = templater.parsestring(tmpl)
539 tmpl = templater.parsestring(tmpl)
578 else:
540 else:
579 mapfile = ui.config('ui', 'style')
541 mapfile = ui.config('ui', 'style')
580
542
581 if tmpl or mapfile:
543 if tmpl or mapfile:
582 if mapfile:
544 if mapfile:
583 if not os.path.split(mapfile)[0]:
545 if not os.path.split(mapfile)[0]:
584 mapname = (templater.templatepath('map-cmdline.' + mapfile)
546 mapname = (templater.templatepath('map-cmdline.' + mapfile)
585 or templater.templatepath(mapfile))
547 or templater.templatepath(mapfile))
586 if mapname: mapfile = mapname
548 if mapname: mapfile = mapname
587 try:
549 try:
588 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
550 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
589 except SyntaxError, inst:
551 except SyntaxError, inst:
590 raise util.Abort(inst.args[0])
552 raise util.Abort(inst.args[0])
591 if tmpl: t.use_template(tmpl)
553 if tmpl: t.use_template(tmpl)
592 return t
554 return t
593 return changeset_printer(ui, repo, patch, br, buffered)
555 return changeset_printer(ui, repo, patch, br, buffered)
594
556
595 def walkchangerevs(ui, repo, pats, change, opts):
557 def walkchangerevs(ui, repo, pats, change, opts):
596 '''Iterate over files and the revs they changed in.
558 '''Iterate over files and the revs they changed in.
597
559
598 Callers most commonly need to iterate backwards over the history
560 Callers most commonly need to iterate backwards over the history
599 it is interested in. Doing so has awful (quadratic-looking)
561 it is interested in. Doing so has awful (quadratic-looking)
600 performance, so we use iterators in a "windowed" way.
562 performance, so we use iterators in a "windowed" way.
601
563
602 We walk a window of revisions in the desired order. Within the
564 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
565 window, we first walk forwards to gather data, then in the desired
604 order (usually backwards) to display it.
566 order (usually backwards) to display it.
605
567
606 This function returns an (iterator, matchfn) tuple. The iterator
568 This function returns an (iterator, matchfn) tuple. The iterator
607 yields 3-tuples. They will be of one of the following forms:
569 yields 3-tuples. They will be of one of the following forms:
608
570
609 "window", incrementing, lastrev: stepping through a window,
571 "window", incrementing, lastrev: stepping through a window,
610 positive if walking forwards through revs, last rev in the
572 positive if walking forwards through revs, last rev in the
611 sequence iterated over - use to reset state for the current window
573 sequence iterated over - use to reset state for the current window
612
574
613 "add", rev, fns: out-of-order traversal of the given file names
575 "add", rev, fns: out-of-order traversal of the given file names
614 fns, which changed during revision rev - use to gather data for
576 fns, which changed during revision rev - use to gather data for
615 possible display
577 possible display
616
578
617 "iter", rev, None: in-order traversal of the revs earlier iterated
579 "iter", rev, None: in-order traversal of the revs earlier iterated
618 over with "add" - use to display data'''
580 over with "add" - use to display data'''
619
581
620 def increasing_windows(start, end, windowsize=8, sizelimit=512):
582 def increasing_windows(start, end, windowsize=8, sizelimit=512):
621 if start < end:
583 if start < end:
622 while start < end:
584 while start < end:
623 yield start, min(windowsize, end-start)
585 yield start, min(windowsize, end-start)
624 start += windowsize
586 start += windowsize
625 if windowsize < sizelimit:
587 if windowsize < sizelimit:
626 windowsize *= 2
588 windowsize *= 2
627 else:
589 else:
628 while start > end:
590 while start > end:
629 yield start, min(windowsize, start-end-1)
591 yield start, min(windowsize, start-end-1)
630 start -= windowsize
592 start -= windowsize
631 if windowsize < sizelimit:
593 if windowsize < sizelimit:
632 windowsize *= 2
594 windowsize *= 2
633
595
634 files, matchfn, anypats = matchpats(repo, pats, opts)
596 files, matchfn, anypats = matchpats(repo, pats, opts)
635 follow = opts.get('follow') or opts.get('follow_first')
597 follow = opts.get('follow') or opts.get('follow_first')
636
598
637 if repo.changelog.count() == 0:
599 if repo.changelog.count() == 0:
638 return [], matchfn
600 return [], matchfn
639
601
640 if follow:
602 if follow:
641 defrange = '%s:0' % repo.changectx().rev()
603 defrange = '%s:0' % repo.changectx().rev()
642 else:
604 else:
643 defrange = 'tip:0'
605 defrange = 'tip:0'
644 revs = revrange(repo, opts['rev'] or [defrange])
606 revs = revrange(repo, opts['rev'] or [defrange])
645 wanted = {}
607 wanted = {}
646 slowpath = anypats or opts.get('removed')
608 slowpath = anypats or opts.get('removed')
647 fncache = {}
609 fncache = {}
648
610
649 if not slowpath and not files:
611 if not slowpath and not files:
650 # No files, no patterns. Display all revs.
612 # No files, no patterns. Display all revs.
651 wanted = dict.fromkeys(revs)
613 wanted = dict.fromkeys(revs)
652 copies = []
614 copies = []
653 if not slowpath:
615 if not slowpath:
654 # Only files, no patterns. Check the history of each file.
616 # Only files, no patterns. Check the history of each file.
655 def filerevgen(filelog, node):
617 def filerevgen(filelog, node):
656 cl_count = repo.changelog.count()
618 cl_count = repo.changelog.count()
657 if node is None:
619 if node is None:
658 last = filelog.count() - 1
620 last = filelog.count() - 1
659 else:
621 else:
660 last = filelog.rev(node)
622 last = filelog.rev(node)
661 for i, window in increasing_windows(last, nullrev):
623 for i, window in increasing_windows(last, nullrev):
662 revs = []
624 revs = []
663 for j in xrange(i - window, i + 1):
625 for j in xrange(i - window, i + 1):
664 n = filelog.node(j)
626 n = filelog.node(j)
665 revs.append((filelog.linkrev(n),
627 revs.append((filelog.linkrev(n),
666 follow and filelog.renamed(n)))
628 follow and filelog.renamed(n)))
667 revs.reverse()
629 revs.reverse()
668 for rev in revs:
630 for rev in revs:
669 # only yield rev for which we have the changelog, it can
631 # only yield rev for which we have the changelog, it can
670 # happen while doing "hg log" during a pull or commit
632 # happen while doing "hg log" during a pull or commit
671 if rev[0] < cl_count:
633 if rev[0] < cl_count:
672 yield rev
634 yield rev
673 def iterfiles():
635 def iterfiles():
674 for filename in files:
636 for filename in files:
675 yield filename, None
637 yield filename, None
676 for filename_node in copies:
638 for filename_node in copies:
677 yield filename_node
639 yield filename_node
678 minrev, maxrev = min(revs), max(revs)
640 minrev, maxrev = min(revs), max(revs)
679 for file_, node in iterfiles():
641 for file_, node in iterfiles():
680 filelog = repo.file(file_)
642 filelog = repo.file(file_)
681 # A zero count may be a directory or deleted file, so
643 # A zero count may be a directory or deleted file, so
682 # try to find matching entries on the slow path.
644 # try to find matching entries on the slow path.
683 if filelog.count() == 0:
645 if filelog.count() == 0:
684 slowpath = True
646 slowpath = True
685 break
647 break
686 for rev, copied in filerevgen(filelog, node):
648 for rev, copied in filerevgen(filelog, node):
687 if rev <= maxrev:
649 if rev <= maxrev:
688 if rev < minrev:
650 if rev < minrev:
689 break
651 break
690 fncache.setdefault(rev, [])
652 fncache.setdefault(rev, [])
691 fncache[rev].append(file_)
653 fncache[rev].append(file_)
692 wanted[rev] = 1
654 wanted[rev] = 1
693 if follow and copied:
655 if follow and copied:
694 copies.append(copied)
656 copies.append(copied)
695 if slowpath:
657 if slowpath:
696 if follow:
658 if follow:
697 raise util.Abort(_('can only follow copies/renames for explicit '
659 raise util.Abort(_('can only follow copies/renames for explicit '
698 'file names'))
660 'file names'))
699
661
700 # The slow path checks files modified in every changeset.
662 # The slow path checks files modified in every changeset.
701 def changerevgen():
663 def changerevgen():
702 for i, window in increasing_windows(repo.changelog.count()-1,
664 for i, window in increasing_windows(repo.changelog.count()-1,
703 nullrev):
665 nullrev):
704 for j in xrange(i - window, i + 1):
666 for j in xrange(i - window, i + 1):
705 yield j, change(j)[3]
667 yield j, change(j)[3]
706
668
707 for rev, changefiles in changerevgen():
669 for rev, changefiles in changerevgen():
708 matches = filter(matchfn, changefiles)
670 matches = filter(matchfn, changefiles)
709 if matches:
671 if matches:
710 fncache[rev] = matches
672 fncache[rev] = matches
711 wanted[rev] = 1
673 wanted[rev] = 1
712
674
713 class followfilter:
675 class followfilter:
714 def __init__(self, onlyfirst=False):
676 def __init__(self, onlyfirst=False):
715 self.startrev = nullrev
677 self.startrev = nullrev
716 self.roots = []
678 self.roots = []
717 self.onlyfirst = onlyfirst
679 self.onlyfirst = onlyfirst
718
680
719 def match(self, rev):
681 def match(self, rev):
720 def realparents(rev):
682 def realparents(rev):
721 if self.onlyfirst:
683 if self.onlyfirst:
722 return repo.changelog.parentrevs(rev)[0:1]
684 return repo.changelog.parentrevs(rev)[0:1]
723 else:
685 else:
724 return filter(lambda x: x != nullrev,
686 return filter(lambda x: x != nullrev,
725 repo.changelog.parentrevs(rev))
687 repo.changelog.parentrevs(rev))
726
688
727 if self.startrev == nullrev:
689 if self.startrev == nullrev:
728 self.startrev = rev
690 self.startrev = rev
729 return True
691 return True
730
692
731 if rev > self.startrev:
693 if rev > self.startrev:
732 # forward: all descendants
694 # forward: all descendants
733 if not self.roots:
695 if not self.roots:
734 self.roots.append(self.startrev)
696 self.roots.append(self.startrev)
735 for parent in realparents(rev):
697 for parent in realparents(rev):
736 if parent in self.roots:
698 if parent in self.roots:
737 self.roots.append(rev)
699 self.roots.append(rev)
738 return True
700 return True
739 else:
701 else:
740 # backwards: all parents
702 # backwards: all parents
741 if not self.roots:
703 if not self.roots:
742 self.roots.extend(realparents(self.startrev))
704 self.roots.extend(realparents(self.startrev))
743 if rev in self.roots:
705 if rev in self.roots:
744 self.roots.remove(rev)
706 self.roots.remove(rev)
745 self.roots.extend(realparents(rev))
707 self.roots.extend(realparents(rev))
746 return True
708 return True
747
709
748 return False
710 return False
749
711
750 # it might be worthwhile to do this in the iterator if the rev range
712 # 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
713 # is descending and the prune args are all within that range
752 for rev in opts.get('prune', ()):
714 for rev in opts.get('prune', ()):
753 rev = repo.changelog.rev(repo.lookup(rev))
715 rev = repo.changelog.rev(repo.lookup(rev))
754 ff = followfilter()
716 ff = followfilter()
755 stop = min(revs[0], revs[-1])
717 stop = min(revs[0], revs[-1])
756 for x in xrange(rev, stop-1, -1):
718 for x in xrange(rev, stop-1, -1):
757 if ff.match(x) and x in wanted:
719 if ff.match(x) and x in wanted:
758 del wanted[x]
720 del wanted[x]
759
721
760 def iterate():
722 def iterate():
761 if follow and not files:
723 if follow and not files:
762 ff = followfilter(onlyfirst=opts.get('follow_first'))
724 ff = followfilter(onlyfirst=opts.get('follow_first'))
763 def want(rev):
725 def want(rev):
764 if ff.match(rev) and rev in wanted:
726 if ff.match(rev) and rev in wanted:
765 return True
727 return True
766 return False
728 return False
767 else:
729 else:
768 def want(rev):
730 def want(rev):
769 return rev in wanted
731 return rev in wanted
770
732
771 for i, window in increasing_windows(0, len(revs)):
733 for i, window in increasing_windows(0, len(revs)):
772 yield 'window', revs[0] < revs[-1], revs[-1]
734 yield 'window', revs[0] < revs[-1], revs[-1]
773 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
735 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
774 srevs = list(nrevs)
736 srevs = list(nrevs)
775 srevs.sort()
737 srevs.sort()
776 for rev in srevs:
738 for rev in srevs:
777 fns = fncache.get(rev)
739 fns = fncache.get(rev)
778 if not fns:
740 if not fns:
779 def fns_generator():
741 def fns_generator():
780 for f in change(rev)[3]:
742 for f in change(rev)[3]:
781 if matchfn(f):
743 if matchfn(f):
782 yield f
744 yield f
783 fns = fns_generator()
745 fns = fns_generator()
784 yield 'add', rev, fns
746 yield 'add', rev, fns
785 for rev in nrevs:
747 for rev in nrevs:
786 yield 'iter', rev, None
748 yield 'iter', rev, None
787 return iterate(), matchfn
749 return iterate(), matchfn
General Comments 0
You need to be logged in to leave comments. Login now