##// END OF EJS Templates
Use util.always instead of creating a new lambda function in show_changeset...
Thomas Arendsen Hein -
r3838:dec4eba7 default
parent child Browse files
Show More
@@ -1,775 +1,773 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 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 and defval is not None:
21 if not val and val != 0 and defval is not None:
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 changeset_printer(object):
199 class changeset_printer(object):
200 '''show changeset information when templating not requested.'''
200 '''show changeset information when templating not requested.'''
201
201
202 def __init__(self, ui, repo, patch, brinfo, buffered):
202 def __init__(self, ui, repo, patch, brinfo, buffered):
203 self.ui = ui
203 self.ui = ui
204 self.repo = repo
204 self.repo = repo
205 self.buffered = buffered
205 self.buffered = buffered
206 self.patch = patch
206 self.patch = patch
207 self.brinfo = brinfo
207 self.brinfo = brinfo
208 self.header = {}
208 self.header = {}
209 self.hunk = {}
209 self.hunk = {}
210 self.lastheader = None
210 self.lastheader = None
211
211
212 def flush(self, rev):
212 def flush(self, rev):
213 if rev in self.header:
213 if rev in self.header:
214 h = self.header[rev]
214 h = self.header[rev]
215 if h != self.lastheader:
215 if h != self.lastheader:
216 self.lastheader = h
216 self.lastheader = h
217 self.ui.write(h)
217 self.ui.write(h)
218 del self.header[rev]
218 del self.header[rev]
219 if rev in self.hunk:
219 if rev in self.hunk:
220 self.ui.write(self.hunk[rev])
220 self.ui.write(self.hunk[rev])
221 del self.hunk[rev]
221 del self.hunk[rev]
222 return 1
222 return 1
223 return 0
223 return 0
224
224
225 def show(self, rev=0, changenode=None, copies=None, **props):
225 def show(self, rev=0, changenode=None, copies=None, **props):
226 if self.buffered:
226 if self.buffered:
227 self.ui.pushbuffer()
227 self.ui.pushbuffer()
228 self._show(rev, changenode, copies, props)
228 self._show(rev, changenode, copies, props)
229 self.hunk[rev] = self.ui.popbuffer()
229 self.hunk[rev] = self.ui.popbuffer()
230 else:
230 else:
231 self._show(rev, changenode, copies, props)
231 self._show(rev, changenode, copies, props)
232
232
233 def _show(self, rev, changenode, copies, props):
233 def _show(self, rev, changenode, copies, props):
234 '''show a single changeset or file revision'''
234 '''show a single changeset or file revision'''
235 log = self.repo.changelog
235 log = self.repo.changelog
236 if changenode is None:
236 if changenode is None:
237 changenode = log.node(rev)
237 changenode = log.node(rev)
238 elif not rev:
238 elif not rev:
239 rev = log.rev(changenode)
239 rev = log.rev(changenode)
240
240
241 if self.ui.quiet:
241 if self.ui.quiet:
242 self.ui.write("%d:%s\n" % (rev, short(changenode)))
242 self.ui.write("%d:%s\n" % (rev, short(changenode)))
243 return
243 return
244
244
245 changes = log.read(changenode)
245 changes = log.read(changenode)
246 date = util.datestr(changes[2])
246 date = util.datestr(changes[2])
247 extra = changes[5]
247 extra = changes[5]
248 branch = extra.get("branch")
248 branch = extra.get("branch")
249
249
250 hexfunc = self.ui.debugflag and hex or short
250 hexfunc = self.ui.debugflag and hex or short
251
251
252 parents = log.parentrevs(rev)
252 parents = log.parentrevs(rev)
253 if not self.ui.debugflag:
253 if not self.ui.debugflag:
254 if parents[1] == nullrev:
254 if parents[1] == nullrev:
255 if parents[0] >= rev - 1:
255 if parents[0] >= rev - 1:
256 parents = []
256 parents = []
257 else:
257 else:
258 parents = [parents[0]]
258 parents = [parents[0]]
259 parents = [(p, hexfunc(log.node(p))) for p in parents]
259 parents = [(p, hexfunc(log.node(p))) for p in parents]
260
260
261 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
261 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
262
262
263 if branch:
263 if branch:
264 branch = util.tolocal(branch)
264 branch = util.tolocal(branch)
265 self.ui.write(_("branch: %s\n") % branch)
265 self.ui.write(_("branch: %s\n") % branch)
266 for tag in self.repo.nodetags(changenode):
266 for tag in self.repo.nodetags(changenode):
267 self.ui.write(_("tag: %s\n") % tag)
267 self.ui.write(_("tag: %s\n") % tag)
268 for parent in parents:
268 for parent in parents:
269 self.ui.write(_("parent: %d:%s\n") % parent)
269 self.ui.write(_("parent: %d:%s\n") % parent)
270
270
271 if self.brinfo:
271 if self.brinfo:
272 br = self.repo.branchlookup([changenode])
272 br = self.repo.branchlookup([changenode])
273 if br:
273 if br:
274 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
274 self.ui.write(_("branch: %s\n") % " ".join(br[changenode]))
275
275
276 if self.ui.debugflag:
276 if self.ui.debugflag:
277 self.ui.write(_("manifest: %d:%s\n") %
277 self.ui.write(_("manifest: %d:%s\n") %
278 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
278 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
279 self.ui.write(_("user: %s\n") % changes[1])
279 self.ui.write(_("user: %s\n") % changes[1])
280 self.ui.write(_("date: %s\n") % date)
280 self.ui.write(_("date: %s\n") % date)
281
281
282 if self.ui.debugflag:
282 if self.ui.debugflag:
283 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
283 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
284 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
284 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
285 files):
285 files):
286 if value:
286 if value:
287 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
287 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
288 elif changes[3] and self.ui.verbose:
288 elif changes[3] and self.ui.verbose:
289 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
289 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
290 if copies and self.ui.verbose:
290 if copies and self.ui.verbose:
291 copies = ['%s (%s)' % c for c in copies]
291 copies = ['%s (%s)' % c for c in copies]
292 self.ui.write(_("copies: %s\n") % ' '.join(copies))
292 self.ui.write(_("copies: %s\n") % ' '.join(copies))
293
293
294 if extra and self.ui.debugflag:
294 if extra and self.ui.debugflag:
295 extraitems = extra.items()
295 extraitems = extra.items()
296 extraitems.sort()
296 extraitems.sort()
297 for key, value in extraitems:
297 for key, value in extraitems:
298 self.ui.write(_("extra: %s=%s\n")
298 self.ui.write(_("extra: %s=%s\n")
299 % (key, value.encode('string_escape')))
299 % (key, value.encode('string_escape')))
300
300
301 description = changes[4].strip()
301 description = changes[4].strip()
302 if description:
302 if description:
303 if self.ui.verbose:
303 if self.ui.verbose:
304 self.ui.write(_("description:\n"))
304 self.ui.write(_("description:\n"))
305 self.ui.write(description)
305 self.ui.write(description)
306 self.ui.write("\n\n")
306 self.ui.write("\n\n")
307 else:
307 else:
308 self.ui.write(_("summary: %s\n") %
308 self.ui.write(_("summary: %s\n") %
309 description.splitlines()[0])
309 description.splitlines()[0])
310 self.ui.write("\n")
310 self.ui.write("\n")
311
311
312 self.showpatch(changenode)
312 self.showpatch(changenode)
313
313
314 def showpatch(self, node):
314 def showpatch(self, node):
315 if self.patch:
315 if self.patch:
316 prev = self.repo.changelog.parents(node)[0]
316 prev = self.repo.changelog.parents(node)[0]
317 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui)
317 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui)
318 self.ui.write("\n")
318 self.ui.write("\n")
319
319
320 class changeset_templater(changeset_printer):
320 class changeset_templater(changeset_printer):
321 '''format changeset information.'''
321 '''format changeset information.'''
322
322
323 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
323 def __init__(self, ui, repo, patch, brinfo, mapfile, buffered):
324 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
324 changeset_printer.__init__(self, ui, repo, patch, brinfo, buffered)
325 self.t = templater.templater(mapfile, templater.common_filters,
325 self.t = templater.templater(mapfile, templater.common_filters,
326 cache={'parent': '{rev}:{node|short} ',
326 cache={'parent': '{rev}:{node|short} ',
327 'manifest': '{rev}:{node|short}',
327 'manifest': '{rev}:{node|short}',
328 'filecopy': '{name} ({source})'})
328 'filecopy': '{name} ({source})'})
329
329
330 def use_template(self, t):
330 def use_template(self, t):
331 '''set template string to use'''
331 '''set template string to use'''
332 self.t.cache['changeset'] = t
332 self.t.cache['changeset'] = t
333
333
334 def _show(self, rev, changenode, copies, props):
334 def _show(self, rev, changenode, copies, props):
335 '''show a single changeset or file revision'''
335 '''show a single changeset or file revision'''
336 log = self.repo.changelog
336 log = self.repo.changelog
337 if changenode is None:
337 if changenode is None:
338 changenode = log.node(rev)
338 changenode = log.node(rev)
339 elif not rev:
339 elif not rev:
340 rev = log.rev(changenode)
340 rev = log.rev(changenode)
341
341
342 changes = log.read(changenode)
342 changes = log.read(changenode)
343
343
344 def showlist(name, values, plural=None, **args):
344 def showlist(name, values, plural=None, **args):
345 '''expand set of values.
345 '''expand set of values.
346 name is name of key in template map.
346 name is name of key in template map.
347 values is list of strings or dicts.
347 values is list of strings or dicts.
348 plural is plural of name, if not simply name + 's'.
348 plural is plural of name, if not simply name + 's'.
349
349
350 expansion works like this, given name 'foo'.
350 expansion works like this, given name 'foo'.
351
351
352 if values is empty, expand 'no_foos'.
352 if values is empty, expand 'no_foos'.
353
353
354 if 'foo' not in template map, return values as a string,
354 if 'foo' not in template map, return values as a string,
355 joined by space.
355 joined by space.
356
356
357 expand 'start_foos'.
357 expand 'start_foos'.
358
358
359 for each value, expand 'foo'. if 'last_foo' in template
359 for each value, expand 'foo'. if 'last_foo' in template
360 map, expand it instead of 'foo' for last key.
360 map, expand it instead of 'foo' for last key.
361
361
362 expand 'end_foos'.
362 expand 'end_foos'.
363 '''
363 '''
364 if plural: names = plural
364 if plural: names = plural
365 else: names = name + 's'
365 else: names = name + 's'
366 if not values:
366 if not values:
367 noname = 'no_' + names
367 noname = 'no_' + names
368 if noname in self.t:
368 if noname in self.t:
369 yield self.t(noname, **args)
369 yield self.t(noname, **args)
370 return
370 return
371 if name not in self.t:
371 if name not in self.t:
372 if isinstance(values[0], str):
372 if isinstance(values[0], str):
373 yield ' '.join(values)
373 yield ' '.join(values)
374 else:
374 else:
375 for v in values:
375 for v in values:
376 yield dict(v, **args)
376 yield dict(v, **args)
377 return
377 return
378 startname = 'start_' + names
378 startname = 'start_' + names
379 if startname in self.t:
379 if startname in self.t:
380 yield self.t(startname, **args)
380 yield self.t(startname, **args)
381 vargs = args.copy()
381 vargs = args.copy()
382 def one(v, tag=name):
382 def one(v, tag=name):
383 try:
383 try:
384 vargs.update(v)
384 vargs.update(v)
385 except (AttributeError, ValueError):
385 except (AttributeError, ValueError):
386 try:
386 try:
387 for a, b in v:
387 for a, b in v:
388 vargs[a] = b
388 vargs[a] = b
389 except ValueError:
389 except ValueError:
390 vargs[name] = v
390 vargs[name] = v
391 return self.t(tag, **vargs)
391 return self.t(tag, **vargs)
392 lastname = 'last_' + name
392 lastname = 'last_' + name
393 if lastname in self.t:
393 if lastname in self.t:
394 last = values.pop()
394 last = values.pop()
395 else:
395 else:
396 last = None
396 last = None
397 for v in values:
397 for v in values:
398 yield one(v)
398 yield one(v)
399 if last is not None:
399 if last is not None:
400 yield one(last, tag=lastname)
400 yield one(last, tag=lastname)
401 endname = 'end_' + names
401 endname = 'end_' + names
402 if endname in self.t:
402 if endname in self.t:
403 yield self.t(endname, **args)
403 yield self.t(endname, **args)
404
404
405 def showbranches(**args):
405 def showbranches(**args):
406 branch = changes[5].get("branch")
406 branch = changes[5].get("branch")
407 if branch:
407 if branch:
408 branch = util.tolocal(branch)
408 branch = util.tolocal(branch)
409 return showlist('branch', [branch], plural='branches', **args)
409 return showlist('branch', [branch], plural='branches', **args)
410 # add old style branches if requested
410 # add old style branches if requested
411 if self.brinfo:
411 if self.brinfo:
412 br = self.repo.branchlookup([changenode])
412 br = self.repo.branchlookup([changenode])
413 if changenode in br:
413 if changenode in br:
414 return showlist('branch', br[changenode],
414 return showlist('branch', br[changenode],
415 plural='branches', **args)
415 plural='branches', **args)
416
416
417 def showparents(**args):
417 def showparents(**args):
418 parents = [[('rev', log.rev(p)), ('node', hex(p))]
418 parents = [[('rev', log.rev(p)), ('node', hex(p))]
419 for p in log.parents(changenode)
419 for p in log.parents(changenode)
420 if self.ui.debugflag or p != nullid]
420 if self.ui.debugflag or p != nullid]
421 if (not self.ui.debugflag and len(parents) == 1 and
421 if (not self.ui.debugflag and len(parents) == 1 and
422 parents[0][0][1] == rev - 1):
422 parents[0][0][1] == rev - 1):
423 return
423 return
424 return showlist('parent', parents, **args)
424 return showlist('parent', parents, **args)
425
425
426 def showtags(**args):
426 def showtags(**args):
427 return showlist('tag', self.repo.nodetags(changenode), **args)
427 return showlist('tag', self.repo.nodetags(changenode), **args)
428
428
429 def showextras(**args):
429 def showextras(**args):
430 extras = changes[5].items()
430 extras = changes[5].items()
431 extras.sort()
431 extras.sort()
432 for key, value in extras:
432 for key, value in extras:
433 args = args.copy()
433 args = args.copy()
434 args.update(dict(key=key, value=value))
434 args.update(dict(key=key, value=value))
435 yield self.t('extra', **args)
435 yield self.t('extra', **args)
436
436
437 def showcopies(**args):
437 def showcopies(**args):
438 c = [{'name': x[0], 'source': x[1]} for x in copies]
438 c = [{'name': x[0], 'source': x[1]} for x in copies]
439 return showlist('file_copy', c, plural='file_copies', **args)
439 return showlist('file_copy', c, plural='file_copies', **args)
440
440
441 if self.ui.debugflag:
441 if self.ui.debugflag:
442 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
442 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
443 def showfiles(**args):
443 def showfiles(**args):
444 return showlist('file', files[0], **args)
444 return showlist('file', files[0], **args)
445 def showadds(**args):
445 def showadds(**args):
446 return showlist('file_add', files[1], **args)
446 return showlist('file_add', files[1], **args)
447 def showdels(**args):
447 def showdels(**args):
448 return showlist('file_del', files[2], **args)
448 return showlist('file_del', files[2], **args)
449 def showmanifest(**args):
449 def showmanifest(**args):
450 args = args.copy()
450 args = args.copy()
451 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
451 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
452 node=hex(changes[0])))
452 node=hex(changes[0])))
453 return self.t('manifest', **args)
453 return self.t('manifest', **args)
454 else:
454 else:
455 def showfiles(**args):
455 def showfiles(**args):
456 return showlist('file', changes[3], **args)
456 return showlist('file', changes[3], **args)
457 showadds = ''
457 showadds = ''
458 showdels = ''
458 showdels = ''
459 showmanifest = ''
459 showmanifest = ''
460
460
461 defprops = {
461 defprops = {
462 'author': changes[1],
462 'author': changes[1],
463 'branches': showbranches,
463 'branches': showbranches,
464 'date': changes[2],
464 'date': changes[2],
465 'desc': changes[4],
465 'desc': changes[4],
466 'file_adds': showadds,
466 'file_adds': showadds,
467 'file_dels': showdels,
467 'file_dels': showdels,
468 'files': showfiles,
468 'files': showfiles,
469 'file_copies': showcopies,
469 'file_copies': showcopies,
470 'manifest': showmanifest,
470 'manifest': showmanifest,
471 'node': hex(changenode),
471 'node': hex(changenode),
472 'parents': showparents,
472 'parents': showparents,
473 'rev': rev,
473 'rev': rev,
474 'tags': showtags,
474 'tags': showtags,
475 'extras': showextras,
475 'extras': showextras,
476 }
476 }
477 props = props.copy()
477 props = props.copy()
478 props.update(defprops)
478 props.update(defprops)
479
479
480 try:
480 try:
481 if self.ui.debugflag and 'header_debug' in self.t:
481 if self.ui.debugflag and 'header_debug' in self.t:
482 key = 'header_debug'
482 key = 'header_debug'
483 elif self.ui.quiet and 'header_quiet' in self.t:
483 elif self.ui.quiet and 'header_quiet' in self.t:
484 key = 'header_quiet'
484 key = 'header_quiet'
485 elif self.ui.verbose and 'header_verbose' in self.t:
485 elif self.ui.verbose and 'header_verbose' in self.t:
486 key = 'header_verbose'
486 key = 'header_verbose'
487 elif 'header' in self.t:
487 elif 'header' in self.t:
488 key = 'header'
488 key = 'header'
489 else:
489 else:
490 key = ''
490 key = ''
491 if key:
491 if key:
492 h = templater.stringify(self.t(key, **props))
492 h = templater.stringify(self.t(key, **props))
493 if self.buffered:
493 if self.buffered:
494 self.header[rev] = h
494 self.header[rev] = h
495 else:
495 else:
496 self.ui.write(h)
496 self.ui.write(h)
497 if self.ui.debugflag and 'changeset_debug' in self.t:
497 if self.ui.debugflag and 'changeset_debug' in self.t:
498 key = 'changeset_debug'
498 key = 'changeset_debug'
499 elif self.ui.quiet and 'changeset_quiet' in self.t:
499 elif self.ui.quiet and 'changeset_quiet' in self.t:
500 key = 'changeset_quiet'
500 key = 'changeset_quiet'
501 elif self.ui.verbose and 'changeset_verbose' in self.t:
501 elif self.ui.verbose and 'changeset_verbose' in self.t:
502 key = 'changeset_verbose'
502 key = 'changeset_verbose'
503 else:
503 else:
504 key = 'changeset'
504 key = 'changeset'
505 self.ui.write(templater.stringify(self.t(key, **props)))
505 self.ui.write(templater.stringify(self.t(key, **props)))
506 self.showpatch(changenode)
506 self.showpatch(changenode)
507 except KeyError, inst:
507 except KeyError, inst:
508 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
508 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
509 inst.args[0]))
509 inst.args[0]))
510 except SyntaxError, inst:
510 except SyntaxError, inst:
511 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
511 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
512
512
513 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
513 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
514 """show one changeset using template or regular display.
514 """show one changeset using template or regular display.
515
515
516 Display format will be the first non-empty hit of:
516 Display format will be the first non-empty hit of:
517 1. option 'template'
517 1. option 'template'
518 2. option 'style'
518 2. option 'style'
519 3. [ui] setting 'logtemplate'
519 3. [ui] setting 'logtemplate'
520 4. [ui] setting 'style'
520 4. [ui] setting 'style'
521 If all of these values are either the unset or the empty string,
521 If all of these values are either the unset or the empty string,
522 regular display via changeset_printer() is done.
522 regular display via changeset_printer() is done.
523 """
523 """
524 # options
524 # options
525 patch = False
525 patch = False
526 if opts.get('patch'):
526 if opts.get('patch'):
527 patch = lambda x: True
527 patch = matchfn or util.always
528 if matchfn:
529 patch = matchfn
530
528
531 br = None
529 br = None
532 if opts.get('branches'):
530 if opts.get('branches'):
533 ui.warn(_("the --branches option is deprecated, "
531 ui.warn(_("the --branches option is deprecated, "
534 "please use 'hg branches' instead\n"))
532 "please use 'hg branches' instead\n"))
535 br = True
533 br = True
536 tmpl = opts.get('template')
534 tmpl = opts.get('template')
537 mapfile = None
535 mapfile = None
538 if tmpl:
536 if tmpl:
539 tmpl = templater.parsestring(tmpl, quoted=False)
537 tmpl = templater.parsestring(tmpl, quoted=False)
540 else:
538 else:
541 mapfile = opts.get('style')
539 mapfile = opts.get('style')
542 # ui settings
540 # ui settings
543 if not mapfile:
541 if not mapfile:
544 tmpl = ui.config('ui', 'logtemplate')
542 tmpl = ui.config('ui', 'logtemplate')
545 if tmpl:
543 if tmpl:
546 tmpl = templater.parsestring(tmpl)
544 tmpl = templater.parsestring(tmpl)
547 else:
545 else:
548 mapfile = ui.config('ui', 'style')
546 mapfile = ui.config('ui', 'style')
549
547
550 if tmpl or mapfile:
548 if tmpl or mapfile:
551 if mapfile:
549 if mapfile:
552 if not os.path.split(mapfile)[0]:
550 if not os.path.split(mapfile)[0]:
553 mapname = (templater.templatepath('map-cmdline.' + mapfile)
551 mapname = (templater.templatepath('map-cmdline.' + mapfile)
554 or templater.templatepath(mapfile))
552 or templater.templatepath(mapfile))
555 if mapname: mapfile = mapname
553 if mapname: mapfile = mapname
556 try:
554 try:
557 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
555 t = changeset_templater(ui, repo, patch, br, mapfile, buffered)
558 except SyntaxError, inst:
556 except SyntaxError, inst:
559 raise util.Abort(inst.args[0])
557 raise util.Abort(inst.args[0])
560 if tmpl: t.use_template(tmpl)
558 if tmpl: t.use_template(tmpl)
561 return t
559 return t
562 return changeset_printer(ui, repo, patch, br, buffered)
560 return changeset_printer(ui, repo, patch, br, buffered)
563
561
564 def finddate(ui, repo, date):
562 def finddate(ui, repo, date):
565 """Find the tipmost changeset that matches the given date spec"""
563 """Find the tipmost changeset that matches the given date spec"""
566 df = util.matchdate(date + " to " + date)
564 df = util.matchdate(date + " to " + date)
567 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
565 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
568 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
566 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
569 results = {}
567 results = {}
570 for st, rev, fns in changeiter:
568 for st, rev, fns in changeiter:
571 if st == 'add':
569 if st == 'add':
572 d = get(rev)[2]
570 d = get(rev)[2]
573 if df(d[0]):
571 if df(d[0]):
574 results[rev] = d
572 results[rev] = d
575 elif st == 'iter':
573 elif st == 'iter':
576 if rev in results:
574 if rev in results:
577 ui.status("Found revision %s from %s\n" %
575 ui.status("Found revision %s from %s\n" %
578 (rev, util.datestr(results[rev])))
576 (rev, util.datestr(results[rev])))
579 return str(rev)
577 return str(rev)
580
578
581 raise util.Abort(_("revision matching date not found"))
579 raise util.Abort(_("revision matching date not found"))
582
580
583 def walkchangerevs(ui, repo, pats, change, opts):
581 def walkchangerevs(ui, repo, pats, change, opts):
584 '''Iterate over files and the revs they changed in.
582 '''Iterate over files and the revs they changed in.
585
583
586 Callers most commonly need to iterate backwards over the history
584 Callers most commonly need to iterate backwards over the history
587 it is interested in. Doing so has awful (quadratic-looking)
585 it is interested in. Doing so has awful (quadratic-looking)
588 performance, so we use iterators in a "windowed" way.
586 performance, so we use iterators in a "windowed" way.
589
587
590 We walk a window of revisions in the desired order. Within the
588 We walk a window of revisions in the desired order. Within the
591 window, we first walk forwards to gather data, then in the desired
589 window, we first walk forwards to gather data, then in the desired
592 order (usually backwards) to display it.
590 order (usually backwards) to display it.
593
591
594 This function returns an (iterator, matchfn) tuple. The iterator
592 This function returns an (iterator, matchfn) tuple. The iterator
595 yields 3-tuples. They will be of one of the following forms:
593 yields 3-tuples. They will be of one of the following forms:
596
594
597 "window", incrementing, lastrev: stepping through a window,
595 "window", incrementing, lastrev: stepping through a window,
598 positive if walking forwards through revs, last rev in the
596 positive if walking forwards through revs, last rev in the
599 sequence iterated over - use to reset state for the current window
597 sequence iterated over - use to reset state for the current window
600
598
601 "add", rev, fns: out-of-order traversal of the given file names
599 "add", rev, fns: out-of-order traversal of the given file names
602 fns, which changed during revision rev - use to gather data for
600 fns, which changed during revision rev - use to gather data for
603 possible display
601 possible display
604
602
605 "iter", rev, None: in-order traversal of the revs earlier iterated
603 "iter", rev, None: in-order traversal of the revs earlier iterated
606 over with "add" - use to display data'''
604 over with "add" - use to display data'''
607
605
608 def increasing_windows(start, end, windowsize=8, sizelimit=512):
606 def increasing_windows(start, end, windowsize=8, sizelimit=512):
609 if start < end:
607 if start < end:
610 while start < end:
608 while start < end:
611 yield start, min(windowsize, end-start)
609 yield start, min(windowsize, end-start)
612 start += windowsize
610 start += windowsize
613 if windowsize < sizelimit:
611 if windowsize < sizelimit:
614 windowsize *= 2
612 windowsize *= 2
615 else:
613 else:
616 while start > end:
614 while start > end:
617 yield start, min(windowsize, start-end-1)
615 yield start, min(windowsize, start-end-1)
618 start -= windowsize
616 start -= windowsize
619 if windowsize < sizelimit:
617 if windowsize < sizelimit:
620 windowsize *= 2
618 windowsize *= 2
621
619
622 files, matchfn, anypats = matchpats(repo, pats, opts)
620 files, matchfn, anypats = matchpats(repo, pats, opts)
623 follow = opts.get('follow') or opts.get('follow_first')
621 follow = opts.get('follow') or opts.get('follow_first')
624
622
625 if repo.changelog.count() == 0:
623 if repo.changelog.count() == 0:
626 return [], matchfn
624 return [], matchfn
627
625
628 if follow:
626 if follow:
629 defrange = '%s:0' % repo.changectx().rev()
627 defrange = '%s:0' % repo.changectx().rev()
630 else:
628 else:
631 defrange = 'tip:0'
629 defrange = 'tip:0'
632 revs = revrange(repo, opts['rev'] or [defrange])
630 revs = revrange(repo, opts['rev'] or [defrange])
633 wanted = {}
631 wanted = {}
634 slowpath = anypats or opts.get('removed')
632 slowpath = anypats or opts.get('removed')
635 fncache = {}
633 fncache = {}
636
634
637 if not slowpath and not files:
635 if not slowpath and not files:
638 # No files, no patterns. Display all revs.
636 # No files, no patterns. Display all revs.
639 wanted = dict.fromkeys(revs)
637 wanted = dict.fromkeys(revs)
640 copies = []
638 copies = []
641 if not slowpath:
639 if not slowpath:
642 # Only files, no patterns. Check the history of each file.
640 # Only files, no patterns. Check the history of each file.
643 def filerevgen(filelog, node):
641 def filerevgen(filelog, node):
644 cl_count = repo.changelog.count()
642 cl_count = repo.changelog.count()
645 if node is None:
643 if node is None:
646 last = filelog.count() - 1
644 last = filelog.count() - 1
647 else:
645 else:
648 last = filelog.rev(node)
646 last = filelog.rev(node)
649 for i, window in increasing_windows(last, nullrev):
647 for i, window in increasing_windows(last, nullrev):
650 revs = []
648 revs = []
651 for j in xrange(i - window, i + 1):
649 for j in xrange(i - window, i + 1):
652 n = filelog.node(j)
650 n = filelog.node(j)
653 revs.append((filelog.linkrev(n),
651 revs.append((filelog.linkrev(n),
654 follow and filelog.renamed(n)))
652 follow and filelog.renamed(n)))
655 revs.reverse()
653 revs.reverse()
656 for rev in revs:
654 for rev in revs:
657 # only yield rev for which we have the changelog, it can
655 # only yield rev for which we have the changelog, it can
658 # happen while doing "hg log" during a pull or commit
656 # happen while doing "hg log" during a pull or commit
659 if rev[0] < cl_count:
657 if rev[0] < cl_count:
660 yield rev
658 yield rev
661 def iterfiles():
659 def iterfiles():
662 for filename in files:
660 for filename in files:
663 yield filename, None
661 yield filename, None
664 for filename_node in copies:
662 for filename_node in copies:
665 yield filename_node
663 yield filename_node
666 minrev, maxrev = min(revs), max(revs)
664 minrev, maxrev = min(revs), max(revs)
667 for file_, node in iterfiles():
665 for file_, node in iterfiles():
668 filelog = repo.file(file_)
666 filelog = repo.file(file_)
669 # A zero count may be a directory or deleted file, so
667 # A zero count may be a directory or deleted file, so
670 # try to find matching entries on the slow path.
668 # try to find matching entries on the slow path.
671 if filelog.count() == 0:
669 if filelog.count() == 0:
672 slowpath = True
670 slowpath = True
673 break
671 break
674 for rev, copied in filerevgen(filelog, node):
672 for rev, copied in filerevgen(filelog, node):
675 if rev <= maxrev:
673 if rev <= maxrev:
676 if rev < minrev:
674 if rev < minrev:
677 break
675 break
678 fncache.setdefault(rev, [])
676 fncache.setdefault(rev, [])
679 fncache[rev].append(file_)
677 fncache[rev].append(file_)
680 wanted[rev] = 1
678 wanted[rev] = 1
681 if follow and copied:
679 if follow and copied:
682 copies.append(copied)
680 copies.append(copied)
683 if slowpath:
681 if slowpath:
684 if follow:
682 if follow:
685 raise util.Abort(_('can only follow copies/renames for explicit '
683 raise util.Abort(_('can only follow copies/renames for explicit '
686 'file names'))
684 'file names'))
687
685
688 # The slow path checks files modified in every changeset.
686 # The slow path checks files modified in every changeset.
689 def changerevgen():
687 def changerevgen():
690 for i, window in increasing_windows(repo.changelog.count()-1,
688 for i, window in increasing_windows(repo.changelog.count()-1,
691 nullrev):
689 nullrev):
692 for j in xrange(i - window, i + 1):
690 for j in xrange(i - window, i + 1):
693 yield j, change(j)[3]
691 yield j, change(j)[3]
694
692
695 for rev, changefiles in changerevgen():
693 for rev, changefiles in changerevgen():
696 matches = filter(matchfn, changefiles)
694 matches = filter(matchfn, changefiles)
697 if matches:
695 if matches:
698 fncache[rev] = matches
696 fncache[rev] = matches
699 wanted[rev] = 1
697 wanted[rev] = 1
700
698
701 class followfilter:
699 class followfilter:
702 def __init__(self, onlyfirst=False):
700 def __init__(self, onlyfirst=False):
703 self.startrev = nullrev
701 self.startrev = nullrev
704 self.roots = []
702 self.roots = []
705 self.onlyfirst = onlyfirst
703 self.onlyfirst = onlyfirst
706
704
707 def match(self, rev):
705 def match(self, rev):
708 def realparents(rev):
706 def realparents(rev):
709 if self.onlyfirst:
707 if self.onlyfirst:
710 return repo.changelog.parentrevs(rev)[0:1]
708 return repo.changelog.parentrevs(rev)[0:1]
711 else:
709 else:
712 return filter(lambda x: x != nullrev,
710 return filter(lambda x: x != nullrev,
713 repo.changelog.parentrevs(rev))
711 repo.changelog.parentrevs(rev))
714
712
715 if self.startrev == nullrev:
713 if self.startrev == nullrev:
716 self.startrev = rev
714 self.startrev = rev
717 return True
715 return True
718
716
719 if rev > self.startrev:
717 if rev > self.startrev:
720 # forward: all descendants
718 # forward: all descendants
721 if not self.roots:
719 if not self.roots:
722 self.roots.append(self.startrev)
720 self.roots.append(self.startrev)
723 for parent in realparents(rev):
721 for parent in realparents(rev):
724 if parent in self.roots:
722 if parent in self.roots:
725 self.roots.append(rev)
723 self.roots.append(rev)
726 return True
724 return True
727 else:
725 else:
728 # backwards: all parents
726 # backwards: all parents
729 if not self.roots:
727 if not self.roots:
730 self.roots.extend(realparents(self.startrev))
728 self.roots.extend(realparents(self.startrev))
731 if rev in self.roots:
729 if rev in self.roots:
732 self.roots.remove(rev)
730 self.roots.remove(rev)
733 self.roots.extend(realparents(rev))
731 self.roots.extend(realparents(rev))
734 return True
732 return True
735
733
736 return False
734 return False
737
735
738 # it might be worthwhile to do this in the iterator if the rev range
736 # it might be worthwhile to do this in the iterator if the rev range
739 # is descending and the prune args are all within that range
737 # is descending and the prune args are all within that range
740 for rev in opts.get('prune', ()):
738 for rev in opts.get('prune', ()):
741 rev = repo.changelog.rev(repo.lookup(rev))
739 rev = repo.changelog.rev(repo.lookup(rev))
742 ff = followfilter()
740 ff = followfilter()
743 stop = min(revs[0], revs[-1])
741 stop = min(revs[0], revs[-1])
744 for x in xrange(rev, stop-1, -1):
742 for x in xrange(rev, stop-1, -1):
745 if ff.match(x) and x in wanted:
743 if ff.match(x) and x in wanted:
746 del wanted[x]
744 del wanted[x]
747
745
748 def iterate():
746 def iterate():
749 if follow and not files:
747 if follow and not files:
750 ff = followfilter(onlyfirst=opts.get('follow_first'))
748 ff = followfilter(onlyfirst=opts.get('follow_first'))
751 def want(rev):
749 def want(rev):
752 if ff.match(rev) and rev in wanted:
750 if ff.match(rev) and rev in wanted:
753 return True
751 return True
754 return False
752 return False
755 else:
753 else:
756 def want(rev):
754 def want(rev):
757 return rev in wanted
755 return rev in wanted
758
756
759 for i, window in increasing_windows(0, len(revs)):
757 for i, window in increasing_windows(0, len(revs)):
760 yield 'window', revs[0] < revs[-1], revs[-1]
758 yield 'window', revs[0] < revs[-1], revs[-1]
761 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
759 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
762 srevs = list(nrevs)
760 srevs = list(nrevs)
763 srevs.sort()
761 srevs.sort()
764 for rev in srevs:
762 for rev in srevs:
765 fns = fncache.get(rev)
763 fns = fncache.get(rev)
766 if not fns:
764 if not fns:
767 def fns_generator():
765 def fns_generator():
768 for f in change(rev)[3]:
766 for f in change(rev)[3]:
769 if matchfn(f):
767 if matchfn(f):
770 yield f
768 yield f
771 fns = fns_generator()
769 fns = fns_generator()
772 yield 'add', rev, fns
770 yield 'add', rev, fns
773 for rev in nrevs:
771 for rev in nrevs:
774 yield 'iter', rev, None
772 yield 'iter', rev, None
775 return iterate(), matchfn
773 return iterate(), matchfn
General Comments 0
You need to be logged in to leave comments. Login now