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