##// END OF EJS Templates
help: add quotes to a few commands we point to...
Martin von Zweigbergk -
r38846:4fe8d1f0 default
parent child Browse files
Show More
@@ -1,560 +1,560 b''
1 # fileset.py - file set queries for mercurial
1 # fileset.py - file set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import re
11 import re
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 error,
15 error,
16 filesetlang,
16 filesetlang,
17 match as matchmod,
17 match as matchmod,
18 merge,
18 merge,
19 pycompat,
19 pycompat,
20 registrar,
20 registrar,
21 scmutil,
21 scmutil,
22 util,
22 util,
23 )
23 )
24 from .utils import (
24 from .utils import (
25 stringutil,
25 stringutil,
26 )
26 )
27
27
28 # helpers for processing parsed tree
28 # helpers for processing parsed tree
29 getsymbol = filesetlang.getsymbol
29 getsymbol = filesetlang.getsymbol
30 getstring = filesetlang.getstring
30 getstring = filesetlang.getstring
31 _getkindpat = filesetlang.getkindpat
31 _getkindpat = filesetlang.getkindpat
32 getpattern = filesetlang.getpattern
32 getpattern = filesetlang.getpattern
33 getargs = filesetlang.getargs
33 getargs = filesetlang.getargs
34
34
35 def getmatch(mctx, x):
35 def getmatch(mctx, x):
36 if not x:
36 if not x:
37 raise error.ParseError(_("missing argument"))
37 raise error.ParseError(_("missing argument"))
38 return methods[x[0]](mctx, *x[1:])
38 return methods[x[0]](mctx, *x[1:])
39
39
40 def stringmatch(mctx, x):
40 def stringmatch(mctx, x):
41 return mctx.matcher([x])
41 return mctx.matcher([x])
42
42
43 def kindpatmatch(mctx, x, y):
43 def kindpatmatch(mctx, x, y):
44 return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
44 return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
45 _("pattern must be a string")))
45 _("pattern must be a string")))
46
46
47 def andmatch(mctx, x, y):
47 def andmatch(mctx, x, y):
48 xm = getmatch(mctx, x)
48 xm = getmatch(mctx, x)
49 ym = getmatch(mctx, y)
49 ym = getmatch(mctx, y)
50 return matchmod.intersectmatchers(xm, ym)
50 return matchmod.intersectmatchers(xm, ym)
51
51
52 def ormatch(mctx, *xs):
52 def ormatch(mctx, *xs):
53 ms = [getmatch(mctx, x) for x in xs]
53 ms = [getmatch(mctx, x) for x in xs]
54 return matchmod.unionmatcher(ms)
54 return matchmod.unionmatcher(ms)
55
55
56 def notmatch(mctx, x):
56 def notmatch(mctx, x):
57 m = getmatch(mctx, x)
57 m = getmatch(mctx, x)
58 return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
58 return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
59
59
60 def minusmatch(mctx, x, y):
60 def minusmatch(mctx, x, y):
61 xm = getmatch(mctx, x)
61 xm = getmatch(mctx, x)
62 ym = getmatch(mctx, y)
62 ym = getmatch(mctx, y)
63 return matchmod.differencematcher(xm, ym)
63 return matchmod.differencematcher(xm, ym)
64
64
65 def negatematch(mctx, x):
65 def negatematch(mctx, x):
66 raise error.ParseError(_("can't use negate operator in this context"))
66 raise error.ParseError(_("can't use negate operator in this context"))
67
67
68 def listmatch(mctx, *xs):
68 def listmatch(mctx, *xs):
69 raise error.ParseError(_("can't use a list in this context"),
69 raise error.ParseError(_("can't use a list in this context"),
70 hint=_('see hg help "filesets.x or y"'))
70 hint=_('see \'hg help "filesets.x or y"\''))
71
71
72 def func(mctx, a, b):
72 def func(mctx, a, b):
73 funcname = getsymbol(a)
73 funcname = getsymbol(a)
74 if funcname in symbols:
74 if funcname in symbols:
75 return symbols[funcname](mctx, b)
75 return symbols[funcname](mctx, b)
76
76
77 keep = lambda fn: getattr(fn, '__doc__', None) is not None
77 keep = lambda fn: getattr(fn, '__doc__', None) is not None
78
78
79 syms = [s for (s, fn) in symbols.items() if keep(fn)]
79 syms = [s for (s, fn) in symbols.items() if keep(fn)]
80 raise error.UnknownIdentifier(funcname, syms)
80 raise error.UnknownIdentifier(funcname, syms)
81
81
82 # symbols are callable like:
82 # symbols are callable like:
83 # fun(mctx, x)
83 # fun(mctx, x)
84 # with:
84 # with:
85 # mctx - current matchctx instance
85 # mctx - current matchctx instance
86 # x - argument in tree form
86 # x - argument in tree form
87 symbols = filesetlang.symbols
87 symbols = filesetlang.symbols
88
88
89 # filesets using matchctx.status()
89 # filesets using matchctx.status()
90 _statuscallers = set()
90 _statuscallers = set()
91
91
92 predicate = registrar.filesetpredicate()
92 predicate = registrar.filesetpredicate()
93
93
94 @predicate('modified()', callstatus=True)
94 @predicate('modified()', callstatus=True)
95 def modified(mctx, x):
95 def modified(mctx, x):
96 """File that is modified according to :hg:`status`.
96 """File that is modified according to :hg:`status`.
97 """
97 """
98 # i18n: "modified" is a keyword
98 # i18n: "modified" is a keyword
99 getargs(x, 0, 0, _("modified takes no arguments"))
99 getargs(x, 0, 0, _("modified takes no arguments"))
100 s = set(mctx.status().modified)
100 s = set(mctx.status().modified)
101 return mctx.predicate(s.__contains__, predrepr='modified')
101 return mctx.predicate(s.__contains__, predrepr='modified')
102
102
103 @predicate('added()', callstatus=True)
103 @predicate('added()', callstatus=True)
104 def added(mctx, x):
104 def added(mctx, x):
105 """File that is added according to :hg:`status`.
105 """File that is added according to :hg:`status`.
106 """
106 """
107 # i18n: "added" is a keyword
107 # i18n: "added" is a keyword
108 getargs(x, 0, 0, _("added takes no arguments"))
108 getargs(x, 0, 0, _("added takes no arguments"))
109 s = set(mctx.status().added)
109 s = set(mctx.status().added)
110 return mctx.predicate(s.__contains__, predrepr='added')
110 return mctx.predicate(s.__contains__, predrepr='added')
111
111
112 @predicate('removed()', callstatus=True)
112 @predicate('removed()', callstatus=True)
113 def removed(mctx, x):
113 def removed(mctx, x):
114 """File that is removed according to :hg:`status`.
114 """File that is removed according to :hg:`status`.
115 """
115 """
116 # i18n: "removed" is a keyword
116 # i18n: "removed" is a keyword
117 getargs(x, 0, 0, _("removed takes no arguments"))
117 getargs(x, 0, 0, _("removed takes no arguments"))
118 s = set(mctx.status().removed)
118 s = set(mctx.status().removed)
119 return mctx.predicate(s.__contains__, predrepr='removed')
119 return mctx.predicate(s.__contains__, predrepr='removed')
120
120
121 @predicate('deleted()', callstatus=True)
121 @predicate('deleted()', callstatus=True)
122 def deleted(mctx, x):
122 def deleted(mctx, x):
123 """Alias for ``missing()``.
123 """Alias for ``missing()``.
124 """
124 """
125 # i18n: "deleted" is a keyword
125 # i18n: "deleted" is a keyword
126 getargs(x, 0, 0, _("deleted takes no arguments"))
126 getargs(x, 0, 0, _("deleted takes no arguments"))
127 s = set(mctx.status().deleted)
127 s = set(mctx.status().deleted)
128 return mctx.predicate(s.__contains__, predrepr='deleted')
128 return mctx.predicate(s.__contains__, predrepr='deleted')
129
129
130 @predicate('missing()', callstatus=True)
130 @predicate('missing()', callstatus=True)
131 def missing(mctx, x):
131 def missing(mctx, x):
132 """File that is missing according to :hg:`status`.
132 """File that is missing according to :hg:`status`.
133 """
133 """
134 # i18n: "missing" is a keyword
134 # i18n: "missing" is a keyword
135 getargs(x, 0, 0, _("missing takes no arguments"))
135 getargs(x, 0, 0, _("missing takes no arguments"))
136 s = set(mctx.status().deleted)
136 s = set(mctx.status().deleted)
137 return mctx.predicate(s.__contains__, predrepr='deleted')
137 return mctx.predicate(s.__contains__, predrepr='deleted')
138
138
139 @predicate('unknown()', callstatus=True)
139 @predicate('unknown()', callstatus=True)
140 def unknown(mctx, x):
140 def unknown(mctx, x):
141 """File that is unknown according to :hg:`status`."""
141 """File that is unknown according to :hg:`status`."""
142 # i18n: "unknown" is a keyword
142 # i18n: "unknown" is a keyword
143 getargs(x, 0, 0, _("unknown takes no arguments"))
143 getargs(x, 0, 0, _("unknown takes no arguments"))
144 s = set(mctx.status().unknown)
144 s = set(mctx.status().unknown)
145 return mctx.predicate(s.__contains__, predrepr='unknown')
145 return mctx.predicate(s.__contains__, predrepr='unknown')
146
146
147 @predicate('ignored()', callstatus=True)
147 @predicate('ignored()', callstatus=True)
148 def ignored(mctx, x):
148 def ignored(mctx, x):
149 """File that is ignored according to :hg:`status`."""
149 """File that is ignored according to :hg:`status`."""
150 # i18n: "ignored" is a keyword
150 # i18n: "ignored" is a keyword
151 getargs(x, 0, 0, _("ignored takes no arguments"))
151 getargs(x, 0, 0, _("ignored takes no arguments"))
152 s = set(mctx.status().ignored)
152 s = set(mctx.status().ignored)
153 return mctx.predicate(s.__contains__, predrepr='ignored')
153 return mctx.predicate(s.__contains__, predrepr='ignored')
154
154
155 @predicate('clean()', callstatus=True)
155 @predicate('clean()', callstatus=True)
156 def clean(mctx, x):
156 def clean(mctx, x):
157 """File that is clean according to :hg:`status`.
157 """File that is clean according to :hg:`status`.
158 """
158 """
159 # i18n: "clean" is a keyword
159 # i18n: "clean" is a keyword
160 getargs(x, 0, 0, _("clean takes no arguments"))
160 getargs(x, 0, 0, _("clean takes no arguments"))
161 s = set(mctx.status().clean)
161 s = set(mctx.status().clean)
162 return mctx.predicate(s.__contains__, predrepr='clean')
162 return mctx.predicate(s.__contains__, predrepr='clean')
163
163
164 @predicate('tracked()')
164 @predicate('tracked()')
165 def tracked(mctx, x):
165 def tracked(mctx, x):
166 """File that is under Mercurial control."""
166 """File that is under Mercurial control."""
167 # i18n: "tracked" is a keyword
167 # i18n: "tracked" is a keyword
168 getargs(x, 0, 0, _("tracked takes no arguments"))
168 getargs(x, 0, 0, _("tracked takes no arguments"))
169 return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
169 return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
170
170
171 @predicate('binary()')
171 @predicate('binary()')
172 def binary(mctx, x):
172 def binary(mctx, x):
173 """File that appears to be binary (contains NUL bytes).
173 """File that appears to be binary (contains NUL bytes).
174 """
174 """
175 # i18n: "binary" is a keyword
175 # i18n: "binary" is a keyword
176 getargs(x, 0, 0, _("binary takes no arguments"))
176 getargs(x, 0, 0, _("binary takes no arguments"))
177 return mctx.fpredicate(lambda fctx: fctx.isbinary(),
177 return mctx.fpredicate(lambda fctx: fctx.isbinary(),
178 predrepr='binary', cache=True)
178 predrepr='binary', cache=True)
179
179
180 @predicate('exec()')
180 @predicate('exec()')
181 def exec_(mctx, x):
181 def exec_(mctx, x):
182 """File that is marked as executable.
182 """File that is marked as executable.
183 """
183 """
184 # i18n: "exec" is a keyword
184 # i18n: "exec" is a keyword
185 getargs(x, 0, 0, _("exec takes no arguments"))
185 getargs(x, 0, 0, _("exec takes no arguments"))
186 ctx = mctx.ctx
186 ctx = mctx.ctx
187 return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
187 return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
188
188
189 @predicate('symlink()')
189 @predicate('symlink()')
190 def symlink(mctx, x):
190 def symlink(mctx, x):
191 """File that is marked as a symlink.
191 """File that is marked as a symlink.
192 """
192 """
193 # i18n: "symlink" is a keyword
193 # i18n: "symlink" is a keyword
194 getargs(x, 0, 0, _("symlink takes no arguments"))
194 getargs(x, 0, 0, _("symlink takes no arguments"))
195 ctx = mctx.ctx
195 ctx = mctx.ctx
196 return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
196 return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
197
197
198 @predicate('resolved()')
198 @predicate('resolved()')
199 def resolved(mctx, x):
199 def resolved(mctx, x):
200 """File that is marked resolved according to :hg:`resolve -l`.
200 """File that is marked resolved according to :hg:`resolve -l`.
201 """
201 """
202 # i18n: "resolved" is a keyword
202 # i18n: "resolved" is a keyword
203 getargs(x, 0, 0, _("resolved takes no arguments"))
203 getargs(x, 0, 0, _("resolved takes no arguments"))
204 if mctx.ctx.rev() is not None:
204 if mctx.ctx.rev() is not None:
205 return mctx.never()
205 return mctx.never()
206 ms = merge.mergestate.read(mctx.ctx.repo())
206 ms = merge.mergestate.read(mctx.ctx.repo())
207 return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
207 return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
208 predrepr='resolved')
208 predrepr='resolved')
209
209
210 @predicate('unresolved()')
210 @predicate('unresolved()')
211 def unresolved(mctx, x):
211 def unresolved(mctx, x):
212 """File that is marked unresolved according to :hg:`resolve -l`.
212 """File that is marked unresolved according to :hg:`resolve -l`.
213 """
213 """
214 # i18n: "unresolved" is a keyword
214 # i18n: "unresolved" is a keyword
215 getargs(x, 0, 0, _("unresolved takes no arguments"))
215 getargs(x, 0, 0, _("unresolved takes no arguments"))
216 if mctx.ctx.rev() is not None:
216 if mctx.ctx.rev() is not None:
217 return mctx.never()
217 return mctx.never()
218 ms = merge.mergestate.read(mctx.ctx.repo())
218 ms = merge.mergestate.read(mctx.ctx.repo())
219 return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
219 return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
220 predrepr='unresolved')
220 predrepr='unresolved')
221
221
222 @predicate('hgignore()')
222 @predicate('hgignore()')
223 def hgignore(mctx, x):
223 def hgignore(mctx, x):
224 """File that matches the active .hgignore pattern.
224 """File that matches the active .hgignore pattern.
225 """
225 """
226 # i18n: "hgignore" is a keyword
226 # i18n: "hgignore" is a keyword
227 getargs(x, 0, 0, _("hgignore takes no arguments"))
227 getargs(x, 0, 0, _("hgignore takes no arguments"))
228 return mctx.ctx.repo().dirstate._ignore
228 return mctx.ctx.repo().dirstate._ignore
229
229
230 @predicate('portable()')
230 @predicate('portable()')
231 def portable(mctx, x):
231 def portable(mctx, x):
232 """File that has a portable name. (This doesn't include filenames with case
232 """File that has a portable name. (This doesn't include filenames with case
233 collisions.)
233 collisions.)
234 """
234 """
235 # i18n: "portable" is a keyword
235 # i18n: "portable" is a keyword
236 getargs(x, 0, 0, _("portable takes no arguments"))
236 getargs(x, 0, 0, _("portable takes no arguments"))
237 return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
237 return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
238 predrepr='portable')
238 predrepr='portable')
239
239
240 @predicate('grep(regex)')
240 @predicate('grep(regex)')
241 def grep(mctx, x):
241 def grep(mctx, x):
242 """File contains the given regular expression.
242 """File contains the given regular expression.
243 """
243 """
244 try:
244 try:
245 # i18n: "grep" is a keyword
245 # i18n: "grep" is a keyword
246 r = re.compile(getstring(x, _("grep requires a pattern")))
246 r = re.compile(getstring(x, _("grep requires a pattern")))
247 except re.error as e:
247 except re.error as e:
248 raise error.ParseError(_('invalid match pattern: %s') %
248 raise error.ParseError(_('invalid match pattern: %s') %
249 stringutil.forcebytestr(e))
249 stringutil.forcebytestr(e))
250 return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
250 return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
251 predrepr=('grep(%r)', r.pattern), cache=True)
251 predrepr=('grep(%r)', r.pattern), cache=True)
252
252
253 def _sizetomax(s):
253 def _sizetomax(s):
254 try:
254 try:
255 s = s.strip().lower()
255 s = s.strip().lower()
256 for k, v in util._sizeunits:
256 for k, v in util._sizeunits:
257 if s.endswith(k):
257 if s.endswith(k):
258 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
258 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
259 n = s[:-len(k)]
259 n = s[:-len(k)]
260 inc = 1.0
260 inc = 1.0
261 if "." in n:
261 if "." in n:
262 inc /= 10 ** len(n.split(".")[1])
262 inc /= 10 ** len(n.split(".")[1])
263 return int((float(n) + inc) * v) - 1
263 return int((float(n) + inc) * v) - 1
264 # no extension, this is a precise value
264 # no extension, this is a precise value
265 return int(s)
265 return int(s)
266 except ValueError:
266 except ValueError:
267 raise error.ParseError(_("couldn't parse size: %s") % s)
267 raise error.ParseError(_("couldn't parse size: %s") % s)
268
268
269 def sizematcher(expr):
269 def sizematcher(expr):
270 """Return a function(size) -> bool from the ``size()`` expression"""
270 """Return a function(size) -> bool from the ``size()`` expression"""
271 expr = expr.strip()
271 expr = expr.strip()
272 if '-' in expr: # do we have a range?
272 if '-' in expr: # do we have a range?
273 a, b = expr.split('-', 1)
273 a, b = expr.split('-', 1)
274 a = util.sizetoint(a)
274 a = util.sizetoint(a)
275 b = util.sizetoint(b)
275 b = util.sizetoint(b)
276 return lambda x: x >= a and x <= b
276 return lambda x: x >= a and x <= b
277 elif expr.startswith("<="):
277 elif expr.startswith("<="):
278 a = util.sizetoint(expr[2:])
278 a = util.sizetoint(expr[2:])
279 return lambda x: x <= a
279 return lambda x: x <= a
280 elif expr.startswith("<"):
280 elif expr.startswith("<"):
281 a = util.sizetoint(expr[1:])
281 a = util.sizetoint(expr[1:])
282 return lambda x: x < a
282 return lambda x: x < a
283 elif expr.startswith(">="):
283 elif expr.startswith(">="):
284 a = util.sizetoint(expr[2:])
284 a = util.sizetoint(expr[2:])
285 return lambda x: x >= a
285 return lambda x: x >= a
286 elif expr.startswith(">"):
286 elif expr.startswith(">"):
287 a = util.sizetoint(expr[1:])
287 a = util.sizetoint(expr[1:])
288 return lambda x: x > a
288 return lambda x: x > a
289 else:
289 else:
290 a = util.sizetoint(expr)
290 a = util.sizetoint(expr)
291 b = _sizetomax(expr)
291 b = _sizetomax(expr)
292 return lambda x: x >= a and x <= b
292 return lambda x: x >= a and x <= b
293
293
294 @predicate('size(expression)')
294 @predicate('size(expression)')
295 def size(mctx, x):
295 def size(mctx, x):
296 """File size matches the given expression. Examples:
296 """File size matches the given expression. Examples:
297
297
298 - size('1k') - files from 1024 to 2047 bytes
298 - size('1k') - files from 1024 to 2047 bytes
299 - size('< 20k') - files less than 20480 bytes
299 - size('< 20k') - files less than 20480 bytes
300 - size('>= .5MB') - files at least 524288 bytes
300 - size('>= .5MB') - files at least 524288 bytes
301 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
301 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
302 """
302 """
303 # i18n: "size" is a keyword
303 # i18n: "size" is a keyword
304 expr = getstring(x, _("size requires an expression"))
304 expr = getstring(x, _("size requires an expression"))
305 m = sizematcher(expr)
305 m = sizematcher(expr)
306 return mctx.fpredicate(lambda fctx: m(fctx.size()),
306 return mctx.fpredicate(lambda fctx: m(fctx.size()),
307 predrepr=('size(%r)', expr), cache=True)
307 predrepr=('size(%r)', expr), cache=True)
308
308
309 @predicate('encoding(name)')
309 @predicate('encoding(name)')
310 def encoding(mctx, x):
310 def encoding(mctx, x):
311 """File can be successfully decoded with the given character
311 """File can be successfully decoded with the given character
312 encoding. May not be useful for encodings other than ASCII and
312 encoding. May not be useful for encodings other than ASCII and
313 UTF-8.
313 UTF-8.
314 """
314 """
315
315
316 # i18n: "encoding" is a keyword
316 # i18n: "encoding" is a keyword
317 enc = getstring(x, _("encoding requires an encoding name"))
317 enc = getstring(x, _("encoding requires an encoding name"))
318
318
319 def encp(fctx):
319 def encp(fctx):
320 d = fctx.data()
320 d = fctx.data()
321 try:
321 try:
322 d.decode(pycompat.sysstr(enc))
322 d.decode(pycompat.sysstr(enc))
323 return True
323 return True
324 except LookupError:
324 except LookupError:
325 raise error.Abort(_("unknown encoding '%s'") % enc)
325 raise error.Abort(_("unknown encoding '%s'") % enc)
326 except UnicodeDecodeError:
326 except UnicodeDecodeError:
327 return False
327 return False
328
328
329 return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
329 return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
330
330
331 @predicate('eol(style)')
331 @predicate('eol(style)')
332 def eol(mctx, x):
332 def eol(mctx, x):
333 """File contains newlines of the given style (dos, unix, mac). Binary
333 """File contains newlines of the given style (dos, unix, mac). Binary
334 files are excluded, files with mixed line endings match multiple
334 files are excluded, files with mixed line endings match multiple
335 styles.
335 styles.
336 """
336 """
337
337
338 # i18n: "eol" is a keyword
338 # i18n: "eol" is a keyword
339 enc = getstring(x, _("eol requires a style name"))
339 enc = getstring(x, _("eol requires a style name"))
340
340
341 def eolp(fctx):
341 def eolp(fctx):
342 if fctx.isbinary():
342 if fctx.isbinary():
343 return False
343 return False
344 d = fctx.data()
344 d = fctx.data()
345 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
345 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
346 return True
346 return True
347 elif enc == 'unix' and re.search('(?<!\r)\n', d):
347 elif enc == 'unix' and re.search('(?<!\r)\n', d):
348 return True
348 return True
349 elif enc == 'mac' and re.search('\r(?!\n)', d):
349 elif enc == 'mac' and re.search('\r(?!\n)', d):
350 return True
350 return True
351 return False
351 return False
352 return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
352 return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
353
353
354 @predicate('copied()')
354 @predicate('copied()')
355 def copied(mctx, x):
355 def copied(mctx, x):
356 """File that is recorded as being copied.
356 """File that is recorded as being copied.
357 """
357 """
358 # i18n: "copied" is a keyword
358 # i18n: "copied" is a keyword
359 getargs(x, 0, 0, _("copied takes no arguments"))
359 getargs(x, 0, 0, _("copied takes no arguments"))
360 def copiedp(fctx):
360 def copiedp(fctx):
361 p = fctx.parents()
361 p = fctx.parents()
362 return p and p[0].path() != fctx.path()
362 return p and p[0].path() != fctx.path()
363 return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
363 return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
364
364
365 @predicate('revs(revs, pattern)')
365 @predicate('revs(revs, pattern)')
366 def revs(mctx, x):
366 def revs(mctx, x):
367 """Evaluate set in the specified revisions. If the revset match multiple
367 """Evaluate set in the specified revisions. If the revset match multiple
368 revs, this will return file matching pattern in any of the revision.
368 revs, this will return file matching pattern in any of the revision.
369 """
369 """
370 # i18n: "revs" is a keyword
370 # i18n: "revs" is a keyword
371 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
371 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
372 # i18n: "revs" is a keyword
372 # i18n: "revs" is a keyword
373 revspec = getstring(r, _("first argument to revs must be a revision"))
373 revspec = getstring(r, _("first argument to revs must be a revision"))
374 repo = mctx.ctx.repo()
374 repo = mctx.ctx.repo()
375 revs = scmutil.revrange(repo, [revspec])
375 revs = scmutil.revrange(repo, [revspec])
376
376
377 matchers = []
377 matchers = []
378 for r in revs:
378 for r in revs:
379 ctx = repo[r]
379 ctx = repo[r]
380 matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x))
380 matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x))
381 if not matchers:
381 if not matchers:
382 return mctx.never()
382 return mctx.never()
383 if len(matchers) == 1:
383 if len(matchers) == 1:
384 return matchers[0]
384 return matchers[0]
385 return matchmod.unionmatcher(matchers)
385 return matchmod.unionmatcher(matchers)
386
386
387 @predicate('status(base, rev, pattern)')
387 @predicate('status(base, rev, pattern)')
388 def status(mctx, x):
388 def status(mctx, x):
389 """Evaluate predicate using status change between ``base`` and
389 """Evaluate predicate using status change between ``base`` and
390 ``rev``. Examples:
390 ``rev``. Examples:
391
391
392 - ``status(3, 7, added())`` - matches files added from "3" to "7"
392 - ``status(3, 7, added())`` - matches files added from "3" to "7"
393 """
393 """
394 repo = mctx.ctx.repo()
394 repo = mctx.ctx.repo()
395 # i18n: "status" is a keyword
395 # i18n: "status" is a keyword
396 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
396 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
397 # i18n: "status" is a keyword
397 # i18n: "status" is a keyword
398 baseerr = _("first argument to status must be a revision")
398 baseerr = _("first argument to status must be a revision")
399 baserevspec = getstring(b, baseerr)
399 baserevspec = getstring(b, baseerr)
400 if not baserevspec:
400 if not baserevspec:
401 raise error.ParseError(baseerr)
401 raise error.ParseError(baseerr)
402 reverr = _("second argument to status must be a revision")
402 reverr = _("second argument to status must be a revision")
403 revspec = getstring(r, reverr)
403 revspec = getstring(r, reverr)
404 if not revspec:
404 if not revspec:
405 raise error.ParseError(reverr)
405 raise error.ParseError(reverr)
406 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
406 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
407 return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
407 return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
408
408
409 @predicate('subrepo([pattern])')
409 @predicate('subrepo([pattern])')
410 def subrepo(mctx, x):
410 def subrepo(mctx, x):
411 """Subrepositories whose paths match the given pattern.
411 """Subrepositories whose paths match the given pattern.
412 """
412 """
413 # i18n: "subrepo" is a keyword
413 # i18n: "subrepo" is a keyword
414 getargs(x, 0, 1, _("subrepo takes at most one argument"))
414 getargs(x, 0, 1, _("subrepo takes at most one argument"))
415 ctx = mctx.ctx
415 ctx = mctx.ctx
416 sstate = ctx.substate
416 sstate = ctx.substate
417 if x:
417 if x:
418 pat = getpattern(x, matchmod.allpatternkinds,
418 pat = getpattern(x, matchmod.allpatternkinds,
419 # i18n: "subrepo" is a keyword
419 # i18n: "subrepo" is a keyword
420 _("subrepo requires a pattern or no arguments"))
420 _("subrepo requires a pattern or no arguments"))
421 fast = not matchmod.patkind(pat)
421 fast = not matchmod.patkind(pat)
422 if fast:
422 if fast:
423 def m(s):
423 def m(s):
424 return (s == pat)
424 return (s == pat)
425 else:
425 else:
426 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
426 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
427 return mctx.predicate(lambda f: f in sstate and m(f),
427 return mctx.predicate(lambda f: f in sstate and m(f),
428 predrepr=('subrepo(%r)', pat))
428 predrepr=('subrepo(%r)', pat))
429 else:
429 else:
430 return mctx.predicate(sstate.__contains__, predrepr='subrepo')
430 return mctx.predicate(sstate.__contains__, predrepr='subrepo')
431
431
432 methods = {
432 methods = {
433 'string': stringmatch,
433 'string': stringmatch,
434 'symbol': stringmatch,
434 'symbol': stringmatch,
435 'kindpat': kindpatmatch,
435 'kindpat': kindpatmatch,
436 'and': andmatch,
436 'and': andmatch,
437 'or': ormatch,
437 'or': ormatch,
438 'minus': minusmatch,
438 'minus': minusmatch,
439 'negate': negatematch,
439 'negate': negatematch,
440 'list': listmatch,
440 'list': listmatch,
441 'group': getmatch,
441 'group': getmatch,
442 'not': notmatch,
442 'not': notmatch,
443 'func': func,
443 'func': func,
444 }
444 }
445
445
446 class matchctx(object):
446 class matchctx(object):
447 def __init__(self, ctx, status=None, badfn=None):
447 def __init__(self, ctx, status=None, badfn=None):
448 self.ctx = ctx
448 self.ctx = ctx
449 self._status = status
449 self._status = status
450 self._badfn = badfn
450 self._badfn = badfn
451
451
452 def status(self):
452 def status(self):
453 return self._status
453 return self._status
454
454
455 def matcher(self, patterns):
455 def matcher(self, patterns):
456 return self.ctx.match(patterns, badfn=self._badfn)
456 return self.ctx.match(patterns, badfn=self._badfn)
457
457
458 def predicate(self, predfn, predrepr=None, cache=False):
458 def predicate(self, predfn, predrepr=None, cache=False):
459 """Create a matcher to select files by predfn(filename)"""
459 """Create a matcher to select files by predfn(filename)"""
460 if cache:
460 if cache:
461 predfn = util.cachefunc(predfn)
461 predfn = util.cachefunc(predfn)
462 repo = self.ctx.repo()
462 repo = self.ctx.repo()
463 return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn,
463 return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn,
464 predrepr=predrepr, badfn=self._badfn)
464 predrepr=predrepr, badfn=self._badfn)
465
465
466 def fpredicate(self, predfn, predrepr=None, cache=False):
466 def fpredicate(self, predfn, predrepr=None, cache=False):
467 """Create a matcher to select files by predfn(fctx) at the current
467 """Create a matcher to select files by predfn(fctx) at the current
468 revision
468 revision
469
469
470 Missing files are ignored.
470 Missing files are ignored.
471 """
471 """
472 ctx = self.ctx
472 ctx = self.ctx
473 if ctx.rev() is None:
473 if ctx.rev() is None:
474 def fctxpredfn(f):
474 def fctxpredfn(f):
475 try:
475 try:
476 fctx = ctx[f]
476 fctx = ctx[f]
477 except error.LookupError:
477 except error.LookupError:
478 return False
478 return False
479 try:
479 try:
480 fctx.audit()
480 fctx.audit()
481 except error.Abort:
481 except error.Abort:
482 return False
482 return False
483 try:
483 try:
484 return predfn(fctx)
484 return predfn(fctx)
485 except (IOError, OSError) as e:
485 except (IOError, OSError) as e:
486 # open()-ing a directory fails with EACCES on Windows
486 # open()-ing a directory fails with EACCES on Windows
487 if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR,
487 if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR,
488 errno.EISDIR):
488 errno.EISDIR):
489 return False
489 return False
490 raise
490 raise
491 else:
491 else:
492 def fctxpredfn(f):
492 def fctxpredfn(f):
493 try:
493 try:
494 fctx = ctx[f]
494 fctx = ctx[f]
495 except error.LookupError:
495 except error.LookupError:
496 return False
496 return False
497 return predfn(fctx)
497 return predfn(fctx)
498 return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
498 return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
499
499
500 def never(self):
500 def never(self):
501 """Create a matcher to select nothing"""
501 """Create a matcher to select nothing"""
502 repo = self.ctx.repo()
502 repo = self.ctx.repo()
503 return matchmod.nevermatcher(repo.root, repo.getcwd(),
503 return matchmod.nevermatcher(repo.root, repo.getcwd(),
504 badfn=self._badfn)
504 badfn=self._badfn)
505
505
506 def switch(self, ctx, status=None):
506 def switch(self, ctx, status=None):
507 return matchctx(ctx, status, self._badfn)
507 return matchctx(ctx, status, self._badfn)
508
508
509 # filesets using matchctx.switch()
509 # filesets using matchctx.switch()
510 _switchcallers = [
510 _switchcallers = [
511 'revs',
511 'revs',
512 'status',
512 'status',
513 ]
513 ]
514
514
515 def _intree(funcs, tree):
515 def _intree(funcs, tree):
516 if isinstance(tree, tuple):
516 if isinstance(tree, tuple):
517 if tree[0] == 'func' and tree[1][0] == 'symbol':
517 if tree[0] == 'func' and tree[1][0] == 'symbol':
518 if tree[1][1] in funcs:
518 if tree[1][1] in funcs:
519 return True
519 return True
520 if tree[1][1] in _switchcallers:
520 if tree[1][1] in _switchcallers:
521 # arguments won't be evaluated in the current context
521 # arguments won't be evaluated in the current context
522 return False
522 return False
523 for s in tree[1:]:
523 for s in tree[1:]:
524 if _intree(funcs, s):
524 if _intree(funcs, s):
525 return True
525 return True
526 return False
526 return False
527
527
528 def match(ctx, expr, badfn=None):
528 def match(ctx, expr, badfn=None):
529 """Create a matcher for a single fileset expression"""
529 """Create a matcher for a single fileset expression"""
530 tree = filesetlang.parse(expr)
530 tree = filesetlang.parse(expr)
531 mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
531 mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
532 return getmatch(mctx, tree)
532 return getmatch(mctx, tree)
533
533
534 def _buildstatus(ctx, tree, basectx=None):
534 def _buildstatus(ctx, tree, basectx=None):
535 # do we need status info?
535 # do we need status info?
536
536
537 if _intree(_statuscallers, tree):
537 if _intree(_statuscallers, tree):
538 unknown = _intree(['unknown'], tree)
538 unknown = _intree(['unknown'], tree)
539 ignored = _intree(['ignored'], tree)
539 ignored = _intree(['ignored'], tree)
540
540
541 if basectx is None:
541 if basectx is None:
542 basectx = ctx.p1()
542 basectx = ctx.p1()
543 return basectx.status(ctx, listunknown=unknown, listignored=ignored,
543 return basectx.status(ctx, listunknown=unknown, listignored=ignored,
544 listclean=True)
544 listclean=True)
545 else:
545 else:
546 return None
546 return None
547
547
548 def loadpredicate(ui, extname, registrarobj):
548 def loadpredicate(ui, extname, registrarobj):
549 """Load fileset predicates from specified registrarobj
549 """Load fileset predicates from specified registrarobj
550 """
550 """
551 for name, func in registrarobj._table.iteritems():
551 for name, func in registrarobj._table.iteritems():
552 symbols[name] = func
552 symbols[name] = func
553 if func._callstatus:
553 if func._callstatus:
554 _statuscallers.add(name)
554 _statuscallers.add(name)
555
555
556 # load built-in predicates explicitly to setup _statuscallers
556 # load built-in predicates explicitly to setup _statuscallers
557 loadpredicate(None, None, predicate)
557 loadpredicate(None, None, predicate)
558
558
559 # tell hggettext to extract docstrings from these functions:
559 # tell hggettext to extract docstrings from these functions:
560 i18nfunctions = symbols.values()
560 i18nfunctions = symbols.values()
@@ -1,92 +1,92 b''
1 # minifileset.py - a simple language to select files
1 # minifileset.py - a simple language to select files
2 #
2 #
3 # Copyright 2017 Facebook, Inc.
3 # Copyright 2017 Facebook, Inc.
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 from .i18n import _
10 from .i18n import _
11 from . import (
11 from . import (
12 error,
12 error,
13 fileset,
13 fileset,
14 filesetlang,
14 filesetlang,
15 pycompat,
15 pycompat,
16 )
16 )
17
17
18 def _sizep(x):
18 def _sizep(x):
19 # i18n: "size" is a keyword
19 # i18n: "size" is a keyword
20 expr = filesetlang.getstring(x, _("size requires an expression"))
20 expr = filesetlang.getstring(x, _("size requires an expression"))
21 return fileset.sizematcher(expr)
21 return fileset.sizematcher(expr)
22
22
23 def _compile(tree):
23 def _compile(tree):
24 if not tree:
24 if not tree:
25 raise error.ParseError(_("missing argument"))
25 raise error.ParseError(_("missing argument"))
26 op = tree[0]
26 op = tree[0]
27 if op in {'symbol', 'string', 'kindpat'}:
27 if op in {'symbol', 'string', 'kindpat'}:
28 name = filesetlang.getpattern(tree, {'path'}, _('invalid file pattern'))
28 name = filesetlang.getpattern(tree, {'path'}, _('invalid file pattern'))
29 if name.startswith('**'): # file extension test, ex. "**.tar.gz"
29 if name.startswith('**'): # file extension test, ex. "**.tar.gz"
30 ext = name[2:]
30 ext = name[2:]
31 for c in pycompat.bytestr(ext):
31 for c in pycompat.bytestr(ext):
32 if c in '*{}[]?/\\':
32 if c in '*{}[]?/\\':
33 raise error.ParseError(_('reserved character: %s') % c)
33 raise error.ParseError(_('reserved character: %s') % c)
34 return lambda n, s: n.endswith(ext)
34 return lambda n, s: n.endswith(ext)
35 elif name.startswith('path:'): # directory or full path test
35 elif name.startswith('path:'): # directory or full path test
36 p = name[5:] # prefix
36 p = name[5:] # prefix
37 pl = len(p)
37 pl = len(p)
38 f = lambda n, s: n.startswith(p) and (len(n) == pl
38 f = lambda n, s: n.startswith(p) and (len(n) == pl
39 or n[pl:pl + 1] == '/')
39 or n[pl:pl + 1] == '/')
40 return f
40 return f
41 raise error.ParseError(_("unsupported file pattern: %s") % name,
41 raise error.ParseError(_("unsupported file pattern: %s") % name,
42 hint=_('paths must be prefixed with "path:"'))
42 hint=_('paths must be prefixed with "path:"'))
43 elif op == 'or':
43 elif op == 'or':
44 funcs = [_compile(x) for x in tree[1:]]
44 funcs = [_compile(x) for x in tree[1:]]
45 return lambda n, s: any(f(n, s) for f in funcs)
45 return lambda n, s: any(f(n, s) for f in funcs)
46 elif op == 'and':
46 elif op == 'and':
47 func1 = _compile(tree[1])
47 func1 = _compile(tree[1])
48 func2 = _compile(tree[2])
48 func2 = _compile(tree[2])
49 return lambda n, s: func1(n, s) and func2(n, s)
49 return lambda n, s: func1(n, s) and func2(n, s)
50 elif op == 'not':
50 elif op == 'not':
51 return lambda n, s: not _compile(tree[1])(n, s)
51 return lambda n, s: not _compile(tree[1])(n, s)
52 elif op == 'group':
52 elif op == 'group':
53 return _compile(tree[1])
53 return _compile(tree[1])
54 elif op == 'func':
54 elif op == 'func':
55 symbols = {
55 symbols = {
56 'all': lambda n, s: True,
56 'all': lambda n, s: True,
57 'none': lambda n, s: False,
57 'none': lambda n, s: False,
58 'size': lambda n, s: _sizep(tree[2])(s),
58 'size': lambda n, s: _sizep(tree[2])(s),
59 }
59 }
60
60
61 name = filesetlang.getsymbol(tree[1])
61 name = filesetlang.getsymbol(tree[1])
62 if name in symbols:
62 if name in symbols:
63 return symbols[name]
63 return symbols[name]
64
64
65 raise error.UnknownIdentifier(name, symbols.keys())
65 raise error.UnknownIdentifier(name, symbols.keys())
66 elif op == 'minus': # equivalent to 'x and not y'
66 elif op == 'minus': # equivalent to 'x and not y'
67 func1 = _compile(tree[1])
67 func1 = _compile(tree[1])
68 func2 = _compile(tree[2])
68 func2 = _compile(tree[2])
69 return lambda n, s: func1(n, s) and not func2(n, s)
69 return lambda n, s: func1(n, s) and not func2(n, s)
70 elif op == 'negate':
70 elif op == 'negate':
71 raise error.ParseError(_("can't use negate operator in this context"))
71 raise error.ParseError(_("can't use negate operator in this context"))
72 elif op == 'list':
72 elif op == 'list':
73 raise error.ParseError(_("can't use a list in this context"),
73 raise error.ParseError(_("can't use a list in this context"),
74 hint=_('see hg help "filesets.x or y"'))
74 hint=_('see \'hg help "filesets.x or y"\''))
75 raise error.ProgrammingError('illegal tree: %r' % (tree,))
75 raise error.ProgrammingError('illegal tree: %r' % (tree,))
76
76
77 def compile(text):
77 def compile(text):
78 """generate a function (path, size) -> bool from filter specification.
78 """generate a function (path, size) -> bool from filter specification.
79
79
80 "text" could contain the operators defined by the fileset language for
80 "text" could contain the operators defined by the fileset language for
81 common logic operations, and parenthesis for grouping. The supported path
81 common logic operations, and parenthesis for grouping. The supported path
82 tests are '**.extname' for file extension test, and '"path:dir/subdir"'
82 tests are '**.extname' for file extension test, and '"path:dir/subdir"'
83 for prefix test. The ``size()`` predicate is borrowed from filesets to test
83 for prefix test. The ``size()`` predicate is borrowed from filesets to test
84 file size. The predicates ``all()`` and ``none()`` are also supported.
84 file size. The predicates ``all()`` and ``none()`` are also supported.
85
85
86 '(**.php & size(">10MB")) | **.zip | (path:bin & !path:bin/README)' for
86 '(**.php & size(">10MB")) | **.zip | (path:bin & !path:bin/README)' for
87 example, will catch all php files whose size is greater than 10 MB, all
87 example, will catch all php files whose size is greater than 10 MB, all
88 files whose name ends with ".zip", and all files under "bin" in the repo
88 files whose name ends with ".zip", and all files under "bin" in the repo
89 root except for "bin/README".
89 root except for "bin/README".
90 """
90 """
91 tree = filesetlang.parse(text)
91 tree = filesetlang.parse(text)
92 return _compile(tree)
92 return _compile(tree)
@@ -1,2282 +1,2282 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 dagop,
14 dagop,
15 destutil,
15 destutil,
16 diffutil,
16 diffutil,
17 encoding,
17 encoding,
18 error,
18 error,
19 hbisect,
19 hbisect,
20 match as matchmod,
20 match as matchmod,
21 node,
21 node,
22 obsolete as obsmod,
22 obsolete as obsmod,
23 obsutil,
23 obsutil,
24 pathutil,
24 pathutil,
25 phases,
25 phases,
26 pycompat,
26 pycompat,
27 registrar,
27 registrar,
28 repoview,
28 repoview,
29 revsetlang,
29 revsetlang,
30 scmutil,
30 scmutil,
31 smartset,
31 smartset,
32 stack as stackmod,
32 stack as stackmod,
33 util,
33 util,
34 )
34 )
35 from .utils import (
35 from .utils import (
36 dateutil,
36 dateutil,
37 stringutil,
37 stringutil,
38 )
38 )
39
39
40 # helpers for processing parsed tree
40 # helpers for processing parsed tree
41 getsymbol = revsetlang.getsymbol
41 getsymbol = revsetlang.getsymbol
42 getstring = revsetlang.getstring
42 getstring = revsetlang.getstring
43 getinteger = revsetlang.getinteger
43 getinteger = revsetlang.getinteger
44 getboolean = revsetlang.getboolean
44 getboolean = revsetlang.getboolean
45 getlist = revsetlang.getlist
45 getlist = revsetlang.getlist
46 getrange = revsetlang.getrange
46 getrange = revsetlang.getrange
47 getargs = revsetlang.getargs
47 getargs = revsetlang.getargs
48 getargsdict = revsetlang.getargsdict
48 getargsdict = revsetlang.getargsdict
49
49
50 baseset = smartset.baseset
50 baseset = smartset.baseset
51 generatorset = smartset.generatorset
51 generatorset = smartset.generatorset
52 spanset = smartset.spanset
52 spanset = smartset.spanset
53 fullreposet = smartset.fullreposet
53 fullreposet = smartset.fullreposet
54
54
55 # Constants for ordering requirement, used in getset():
55 # Constants for ordering requirement, used in getset():
56 #
56 #
57 # If 'define', any nested functions and operations MAY change the ordering of
57 # If 'define', any nested functions and operations MAY change the ordering of
58 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
58 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
59 # it). If 'follow', any nested functions and operations MUST take the ordering
59 # it). If 'follow', any nested functions and operations MUST take the ordering
60 # specified by the first operand to the '&' operator.
60 # specified by the first operand to the '&' operator.
61 #
61 #
62 # For instance,
62 # For instance,
63 #
63 #
64 # X & (Y | Z)
64 # X & (Y | Z)
65 # ^ ^^^^^^^
65 # ^ ^^^^^^^
66 # | follow
66 # | follow
67 # define
67 # define
68 #
68 #
69 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
69 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
70 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
70 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
71 #
71 #
72 # 'any' means the order doesn't matter. For instance,
72 # 'any' means the order doesn't matter. For instance,
73 #
73 #
74 # (X & !Y) | ancestors(Z)
74 # (X & !Y) | ancestors(Z)
75 # ^ ^
75 # ^ ^
76 # any any
76 # any any
77 #
77 #
78 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
78 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
79 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
79 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
80 # since 'ancestors' does not care about the order of its argument.
80 # since 'ancestors' does not care about the order of its argument.
81 #
81 #
82 # Currently, most revsets do not care about the order, so 'define' is
82 # Currently, most revsets do not care about the order, so 'define' is
83 # equivalent to 'follow' for them, and the resulting order is based on the
83 # equivalent to 'follow' for them, and the resulting order is based on the
84 # 'subset' parameter passed down to them:
84 # 'subset' parameter passed down to them:
85 #
85 #
86 # m = revset.match(...)
86 # m = revset.match(...)
87 # m(repo, subset, order=defineorder)
87 # m(repo, subset, order=defineorder)
88 # ^^^^^^
88 # ^^^^^^
89 # For most revsets, 'define' means using the order this subset provides
89 # For most revsets, 'define' means using the order this subset provides
90 #
90 #
91 # There are a few revsets that always redefine the order if 'define' is
91 # There are a few revsets that always redefine the order if 'define' is
92 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
92 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
93 anyorder = 'any' # don't care the order, could be even random-shuffled
93 anyorder = 'any' # don't care the order, could be even random-shuffled
94 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
94 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
95 followorder = 'follow' # MUST follow the current order
95 followorder = 'follow' # MUST follow the current order
96
96
97 # helpers
97 # helpers
98
98
99 def getset(repo, subset, x, order=defineorder):
99 def getset(repo, subset, x, order=defineorder):
100 if not x:
100 if not x:
101 raise error.ParseError(_("missing argument"))
101 raise error.ParseError(_("missing argument"))
102 return methods[x[0]](repo, subset, *x[1:], order=order)
102 return methods[x[0]](repo, subset, *x[1:], order=order)
103
103
104 def _getrevsource(repo, r):
104 def _getrevsource(repo, r):
105 extra = repo[r].extra()
105 extra = repo[r].extra()
106 for label in ('source', 'transplant_source', 'rebase_source'):
106 for label in ('source', 'transplant_source', 'rebase_source'):
107 if label in extra:
107 if label in extra:
108 try:
108 try:
109 return repo[extra[label]].rev()
109 return repo[extra[label]].rev()
110 except error.RepoLookupError:
110 except error.RepoLookupError:
111 pass
111 pass
112 return None
112 return None
113
113
114 def _sortedb(xs):
114 def _sortedb(xs):
115 return sorted(pycompat.rapply(pycompat.maybebytestr, xs))
115 return sorted(pycompat.rapply(pycompat.maybebytestr, xs))
116
116
117 # operator methods
117 # operator methods
118
118
119 def stringset(repo, subset, x, order):
119 def stringset(repo, subset, x, order):
120 if not x:
120 if not x:
121 raise error.ParseError(_("empty string is not a valid revision"))
121 raise error.ParseError(_("empty string is not a valid revision"))
122 x = scmutil.intrev(scmutil.revsymbol(repo, x))
122 x = scmutil.intrev(scmutil.revsymbol(repo, x))
123 if (x in subset
123 if (x in subset
124 or x == node.nullrev and isinstance(subset, fullreposet)):
124 or x == node.nullrev and isinstance(subset, fullreposet)):
125 return baseset([x])
125 return baseset([x])
126 return baseset()
126 return baseset()
127
127
128 def rangeset(repo, subset, x, y, order):
128 def rangeset(repo, subset, x, y, order):
129 m = getset(repo, fullreposet(repo), x)
129 m = getset(repo, fullreposet(repo), x)
130 n = getset(repo, fullreposet(repo), y)
130 n = getset(repo, fullreposet(repo), y)
131
131
132 if not m or not n:
132 if not m or not n:
133 return baseset()
133 return baseset()
134 return _makerangeset(repo, subset, m.first(), n.last(), order)
134 return _makerangeset(repo, subset, m.first(), n.last(), order)
135
135
136 def rangeall(repo, subset, x, order):
136 def rangeall(repo, subset, x, order):
137 assert x is None
137 assert x is None
138 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order)
138 return _makerangeset(repo, subset, 0, repo.changelog.tiprev(), order)
139
139
140 def rangepre(repo, subset, y, order):
140 def rangepre(repo, subset, y, order):
141 # ':y' can't be rewritten to '0:y' since '0' may be hidden
141 # ':y' can't be rewritten to '0:y' since '0' may be hidden
142 n = getset(repo, fullreposet(repo), y)
142 n = getset(repo, fullreposet(repo), y)
143 if not n:
143 if not n:
144 return baseset()
144 return baseset()
145 return _makerangeset(repo, subset, 0, n.last(), order)
145 return _makerangeset(repo, subset, 0, n.last(), order)
146
146
147 def rangepost(repo, subset, x, order):
147 def rangepost(repo, subset, x, order):
148 m = getset(repo, fullreposet(repo), x)
148 m = getset(repo, fullreposet(repo), x)
149 if not m:
149 if not m:
150 return baseset()
150 return baseset()
151 return _makerangeset(repo, subset, m.first(), repo.changelog.tiprev(),
151 return _makerangeset(repo, subset, m.first(), repo.changelog.tiprev(),
152 order)
152 order)
153
153
154 def _makerangeset(repo, subset, m, n, order):
154 def _makerangeset(repo, subset, m, n, order):
155 if m == n:
155 if m == n:
156 r = baseset([m])
156 r = baseset([m])
157 elif n == node.wdirrev:
157 elif n == node.wdirrev:
158 r = spanset(repo, m, len(repo)) + baseset([n])
158 r = spanset(repo, m, len(repo)) + baseset([n])
159 elif m == node.wdirrev:
159 elif m == node.wdirrev:
160 r = baseset([m]) + spanset(repo, repo.changelog.tiprev(), n - 1)
160 r = baseset([m]) + spanset(repo, repo.changelog.tiprev(), n - 1)
161 elif m < n:
161 elif m < n:
162 r = spanset(repo, m, n + 1)
162 r = spanset(repo, m, n + 1)
163 else:
163 else:
164 r = spanset(repo, m, n - 1)
164 r = spanset(repo, m, n - 1)
165
165
166 if order == defineorder:
166 if order == defineorder:
167 return r & subset
167 return r & subset
168 else:
168 else:
169 # carrying the sorting over when possible would be more efficient
169 # carrying the sorting over when possible would be more efficient
170 return subset & r
170 return subset & r
171
171
172 def dagrange(repo, subset, x, y, order):
172 def dagrange(repo, subset, x, y, order):
173 r = fullreposet(repo)
173 r = fullreposet(repo)
174 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
174 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
175 includepath=True)
175 includepath=True)
176 return subset & xs
176 return subset & xs
177
177
178 def andset(repo, subset, x, y, order):
178 def andset(repo, subset, x, y, order):
179 if order == anyorder:
179 if order == anyorder:
180 yorder = anyorder
180 yorder = anyorder
181 else:
181 else:
182 yorder = followorder
182 yorder = followorder
183 return getset(repo, getset(repo, subset, x, order), y, yorder)
183 return getset(repo, getset(repo, subset, x, order), y, yorder)
184
184
185 def andsmallyset(repo, subset, x, y, order):
185 def andsmallyset(repo, subset, x, y, order):
186 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
186 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
187 if order == anyorder:
187 if order == anyorder:
188 yorder = anyorder
188 yorder = anyorder
189 else:
189 else:
190 yorder = followorder
190 yorder = followorder
191 return getset(repo, getset(repo, subset, y, yorder), x, order)
191 return getset(repo, getset(repo, subset, y, yorder), x, order)
192
192
193 def differenceset(repo, subset, x, y, order):
193 def differenceset(repo, subset, x, y, order):
194 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
194 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
195
195
196 def _orsetlist(repo, subset, xs, order):
196 def _orsetlist(repo, subset, xs, order):
197 assert xs
197 assert xs
198 if len(xs) == 1:
198 if len(xs) == 1:
199 return getset(repo, subset, xs[0], order)
199 return getset(repo, subset, xs[0], order)
200 p = len(xs) // 2
200 p = len(xs) // 2
201 a = _orsetlist(repo, subset, xs[:p], order)
201 a = _orsetlist(repo, subset, xs[:p], order)
202 b = _orsetlist(repo, subset, xs[p:], order)
202 b = _orsetlist(repo, subset, xs[p:], order)
203 return a + b
203 return a + b
204
204
205 def orset(repo, subset, x, order):
205 def orset(repo, subset, x, order):
206 xs = getlist(x)
206 xs = getlist(x)
207 if not xs:
207 if not xs:
208 return baseset()
208 return baseset()
209 if order == followorder:
209 if order == followorder:
210 # slow path to take the subset order
210 # slow path to take the subset order
211 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
211 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
212 else:
212 else:
213 return _orsetlist(repo, subset, xs, order)
213 return _orsetlist(repo, subset, xs, order)
214
214
215 def notset(repo, subset, x, order):
215 def notset(repo, subset, x, order):
216 return subset - getset(repo, subset, x, anyorder)
216 return subset - getset(repo, subset, x, anyorder)
217
217
218 def relationset(repo, subset, x, y, order):
218 def relationset(repo, subset, x, y, order):
219 raise error.ParseError(_("can't use a relation in this context"))
219 raise error.ParseError(_("can't use a relation in this context"))
220
220
221 def relsubscriptset(repo, subset, x, y, z, order):
221 def relsubscriptset(repo, subset, x, y, z, order):
222 # this is pretty basic implementation of 'x#y[z]' operator, still
222 # this is pretty basic implementation of 'x#y[z]' operator, still
223 # experimental so undocumented. see the wiki for further ideas.
223 # experimental so undocumented. see the wiki for further ideas.
224 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
224 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
225 rel = getsymbol(y)
225 rel = getsymbol(y)
226 n = getinteger(z, _("relation subscript must be an integer"))
226 n = getinteger(z, _("relation subscript must be an integer"))
227
227
228 # TODO: perhaps this should be a table of relation functions
228 # TODO: perhaps this should be a table of relation functions
229 if rel in ('g', 'generations'):
229 if rel in ('g', 'generations'):
230 # TODO: support range, rewrite tests, and drop startdepth argument
230 # TODO: support range, rewrite tests, and drop startdepth argument
231 # from ancestors() and descendants() predicates
231 # from ancestors() and descendants() predicates
232 if n <= 0:
232 if n <= 0:
233 n = -n
233 n = -n
234 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
234 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
235 else:
235 else:
236 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
236 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
237
237
238 raise error.UnknownIdentifier(rel, ['generations'])
238 raise error.UnknownIdentifier(rel, ['generations'])
239
239
240 def subscriptset(repo, subset, x, y, order):
240 def subscriptset(repo, subset, x, y, order):
241 raise error.ParseError(_("can't use a subscript in this context"))
241 raise error.ParseError(_("can't use a subscript in this context"))
242
242
243 def listset(repo, subset, *xs, **opts):
243 def listset(repo, subset, *xs, **opts):
244 raise error.ParseError(_("can't use a list in this context"),
244 raise error.ParseError(_("can't use a list in this context"),
245 hint=_('see hg help "revsets.x or y"'))
245 hint=_('see \'hg help "revsets.x or y"\''))
246
246
247 def keyvaluepair(repo, subset, k, v, order):
247 def keyvaluepair(repo, subset, k, v, order):
248 raise error.ParseError(_("can't use a key-value pair in this context"))
248 raise error.ParseError(_("can't use a key-value pair in this context"))
249
249
250 def func(repo, subset, a, b, order):
250 def func(repo, subset, a, b, order):
251 f = getsymbol(a)
251 f = getsymbol(a)
252 if f in symbols:
252 if f in symbols:
253 func = symbols[f]
253 func = symbols[f]
254 if getattr(func, '_takeorder', False):
254 if getattr(func, '_takeorder', False):
255 return func(repo, subset, b, order)
255 return func(repo, subset, b, order)
256 return func(repo, subset, b)
256 return func(repo, subset, b)
257
257
258 keep = lambda fn: getattr(fn, '__doc__', None) is not None
258 keep = lambda fn: getattr(fn, '__doc__', None) is not None
259
259
260 syms = [s for (s, fn) in symbols.items() if keep(fn)]
260 syms = [s for (s, fn) in symbols.items() if keep(fn)]
261 raise error.UnknownIdentifier(f, syms)
261 raise error.UnknownIdentifier(f, syms)
262
262
263 # functions
263 # functions
264
264
265 # symbols are callables like:
265 # symbols are callables like:
266 # fn(repo, subset, x)
266 # fn(repo, subset, x)
267 # with:
267 # with:
268 # repo - current repository instance
268 # repo - current repository instance
269 # subset - of revisions to be examined
269 # subset - of revisions to be examined
270 # x - argument in tree form
270 # x - argument in tree form
271 symbols = revsetlang.symbols
271 symbols = revsetlang.symbols
272
272
273 # symbols which can't be used for a DoS attack for any given input
273 # symbols which can't be used for a DoS attack for any given input
274 # (e.g. those which accept regexes as plain strings shouldn't be included)
274 # (e.g. those which accept regexes as plain strings shouldn't be included)
275 # functions that just return a lot of changesets (like all) don't count here
275 # functions that just return a lot of changesets (like all) don't count here
276 safesymbols = set()
276 safesymbols = set()
277
277
278 predicate = registrar.revsetpredicate()
278 predicate = registrar.revsetpredicate()
279
279
280 @predicate('_destupdate')
280 @predicate('_destupdate')
281 def _destupdate(repo, subset, x):
281 def _destupdate(repo, subset, x):
282 # experimental revset for update destination
282 # experimental revset for update destination
283 args = getargsdict(x, 'limit', 'clean')
283 args = getargsdict(x, 'limit', 'clean')
284 return subset & baseset([destutil.destupdate(repo,
284 return subset & baseset([destutil.destupdate(repo,
285 **pycompat.strkwargs(args))[0]])
285 **pycompat.strkwargs(args))[0]])
286
286
287 @predicate('_destmerge')
287 @predicate('_destmerge')
288 def _destmerge(repo, subset, x):
288 def _destmerge(repo, subset, x):
289 # experimental revset for merge destination
289 # experimental revset for merge destination
290 sourceset = None
290 sourceset = None
291 if x is not None:
291 if x is not None:
292 sourceset = getset(repo, fullreposet(repo), x)
292 sourceset = getset(repo, fullreposet(repo), x)
293 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
293 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
294
294
295 @predicate('adds(pattern)', safe=True, weight=30)
295 @predicate('adds(pattern)', safe=True, weight=30)
296 def adds(repo, subset, x):
296 def adds(repo, subset, x):
297 """Changesets that add a file matching pattern.
297 """Changesets that add a file matching pattern.
298
298
299 The pattern without explicit kind like ``glob:`` is expected to be
299 The pattern without explicit kind like ``glob:`` is expected to be
300 relative to the current directory and match against a file or a
300 relative to the current directory and match against a file or a
301 directory.
301 directory.
302 """
302 """
303 # i18n: "adds" is a keyword
303 # i18n: "adds" is a keyword
304 pat = getstring(x, _("adds requires a pattern"))
304 pat = getstring(x, _("adds requires a pattern"))
305 return checkstatus(repo, subset, pat, 1)
305 return checkstatus(repo, subset, pat, 1)
306
306
307 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
307 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
308 def ancestor(repo, subset, x):
308 def ancestor(repo, subset, x):
309 """A greatest common ancestor of the changesets.
309 """A greatest common ancestor of the changesets.
310
310
311 Accepts 0 or more changesets.
311 Accepts 0 or more changesets.
312 Will return empty list when passed no args.
312 Will return empty list when passed no args.
313 Greatest common ancestor of a single changeset is that changeset.
313 Greatest common ancestor of a single changeset is that changeset.
314 """
314 """
315 reviter = iter(orset(repo, fullreposet(repo), x, order=anyorder))
315 reviter = iter(orset(repo, fullreposet(repo), x, order=anyorder))
316 try:
316 try:
317 anc = repo[next(reviter)]
317 anc = repo[next(reviter)]
318 except StopIteration:
318 except StopIteration:
319 return baseset()
319 return baseset()
320 for r in reviter:
320 for r in reviter:
321 anc = anc.ancestor(repo[r])
321 anc = anc.ancestor(repo[r])
322
322
323 r = scmutil.intrev(anc)
323 r = scmutil.intrev(anc)
324 if r in subset:
324 if r in subset:
325 return baseset([r])
325 return baseset([r])
326 return baseset()
326 return baseset()
327
327
328 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
328 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
329 stopdepth=None):
329 stopdepth=None):
330 heads = getset(repo, fullreposet(repo), x)
330 heads = getset(repo, fullreposet(repo), x)
331 if not heads:
331 if not heads:
332 return baseset()
332 return baseset()
333 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
333 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
334 return subset & s
334 return subset & s
335
335
336 @predicate('ancestors(set[, depth])', safe=True)
336 @predicate('ancestors(set[, depth])', safe=True)
337 def ancestors(repo, subset, x):
337 def ancestors(repo, subset, x):
338 """Changesets that are ancestors of changesets in set, including the
338 """Changesets that are ancestors of changesets in set, including the
339 given changesets themselves.
339 given changesets themselves.
340
340
341 If depth is specified, the result only includes changesets up to
341 If depth is specified, the result only includes changesets up to
342 the specified generation.
342 the specified generation.
343 """
343 """
344 # startdepth is for internal use only until we can decide the UI
344 # startdepth is for internal use only until we can decide the UI
345 args = getargsdict(x, 'ancestors', 'set depth startdepth')
345 args = getargsdict(x, 'ancestors', 'set depth startdepth')
346 if 'set' not in args:
346 if 'set' not in args:
347 # i18n: "ancestors" is a keyword
347 # i18n: "ancestors" is a keyword
348 raise error.ParseError(_('ancestors takes at least 1 argument'))
348 raise error.ParseError(_('ancestors takes at least 1 argument'))
349 startdepth = stopdepth = None
349 startdepth = stopdepth = None
350 if 'startdepth' in args:
350 if 'startdepth' in args:
351 n = getinteger(args['startdepth'],
351 n = getinteger(args['startdepth'],
352 "ancestors expects an integer startdepth")
352 "ancestors expects an integer startdepth")
353 if n < 0:
353 if n < 0:
354 raise error.ParseError("negative startdepth")
354 raise error.ParseError("negative startdepth")
355 startdepth = n
355 startdepth = n
356 if 'depth' in args:
356 if 'depth' in args:
357 # i18n: "ancestors" is a keyword
357 # i18n: "ancestors" is a keyword
358 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
358 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
359 if n < 0:
359 if n < 0:
360 raise error.ParseError(_("negative depth"))
360 raise error.ParseError(_("negative depth"))
361 stopdepth = n + 1
361 stopdepth = n + 1
362 return _ancestors(repo, subset, args['set'],
362 return _ancestors(repo, subset, args['set'],
363 startdepth=startdepth, stopdepth=stopdepth)
363 startdepth=startdepth, stopdepth=stopdepth)
364
364
365 @predicate('_firstancestors', safe=True)
365 @predicate('_firstancestors', safe=True)
366 def _firstancestors(repo, subset, x):
366 def _firstancestors(repo, subset, x):
367 # ``_firstancestors(set)``
367 # ``_firstancestors(set)``
368 # Like ``ancestors(set)`` but follows only the first parents.
368 # Like ``ancestors(set)`` but follows only the first parents.
369 return _ancestors(repo, subset, x, followfirst=True)
369 return _ancestors(repo, subset, x, followfirst=True)
370
370
371 def _childrenspec(repo, subset, x, n, order):
371 def _childrenspec(repo, subset, x, n, order):
372 """Changesets that are the Nth child of a changeset
372 """Changesets that are the Nth child of a changeset
373 in set.
373 in set.
374 """
374 """
375 cs = set()
375 cs = set()
376 for r in getset(repo, fullreposet(repo), x):
376 for r in getset(repo, fullreposet(repo), x):
377 for i in range(n):
377 for i in range(n):
378 c = repo[r].children()
378 c = repo[r].children()
379 if len(c) == 0:
379 if len(c) == 0:
380 break
380 break
381 if len(c) > 1:
381 if len(c) > 1:
382 raise error.RepoLookupError(
382 raise error.RepoLookupError(
383 _("revision in set has more than one child"))
383 _("revision in set has more than one child"))
384 r = c[0].rev()
384 r = c[0].rev()
385 else:
385 else:
386 cs.add(r)
386 cs.add(r)
387 return subset & cs
387 return subset & cs
388
388
389 def ancestorspec(repo, subset, x, n, order):
389 def ancestorspec(repo, subset, x, n, order):
390 """``set~n``
390 """``set~n``
391 Changesets that are the Nth ancestor (first parents only) of a changeset
391 Changesets that are the Nth ancestor (first parents only) of a changeset
392 in set.
392 in set.
393 """
393 """
394 n = getinteger(n, _("~ expects a number"))
394 n = getinteger(n, _("~ expects a number"))
395 if n < 0:
395 if n < 0:
396 # children lookup
396 # children lookup
397 return _childrenspec(repo, subset, x, -n, order)
397 return _childrenspec(repo, subset, x, -n, order)
398 ps = set()
398 ps = set()
399 cl = repo.changelog
399 cl = repo.changelog
400 for r in getset(repo, fullreposet(repo), x):
400 for r in getset(repo, fullreposet(repo), x):
401 for i in range(n):
401 for i in range(n):
402 try:
402 try:
403 r = cl.parentrevs(r)[0]
403 r = cl.parentrevs(r)[0]
404 except error.WdirUnsupported:
404 except error.WdirUnsupported:
405 r = repo[r].parents()[0].rev()
405 r = repo[r].parents()[0].rev()
406 ps.add(r)
406 ps.add(r)
407 return subset & ps
407 return subset & ps
408
408
409 @predicate('author(string)', safe=True, weight=10)
409 @predicate('author(string)', safe=True, weight=10)
410 def author(repo, subset, x):
410 def author(repo, subset, x):
411 """Alias for ``user(string)``.
411 """Alias for ``user(string)``.
412 """
412 """
413 # i18n: "author" is a keyword
413 # i18n: "author" is a keyword
414 n = getstring(x, _("author requires a string"))
414 n = getstring(x, _("author requires a string"))
415 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
415 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
416 return subset.filter(lambda x: matcher(repo[x].user()),
416 return subset.filter(lambda x: matcher(repo[x].user()),
417 condrepr=('<user %r>', n))
417 condrepr=('<user %r>', n))
418
418
419 @predicate('bisect(string)', safe=True)
419 @predicate('bisect(string)', safe=True)
420 def bisect(repo, subset, x):
420 def bisect(repo, subset, x):
421 """Changesets marked in the specified bisect status:
421 """Changesets marked in the specified bisect status:
422
422
423 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
423 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
424 - ``goods``, ``bads`` : csets topologically good/bad
424 - ``goods``, ``bads`` : csets topologically good/bad
425 - ``range`` : csets taking part in the bisection
425 - ``range`` : csets taking part in the bisection
426 - ``pruned`` : csets that are goods, bads or skipped
426 - ``pruned`` : csets that are goods, bads or skipped
427 - ``untested`` : csets whose fate is yet unknown
427 - ``untested`` : csets whose fate is yet unknown
428 - ``ignored`` : csets ignored due to DAG topology
428 - ``ignored`` : csets ignored due to DAG topology
429 - ``current`` : the cset currently being bisected
429 - ``current`` : the cset currently being bisected
430 """
430 """
431 # i18n: "bisect" is a keyword
431 # i18n: "bisect" is a keyword
432 status = getstring(x, _("bisect requires a string")).lower()
432 status = getstring(x, _("bisect requires a string")).lower()
433 state = set(hbisect.get(repo, status))
433 state = set(hbisect.get(repo, status))
434 return subset & state
434 return subset & state
435
435
436 # Backward-compatibility
436 # Backward-compatibility
437 # - no help entry so that we do not advertise it any more
437 # - no help entry so that we do not advertise it any more
438 @predicate('bisected', safe=True)
438 @predicate('bisected', safe=True)
439 def bisected(repo, subset, x):
439 def bisected(repo, subset, x):
440 return bisect(repo, subset, x)
440 return bisect(repo, subset, x)
441
441
442 @predicate('bookmark([name])', safe=True)
442 @predicate('bookmark([name])', safe=True)
443 def bookmark(repo, subset, x):
443 def bookmark(repo, subset, x):
444 """The named bookmark or all bookmarks.
444 """The named bookmark or all bookmarks.
445
445
446 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
446 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
447 """
447 """
448 # i18n: "bookmark" is a keyword
448 # i18n: "bookmark" is a keyword
449 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
449 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
450 if args:
450 if args:
451 bm = getstring(args[0],
451 bm = getstring(args[0],
452 # i18n: "bookmark" is a keyword
452 # i18n: "bookmark" is a keyword
453 _('the argument to bookmark must be a string'))
453 _('the argument to bookmark must be a string'))
454 kind, pattern, matcher = stringutil.stringmatcher(bm)
454 kind, pattern, matcher = stringutil.stringmatcher(bm)
455 bms = set()
455 bms = set()
456 if kind == 'literal':
456 if kind == 'literal':
457 bmrev = repo._bookmarks.get(pattern, None)
457 bmrev = repo._bookmarks.get(pattern, None)
458 if not bmrev:
458 if not bmrev:
459 raise error.RepoLookupError(_("bookmark '%s' does not exist")
459 raise error.RepoLookupError(_("bookmark '%s' does not exist")
460 % pattern)
460 % pattern)
461 bms.add(repo[bmrev].rev())
461 bms.add(repo[bmrev].rev())
462 else:
462 else:
463 matchrevs = set()
463 matchrevs = set()
464 for name, bmrev in repo._bookmarks.iteritems():
464 for name, bmrev in repo._bookmarks.iteritems():
465 if matcher(name):
465 if matcher(name):
466 matchrevs.add(bmrev)
466 matchrevs.add(bmrev)
467 if not matchrevs:
467 if not matchrevs:
468 raise error.RepoLookupError(_("no bookmarks exist"
468 raise error.RepoLookupError(_("no bookmarks exist"
469 " that match '%s'") % pattern)
469 " that match '%s'") % pattern)
470 for bmrev in matchrevs:
470 for bmrev in matchrevs:
471 bms.add(repo[bmrev].rev())
471 bms.add(repo[bmrev].rev())
472 else:
472 else:
473 bms = {repo[r].rev() for r in repo._bookmarks.values()}
473 bms = {repo[r].rev() for r in repo._bookmarks.values()}
474 bms -= {node.nullrev}
474 bms -= {node.nullrev}
475 return subset & bms
475 return subset & bms
476
476
477 @predicate('branch(string or set)', safe=True, weight=10)
477 @predicate('branch(string or set)', safe=True, weight=10)
478 def branch(repo, subset, x):
478 def branch(repo, subset, x):
479 """
479 """
480 All changesets belonging to the given branch or the branches of the given
480 All changesets belonging to the given branch or the branches of the given
481 changesets.
481 changesets.
482
482
483 Pattern matching is supported for `string`. See
483 Pattern matching is supported for `string`. See
484 :hg:`help revisions.patterns`.
484 :hg:`help revisions.patterns`.
485 """
485 """
486 getbi = repo.revbranchcache().branchinfo
486 getbi = repo.revbranchcache().branchinfo
487 def getbranch(r):
487 def getbranch(r):
488 try:
488 try:
489 return getbi(r)[0]
489 return getbi(r)[0]
490 except error.WdirUnsupported:
490 except error.WdirUnsupported:
491 return repo[r].branch()
491 return repo[r].branch()
492
492
493 try:
493 try:
494 b = getstring(x, '')
494 b = getstring(x, '')
495 except error.ParseError:
495 except error.ParseError:
496 # not a string, but another revspec, e.g. tip()
496 # not a string, but another revspec, e.g. tip()
497 pass
497 pass
498 else:
498 else:
499 kind, pattern, matcher = stringutil.stringmatcher(b)
499 kind, pattern, matcher = stringutil.stringmatcher(b)
500 if kind == 'literal':
500 if kind == 'literal':
501 # note: falls through to the revspec case if no branch with
501 # note: falls through to the revspec case if no branch with
502 # this name exists and pattern kind is not specified explicitly
502 # this name exists and pattern kind is not specified explicitly
503 if pattern in repo.branchmap():
503 if pattern in repo.branchmap():
504 return subset.filter(lambda r: matcher(getbranch(r)),
504 return subset.filter(lambda r: matcher(getbranch(r)),
505 condrepr=('<branch %r>', b))
505 condrepr=('<branch %r>', b))
506 if b.startswith('literal:'):
506 if b.startswith('literal:'):
507 raise error.RepoLookupError(_("branch '%s' does not exist")
507 raise error.RepoLookupError(_("branch '%s' does not exist")
508 % pattern)
508 % pattern)
509 else:
509 else:
510 return subset.filter(lambda r: matcher(getbranch(r)),
510 return subset.filter(lambda r: matcher(getbranch(r)),
511 condrepr=('<branch %r>', b))
511 condrepr=('<branch %r>', b))
512
512
513 s = getset(repo, fullreposet(repo), x)
513 s = getset(repo, fullreposet(repo), x)
514 b = set()
514 b = set()
515 for r in s:
515 for r in s:
516 b.add(getbranch(r))
516 b.add(getbranch(r))
517 c = s.__contains__
517 c = s.__contains__
518 return subset.filter(lambda r: c(r) or getbranch(r) in b,
518 return subset.filter(lambda r: c(r) or getbranch(r) in b,
519 condrepr=lambda: '<branch %r>' % _sortedb(b))
519 condrepr=lambda: '<branch %r>' % _sortedb(b))
520
520
521 @predicate('phasedivergent()', safe=True)
521 @predicate('phasedivergent()', safe=True)
522 def phasedivergent(repo, subset, x):
522 def phasedivergent(repo, subset, x):
523 """Mutable changesets marked as successors of public changesets.
523 """Mutable changesets marked as successors of public changesets.
524
524
525 Only non-public and non-obsolete changesets can be `phasedivergent`.
525 Only non-public and non-obsolete changesets can be `phasedivergent`.
526 (EXPERIMENTAL)
526 (EXPERIMENTAL)
527 """
527 """
528 # i18n: "phasedivergent" is a keyword
528 # i18n: "phasedivergent" is a keyword
529 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
529 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
530 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
530 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
531 return subset & phasedivergent
531 return subset & phasedivergent
532
532
533 @predicate('bundle()', safe=True)
533 @predicate('bundle()', safe=True)
534 def bundle(repo, subset, x):
534 def bundle(repo, subset, x):
535 """Changesets in the bundle.
535 """Changesets in the bundle.
536
536
537 Bundle must be specified by the -R option."""
537 Bundle must be specified by the -R option."""
538
538
539 try:
539 try:
540 bundlerevs = repo.changelog.bundlerevs
540 bundlerevs = repo.changelog.bundlerevs
541 except AttributeError:
541 except AttributeError:
542 raise error.Abort(_("no bundle provided - specify with -R"))
542 raise error.Abort(_("no bundle provided - specify with -R"))
543 return subset & bundlerevs
543 return subset & bundlerevs
544
544
545 def checkstatus(repo, subset, pat, field):
545 def checkstatus(repo, subset, pat, field):
546 hasset = matchmod.patkind(pat) == 'set'
546 hasset = matchmod.patkind(pat) == 'set'
547
547
548 mcache = [None]
548 mcache = [None]
549 def matches(x):
549 def matches(x):
550 c = repo[x]
550 c = repo[x]
551 if not mcache[0] or hasset:
551 if not mcache[0] or hasset:
552 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
552 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
553 m = mcache[0]
553 m = mcache[0]
554 fname = None
554 fname = None
555 if not m.anypats() and len(m.files()) == 1:
555 if not m.anypats() and len(m.files()) == 1:
556 fname = m.files()[0]
556 fname = m.files()[0]
557 if fname is not None:
557 if fname is not None:
558 if fname not in c.files():
558 if fname not in c.files():
559 return False
559 return False
560 else:
560 else:
561 for f in c.files():
561 for f in c.files():
562 if m(f):
562 if m(f):
563 break
563 break
564 else:
564 else:
565 return False
565 return False
566 files = repo.status(c.p1().node(), c.node())[field]
566 files = repo.status(c.p1().node(), c.node())[field]
567 if fname is not None:
567 if fname is not None:
568 if fname in files:
568 if fname in files:
569 return True
569 return True
570 else:
570 else:
571 for f in files:
571 for f in files:
572 if m(f):
572 if m(f):
573 return True
573 return True
574
574
575 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
575 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
576
576
577 def _children(repo, subset, parentset):
577 def _children(repo, subset, parentset):
578 if not parentset:
578 if not parentset:
579 return baseset()
579 return baseset()
580 cs = set()
580 cs = set()
581 pr = repo.changelog.parentrevs
581 pr = repo.changelog.parentrevs
582 minrev = parentset.min()
582 minrev = parentset.min()
583 nullrev = node.nullrev
583 nullrev = node.nullrev
584 for r in subset:
584 for r in subset:
585 if r <= minrev:
585 if r <= minrev:
586 continue
586 continue
587 p1, p2 = pr(r)
587 p1, p2 = pr(r)
588 if p1 in parentset:
588 if p1 in parentset:
589 cs.add(r)
589 cs.add(r)
590 if p2 != nullrev and p2 in parentset:
590 if p2 != nullrev and p2 in parentset:
591 cs.add(r)
591 cs.add(r)
592 return baseset(cs)
592 return baseset(cs)
593
593
594 @predicate('children(set)', safe=True)
594 @predicate('children(set)', safe=True)
595 def children(repo, subset, x):
595 def children(repo, subset, x):
596 """Child changesets of changesets in set.
596 """Child changesets of changesets in set.
597 """
597 """
598 s = getset(repo, fullreposet(repo), x)
598 s = getset(repo, fullreposet(repo), x)
599 cs = _children(repo, subset, s)
599 cs = _children(repo, subset, s)
600 return subset & cs
600 return subset & cs
601
601
602 @predicate('closed()', safe=True, weight=10)
602 @predicate('closed()', safe=True, weight=10)
603 def closed(repo, subset, x):
603 def closed(repo, subset, x):
604 """Changeset is closed.
604 """Changeset is closed.
605 """
605 """
606 # i18n: "closed" is a keyword
606 # i18n: "closed" is a keyword
607 getargs(x, 0, 0, _("closed takes no arguments"))
607 getargs(x, 0, 0, _("closed takes no arguments"))
608 return subset.filter(lambda r: repo[r].closesbranch(),
608 return subset.filter(lambda r: repo[r].closesbranch(),
609 condrepr='<branch closed>')
609 condrepr='<branch closed>')
610
610
611 # for internal use
611 # for internal use
612 @predicate('_commonancestorheads(set)', safe=True)
612 @predicate('_commonancestorheads(set)', safe=True)
613 def _commonancestorheads(repo, subset, x):
613 def _commonancestorheads(repo, subset, x):
614 # This is an internal method is for quickly calculating "heads(::x and
614 # This is an internal method is for quickly calculating "heads(::x and
615 # ::y)"
615 # ::y)"
616
616
617 # These greatest common ancestors are the same ones that the consesus bid
617 # These greatest common ancestors are the same ones that the consesus bid
618 # merge will find.
618 # merge will find.
619 h = heads(repo, fullreposet(repo), x, anyorder)
619 h = heads(repo, fullreposet(repo), x, anyorder)
620
620
621 ancs = repo.changelog._commonancestorsheads(*list(h))
621 ancs = repo.changelog._commonancestorsheads(*list(h))
622 return subset & baseset(ancs)
622 return subset & baseset(ancs)
623
623
624 @predicate('commonancestors(set)', safe=True)
624 @predicate('commonancestors(set)', safe=True)
625 def commonancestors(repo, subset, x):
625 def commonancestors(repo, subset, x):
626 """Returns all common ancestors of the set.
626 """Returns all common ancestors of the set.
627
627
628 This method is for calculating "::x and ::y" (i.e. all the ancestors that
628 This method is for calculating "::x and ::y" (i.e. all the ancestors that
629 are common to both x and y) in an easy and optimized way. We can't quite
629 are common to both x and y) in an easy and optimized way. We can't quite
630 use "::head()" because that revset returns "::x + ::y + ..." for each head
630 use "::head()" because that revset returns "::x + ::y + ..." for each head
631 in the repo (whereas we want "::x *and* ::y").
631 in the repo (whereas we want "::x *and* ::y").
632
632
633 """
633 """
634 # only wants the heads of the set passed in
634 # only wants the heads of the set passed in
635 h = heads(repo, fullreposet(repo), x, anyorder)
635 h = heads(repo, fullreposet(repo), x, anyorder)
636 if not h:
636 if not h:
637 return baseset()
637 return baseset()
638 for r in h:
638 for r in h:
639 subset &= dagop.revancestors(repo, baseset([r]))
639 subset &= dagop.revancestors(repo, baseset([r]))
640
640
641 return subset
641 return subset
642
642
643 @predicate('contains(pattern)', weight=100)
643 @predicate('contains(pattern)', weight=100)
644 def contains(repo, subset, x):
644 def contains(repo, subset, x):
645 """The revision's manifest contains a file matching pattern (but might not
645 """The revision's manifest contains a file matching pattern (but might not
646 modify it). See :hg:`help patterns` for information about file patterns.
646 modify it). See :hg:`help patterns` for information about file patterns.
647
647
648 The pattern without explicit kind like ``glob:`` is expected to be
648 The pattern without explicit kind like ``glob:`` is expected to be
649 relative to the current directory and match against a file exactly
649 relative to the current directory and match against a file exactly
650 for efficiency.
650 for efficiency.
651 """
651 """
652 # i18n: "contains" is a keyword
652 # i18n: "contains" is a keyword
653 pat = getstring(x, _("contains requires a pattern"))
653 pat = getstring(x, _("contains requires a pattern"))
654
654
655 def matches(x):
655 def matches(x):
656 if not matchmod.patkind(pat):
656 if not matchmod.patkind(pat):
657 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
657 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
658 if pats in repo[x]:
658 if pats in repo[x]:
659 return True
659 return True
660 else:
660 else:
661 c = repo[x]
661 c = repo[x]
662 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
662 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
663 for f in c.manifest():
663 for f in c.manifest():
664 if m(f):
664 if m(f):
665 return True
665 return True
666 return False
666 return False
667
667
668 return subset.filter(matches, condrepr=('<contains %r>', pat))
668 return subset.filter(matches, condrepr=('<contains %r>', pat))
669
669
670 @predicate('converted([id])', safe=True)
670 @predicate('converted([id])', safe=True)
671 def converted(repo, subset, x):
671 def converted(repo, subset, x):
672 """Changesets converted from the given identifier in the old repository if
672 """Changesets converted from the given identifier in the old repository if
673 present, or all converted changesets if no identifier is specified.
673 present, or all converted changesets if no identifier is specified.
674 """
674 """
675
675
676 # There is exactly no chance of resolving the revision, so do a simple
676 # There is exactly no chance of resolving the revision, so do a simple
677 # string compare and hope for the best
677 # string compare and hope for the best
678
678
679 rev = None
679 rev = None
680 # i18n: "converted" is a keyword
680 # i18n: "converted" is a keyword
681 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
681 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
682 if l:
682 if l:
683 # i18n: "converted" is a keyword
683 # i18n: "converted" is a keyword
684 rev = getstring(l[0], _('converted requires a revision'))
684 rev = getstring(l[0], _('converted requires a revision'))
685
685
686 def _matchvalue(r):
686 def _matchvalue(r):
687 source = repo[r].extra().get('convert_revision', None)
687 source = repo[r].extra().get('convert_revision', None)
688 return source is not None and (rev is None or source.startswith(rev))
688 return source is not None and (rev is None or source.startswith(rev))
689
689
690 return subset.filter(lambda r: _matchvalue(r),
690 return subset.filter(lambda r: _matchvalue(r),
691 condrepr=('<converted %r>', rev))
691 condrepr=('<converted %r>', rev))
692
692
693 @predicate('date(interval)', safe=True, weight=10)
693 @predicate('date(interval)', safe=True, weight=10)
694 def date(repo, subset, x):
694 def date(repo, subset, x):
695 """Changesets within the interval, see :hg:`help dates`.
695 """Changesets within the interval, see :hg:`help dates`.
696 """
696 """
697 # i18n: "date" is a keyword
697 # i18n: "date" is a keyword
698 ds = getstring(x, _("date requires a string"))
698 ds = getstring(x, _("date requires a string"))
699 dm = dateutil.matchdate(ds)
699 dm = dateutil.matchdate(ds)
700 return subset.filter(lambda x: dm(repo[x].date()[0]),
700 return subset.filter(lambda x: dm(repo[x].date()[0]),
701 condrepr=('<date %r>', ds))
701 condrepr=('<date %r>', ds))
702
702
703 @predicate('desc(string)', safe=True, weight=10)
703 @predicate('desc(string)', safe=True, weight=10)
704 def desc(repo, subset, x):
704 def desc(repo, subset, x):
705 """Search commit message for string. The match is case-insensitive.
705 """Search commit message for string. The match is case-insensitive.
706
706
707 Pattern matching is supported for `string`. See
707 Pattern matching is supported for `string`. See
708 :hg:`help revisions.patterns`.
708 :hg:`help revisions.patterns`.
709 """
709 """
710 # i18n: "desc" is a keyword
710 # i18n: "desc" is a keyword
711 ds = getstring(x, _("desc requires a string"))
711 ds = getstring(x, _("desc requires a string"))
712
712
713 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
713 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
714
714
715 return subset.filter(lambda r: matcher(repo[r].description()),
715 return subset.filter(lambda r: matcher(repo[r].description()),
716 condrepr=('<desc %r>', ds))
716 condrepr=('<desc %r>', ds))
717
717
718 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
718 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
719 stopdepth=None):
719 stopdepth=None):
720 roots = getset(repo, fullreposet(repo), x)
720 roots = getset(repo, fullreposet(repo), x)
721 if not roots:
721 if not roots:
722 return baseset()
722 return baseset()
723 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
723 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
724 return subset & s
724 return subset & s
725
725
726 @predicate('descendants(set[, depth])', safe=True)
726 @predicate('descendants(set[, depth])', safe=True)
727 def descendants(repo, subset, x):
727 def descendants(repo, subset, x):
728 """Changesets which are descendants of changesets in set, including the
728 """Changesets which are descendants of changesets in set, including the
729 given changesets themselves.
729 given changesets themselves.
730
730
731 If depth is specified, the result only includes changesets up to
731 If depth is specified, the result only includes changesets up to
732 the specified generation.
732 the specified generation.
733 """
733 """
734 # startdepth is for internal use only until we can decide the UI
734 # startdepth is for internal use only until we can decide the UI
735 args = getargsdict(x, 'descendants', 'set depth startdepth')
735 args = getargsdict(x, 'descendants', 'set depth startdepth')
736 if 'set' not in args:
736 if 'set' not in args:
737 # i18n: "descendants" is a keyword
737 # i18n: "descendants" is a keyword
738 raise error.ParseError(_('descendants takes at least 1 argument'))
738 raise error.ParseError(_('descendants takes at least 1 argument'))
739 startdepth = stopdepth = None
739 startdepth = stopdepth = None
740 if 'startdepth' in args:
740 if 'startdepth' in args:
741 n = getinteger(args['startdepth'],
741 n = getinteger(args['startdepth'],
742 "descendants expects an integer startdepth")
742 "descendants expects an integer startdepth")
743 if n < 0:
743 if n < 0:
744 raise error.ParseError("negative startdepth")
744 raise error.ParseError("negative startdepth")
745 startdepth = n
745 startdepth = n
746 if 'depth' in args:
746 if 'depth' in args:
747 # i18n: "descendants" is a keyword
747 # i18n: "descendants" is a keyword
748 n = getinteger(args['depth'], _("descendants expects an integer depth"))
748 n = getinteger(args['depth'], _("descendants expects an integer depth"))
749 if n < 0:
749 if n < 0:
750 raise error.ParseError(_("negative depth"))
750 raise error.ParseError(_("negative depth"))
751 stopdepth = n + 1
751 stopdepth = n + 1
752 return _descendants(repo, subset, args['set'],
752 return _descendants(repo, subset, args['set'],
753 startdepth=startdepth, stopdepth=stopdepth)
753 startdepth=startdepth, stopdepth=stopdepth)
754
754
755 @predicate('_firstdescendants', safe=True)
755 @predicate('_firstdescendants', safe=True)
756 def _firstdescendants(repo, subset, x):
756 def _firstdescendants(repo, subset, x):
757 # ``_firstdescendants(set)``
757 # ``_firstdescendants(set)``
758 # Like ``descendants(set)`` but follows only the first parents.
758 # Like ``descendants(set)`` but follows only the first parents.
759 return _descendants(repo, subset, x, followfirst=True)
759 return _descendants(repo, subset, x, followfirst=True)
760
760
761 @predicate('destination([set])', safe=True, weight=10)
761 @predicate('destination([set])', safe=True, weight=10)
762 def destination(repo, subset, x):
762 def destination(repo, subset, x):
763 """Changesets that were created by a graft, transplant or rebase operation,
763 """Changesets that were created by a graft, transplant or rebase operation,
764 with the given revisions specified as the source. Omitting the optional set
764 with the given revisions specified as the source. Omitting the optional set
765 is the same as passing all().
765 is the same as passing all().
766 """
766 """
767 if x is not None:
767 if x is not None:
768 sources = getset(repo, fullreposet(repo), x)
768 sources = getset(repo, fullreposet(repo), x)
769 else:
769 else:
770 sources = fullreposet(repo)
770 sources = fullreposet(repo)
771
771
772 dests = set()
772 dests = set()
773
773
774 # subset contains all of the possible destinations that can be returned, so
774 # subset contains all of the possible destinations that can be returned, so
775 # iterate over them and see if their source(s) were provided in the arg set.
775 # iterate over them and see if their source(s) were provided in the arg set.
776 # Even if the immediate src of r is not in the arg set, src's source (or
776 # Even if the immediate src of r is not in the arg set, src's source (or
777 # further back) may be. Scanning back further than the immediate src allows
777 # further back) may be. Scanning back further than the immediate src allows
778 # transitive transplants and rebases to yield the same results as transitive
778 # transitive transplants and rebases to yield the same results as transitive
779 # grafts.
779 # grafts.
780 for r in subset:
780 for r in subset:
781 src = _getrevsource(repo, r)
781 src = _getrevsource(repo, r)
782 lineage = None
782 lineage = None
783
783
784 while src is not None:
784 while src is not None:
785 if lineage is None:
785 if lineage is None:
786 lineage = list()
786 lineage = list()
787
787
788 lineage.append(r)
788 lineage.append(r)
789
789
790 # The visited lineage is a match if the current source is in the arg
790 # The visited lineage is a match if the current source is in the arg
791 # set. Since every candidate dest is visited by way of iterating
791 # set. Since every candidate dest is visited by way of iterating
792 # subset, any dests further back in the lineage will be tested by a
792 # subset, any dests further back in the lineage will be tested by a
793 # different iteration over subset. Likewise, if the src was already
793 # different iteration over subset. Likewise, if the src was already
794 # selected, the current lineage can be selected without going back
794 # selected, the current lineage can be selected without going back
795 # further.
795 # further.
796 if src in sources or src in dests:
796 if src in sources or src in dests:
797 dests.update(lineage)
797 dests.update(lineage)
798 break
798 break
799
799
800 r = src
800 r = src
801 src = _getrevsource(repo, r)
801 src = _getrevsource(repo, r)
802
802
803 return subset.filter(dests.__contains__,
803 return subset.filter(dests.__contains__,
804 condrepr=lambda: '<destination %r>' % _sortedb(dests))
804 condrepr=lambda: '<destination %r>' % _sortedb(dests))
805
805
806 @predicate('contentdivergent()', safe=True)
806 @predicate('contentdivergent()', safe=True)
807 def contentdivergent(repo, subset, x):
807 def contentdivergent(repo, subset, x):
808 """
808 """
809 Final successors of changesets with an alternative set of final
809 Final successors of changesets with an alternative set of final
810 successors. (EXPERIMENTAL)
810 successors. (EXPERIMENTAL)
811 """
811 """
812 # i18n: "contentdivergent" is a keyword
812 # i18n: "contentdivergent" is a keyword
813 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
813 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
814 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
814 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
815 return subset & contentdivergent
815 return subset & contentdivergent
816
816
817 @predicate('extdata(source)', safe=False, weight=100)
817 @predicate('extdata(source)', safe=False, weight=100)
818 def extdata(repo, subset, x):
818 def extdata(repo, subset, x):
819 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
819 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
820 # i18n: "extdata" is a keyword
820 # i18n: "extdata" is a keyword
821 args = getargsdict(x, 'extdata', 'source')
821 args = getargsdict(x, 'extdata', 'source')
822 source = getstring(args.get('source'),
822 source = getstring(args.get('source'),
823 # i18n: "extdata" is a keyword
823 # i18n: "extdata" is a keyword
824 _('extdata takes at least 1 string argument'))
824 _('extdata takes at least 1 string argument'))
825 data = scmutil.extdatasource(repo, source)
825 data = scmutil.extdatasource(repo, source)
826 return subset & baseset(data)
826 return subset & baseset(data)
827
827
828 @predicate('extinct()', safe=True)
828 @predicate('extinct()', safe=True)
829 def extinct(repo, subset, x):
829 def extinct(repo, subset, x):
830 """Obsolete changesets with obsolete descendants only.
830 """Obsolete changesets with obsolete descendants only.
831 """
831 """
832 # i18n: "extinct" is a keyword
832 # i18n: "extinct" is a keyword
833 getargs(x, 0, 0, _("extinct takes no arguments"))
833 getargs(x, 0, 0, _("extinct takes no arguments"))
834 extincts = obsmod.getrevs(repo, 'extinct')
834 extincts = obsmod.getrevs(repo, 'extinct')
835 return subset & extincts
835 return subset & extincts
836
836
837 @predicate('extra(label, [value])', safe=True)
837 @predicate('extra(label, [value])', safe=True)
838 def extra(repo, subset, x):
838 def extra(repo, subset, x):
839 """Changesets with the given label in the extra metadata, with the given
839 """Changesets with the given label in the extra metadata, with the given
840 optional value.
840 optional value.
841
841
842 Pattern matching is supported for `value`. See
842 Pattern matching is supported for `value`. See
843 :hg:`help revisions.patterns`.
843 :hg:`help revisions.patterns`.
844 """
844 """
845 args = getargsdict(x, 'extra', 'label value')
845 args = getargsdict(x, 'extra', 'label value')
846 if 'label' not in args:
846 if 'label' not in args:
847 # i18n: "extra" is a keyword
847 # i18n: "extra" is a keyword
848 raise error.ParseError(_('extra takes at least 1 argument'))
848 raise error.ParseError(_('extra takes at least 1 argument'))
849 # i18n: "extra" is a keyword
849 # i18n: "extra" is a keyword
850 label = getstring(args['label'], _('first argument to extra must be '
850 label = getstring(args['label'], _('first argument to extra must be '
851 'a string'))
851 'a string'))
852 value = None
852 value = None
853
853
854 if 'value' in args:
854 if 'value' in args:
855 # i18n: "extra" is a keyword
855 # i18n: "extra" is a keyword
856 value = getstring(args['value'], _('second argument to extra must be '
856 value = getstring(args['value'], _('second argument to extra must be '
857 'a string'))
857 'a string'))
858 kind, value, matcher = stringutil.stringmatcher(value)
858 kind, value, matcher = stringutil.stringmatcher(value)
859
859
860 def _matchvalue(r):
860 def _matchvalue(r):
861 extra = repo[r].extra()
861 extra = repo[r].extra()
862 return label in extra and (value is None or matcher(extra[label]))
862 return label in extra and (value is None or matcher(extra[label]))
863
863
864 return subset.filter(lambda r: _matchvalue(r),
864 return subset.filter(lambda r: _matchvalue(r),
865 condrepr=('<extra[%r] %r>', label, value))
865 condrepr=('<extra[%r] %r>', label, value))
866
866
867 @predicate('filelog(pattern)', safe=True)
867 @predicate('filelog(pattern)', safe=True)
868 def filelog(repo, subset, x):
868 def filelog(repo, subset, x):
869 """Changesets connected to the specified filelog.
869 """Changesets connected to the specified filelog.
870
870
871 For performance reasons, visits only revisions mentioned in the file-level
871 For performance reasons, visits only revisions mentioned in the file-level
872 filelog, rather than filtering through all changesets (much faster, but
872 filelog, rather than filtering through all changesets (much faster, but
873 doesn't include deletes or duplicate changes). For a slower, more accurate
873 doesn't include deletes or duplicate changes). For a slower, more accurate
874 result, use ``file()``.
874 result, use ``file()``.
875
875
876 The pattern without explicit kind like ``glob:`` is expected to be
876 The pattern without explicit kind like ``glob:`` is expected to be
877 relative to the current directory and match against a file exactly
877 relative to the current directory and match against a file exactly
878 for efficiency.
878 for efficiency.
879
879
880 If some linkrev points to revisions filtered by the current repoview, we'll
880 If some linkrev points to revisions filtered by the current repoview, we'll
881 work around it to return a non-filtered value.
881 work around it to return a non-filtered value.
882 """
882 """
883
883
884 # i18n: "filelog" is a keyword
884 # i18n: "filelog" is a keyword
885 pat = getstring(x, _("filelog requires a pattern"))
885 pat = getstring(x, _("filelog requires a pattern"))
886 s = set()
886 s = set()
887 cl = repo.changelog
887 cl = repo.changelog
888
888
889 if not matchmod.patkind(pat):
889 if not matchmod.patkind(pat):
890 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
890 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
891 files = [f]
891 files = [f]
892 else:
892 else:
893 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
893 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
894 files = (f for f in repo[None] if m(f))
894 files = (f for f in repo[None] if m(f))
895
895
896 for f in files:
896 for f in files:
897 fl = repo.file(f)
897 fl = repo.file(f)
898 known = {}
898 known = {}
899 scanpos = 0
899 scanpos = 0
900 for fr in list(fl):
900 for fr in list(fl):
901 fn = fl.node(fr)
901 fn = fl.node(fr)
902 if fn in known:
902 if fn in known:
903 s.add(known[fn])
903 s.add(known[fn])
904 continue
904 continue
905
905
906 lr = fl.linkrev(fr)
906 lr = fl.linkrev(fr)
907 if lr in cl:
907 if lr in cl:
908 s.add(lr)
908 s.add(lr)
909 elif scanpos is not None:
909 elif scanpos is not None:
910 # lowest matching changeset is filtered, scan further
910 # lowest matching changeset is filtered, scan further
911 # ahead in changelog
911 # ahead in changelog
912 start = max(lr, scanpos) + 1
912 start = max(lr, scanpos) + 1
913 scanpos = None
913 scanpos = None
914 for r in cl.revs(start):
914 for r in cl.revs(start):
915 # minimize parsing of non-matching entries
915 # minimize parsing of non-matching entries
916 if f in cl.revision(r) and f in cl.readfiles(r):
916 if f in cl.revision(r) and f in cl.readfiles(r):
917 try:
917 try:
918 # try to use manifest delta fastpath
918 # try to use manifest delta fastpath
919 n = repo[r].filenode(f)
919 n = repo[r].filenode(f)
920 if n not in known:
920 if n not in known:
921 if n == fn:
921 if n == fn:
922 s.add(r)
922 s.add(r)
923 scanpos = r
923 scanpos = r
924 break
924 break
925 else:
925 else:
926 known[n] = r
926 known[n] = r
927 except error.ManifestLookupError:
927 except error.ManifestLookupError:
928 # deletion in changelog
928 # deletion in changelog
929 continue
929 continue
930
930
931 return subset & s
931 return subset & s
932
932
933 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
933 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
934 def first(repo, subset, x, order):
934 def first(repo, subset, x, order):
935 """An alias for limit().
935 """An alias for limit().
936 """
936 """
937 return limit(repo, subset, x, order)
937 return limit(repo, subset, x, order)
938
938
939 def _follow(repo, subset, x, name, followfirst=False):
939 def _follow(repo, subset, x, name, followfirst=False):
940 args = getargsdict(x, name, 'file startrev')
940 args = getargsdict(x, name, 'file startrev')
941 revs = None
941 revs = None
942 if 'startrev' in args:
942 if 'startrev' in args:
943 revs = getset(repo, fullreposet(repo), args['startrev'])
943 revs = getset(repo, fullreposet(repo), args['startrev'])
944 if 'file' in args:
944 if 'file' in args:
945 x = getstring(args['file'], _("%s expected a pattern") % name)
945 x = getstring(args['file'], _("%s expected a pattern") % name)
946 if revs is None:
946 if revs is None:
947 revs = [None]
947 revs = [None]
948 fctxs = []
948 fctxs = []
949 for r in revs:
949 for r in revs:
950 ctx = mctx = repo[r]
950 ctx = mctx = repo[r]
951 if r is None:
951 if r is None:
952 ctx = repo['.']
952 ctx = repo['.']
953 m = matchmod.match(repo.root, repo.getcwd(), [x],
953 m = matchmod.match(repo.root, repo.getcwd(), [x],
954 ctx=mctx, default='path')
954 ctx=mctx, default='path')
955 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
955 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
956 s = dagop.filerevancestors(fctxs, followfirst)
956 s = dagop.filerevancestors(fctxs, followfirst)
957 else:
957 else:
958 if revs is None:
958 if revs is None:
959 revs = baseset([repo['.'].rev()])
959 revs = baseset([repo['.'].rev()])
960 s = dagop.revancestors(repo, revs, followfirst)
960 s = dagop.revancestors(repo, revs, followfirst)
961
961
962 return subset & s
962 return subset & s
963
963
964 @predicate('follow([file[, startrev]])', safe=True)
964 @predicate('follow([file[, startrev]])', safe=True)
965 def follow(repo, subset, x):
965 def follow(repo, subset, x):
966 """
966 """
967 An alias for ``::.`` (ancestors of the working directory's first parent).
967 An alias for ``::.`` (ancestors of the working directory's first parent).
968 If file pattern is specified, the histories of files matching given
968 If file pattern is specified, the histories of files matching given
969 pattern in the revision given by startrev are followed, including copies.
969 pattern in the revision given by startrev are followed, including copies.
970 """
970 """
971 return _follow(repo, subset, x, 'follow')
971 return _follow(repo, subset, x, 'follow')
972
972
973 @predicate('_followfirst', safe=True)
973 @predicate('_followfirst', safe=True)
974 def _followfirst(repo, subset, x):
974 def _followfirst(repo, subset, x):
975 # ``followfirst([file[, startrev]])``
975 # ``followfirst([file[, startrev]])``
976 # Like ``follow([file[, startrev]])`` but follows only the first parent
976 # Like ``follow([file[, startrev]])`` but follows only the first parent
977 # of every revisions or files revisions.
977 # of every revisions or files revisions.
978 return _follow(repo, subset, x, '_followfirst', followfirst=True)
978 return _follow(repo, subset, x, '_followfirst', followfirst=True)
979
979
980 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
980 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
981 safe=True)
981 safe=True)
982 def followlines(repo, subset, x):
982 def followlines(repo, subset, x):
983 """Changesets modifying `file` in line range ('fromline', 'toline').
983 """Changesets modifying `file` in line range ('fromline', 'toline').
984
984
985 Line range corresponds to 'file' content at 'startrev' and should hence be
985 Line range corresponds to 'file' content at 'startrev' and should hence be
986 consistent with file size. If startrev is not specified, working directory's
986 consistent with file size. If startrev is not specified, working directory's
987 parent is used.
987 parent is used.
988
988
989 By default, ancestors of 'startrev' are returned. If 'descend' is True,
989 By default, ancestors of 'startrev' are returned. If 'descend' is True,
990 descendants of 'startrev' are returned though renames are (currently) not
990 descendants of 'startrev' are returned though renames are (currently) not
991 followed in this direction.
991 followed in this direction.
992 """
992 """
993 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
993 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
994 if len(args['lines']) != 1:
994 if len(args['lines']) != 1:
995 raise error.ParseError(_("followlines requires a line range"))
995 raise error.ParseError(_("followlines requires a line range"))
996
996
997 rev = '.'
997 rev = '.'
998 if 'startrev' in args:
998 if 'startrev' in args:
999 revs = getset(repo, fullreposet(repo), args['startrev'])
999 revs = getset(repo, fullreposet(repo), args['startrev'])
1000 if len(revs) != 1:
1000 if len(revs) != 1:
1001 raise error.ParseError(
1001 raise error.ParseError(
1002 # i18n: "followlines" is a keyword
1002 # i18n: "followlines" is a keyword
1003 _("followlines expects exactly one revision"))
1003 _("followlines expects exactly one revision"))
1004 rev = revs.last()
1004 rev = revs.last()
1005
1005
1006 pat = getstring(args['file'], _("followlines requires a pattern"))
1006 pat = getstring(args['file'], _("followlines requires a pattern"))
1007 # i18n: "followlines" is a keyword
1007 # i18n: "followlines" is a keyword
1008 msg = _("followlines expects exactly one file")
1008 msg = _("followlines expects exactly one file")
1009 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
1009 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
1010 # i18n: "followlines" is a keyword
1010 # i18n: "followlines" is a keyword
1011 lr = getrange(args['lines'][0], _("followlines expects a line range"))
1011 lr = getrange(args['lines'][0], _("followlines expects a line range"))
1012 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
1012 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
1013 for a in lr]
1013 for a in lr]
1014 fromline, toline = util.processlinerange(fromline, toline)
1014 fromline, toline = util.processlinerange(fromline, toline)
1015
1015
1016 fctx = repo[rev].filectx(fname)
1016 fctx = repo[rev].filectx(fname)
1017 descend = False
1017 descend = False
1018 if 'descend' in args:
1018 if 'descend' in args:
1019 descend = getboolean(args['descend'],
1019 descend = getboolean(args['descend'],
1020 # i18n: "descend" is a keyword
1020 # i18n: "descend" is a keyword
1021 _("descend argument must be a boolean"))
1021 _("descend argument must be a boolean"))
1022 if descend:
1022 if descend:
1023 rs = generatorset(
1023 rs = generatorset(
1024 (c.rev() for c, _linerange
1024 (c.rev() for c, _linerange
1025 in dagop.blockdescendants(fctx, fromline, toline)),
1025 in dagop.blockdescendants(fctx, fromline, toline)),
1026 iterasc=True)
1026 iterasc=True)
1027 else:
1027 else:
1028 rs = generatorset(
1028 rs = generatorset(
1029 (c.rev() for c, _linerange
1029 (c.rev() for c, _linerange
1030 in dagop.blockancestors(fctx, fromline, toline)),
1030 in dagop.blockancestors(fctx, fromline, toline)),
1031 iterasc=False)
1031 iterasc=False)
1032 return subset & rs
1032 return subset & rs
1033
1033
1034 @predicate('all()', safe=True)
1034 @predicate('all()', safe=True)
1035 def getall(repo, subset, x):
1035 def getall(repo, subset, x):
1036 """All changesets, the same as ``0:tip``.
1036 """All changesets, the same as ``0:tip``.
1037 """
1037 """
1038 # i18n: "all" is a keyword
1038 # i18n: "all" is a keyword
1039 getargs(x, 0, 0, _("all takes no arguments"))
1039 getargs(x, 0, 0, _("all takes no arguments"))
1040 return subset & spanset(repo) # drop "null" if any
1040 return subset & spanset(repo) # drop "null" if any
1041
1041
1042 @predicate('grep(regex)', weight=10)
1042 @predicate('grep(regex)', weight=10)
1043 def grep(repo, subset, x):
1043 def grep(repo, subset, x):
1044 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1044 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1045 to ensure special escape characters are handled correctly. Unlike
1045 to ensure special escape characters are handled correctly. Unlike
1046 ``keyword(string)``, the match is case-sensitive.
1046 ``keyword(string)``, the match is case-sensitive.
1047 """
1047 """
1048 try:
1048 try:
1049 # i18n: "grep" is a keyword
1049 # i18n: "grep" is a keyword
1050 gr = re.compile(getstring(x, _("grep requires a string")))
1050 gr = re.compile(getstring(x, _("grep requires a string")))
1051 except re.error as e:
1051 except re.error as e:
1052 raise error.ParseError(
1052 raise error.ParseError(
1053 _('invalid match pattern: %s') % stringutil.forcebytestr(e))
1053 _('invalid match pattern: %s') % stringutil.forcebytestr(e))
1054
1054
1055 def matches(x):
1055 def matches(x):
1056 c = repo[x]
1056 c = repo[x]
1057 for e in c.files() + [c.user(), c.description()]:
1057 for e in c.files() + [c.user(), c.description()]:
1058 if gr.search(e):
1058 if gr.search(e):
1059 return True
1059 return True
1060 return False
1060 return False
1061
1061
1062 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1062 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1063
1063
1064 @predicate('_matchfiles', safe=True)
1064 @predicate('_matchfiles', safe=True)
1065 def _matchfiles(repo, subset, x):
1065 def _matchfiles(repo, subset, x):
1066 # _matchfiles takes a revset list of prefixed arguments:
1066 # _matchfiles takes a revset list of prefixed arguments:
1067 #
1067 #
1068 # [p:foo, i:bar, x:baz]
1068 # [p:foo, i:bar, x:baz]
1069 #
1069 #
1070 # builds a match object from them and filters subset. Allowed
1070 # builds a match object from them and filters subset. Allowed
1071 # prefixes are 'p:' for regular patterns, 'i:' for include
1071 # prefixes are 'p:' for regular patterns, 'i:' for include
1072 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1072 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1073 # a revision identifier, or the empty string to reference the
1073 # a revision identifier, or the empty string to reference the
1074 # working directory, from which the match object is
1074 # working directory, from which the match object is
1075 # initialized. Use 'd:' to set the default matching mode, default
1075 # initialized. Use 'd:' to set the default matching mode, default
1076 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1076 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1077
1077
1078 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1078 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1079 pats, inc, exc = [], [], []
1079 pats, inc, exc = [], [], []
1080 rev, default = None, None
1080 rev, default = None, None
1081 for arg in l:
1081 for arg in l:
1082 s = getstring(arg, "_matchfiles requires string arguments")
1082 s = getstring(arg, "_matchfiles requires string arguments")
1083 prefix, value = s[:2], s[2:]
1083 prefix, value = s[:2], s[2:]
1084 if prefix == 'p:':
1084 if prefix == 'p:':
1085 pats.append(value)
1085 pats.append(value)
1086 elif prefix == 'i:':
1086 elif prefix == 'i:':
1087 inc.append(value)
1087 inc.append(value)
1088 elif prefix == 'x:':
1088 elif prefix == 'x:':
1089 exc.append(value)
1089 exc.append(value)
1090 elif prefix == 'r:':
1090 elif prefix == 'r:':
1091 if rev is not None:
1091 if rev is not None:
1092 raise error.ParseError('_matchfiles expected at most one '
1092 raise error.ParseError('_matchfiles expected at most one '
1093 'revision')
1093 'revision')
1094 if value == '': # empty means working directory
1094 if value == '': # empty means working directory
1095 rev = node.wdirrev
1095 rev = node.wdirrev
1096 else:
1096 else:
1097 rev = value
1097 rev = value
1098 elif prefix == 'd:':
1098 elif prefix == 'd:':
1099 if default is not None:
1099 if default is not None:
1100 raise error.ParseError('_matchfiles expected at most one '
1100 raise error.ParseError('_matchfiles expected at most one '
1101 'default mode')
1101 'default mode')
1102 default = value
1102 default = value
1103 else:
1103 else:
1104 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1104 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1105 if not default:
1105 if not default:
1106 default = 'glob'
1106 default = 'glob'
1107 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc)
1107 hasset = any(matchmod.patkind(p) == 'set' for p in pats + inc + exc)
1108
1108
1109 mcache = [None]
1109 mcache = [None]
1110
1110
1111 # This directly read the changelog data as creating changectx for all
1111 # This directly read the changelog data as creating changectx for all
1112 # revisions is quite expensive.
1112 # revisions is quite expensive.
1113 getfiles = repo.changelog.readfiles
1113 getfiles = repo.changelog.readfiles
1114 wdirrev = node.wdirrev
1114 wdirrev = node.wdirrev
1115 def matches(x):
1115 def matches(x):
1116 if x == wdirrev:
1116 if x == wdirrev:
1117 files = repo[x].files()
1117 files = repo[x].files()
1118 else:
1118 else:
1119 files = getfiles(x)
1119 files = getfiles(x)
1120
1120
1121 if not mcache[0] or (hasset and rev is None):
1121 if not mcache[0] or (hasset and rev is None):
1122 r = x if rev is None else rev
1122 r = x if rev is None else rev
1123 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats,
1123 mcache[0] = matchmod.match(repo.root, repo.getcwd(), pats,
1124 include=inc, exclude=exc, ctx=repo[r],
1124 include=inc, exclude=exc, ctx=repo[r],
1125 default=default)
1125 default=default)
1126 m = mcache[0]
1126 m = mcache[0]
1127
1127
1128 for f in files:
1128 for f in files:
1129 if m(f):
1129 if m(f):
1130 return True
1130 return True
1131 return False
1131 return False
1132
1132
1133 return subset.filter(matches,
1133 return subset.filter(matches,
1134 condrepr=('<matchfiles patterns=%r, include=%r '
1134 condrepr=('<matchfiles patterns=%r, include=%r '
1135 'exclude=%r, default=%r, rev=%r>',
1135 'exclude=%r, default=%r, rev=%r>',
1136 pats, inc, exc, default, rev))
1136 pats, inc, exc, default, rev))
1137
1137
1138 @predicate('file(pattern)', safe=True, weight=10)
1138 @predicate('file(pattern)', safe=True, weight=10)
1139 def hasfile(repo, subset, x):
1139 def hasfile(repo, subset, x):
1140 """Changesets affecting files matched by pattern.
1140 """Changesets affecting files matched by pattern.
1141
1141
1142 For a faster but less accurate result, consider using ``filelog()``
1142 For a faster but less accurate result, consider using ``filelog()``
1143 instead.
1143 instead.
1144
1144
1145 This predicate uses ``glob:`` as the default kind of pattern.
1145 This predicate uses ``glob:`` as the default kind of pattern.
1146 """
1146 """
1147 # i18n: "file" is a keyword
1147 # i18n: "file" is a keyword
1148 pat = getstring(x, _("file requires a pattern"))
1148 pat = getstring(x, _("file requires a pattern"))
1149 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1149 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1150
1150
1151 @predicate('head()', safe=True)
1151 @predicate('head()', safe=True)
1152 def head(repo, subset, x):
1152 def head(repo, subset, x):
1153 """Changeset is a named branch head.
1153 """Changeset is a named branch head.
1154 """
1154 """
1155 # i18n: "head" is a keyword
1155 # i18n: "head" is a keyword
1156 getargs(x, 0, 0, _("head takes no arguments"))
1156 getargs(x, 0, 0, _("head takes no arguments"))
1157 hs = set()
1157 hs = set()
1158 cl = repo.changelog
1158 cl = repo.changelog
1159 for ls in repo.branchmap().itervalues():
1159 for ls in repo.branchmap().itervalues():
1160 hs.update(cl.rev(h) for h in ls)
1160 hs.update(cl.rev(h) for h in ls)
1161 return subset & baseset(hs)
1161 return subset & baseset(hs)
1162
1162
1163 @predicate('heads(set)', safe=True, takeorder=True)
1163 @predicate('heads(set)', safe=True, takeorder=True)
1164 def heads(repo, subset, x, order):
1164 def heads(repo, subset, x, order):
1165 """Members of set with no children in set.
1165 """Members of set with no children in set.
1166 """
1166 """
1167 # argument set should never define order
1167 # argument set should never define order
1168 if order == defineorder:
1168 if order == defineorder:
1169 order = followorder
1169 order = followorder
1170 s = getset(repo, subset, x, order=order)
1170 s = getset(repo, subset, x, order=order)
1171 ps = parents(repo, subset, x)
1171 ps = parents(repo, subset, x)
1172 return s - ps
1172 return s - ps
1173
1173
1174 @predicate('hidden()', safe=True)
1174 @predicate('hidden()', safe=True)
1175 def hidden(repo, subset, x):
1175 def hidden(repo, subset, x):
1176 """Hidden changesets.
1176 """Hidden changesets.
1177 """
1177 """
1178 # i18n: "hidden" is a keyword
1178 # i18n: "hidden" is a keyword
1179 getargs(x, 0, 0, _("hidden takes no arguments"))
1179 getargs(x, 0, 0, _("hidden takes no arguments"))
1180 hiddenrevs = repoview.filterrevs(repo, 'visible')
1180 hiddenrevs = repoview.filterrevs(repo, 'visible')
1181 return subset & hiddenrevs
1181 return subset & hiddenrevs
1182
1182
1183 @predicate('keyword(string)', safe=True, weight=10)
1183 @predicate('keyword(string)', safe=True, weight=10)
1184 def keyword(repo, subset, x):
1184 def keyword(repo, subset, x):
1185 """Search commit message, user name, and names of changed files for
1185 """Search commit message, user name, and names of changed files for
1186 string. The match is case-insensitive.
1186 string. The match is case-insensitive.
1187
1187
1188 For a regular expression or case sensitive search of these fields, use
1188 For a regular expression or case sensitive search of these fields, use
1189 ``grep(regex)``.
1189 ``grep(regex)``.
1190 """
1190 """
1191 # i18n: "keyword" is a keyword
1191 # i18n: "keyword" is a keyword
1192 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1192 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1193
1193
1194 def matches(r):
1194 def matches(r):
1195 c = repo[r]
1195 c = repo[r]
1196 return any(kw in encoding.lower(t)
1196 return any(kw in encoding.lower(t)
1197 for t in c.files() + [c.user(), c.description()])
1197 for t in c.files() + [c.user(), c.description()])
1198
1198
1199 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1199 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1200
1200
1201 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1201 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1202 def limit(repo, subset, x, order):
1202 def limit(repo, subset, x, order):
1203 """First n members of set, defaulting to 1, starting from offset.
1203 """First n members of set, defaulting to 1, starting from offset.
1204 """
1204 """
1205 args = getargsdict(x, 'limit', 'set n offset')
1205 args = getargsdict(x, 'limit', 'set n offset')
1206 if 'set' not in args:
1206 if 'set' not in args:
1207 # i18n: "limit" is a keyword
1207 # i18n: "limit" is a keyword
1208 raise error.ParseError(_("limit requires one to three arguments"))
1208 raise error.ParseError(_("limit requires one to three arguments"))
1209 # i18n: "limit" is a keyword
1209 # i18n: "limit" is a keyword
1210 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1210 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1211 if lim < 0:
1211 if lim < 0:
1212 raise error.ParseError(_("negative number to select"))
1212 raise error.ParseError(_("negative number to select"))
1213 # i18n: "limit" is a keyword
1213 # i18n: "limit" is a keyword
1214 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1214 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1215 if ofs < 0:
1215 if ofs < 0:
1216 raise error.ParseError(_("negative offset"))
1216 raise error.ParseError(_("negative offset"))
1217 os = getset(repo, fullreposet(repo), args['set'])
1217 os = getset(repo, fullreposet(repo), args['set'])
1218 ls = os.slice(ofs, ofs + lim)
1218 ls = os.slice(ofs, ofs + lim)
1219 if order == followorder and lim > 1:
1219 if order == followorder and lim > 1:
1220 return subset & ls
1220 return subset & ls
1221 return ls & subset
1221 return ls & subset
1222
1222
1223 @predicate('last(set, [n])', safe=True, takeorder=True)
1223 @predicate('last(set, [n])', safe=True, takeorder=True)
1224 def last(repo, subset, x, order):
1224 def last(repo, subset, x, order):
1225 """Last n members of set, defaulting to 1.
1225 """Last n members of set, defaulting to 1.
1226 """
1226 """
1227 # i18n: "last" is a keyword
1227 # i18n: "last" is a keyword
1228 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1228 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1229 lim = 1
1229 lim = 1
1230 if len(l) == 2:
1230 if len(l) == 2:
1231 # i18n: "last" is a keyword
1231 # i18n: "last" is a keyword
1232 lim = getinteger(l[1], _("last expects a number"))
1232 lim = getinteger(l[1], _("last expects a number"))
1233 if lim < 0:
1233 if lim < 0:
1234 raise error.ParseError(_("negative number to select"))
1234 raise error.ParseError(_("negative number to select"))
1235 os = getset(repo, fullreposet(repo), l[0])
1235 os = getset(repo, fullreposet(repo), l[0])
1236 os.reverse()
1236 os.reverse()
1237 ls = os.slice(0, lim)
1237 ls = os.slice(0, lim)
1238 if order == followorder and lim > 1:
1238 if order == followorder and lim > 1:
1239 return subset & ls
1239 return subset & ls
1240 ls.reverse()
1240 ls.reverse()
1241 return ls & subset
1241 return ls & subset
1242
1242
1243 @predicate('max(set)', safe=True)
1243 @predicate('max(set)', safe=True)
1244 def maxrev(repo, subset, x):
1244 def maxrev(repo, subset, x):
1245 """Changeset with highest revision number in set.
1245 """Changeset with highest revision number in set.
1246 """
1246 """
1247 os = getset(repo, fullreposet(repo), x)
1247 os = getset(repo, fullreposet(repo), x)
1248 try:
1248 try:
1249 m = os.max()
1249 m = os.max()
1250 if m in subset:
1250 if m in subset:
1251 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1251 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1252 except ValueError:
1252 except ValueError:
1253 # os.max() throws a ValueError when the collection is empty.
1253 # os.max() throws a ValueError when the collection is empty.
1254 # Same as python's max().
1254 # Same as python's max().
1255 pass
1255 pass
1256 return baseset(datarepr=('<max %r, %r>', subset, os))
1256 return baseset(datarepr=('<max %r, %r>', subset, os))
1257
1257
1258 @predicate('merge()', safe=True)
1258 @predicate('merge()', safe=True)
1259 def merge(repo, subset, x):
1259 def merge(repo, subset, x):
1260 """Changeset is a merge changeset.
1260 """Changeset is a merge changeset.
1261 """
1261 """
1262 # i18n: "merge" is a keyword
1262 # i18n: "merge" is a keyword
1263 getargs(x, 0, 0, _("merge takes no arguments"))
1263 getargs(x, 0, 0, _("merge takes no arguments"))
1264 cl = repo.changelog
1264 cl = repo.changelog
1265 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1265 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1266 condrepr='<merge>')
1266 condrepr='<merge>')
1267
1267
1268 @predicate('branchpoint()', safe=True)
1268 @predicate('branchpoint()', safe=True)
1269 def branchpoint(repo, subset, x):
1269 def branchpoint(repo, subset, x):
1270 """Changesets with more than one child.
1270 """Changesets with more than one child.
1271 """
1271 """
1272 # i18n: "branchpoint" is a keyword
1272 # i18n: "branchpoint" is a keyword
1273 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1273 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1274 cl = repo.changelog
1274 cl = repo.changelog
1275 if not subset:
1275 if not subset:
1276 return baseset()
1276 return baseset()
1277 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1277 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1278 # (and if it is not, it should.)
1278 # (and if it is not, it should.)
1279 baserev = min(subset)
1279 baserev = min(subset)
1280 parentscount = [0]*(len(repo) - baserev)
1280 parentscount = [0]*(len(repo) - baserev)
1281 for r in cl.revs(start=baserev + 1):
1281 for r in cl.revs(start=baserev + 1):
1282 for p in cl.parentrevs(r):
1282 for p in cl.parentrevs(r):
1283 if p >= baserev:
1283 if p >= baserev:
1284 parentscount[p - baserev] += 1
1284 parentscount[p - baserev] += 1
1285 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1285 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1286 condrepr='<branchpoint>')
1286 condrepr='<branchpoint>')
1287
1287
1288 @predicate('min(set)', safe=True)
1288 @predicate('min(set)', safe=True)
1289 def minrev(repo, subset, x):
1289 def minrev(repo, subset, x):
1290 """Changeset with lowest revision number in set.
1290 """Changeset with lowest revision number in set.
1291 """
1291 """
1292 os = getset(repo, fullreposet(repo), x)
1292 os = getset(repo, fullreposet(repo), x)
1293 try:
1293 try:
1294 m = os.min()
1294 m = os.min()
1295 if m in subset:
1295 if m in subset:
1296 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1296 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1297 except ValueError:
1297 except ValueError:
1298 # os.min() throws a ValueError when the collection is empty.
1298 # os.min() throws a ValueError when the collection is empty.
1299 # Same as python's min().
1299 # Same as python's min().
1300 pass
1300 pass
1301 return baseset(datarepr=('<min %r, %r>', subset, os))
1301 return baseset(datarepr=('<min %r, %r>', subset, os))
1302
1302
1303 @predicate('modifies(pattern)', safe=True, weight=30)
1303 @predicate('modifies(pattern)', safe=True, weight=30)
1304 def modifies(repo, subset, x):
1304 def modifies(repo, subset, x):
1305 """Changesets modifying files matched by pattern.
1305 """Changesets modifying files matched by pattern.
1306
1306
1307 The pattern without explicit kind like ``glob:`` is expected to be
1307 The pattern without explicit kind like ``glob:`` is expected to be
1308 relative to the current directory and match against a file or a
1308 relative to the current directory and match against a file or a
1309 directory.
1309 directory.
1310 """
1310 """
1311 # i18n: "modifies" is a keyword
1311 # i18n: "modifies" is a keyword
1312 pat = getstring(x, _("modifies requires a pattern"))
1312 pat = getstring(x, _("modifies requires a pattern"))
1313 return checkstatus(repo, subset, pat, 0)
1313 return checkstatus(repo, subset, pat, 0)
1314
1314
1315 @predicate('named(namespace)')
1315 @predicate('named(namespace)')
1316 def named(repo, subset, x):
1316 def named(repo, subset, x):
1317 """The changesets in a given namespace.
1317 """The changesets in a given namespace.
1318
1318
1319 Pattern matching is supported for `namespace`. See
1319 Pattern matching is supported for `namespace`. See
1320 :hg:`help revisions.patterns`.
1320 :hg:`help revisions.patterns`.
1321 """
1321 """
1322 # i18n: "named" is a keyword
1322 # i18n: "named" is a keyword
1323 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1323 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1324
1324
1325 ns = getstring(args[0],
1325 ns = getstring(args[0],
1326 # i18n: "named" is a keyword
1326 # i18n: "named" is a keyword
1327 _('the argument to named must be a string'))
1327 _('the argument to named must be a string'))
1328 kind, pattern, matcher = stringutil.stringmatcher(ns)
1328 kind, pattern, matcher = stringutil.stringmatcher(ns)
1329 namespaces = set()
1329 namespaces = set()
1330 if kind == 'literal':
1330 if kind == 'literal':
1331 if pattern not in repo.names:
1331 if pattern not in repo.names:
1332 raise error.RepoLookupError(_("namespace '%s' does not exist")
1332 raise error.RepoLookupError(_("namespace '%s' does not exist")
1333 % ns)
1333 % ns)
1334 namespaces.add(repo.names[pattern])
1334 namespaces.add(repo.names[pattern])
1335 else:
1335 else:
1336 for name, ns in repo.names.iteritems():
1336 for name, ns in repo.names.iteritems():
1337 if matcher(name):
1337 if matcher(name):
1338 namespaces.add(ns)
1338 namespaces.add(ns)
1339 if not namespaces:
1339 if not namespaces:
1340 raise error.RepoLookupError(_("no namespace exists"
1340 raise error.RepoLookupError(_("no namespace exists"
1341 " that match '%s'") % pattern)
1341 " that match '%s'") % pattern)
1342
1342
1343 names = set()
1343 names = set()
1344 for ns in namespaces:
1344 for ns in namespaces:
1345 for name in ns.listnames(repo):
1345 for name in ns.listnames(repo):
1346 if name not in ns.deprecated:
1346 if name not in ns.deprecated:
1347 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1347 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1348
1348
1349 names -= {node.nullrev}
1349 names -= {node.nullrev}
1350 return subset & names
1350 return subset & names
1351
1351
1352 @predicate('id(string)', safe=True)
1352 @predicate('id(string)', safe=True)
1353 def node_(repo, subset, x):
1353 def node_(repo, subset, x):
1354 """Revision non-ambiguously specified by the given hex string prefix.
1354 """Revision non-ambiguously specified by the given hex string prefix.
1355 """
1355 """
1356 # i18n: "id" is a keyword
1356 # i18n: "id" is a keyword
1357 l = getargs(x, 1, 1, _("id requires one argument"))
1357 l = getargs(x, 1, 1, _("id requires one argument"))
1358 # i18n: "id" is a keyword
1358 # i18n: "id" is a keyword
1359 n = getstring(l[0], _("id requires a string"))
1359 n = getstring(l[0], _("id requires a string"))
1360 if len(n) == 40:
1360 if len(n) == 40:
1361 try:
1361 try:
1362 rn = repo.changelog.rev(node.bin(n))
1362 rn = repo.changelog.rev(node.bin(n))
1363 except error.WdirUnsupported:
1363 except error.WdirUnsupported:
1364 rn = node.wdirrev
1364 rn = node.wdirrev
1365 except (LookupError, TypeError):
1365 except (LookupError, TypeError):
1366 rn = None
1366 rn = None
1367 else:
1367 else:
1368 rn = None
1368 rn = None
1369 try:
1369 try:
1370 pm = scmutil.resolvehexnodeidprefix(repo, n)
1370 pm = scmutil.resolvehexnodeidprefix(repo, n)
1371 if pm is not None:
1371 if pm is not None:
1372 rn = repo.changelog.rev(pm)
1372 rn = repo.changelog.rev(pm)
1373 except LookupError:
1373 except LookupError:
1374 pass
1374 pass
1375 except error.WdirUnsupported:
1375 except error.WdirUnsupported:
1376 rn = node.wdirrev
1376 rn = node.wdirrev
1377
1377
1378 if rn is None:
1378 if rn is None:
1379 return baseset()
1379 return baseset()
1380 result = baseset([rn])
1380 result = baseset([rn])
1381 return result & subset
1381 return result & subset
1382
1382
1383 @predicate('none()', safe=True)
1383 @predicate('none()', safe=True)
1384 def none(repo, subset, x):
1384 def none(repo, subset, x):
1385 """No changesets.
1385 """No changesets.
1386 """
1386 """
1387 # i18n: "none" is a keyword
1387 # i18n: "none" is a keyword
1388 getargs(x, 0, 0, _("none takes no arguments"))
1388 getargs(x, 0, 0, _("none takes no arguments"))
1389 return baseset()
1389 return baseset()
1390
1390
1391 @predicate('obsolete()', safe=True)
1391 @predicate('obsolete()', safe=True)
1392 def obsolete(repo, subset, x):
1392 def obsolete(repo, subset, x):
1393 """Mutable changeset with a newer version."""
1393 """Mutable changeset with a newer version."""
1394 # i18n: "obsolete" is a keyword
1394 # i18n: "obsolete" is a keyword
1395 getargs(x, 0, 0, _("obsolete takes no arguments"))
1395 getargs(x, 0, 0, _("obsolete takes no arguments"))
1396 obsoletes = obsmod.getrevs(repo, 'obsolete')
1396 obsoletes = obsmod.getrevs(repo, 'obsolete')
1397 return subset & obsoletes
1397 return subset & obsoletes
1398
1398
1399 @predicate('only(set, [set])', safe=True)
1399 @predicate('only(set, [set])', safe=True)
1400 def only(repo, subset, x):
1400 def only(repo, subset, x):
1401 """Changesets that are ancestors of the first set that are not ancestors
1401 """Changesets that are ancestors of the first set that are not ancestors
1402 of any other head in the repo. If a second set is specified, the result
1402 of any other head in the repo. If a second set is specified, the result
1403 is ancestors of the first set that are not ancestors of the second set
1403 is ancestors of the first set that are not ancestors of the second set
1404 (i.e. ::<set1> - ::<set2>).
1404 (i.e. ::<set1> - ::<set2>).
1405 """
1405 """
1406 cl = repo.changelog
1406 cl = repo.changelog
1407 # i18n: "only" is a keyword
1407 # i18n: "only" is a keyword
1408 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1408 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1409 include = getset(repo, fullreposet(repo), args[0])
1409 include = getset(repo, fullreposet(repo), args[0])
1410 if len(args) == 1:
1410 if len(args) == 1:
1411 if not include:
1411 if not include:
1412 return baseset()
1412 return baseset()
1413
1413
1414 descendants = set(dagop.revdescendants(repo, include, False))
1414 descendants = set(dagop.revdescendants(repo, include, False))
1415 exclude = [rev for rev in cl.headrevs()
1415 exclude = [rev for rev in cl.headrevs()
1416 if not rev in descendants and not rev in include]
1416 if not rev in descendants and not rev in include]
1417 else:
1417 else:
1418 exclude = getset(repo, fullreposet(repo), args[1])
1418 exclude = getset(repo, fullreposet(repo), args[1])
1419
1419
1420 results = set(cl.findmissingrevs(common=exclude, heads=include))
1420 results = set(cl.findmissingrevs(common=exclude, heads=include))
1421 # XXX we should turn this into a baseset instead of a set, smartset may do
1421 # XXX we should turn this into a baseset instead of a set, smartset may do
1422 # some optimizations from the fact this is a baseset.
1422 # some optimizations from the fact this is a baseset.
1423 return subset & results
1423 return subset & results
1424
1424
1425 @predicate('origin([set])', safe=True)
1425 @predicate('origin([set])', safe=True)
1426 def origin(repo, subset, x):
1426 def origin(repo, subset, x):
1427 """
1427 """
1428 Changesets that were specified as a source for the grafts, transplants or
1428 Changesets that were specified as a source for the grafts, transplants or
1429 rebases that created the given revisions. Omitting the optional set is the
1429 rebases that created the given revisions. Omitting the optional set is the
1430 same as passing all(). If a changeset created by these operations is itself
1430 same as passing all(). If a changeset created by these operations is itself
1431 specified as a source for one of these operations, only the source changeset
1431 specified as a source for one of these operations, only the source changeset
1432 for the first operation is selected.
1432 for the first operation is selected.
1433 """
1433 """
1434 if x is not None:
1434 if x is not None:
1435 dests = getset(repo, fullreposet(repo), x)
1435 dests = getset(repo, fullreposet(repo), x)
1436 else:
1436 else:
1437 dests = fullreposet(repo)
1437 dests = fullreposet(repo)
1438
1438
1439 def _firstsrc(rev):
1439 def _firstsrc(rev):
1440 src = _getrevsource(repo, rev)
1440 src = _getrevsource(repo, rev)
1441 if src is None:
1441 if src is None:
1442 return None
1442 return None
1443
1443
1444 while True:
1444 while True:
1445 prev = _getrevsource(repo, src)
1445 prev = _getrevsource(repo, src)
1446
1446
1447 if prev is None:
1447 if prev is None:
1448 return src
1448 return src
1449 src = prev
1449 src = prev
1450
1450
1451 o = {_firstsrc(r) for r in dests}
1451 o = {_firstsrc(r) for r in dests}
1452 o -= {None}
1452 o -= {None}
1453 # XXX we should turn this into a baseset instead of a set, smartset may do
1453 # XXX we should turn this into a baseset instead of a set, smartset may do
1454 # some optimizations from the fact this is a baseset.
1454 # some optimizations from the fact this is a baseset.
1455 return subset & o
1455 return subset & o
1456
1456
1457 @predicate('outgoing([path])', safe=False, weight=10)
1457 @predicate('outgoing([path])', safe=False, weight=10)
1458 def outgoing(repo, subset, x):
1458 def outgoing(repo, subset, x):
1459 """Changesets not found in the specified destination repository, or the
1459 """Changesets not found in the specified destination repository, or the
1460 default push location.
1460 default push location.
1461 """
1461 """
1462 # Avoid cycles.
1462 # Avoid cycles.
1463 from . import (
1463 from . import (
1464 discovery,
1464 discovery,
1465 hg,
1465 hg,
1466 )
1466 )
1467 # i18n: "outgoing" is a keyword
1467 # i18n: "outgoing" is a keyword
1468 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1468 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1469 # i18n: "outgoing" is a keyword
1469 # i18n: "outgoing" is a keyword
1470 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1470 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1471 if not dest:
1471 if not dest:
1472 # ui.paths.getpath() explicitly tests for None, not just a boolean
1472 # ui.paths.getpath() explicitly tests for None, not just a boolean
1473 dest = None
1473 dest = None
1474 path = repo.ui.paths.getpath(dest, default=('default-push', 'default'))
1474 path = repo.ui.paths.getpath(dest, default=('default-push', 'default'))
1475 if not path:
1475 if not path:
1476 raise error.Abort(_('default repository not configured!'),
1476 raise error.Abort(_('default repository not configured!'),
1477 hint=_("see 'hg help config.paths'"))
1477 hint=_("see 'hg help config.paths'"))
1478 dest = path.pushloc or path.loc
1478 dest = path.pushloc or path.loc
1479 branches = path.branch, []
1479 branches = path.branch, []
1480
1480
1481 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1481 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1482 if revs:
1482 if revs:
1483 revs = [repo.lookup(rev) for rev in revs]
1483 revs = [repo.lookup(rev) for rev in revs]
1484 other = hg.peer(repo, {}, dest)
1484 other = hg.peer(repo, {}, dest)
1485 repo.ui.pushbuffer()
1485 repo.ui.pushbuffer()
1486 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1486 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1487 repo.ui.popbuffer()
1487 repo.ui.popbuffer()
1488 cl = repo.changelog
1488 cl = repo.changelog
1489 o = {cl.rev(r) for r in outgoing.missing}
1489 o = {cl.rev(r) for r in outgoing.missing}
1490 return subset & o
1490 return subset & o
1491
1491
1492 @predicate('p1([set])', safe=True)
1492 @predicate('p1([set])', safe=True)
1493 def p1(repo, subset, x):
1493 def p1(repo, subset, x):
1494 """First parent of changesets in set, or the working directory.
1494 """First parent of changesets in set, or the working directory.
1495 """
1495 """
1496 if x is None:
1496 if x is None:
1497 p = repo[x].p1().rev()
1497 p = repo[x].p1().rev()
1498 if p >= 0:
1498 if p >= 0:
1499 return subset & baseset([p])
1499 return subset & baseset([p])
1500 return baseset()
1500 return baseset()
1501
1501
1502 ps = set()
1502 ps = set()
1503 cl = repo.changelog
1503 cl = repo.changelog
1504 for r in getset(repo, fullreposet(repo), x):
1504 for r in getset(repo, fullreposet(repo), x):
1505 try:
1505 try:
1506 ps.add(cl.parentrevs(r)[0])
1506 ps.add(cl.parentrevs(r)[0])
1507 except error.WdirUnsupported:
1507 except error.WdirUnsupported:
1508 ps.add(repo[r].parents()[0].rev())
1508 ps.add(repo[r].parents()[0].rev())
1509 ps -= {node.nullrev}
1509 ps -= {node.nullrev}
1510 # XXX we should turn this into a baseset instead of a set, smartset may do
1510 # XXX we should turn this into a baseset instead of a set, smartset may do
1511 # some optimizations from the fact this is a baseset.
1511 # some optimizations from the fact this is a baseset.
1512 return subset & ps
1512 return subset & ps
1513
1513
1514 @predicate('p2([set])', safe=True)
1514 @predicate('p2([set])', safe=True)
1515 def p2(repo, subset, x):
1515 def p2(repo, subset, x):
1516 """Second parent of changesets in set, or the working directory.
1516 """Second parent of changesets in set, or the working directory.
1517 """
1517 """
1518 if x is None:
1518 if x is None:
1519 ps = repo[x].parents()
1519 ps = repo[x].parents()
1520 try:
1520 try:
1521 p = ps[1].rev()
1521 p = ps[1].rev()
1522 if p >= 0:
1522 if p >= 0:
1523 return subset & baseset([p])
1523 return subset & baseset([p])
1524 return baseset()
1524 return baseset()
1525 except IndexError:
1525 except IndexError:
1526 return baseset()
1526 return baseset()
1527
1527
1528 ps = set()
1528 ps = set()
1529 cl = repo.changelog
1529 cl = repo.changelog
1530 for r in getset(repo, fullreposet(repo), x):
1530 for r in getset(repo, fullreposet(repo), x):
1531 try:
1531 try:
1532 ps.add(cl.parentrevs(r)[1])
1532 ps.add(cl.parentrevs(r)[1])
1533 except error.WdirUnsupported:
1533 except error.WdirUnsupported:
1534 parents = repo[r].parents()
1534 parents = repo[r].parents()
1535 if len(parents) == 2:
1535 if len(parents) == 2:
1536 ps.add(parents[1])
1536 ps.add(parents[1])
1537 ps -= {node.nullrev}
1537 ps -= {node.nullrev}
1538 # XXX we should turn this into a baseset instead of a set, smartset may do
1538 # XXX we should turn this into a baseset instead of a set, smartset may do
1539 # some optimizations from the fact this is a baseset.
1539 # some optimizations from the fact this is a baseset.
1540 return subset & ps
1540 return subset & ps
1541
1541
1542 def parentpost(repo, subset, x, order):
1542 def parentpost(repo, subset, x, order):
1543 return p1(repo, subset, x)
1543 return p1(repo, subset, x)
1544
1544
1545 @predicate('parents([set])', safe=True)
1545 @predicate('parents([set])', safe=True)
1546 def parents(repo, subset, x):
1546 def parents(repo, subset, x):
1547 """
1547 """
1548 The set of all parents for all changesets in set, or the working directory.
1548 The set of all parents for all changesets in set, or the working directory.
1549 """
1549 """
1550 if x is None:
1550 if x is None:
1551 ps = set(p.rev() for p in repo[x].parents())
1551 ps = set(p.rev() for p in repo[x].parents())
1552 else:
1552 else:
1553 ps = set()
1553 ps = set()
1554 cl = repo.changelog
1554 cl = repo.changelog
1555 up = ps.update
1555 up = ps.update
1556 parentrevs = cl.parentrevs
1556 parentrevs = cl.parentrevs
1557 for r in getset(repo, fullreposet(repo), x):
1557 for r in getset(repo, fullreposet(repo), x):
1558 try:
1558 try:
1559 up(parentrevs(r))
1559 up(parentrevs(r))
1560 except error.WdirUnsupported:
1560 except error.WdirUnsupported:
1561 up(p.rev() for p in repo[r].parents())
1561 up(p.rev() for p in repo[r].parents())
1562 ps -= {node.nullrev}
1562 ps -= {node.nullrev}
1563 return subset & ps
1563 return subset & ps
1564
1564
1565 def _phase(repo, subset, *targets):
1565 def _phase(repo, subset, *targets):
1566 """helper to select all rev in <targets> phases"""
1566 """helper to select all rev in <targets> phases"""
1567 return repo._phasecache.getrevset(repo, targets, subset)
1567 return repo._phasecache.getrevset(repo, targets, subset)
1568
1568
1569 @predicate('draft()', safe=True)
1569 @predicate('draft()', safe=True)
1570 def draft(repo, subset, x):
1570 def draft(repo, subset, x):
1571 """Changeset in draft phase."""
1571 """Changeset in draft phase."""
1572 # i18n: "draft" is a keyword
1572 # i18n: "draft" is a keyword
1573 getargs(x, 0, 0, _("draft takes no arguments"))
1573 getargs(x, 0, 0, _("draft takes no arguments"))
1574 target = phases.draft
1574 target = phases.draft
1575 return _phase(repo, subset, target)
1575 return _phase(repo, subset, target)
1576
1576
1577 @predicate('secret()', safe=True)
1577 @predicate('secret()', safe=True)
1578 def secret(repo, subset, x):
1578 def secret(repo, subset, x):
1579 """Changeset in secret phase."""
1579 """Changeset in secret phase."""
1580 # i18n: "secret" is a keyword
1580 # i18n: "secret" is a keyword
1581 getargs(x, 0, 0, _("secret takes no arguments"))
1581 getargs(x, 0, 0, _("secret takes no arguments"))
1582 target = phases.secret
1582 target = phases.secret
1583 return _phase(repo, subset, target)
1583 return _phase(repo, subset, target)
1584
1584
1585 @predicate('stack([revs])', safe=True)
1585 @predicate('stack([revs])', safe=True)
1586 def stack(repo, subset, x):
1586 def stack(repo, subset, x):
1587 """Experimental revset for the stack of changesets or working directory
1587 """Experimental revset for the stack of changesets or working directory
1588 parent. (EXPERIMENTAL)
1588 parent. (EXPERIMENTAL)
1589 """
1589 """
1590 if x is None:
1590 if x is None:
1591 stacks = stackmod.getstack(repo, x)
1591 stacks = stackmod.getstack(repo, x)
1592 else:
1592 else:
1593 stacks = smartset.baseset([])
1593 stacks = smartset.baseset([])
1594 for revision in getset(repo, fullreposet(repo), x):
1594 for revision in getset(repo, fullreposet(repo), x):
1595 currentstack = stackmod.getstack(repo, revision)
1595 currentstack = stackmod.getstack(repo, revision)
1596 stacks = stacks + currentstack
1596 stacks = stacks + currentstack
1597
1597
1598 return subset & stacks
1598 return subset & stacks
1599
1599
1600 def parentspec(repo, subset, x, n, order):
1600 def parentspec(repo, subset, x, n, order):
1601 """``set^0``
1601 """``set^0``
1602 The set.
1602 The set.
1603 ``set^1`` (or ``set^``), ``set^2``
1603 ``set^1`` (or ``set^``), ``set^2``
1604 First or second parent, respectively, of all changesets in set.
1604 First or second parent, respectively, of all changesets in set.
1605 """
1605 """
1606 try:
1606 try:
1607 n = int(n[1])
1607 n = int(n[1])
1608 if n not in (0, 1, 2):
1608 if n not in (0, 1, 2):
1609 raise ValueError
1609 raise ValueError
1610 except (TypeError, ValueError):
1610 except (TypeError, ValueError):
1611 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1611 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1612 ps = set()
1612 ps = set()
1613 cl = repo.changelog
1613 cl = repo.changelog
1614 for r in getset(repo, fullreposet(repo), x):
1614 for r in getset(repo, fullreposet(repo), x):
1615 if n == 0:
1615 if n == 0:
1616 ps.add(r)
1616 ps.add(r)
1617 elif n == 1:
1617 elif n == 1:
1618 try:
1618 try:
1619 ps.add(cl.parentrevs(r)[0])
1619 ps.add(cl.parentrevs(r)[0])
1620 except error.WdirUnsupported:
1620 except error.WdirUnsupported:
1621 ps.add(repo[r].parents()[0].rev())
1621 ps.add(repo[r].parents()[0].rev())
1622 else:
1622 else:
1623 try:
1623 try:
1624 parents = cl.parentrevs(r)
1624 parents = cl.parentrevs(r)
1625 if parents[1] != node.nullrev:
1625 if parents[1] != node.nullrev:
1626 ps.add(parents[1])
1626 ps.add(parents[1])
1627 except error.WdirUnsupported:
1627 except error.WdirUnsupported:
1628 parents = repo[r].parents()
1628 parents = repo[r].parents()
1629 if len(parents) == 2:
1629 if len(parents) == 2:
1630 ps.add(parents[1].rev())
1630 ps.add(parents[1].rev())
1631 return subset & ps
1631 return subset & ps
1632
1632
1633 @predicate('present(set)', safe=True, takeorder=True)
1633 @predicate('present(set)', safe=True, takeorder=True)
1634 def present(repo, subset, x, order):
1634 def present(repo, subset, x, order):
1635 """An empty set, if any revision in set isn't found; otherwise,
1635 """An empty set, if any revision in set isn't found; otherwise,
1636 all revisions in set.
1636 all revisions in set.
1637
1637
1638 If any of specified revisions is not present in the local repository,
1638 If any of specified revisions is not present in the local repository,
1639 the query is normally aborted. But this predicate allows the query
1639 the query is normally aborted. But this predicate allows the query
1640 to continue even in such cases.
1640 to continue even in such cases.
1641 """
1641 """
1642 try:
1642 try:
1643 return getset(repo, subset, x, order)
1643 return getset(repo, subset, x, order)
1644 except error.RepoLookupError:
1644 except error.RepoLookupError:
1645 return baseset()
1645 return baseset()
1646
1646
1647 # for internal use
1647 # for internal use
1648 @predicate('_notpublic', safe=True)
1648 @predicate('_notpublic', safe=True)
1649 def _notpublic(repo, subset, x):
1649 def _notpublic(repo, subset, x):
1650 getargs(x, 0, 0, "_notpublic takes no arguments")
1650 getargs(x, 0, 0, "_notpublic takes no arguments")
1651 return _phase(repo, subset, phases.draft, phases.secret)
1651 return _phase(repo, subset, phases.draft, phases.secret)
1652
1652
1653 # for internal use
1653 # for internal use
1654 @predicate('_phaseandancestors(phasename, set)', safe=True)
1654 @predicate('_phaseandancestors(phasename, set)', safe=True)
1655 def _phaseandancestors(repo, subset, x):
1655 def _phaseandancestors(repo, subset, x):
1656 # equivalent to (phasename() & ancestors(set)) but more efficient
1656 # equivalent to (phasename() & ancestors(set)) but more efficient
1657 # phasename could be one of 'draft', 'secret', or '_notpublic'
1657 # phasename could be one of 'draft', 'secret', or '_notpublic'
1658 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1658 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1659 phasename = getsymbol(args[0])
1659 phasename = getsymbol(args[0])
1660 s = getset(repo, fullreposet(repo), args[1])
1660 s = getset(repo, fullreposet(repo), args[1])
1661
1661
1662 draft = phases.draft
1662 draft = phases.draft
1663 secret = phases.secret
1663 secret = phases.secret
1664 phasenamemap = {
1664 phasenamemap = {
1665 '_notpublic': draft,
1665 '_notpublic': draft,
1666 'draft': draft, # follow secret's ancestors
1666 'draft': draft, # follow secret's ancestors
1667 'secret': secret,
1667 'secret': secret,
1668 }
1668 }
1669 if phasename not in phasenamemap:
1669 if phasename not in phasenamemap:
1670 raise error.ParseError('%r is not a valid phasename' % phasename)
1670 raise error.ParseError('%r is not a valid phasename' % phasename)
1671
1671
1672 minimalphase = phasenamemap[phasename]
1672 minimalphase = phasenamemap[phasename]
1673 getphase = repo._phasecache.phase
1673 getphase = repo._phasecache.phase
1674
1674
1675 def cutfunc(rev):
1675 def cutfunc(rev):
1676 return getphase(repo, rev) < minimalphase
1676 return getphase(repo, rev) < minimalphase
1677
1677
1678 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1678 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1679
1679
1680 if phasename == 'draft': # need to remove secret changesets
1680 if phasename == 'draft': # need to remove secret changesets
1681 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1681 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1682 return subset & revs
1682 return subset & revs
1683
1683
1684 @predicate('public()', safe=True)
1684 @predicate('public()', safe=True)
1685 def public(repo, subset, x):
1685 def public(repo, subset, x):
1686 """Changeset in public phase."""
1686 """Changeset in public phase."""
1687 # i18n: "public" is a keyword
1687 # i18n: "public" is a keyword
1688 getargs(x, 0, 0, _("public takes no arguments"))
1688 getargs(x, 0, 0, _("public takes no arguments"))
1689 return _phase(repo, subset, phases.public)
1689 return _phase(repo, subset, phases.public)
1690
1690
1691 @predicate('remote([id [,path]])', safe=False)
1691 @predicate('remote([id [,path]])', safe=False)
1692 def remote(repo, subset, x):
1692 def remote(repo, subset, x):
1693 """Local revision that corresponds to the given identifier in a
1693 """Local revision that corresponds to the given identifier in a
1694 remote repository, if present. Here, the '.' identifier is a
1694 remote repository, if present. Here, the '.' identifier is a
1695 synonym for the current local branch.
1695 synonym for the current local branch.
1696 """
1696 """
1697
1697
1698 from . import hg # avoid start-up nasties
1698 from . import hg # avoid start-up nasties
1699 # i18n: "remote" is a keyword
1699 # i18n: "remote" is a keyword
1700 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1700 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1701
1701
1702 q = '.'
1702 q = '.'
1703 if len(l) > 0:
1703 if len(l) > 0:
1704 # i18n: "remote" is a keyword
1704 # i18n: "remote" is a keyword
1705 q = getstring(l[0], _("remote requires a string id"))
1705 q = getstring(l[0], _("remote requires a string id"))
1706 if q == '.':
1706 if q == '.':
1707 q = repo['.'].branch()
1707 q = repo['.'].branch()
1708
1708
1709 dest = ''
1709 dest = ''
1710 if len(l) > 1:
1710 if len(l) > 1:
1711 # i18n: "remote" is a keyword
1711 # i18n: "remote" is a keyword
1712 dest = getstring(l[1], _("remote requires a repository path"))
1712 dest = getstring(l[1], _("remote requires a repository path"))
1713 dest = repo.ui.expandpath(dest or 'default')
1713 dest = repo.ui.expandpath(dest or 'default')
1714 dest, branches = hg.parseurl(dest)
1714 dest, branches = hg.parseurl(dest)
1715 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1715 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1716 if revs:
1716 if revs:
1717 revs = [repo.lookup(rev) for rev in revs]
1717 revs = [repo.lookup(rev) for rev in revs]
1718 other = hg.peer(repo, {}, dest)
1718 other = hg.peer(repo, {}, dest)
1719 n = other.lookup(q)
1719 n = other.lookup(q)
1720 if n in repo:
1720 if n in repo:
1721 r = repo[n].rev()
1721 r = repo[n].rev()
1722 if r in subset:
1722 if r in subset:
1723 return baseset([r])
1723 return baseset([r])
1724 return baseset()
1724 return baseset()
1725
1725
1726 @predicate('removes(pattern)', safe=True, weight=30)
1726 @predicate('removes(pattern)', safe=True, weight=30)
1727 def removes(repo, subset, x):
1727 def removes(repo, subset, x):
1728 """Changesets which remove files matching pattern.
1728 """Changesets which remove files matching pattern.
1729
1729
1730 The pattern without explicit kind like ``glob:`` is expected to be
1730 The pattern without explicit kind like ``glob:`` is expected to be
1731 relative to the current directory and match against a file or a
1731 relative to the current directory and match against a file or a
1732 directory.
1732 directory.
1733 """
1733 """
1734 # i18n: "removes" is a keyword
1734 # i18n: "removes" is a keyword
1735 pat = getstring(x, _("removes requires a pattern"))
1735 pat = getstring(x, _("removes requires a pattern"))
1736 return checkstatus(repo, subset, pat, 2)
1736 return checkstatus(repo, subset, pat, 2)
1737
1737
1738 @predicate('rev(number)', safe=True)
1738 @predicate('rev(number)', safe=True)
1739 def rev(repo, subset, x):
1739 def rev(repo, subset, x):
1740 """Revision with the given numeric identifier.
1740 """Revision with the given numeric identifier.
1741 """
1741 """
1742 # i18n: "rev" is a keyword
1742 # i18n: "rev" is a keyword
1743 l = getargs(x, 1, 1, _("rev requires one argument"))
1743 l = getargs(x, 1, 1, _("rev requires one argument"))
1744 try:
1744 try:
1745 # i18n: "rev" is a keyword
1745 # i18n: "rev" is a keyword
1746 l = int(getstring(l[0], _("rev requires a number")))
1746 l = int(getstring(l[0], _("rev requires a number")))
1747 except (TypeError, ValueError):
1747 except (TypeError, ValueError):
1748 # i18n: "rev" is a keyword
1748 # i18n: "rev" is a keyword
1749 raise error.ParseError(_("rev expects a number"))
1749 raise error.ParseError(_("rev expects a number"))
1750 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1750 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1751 return baseset()
1751 return baseset()
1752 return subset & baseset([l])
1752 return subset & baseset([l])
1753
1753
1754 @predicate('matching(revision [, field])', safe=True)
1754 @predicate('matching(revision [, field])', safe=True)
1755 def matching(repo, subset, x):
1755 def matching(repo, subset, x):
1756 """Changesets in which a given set of fields match the set of fields in the
1756 """Changesets in which a given set of fields match the set of fields in the
1757 selected revision or set.
1757 selected revision or set.
1758
1758
1759 To match more than one field pass the list of fields to match separated
1759 To match more than one field pass the list of fields to match separated
1760 by spaces (e.g. ``author description``).
1760 by spaces (e.g. ``author description``).
1761
1761
1762 Valid fields are most regular revision fields and some special fields.
1762 Valid fields are most regular revision fields and some special fields.
1763
1763
1764 Regular revision fields are ``description``, ``author``, ``branch``,
1764 Regular revision fields are ``description``, ``author``, ``branch``,
1765 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1765 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1766 and ``diff``.
1766 and ``diff``.
1767 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1767 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1768 contents of the revision. Two revisions matching their ``diff`` will
1768 contents of the revision. Two revisions matching their ``diff`` will
1769 also match their ``files``.
1769 also match their ``files``.
1770
1770
1771 Special fields are ``summary`` and ``metadata``:
1771 Special fields are ``summary`` and ``metadata``:
1772 ``summary`` matches the first line of the description.
1772 ``summary`` matches the first line of the description.
1773 ``metadata`` is equivalent to matching ``description user date``
1773 ``metadata`` is equivalent to matching ``description user date``
1774 (i.e. it matches the main metadata fields).
1774 (i.e. it matches the main metadata fields).
1775
1775
1776 ``metadata`` is the default field which is used when no fields are
1776 ``metadata`` is the default field which is used when no fields are
1777 specified. You can match more than one field at a time.
1777 specified. You can match more than one field at a time.
1778 """
1778 """
1779 # i18n: "matching" is a keyword
1779 # i18n: "matching" is a keyword
1780 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1780 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1781
1781
1782 revs = getset(repo, fullreposet(repo), l[0])
1782 revs = getset(repo, fullreposet(repo), l[0])
1783
1783
1784 fieldlist = ['metadata']
1784 fieldlist = ['metadata']
1785 if len(l) > 1:
1785 if len(l) > 1:
1786 fieldlist = getstring(l[1],
1786 fieldlist = getstring(l[1],
1787 # i18n: "matching" is a keyword
1787 # i18n: "matching" is a keyword
1788 _("matching requires a string "
1788 _("matching requires a string "
1789 "as its second argument")).split()
1789 "as its second argument")).split()
1790
1790
1791 # Make sure that there are no repeated fields,
1791 # Make sure that there are no repeated fields,
1792 # expand the 'special' 'metadata' field type
1792 # expand the 'special' 'metadata' field type
1793 # and check the 'files' whenever we check the 'diff'
1793 # and check the 'files' whenever we check the 'diff'
1794 fields = []
1794 fields = []
1795 for field in fieldlist:
1795 for field in fieldlist:
1796 if field == 'metadata':
1796 if field == 'metadata':
1797 fields += ['user', 'description', 'date']
1797 fields += ['user', 'description', 'date']
1798 elif field == 'diff':
1798 elif field == 'diff':
1799 # a revision matching the diff must also match the files
1799 # a revision matching the diff must also match the files
1800 # since matching the diff is very costly, make sure to
1800 # since matching the diff is very costly, make sure to
1801 # also match the files first
1801 # also match the files first
1802 fields += ['files', 'diff']
1802 fields += ['files', 'diff']
1803 else:
1803 else:
1804 if field == 'author':
1804 if field == 'author':
1805 field = 'user'
1805 field = 'user'
1806 fields.append(field)
1806 fields.append(field)
1807 fields = set(fields)
1807 fields = set(fields)
1808 if 'summary' in fields and 'description' in fields:
1808 if 'summary' in fields and 'description' in fields:
1809 # If a revision matches its description it also matches its summary
1809 # If a revision matches its description it also matches its summary
1810 fields.discard('summary')
1810 fields.discard('summary')
1811
1811
1812 # We may want to match more than one field
1812 # We may want to match more than one field
1813 # Not all fields take the same amount of time to be matched
1813 # Not all fields take the same amount of time to be matched
1814 # Sort the selected fields in order of increasing matching cost
1814 # Sort the selected fields in order of increasing matching cost
1815 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1815 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1816 'files', 'description', 'substate', 'diff']
1816 'files', 'description', 'substate', 'diff']
1817 def fieldkeyfunc(f):
1817 def fieldkeyfunc(f):
1818 try:
1818 try:
1819 return fieldorder.index(f)
1819 return fieldorder.index(f)
1820 except ValueError:
1820 except ValueError:
1821 # assume an unknown field is very costly
1821 # assume an unknown field is very costly
1822 return len(fieldorder)
1822 return len(fieldorder)
1823 fields = list(fields)
1823 fields = list(fields)
1824 fields.sort(key=fieldkeyfunc)
1824 fields.sort(key=fieldkeyfunc)
1825
1825
1826 # Each field will be matched with its own "getfield" function
1826 # Each field will be matched with its own "getfield" function
1827 # which will be added to the getfieldfuncs array of functions
1827 # which will be added to the getfieldfuncs array of functions
1828 getfieldfuncs = []
1828 getfieldfuncs = []
1829 _funcs = {
1829 _funcs = {
1830 'user': lambda r: repo[r].user(),
1830 'user': lambda r: repo[r].user(),
1831 'branch': lambda r: repo[r].branch(),
1831 'branch': lambda r: repo[r].branch(),
1832 'date': lambda r: repo[r].date(),
1832 'date': lambda r: repo[r].date(),
1833 'description': lambda r: repo[r].description(),
1833 'description': lambda r: repo[r].description(),
1834 'files': lambda r: repo[r].files(),
1834 'files': lambda r: repo[r].files(),
1835 'parents': lambda r: repo[r].parents(),
1835 'parents': lambda r: repo[r].parents(),
1836 'phase': lambda r: repo[r].phase(),
1836 'phase': lambda r: repo[r].phase(),
1837 'substate': lambda r: repo[r].substate,
1837 'substate': lambda r: repo[r].substate,
1838 'summary': lambda r: repo[r].description().splitlines()[0],
1838 'summary': lambda r: repo[r].description().splitlines()[0],
1839 'diff': lambda r: list(repo[r].diff(
1839 'diff': lambda r: list(repo[r].diff(
1840 opts=diffutil.diffallopts(repo.ui, {'git': True}))),
1840 opts=diffutil.diffallopts(repo.ui, {'git': True}))),
1841 }
1841 }
1842 for info in fields:
1842 for info in fields:
1843 getfield = _funcs.get(info, None)
1843 getfield = _funcs.get(info, None)
1844 if getfield is None:
1844 if getfield is None:
1845 raise error.ParseError(
1845 raise error.ParseError(
1846 # i18n: "matching" is a keyword
1846 # i18n: "matching" is a keyword
1847 _("unexpected field name passed to matching: %s") % info)
1847 _("unexpected field name passed to matching: %s") % info)
1848 getfieldfuncs.append(getfield)
1848 getfieldfuncs.append(getfield)
1849 # convert the getfield array of functions into a "getinfo" function
1849 # convert the getfield array of functions into a "getinfo" function
1850 # which returns an array of field values (or a single value if there
1850 # which returns an array of field values (or a single value if there
1851 # is only one field to match)
1851 # is only one field to match)
1852 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1852 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1853
1853
1854 def matches(x):
1854 def matches(x):
1855 for rev in revs:
1855 for rev in revs:
1856 target = getinfo(rev)
1856 target = getinfo(rev)
1857 match = True
1857 match = True
1858 for n, f in enumerate(getfieldfuncs):
1858 for n, f in enumerate(getfieldfuncs):
1859 if target[n] != f(x):
1859 if target[n] != f(x):
1860 match = False
1860 match = False
1861 if match:
1861 if match:
1862 return True
1862 return True
1863 return False
1863 return False
1864
1864
1865 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1865 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1866
1866
1867 @predicate('reverse(set)', safe=True, takeorder=True, weight=0)
1867 @predicate('reverse(set)', safe=True, takeorder=True, weight=0)
1868 def reverse(repo, subset, x, order):
1868 def reverse(repo, subset, x, order):
1869 """Reverse order of set.
1869 """Reverse order of set.
1870 """
1870 """
1871 l = getset(repo, subset, x, order)
1871 l = getset(repo, subset, x, order)
1872 if order == defineorder:
1872 if order == defineorder:
1873 l.reverse()
1873 l.reverse()
1874 return l
1874 return l
1875
1875
1876 @predicate('roots(set)', safe=True)
1876 @predicate('roots(set)', safe=True)
1877 def roots(repo, subset, x):
1877 def roots(repo, subset, x):
1878 """Changesets in set with no parent changeset in set.
1878 """Changesets in set with no parent changeset in set.
1879 """
1879 """
1880 s = getset(repo, fullreposet(repo), x)
1880 s = getset(repo, fullreposet(repo), x)
1881 parents = repo.changelog.parentrevs
1881 parents = repo.changelog.parentrevs
1882 def filter(r):
1882 def filter(r):
1883 for p in parents(r):
1883 for p in parents(r):
1884 if 0 <= p and p in s:
1884 if 0 <= p and p in s:
1885 return False
1885 return False
1886 return True
1886 return True
1887 return subset & s.filter(filter, condrepr='<roots>')
1887 return subset & s.filter(filter, condrepr='<roots>')
1888
1888
1889 _sortkeyfuncs = {
1889 _sortkeyfuncs = {
1890 'rev': lambda c: c.rev(),
1890 'rev': lambda c: c.rev(),
1891 'branch': lambda c: c.branch(),
1891 'branch': lambda c: c.branch(),
1892 'desc': lambda c: c.description(),
1892 'desc': lambda c: c.description(),
1893 'user': lambda c: c.user(),
1893 'user': lambda c: c.user(),
1894 'author': lambda c: c.user(),
1894 'author': lambda c: c.user(),
1895 'date': lambda c: c.date()[0],
1895 'date': lambda c: c.date()[0],
1896 }
1896 }
1897
1897
1898 def _getsortargs(x):
1898 def _getsortargs(x):
1899 """Parse sort options into (set, [(key, reverse)], opts)"""
1899 """Parse sort options into (set, [(key, reverse)], opts)"""
1900 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1900 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1901 if 'set' not in args:
1901 if 'set' not in args:
1902 # i18n: "sort" is a keyword
1902 # i18n: "sort" is a keyword
1903 raise error.ParseError(_('sort requires one or two arguments'))
1903 raise error.ParseError(_('sort requires one or two arguments'))
1904 keys = "rev"
1904 keys = "rev"
1905 if 'keys' in args:
1905 if 'keys' in args:
1906 # i18n: "sort" is a keyword
1906 # i18n: "sort" is a keyword
1907 keys = getstring(args['keys'], _("sort spec must be a string"))
1907 keys = getstring(args['keys'], _("sort spec must be a string"))
1908
1908
1909 keyflags = []
1909 keyflags = []
1910 for k in keys.split():
1910 for k in keys.split():
1911 fk = k
1911 fk = k
1912 reverse = (k.startswith('-'))
1912 reverse = (k.startswith('-'))
1913 if reverse:
1913 if reverse:
1914 k = k[1:]
1914 k = k[1:]
1915 if k not in _sortkeyfuncs and k != 'topo':
1915 if k not in _sortkeyfuncs and k != 'topo':
1916 raise error.ParseError(
1916 raise error.ParseError(
1917 _("unknown sort key %r") % pycompat.bytestr(fk))
1917 _("unknown sort key %r") % pycompat.bytestr(fk))
1918 keyflags.append((k, reverse))
1918 keyflags.append((k, reverse))
1919
1919
1920 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1920 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1921 # i18n: "topo" is a keyword
1921 # i18n: "topo" is a keyword
1922 raise error.ParseError(_('topo sort order cannot be combined '
1922 raise error.ParseError(_('topo sort order cannot be combined '
1923 'with other sort keys'))
1923 'with other sort keys'))
1924
1924
1925 opts = {}
1925 opts = {}
1926 if 'topo.firstbranch' in args:
1926 if 'topo.firstbranch' in args:
1927 if any(k == 'topo' for k, reverse in keyflags):
1927 if any(k == 'topo' for k, reverse in keyflags):
1928 opts['topo.firstbranch'] = args['topo.firstbranch']
1928 opts['topo.firstbranch'] = args['topo.firstbranch']
1929 else:
1929 else:
1930 # i18n: "topo" and "topo.firstbranch" are keywords
1930 # i18n: "topo" and "topo.firstbranch" are keywords
1931 raise error.ParseError(_('topo.firstbranch can only be used '
1931 raise error.ParseError(_('topo.firstbranch can only be used '
1932 'when using the topo sort key'))
1932 'when using the topo sort key'))
1933
1933
1934 return args['set'], keyflags, opts
1934 return args['set'], keyflags, opts
1935
1935
1936 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
1936 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
1937 weight=10)
1937 weight=10)
1938 def sort(repo, subset, x, order):
1938 def sort(repo, subset, x, order):
1939 """Sort set by keys. The default sort order is ascending, specify a key
1939 """Sort set by keys. The default sort order is ascending, specify a key
1940 as ``-key`` to sort in descending order.
1940 as ``-key`` to sort in descending order.
1941
1941
1942 The keys can be:
1942 The keys can be:
1943
1943
1944 - ``rev`` for the revision number,
1944 - ``rev`` for the revision number,
1945 - ``branch`` for the branch name,
1945 - ``branch`` for the branch name,
1946 - ``desc`` for the commit message (description),
1946 - ``desc`` for the commit message (description),
1947 - ``user`` for user name (``author`` can be used as an alias),
1947 - ``user`` for user name (``author`` can be used as an alias),
1948 - ``date`` for the commit date
1948 - ``date`` for the commit date
1949 - ``topo`` for a reverse topographical sort
1949 - ``topo`` for a reverse topographical sort
1950
1950
1951 The ``topo`` sort order cannot be combined with other sort keys. This sort
1951 The ``topo`` sort order cannot be combined with other sort keys. This sort
1952 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1952 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1953 specifies what topographical branches to prioritize in the sort.
1953 specifies what topographical branches to prioritize in the sort.
1954
1954
1955 """
1955 """
1956 s, keyflags, opts = _getsortargs(x)
1956 s, keyflags, opts = _getsortargs(x)
1957 revs = getset(repo, subset, s, order)
1957 revs = getset(repo, subset, s, order)
1958
1958
1959 if not keyflags or order != defineorder:
1959 if not keyflags or order != defineorder:
1960 return revs
1960 return revs
1961 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1961 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1962 revs.sort(reverse=keyflags[0][1])
1962 revs.sort(reverse=keyflags[0][1])
1963 return revs
1963 return revs
1964 elif keyflags[0][0] == "topo":
1964 elif keyflags[0][0] == "topo":
1965 firstbranch = ()
1965 firstbranch = ()
1966 if 'topo.firstbranch' in opts:
1966 if 'topo.firstbranch' in opts:
1967 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1967 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1968 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1968 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1969 firstbranch),
1969 firstbranch),
1970 istopo=True)
1970 istopo=True)
1971 if keyflags[0][1]:
1971 if keyflags[0][1]:
1972 revs.reverse()
1972 revs.reverse()
1973 return revs
1973 return revs
1974
1974
1975 # sort() is guaranteed to be stable
1975 # sort() is guaranteed to be stable
1976 ctxs = [repo[r] for r in revs]
1976 ctxs = [repo[r] for r in revs]
1977 for k, reverse in reversed(keyflags):
1977 for k, reverse in reversed(keyflags):
1978 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1978 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1979 return baseset([c.rev() for c in ctxs])
1979 return baseset([c.rev() for c in ctxs])
1980
1980
1981 @predicate('subrepo([pattern])')
1981 @predicate('subrepo([pattern])')
1982 def subrepo(repo, subset, x):
1982 def subrepo(repo, subset, x):
1983 """Changesets that add, modify or remove the given subrepo. If no subrepo
1983 """Changesets that add, modify or remove the given subrepo. If no subrepo
1984 pattern is named, any subrepo changes are returned.
1984 pattern is named, any subrepo changes are returned.
1985 """
1985 """
1986 # i18n: "subrepo" is a keyword
1986 # i18n: "subrepo" is a keyword
1987 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1987 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1988 pat = None
1988 pat = None
1989 if len(args) != 0:
1989 if len(args) != 0:
1990 pat = getstring(args[0], _("subrepo requires a pattern"))
1990 pat = getstring(args[0], _("subrepo requires a pattern"))
1991
1991
1992 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1992 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1993
1993
1994 def submatches(names):
1994 def submatches(names):
1995 k, p, m = stringutil.stringmatcher(pat)
1995 k, p, m = stringutil.stringmatcher(pat)
1996 for name in names:
1996 for name in names:
1997 if m(name):
1997 if m(name):
1998 yield name
1998 yield name
1999
1999
2000 def matches(x):
2000 def matches(x):
2001 c = repo[x]
2001 c = repo[x]
2002 s = repo.status(c.p1().node(), c.node(), match=m)
2002 s = repo.status(c.p1().node(), c.node(), match=m)
2003
2003
2004 if pat is None:
2004 if pat is None:
2005 return s.added or s.modified or s.removed
2005 return s.added or s.modified or s.removed
2006
2006
2007 if s.added:
2007 if s.added:
2008 return any(submatches(c.substate.keys()))
2008 return any(submatches(c.substate.keys()))
2009
2009
2010 if s.modified:
2010 if s.modified:
2011 subs = set(c.p1().substate.keys())
2011 subs = set(c.p1().substate.keys())
2012 subs.update(c.substate.keys())
2012 subs.update(c.substate.keys())
2013
2013
2014 for path in submatches(subs):
2014 for path in submatches(subs):
2015 if c.p1().substate.get(path) != c.substate.get(path):
2015 if c.p1().substate.get(path) != c.substate.get(path):
2016 return True
2016 return True
2017
2017
2018 if s.removed:
2018 if s.removed:
2019 return any(submatches(c.p1().substate.keys()))
2019 return any(submatches(c.p1().substate.keys()))
2020
2020
2021 return False
2021 return False
2022
2022
2023 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2023 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
2024
2024
2025 def _mapbynodefunc(repo, s, f):
2025 def _mapbynodefunc(repo, s, f):
2026 """(repo, smartset, [node] -> [node]) -> smartset
2026 """(repo, smartset, [node] -> [node]) -> smartset
2027
2027
2028 Helper method to map a smartset to another smartset given a function only
2028 Helper method to map a smartset to another smartset given a function only
2029 talking about nodes. Handles converting between rev numbers and nodes, and
2029 talking about nodes. Handles converting between rev numbers and nodes, and
2030 filtering.
2030 filtering.
2031 """
2031 """
2032 cl = repo.unfiltered().changelog
2032 cl = repo.unfiltered().changelog
2033 torev = cl.rev
2033 torev = cl.rev
2034 tonode = cl.node
2034 tonode = cl.node
2035 nodemap = cl.nodemap
2035 nodemap = cl.nodemap
2036 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
2036 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
2037 return smartset.baseset(result - repo.changelog.filteredrevs)
2037 return smartset.baseset(result - repo.changelog.filteredrevs)
2038
2038
2039 @predicate('successors(set)', safe=True)
2039 @predicate('successors(set)', safe=True)
2040 def successors(repo, subset, x):
2040 def successors(repo, subset, x):
2041 """All successors for set, including the given set themselves"""
2041 """All successors for set, including the given set themselves"""
2042 s = getset(repo, fullreposet(repo), x)
2042 s = getset(repo, fullreposet(repo), x)
2043 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
2043 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
2044 d = _mapbynodefunc(repo, s, f)
2044 d = _mapbynodefunc(repo, s, f)
2045 return subset & d
2045 return subset & d
2046
2046
2047 def _substringmatcher(pattern, casesensitive=True):
2047 def _substringmatcher(pattern, casesensitive=True):
2048 kind, pattern, matcher = stringutil.stringmatcher(
2048 kind, pattern, matcher = stringutil.stringmatcher(
2049 pattern, casesensitive=casesensitive)
2049 pattern, casesensitive=casesensitive)
2050 if kind == 'literal':
2050 if kind == 'literal':
2051 if not casesensitive:
2051 if not casesensitive:
2052 pattern = encoding.lower(pattern)
2052 pattern = encoding.lower(pattern)
2053 matcher = lambda s: pattern in encoding.lower(s)
2053 matcher = lambda s: pattern in encoding.lower(s)
2054 else:
2054 else:
2055 matcher = lambda s: pattern in s
2055 matcher = lambda s: pattern in s
2056 return kind, pattern, matcher
2056 return kind, pattern, matcher
2057
2057
2058 @predicate('tag([name])', safe=True)
2058 @predicate('tag([name])', safe=True)
2059 def tag(repo, subset, x):
2059 def tag(repo, subset, x):
2060 """The specified tag by name, or all tagged revisions if no name is given.
2060 """The specified tag by name, or all tagged revisions if no name is given.
2061
2061
2062 Pattern matching is supported for `name`. See
2062 Pattern matching is supported for `name`. See
2063 :hg:`help revisions.patterns`.
2063 :hg:`help revisions.patterns`.
2064 """
2064 """
2065 # i18n: "tag" is a keyword
2065 # i18n: "tag" is a keyword
2066 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2066 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
2067 cl = repo.changelog
2067 cl = repo.changelog
2068 if args:
2068 if args:
2069 pattern = getstring(args[0],
2069 pattern = getstring(args[0],
2070 # i18n: "tag" is a keyword
2070 # i18n: "tag" is a keyword
2071 _('the argument to tag must be a string'))
2071 _('the argument to tag must be a string'))
2072 kind, pattern, matcher = stringutil.stringmatcher(pattern)
2072 kind, pattern, matcher = stringutil.stringmatcher(pattern)
2073 if kind == 'literal':
2073 if kind == 'literal':
2074 # avoid resolving all tags
2074 # avoid resolving all tags
2075 tn = repo._tagscache.tags.get(pattern, None)
2075 tn = repo._tagscache.tags.get(pattern, None)
2076 if tn is None:
2076 if tn is None:
2077 raise error.RepoLookupError(_("tag '%s' does not exist")
2077 raise error.RepoLookupError(_("tag '%s' does not exist")
2078 % pattern)
2078 % pattern)
2079 s = {repo[tn].rev()}
2079 s = {repo[tn].rev()}
2080 else:
2080 else:
2081 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2081 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2082 else:
2082 else:
2083 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2083 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2084 return subset & s
2084 return subset & s
2085
2085
2086 @predicate('tagged', safe=True)
2086 @predicate('tagged', safe=True)
2087 def tagged(repo, subset, x):
2087 def tagged(repo, subset, x):
2088 return tag(repo, subset, x)
2088 return tag(repo, subset, x)
2089
2089
2090 @predicate('orphan()', safe=True)
2090 @predicate('orphan()', safe=True)
2091 def orphan(repo, subset, x):
2091 def orphan(repo, subset, x):
2092 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2092 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2093 """
2093 """
2094 # i18n: "orphan" is a keyword
2094 # i18n: "orphan" is a keyword
2095 getargs(x, 0, 0, _("orphan takes no arguments"))
2095 getargs(x, 0, 0, _("orphan takes no arguments"))
2096 orphan = obsmod.getrevs(repo, 'orphan')
2096 orphan = obsmod.getrevs(repo, 'orphan')
2097 return subset & orphan
2097 return subset & orphan
2098
2098
2099
2099
2100 @predicate('user(string)', safe=True, weight=10)
2100 @predicate('user(string)', safe=True, weight=10)
2101 def user(repo, subset, x):
2101 def user(repo, subset, x):
2102 """User name contains string. The match is case-insensitive.
2102 """User name contains string. The match is case-insensitive.
2103
2103
2104 Pattern matching is supported for `string`. See
2104 Pattern matching is supported for `string`. See
2105 :hg:`help revisions.patterns`.
2105 :hg:`help revisions.patterns`.
2106 """
2106 """
2107 return author(repo, subset, x)
2107 return author(repo, subset, x)
2108
2108
2109 @predicate('wdir()', safe=True, weight=0)
2109 @predicate('wdir()', safe=True, weight=0)
2110 def wdir(repo, subset, x):
2110 def wdir(repo, subset, x):
2111 """Working directory. (EXPERIMENTAL)"""
2111 """Working directory. (EXPERIMENTAL)"""
2112 # i18n: "wdir" is a keyword
2112 # i18n: "wdir" is a keyword
2113 getargs(x, 0, 0, _("wdir takes no arguments"))
2113 getargs(x, 0, 0, _("wdir takes no arguments"))
2114 if node.wdirrev in subset or isinstance(subset, fullreposet):
2114 if node.wdirrev in subset or isinstance(subset, fullreposet):
2115 return baseset([node.wdirrev])
2115 return baseset([node.wdirrev])
2116 return baseset()
2116 return baseset()
2117
2117
2118 def _orderedlist(repo, subset, x):
2118 def _orderedlist(repo, subset, x):
2119 s = getstring(x, "internal error")
2119 s = getstring(x, "internal error")
2120 if not s:
2120 if not s:
2121 return baseset()
2121 return baseset()
2122 # remove duplicates here. it's difficult for caller to deduplicate sets
2122 # remove duplicates here. it's difficult for caller to deduplicate sets
2123 # because different symbols can point to the same rev.
2123 # because different symbols can point to the same rev.
2124 cl = repo.changelog
2124 cl = repo.changelog
2125 ls = []
2125 ls = []
2126 seen = set()
2126 seen = set()
2127 for t in s.split('\0'):
2127 for t in s.split('\0'):
2128 try:
2128 try:
2129 # fast path for integer revision
2129 # fast path for integer revision
2130 r = int(t)
2130 r = int(t)
2131 if ('%d' % r) != t or r not in cl:
2131 if ('%d' % r) != t or r not in cl:
2132 raise ValueError
2132 raise ValueError
2133 revs = [r]
2133 revs = [r]
2134 except ValueError:
2134 except ValueError:
2135 revs = stringset(repo, subset, t, defineorder)
2135 revs = stringset(repo, subset, t, defineorder)
2136
2136
2137 for r in revs:
2137 for r in revs:
2138 if r in seen:
2138 if r in seen:
2139 continue
2139 continue
2140 if (r in subset
2140 if (r in subset
2141 or r == node.nullrev and isinstance(subset, fullreposet)):
2141 or r == node.nullrev and isinstance(subset, fullreposet)):
2142 ls.append(r)
2142 ls.append(r)
2143 seen.add(r)
2143 seen.add(r)
2144 return baseset(ls)
2144 return baseset(ls)
2145
2145
2146 # for internal use
2146 # for internal use
2147 @predicate('_list', safe=True, takeorder=True)
2147 @predicate('_list', safe=True, takeorder=True)
2148 def _list(repo, subset, x, order):
2148 def _list(repo, subset, x, order):
2149 if order == followorder:
2149 if order == followorder:
2150 # slow path to take the subset order
2150 # slow path to take the subset order
2151 return subset & _orderedlist(repo, fullreposet(repo), x)
2151 return subset & _orderedlist(repo, fullreposet(repo), x)
2152 else:
2152 else:
2153 return _orderedlist(repo, subset, x)
2153 return _orderedlist(repo, subset, x)
2154
2154
2155 def _orderedintlist(repo, subset, x):
2155 def _orderedintlist(repo, subset, x):
2156 s = getstring(x, "internal error")
2156 s = getstring(x, "internal error")
2157 if not s:
2157 if not s:
2158 return baseset()
2158 return baseset()
2159 ls = [int(r) for r in s.split('\0')]
2159 ls = [int(r) for r in s.split('\0')]
2160 s = subset
2160 s = subset
2161 return baseset([r for r in ls if r in s])
2161 return baseset([r for r in ls if r in s])
2162
2162
2163 # for internal use
2163 # for internal use
2164 @predicate('_intlist', safe=True, takeorder=True, weight=0)
2164 @predicate('_intlist', safe=True, takeorder=True, weight=0)
2165 def _intlist(repo, subset, x, order):
2165 def _intlist(repo, subset, x, order):
2166 if order == followorder:
2166 if order == followorder:
2167 # slow path to take the subset order
2167 # slow path to take the subset order
2168 return subset & _orderedintlist(repo, fullreposet(repo), x)
2168 return subset & _orderedintlist(repo, fullreposet(repo), x)
2169 else:
2169 else:
2170 return _orderedintlist(repo, subset, x)
2170 return _orderedintlist(repo, subset, x)
2171
2171
2172 def _orderedhexlist(repo, subset, x):
2172 def _orderedhexlist(repo, subset, x):
2173 s = getstring(x, "internal error")
2173 s = getstring(x, "internal error")
2174 if not s:
2174 if not s:
2175 return baseset()
2175 return baseset()
2176 cl = repo.changelog
2176 cl = repo.changelog
2177 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2177 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2178 s = subset
2178 s = subset
2179 return baseset([r for r in ls if r in s])
2179 return baseset([r for r in ls if r in s])
2180
2180
2181 # for internal use
2181 # for internal use
2182 @predicate('_hexlist', safe=True, takeorder=True)
2182 @predicate('_hexlist', safe=True, takeorder=True)
2183 def _hexlist(repo, subset, x, order):
2183 def _hexlist(repo, subset, x, order):
2184 if order == followorder:
2184 if order == followorder:
2185 # slow path to take the subset order
2185 # slow path to take the subset order
2186 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2186 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2187 else:
2187 else:
2188 return _orderedhexlist(repo, subset, x)
2188 return _orderedhexlist(repo, subset, x)
2189
2189
2190 methods = {
2190 methods = {
2191 "range": rangeset,
2191 "range": rangeset,
2192 "rangeall": rangeall,
2192 "rangeall": rangeall,
2193 "rangepre": rangepre,
2193 "rangepre": rangepre,
2194 "rangepost": rangepost,
2194 "rangepost": rangepost,
2195 "dagrange": dagrange,
2195 "dagrange": dagrange,
2196 "string": stringset,
2196 "string": stringset,
2197 "symbol": stringset,
2197 "symbol": stringset,
2198 "and": andset,
2198 "and": andset,
2199 "andsmally": andsmallyset,
2199 "andsmally": andsmallyset,
2200 "or": orset,
2200 "or": orset,
2201 "not": notset,
2201 "not": notset,
2202 "difference": differenceset,
2202 "difference": differenceset,
2203 "relation": relationset,
2203 "relation": relationset,
2204 "relsubscript": relsubscriptset,
2204 "relsubscript": relsubscriptset,
2205 "subscript": subscriptset,
2205 "subscript": subscriptset,
2206 "list": listset,
2206 "list": listset,
2207 "keyvalue": keyvaluepair,
2207 "keyvalue": keyvaluepair,
2208 "func": func,
2208 "func": func,
2209 "ancestor": ancestorspec,
2209 "ancestor": ancestorspec,
2210 "parent": parentspec,
2210 "parent": parentspec,
2211 "parentpost": parentpost,
2211 "parentpost": parentpost,
2212 }
2212 }
2213
2213
2214 def lookupfn(repo):
2214 def lookupfn(repo):
2215 return lambda symbol: scmutil.isrevsymbol(repo, symbol)
2215 return lambda symbol: scmutil.isrevsymbol(repo, symbol)
2216
2216
2217 def match(ui, spec, lookup=None):
2217 def match(ui, spec, lookup=None):
2218 """Create a matcher for a single revision spec"""
2218 """Create a matcher for a single revision spec"""
2219 return matchany(ui, [spec], lookup=lookup)
2219 return matchany(ui, [spec], lookup=lookup)
2220
2220
2221 def matchany(ui, specs, lookup=None, localalias=None):
2221 def matchany(ui, specs, lookup=None, localalias=None):
2222 """Create a matcher that will include any revisions matching one of the
2222 """Create a matcher that will include any revisions matching one of the
2223 given specs
2223 given specs
2224
2224
2225 If lookup function is not None, the parser will first attempt to handle
2225 If lookup function is not None, the parser will first attempt to handle
2226 old-style ranges, which may contain operator characters.
2226 old-style ranges, which may contain operator characters.
2227
2227
2228 If localalias is not None, it is a dict {name: definitionstring}. It takes
2228 If localalias is not None, it is a dict {name: definitionstring}. It takes
2229 precedence over [revsetalias] config section.
2229 precedence over [revsetalias] config section.
2230 """
2230 """
2231 if not specs:
2231 if not specs:
2232 def mfunc(repo, subset=None):
2232 def mfunc(repo, subset=None):
2233 return baseset()
2233 return baseset()
2234 return mfunc
2234 return mfunc
2235 if not all(specs):
2235 if not all(specs):
2236 raise error.ParseError(_("empty query"))
2236 raise error.ParseError(_("empty query"))
2237 if len(specs) == 1:
2237 if len(specs) == 1:
2238 tree = revsetlang.parse(specs[0], lookup)
2238 tree = revsetlang.parse(specs[0], lookup)
2239 else:
2239 else:
2240 tree = ('or',
2240 tree = ('or',
2241 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2241 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2242
2242
2243 aliases = []
2243 aliases = []
2244 warn = None
2244 warn = None
2245 if ui:
2245 if ui:
2246 aliases.extend(ui.configitems('revsetalias'))
2246 aliases.extend(ui.configitems('revsetalias'))
2247 warn = ui.warn
2247 warn = ui.warn
2248 if localalias:
2248 if localalias:
2249 aliases.extend(localalias.items())
2249 aliases.extend(localalias.items())
2250 if aliases:
2250 if aliases:
2251 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2251 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2252 tree = revsetlang.foldconcat(tree)
2252 tree = revsetlang.foldconcat(tree)
2253 tree = revsetlang.analyze(tree)
2253 tree = revsetlang.analyze(tree)
2254 tree = revsetlang.optimize(tree)
2254 tree = revsetlang.optimize(tree)
2255 return makematcher(tree)
2255 return makematcher(tree)
2256
2256
2257 def makematcher(tree):
2257 def makematcher(tree):
2258 """Create a matcher from an evaluatable tree"""
2258 """Create a matcher from an evaluatable tree"""
2259 def mfunc(repo, subset=None, order=None):
2259 def mfunc(repo, subset=None, order=None):
2260 if order is None:
2260 if order is None:
2261 if subset is None:
2261 if subset is None:
2262 order = defineorder # 'x'
2262 order = defineorder # 'x'
2263 else:
2263 else:
2264 order = followorder # 'subset & x'
2264 order = followorder # 'subset & x'
2265 if subset is None:
2265 if subset is None:
2266 subset = fullreposet(repo)
2266 subset = fullreposet(repo)
2267 return getset(repo, subset, tree, order)
2267 return getset(repo, subset, tree, order)
2268 return mfunc
2268 return mfunc
2269
2269
2270 def loadpredicate(ui, extname, registrarobj):
2270 def loadpredicate(ui, extname, registrarobj):
2271 """Load revset predicates from specified registrarobj
2271 """Load revset predicates from specified registrarobj
2272 """
2272 """
2273 for name, func in registrarobj._table.iteritems():
2273 for name, func in registrarobj._table.iteritems():
2274 symbols[name] = func
2274 symbols[name] = func
2275 if func._safe:
2275 if func._safe:
2276 safesymbols.add(name)
2276 safesymbols.add(name)
2277
2277
2278 # load built-in predicates explicitly to setup safesymbols
2278 # load built-in predicates explicitly to setup safesymbols
2279 loadpredicate(None, None, predicate)
2279 loadpredicate(None, None, predicate)
2280
2280
2281 # tell hggettext to extract docstrings from these functions:
2281 # tell hggettext to extract docstrings from these functions:
2282 i18nfunctions = symbols.values()
2282 i18nfunctions = symbols.values()
@@ -1,743 +1,743 b''
1 $ fileset() {
1 $ fileset() {
2 > hg debugfileset --all-files "$@"
2 > hg debugfileset --all-files "$@"
3 > }
3 > }
4
4
5 $ hg init repo
5 $ hg init repo
6 $ cd repo
6 $ cd repo
7 $ echo a > a1
7 $ echo a > a1
8 $ echo a > a2
8 $ echo a > a2
9 $ echo b > b1
9 $ echo b > b1
10 $ echo b > b2
10 $ echo b > b2
11 $ hg ci -Am addfiles
11 $ hg ci -Am addfiles
12 adding a1
12 adding a1
13 adding a2
13 adding a2
14 adding b1
14 adding b1
15 adding b2
15 adding b2
16
16
17 Test operators and basic patterns
17 Test operators and basic patterns
18
18
19 $ fileset -v a1
19 $ fileset -v a1
20 (symbol 'a1')
20 (symbol 'a1')
21 * matcher:
21 * matcher:
22 <patternmatcher patterns='(?:a1$)'>
22 <patternmatcher patterns='(?:a1$)'>
23 a1
23 a1
24 $ fileset -v 'a*'
24 $ fileset -v 'a*'
25 (symbol 'a*')
25 (symbol 'a*')
26 * matcher:
26 * matcher:
27 <patternmatcher patterns='(?:a[^/]*$)'>
27 <patternmatcher patterns='(?:a[^/]*$)'>
28 a1
28 a1
29 a2
29 a2
30 $ fileset -v '"re:a\d"'
30 $ fileset -v '"re:a\d"'
31 (string 're:a\\d')
31 (string 're:a\\d')
32 * matcher:
32 * matcher:
33 <patternmatcher patterns='(?:a\\d)'>
33 <patternmatcher patterns='(?:a\\d)'>
34 a1
34 a1
35 a2
35 a2
36 $ fileset -v '!re:"a\d"'
36 $ fileset -v '!re:"a\d"'
37 (not
37 (not
38 (kindpat
38 (kindpat
39 (symbol 're')
39 (symbol 're')
40 (string 'a\\d')))
40 (string 'a\\d')))
41 * matcher:
41 * matcher:
42 <predicatenmatcher
42 <predicatenmatcher
43 pred=<not
43 pred=<not
44 <patternmatcher patterns='(?:a\\d)'>>>
44 <patternmatcher patterns='(?:a\\d)'>>>
45 b1
45 b1
46 b2
46 b2
47 $ fileset -v 'path:a1 or glob:b?'
47 $ fileset -v 'path:a1 or glob:b?'
48 (or
48 (or
49 (kindpat
49 (kindpat
50 (symbol 'path')
50 (symbol 'path')
51 (symbol 'a1'))
51 (symbol 'a1'))
52 (kindpat
52 (kindpat
53 (symbol 'glob')
53 (symbol 'glob')
54 (symbol 'b?')))
54 (symbol 'b?')))
55 * matcher:
55 * matcher:
56 <unionmatcher matchers=[
56 <unionmatcher matchers=[
57 <patternmatcher patterns='(?:a1(?:/|$))'>,
57 <patternmatcher patterns='(?:a1(?:/|$))'>,
58 <patternmatcher patterns='(?:b.$)'>]>
58 <patternmatcher patterns='(?:b.$)'>]>
59 a1
59 a1
60 b1
60 b1
61 b2
61 b2
62 $ fileset -v --no-show-matcher 'a1 or a2'
62 $ fileset -v --no-show-matcher 'a1 or a2'
63 (or
63 (or
64 (symbol 'a1')
64 (symbol 'a1')
65 (symbol 'a2'))
65 (symbol 'a2'))
66 a1
66 a1
67 a2
67 a2
68 $ fileset 'a1 | a2'
68 $ fileset 'a1 | a2'
69 a1
69 a1
70 a2
70 a2
71 $ fileset 'a* and "*1"'
71 $ fileset 'a* and "*1"'
72 a1
72 a1
73 $ fileset 'a* & "*1"'
73 $ fileset 'a* & "*1"'
74 a1
74 a1
75 $ fileset 'not (r"a*")'
75 $ fileset 'not (r"a*")'
76 b1
76 b1
77 b2
77 b2
78 $ fileset '! ("a*")'
78 $ fileset '! ("a*")'
79 b1
79 b1
80 b2
80 b2
81 $ fileset 'a* - a1'
81 $ fileset 'a* - a1'
82 a2
82 a2
83 $ fileset 'a_b'
83 $ fileset 'a_b'
84 $ fileset '"\xy"'
84 $ fileset '"\xy"'
85 hg: parse error: invalid \x escape* (glob)
85 hg: parse error: invalid \x escape* (glob)
86 [255]
86 [255]
87
87
88 Test invalid syntax
88 Test invalid syntax
89
89
90 $ fileset -v '"added"()'
90 $ fileset -v '"added"()'
91 (func
91 (func
92 (string 'added')
92 (string 'added')
93 None)
93 None)
94 hg: parse error: not a symbol
94 hg: parse error: not a symbol
95 [255]
95 [255]
96 $ fileset -v '()()'
96 $ fileset -v '()()'
97 (func
97 (func
98 (group
98 (group
99 None)
99 None)
100 None)
100 None)
101 hg: parse error: not a symbol
101 hg: parse error: not a symbol
102 [255]
102 [255]
103 $ fileset -v -- '-x'
103 $ fileset -v -- '-x'
104 (negate
104 (negate
105 (symbol 'x'))
105 (symbol 'x'))
106 hg: parse error: can't use negate operator in this context
106 hg: parse error: can't use negate operator in this context
107 [255]
107 [255]
108 $ fileset -v -- '-()'
108 $ fileset -v -- '-()'
109 (negate
109 (negate
110 (group
110 (group
111 None))
111 None))
112 hg: parse error: can't use negate operator in this context
112 hg: parse error: can't use negate operator in this context
113 [255]
113 [255]
114 $ fileset -p parsed 'a, b, c'
114 $ fileset -p parsed 'a, b, c'
115 * parsed:
115 * parsed:
116 (list
116 (list
117 (symbol 'a')
117 (symbol 'a')
118 (symbol 'b')
118 (symbol 'b')
119 (symbol 'c'))
119 (symbol 'c'))
120 hg: parse error: can't use a list in this context
120 hg: parse error: can't use a list in this context
121 (see hg help "filesets.x or y")
121 (see 'hg help "filesets.x or y"')
122 [255]
122 [255]
123
123
124 $ fileset '"path":.'
124 $ fileset '"path":.'
125 hg: parse error: not a symbol
125 hg: parse error: not a symbol
126 [255]
126 [255]
127 $ fileset 'path:foo bar'
127 $ fileset 'path:foo bar'
128 hg: parse error at 9: invalid token
128 hg: parse error at 9: invalid token
129 [255]
129 [255]
130 $ fileset 'foo:bar:baz'
130 $ fileset 'foo:bar:baz'
131 hg: parse error: not a symbol
131 hg: parse error: not a symbol
132 [255]
132 [255]
133 $ fileset 'foo:bar()'
133 $ fileset 'foo:bar()'
134 hg: parse error: pattern must be a string
134 hg: parse error: pattern must be a string
135 [255]
135 [255]
136 $ fileset 'foo:bar'
136 $ fileset 'foo:bar'
137 hg: parse error: invalid pattern kind: foo
137 hg: parse error: invalid pattern kind: foo
138 [255]
138 [255]
139
139
140 Show parsed tree at stages:
140 Show parsed tree at stages:
141
141
142 $ fileset -p unknown a
142 $ fileset -p unknown a
143 abort: invalid stage name: unknown
143 abort: invalid stage name: unknown
144 [255]
144 [255]
145
145
146 $ fileset -p parsed 'path:a1 or glob:b?'
146 $ fileset -p parsed 'path:a1 or glob:b?'
147 * parsed:
147 * parsed:
148 (or
148 (or
149 (kindpat
149 (kindpat
150 (symbol 'path')
150 (symbol 'path')
151 (symbol 'a1'))
151 (symbol 'a1'))
152 (kindpat
152 (kindpat
153 (symbol 'glob')
153 (symbol 'glob')
154 (symbol 'b?')))
154 (symbol 'b?')))
155 a1
155 a1
156 b1
156 b1
157 b2
157 b2
158
158
159 $ fileset -p all -s 'a1 or a2 or (grep("b") & clean())'
159 $ fileset -p all -s 'a1 or a2 or (grep("b") & clean())'
160 * parsed:
160 * parsed:
161 (or
161 (or
162 (symbol 'a1')
162 (symbol 'a1')
163 (symbol 'a2')
163 (symbol 'a2')
164 (group
164 (group
165 (and
165 (and
166 (func
166 (func
167 (symbol 'grep')
167 (symbol 'grep')
168 (string 'b'))
168 (string 'b'))
169 (func
169 (func
170 (symbol 'clean')
170 (symbol 'clean')
171 None))))
171 None))))
172 * matcher:
172 * matcher:
173 <unionmatcher matchers=[
173 <unionmatcher matchers=[
174 <patternmatcher patterns='(?:a1$)'>,
174 <patternmatcher patterns='(?:a1$)'>,
175 <patternmatcher patterns='(?:a2$)'>,
175 <patternmatcher patterns='(?:a2$)'>,
176 <intersectionmatcher
176 <intersectionmatcher
177 m1=<predicatenmatcher pred=grep('b')>,
177 m1=<predicatenmatcher pred=grep('b')>,
178 m2=<predicatenmatcher pred=clean>>]>
178 m2=<predicatenmatcher pred=clean>>]>
179 a1
179 a1
180 a2
180 a2
181 b1
181 b1
182 b2
182 b2
183
183
184 Test files status
184 Test files status
185
185
186 $ rm a1
186 $ rm a1
187 $ hg rm a2
187 $ hg rm a2
188 $ echo b >> b2
188 $ echo b >> b2
189 $ hg cp b1 c1
189 $ hg cp b1 c1
190 $ echo c > c2
190 $ echo c > c2
191 $ echo c > c3
191 $ echo c > c3
192 $ cat > .hgignore <<EOF
192 $ cat > .hgignore <<EOF
193 > \.hgignore
193 > \.hgignore
194 > 2$
194 > 2$
195 > EOF
195 > EOF
196 $ fileset 'modified()'
196 $ fileset 'modified()'
197 b2
197 b2
198 $ fileset 'added()'
198 $ fileset 'added()'
199 c1
199 c1
200 $ fileset 'removed()'
200 $ fileset 'removed()'
201 a2
201 a2
202 $ fileset 'deleted()'
202 $ fileset 'deleted()'
203 a1
203 a1
204 $ fileset 'missing()'
204 $ fileset 'missing()'
205 a1
205 a1
206 $ fileset 'unknown()'
206 $ fileset 'unknown()'
207 c3
207 c3
208 $ fileset 'ignored()'
208 $ fileset 'ignored()'
209 .hgignore
209 .hgignore
210 c2
210 c2
211 $ fileset 'hgignore()'
211 $ fileset 'hgignore()'
212 .hgignore
212 .hgignore
213 a2
213 a2
214 b2
214 b2
215 c2
215 c2
216 $ fileset 'clean()'
216 $ fileset 'clean()'
217 b1
217 b1
218 $ fileset 'copied()'
218 $ fileset 'copied()'
219 c1
219 c1
220
220
221 Test files status in different revisions
221 Test files status in different revisions
222
222
223 $ hg status -m
223 $ hg status -m
224 M b2
224 M b2
225 $ fileset -r0 'revs("wdir()", modified())' --traceback
225 $ fileset -r0 'revs("wdir()", modified())' --traceback
226 b2
226 b2
227 $ hg status -a
227 $ hg status -a
228 A c1
228 A c1
229 $ fileset -r0 'revs("wdir()", added())'
229 $ fileset -r0 'revs("wdir()", added())'
230 c1
230 c1
231 $ hg status --change 0 -a
231 $ hg status --change 0 -a
232 A a1
232 A a1
233 A a2
233 A a2
234 A b1
234 A b1
235 A b2
235 A b2
236 $ hg status -mru
236 $ hg status -mru
237 M b2
237 M b2
238 R a2
238 R a2
239 ? c3
239 ? c3
240 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
240 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
241 a2
241 a2
242 b2
242 b2
243 $ fileset -r0 'added() or revs("wdir()", added())'
243 $ fileset -r0 'added() or revs("wdir()", added())'
244 a1
244 a1
245 a2
245 a2
246 b1
246 b1
247 b2
247 b2
248 c1
248 c1
249
249
250 Test files properties
250 Test files properties
251
251
252 >>> open('bin', 'wb').write(b'\0a') and None
252 >>> open('bin', 'wb').write(b'\0a') and None
253 $ fileset 'binary()'
253 $ fileset 'binary()'
254 bin
254 bin
255 $ fileset 'binary() and unknown()'
255 $ fileset 'binary() and unknown()'
256 bin
256 bin
257 $ echo '^bin$' >> .hgignore
257 $ echo '^bin$' >> .hgignore
258 $ fileset 'binary() and ignored()'
258 $ fileset 'binary() and ignored()'
259 bin
259 bin
260 $ hg add bin
260 $ hg add bin
261 $ fileset 'binary()'
261 $ fileset 'binary()'
262 bin
262 bin
263
263
264 $ fileset 'grep("b{1}")'
264 $ fileset 'grep("b{1}")'
265 .hgignore
265 .hgignore
266 b1
266 b1
267 b2
267 b2
268 c1
268 c1
269 $ fileset 'grep("missingparens(")'
269 $ fileset 'grep("missingparens(")'
270 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \)).* (re)
270 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \)).* (re)
271 [255]
271 [255]
272
272
273 #if execbit
273 #if execbit
274 $ chmod +x b2
274 $ chmod +x b2
275 $ fileset 'exec()'
275 $ fileset 'exec()'
276 b2
276 b2
277 #endif
277 #endif
278
278
279 #if symlink
279 #if symlink
280 $ ln -s b2 b2link
280 $ ln -s b2 b2link
281 $ fileset 'symlink() and unknown()'
281 $ fileset 'symlink() and unknown()'
282 b2link
282 b2link
283 $ hg add b2link
283 $ hg add b2link
284 #endif
284 #endif
285
285
286 #if no-windows
286 #if no-windows
287 $ echo foo > con.xml
287 $ echo foo > con.xml
288 $ fileset 'not portable()'
288 $ fileset 'not portable()'
289 con.xml
289 con.xml
290 $ hg --config ui.portablefilenames=ignore add con.xml
290 $ hg --config ui.portablefilenames=ignore add con.xml
291 #endif
291 #endif
292
292
293 >>> open('1k', 'wb').write(b' '*1024) and None
293 >>> open('1k', 'wb').write(b' '*1024) and None
294 >>> open('2k', 'wb').write(b' '*2048) and None
294 >>> open('2k', 'wb').write(b' '*2048) and None
295 $ hg add 1k 2k
295 $ hg add 1k 2k
296 $ fileset 'size("bar")'
296 $ fileset 'size("bar")'
297 hg: parse error: couldn't parse size: bar
297 hg: parse error: couldn't parse size: bar
298 [255]
298 [255]
299 $ fileset '(1k, 2k)'
299 $ fileset '(1k, 2k)'
300 hg: parse error: can't use a list in this context
300 hg: parse error: can't use a list in this context
301 (see hg help "filesets.x or y")
301 (see 'hg help "filesets.x or y"')
302 [255]
302 [255]
303 $ fileset 'size(1k)'
303 $ fileset 'size(1k)'
304 1k
304 1k
305 $ fileset '(1k or 2k) and size("< 2k")'
305 $ fileset '(1k or 2k) and size("< 2k")'
306 1k
306 1k
307 $ fileset '(1k or 2k) and size("<=2k")'
307 $ fileset '(1k or 2k) and size("<=2k")'
308 1k
308 1k
309 2k
309 2k
310 $ fileset '(1k or 2k) and size("> 1k")'
310 $ fileset '(1k or 2k) and size("> 1k")'
311 2k
311 2k
312 $ fileset '(1k or 2k) and size(">=1K")'
312 $ fileset '(1k or 2k) and size(">=1K")'
313 1k
313 1k
314 2k
314 2k
315 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
315 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
316 1k
316 1k
317 $ fileset 'size("1M")'
317 $ fileset 'size("1M")'
318 $ fileset 'size("1 GB")'
318 $ fileset 'size("1 GB")'
319
319
320 Test merge states
320 Test merge states
321
321
322 $ hg ci -m manychanges
322 $ hg ci -m manychanges
323 $ hg file -r . 'set:copied() & modified()'
323 $ hg file -r . 'set:copied() & modified()'
324 [1]
324 [1]
325 $ hg up -C 0
325 $ hg up -C 0
326 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
326 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
327 $ echo c >> b2
327 $ echo c >> b2
328 $ hg ci -m diverging b2
328 $ hg ci -m diverging b2
329 created new head
329 created new head
330 $ fileset 'resolved()'
330 $ fileset 'resolved()'
331 $ fileset 'unresolved()'
331 $ fileset 'unresolved()'
332 $ hg merge
332 $ hg merge
333 merging b2
333 merging b2
334 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
334 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
335 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
335 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
336 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
336 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
337 [1]
337 [1]
338 $ fileset 'resolved()'
338 $ fileset 'resolved()'
339 $ fileset 'unresolved()'
339 $ fileset 'unresolved()'
340 b2
340 b2
341 $ echo e > b2
341 $ echo e > b2
342 $ hg resolve -m b2
342 $ hg resolve -m b2
343 (no more unresolved files)
343 (no more unresolved files)
344 $ fileset 'resolved()'
344 $ fileset 'resolved()'
345 b2
345 b2
346 $ fileset 'unresolved()'
346 $ fileset 'unresolved()'
347 $ hg ci -m merge
347 $ hg ci -m merge
348
348
349 Test subrepo predicate
349 Test subrepo predicate
350
350
351 $ hg init sub
351 $ hg init sub
352 $ echo a > sub/suba
352 $ echo a > sub/suba
353 $ hg -R sub add sub/suba
353 $ hg -R sub add sub/suba
354 $ hg -R sub ci -m sub
354 $ hg -R sub ci -m sub
355 $ echo 'sub = sub' > .hgsub
355 $ echo 'sub = sub' > .hgsub
356 $ hg init sub2
356 $ hg init sub2
357 $ echo b > sub2/b
357 $ echo b > sub2/b
358 $ hg -R sub2 ci -Am sub2
358 $ hg -R sub2 ci -Am sub2
359 adding b
359 adding b
360 $ echo 'sub2 = sub2' >> .hgsub
360 $ echo 'sub2 = sub2' >> .hgsub
361 $ fileset 'subrepo()'
361 $ fileset 'subrepo()'
362 $ hg add .hgsub
362 $ hg add .hgsub
363 $ fileset 'subrepo()'
363 $ fileset 'subrepo()'
364 sub
364 sub
365 sub2
365 sub2
366 $ fileset 'subrepo("sub")'
366 $ fileset 'subrepo("sub")'
367 sub
367 sub
368 $ fileset 'subrepo("glob:*")'
368 $ fileset 'subrepo("glob:*")'
369 sub
369 sub
370 sub2
370 sub2
371 $ hg ci -m subrepo
371 $ hg ci -m subrepo
372
372
373 Test that .hgsubstate is updated as appropriate during a conversion. The
373 Test that .hgsubstate is updated as appropriate during a conversion. The
374 saverev property is enough to alter the hashes of the subrepo.
374 saverev property is enough to alter the hashes of the subrepo.
375
375
376 $ hg init ../converted
376 $ hg init ../converted
377 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
377 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
378 > sub ../converted/sub
378 > sub ../converted/sub
379 initializing destination ../converted/sub repository
379 initializing destination ../converted/sub repository
380 scanning source...
380 scanning source...
381 sorting...
381 sorting...
382 converting...
382 converting...
383 0 sub
383 0 sub
384 $ hg clone -U sub2 ../converted/sub2
384 $ hg clone -U sub2 ../converted/sub2
385 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
385 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
386 > . ../converted
386 > . ../converted
387 scanning source...
387 scanning source...
388 sorting...
388 sorting...
389 converting...
389 converting...
390 4 addfiles
390 4 addfiles
391 3 manychanges
391 3 manychanges
392 2 diverging
392 2 diverging
393 1 merge
393 1 merge
394 0 subrepo
394 0 subrepo
395 no ".hgsubstate" updates will be made for "sub2"
395 no ".hgsubstate" updates will be made for "sub2"
396 $ hg up -q -R ../converted -r tip
396 $ hg up -q -R ../converted -r tip
397 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
397 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
398 a
398 a
399 b
399 b
400 $ oldnode=`hg log -r tip -T "{node}\n"`
400 $ oldnode=`hg log -r tip -T "{node}\n"`
401 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
401 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
402 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
402 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
403
403
404 Test with a revision
404 Test with a revision
405
405
406 $ hg log -G --template '{rev} {desc}\n'
406 $ hg log -G --template '{rev} {desc}\n'
407 @ 4 subrepo
407 @ 4 subrepo
408 |
408 |
409 o 3 merge
409 o 3 merge
410 |\
410 |\
411 | o 2 diverging
411 | o 2 diverging
412 | |
412 | |
413 o | 1 manychanges
413 o | 1 manychanges
414 |/
414 |/
415 o 0 addfiles
415 o 0 addfiles
416
416
417 $ echo unknown > unknown
417 $ echo unknown > unknown
418 $ fileset -r1 'modified()'
418 $ fileset -r1 'modified()'
419 b2
419 b2
420 $ fileset -r1 'added() and c1'
420 $ fileset -r1 'added() and c1'
421 c1
421 c1
422 $ fileset -r1 'removed()'
422 $ fileset -r1 'removed()'
423 a2
423 a2
424 $ fileset -r1 'deleted()'
424 $ fileset -r1 'deleted()'
425 $ fileset -r1 'unknown()'
425 $ fileset -r1 'unknown()'
426 $ fileset -r1 'ignored()'
426 $ fileset -r1 'ignored()'
427 $ fileset -r1 'hgignore()'
427 $ fileset -r1 'hgignore()'
428 .hgignore
428 .hgignore
429 a2
429 a2
430 b2
430 b2
431 bin
431 bin
432 c2
432 c2
433 sub2
433 sub2
434 $ fileset -r1 'binary()'
434 $ fileset -r1 'binary()'
435 bin
435 bin
436 $ fileset -r1 'size(1k)'
436 $ fileset -r1 'size(1k)'
437 1k
437 1k
438 $ fileset -r3 'resolved()'
438 $ fileset -r3 'resolved()'
439 $ fileset -r3 'unresolved()'
439 $ fileset -r3 'unresolved()'
440
440
441 #if execbit
441 #if execbit
442 $ fileset -r1 'exec()'
442 $ fileset -r1 'exec()'
443 b2
443 b2
444 #endif
444 #endif
445
445
446 #if symlink
446 #if symlink
447 $ fileset -r1 'symlink()'
447 $ fileset -r1 'symlink()'
448 b2link
448 b2link
449 #endif
449 #endif
450
450
451 #if no-windows
451 #if no-windows
452 $ fileset -r1 'not portable()'
452 $ fileset -r1 'not portable()'
453 con.xml
453 con.xml
454 $ hg forget 'con.xml'
454 $ hg forget 'con.xml'
455 #endif
455 #endif
456
456
457 $ fileset -r4 'subrepo("re:su.*")'
457 $ fileset -r4 'subrepo("re:su.*")'
458 sub
458 sub
459 sub2
459 sub2
460 $ fileset -r4 'subrepo(re:su.*)'
460 $ fileset -r4 'subrepo(re:su.*)'
461 sub
461 sub
462 sub2
462 sub2
463 $ fileset -r4 'subrepo("sub")'
463 $ fileset -r4 'subrepo("sub")'
464 sub
464 sub
465 $ fileset -r4 'b2 or c1'
465 $ fileset -r4 'b2 or c1'
466 b2
466 b2
467 c1
467 c1
468
468
469 >>> open('dos', 'wb').write(b"dos\r\n") and None
469 >>> open('dos', 'wb').write(b"dos\r\n") and None
470 >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
470 >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
471 >>> open('mac', 'wb').write(b"mac\r") and None
471 >>> open('mac', 'wb').write(b"mac\r") and None
472 $ hg add dos mixed mac
472 $ hg add dos mixed mac
473
473
474 (remove a1, to examine safety of 'eol' on removed files)
474 (remove a1, to examine safety of 'eol' on removed files)
475 $ rm a1
475 $ rm a1
476
476
477 $ fileset 'eol(dos)'
477 $ fileset 'eol(dos)'
478 dos
478 dos
479 mixed
479 mixed
480 $ fileset 'eol(unix)'
480 $ fileset 'eol(unix)'
481 .hgignore
481 .hgignore
482 .hgsub
482 .hgsub
483 .hgsubstate
483 .hgsubstate
484 b1
484 b1
485 b2
485 b2
486 b2.orig
486 b2.orig
487 c1
487 c1
488 c2
488 c2
489 c3
489 c3
490 con.xml (no-windows !)
490 con.xml (no-windows !)
491 mixed
491 mixed
492 unknown
492 unknown
493 $ fileset 'eol(mac)'
493 $ fileset 'eol(mac)'
494 mac
494 mac
495
495
496 Test safety of 'encoding' on removed files
496 Test safety of 'encoding' on removed files
497
497
498 $ fileset 'encoding("ascii")'
498 $ fileset 'encoding("ascii")'
499 .hgignore
499 .hgignore
500 .hgsub
500 .hgsub
501 .hgsubstate
501 .hgsubstate
502 1k
502 1k
503 2k
503 2k
504 b1
504 b1
505 b2
505 b2
506 b2.orig
506 b2.orig
507 b2link (symlink !)
507 b2link (symlink !)
508 bin
508 bin
509 c1
509 c1
510 c2
510 c2
511 c3
511 c3
512 con.xml (no-windows !)
512 con.xml (no-windows !)
513 dos
513 dos
514 mac
514 mac
515 mixed
515 mixed
516 unknown
516 unknown
517
517
518 Test 'revs(...)'
518 Test 'revs(...)'
519 ================
519 ================
520
520
521 small reminder of the repository state
521 small reminder of the repository state
522
522
523 $ hg log -G
523 $ hg log -G
524 @ changeset: 4:* (glob)
524 @ changeset: 4:* (glob)
525 | tag: tip
525 | tag: tip
526 | user: test
526 | user: test
527 | date: Thu Jan 01 00:00:00 1970 +0000
527 | date: Thu Jan 01 00:00:00 1970 +0000
528 | summary: subrepo
528 | summary: subrepo
529 |
529 |
530 o changeset: 3:* (glob)
530 o changeset: 3:* (glob)
531 |\ parent: 2:55b05bdebf36
531 |\ parent: 2:55b05bdebf36
532 | | parent: 1:* (glob)
532 | | parent: 1:* (glob)
533 | | user: test
533 | | user: test
534 | | date: Thu Jan 01 00:00:00 1970 +0000
534 | | date: Thu Jan 01 00:00:00 1970 +0000
535 | | summary: merge
535 | | summary: merge
536 | |
536 | |
537 | o changeset: 2:55b05bdebf36
537 | o changeset: 2:55b05bdebf36
538 | | parent: 0:8a9576c51c1f
538 | | parent: 0:8a9576c51c1f
539 | | user: test
539 | | user: test
540 | | date: Thu Jan 01 00:00:00 1970 +0000
540 | | date: Thu Jan 01 00:00:00 1970 +0000
541 | | summary: diverging
541 | | summary: diverging
542 | |
542 | |
543 o | changeset: 1:* (glob)
543 o | changeset: 1:* (glob)
544 |/ user: test
544 |/ user: test
545 | date: Thu Jan 01 00:00:00 1970 +0000
545 | date: Thu Jan 01 00:00:00 1970 +0000
546 | summary: manychanges
546 | summary: manychanges
547 |
547 |
548 o changeset: 0:8a9576c51c1f
548 o changeset: 0:8a9576c51c1f
549 user: test
549 user: test
550 date: Thu Jan 01 00:00:00 1970 +0000
550 date: Thu Jan 01 00:00:00 1970 +0000
551 summary: addfiles
551 summary: addfiles
552
552
553 $ hg status --change 0
553 $ hg status --change 0
554 A a1
554 A a1
555 A a2
555 A a2
556 A b1
556 A b1
557 A b2
557 A b2
558 $ hg status --change 1
558 $ hg status --change 1
559 M b2
559 M b2
560 A 1k
560 A 1k
561 A 2k
561 A 2k
562 A b2link (no-windows !)
562 A b2link (no-windows !)
563 A bin
563 A bin
564 A c1
564 A c1
565 A con.xml (no-windows !)
565 A con.xml (no-windows !)
566 R a2
566 R a2
567 $ hg status --change 2
567 $ hg status --change 2
568 M b2
568 M b2
569 $ hg status --change 3
569 $ hg status --change 3
570 M b2
570 M b2
571 A 1k
571 A 1k
572 A 2k
572 A 2k
573 A b2link (no-windows !)
573 A b2link (no-windows !)
574 A bin
574 A bin
575 A c1
575 A c1
576 A con.xml (no-windows !)
576 A con.xml (no-windows !)
577 R a2
577 R a2
578 $ hg status --change 4
578 $ hg status --change 4
579 A .hgsub
579 A .hgsub
580 A .hgsubstate
580 A .hgsubstate
581 $ hg status
581 $ hg status
582 A dos
582 A dos
583 A mac
583 A mac
584 A mixed
584 A mixed
585 R con.xml (no-windows !)
585 R con.xml (no-windows !)
586 ! a1
586 ! a1
587 ? b2.orig
587 ? b2.orig
588 ? c3
588 ? c3
589 ? unknown
589 ? unknown
590
590
591 Test files at -r0 should be filtered by files at wdir
591 Test files at -r0 should be filtered by files at wdir
592 -----------------------------------------------------
592 -----------------------------------------------------
593
593
594 $ fileset -r0 'tracked() and revs("wdir()", tracked())'
594 $ fileset -r0 'tracked() and revs("wdir()", tracked())'
595 a1
595 a1
596 b1
596 b1
597 b2
597 b2
598
598
599 Test that "revs()" work at all
599 Test that "revs()" work at all
600 ------------------------------
600 ------------------------------
601
601
602 $ fileset "revs('2', modified())"
602 $ fileset "revs('2', modified())"
603 b2
603 b2
604
604
605 Test that "revs()" work for file missing in the working copy/current context
605 Test that "revs()" work for file missing in the working copy/current context
606 ----------------------------------------------------------------------------
606 ----------------------------------------------------------------------------
607
607
608 (a2 not in working copy)
608 (a2 not in working copy)
609
609
610 $ fileset "revs('0', added())"
610 $ fileset "revs('0', added())"
611 a1
611 a1
612 a2
612 a2
613 b1
613 b1
614 b2
614 b2
615
615
616 (none of the file exist in "0")
616 (none of the file exist in "0")
617
617
618 $ fileset -r 0 "revs('4', added())"
618 $ fileset -r 0 "revs('4', added())"
619 .hgsub
619 .hgsub
620 .hgsubstate
620 .hgsubstate
621
621
622 Call with empty revset
622 Call with empty revset
623 --------------------------
623 --------------------------
624
624
625 $ fileset "revs('2-2', modified())"
625 $ fileset "revs('2-2', modified())"
626
626
627 Call with revset matching multiple revs
627 Call with revset matching multiple revs
628 ---------------------------------------
628 ---------------------------------------
629
629
630 $ fileset "revs('0+4', added())"
630 $ fileset "revs('0+4', added())"
631 .hgsub
631 .hgsub
632 .hgsubstate
632 .hgsubstate
633 a1
633 a1
634 a2
634 a2
635 b1
635 b1
636 b2
636 b2
637
637
638 overlapping set
638 overlapping set
639
639
640 $ fileset "revs('1+2', modified())"
640 $ fileset "revs('1+2', modified())"
641 b2
641 b2
642
642
643 test 'status(...)'
643 test 'status(...)'
644 =================
644 =================
645
645
646 Simple case
646 Simple case
647 -----------
647 -----------
648
648
649 $ fileset "status(3, 4, added())"
649 $ fileset "status(3, 4, added())"
650 .hgsub
650 .hgsub
651 .hgsubstate
651 .hgsubstate
652
652
653 use rev to restrict matched file
653 use rev to restrict matched file
654 -----------------------------------------
654 -----------------------------------------
655
655
656 $ hg status --removed --rev 0 --rev 1
656 $ hg status --removed --rev 0 --rev 1
657 R a2
657 R a2
658 $ fileset "status(0, 1, removed())"
658 $ fileset "status(0, 1, removed())"
659 a2
659 a2
660 $ fileset "tracked() and status(0, 1, removed())"
660 $ fileset "tracked() and status(0, 1, removed())"
661 $ fileset -r 4 "status(0, 1, removed())"
661 $ fileset -r 4 "status(0, 1, removed())"
662 a2
662 a2
663 $ fileset -r 4 "tracked() and status(0, 1, removed())"
663 $ fileset -r 4 "tracked() and status(0, 1, removed())"
664 $ fileset "revs('4', tracked() and status(0, 1, removed()))"
664 $ fileset "revs('4', tracked() and status(0, 1, removed()))"
665 $ fileset "revs('0', tracked() and status(0, 1, removed()))"
665 $ fileset "revs('0', tracked() and status(0, 1, removed()))"
666 a2
666 a2
667
667
668 check wdir()
668 check wdir()
669 ------------
669 ------------
670
670
671 $ hg status --removed --rev 4
671 $ hg status --removed --rev 4
672 R con.xml (no-windows !)
672 R con.xml (no-windows !)
673 $ fileset "status(4, 'wdir()', removed())"
673 $ fileset "status(4, 'wdir()', removed())"
674 con.xml (no-windows !)
674 con.xml (no-windows !)
675
675
676 $ hg status --removed --rev 2
676 $ hg status --removed --rev 2
677 R a2
677 R a2
678 $ fileset "status('2', 'wdir()', removed())"
678 $ fileset "status('2', 'wdir()', removed())"
679 a2
679 a2
680
680
681 test backward status
681 test backward status
682 --------------------
682 --------------------
683
683
684 $ hg status --removed --rev 0 --rev 4
684 $ hg status --removed --rev 0 --rev 4
685 R a2
685 R a2
686 $ hg status --added --rev 4 --rev 0
686 $ hg status --added --rev 4 --rev 0
687 A a2
687 A a2
688 $ fileset "status(4, 0, added())"
688 $ fileset "status(4, 0, added())"
689 a2
689 a2
690
690
691 test cross branch status
691 test cross branch status
692 ------------------------
692 ------------------------
693
693
694 $ hg status --added --rev 1 --rev 2
694 $ hg status --added --rev 1 --rev 2
695 A a2
695 A a2
696 $ fileset "status(1, 2, added())"
696 $ fileset "status(1, 2, added())"
697 a2
697 a2
698
698
699 test with multi revs revset
699 test with multi revs revset
700 ---------------------------
700 ---------------------------
701 $ hg status --added --rev 0:1 --rev 3:4
701 $ hg status --added --rev 0:1 --rev 3:4
702 A .hgsub
702 A .hgsub
703 A .hgsubstate
703 A .hgsubstate
704 A 1k
704 A 1k
705 A 2k
705 A 2k
706 A b2link (no-windows !)
706 A b2link (no-windows !)
707 A bin
707 A bin
708 A c1
708 A c1
709 A con.xml (no-windows !)
709 A con.xml (no-windows !)
710 $ fileset "status('0:1', '3:4', added())"
710 $ fileset "status('0:1', '3:4', added())"
711 .hgsub
711 .hgsub
712 .hgsubstate
712 .hgsubstate
713 1k
713 1k
714 2k
714 2k
715 b2link (no-windows !)
715 b2link (no-windows !)
716 bin
716 bin
717 c1
717 c1
718 con.xml (no-windows !)
718 con.xml (no-windows !)
719
719
720 tests with empty value
720 tests with empty value
721 ----------------------
721 ----------------------
722
722
723 Fully empty revset
723 Fully empty revset
724
724
725 $ fileset "status('', '4', added())"
725 $ fileset "status('', '4', added())"
726 hg: parse error: first argument to status must be a revision
726 hg: parse error: first argument to status must be a revision
727 [255]
727 [255]
728 $ fileset "status('2', '', added())"
728 $ fileset "status('2', '', added())"
729 hg: parse error: second argument to status must be a revision
729 hg: parse error: second argument to status must be a revision
730 [255]
730 [255]
731
731
732 Empty revset will error at the revset layer
732 Empty revset will error at the revset layer
733
733
734 $ fileset "status(' ', '4', added())"
734 $ fileset "status(' ', '4', added())"
735 hg: parse error at 1: not a prefix: end
735 hg: parse error at 1: not a prefix: end
736 (
736 (
737 ^ here)
737 ^ here)
738 [255]
738 [255]
739 $ fileset "status('2', ' ', added())"
739 $ fileset "status('2', ' ', added())"
740 hg: parse error at 1: not a prefix: end
740 hg: parse error at 1: not a prefix: end
741 (
741 (
742 ^ here)
742 ^ here)
743 [255]
743 [255]
@@ -1,1854 +1,1854 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3 $ cat >> $HGRCPATH << EOF
3 $ cat >> $HGRCPATH << EOF
4 > [extensions]
4 > [extensions]
5 > drawdag=$TESTDIR/drawdag.py
5 > drawdag=$TESTDIR/drawdag.py
6 > EOF
6 > EOF
7
7
8 $ try() {
8 $ try() {
9 > hg debugrevspec --debug "$@"
9 > hg debugrevspec --debug "$@"
10 > }
10 > }
11
11
12 $ log() {
12 $ log() {
13 > hg log --template '{rev}\n' -r "$1"
13 > hg log --template '{rev}\n' -r "$1"
14 > }
14 > }
15
15
16 $ hg init repo
16 $ hg init repo
17 $ cd repo
17 $ cd repo
18
18
19 $ echo a > a
19 $ echo a > a
20 $ hg branch a
20 $ hg branch a
21 marked working directory as branch a
21 marked working directory as branch a
22 (branches are permanent and global, did you want a bookmark?)
22 (branches are permanent and global, did you want a bookmark?)
23 $ hg ci -Aqm0
23 $ hg ci -Aqm0
24
24
25 $ echo b > b
25 $ echo b > b
26 $ hg branch b
26 $ hg branch b
27 marked working directory as branch b
27 marked working directory as branch b
28 $ hg ci -Aqm1
28 $ hg ci -Aqm1
29
29
30 $ rm a
30 $ rm a
31 $ hg branch a-b-c-
31 $ hg branch a-b-c-
32 marked working directory as branch a-b-c-
32 marked working directory as branch a-b-c-
33 $ hg ci -Aqm2 -u Bob
33 $ hg ci -Aqm2 -u Bob
34
34
35 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
35 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
36 2
36 2
37 $ hg log -r "extra('branch')" --template '{rev}\n'
37 $ hg log -r "extra('branch')" --template '{rev}\n'
38 0
38 0
39 1
39 1
40 2
40 2
41 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
41 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
42 0 a
42 0 a
43 2 a-b-c-
43 2 a-b-c-
44
44
45 $ hg co 1
45 $ hg co 1
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47 $ hg branch +a+b+c+
47 $ hg branch +a+b+c+
48 marked working directory as branch +a+b+c+
48 marked working directory as branch +a+b+c+
49 $ hg ci -Aqm3
49 $ hg ci -Aqm3
50
50
51 $ hg co 2 # interleave
51 $ hg co 2 # interleave
52 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
52 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
53 $ echo bb > b
53 $ echo bb > b
54 $ hg branch -- -a-b-c-
54 $ hg branch -- -a-b-c-
55 marked working directory as branch -a-b-c-
55 marked working directory as branch -a-b-c-
56 $ hg ci -Aqm4 -d "May 12 2005"
56 $ hg ci -Aqm4 -d "May 12 2005"
57
57
58 $ hg co 3
58 $ hg co 3
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 $ hg branch !a/b/c/
60 $ hg branch !a/b/c/
61 marked working directory as branch !a/b/c/
61 marked working directory as branch !a/b/c/
62 $ hg ci -Aqm"5 bug"
62 $ hg ci -Aqm"5 bug"
63
63
64 $ hg merge 4
64 $ hg merge 4
65 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
65 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
66 (branch merge, don't forget to commit)
66 (branch merge, don't forget to commit)
67 $ hg branch _a_b_c_
67 $ hg branch _a_b_c_
68 marked working directory as branch _a_b_c_
68 marked working directory as branch _a_b_c_
69 $ hg ci -Aqm"6 issue619"
69 $ hg ci -Aqm"6 issue619"
70
70
71 $ hg branch .a.b.c.
71 $ hg branch .a.b.c.
72 marked working directory as branch .a.b.c.
72 marked working directory as branch .a.b.c.
73 $ hg ci -Aqm7
73 $ hg ci -Aqm7
74
74
75 $ hg branch all
75 $ hg branch all
76 marked working directory as branch all
76 marked working directory as branch all
77
77
78 $ hg co 4
78 $ hg co 4
79 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
80 $ hg branch Γ©
80 $ hg branch Γ©
81 marked working directory as branch \xc3\xa9 (esc)
81 marked working directory as branch \xc3\xa9 (esc)
82 $ hg ci -Aqm9
82 $ hg ci -Aqm9
83
83
84 $ hg tag -r6 1.0
84 $ hg tag -r6 1.0
85 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
85 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
86
86
87 $ hg clone --quiet -U -r 7 . ../remote1
87 $ hg clone --quiet -U -r 7 . ../remote1
88 $ hg clone --quiet -U -r 8 . ../remote2
88 $ hg clone --quiet -U -r 8 . ../remote2
89 $ echo "[paths]" >> .hg/hgrc
89 $ echo "[paths]" >> .hg/hgrc
90 $ echo "default = ../remote1" >> .hg/hgrc
90 $ echo "default = ../remote1" >> .hg/hgrc
91
91
92 test subtracting something from an addset
92 test subtracting something from an addset
93
93
94 $ log '(outgoing() or removes(a)) - removes(a)'
94 $ log '(outgoing() or removes(a)) - removes(a)'
95 8
95 8
96 9
96 9
97
97
98 test intersecting something with an addset
98 test intersecting something with an addset
99
99
100 $ log 'parents(outgoing() or removes(a))'
100 $ log 'parents(outgoing() or removes(a))'
101 1
101 1
102 4
102 4
103 5
103 5
104 8
104 8
105
105
106 test that `or` operation combines elements in the right order:
106 test that `or` operation combines elements in the right order:
107
107
108 $ log '3:4 or 2:5'
108 $ log '3:4 or 2:5'
109 3
109 3
110 4
110 4
111 2
111 2
112 5
112 5
113 $ log '3:4 or 5:2'
113 $ log '3:4 or 5:2'
114 3
114 3
115 4
115 4
116 5
116 5
117 2
117 2
118 $ log 'sort(3:4 or 2:5)'
118 $ log 'sort(3:4 or 2:5)'
119 2
119 2
120 3
120 3
121 4
121 4
122 5
122 5
123 $ log 'sort(3:4 or 5:2)'
123 $ log 'sort(3:4 or 5:2)'
124 2
124 2
125 3
125 3
126 4
126 4
127 5
127 5
128
128
129 test that more than one `-r`s are combined in the right order and deduplicated:
129 test that more than one `-r`s are combined in the right order and deduplicated:
130
130
131 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
131 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
132 3
132 3
133 4
133 4
134 5
134 5
135 2
135 2
136 0
136 0
137 1
137 1
138
138
139 test that `or` operation skips duplicated revisions from right-hand side
139 test that `or` operation skips duplicated revisions from right-hand side
140
140
141 $ try 'reverse(1::5) or ancestors(4)'
141 $ try 'reverse(1::5) or ancestors(4)'
142 (or
142 (or
143 (list
143 (list
144 (func
144 (func
145 (symbol 'reverse')
145 (symbol 'reverse')
146 (dagrange
146 (dagrange
147 (symbol '1')
147 (symbol '1')
148 (symbol '5')))
148 (symbol '5')))
149 (func
149 (func
150 (symbol 'ancestors')
150 (symbol 'ancestors')
151 (symbol '4'))))
151 (symbol '4'))))
152 * set:
152 * set:
153 <addset
153 <addset
154 <baseset- [1, 3, 5]>,
154 <baseset- [1, 3, 5]>,
155 <generatorsetdesc+>>
155 <generatorsetdesc+>>
156 5
156 5
157 3
157 3
158 1
158 1
159 0
159 0
160 2
160 2
161 4
161 4
162 $ try 'sort(ancestors(4) or reverse(1::5))'
162 $ try 'sort(ancestors(4) or reverse(1::5))'
163 (func
163 (func
164 (symbol 'sort')
164 (symbol 'sort')
165 (or
165 (or
166 (list
166 (list
167 (func
167 (func
168 (symbol 'ancestors')
168 (symbol 'ancestors')
169 (symbol '4'))
169 (symbol '4'))
170 (func
170 (func
171 (symbol 'reverse')
171 (symbol 'reverse')
172 (dagrange
172 (dagrange
173 (symbol '1')
173 (symbol '1')
174 (symbol '5'))))))
174 (symbol '5'))))))
175 * set:
175 * set:
176 <addset+
176 <addset+
177 <generatorsetdesc+>,
177 <generatorsetdesc+>,
178 <baseset- [1, 3, 5]>>
178 <baseset- [1, 3, 5]>>
179 0
179 0
180 1
180 1
181 2
181 2
182 3
182 3
183 4
183 4
184 5
184 5
185
185
186 test optimization of trivial `or` operation
186 test optimization of trivial `or` operation
187
187
188 $ try --optimize '0|(1)|"2"|-2|tip|null'
188 $ try --optimize '0|(1)|"2"|-2|tip|null'
189 (or
189 (or
190 (list
190 (list
191 (symbol '0')
191 (symbol '0')
192 (group
192 (group
193 (symbol '1'))
193 (symbol '1'))
194 (string '2')
194 (string '2')
195 (negate
195 (negate
196 (symbol '2'))
196 (symbol '2'))
197 (symbol 'tip')
197 (symbol 'tip')
198 (symbol 'null')))
198 (symbol 'null')))
199 * optimized:
199 * optimized:
200 (func
200 (func
201 (symbol '_list')
201 (symbol '_list')
202 (string '0\x001\x002\x00-2\x00tip\x00null'))
202 (string '0\x001\x002\x00-2\x00tip\x00null'))
203 * set:
203 * set:
204 <baseset [0, 1, 2, 8, 9, -1]>
204 <baseset [0, 1, 2, 8, 9, -1]>
205 0
205 0
206 1
206 1
207 2
207 2
208 8
208 8
209 9
209 9
210 -1
210 -1
211
211
212 $ try --optimize '0|1|2:3'
212 $ try --optimize '0|1|2:3'
213 (or
213 (or
214 (list
214 (list
215 (symbol '0')
215 (symbol '0')
216 (symbol '1')
216 (symbol '1')
217 (range
217 (range
218 (symbol '2')
218 (symbol '2')
219 (symbol '3'))))
219 (symbol '3'))))
220 * optimized:
220 * optimized:
221 (or
221 (or
222 (list
222 (list
223 (func
223 (func
224 (symbol '_list')
224 (symbol '_list')
225 (string '0\x001'))
225 (string '0\x001'))
226 (range
226 (range
227 (symbol '2')
227 (symbol '2')
228 (symbol '3'))))
228 (symbol '3'))))
229 * set:
229 * set:
230 <addset
230 <addset
231 <baseset [0, 1]>,
231 <baseset [0, 1]>,
232 <spanset+ 2:4>>
232 <spanset+ 2:4>>
233 0
233 0
234 1
234 1
235 2
235 2
236 3
236 3
237
237
238 $ try --optimize '0:1|2|3:4|5|6'
238 $ try --optimize '0:1|2|3:4|5|6'
239 (or
239 (or
240 (list
240 (list
241 (range
241 (range
242 (symbol '0')
242 (symbol '0')
243 (symbol '1'))
243 (symbol '1'))
244 (symbol '2')
244 (symbol '2')
245 (range
245 (range
246 (symbol '3')
246 (symbol '3')
247 (symbol '4'))
247 (symbol '4'))
248 (symbol '5')
248 (symbol '5')
249 (symbol '6')))
249 (symbol '6')))
250 * optimized:
250 * optimized:
251 (or
251 (or
252 (list
252 (list
253 (range
253 (range
254 (symbol '0')
254 (symbol '0')
255 (symbol '1'))
255 (symbol '1'))
256 (symbol '2')
256 (symbol '2')
257 (range
257 (range
258 (symbol '3')
258 (symbol '3')
259 (symbol '4'))
259 (symbol '4'))
260 (func
260 (func
261 (symbol '_list')
261 (symbol '_list')
262 (string '5\x006'))))
262 (string '5\x006'))))
263 * set:
263 * set:
264 <addset
264 <addset
265 <addset
265 <addset
266 <spanset+ 0:2>,
266 <spanset+ 0:2>,
267 <baseset [2]>>,
267 <baseset [2]>>,
268 <addset
268 <addset
269 <spanset+ 3:5>,
269 <spanset+ 3:5>,
270 <baseset [5, 6]>>>
270 <baseset [5, 6]>>>
271 0
271 0
272 1
272 1
273 2
273 2
274 3
274 3
275 4
275 4
276 5
276 5
277 6
277 6
278
278
279 unoptimized `or` looks like this
279 unoptimized `or` looks like this
280
280
281 $ try --no-optimized -p analyzed '0|1|2|3|4'
281 $ try --no-optimized -p analyzed '0|1|2|3|4'
282 * analyzed:
282 * analyzed:
283 (or
283 (or
284 (list
284 (list
285 (symbol '0')
285 (symbol '0')
286 (symbol '1')
286 (symbol '1')
287 (symbol '2')
287 (symbol '2')
288 (symbol '3')
288 (symbol '3')
289 (symbol '4')))
289 (symbol '4')))
290 * set:
290 * set:
291 <addset
291 <addset
292 <addset
292 <addset
293 <baseset [0]>,
293 <baseset [0]>,
294 <baseset [1]>>,
294 <baseset [1]>>,
295 <addset
295 <addset
296 <baseset [2]>,
296 <baseset [2]>,
297 <addset
297 <addset
298 <baseset [3]>,
298 <baseset [3]>,
299 <baseset [4]>>>>
299 <baseset [4]>>>>
300 0
300 0
301 1
301 1
302 2
302 2
303 3
303 3
304 4
304 4
305
305
306 test that `_list` should be narrowed by provided `subset`
306 test that `_list` should be narrowed by provided `subset`
307
307
308 $ log '0:2 and (null|1|2|3)'
308 $ log '0:2 and (null|1|2|3)'
309 1
309 1
310 2
310 2
311
311
312 test that `_list` should remove duplicates
312 test that `_list` should remove duplicates
313
313
314 $ log '0|1|2|1|2|-1|tip'
314 $ log '0|1|2|1|2|-1|tip'
315 0
315 0
316 1
316 1
317 2
317 2
318 9
318 9
319
319
320 test unknown revision in `_list`
320 test unknown revision in `_list`
321
321
322 $ log '0|unknown'
322 $ log '0|unknown'
323 abort: unknown revision 'unknown'!
323 abort: unknown revision 'unknown'!
324 [255]
324 [255]
325
325
326 test integer range in `_list`
326 test integer range in `_list`
327
327
328 $ log '-1|-10'
328 $ log '-1|-10'
329 9
329 9
330 0
330 0
331
331
332 $ log '-10|-11'
332 $ log '-10|-11'
333 abort: unknown revision '-11'!
333 abort: unknown revision '-11'!
334 [255]
334 [255]
335
335
336 $ log '9|10'
336 $ log '9|10'
337 abort: unknown revision '10'!
337 abort: unknown revision '10'!
338 [255]
338 [255]
339
339
340 test '0000' != '0' in `_list`
340 test '0000' != '0' in `_list`
341
341
342 $ log '0|0000'
342 $ log '0|0000'
343 0
343 0
344 -1
344 -1
345
345
346 test ',' in `_list`
346 test ',' in `_list`
347 $ log '0,1'
347 $ log '0,1'
348 hg: parse error: can't use a list in this context
348 hg: parse error: can't use a list in this context
349 (see hg help "revsets.x or y")
349 (see 'hg help "revsets.x or y"')
350 [255]
350 [255]
351 $ try '0,1,2'
351 $ try '0,1,2'
352 (list
352 (list
353 (symbol '0')
353 (symbol '0')
354 (symbol '1')
354 (symbol '1')
355 (symbol '2'))
355 (symbol '2'))
356 hg: parse error: can't use a list in this context
356 hg: parse error: can't use a list in this context
357 (see hg help "revsets.x or y")
357 (see 'hg help "revsets.x or y"')
358 [255]
358 [255]
359
359
360 test that chained `or` operations make balanced addsets
360 test that chained `or` operations make balanced addsets
361
361
362 $ try '0:1|1:2|2:3|3:4|4:5'
362 $ try '0:1|1:2|2:3|3:4|4:5'
363 (or
363 (or
364 (list
364 (list
365 (range
365 (range
366 (symbol '0')
366 (symbol '0')
367 (symbol '1'))
367 (symbol '1'))
368 (range
368 (range
369 (symbol '1')
369 (symbol '1')
370 (symbol '2'))
370 (symbol '2'))
371 (range
371 (range
372 (symbol '2')
372 (symbol '2')
373 (symbol '3'))
373 (symbol '3'))
374 (range
374 (range
375 (symbol '3')
375 (symbol '3')
376 (symbol '4'))
376 (symbol '4'))
377 (range
377 (range
378 (symbol '4')
378 (symbol '4')
379 (symbol '5'))))
379 (symbol '5'))))
380 * set:
380 * set:
381 <addset
381 <addset
382 <addset
382 <addset
383 <spanset+ 0:2>,
383 <spanset+ 0:2>,
384 <spanset+ 1:3>>,
384 <spanset+ 1:3>>,
385 <addset
385 <addset
386 <spanset+ 2:4>,
386 <spanset+ 2:4>,
387 <addset
387 <addset
388 <spanset+ 3:5>,
388 <spanset+ 3:5>,
389 <spanset+ 4:6>>>>
389 <spanset+ 4:6>>>>
390 0
390 0
391 1
391 1
392 2
392 2
393 3
393 3
394 4
394 4
395 5
395 5
396
396
397 no crash by empty group "()" while optimizing `or` operations
397 no crash by empty group "()" while optimizing `or` operations
398
398
399 $ try --optimize '0|()'
399 $ try --optimize '0|()'
400 (or
400 (or
401 (list
401 (list
402 (symbol '0')
402 (symbol '0')
403 (group
403 (group
404 None)))
404 None)))
405 * optimized:
405 * optimized:
406 (or
406 (or
407 (list
407 (list
408 (symbol '0')
408 (symbol '0')
409 None))
409 None))
410 hg: parse error: missing argument
410 hg: parse error: missing argument
411 [255]
411 [255]
412
412
413 test that chained `or` operations never eat up stack (issue4624)
413 test that chained `or` operations never eat up stack (issue4624)
414 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
414 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
415
415
416 $ hg log -T '{rev}\n' -r `$PYTHON -c "print '+'.join(['0:1'] * 500)"`
416 $ hg log -T '{rev}\n' -r `$PYTHON -c "print '+'.join(['0:1'] * 500)"`
417 0
417 0
418 1
418 1
419
419
420 test that repeated `-r` options never eat up stack (issue4565)
420 test that repeated `-r` options never eat up stack (issue4565)
421 (uses `-r 0::1` to avoid possible optimization at old-style parser)
421 (uses `-r 0::1` to avoid possible optimization at old-style parser)
422
422
423 $ hg log -T '{rev}\n' `$PYTHON -c "for i in range(500): print '-r 0::1 ',"`
423 $ hg log -T '{rev}\n' `$PYTHON -c "for i in range(500): print '-r 0::1 ',"`
424 0
424 0
425 1
425 1
426
426
427 check that conversion to only works
427 check that conversion to only works
428 $ try --optimize '::3 - ::1'
428 $ try --optimize '::3 - ::1'
429 (minus
429 (minus
430 (dagrangepre
430 (dagrangepre
431 (symbol '3'))
431 (symbol '3'))
432 (dagrangepre
432 (dagrangepre
433 (symbol '1')))
433 (symbol '1')))
434 * optimized:
434 * optimized:
435 (func
435 (func
436 (symbol 'only')
436 (symbol 'only')
437 (list
437 (list
438 (symbol '3')
438 (symbol '3')
439 (symbol '1')))
439 (symbol '1')))
440 * set:
440 * set:
441 <baseset+ [3]>
441 <baseset+ [3]>
442 3
442 3
443 $ try --optimize 'ancestors(1) - ancestors(3)'
443 $ try --optimize 'ancestors(1) - ancestors(3)'
444 (minus
444 (minus
445 (func
445 (func
446 (symbol 'ancestors')
446 (symbol 'ancestors')
447 (symbol '1'))
447 (symbol '1'))
448 (func
448 (func
449 (symbol 'ancestors')
449 (symbol 'ancestors')
450 (symbol '3')))
450 (symbol '3')))
451 * optimized:
451 * optimized:
452 (func
452 (func
453 (symbol 'only')
453 (symbol 'only')
454 (list
454 (list
455 (symbol '1')
455 (symbol '1')
456 (symbol '3')))
456 (symbol '3')))
457 * set:
457 * set:
458 <baseset+ []>
458 <baseset+ []>
459 $ try --optimize 'not ::2 and ::6'
459 $ try --optimize 'not ::2 and ::6'
460 (and
460 (and
461 (not
461 (not
462 (dagrangepre
462 (dagrangepre
463 (symbol '2')))
463 (symbol '2')))
464 (dagrangepre
464 (dagrangepre
465 (symbol '6')))
465 (symbol '6')))
466 * optimized:
466 * optimized:
467 (func
467 (func
468 (symbol 'only')
468 (symbol 'only')
469 (list
469 (list
470 (symbol '6')
470 (symbol '6')
471 (symbol '2')))
471 (symbol '2')))
472 * set:
472 * set:
473 <baseset+ [3, 4, 5, 6]>
473 <baseset+ [3, 4, 5, 6]>
474 3
474 3
475 4
475 4
476 5
476 5
477 6
477 6
478 $ try --optimize 'ancestors(6) and not ancestors(4)'
478 $ try --optimize 'ancestors(6) and not ancestors(4)'
479 (and
479 (and
480 (func
480 (func
481 (symbol 'ancestors')
481 (symbol 'ancestors')
482 (symbol '6'))
482 (symbol '6'))
483 (not
483 (not
484 (func
484 (func
485 (symbol 'ancestors')
485 (symbol 'ancestors')
486 (symbol '4'))))
486 (symbol '4'))))
487 * optimized:
487 * optimized:
488 (func
488 (func
489 (symbol 'only')
489 (symbol 'only')
490 (list
490 (list
491 (symbol '6')
491 (symbol '6')
492 (symbol '4')))
492 (symbol '4')))
493 * set:
493 * set:
494 <baseset+ [3, 5, 6]>
494 <baseset+ [3, 5, 6]>
495 3
495 3
496 5
496 5
497 6
497 6
498
498
499 no crash by empty group "()" while optimizing to "only()"
499 no crash by empty group "()" while optimizing to "only()"
500
500
501 $ try --optimize '::1 and ()'
501 $ try --optimize '::1 and ()'
502 (and
502 (and
503 (dagrangepre
503 (dagrangepre
504 (symbol '1'))
504 (symbol '1'))
505 (group
505 (group
506 None))
506 None))
507 * optimized:
507 * optimized:
508 (andsmally
508 (andsmally
509 (func
509 (func
510 (symbol 'ancestors')
510 (symbol 'ancestors')
511 (symbol '1'))
511 (symbol '1'))
512 None)
512 None)
513 hg: parse error: missing argument
513 hg: parse error: missing argument
514 [255]
514 [255]
515
515
516 optimization to only() works only if ancestors() takes only one argument
516 optimization to only() works only if ancestors() takes only one argument
517
517
518 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
518 $ hg debugrevspec -p optimized 'ancestors(6) - ancestors(4, 1)'
519 * optimized:
519 * optimized:
520 (difference
520 (difference
521 (func
521 (func
522 (symbol 'ancestors')
522 (symbol 'ancestors')
523 (symbol '6'))
523 (symbol '6'))
524 (func
524 (func
525 (symbol 'ancestors')
525 (symbol 'ancestors')
526 (list
526 (list
527 (symbol '4')
527 (symbol '4')
528 (symbol '1'))))
528 (symbol '1'))))
529 0
529 0
530 1
530 1
531 3
531 3
532 5
532 5
533 6
533 6
534 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
534 $ hg debugrevspec -p optimized 'ancestors(6, 1) - ancestors(4)'
535 * optimized:
535 * optimized:
536 (difference
536 (difference
537 (func
537 (func
538 (symbol 'ancestors')
538 (symbol 'ancestors')
539 (list
539 (list
540 (symbol '6')
540 (symbol '6')
541 (symbol '1')))
541 (symbol '1')))
542 (func
542 (func
543 (symbol 'ancestors')
543 (symbol 'ancestors')
544 (symbol '4')))
544 (symbol '4')))
545 5
545 5
546 6
546 6
547
547
548 optimization disabled if keyword arguments passed (because we're too lazy
548 optimization disabled if keyword arguments passed (because we're too lazy
549 to support it)
549 to support it)
550
550
551 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
551 $ hg debugrevspec -p optimized 'ancestors(set=6) - ancestors(set=4)'
552 * optimized:
552 * optimized:
553 (difference
553 (difference
554 (func
554 (func
555 (symbol 'ancestors')
555 (symbol 'ancestors')
556 (keyvalue
556 (keyvalue
557 (symbol 'set')
557 (symbol 'set')
558 (symbol '6')))
558 (symbol '6')))
559 (func
559 (func
560 (symbol 'ancestors')
560 (symbol 'ancestors')
561 (keyvalue
561 (keyvalue
562 (symbol 'set')
562 (symbol 'set')
563 (symbol '4'))))
563 (symbol '4'))))
564 3
564 3
565 5
565 5
566 6
566 6
567
567
568 invalid function call should not be optimized to only()
568 invalid function call should not be optimized to only()
569
569
570 $ log '"ancestors"(6) and not ancestors(4)'
570 $ log '"ancestors"(6) and not ancestors(4)'
571 hg: parse error: not a symbol
571 hg: parse error: not a symbol
572 [255]
572 [255]
573
573
574 $ log 'ancestors(6) and not "ancestors"(4)'
574 $ log 'ancestors(6) and not "ancestors"(4)'
575 hg: parse error: not a symbol
575 hg: parse error: not a symbol
576 [255]
576 [255]
577
577
578 test empty string
578 test empty string
579
579
580 $ log ''
580 $ log ''
581 hg: parse error: empty query
581 hg: parse error: empty query
582 [255]
582 [255]
583 $ log 'parents("")'
583 $ log 'parents("")'
584 hg: parse error: empty string is not a valid revision
584 hg: parse error: empty string is not a valid revision
585 [255]
585 [255]
586
586
587 test empty revset
587 test empty revset
588 $ hg log 'none()'
588 $ hg log 'none()'
589
589
590 we can use patterns when searching for tags
590 we can use patterns when searching for tags
591
591
592 $ log 'tag("1..*")'
592 $ log 'tag("1..*")'
593 abort: tag '1..*' does not exist!
593 abort: tag '1..*' does not exist!
594 [255]
594 [255]
595 $ log 'tag("re:1..*")'
595 $ log 'tag("re:1..*")'
596 6
596 6
597 $ log 'tag("re:[0-9].[0-9]")'
597 $ log 'tag("re:[0-9].[0-9]")'
598 6
598 6
599 $ log 'tag("literal:1.0")'
599 $ log 'tag("literal:1.0")'
600 6
600 6
601 $ log 'tag("re:0..*")'
601 $ log 'tag("re:0..*")'
602
602
603 $ log 'tag(unknown)'
603 $ log 'tag(unknown)'
604 abort: tag 'unknown' does not exist!
604 abort: tag 'unknown' does not exist!
605 [255]
605 [255]
606 $ log 'tag("re:unknown")'
606 $ log 'tag("re:unknown")'
607 $ log 'present(tag("unknown"))'
607 $ log 'present(tag("unknown"))'
608 $ log 'present(tag("re:unknown"))'
608 $ log 'present(tag("re:unknown"))'
609 $ log 'branch(unknown)'
609 $ log 'branch(unknown)'
610 abort: unknown revision 'unknown'!
610 abort: unknown revision 'unknown'!
611 [255]
611 [255]
612 $ log 'branch("literal:unknown")'
612 $ log 'branch("literal:unknown")'
613 abort: branch 'unknown' does not exist!
613 abort: branch 'unknown' does not exist!
614 [255]
614 [255]
615 $ log 'branch("re:unknown")'
615 $ log 'branch("re:unknown")'
616 $ log 'present(branch("unknown"))'
616 $ log 'present(branch("unknown"))'
617 $ log 'present(branch("re:unknown"))'
617 $ log 'present(branch("re:unknown"))'
618 $ log 'user(bob)'
618 $ log 'user(bob)'
619 2
619 2
620
620
621 $ log '4::8'
621 $ log '4::8'
622 4
622 4
623 8
623 8
624 $ log '4:8'
624 $ log '4:8'
625 4
625 4
626 5
626 5
627 6
627 6
628 7
628 7
629 8
629 8
630
630
631 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
631 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
632 4
632 4
633 2
633 2
634 5
634 5
635
635
636 $ log 'not 0 and 0:2'
636 $ log 'not 0 and 0:2'
637 1
637 1
638 2
638 2
639 $ log 'not 1 and 0:2'
639 $ log 'not 1 and 0:2'
640 0
640 0
641 2
641 2
642 $ log 'not 2 and 0:2'
642 $ log 'not 2 and 0:2'
643 0
643 0
644 1
644 1
645 $ log '(1 and 2)::'
645 $ log '(1 and 2)::'
646 $ log '(1 and 2):'
646 $ log '(1 and 2):'
647 $ log '(1 and 2):3'
647 $ log '(1 and 2):3'
648 $ log 'sort(head(), -rev)'
648 $ log 'sort(head(), -rev)'
649 9
649 9
650 7
650 7
651 6
651 6
652 5
652 5
653 4
653 4
654 3
654 3
655 2
655 2
656 1
656 1
657 0
657 0
658 $ log '4::8 - 8'
658 $ log '4::8 - 8'
659 4
659 4
660
660
661 matching() should preserve the order of the input set:
661 matching() should preserve the order of the input set:
662
662
663 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
663 $ log '(2 or 3 or 1) and matching(1 or 2 or 3)'
664 2
664 2
665 3
665 3
666 1
666 1
667
667
668 $ log 'named("unknown")'
668 $ log 'named("unknown")'
669 abort: namespace 'unknown' does not exist!
669 abort: namespace 'unknown' does not exist!
670 [255]
670 [255]
671 $ log 'named("re:unknown")'
671 $ log 'named("re:unknown")'
672 abort: no namespace exists that match 'unknown'!
672 abort: no namespace exists that match 'unknown'!
673 [255]
673 [255]
674 $ log 'present(named("unknown"))'
674 $ log 'present(named("unknown"))'
675 $ log 'present(named("re:unknown"))'
675 $ log 'present(named("re:unknown"))'
676
676
677 $ log 'tag()'
677 $ log 'tag()'
678 6
678 6
679 $ log 'named("tags")'
679 $ log 'named("tags")'
680 6
680 6
681
681
682 issue2437
682 issue2437
683
683
684 $ log '3 and p1(5)'
684 $ log '3 and p1(5)'
685 3
685 3
686 $ log '4 and p2(6)'
686 $ log '4 and p2(6)'
687 4
687 4
688 $ log '1 and parents(:2)'
688 $ log '1 and parents(:2)'
689 1
689 1
690 $ log '2 and children(1:)'
690 $ log '2 and children(1:)'
691 2
691 2
692 $ log 'roots(all()) or roots(all())'
692 $ log 'roots(all()) or roots(all())'
693 0
693 0
694 $ hg debugrevspec 'roots(all()) or roots(all())'
694 $ hg debugrevspec 'roots(all()) or roots(all())'
695 0
695 0
696 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
696 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
697 9
697 9
698 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
698 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
699 4
699 4
700
700
701 issue2654: report a parse error if the revset was not completely parsed
701 issue2654: report a parse error if the revset was not completely parsed
702
702
703 $ log '1 OR 2'
703 $ log '1 OR 2'
704 hg: parse error at 2: invalid token
704 hg: parse error at 2: invalid token
705 (1 OR 2
705 (1 OR 2
706 ^ here)
706 ^ here)
707 [255]
707 [255]
708
708
709 or operator should preserve ordering:
709 or operator should preserve ordering:
710 $ log 'reverse(2::4) or tip'
710 $ log 'reverse(2::4) or tip'
711 4
711 4
712 2
712 2
713 9
713 9
714
714
715 parentrevspec
715 parentrevspec
716
716
717 $ log 'merge()^0'
717 $ log 'merge()^0'
718 6
718 6
719 $ log 'merge()^'
719 $ log 'merge()^'
720 5
720 5
721 $ log 'merge()^1'
721 $ log 'merge()^1'
722 5
722 5
723 $ log 'merge()^2'
723 $ log 'merge()^2'
724 4
724 4
725 $ log '(not merge())^2'
725 $ log '(not merge())^2'
726 $ log 'merge()^^'
726 $ log 'merge()^^'
727 3
727 3
728 $ log 'merge()^1^'
728 $ log 'merge()^1^'
729 3
729 3
730 $ log 'merge()^^^'
730 $ log 'merge()^^^'
731 1
731 1
732
732
733 $ hg debugrevspec -s '(merge() | 0)~-1'
733 $ hg debugrevspec -s '(merge() | 0)~-1'
734 * set:
734 * set:
735 <baseset+ [1, 7]>
735 <baseset+ [1, 7]>
736 1
736 1
737 7
737 7
738 $ log 'merge()~-1'
738 $ log 'merge()~-1'
739 7
739 7
740 $ log 'tip~-1'
740 $ log 'tip~-1'
741 $ log '(tip | merge())~-1'
741 $ log '(tip | merge())~-1'
742 7
742 7
743 $ log 'merge()~0'
743 $ log 'merge()~0'
744 6
744 6
745 $ log 'merge()~1'
745 $ log 'merge()~1'
746 5
746 5
747 $ log 'merge()~2'
747 $ log 'merge()~2'
748 3
748 3
749 $ log 'merge()~2^1'
749 $ log 'merge()~2^1'
750 1
750 1
751 $ log 'merge()~3'
751 $ log 'merge()~3'
752 1
752 1
753
753
754 $ log '(-3:tip)^'
754 $ log '(-3:tip)^'
755 4
755 4
756 6
756 6
757 8
757 8
758
758
759 $ log 'tip^foo'
759 $ log 'tip^foo'
760 hg: parse error: ^ expects a number 0, 1, or 2
760 hg: parse error: ^ expects a number 0, 1, or 2
761 [255]
761 [255]
762
762
763 $ log 'branchpoint()~-1'
763 $ log 'branchpoint()~-1'
764 abort: revision in set has more than one child!
764 abort: revision in set has more than one child!
765 [255]
765 [255]
766
766
767 Bogus function gets suggestions
767 Bogus function gets suggestions
768 $ log 'add()'
768 $ log 'add()'
769 hg: parse error: unknown identifier: add
769 hg: parse error: unknown identifier: add
770 (did you mean adds?)
770 (did you mean adds?)
771 [255]
771 [255]
772 $ log 'added()'
772 $ log 'added()'
773 hg: parse error: unknown identifier: added
773 hg: parse error: unknown identifier: added
774 (did you mean adds?)
774 (did you mean adds?)
775 [255]
775 [255]
776 $ log 'remo()'
776 $ log 'remo()'
777 hg: parse error: unknown identifier: remo
777 hg: parse error: unknown identifier: remo
778 (did you mean one of remote, removes?)
778 (did you mean one of remote, removes?)
779 [255]
779 [255]
780 $ log 'babar()'
780 $ log 'babar()'
781 hg: parse error: unknown identifier: babar
781 hg: parse error: unknown identifier: babar
782 [255]
782 [255]
783
783
784 Bogus function with a similar internal name doesn't suggest the internal name
784 Bogus function with a similar internal name doesn't suggest the internal name
785 $ log 'matches()'
785 $ log 'matches()'
786 hg: parse error: unknown identifier: matches
786 hg: parse error: unknown identifier: matches
787 (did you mean matching?)
787 (did you mean matching?)
788 [255]
788 [255]
789
789
790 Undocumented functions aren't suggested as similar either
790 Undocumented functions aren't suggested as similar either
791 $ log 'tagged2()'
791 $ log 'tagged2()'
792 hg: parse error: unknown identifier: tagged2
792 hg: parse error: unknown identifier: tagged2
793 [255]
793 [255]
794
794
795 multiple revspecs
795 multiple revspecs
796
796
797 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
797 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
798 8
798 8
799 9
799 9
800 4
800 4
801 5
801 5
802 6
802 6
803 7
803 7
804
804
805 test usage in revpair (with "+")
805 test usage in revpair (with "+")
806
806
807 (real pair)
807 (real pair)
808
808
809 $ hg diff -r 'tip^^' -r 'tip'
809 $ hg diff -r 'tip^^' -r 'tip'
810 diff -r 2326846efdab -r 24286f4ae135 .hgtags
810 diff -r 2326846efdab -r 24286f4ae135 .hgtags
811 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
811 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
812 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
812 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
813 @@ -0,0 +1,1 @@
813 @@ -0,0 +1,1 @@
814 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
814 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
815 $ hg diff -r 'tip^^::tip'
815 $ hg diff -r 'tip^^::tip'
816 diff -r 2326846efdab -r 24286f4ae135 .hgtags
816 diff -r 2326846efdab -r 24286f4ae135 .hgtags
817 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
817 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
818 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
818 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
819 @@ -0,0 +1,1 @@
819 @@ -0,0 +1,1 @@
820 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
820 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
821
821
822 (single rev)
822 (single rev)
823
823
824 $ hg diff -r 'tip^' -r 'tip^'
824 $ hg diff -r 'tip^' -r 'tip^'
825 $ hg diff -r 'tip^:tip^'
825 $ hg diff -r 'tip^:tip^'
826
826
827 (single rev that does not looks like a range)
827 (single rev that does not looks like a range)
828
828
829 $ hg diff -r 'tip^::tip^ or tip^'
829 $ hg diff -r 'tip^::tip^ or tip^'
830 diff -r d5d0dcbdc4d9 .hgtags
830 diff -r d5d0dcbdc4d9 .hgtags
831 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
831 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
832 +++ b/.hgtags * (glob)
832 +++ b/.hgtags * (glob)
833 @@ -0,0 +1,1 @@
833 @@ -0,0 +1,1 @@
834 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
834 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
835 $ hg diff -r 'tip^ or tip^'
835 $ hg diff -r 'tip^ or tip^'
836 diff -r d5d0dcbdc4d9 .hgtags
836 diff -r d5d0dcbdc4d9 .hgtags
837 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
837 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
838 +++ b/.hgtags * (glob)
838 +++ b/.hgtags * (glob)
839 @@ -0,0 +1,1 @@
839 @@ -0,0 +1,1 @@
840 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
840 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
841
841
842 (no rev)
842 (no rev)
843
843
844 $ hg diff -r 'author("babar") or author("celeste")'
844 $ hg diff -r 'author("babar") or author("celeste")'
845 abort: empty revision range
845 abort: empty revision range
846 [255]
846 [255]
847
847
848 aliases:
848 aliases:
849
849
850 $ echo '[revsetalias]' >> .hg/hgrc
850 $ echo '[revsetalias]' >> .hg/hgrc
851 $ echo 'm = merge()' >> .hg/hgrc
851 $ echo 'm = merge()' >> .hg/hgrc
852 (revset aliases can override builtin revsets)
852 (revset aliases can override builtin revsets)
853 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
853 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
854 $ echo 'sincem = descendants(m)' >> .hg/hgrc
854 $ echo 'sincem = descendants(m)' >> .hg/hgrc
855 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
855 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
856 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
856 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
857 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
857 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
858
858
859 $ try m
859 $ try m
860 (symbol 'm')
860 (symbol 'm')
861 * expanded:
861 * expanded:
862 (func
862 (func
863 (symbol 'merge')
863 (symbol 'merge')
864 None)
864 None)
865 * set:
865 * set:
866 <filteredset
866 <filteredset
867 <fullreposet+ 0:10>,
867 <fullreposet+ 0:10>,
868 <merge>>
868 <merge>>
869 6
869 6
870
870
871 $ HGPLAIN=1
871 $ HGPLAIN=1
872 $ export HGPLAIN
872 $ export HGPLAIN
873 $ try m
873 $ try m
874 (symbol 'm')
874 (symbol 'm')
875 abort: unknown revision 'm'!
875 abort: unknown revision 'm'!
876 [255]
876 [255]
877
877
878 $ HGPLAINEXCEPT=revsetalias
878 $ HGPLAINEXCEPT=revsetalias
879 $ export HGPLAINEXCEPT
879 $ export HGPLAINEXCEPT
880 $ try m
880 $ try m
881 (symbol 'm')
881 (symbol 'm')
882 * expanded:
882 * expanded:
883 (func
883 (func
884 (symbol 'merge')
884 (symbol 'merge')
885 None)
885 None)
886 * set:
886 * set:
887 <filteredset
887 <filteredset
888 <fullreposet+ 0:10>,
888 <fullreposet+ 0:10>,
889 <merge>>
889 <merge>>
890 6
890 6
891
891
892 $ unset HGPLAIN
892 $ unset HGPLAIN
893 $ unset HGPLAINEXCEPT
893 $ unset HGPLAINEXCEPT
894
894
895 $ try 'p2(.)'
895 $ try 'p2(.)'
896 (func
896 (func
897 (symbol 'p2')
897 (symbol 'p2')
898 (symbol '.'))
898 (symbol '.'))
899 * expanded:
899 * expanded:
900 (func
900 (func
901 (symbol 'p1')
901 (symbol 'p1')
902 (symbol '.'))
902 (symbol '.'))
903 * set:
903 * set:
904 <baseset+ [8]>
904 <baseset+ [8]>
905 8
905 8
906
906
907 $ HGPLAIN=1
907 $ HGPLAIN=1
908 $ export HGPLAIN
908 $ export HGPLAIN
909 $ try 'p2(.)'
909 $ try 'p2(.)'
910 (func
910 (func
911 (symbol 'p2')
911 (symbol 'p2')
912 (symbol '.'))
912 (symbol '.'))
913 * set:
913 * set:
914 <baseset+ []>
914 <baseset+ []>
915
915
916 $ HGPLAINEXCEPT=revsetalias
916 $ HGPLAINEXCEPT=revsetalias
917 $ export HGPLAINEXCEPT
917 $ export HGPLAINEXCEPT
918 $ try 'p2(.)'
918 $ try 'p2(.)'
919 (func
919 (func
920 (symbol 'p2')
920 (symbol 'p2')
921 (symbol '.'))
921 (symbol '.'))
922 * expanded:
922 * expanded:
923 (func
923 (func
924 (symbol 'p1')
924 (symbol 'p1')
925 (symbol '.'))
925 (symbol '.'))
926 * set:
926 * set:
927 <baseset+ [8]>
927 <baseset+ [8]>
928 8
928 8
929
929
930 $ unset HGPLAIN
930 $ unset HGPLAIN
931 $ unset HGPLAINEXCEPT
931 $ unset HGPLAINEXCEPT
932
932
933 test alias recursion
933 test alias recursion
934
934
935 $ try sincem
935 $ try sincem
936 (symbol 'sincem')
936 (symbol 'sincem')
937 * expanded:
937 * expanded:
938 (func
938 (func
939 (symbol 'descendants')
939 (symbol 'descendants')
940 (func
940 (func
941 (symbol 'merge')
941 (symbol 'merge')
942 None))
942 None))
943 * set:
943 * set:
944 <generatorsetasc+>
944 <generatorsetasc+>
945 6
945 6
946 7
946 7
947
947
948 test infinite recursion
948 test infinite recursion
949
949
950 $ echo 'recurse1 = recurse2' >> .hg/hgrc
950 $ echo 'recurse1 = recurse2' >> .hg/hgrc
951 $ echo 'recurse2 = recurse1' >> .hg/hgrc
951 $ echo 'recurse2 = recurse1' >> .hg/hgrc
952 $ try recurse1
952 $ try recurse1
953 (symbol 'recurse1')
953 (symbol 'recurse1')
954 hg: parse error: infinite expansion of revset alias "recurse1" detected
954 hg: parse error: infinite expansion of revset alias "recurse1" detected
955 [255]
955 [255]
956
956
957 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
957 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
958 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
958 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
959 $ try "level2(level1(1, 2), 3)"
959 $ try "level2(level1(1, 2), 3)"
960 (func
960 (func
961 (symbol 'level2')
961 (symbol 'level2')
962 (list
962 (list
963 (func
963 (func
964 (symbol 'level1')
964 (symbol 'level1')
965 (list
965 (list
966 (symbol '1')
966 (symbol '1')
967 (symbol '2')))
967 (symbol '2')))
968 (symbol '3')))
968 (symbol '3')))
969 * expanded:
969 * expanded:
970 (or
970 (or
971 (list
971 (list
972 (symbol '3')
972 (symbol '3')
973 (or
973 (or
974 (list
974 (list
975 (symbol '1')
975 (symbol '1')
976 (symbol '2')))))
976 (symbol '2')))))
977 * set:
977 * set:
978 <addset
978 <addset
979 <baseset [3]>,
979 <baseset [3]>,
980 <baseset [1, 2]>>
980 <baseset [1, 2]>>
981 3
981 3
982 1
982 1
983 2
983 2
984
984
985 test nesting and variable passing
985 test nesting and variable passing
986
986
987 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
987 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
988 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
988 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
989 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
989 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
990 $ try 'nested(2:5)'
990 $ try 'nested(2:5)'
991 (func
991 (func
992 (symbol 'nested')
992 (symbol 'nested')
993 (range
993 (range
994 (symbol '2')
994 (symbol '2')
995 (symbol '5')))
995 (symbol '5')))
996 * expanded:
996 * expanded:
997 (func
997 (func
998 (symbol 'max')
998 (symbol 'max')
999 (range
999 (range
1000 (symbol '2')
1000 (symbol '2')
1001 (symbol '5')))
1001 (symbol '5')))
1002 * set:
1002 * set:
1003 <baseset
1003 <baseset
1004 <max
1004 <max
1005 <fullreposet+ 0:10>,
1005 <fullreposet+ 0:10>,
1006 <spanset+ 2:6>>>
1006 <spanset+ 2:6>>>
1007 5
1007 5
1008
1008
1009 test chained `or` operations are flattened at parsing phase
1009 test chained `or` operations are flattened at parsing phase
1010
1010
1011 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1011 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1012 $ try 'chainedorops(0:1, 1:2, 2:3)'
1012 $ try 'chainedorops(0:1, 1:2, 2:3)'
1013 (func
1013 (func
1014 (symbol 'chainedorops')
1014 (symbol 'chainedorops')
1015 (list
1015 (list
1016 (range
1016 (range
1017 (symbol '0')
1017 (symbol '0')
1018 (symbol '1'))
1018 (symbol '1'))
1019 (range
1019 (range
1020 (symbol '1')
1020 (symbol '1')
1021 (symbol '2'))
1021 (symbol '2'))
1022 (range
1022 (range
1023 (symbol '2')
1023 (symbol '2')
1024 (symbol '3'))))
1024 (symbol '3'))))
1025 * expanded:
1025 * expanded:
1026 (or
1026 (or
1027 (list
1027 (list
1028 (range
1028 (range
1029 (symbol '0')
1029 (symbol '0')
1030 (symbol '1'))
1030 (symbol '1'))
1031 (range
1031 (range
1032 (symbol '1')
1032 (symbol '1')
1033 (symbol '2'))
1033 (symbol '2'))
1034 (range
1034 (range
1035 (symbol '2')
1035 (symbol '2')
1036 (symbol '3'))))
1036 (symbol '3'))))
1037 * set:
1037 * set:
1038 <addset
1038 <addset
1039 <spanset+ 0:2>,
1039 <spanset+ 0:2>,
1040 <addset
1040 <addset
1041 <spanset+ 1:3>,
1041 <spanset+ 1:3>,
1042 <spanset+ 2:4>>>
1042 <spanset+ 2:4>>>
1043 0
1043 0
1044 1
1044 1
1045 2
1045 2
1046 3
1046 3
1047
1047
1048 test variable isolation, variable placeholders are rewritten as string
1048 test variable isolation, variable placeholders are rewritten as string
1049 then parsed and matched again as string. Check they do not leak too
1049 then parsed and matched again as string. Check they do not leak too
1050 far away.
1050 far away.
1051
1051
1052 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1052 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1053 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1053 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1054 $ try 'callinjection(2:5)'
1054 $ try 'callinjection(2:5)'
1055 (func
1055 (func
1056 (symbol 'callinjection')
1056 (symbol 'callinjection')
1057 (range
1057 (range
1058 (symbol '2')
1058 (symbol '2')
1059 (symbol '5')))
1059 (symbol '5')))
1060 * expanded:
1060 * expanded:
1061 (func
1061 (func
1062 (symbol 'descendants')
1062 (symbol 'descendants')
1063 (func
1063 (func
1064 (symbol 'max')
1064 (symbol 'max')
1065 (string '$1')))
1065 (string '$1')))
1066 abort: unknown revision '$1'!
1066 abort: unknown revision '$1'!
1067 [255]
1067 [255]
1068
1068
1069 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
1069 test scope of alias expansion: 'universe' is expanded prior to 'shadowall(0)',
1070 but 'all()' should never be substituted to '0()'.
1070 but 'all()' should never be substituted to '0()'.
1071
1071
1072 $ echo 'universe = all()' >> .hg/hgrc
1072 $ echo 'universe = all()' >> .hg/hgrc
1073 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
1073 $ echo 'shadowall(all) = all and universe' >> .hg/hgrc
1074 $ try 'shadowall(0)'
1074 $ try 'shadowall(0)'
1075 (func
1075 (func
1076 (symbol 'shadowall')
1076 (symbol 'shadowall')
1077 (symbol '0'))
1077 (symbol '0'))
1078 * expanded:
1078 * expanded:
1079 (and
1079 (and
1080 (symbol '0')
1080 (symbol '0')
1081 (func
1081 (func
1082 (symbol 'all')
1082 (symbol 'all')
1083 None))
1083 None))
1084 * set:
1084 * set:
1085 <filteredset
1085 <filteredset
1086 <baseset [0]>,
1086 <baseset [0]>,
1087 <spanset+ 0:10>>
1087 <spanset+ 0:10>>
1088 0
1088 0
1089
1089
1090 test unknown reference:
1090 test unknown reference:
1091
1091
1092 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
1092 $ try "unknownref(0)" --config 'revsetalias.unknownref($1)=$1:$2'
1093 (func
1093 (func
1094 (symbol 'unknownref')
1094 (symbol 'unknownref')
1095 (symbol '0'))
1095 (symbol '0'))
1096 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
1096 abort: bad definition of revset alias "unknownref": invalid symbol '$2'
1097 [255]
1097 [255]
1098
1098
1099 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1099 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1100 (symbol 'tip')
1100 (symbol 'tip')
1101 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
1101 warning: bad definition of revset alias "anotherbadone": at 7: not a prefix: end
1102 * set:
1102 * set:
1103 <baseset [9]>
1103 <baseset [9]>
1104 9
1104 9
1105
1105
1106 $ try 'tip'
1106 $ try 'tip'
1107 (symbol 'tip')
1107 (symbol 'tip')
1108 * set:
1108 * set:
1109 <baseset [9]>
1109 <baseset [9]>
1110 9
1110 9
1111
1111
1112 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1112 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1113 (symbol 'tip')
1113 (symbol 'tip')
1114 warning: bad declaration of revset alias "bad name": at 4: invalid token
1114 warning: bad declaration of revset alias "bad name": at 4: invalid token
1115 * set:
1115 * set:
1116 <baseset [9]>
1116 <baseset [9]>
1117 9
1117 9
1118 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1118 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1119 $ try 'strictreplacing("foo", tip)'
1119 $ try 'strictreplacing("foo", tip)'
1120 (func
1120 (func
1121 (symbol 'strictreplacing')
1121 (symbol 'strictreplacing')
1122 (list
1122 (list
1123 (string 'foo')
1123 (string 'foo')
1124 (symbol 'tip')))
1124 (symbol 'tip')))
1125 * expanded:
1125 * expanded:
1126 (or
1126 (or
1127 (list
1127 (list
1128 (symbol 'tip')
1128 (symbol 'tip')
1129 (func
1129 (func
1130 (symbol 'desc')
1130 (symbol 'desc')
1131 (string '$1'))))
1131 (string '$1'))))
1132 * set:
1132 * set:
1133 <addset
1133 <addset
1134 <baseset [9]>,
1134 <baseset [9]>,
1135 <filteredset
1135 <filteredset
1136 <fullreposet+ 0:10>,
1136 <fullreposet+ 0:10>,
1137 <desc '$1'>>>
1137 <desc '$1'>>>
1138 9
1138 9
1139
1139
1140 $ try 'd(2:5)'
1140 $ try 'd(2:5)'
1141 (func
1141 (func
1142 (symbol 'd')
1142 (symbol 'd')
1143 (range
1143 (range
1144 (symbol '2')
1144 (symbol '2')
1145 (symbol '5')))
1145 (symbol '5')))
1146 * expanded:
1146 * expanded:
1147 (func
1147 (func
1148 (symbol 'reverse')
1148 (symbol 'reverse')
1149 (func
1149 (func
1150 (symbol 'sort')
1150 (symbol 'sort')
1151 (list
1151 (list
1152 (range
1152 (range
1153 (symbol '2')
1153 (symbol '2')
1154 (symbol '5'))
1154 (symbol '5'))
1155 (symbol 'date'))))
1155 (symbol 'date'))))
1156 * set:
1156 * set:
1157 <baseset [4, 5, 3, 2]>
1157 <baseset [4, 5, 3, 2]>
1158 4
1158 4
1159 5
1159 5
1160 3
1160 3
1161 2
1161 2
1162 $ try 'rs(2 or 3, date)'
1162 $ try 'rs(2 or 3, date)'
1163 (func
1163 (func
1164 (symbol 'rs')
1164 (symbol 'rs')
1165 (list
1165 (list
1166 (or
1166 (or
1167 (list
1167 (list
1168 (symbol '2')
1168 (symbol '2')
1169 (symbol '3')))
1169 (symbol '3')))
1170 (symbol 'date')))
1170 (symbol 'date')))
1171 * expanded:
1171 * expanded:
1172 (func
1172 (func
1173 (symbol 'reverse')
1173 (symbol 'reverse')
1174 (func
1174 (func
1175 (symbol 'sort')
1175 (symbol 'sort')
1176 (list
1176 (list
1177 (or
1177 (or
1178 (list
1178 (list
1179 (symbol '2')
1179 (symbol '2')
1180 (symbol '3')))
1180 (symbol '3')))
1181 (symbol 'date'))))
1181 (symbol 'date'))))
1182 * set:
1182 * set:
1183 <baseset [3, 2]>
1183 <baseset [3, 2]>
1184 3
1184 3
1185 2
1185 2
1186 $ try 'rs()'
1186 $ try 'rs()'
1187 (func
1187 (func
1188 (symbol 'rs')
1188 (symbol 'rs')
1189 None)
1189 None)
1190 hg: parse error: invalid number of arguments: 0
1190 hg: parse error: invalid number of arguments: 0
1191 [255]
1191 [255]
1192 $ try 'rs(2)'
1192 $ try 'rs(2)'
1193 (func
1193 (func
1194 (symbol 'rs')
1194 (symbol 'rs')
1195 (symbol '2'))
1195 (symbol '2'))
1196 hg: parse error: invalid number of arguments: 1
1196 hg: parse error: invalid number of arguments: 1
1197 [255]
1197 [255]
1198 $ try 'rs(2, data, 7)'
1198 $ try 'rs(2, data, 7)'
1199 (func
1199 (func
1200 (symbol 'rs')
1200 (symbol 'rs')
1201 (list
1201 (list
1202 (symbol '2')
1202 (symbol '2')
1203 (symbol 'data')
1203 (symbol 'data')
1204 (symbol '7')))
1204 (symbol '7')))
1205 hg: parse error: invalid number of arguments: 3
1205 hg: parse error: invalid number of arguments: 3
1206 [255]
1206 [255]
1207 $ try 'rs4(2 or 3, x, x, date)'
1207 $ try 'rs4(2 or 3, x, x, date)'
1208 (func
1208 (func
1209 (symbol 'rs4')
1209 (symbol 'rs4')
1210 (list
1210 (list
1211 (or
1211 (or
1212 (list
1212 (list
1213 (symbol '2')
1213 (symbol '2')
1214 (symbol '3')))
1214 (symbol '3')))
1215 (symbol 'x')
1215 (symbol 'x')
1216 (symbol 'x')
1216 (symbol 'x')
1217 (symbol 'date')))
1217 (symbol 'date')))
1218 * expanded:
1218 * expanded:
1219 (func
1219 (func
1220 (symbol 'reverse')
1220 (symbol 'reverse')
1221 (func
1221 (func
1222 (symbol 'sort')
1222 (symbol 'sort')
1223 (list
1223 (list
1224 (or
1224 (or
1225 (list
1225 (list
1226 (symbol '2')
1226 (symbol '2')
1227 (symbol '3')))
1227 (symbol '3')))
1228 (symbol 'date'))))
1228 (symbol 'date'))))
1229 * set:
1229 * set:
1230 <baseset [3, 2]>
1230 <baseset [3, 2]>
1231 3
1231 3
1232 2
1232 2
1233
1233
1234 issue4553: check that revset aliases override existing hash prefix
1234 issue4553: check that revset aliases override existing hash prefix
1235
1235
1236 $ hg log -qr e
1236 $ hg log -qr e
1237 6:e0cc66ef77e8
1237 6:e0cc66ef77e8
1238
1238
1239 $ hg log -qr e --config revsetalias.e="all()"
1239 $ hg log -qr e --config revsetalias.e="all()"
1240 0:2785f51eece5
1240 0:2785f51eece5
1241 1:d75937da8da0
1241 1:d75937da8da0
1242 2:5ed5505e9f1c
1242 2:5ed5505e9f1c
1243 3:8528aa5637f2
1243 3:8528aa5637f2
1244 4:2326846efdab
1244 4:2326846efdab
1245 5:904fa392b941
1245 5:904fa392b941
1246 6:e0cc66ef77e8
1246 6:e0cc66ef77e8
1247 7:013af1973af4
1247 7:013af1973af4
1248 8:d5d0dcbdc4d9
1248 8:d5d0dcbdc4d9
1249 9:24286f4ae135
1249 9:24286f4ae135
1250
1250
1251 $ hg log -qr e: --config revsetalias.e="0"
1251 $ hg log -qr e: --config revsetalias.e="0"
1252 0:2785f51eece5
1252 0:2785f51eece5
1253 1:d75937da8da0
1253 1:d75937da8da0
1254 2:5ed5505e9f1c
1254 2:5ed5505e9f1c
1255 3:8528aa5637f2
1255 3:8528aa5637f2
1256 4:2326846efdab
1256 4:2326846efdab
1257 5:904fa392b941
1257 5:904fa392b941
1258 6:e0cc66ef77e8
1258 6:e0cc66ef77e8
1259 7:013af1973af4
1259 7:013af1973af4
1260 8:d5d0dcbdc4d9
1260 8:d5d0dcbdc4d9
1261 9:24286f4ae135
1261 9:24286f4ae135
1262
1262
1263 $ hg log -qr :e --config revsetalias.e="9"
1263 $ hg log -qr :e --config revsetalias.e="9"
1264 0:2785f51eece5
1264 0:2785f51eece5
1265 1:d75937da8da0
1265 1:d75937da8da0
1266 2:5ed5505e9f1c
1266 2:5ed5505e9f1c
1267 3:8528aa5637f2
1267 3:8528aa5637f2
1268 4:2326846efdab
1268 4:2326846efdab
1269 5:904fa392b941
1269 5:904fa392b941
1270 6:e0cc66ef77e8
1270 6:e0cc66ef77e8
1271 7:013af1973af4
1271 7:013af1973af4
1272 8:d5d0dcbdc4d9
1272 8:d5d0dcbdc4d9
1273 9:24286f4ae135
1273 9:24286f4ae135
1274
1274
1275 $ hg log -qr e:
1275 $ hg log -qr e:
1276 6:e0cc66ef77e8
1276 6:e0cc66ef77e8
1277 7:013af1973af4
1277 7:013af1973af4
1278 8:d5d0dcbdc4d9
1278 8:d5d0dcbdc4d9
1279 9:24286f4ae135
1279 9:24286f4ae135
1280
1280
1281 $ hg log -qr :e
1281 $ hg log -qr :e
1282 0:2785f51eece5
1282 0:2785f51eece5
1283 1:d75937da8da0
1283 1:d75937da8da0
1284 2:5ed5505e9f1c
1284 2:5ed5505e9f1c
1285 3:8528aa5637f2
1285 3:8528aa5637f2
1286 4:2326846efdab
1286 4:2326846efdab
1287 5:904fa392b941
1287 5:904fa392b941
1288 6:e0cc66ef77e8
1288 6:e0cc66ef77e8
1289
1289
1290 issue2549 - correct optimizations
1290 issue2549 - correct optimizations
1291
1291
1292 $ try 'limit(1 or 2 or 3, 2) and not 2'
1292 $ try 'limit(1 or 2 or 3, 2) and not 2'
1293 (and
1293 (and
1294 (func
1294 (func
1295 (symbol 'limit')
1295 (symbol 'limit')
1296 (list
1296 (list
1297 (or
1297 (or
1298 (list
1298 (list
1299 (symbol '1')
1299 (symbol '1')
1300 (symbol '2')
1300 (symbol '2')
1301 (symbol '3')))
1301 (symbol '3')))
1302 (symbol '2')))
1302 (symbol '2')))
1303 (not
1303 (not
1304 (symbol '2')))
1304 (symbol '2')))
1305 * set:
1305 * set:
1306 <filteredset
1306 <filteredset
1307 <baseset [1, 2]>,
1307 <baseset [1, 2]>,
1308 <not
1308 <not
1309 <baseset [2]>>>
1309 <baseset [2]>>>
1310 1
1310 1
1311 $ try 'max(1 or 2) and not 2'
1311 $ try 'max(1 or 2) and not 2'
1312 (and
1312 (and
1313 (func
1313 (func
1314 (symbol 'max')
1314 (symbol 'max')
1315 (or
1315 (or
1316 (list
1316 (list
1317 (symbol '1')
1317 (symbol '1')
1318 (symbol '2'))))
1318 (symbol '2'))))
1319 (not
1319 (not
1320 (symbol '2')))
1320 (symbol '2')))
1321 * set:
1321 * set:
1322 <filteredset
1322 <filteredset
1323 <baseset
1323 <baseset
1324 <max
1324 <max
1325 <fullreposet+ 0:10>,
1325 <fullreposet+ 0:10>,
1326 <baseset [1, 2]>>>,
1326 <baseset [1, 2]>>>,
1327 <not
1327 <not
1328 <baseset [2]>>>
1328 <baseset [2]>>>
1329 $ try 'min(1 or 2) and not 1'
1329 $ try 'min(1 or 2) and not 1'
1330 (and
1330 (and
1331 (func
1331 (func
1332 (symbol 'min')
1332 (symbol 'min')
1333 (or
1333 (or
1334 (list
1334 (list
1335 (symbol '1')
1335 (symbol '1')
1336 (symbol '2'))))
1336 (symbol '2'))))
1337 (not
1337 (not
1338 (symbol '1')))
1338 (symbol '1')))
1339 * set:
1339 * set:
1340 <filteredset
1340 <filteredset
1341 <baseset
1341 <baseset
1342 <min
1342 <min
1343 <fullreposet+ 0:10>,
1343 <fullreposet+ 0:10>,
1344 <baseset [1, 2]>>>,
1344 <baseset [1, 2]>>>,
1345 <not
1345 <not
1346 <baseset [1]>>>
1346 <baseset [1]>>>
1347 $ try 'last(1 or 2, 1) and not 2'
1347 $ try 'last(1 or 2, 1) and not 2'
1348 (and
1348 (and
1349 (func
1349 (func
1350 (symbol 'last')
1350 (symbol 'last')
1351 (list
1351 (list
1352 (or
1352 (or
1353 (list
1353 (list
1354 (symbol '1')
1354 (symbol '1')
1355 (symbol '2')))
1355 (symbol '2')))
1356 (symbol '1')))
1356 (symbol '1')))
1357 (not
1357 (not
1358 (symbol '2')))
1358 (symbol '2')))
1359 * set:
1359 * set:
1360 <filteredset
1360 <filteredset
1361 <baseset [2]>,
1361 <baseset [2]>,
1362 <not
1362 <not
1363 <baseset [2]>>>
1363 <baseset [2]>>>
1364
1364
1365 issue4289 - ordering of built-ins
1365 issue4289 - ordering of built-ins
1366 $ hg log -M -q -r 3:2
1366 $ hg log -M -q -r 3:2
1367 3:8528aa5637f2
1367 3:8528aa5637f2
1368 2:5ed5505e9f1c
1368 2:5ed5505e9f1c
1369
1369
1370 test revsets started with 40-chars hash (issue3669)
1370 test revsets started with 40-chars hash (issue3669)
1371
1371
1372 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1372 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1373 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1373 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1374 9
1374 9
1375 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1375 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1376 8
1376 8
1377
1377
1378 test or-ed indirect predicates (issue3775)
1378 test or-ed indirect predicates (issue3775)
1379
1379
1380 $ log '6 or 6^1' | sort
1380 $ log '6 or 6^1' | sort
1381 5
1381 5
1382 6
1382 6
1383 $ log '6^1 or 6' | sort
1383 $ log '6^1 or 6' | sort
1384 5
1384 5
1385 6
1385 6
1386 $ log '4 or 4~1' | sort
1386 $ log '4 or 4~1' | sort
1387 2
1387 2
1388 4
1388 4
1389 $ log '4~1 or 4' | sort
1389 $ log '4~1 or 4' | sort
1390 2
1390 2
1391 4
1391 4
1392 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1392 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1393 0
1393 0
1394 1
1394 1
1395 2
1395 2
1396 3
1396 3
1397 4
1397 4
1398 5
1398 5
1399 6
1399 6
1400 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1400 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1401 0
1401 0
1402 1
1402 1
1403 2
1403 2
1404 3
1404 3
1405 4
1405 4
1406 5
1406 5
1407 6
1407 6
1408
1408
1409 tests for 'remote()' predicate:
1409 tests for 'remote()' predicate:
1410 #. (csets in remote) (id) (remote)
1410 #. (csets in remote) (id) (remote)
1411 1. less than local current branch "default"
1411 1. less than local current branch "default"
1412 2. same with local specified "default"
1412 2. same with local specified "default"
1413 3. more than local specified specified
1413 3. more than local specified specified
1414
1414
1415 $ hg clone --quiet -U . ../remote3
1415 $ hg clone --quiet -U . ../remote3
1416 $ cd ../remote3
1416 $ cd ../remote3
1417 $ hg update -q 7
1417 $ hg update -q 7
1418 $ echo r > r
1418 $ echo r > r
1419 $ hg ci -Aqm 10
1419 $ hg ci -Aqm 10
1420 $ log 'remote()'
1420 $ log 'remote()'
1421 7
1421 7
1422 $ log 'remote("a-b-c-")'
1422 $ log 'remote("a-b-c-")'
1423 2
1423 2
1424 $ cd ../repo
1424 $ cd ../repo
1425 $ log 'remote(".a.b.c.", "../remote3")'
1425 $ log 'remote(".a.b.c.", "../remote3")'
1426
1426
1427 tests for concatenation of strings/symbols by "##"
1427 tests for concatenation of strings/symbols by "##"
1428
1428
1429 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1429 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1430 (_concat
1430 (_concat
1431 (_concat
1431 (_concat
1432 (_concat
1432 (_concat
1433 (symbol '278')
1433 (symbol '278')
1434 (string '5f5'))
1434 (string '5f5'))
1435 (symbol '1ee'))
1435 (symbol '1ee'))
1436 (string 'ce5'))
1436 (string 'ce5'))
1437 * concatenated:
1437 * concatenated:
1438 (string '2785f51eece5')
1438 (string '2785f51eece5')
1439 * set:
1439 * set:
1440 <baseset [0]>
1440 <baseset [0]>
1441 0
1441 0
1442
1442
1443 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1443 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1444 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1444 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1445 (func
1445 (func
1446 (symbol 'cat4')
1446 (symbol 'cat4')
1447 (list
1447 (list
1448 (symbol '278')
1448 (symbol '278')
1449 (string '5f5')
1449 (string '5f5')
1450 (symbol '1ee')
1450 (symbol '1ee')
1451 (string 'ce5')))
1451 (string 'ce5')))
1452 * expanded:
1452 * expanded:
1453 (_concat
1453 (_concat
1454 (_concat
1454 (_concat
1455 (_concat
1455 (_concat
1456 (symbol '278')
1456 (symbol '278')
1457 (string '5f5'))
1457 (string '5f5'))
1458 (symbol '1ee'))
1458 (symbol '1ee'))
1459 (string 'ce5'))
1459 (string 'ce5'))
1460 * concatenated:
1460 * concatenated:
1461 (string '2785f51eece5')
1461 (string '2785f51eece5')
1462 * set:
1462 * set:
1463 <baseset [0]>
1463 <baseset [0]>
1464 0
1464 0
1465
1465
1466 (check concatenation in alias nesting)
1466 (check concatenation in alias nesting)
1467
1467
1468 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1468 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1469 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1469 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1470 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1470 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1471 0
1471 0
1472
1472
1473 (check operator priority)
1473 (check operator priority)
1474
1474
1475 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1475 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1476 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1476 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1477 0
1477 0
1478 4
1478 4
1479
1479
1480 $ cd ..
1480 $ cd ..
1481
1481
1482 prepare repository that has "default" branches of multiple roots
1482 prepare repository that has "default" branches of multiple roots
1483
1483
1484 $ hg init namedbranch
1484 $ hg init namedbranch
1485 $ cd namedbranch
1485 $ cd namedbranch
1486
1486
1487 $ echo default0 >> a
1487 $ echo default0 >> a
1488 $ hg ci -Aqm0
1488 $ hg ci -Aqm0
1489 $ echo default1 >> a
1489 $ echo default1 >> a
1490 $ hg ci -m1
1490 $ hg ci -m1
1491
1491
1492 $ hg branch -q stable
1492 $ hg branch -q stable
1493 $ echo stable2 >> a
1493 $ echo stable2 >> a
1494 $ hg ci -m2
1494 $ hg ci -m2
1495 $ echo stable3 >> a
1495 $ echo stable3 >> a
1496 $ hg ci -m3
1496 $ hg ci -m3
1497
1497
1498 $ hg update -q null
1498 $ hg update -q null
1499 $ echo default4 >> a
1499 $ echo default4 >> a
1500 $ hg ci -Aqm4
1500 $ hg ci -Aqm4
1501 $ echo default5 >> a
1501 $ echo default5 >> a
1502 $ hg ci -m5
1502 $ hg ci -m5
1503
1503
1504 "null" revision belongs to "default" branch (issue4683)
1504 "null" revision belongs to "default" branch (issue4683)
1505
1505
1506 $ log 'branch(null)'
1506 $ log 'branch(null)'
1507 0
1507 0
1508 1
1508 1
1509 4
1509 4
1510 5
1510 5
1511
1511
1512 "null" revision belongs to "default" branch, but it shouldn't appear in set
1512 "null" revision belongs to "default" branch, but it shouldn't appear in set
1513 unless explicitly specified (issue4682)
1513 unless explicitly specified (issue4682)
1514
1514
1515 $ log 'children(branch(default))'
1515 $ log 'children(branch(default))'
1516 1
1516 1
1517 2
1517 2
1518 5
1518 5
1519
1519
1520 $ cd ..
1520 $ cd ..
1521
1521
1522 test author/desc/keyword in problematic encoding
1522 test author/desc/keyword in problematic encoding
1523 # unicode: cp932:
1523 # unicode: cp932:
1524 # u30A2 0x83 0x41(= 'A')
1524 # u30A2 0x83 0x41(= 'A')
1525 # u30C2 0x83 0x61(= 'a')
1525 # u30C2 0x83 0x61(= 'a')
1526
1526
1527 $ hg init problematicencoding
1527 $ hg init problematicencoding
1528 $ cd problematicencoding
1528 $ cd problematicencoding
1529
1529
1530 $ $PYTHON > setup.sh <<EOF
1530 $ $PYTHON > setup.sh <<EOF
1531 > print u'''
1531 > print u'''
1532 > echo a > text
1532 > echo a > text
1533 > hg add text
1533 > hg add text
1534 > hg --encoding utf-8 commit -u '\u30A2' -m none
1534 > hg --encoding utf-8 commit -u '\u30A2' -m none
1535 > echo b > text
1535 > echo b > text
1536 > hg --encoding utf-8 commit -u '\u30C2' -m none
1536 > hg --encoding utf-8 commit -u '\u30C2' -m none
1537 > echo c > text
1537 > echo c > text
1538 > hg --encoding utf-8 commit -u none -m '\u30A2'
1538 > hg --encoding utf-8 commit -u none -m '\u30A2'
1539 > echo d > text
1539 > echo d > text
1540 > hg --encoding utf-8 commit -u none -m '\u30C2'
1540 > hg --encoding utf-8 commit -u none -m '\u30C2'
1541 > '''.encode('utf-8')
1541 > '''.encode('utf-8')
1542 > EOF
1542 > EOF
1543 $ sh < setup.sh
1543 $ sh < setup.sh
1544
1544
1545 test in problematic encoding
1545 test in problematic encoding
1546 $ $PYTHON > test.sh <<EOF
1546 $ $PYTHON > test.sh <<EOF
1547 > print u'''
1547 > print u'''
1548 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1548 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1549 > echo ====
1549 > echo ====
1550 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1550 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1551 > echo ====
1551 > echo ====
1552 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1552 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1553 > echo ====
1553 > echo ====
1554 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1554 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1555 > echo ====
1555 > echo ====
1556 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1556 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1557 > echo ====
1557 > echo ====
1558 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1558 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1559 > '''.encode('cp932')
1559 > '''.encode('cp932')
1560 > EOF
1560 > EOF
1561 $ sh < test.sh
1561 $ sh < test.sh
1562 0
1562 0
1563 ====
1563 ====
1564 1
1564 1
1565 ====
1565 ====
1566 2
1566 2
1567 ====
1567 ====
1568 3
1568 3
1569 ====
1569 ====
1570 0
1570 0
1571 2
1571 2
1572 ====
1572 ====
1573 1
1573 1
1574 3
1574 3
1575
1575
1576 test error message of bad revset
1576 test error message of bad revset
1577 $ hg log -r 'foo\\'
1577 $ hg log -r 'foo\\'
1578 hg: parse error at 3: syntax error in revset 'foo\\'
1578 hg: parse error at 3: syntax error in revset 'foo\\'
1579 (foo\\
1579 (foo\\
1580 ^ here)
1580 ^ here)
1581 [255]
1581 [255]
1582
1582
1583 $ cd ..
1583 $ cd ..
1584
1584
1585 Test that revset predicate of extension isn't loaded at failure of
1585 Test that revset predicate of extension isn't loaded at failure of
1586 loading it
1586 loading it
1587
1587
1588 $ cd repo
1588 $ cd repo
1589
1589
1590 $ cat <<EOF > $TESTTMP/custompredicate.py
1590 $ cat <<EOF > $TESTTMP/custompredicate.py
1591 > from mercurial import error, registrar, revset
1591 > from mercurial import error, registrar, revset
1592 >
1592 >
1593 > revsetpredicate = registrar.revsetpredicate()
1593 > revsetpredicate = registrar.revsetpredicate()
1594 >
1594 >
1595 > @revsetpredicate(b'custom1()')
1595 > @revsetpredicate(b'custom1()')
1596 > def custom1(repo, subset, x):
1596 > def custom1(repo, subset, x):
1597 > return revset.baseset([1])
1597 > return revset.baseset([1])
1598 >
1598 >
1599 > raise error.Abort(b'intentional failure of loading extension')
1599 > raise error.Abort(b'intentional failure of loading extension')
1600 > EOF
1600 > EOF
1601 $ cat <<EOF > .hg/hgrc
1601 $ cat <<EOF > .hg/hgrc
1602 > [extensions]
1602 > [extensions]
1603 > custompredicate = $TESTTMP/custompredicate.py
1603 > custompredicate = $TESTTMP/custompredicate.py
1604 > EOF
1604 > EOF
1605
1605
1606 $ hg debugrevspec "custom1()"
1606 $ hg debugrevspec "custom1()"
1607 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
1607 *** failed to import extension custompredicate from $TESTTMP/custompredicate.py: intentional failure of loading extension
1608 hg: parse error: unknown identifier: custom1
1608 hg: parse error: unknown identifier: custom1
1609 [255]
1609 [255]
1610
1610
1611 Test repo.anyrevs with customized revset overrides
1611 Test repo.anyrevs with customized revset overrides
1612
1612
1613 $ cat > $TESTTMP/printprevset.py <<EOF
1613 $ cat > $TESTTMP/printprevset.py <<EOF
1614 > from mercurial import encoding, registrar
1614 > from mercurial import encoding, registrar
1615 > cmdtable = {}
1615 > cmdtable = {}
1616 > command = registrar.command(cmdtable)
1616 > command = registrar.command(cmdtable)
1617 > @command(b'printprevset')
1617 > @command(b'printprevset')
1618 > def printprevset(ui, repo):
1618 > def printprevset(ui, repo):
1619 > alias = {}
1619 > alias = {}
1620 > p = encoding.environ.get(b'P')
1620 > p = encoding.environ.get(b'P')
1621 > if p:
1621 > if p:
1622 > alias[b'P'] = p
1622 > alias[b'P'] = p
1623 > revs = repo.anyrevs([b'P'], user=True, localalias=alias)
1623 > revs = repo.anyrevs([b'P'], user=True, localalias=alias)
1624 > ui.write(b'P=%r\n' % list(revs))
1624 > ui.write(b'P=%r\n' % list(revs))
1625 > EOF
1625 > EOF
1626
1626
1627 $ cat >> .hg/hgrc <<EOF
1627 $ cat >> .hg/hgrc <<EOF
1628 > custompredicate = !
1628 > custompredicate = !
1629 > printprevset = $TESTTMP/printprevset.py
1629 > printprevset = $TESTTMP/printprevset.py
1630 > EOF
1630 > EOF
1631
1631
1632 $ hg --config revsetalias.P=1 printprevset
1632 $ hg --config revsetalias.P=1 printprevset
1633 P=[1]
1633 P=[1]
1634 $ P=3 hg --config revsetalias.P=2 printprevset
1634 $ P=3 hg --config revsetalias.P=2 printprevset
1635 P=[3]
1635 P=[3]
1636
1636
1637 $ cd ..
1637 $ cd ..
1638
1638
1639 Test obsstore related revsets
1639 Test obsstore related revsets
1640
1640
1641 $ hg init repo1
1641 $ hg init repo1
1642 $ cd repo1
1642 $ cd repo1
1643 $ cat <<EOF >> .hg/hgrc
1643 $ cat <<EOF >> .hg/hgrc
1644 > [experimental]
1644 > [experimental]
1645 > evolution.createmarkers=True
1645 > evolution.createmarkers=True
1646 > EOF
1646 > EOF
1647
1647
1648 $ hg debugdrawdag <<'EOS'
1648 $ hg debugdrawdag <<'EOS'
1649 > F G
1649 > F G
1650 > |/ # split: B -> E, F
1650 > |/ # split: B -> E, F
1651 > B C D E # amend: B -> C -> D
1651 > B C D E # amend: B -> C -> D
1652 > \|/ | # amend: F -> G
1652 > \|/ | # amend: F -> G
1653 > A A Z # amend: A -> Z
1653 > A A Z # amend: A -> Z
1654 > EOS
1654 > EOS
1655 3 new orphan changesets
1655 3 new orphan changesets
1656 3 new content-divergent changesets
1656 3 new content-divergent changesets
1657
1657
1658 $ hg log -r 'successors(Z)' -T '{desc}\n'
1658 $ hg log -r 'successors(Z)' -T '{desc}\n'
1659 Z
1659 Z
1660
1660
1661 $ hg log -r 'successors(F)' -T '{desc}\n'
1661 $ hg log -r 'successors(F)' -T '{desc}\n'
1662 F
1662 F
1663 G
1663 G
1664
1664
1665 $ hg tag --remove --local C D E F G
1665 $ hg tag --remove --local C D E F G
1666
1666
1667 $ hg log -r 'successors(B)' -T '{desc}\n'
1667 $ hg log -r 'successors(B)' -T '{desc}\n'
1668 B
1668 B
1669 D
1669 D
1670 E
1670 E
1671 G
1671 G
1672
1672
1673 $ hg log -r 'successors(B)' -T '{desc}\n' --hidden
1673 $ hg log -r 'successors(B)' -T '{desc}\n' --hidden
1674 B
1674 B
1675 C
1675 C
1676 D
1676 D
1677 E
1677 E
1678 F
1678 F
1679 G
1679 G
1680
1680
1681 $ hg log -r 'successors(B)-obsolete()' -T '{desc}\n' --hidden
1681 $ hg log -r 'successors(B)-obsolete()' -T '{desc}\n' --hidden
1682 D
1682 D
1683 E
1683 E
1684 G
1684 G
1685
1685
1686 $ hg log -r 'successors(B+A)-contentdivergent()' -T '{desc}\n'
1686 $ hg log -r 'successors(B+A)-contentdivergent()' -T '{desc}\n'
1687 A
1687 A
1688 Z
1688 Z
1689 B
1689 B
1690
1690
1691 $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n'
1691 $ hg log -r 'successors(B+A)-contentdivergent()-obsolete()' -T '{desc}\n'
1692 Z
1692 Z
1693
1693
1694 Test `draft() & ::x` optimization
1694 Test `draft() & ::x` optimization
1695
1695
1696 $ hg init $TESTTMP/repo2
1696 $ hg init $TESTTMP/repo2
1697 $ cd $TESTTMP/repo2
1697 $ cd $TESTTMP/repo2
1698 $ hg debugdrawdag <<'EOS'
1698 $ hg debugdrawdag <<'EOS'
1699 > P5 S1
1699 > P5 S1
1700 > | |
1700 > | |
1701 > S2 | D3
1701 > S2 | D3
1702 > \|/
1702 > \|/
1703 > P4
1703 > P4
1704 > |
1704 > |
1705 > P3 D2
1705 > P3 D2
1706 > | |
1706 > | |
1707 > P2 D1
1707 > P2 D1
1708 > |/
1708 > |/
1709 > P1
1709 > P1
1710 > |
1710 > |
1711 > P0
1711 > P0
1712 > EOS
1712 > EOS
1713 $ hg phase --public -r P5
1713 $ hg phase --public -r P5
1714 $ hg phase --force --secret -r S1+S2
1714 $ hg phase --force --secret -r S1+S2
1715 $ hg log -G -T '{rev} {desc} {phase}' -r 'sort(all(), topo, topo.firstbranch=P5)'
1715 $ hg log -G -T '{rev} {desc} {phase}' -r 'sort(all(), topo, topo.firstbranch=P5)'
1716 o 8 P5 public
1716 o 8 P5 public
1717 |
1717 |
1718 | o 10 S1 secret
1718 | o 10 S1 secret
1719 | |
1719 | |
1720 | o 7 D3 draft
1720 | o 7 D3 draft
1721 |/
1721 |/
1722 | o 9 S2 secret
1722 | o 9 S2 secret
1723 |/
1723 |/
1724 o 6 P4 public
1724 o 6 P4 public
1725 |
1725 |
1726 o 5 P3 public
1726 o 5 P3 public
1727 |
1727 |
1728 o 3 P2 public
1728 o 3 P2 public
1729 |
1729 |
1730 | o 4 D2 draft
1730 | o 4 D2 draft
1731 | |
1731 | |
1732 | o 2 D1 draft
1732 | o 2 D1 draft
1733 |/
1733 |/
1734 o 1 P1 public
1734 o 1 P1 public
1735 |
1735 |
1736 o 0 P0 public
1736 o 0 P0 public
1737
1737
1738 $ hg debugrevspec --verify -p analyzed -p optimized 'draft() & ::(((S1+D1+P5)-D3)+S2)'
1738 $ hg debugrevspec --verify -p analyzed -p optimized 'draft() & ::(((S1+D1+P5)-D3)+S2)'
1739 * analyzed:
1739 * analyzed:
1740 (and
1740 (and
1741 (func
1741 (func
1742 (symbol 'draft')
1742 (symbol 'draft')
1743 None)
1743 None)
1744 (func
1744 (func
1745 (symbol 'ancestors')
1745 (symbol 'ancestors')
1746 (or
1746 (or
1747 (list
1747 (list
1748 (and
1748 (and
1749 (or
1749 (or
1750 (list
1750 (list
1751 (symbol 'S1')
1751 (symbol 'S1')
1752 (symbol 'D1')
1752 (symbol 'D1')
1753 (symbol 'P5')))
1753 (symbol 'P5')))
1754 (not
1754 (not
1755 (symbol 'D3')))
1755 (symbol 'D3')))
1756 (symbol 'S2')))))
1756 (symbol 'S2')))))
1757 * optimized:
1757 * optimized:
1758 (func
1758 (func
1759 (symbol '_phaseandancestors')
1759 (symbol '_phaseandancestors')
1760 (list
1760 (list
1761 (symbol 'draft')
1761 (symbol 'draft')
1762 (or
1762 (or
1763 (list
1763 (list
1764 (difference
1764 (difference
1765 (func
1765 (func
1766 (symbol '_list')
1766 (symbol '_list')
1767 (string 'S1\x00D1\x00P5'))
1767 (string 'S1\x00D1\x00P5'))
1768 (symbol 'D3'))
1768 (symbol 'D3'))
1769 (symbol 'S2')))))
1769 (symbol 'S2')))))
1770 $ hg debugrevspec --verify -p analyzed -p optimized 'secret() & ::9'
1770 $ hg debugrevspec --verify -p analyzed -p optimized 'secret() & ::9'
1771 * analyzed:
1771 * analyzed:
1772 (and
1772 (and
1773 (func
1773 (func
1774 (symbol 'secret')
1774 (symbol 'secret')
1775 None)
1775 None)
1776 (func
1776 (func
1777 (symbol 'ancestors')
1777 (symbol 'ancestors')
1778 (symbol '9')))
1778 (symbol '9')))
1779 * optimized:
1779 * optimized:
1780 (func
1780 (func
1781 (symbol '_phaseandancestors')
1781 (symbol '_phaseandancestors')
1782 (list
1782 (list
1783 (symbol 'secret')
1783 (symbol 'secret')
1784 (symbol '9')))
1784 (symbol '9')))
1785 $ hg debugrevspec --verify -p analyzed -p optimized '7 & ( (not public()) & ::(tag()) )'
1785 $ hg debugrevspec --verify -p analyzed -p optimized '7 & ( (not public()) & ::(tag()) )'
1786 * analyzed:
1786 * analyzed:
1787 (and
1787 (and
1788 (symbol '7')
1788 (symbol '7')
1789 (and
1789 (and
1790 (not
1790 (not
1791 (func
1791 (func
1792 (symbol 'public')
1792 (symbol 'public')
1793 None))
1793 None))
1794 (func
1794 (func
1795 (symbol 'ancestors')
1795 (symbol 'ancestors')
1796 (func
1796 (func
1797 (symbol 'tag')
1797 (symbol 'tag')
1798 None))))
1798 None))))
1799 * optimized:
1799 * optimized:
1800 (and
1800 (and
1801 (symbol '7')
1801 (symbol '7')
1802 (func
1802 (func
1803 (symbol '_phaseandancestors')
1803 (symbol '_phaseandancestors')
1804 (list
1804 (list
1805 (symbol '_notpublic')
1805 (symbol '_notpublic')
1806 (func
1806 (func
1807 (symbol 'tag')
1807 (symbol 'tag')
1808 None))))
1808 None))))
1809 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, 1)'
1809 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, 1)'
1810 * optimized:
1810 * optimized:
1811 (and
1811 (and
1812 (func
1812 (func
1813 (symbol '_notpublic')
1813 (symbol '_notpublic')
1814 None)
1814 None)
1815 (func
1815 (func
1816 (symbol 'ancestors')
1816 (symbol 'ancestors')
1817 (list
1817 (list
1818 (func
1818 (func
1819 (symbol '_list')
1819 (symbol '_list')
1820 (string 'S1\x00D2\x00P5'))
1820 (string 'S1\x00D2\x00P5'))
1821 (symbol '1'))))
1821 (symbol '1'))))
1822 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, depth=1)'
1822 $ hg debugrevspec --verify -p optimized '(not public()) & ancestors(S1+D2+P5, depth=1)'
1823 * optimized:
1823 * optimized:
1824 (and
1824 (and
1825 (func
1825 (func
1826 (symbol '_notpublic')
1826 (symbol '_notpublic')
1827 None)
1827 None)
1828 (func
1828 (func
1829 (symbol 'ancestors')
1829 (symbol 'ancestors')
1830 (list
1830 (list
1831 (func
1831 (func
1832 (symbol '_list')
1832 (symbol '_list')
1833 (string 'S1\x00D2\x00P5'))
1833 (string 'S1\x00D2\x00P5'))
1834 (keyvalue
1834 (keyvalue
1835 (symbol 'depth')
1835 (symbol 'depth')
1836 (symbol '1')))))
1836 (symbol '1')))))
1837
1837
1838 test commonancestors and its optimization
1838 test commonancestors and its optimization
1839
1839
1840 $ hg debugrevspec --verify -p analyzed -p optimized 'heads(commonancestors(head()))'
1840 $ hg debugrevspec --verify -p analyzed -p optimized 'heads(commonancestors(head()))'
1841 * analyzed:
1841 * analyzed:
1842 (func
1842 (func
1843 (symbol 'heads')
1843 (symbol 'heads')
1844 (func
1844 (func
1845 (symbol 'commonancestors')
1845 (symbol 'commonancestors')
1846 (func
1846 (func
1847 (symbol 'head')
1847 (symbol 'head')
1848 None)))
1848 None)))
1849 * optimized:
1849 * optimized:
1850 (func
1850 (func
1851 (symbol '_commonancestorheads')
1851 (symbol '_commonancestorheads')
1852 (func
1852 (func
1853 (symbol 'head')
1853 (symbol 'head')
1854 None))
1854 None))
General Comments 0
You need to be logged in to leave comments. Login now