##// END OF EJS Templates
help: filesets show hg status command
timeless@mozdev.org -
r26194:fc65d63e default
parent child Browse files
Show More
@@ -1,537 +1,537 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 re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 error,
14 error,
15 merge,
15 merge,
16 parser,
16 parser,
17 util,
17 util,
18 )
18 )
19
19
20 elements = {
20 elements = {
21 # token-type: binding-strength, primary, prefix, infix, suffix
21 # token-type: binding-strength, primary, prefix, infix, suffix
22 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
22 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
23 "-": (5, None, ("negate", 19), ("minus", 5), None),
23 "-": (5, None, ("negate", 19), ("minus", 5), None),
24 "not": (10, None, ("not", 10), None, None),
24 "not": (10, None, ("not", 10), None, None),
25 "!": (10, None, ("not", 10), None, None),
25 "!": (10, None, ("not", 10), None, None),
26 "and": (5, None, None, ("and", 5), None),
26 "and": (5, None, None, ("and", 5), None),
27 "&": (5, None, None, ("and", 5), None),
27 "&": (5, None, None, ("and", 5), None),
28 "or": (4, None, None, ("or", 4), None),
28 "or": (4, None, None, ("or", 4), None),
29 "|": (4, None, None, ("or", 4), None),
29 "|": (4, None, None, ("or", 4), None),
30 "+": (4, None, None, ("or", 4), None),
30 "+": (4, None, None, ("or", 4), None),
31 ",": (2, None, None, ("list", 2), None),
31 ",": (2, None, None, ("list", 2), None),
32 ")": (0, None, None, None, None),
32 ")": (0, None, None, None, None),
33 "symbol": (0, "symbol", None, None, None),
33 "symbol": (0, "symbol", None, None, None),
34 "string": (0, "string", None, None, None),
34 "string": (0, "string", None, None, None),
35 "end": (0, None, None, None, None),
35 "end": (0, None, None, None, None),
36 }
36 }
37
37
38 keywords = set(['and', 'or', 'not'])
38 keywords = set(['and', 'or', 'not'])
39
39
40 globchars = ".*{}[]?/\\_"
40 globchars = ".*{}[]?/\\_"
41
41
42 def tokenize(program):
42 def tokenize(program):
43 pos, l = 0, len(program)
43 pos, l = 0, len(program)
44 while pos < l:
44 while pos < l:
45 c = program[pos]
45 c = program[pos]
46 if c.isspace(): # skip inter-token whitespace
46 if c.isspace(): # skip inter-token whitespace
47 pass
47 pass
48 elif c in "(),-|&+!": # handle simple operators
48 elif c in "(),-|&+!": # handle simple operators
49 yield (c, None, pos)
49 yield (c, None, pos)
50 elif (c in '"\'' or c == 'r' and
50 elif (c in '"\'' or c == 'r' and
51 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
51 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
52 if c == 'r':
52 if c == 'r':
53 pos += 1
53 pos += 1
54 c = program[pos]
54 c = program[pos]
55 decode = lambda x: x
55 decode = lambda x: x
56 else:
56 else:
57 decode = lambda x: x.decode('string-escape')
57 decode = lambda x: x.decode('string-escape')
58 pos += 1
58 pos += 1
59 s = pos
59 s = pos
60 while pos < l: # find closing quote
60 while pos < l: # find closing quote
61 d = program[pos]
61 d = program[pos]
62 if d == '\\': # skip over escaped characters
62 if d == '\\': # skip over escaped characters
63 pos += 2
63 pos += 2
64 continue
64 continue
65 if d == c:
65 if d == c:
66 yield ('string', decode(program[s:pos]), s)
66 yield ('string', decode(program[s:pos]), s)
67 break
67 break
68 pos += 1
68 pos += 1
69 else:
69 else:
70 raise error.ParseError(_("unterminated string"), s)
70 raise error.ParseError(_("unterminated string"), s)
71 elif c.isalnum() or c in globchars or ord(c) > 127:
71 elif c.isalnum() or c in globchars or ord(c) > 127:
72 # gather up a symbol/keyword
72 # gather up a symbol/keyword
73 s = pos
73 s = pos
74 pos += 1
74 pos += 1
75 while pos < l: # find end of symbol
75 while pos < l: # find end of symbol
76 d = program[pos]
76 d = program[pos]
77 if not (d.isalnum() or d in globchars or ord(d) > 127):
77 if not (d.isalnum() or d in globchars or ord(d) > 127):
78 break
78 break
79 pos += 1
79 pos += 1
80 sym = program[s:pos]
80 sym = program[s:pos]
81 if sym in keywords: # operator keywords
81 if sym in keywords: # operator keywords
82 yield (sym, None, s)
82 yield (sym, None, s)
83 else:
83 else:
84 yield ('symbol', sym, s)
84 yield ('symbol', sym, s)
85 pos -= 1
85 pos -= 1
86 else:
86 else:
87 raise error.ParseError(_("syntax error"), pos)
87 raise error.ParseError(_("syntax error"), pos)
88 pos += 1
88 pos += 1
89 yield ('end', None, pos)
89 yield ('end', None, pos)
90
90
91 def parse(expr):
91 def parse(expr):
92 p = parser.parser(elements)
92 p = parser.parser(elements)
93 tree, pos = p.parse(tokenize(expr))
93 tree, pos = p.parse(tokenize(expr))
94 if pos != len(expr):
94 if pos != len(expr):
95 raise error.ParseError(_("invalid token"), pos)
95 raise error.ParseError(_("invalid token"), pos)
96 return tree
96 return tree
97
97
98 def getstring(x, err):
98 def getstring(x, err):
99 if x and (x[0] == 'string' or x[0] == 'symbol'):
99 if x and (x[0] == 'string' or x[0] == 'symbol'):
100 return x[1]
100 return x[1]
101 raise error.ParseError(err)
101 raise error.ParseError(err)
102
102
103 def getset(mctx, x):
103 def getset(mctx, x):
104 if not x:
104 if not x:
105 raise error.ParseError(_("missing argument"))
105 raise error.ParseError(_("missing argument"))
106 return methods[x[0]](mctx, *x[1:])
106 return methods[x[0]](mctx, *x[1:])
107
107
108 def stringset(mctx, x):
108 def stringset(mctx, x):
109 m = mctx.matcher([x])
109 m = mctx.matcher([x])
110 return [f for f in mctx.subset if m(f)]
110 return [f for f in mctx.subset if m(f)]
111
111
112 def andset(mctx, x, y):
112 def andset(mctx, x, y):
113 return getset(mctx.narrow(getset(mctx, x)), y)
113 return getset(mctx.narrow(getset(mctx, x)), y)
114
114
115 def orset(mctx, x, y):
115 def orset(mctx, x, y):
116 # needs optimizing
116 # needs optimizing
117 xl = getset(mctx, x)
117 xl = getset(mctx, x)
118 yl = getset(mctx, y)
118 yl = getset(mctx, y)
119 return xl + [f for f in yl if f not in xl]
119 return xl + [f for f in yl if f not in xl]
120
120
121 def notset(mctx, x):
121 def notset(mctx, x):
122 s = set(getset(mctx, x))
122 s = set(getset(mctx, x))
123 return [r for r in mctx.subset if r not in s]
123 return [r for r in mctx.subset if r not in s]
124
124
125 def minusset(mctx, x, y):
125 def minusset(mctx, x, y):
126 xl = getset(mctx, x)
126 xl = getset(mctx, x)
127 yl = set(getset(mctx, y))
127 yl = set(getset(mctx, y))
128 return [f for f in xl if f not in yl]
128 return [f for f in xl if f not in yl]
129
129
130 def listset(mctx, a, b):
130 def listset(mctx, a, b):
131 raise error.ParseError(_("can't use a list in this context"))
131 raise error.ParseError(_("can't use a list in this context"))
132
132
133 def modified(mctx, x):
133 def modified(mctx, x):
134 """``modified()``
134 """``modified()``
135 File that is modified according to status.
135 File that is modified according to :hg:`status`.
136 """
136 """
137 # i18n: "modified" is a keyword
137 # i18n: "modified" is a keyword
138 getargs(x, 0, 0, _("modified takes no arguments"))
138 getargs(x, 0, 0, _("modified takes no arguments"))
139 s = mctx.status().modified
139 s = mctx.status().modified
140 return [f for f in mctx.subset if f in s]
140 return [f for f in mctx.subset if f in s]
141
141
142 def added(mctx, x):
142 def added(mctx, x):
143 """``added()``
143 """``added()``
144 File that is added according to status.
144 File that is added according to :hg:`status`.
145 """
145 """
146 # i18n: "added" is a keyword
146 # i18n: "added" is a keyword
147 getargs(x, 0, 0, _("added takes no arguments"))
147 getargs(x, 0, 0, _("added takes no arguments"))
148 s = mctx.status().added
148 s = mctx.status().added
149 return [f for f in mctx.subset if f in s]
149 return [f for f in mctx.subset if f in s]
150
150
151 def removed(mctx, x):
151 def removed(mctx, x):
152 """``removed()``
152 """``removed()``
153 File that is removed according to status.
153 File that is removed according to :hg:`status`.
154 """
154 """
155 # i18n: "removed" is a keyword
155 # i18n: "removed" is a keyword
156 getargs(x, 0, 0, _("removed takes no arguments"))
156 getargs(x, 0, 0, _("removed takes no arguments"))
157 s = mctx.status().removed
157 s = mctx.status().removed
158 return [f for f in mctx.subset if f in s]
158 return [f for f in mctx.subset if f in s]
159
159
160 def deleted(mctx, x):
160 def deleted(mctx, x):
161 """``deleted()``
161 """``deleted()``
162 File that is deleted according to status.
162 File that is deleted according to :hg:`status`.
163 """
163 """
164 # i18n: "deleted" is a keyword
164 # i18n: "deleted" is a keyword
165 getargs(x, 0, 0, _("deleted takes no arguments"))
165 getargs(x, 0, 0, _("deleted takes no arguments"))
166 s = mctx.status().deleted
166 s = mctx.status().deleted
167 return [f for f in mctx.subset if f in s]
167 return [f for f in mctx.subset if f in s]
168
168
169 def unknown(mctx, x):
169 def unknown(mctx, x):
170 """``unknown()``
170 """``unknown()``
171 File that is unknown according to status. These files will only be
171 File that is unknown according to :hg:`status`. These files will only be
172 considered if this predicate is used.
172 considered if this predicate is used.
173 """
173 """
174 # i18n: "unknown" is a keyword
174 # i18n: "unknown" is a keyword
175 getargs(x, 0, 0, _("unknown takes no arguments"))
175 getargs(x, 0, 0, _("unknown takes no arguments"))
176 s = mctx.status().unknown
176 s = mctx.status().unknown
177 return [f for f in mctx.subset if f in s]
177 return [f for f in mctx.subset if f in s]
178
178
179 def ignored(mctx, x):
179 def ignored(mctx, x):
180 """``ignored()``
180 """``ignored()``
181 File that is ignored according to status. These files will only be
181 File that is ignored according to :hg:`status`. These files will only be
182 considered if this predicate is used.
182 considered if this predicate is used.
183 """
183 """
184 # i18n: "ignored" is a keyword
184 # i18n: "ignored" is a keyword
185 getargs(x, 0, 0, _("ignored takes no arguments"))
185 getargs(x, 0, 0, _("ignored takes no arguments"))
186 s = mctx.status().ignored
186 s = mctx.status().ignored
187 return [f for f in mctx.subset if f in s]
187 return [f for f in mctx.subset if f in s]
188
188
189 def clean(mctx, x):
189 def clean(mctx, x):
190 """``clean()``
190 """``clean()``
191 File that is clean according to status.
191 File that is clean according to :hg:`status`.
192 """
192 """
193 # i18n: "clean" is a keyword
193 # i18n: "clean" is a keyword
194 getargs(x, 0, 0, _("clean takes no arguments"))
194 getargs(x, 0, 0, _("clean takes no arguments"))
195 s = mctx.status().clean
195 s = mctx.status().clean
196 return [f for f in mctx.subset if f in s]
196 return [f for f in mctx.subset if f in s]
197
197
198 def func(mctx, a, b):
198 def func(mctx, a, b):
199 if a[0] == 'symbol' and a[1] in symbols:
199 if a[0] == 'symbol' and a[1] in symbols:
200 return symbols[a[1]](mctx, b)
200 return symbols[a[1]](mctx, b)
201
201
202 keep = lambda fn: getattr(fn, '__doc__', None) is not None
202 keep = lambda fn: getattr(fn, '__doc__', None) is not None
203
203
204 syms = [s for (s, fn) in symbols.items() if keep(fn)]
204 syms = [s for (s, fn) in symbols.items() if keep(fn)]
205 raise error.UnknownIdentifier(a[1], syms)
205 raise error.UnknownIdentifier(a[1], syms)
206
206
207 def getlist(x):
207 def getlist(x):
208 if not x:
208 if not x:
209 return []
209 return []
210 if x[0] == 'list':
210 if x[0] == 'list':
211 return getlist(x[1]) + [x[2]]
211 return getlist(x[1]) + [x[2]]
212 return [x]
212 return [x]
213
213
214 def getargs(x, min, max, err):
214 def getargs(x, min, max, err):
215 l = getlist(x)
215 l = getlist(x)
216 if len(l) < min or len(l) > max:
216 if len(l) < min or len(l) > max:
217 raise error.ParseError(err)
217 raise error.ParseError(err)
218 return l
218 return l
219
219
220 def binary(mctx, x):
220 def binary(mctx, x):
221 """``binary()``
221 """``binary()``
222 File that appears to be binary (contains NUL bytes).
222 File that appears to be binary (contains NUL bytes).
223 """
223 """
224 # i18n: "binary" is a keyword
224 # i18n: "binary" is a keyword
225 getargs(x, 0, 0, _("binary takes no arguments"))
225 getargs(x, 0, 0, _("binary takes no arguments"))
226 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
226 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
227
227
228 def exec_(mctx, x):
228 def exec_(mctx, x):
229 """``exec()``
229 """``exec()``
230 File that is marked as executable.
230 File that is marked as executable.
231 """
231 """
232 # i18n: "exec" is a keyword
232 # i18n: "exec" is a keyword
233 getargs(x, 0, 0, _("exec takes no arguments"))
233 getargs(x, 0, 0, _("exec takes no arguments"))
234 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
234 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
235
235
236 def symlink(mctx, x):
236 def symlink(mctx, x):
237 """``symlink()``
237 """``symlink()``
238 File that is marked as a symlink.
238 File that is marked as a symlink.
239 """
239 """
240 # i18n: "symlink" is a keyword
240 # i18n: "symlink" is a keyword
241 getargs(x, 0, 0, _("symlink takes no arguments"))
241 getargs(x, 0, 0, _("symlink takes no arguments"))
242 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
242 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
243
243
244 def resolved(mctx, x):
244 def resolved(mctx, x):
245 """``resolved()``
245 """``resolved()``
246 File that is marked resolved according to the resolve state.
246 File that is marked resolved according to the resolve state.
247 """
247 """
248 # i18n: "resolved" is a keyword
248 # i18n: "resolved" is a keyword
249 getargs(x, 0, 0, _("resolved takes no arguments"))
249 getargs(x, 0, 0, _("resolved takes no arguments"))
250 if mctx.ctx.rev() is not None:
250 if mctx.ctx.rev() is not None:
251 return []
251 return []
252 ms = merge.mergestate(mctx.ctx.repo())
252 ms = merge.mergestate(mctx.ctx.repo())
253 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
253 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
254
254
255 def unresolved(mctx, x):
255 def unresolved(mctx, x):
256 """``unresolved()``
256 """``unresolved()``
257 File that is marked unresolved according to the resolve state.
257 File that is marked unresolved according to the resolve state.
258 """
258 """
259 # i18n: "unresolved" is a keyword
259 # i18n: "unresolved" is a keyword
260 getargs(x, 0, 0, _("unresolved takes no arguments"))
260 getargs(x, 0, 0, _("unresolved takes no arguments"))
261 if mctx.ctx.rev() is not None:
261 if mctx.ctx.rev() is not None:
262 return []
262 return []
263 ms = merge.mergestate(mctx.ctx.repo())
263 ms = merge.mergestate(mctx.ctx.repo())
264 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
264 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
265
265
266 def hgignore(mctx, x):
266 def hgignore(mctx, x):
267 """``hgignore()``
267 """``hgignore()``
268 File that matches the active .hgignore pattern.
268 File that matches the active .hgignore pattern.
269 """
269 """
270 # i18n: "hgignore" is a keyword
270 # i18n: "hgignore" is a keyword
271 getargs(x, 0, 0, _("hgignore takes no arguments"))
271 getargs(x, 0, 0, _("hgignore takes no arguments"))
272 ignore = mctx.ctx.repo().dirstate._ignore
272 ignore = mctx.ctx.repo().dirstate._ignore
273 return [f for f in mctx.subset if ignore(f)]
273 return [f for f in mctx.subset if ignore(f)]
274
274
275 def portable(mctx, x):
275 def portable(mctx, x):
276 """``portable()``
276 """``portable()``
277 File that has a portable name. (This doesn't include filenames with case
277 File that has a portable name. (This doesn't include filenames with case
278 collisions.)
278 collisions.)
279 """
279 """
280 # i18n: "portable" is a keyword
280 # i18n: "portable" is a keyword
281 getargs(x, 0, 0, _("portable takes no arguments"))
281 getargs(x, 0, 0, _("portable takes no arguments"))
282 checkwinfilename = util.checkwinfilename
282 checkwinfilename = util.checkwinfilename
283 return [f for f in mctx.subset if checkwinfilename(f) is None]
283 return [f for f in mctx.subset if checkwinfilename(f) is None]
284
284
285 def grep(mctx, x):
285 def grep(mctx, x):
286 """``grep(regex)``
286 """``grep(regex)``
287 File contains the given regular expression.
287 File contains the given regular expression.
288 """
288 """
289 try:
289 try:
290 # i18n: "grep" is a keyword
290 # i18n: "grep" is a keyword
291 r = re.compile(getstring(x, _("grep requires a pattern")))
291 r = re.compile(getstring(x, _("grep requires a pattern")))
292 except re.error as e:
292 except re.error as e:
293 raise error.ParseError(_('invalid match pattern: %s') % e)
293 raise error.ParseError(_('invalid match pattern: %s') % e)
294 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
294 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
295
295
296 def _sizetomax(s):
296 def _sizetomax(s):
297 try:
297 try:
298 s = s.strip().lower()
298 s = s.strip().lower()
299 for k, v in util._sizeunits:
299 for k, v in util._sizeunits:
300 if s.endswith(k):
300 if s.endswith(k):
301 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
301 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
302 n = s[:-len(k)]
302 n = s[:-len(k)]
303 inc = 1.0
303 inc = 1.0
304 if "." in n:
304 if "." in n:
305 inc /= 10 ** len(n.split(".")[1])
305 inc /= 10 ** len(n.split(".")[1])
306 return int((float(n) + inc) * v) - 1
306 return int((float(n) + inc) * v) - 1
307 # no extension, this is a precise value
307 # no extension, this is a precise value
308 return int(s)
308 return int(s)
309 except ValueError:
309 except ValueError:
310 raise error.ParseError(_("couldn't parse size: %s") % s)
310 raise error.ParseError(_("couldn't parse size: %s") % s)
311
311
312 def size(mctx, x):
312 def size(mctx, x):
313 """``size(expression)``
313 """``size(expression)``
314 File size matches the given expression. Examples:
314 File size matches the given expression. Examples:
315
315
316 - 1k (files from 1024 to 2047 bytes)
316 - 1k (files from 1024 to 2047 bytes)
317 - < 20k (files less than 20480 bytes)
317 - < 20k (files less than 20480 bytes)
318 - >= .5MB (files at least 524288 bytes)
318 - >= .5MB (files at least 524288 bytes)
319 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
319 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
320 """
320 """
321
321
322 # i18n: "size" is a keyword
322 # i18n: "size" is a keyword
323 expr = getstring(x, _("size requires an expression")).strip()
323 expr = getstring(x, _("size requires an expression")).strip()
324 if '-' in expr: # do we have a range?
324 if '-' in expr: # do we have a range?
325 a, b = expr.split('-', 1)
325 a, b = expr.split('-', 1)
326 a = util.sizetoint(a)
326 a = util.sizetoint(a)
327 b = util.sizetoint(b)
327 b = util.sizetoint(b)
328 m = lambda x: x >= a and x <= b
328 m = lambda x: x >= a and x <= b
329 elif expr.startswith("<="):
329 elif expr.startswith("<="):
330 a = util.sizetoint(expr[2:])
330 a = util.sizetoint(expr[2:])
331 m = lambda x: x <= a
331 m = lambda x: x <= a
332 elif expr.startswith("<"):
332 elif expr.startswith("<"):
333 a = util.sizetoint(expr[1:])
333 a = util.sizetoint(expr[1:])
334 m = lambda x: x < a
334 m = lambda x: x < a
335 elif expr.startswith(">="):
335 elif expr.startswith(">="):
336 a = util.sizetoint(expr[2:])
336 a = util.sizetoint(expr[2:])
337 m = lambda x: x >= a
337 m = lambda x: x >= a
338 elif expr.startswith(">"):
338 elif expr.startswith(">"):
339 a = util.sizetoint(expr[1:])
339 a = util.sizetoint(expr[1:])
340 m = lambda x: x > a
340 m = lambda x: x > a
341 elif expr[0].isdigit or expr[0] == '.':
341 elif expr[0].isdigit or expr[0] == '.':
342 a = util.sizetoint(expr)
342 a = util.sizetoint(expr)
343 b = _sizetomax(expr)
343 b = _sizetomax(expr)
344 m = lambda x: x >= a and x <= b
344 m = lambda x: x >= a and x <= b
345 else:
345 else:
346 raise error.ParseError(_("couldn't parse size: %s") % expr)
346 raise error.ParseError(_("couldn't parse size: %s") % expr)
347
347
348 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
348 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
349
349
350 def encoding(mctx, x):
350 def encoding(mctx, x):
351 """``encoding(name)``
351 """``encoding(name)``
352 File can be successfully decoded with the given character
352 File can be successfully decoded with the given character
353 encoding. May not be useful for encodings other than ASCII and
353 encoding. May not be useful for encodings other than ASCII and
354 UTF-8.
354 UTF-8.
355 """
355 """
356
356
357 # i18n: "encoding" is a keyword
357 # i18n: "encoding" is a keyword
358 enc = getstring(x, _("encoding requires an encoding name"))
358 enc = getstring(x, _("encoding requires an encoding name"))
359
359
360 s = []
360 s = []
361 for f in mctx.existing():
361 for f in mctx.existing():
362 d = mctx.ctx[f].data()
362 d = mctx.ctx[f].data()
363 try:
363 try:
364 d.decode(enc)
364 d.decode(enc)
365 except LookupError:
365 except LookupError:
366 raise util.Abort(_("unknown encoding '%s'") % enc)
366 raise util.Abort(_("unknown encoding '%s'") % enc)
367 except UnicodeDecodeError:
367 except UnicodeDecodeError:
368 continue
368 continue
369 s.append(f)
369 s.append(f)
370
370
371 return s
371 return s
372
372
373 def eol(mctx, x):
373 def eol(mctx, x):
374 """``eol(style)``
374 """``eol(style)``
375 File contains newlines of the given style (dos, unix, mac). Binary
375 File contains newlines of the given style (dos, unix, mac). Binary
376 files are excluded, files with mixed line endings match multiple
376 files are excluded, files with mixed line endings match multiple
377 styles.
377 styles.
378 """
378 """
379
379
380 # i18n: "encoding" is a keyword
380 # i18n: "encoding" is a keyword
381 enc = getstring(x, _("encoding requires an encoding name"))
381 enc = getstring(x, _("encoding requires an encoding name"))
382
382
383 s = []
383 s = []
384 for f in mctx.existing():
384 for f in mctx.existing():
385 d = mctx.ctx[f].data()
385 d = mctx.ctx[f].data()
386 if util.binary(d):
386 if util.binary(d):
387 continue
387 continue
388 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
388 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
389 s.append(f)
389 s.append(f)
390 elif enc == 'unix' and re.search('(?<!\r)\n', d):
390 elif enc == 'unix' and re.search('(?<!\r)\n', d):
391 s.append(f)
391 s.append(f)
392 elif enc == 'mac' and re.search('\r(?!\n)', d):
392 elif enc == 'mac' and re.search('\r(?!\n)', d):
393 s.append(f)
393 s.append(f)
394 return s
394 return s
395
395
396 def copied(mctx, x):
396 def copied(mctx, x):
397 """``copied()``
397 """``copied()``
398 File that is recorded as being copied.
398 File that is recorded as being copied.
399 """
399 """
400 # i18n: "copied" is a keyword
400 # i18n: "copied" is a keyword
401 getargs(x, 0, 0, _("copied takes no arguments"))
401 getargs(x, 0, 0, _("copied takes no arguments"))
402 s = []
402 s = []
403 for f in mctx.subset:
403 for f in mctx.subset:
404 p = mctx.ctx[f].parents()
404 p = mctx.ctx[f].parents()
405 if p and p[0].path() != f:
405 if p and p[0].path() != f:
406 s.append(f)
406 s.append(f)
407 return s
407 return s
408
408
409 def subrepo(mctx, x):
409 def subrepo(mctx, x):
410 """``subrepo([pattern])``
410 """``subrepo([pattern])``
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 = sorted(ctx.substate)
416 sstate = sorted(ctx.substate)
417 if x:
417 if x:
418 # i18n: "subrepo" is a keyword
418 # i18n: "subrepo" is a keyword
419 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
419 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
420
420
421 from . import match as matchmod # avoid circular import issues
421 from . import match as matchmod # avoid circular import issues
422 fast = not matchmod.patkind(pat)
422 fast = not matchmod.patkind(pat)
423 if fast:
423 if fast:
424 def m(s):
424 def m(s):
425 return (s == pat)
425 return (s == pat)
426 else:
426 else:
427 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
427 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
428 return [sub for sub in sstate if m(sub)]
428 return [sub for sub in sstate if m(sub)]
429 else:
429 else:
430 return [sub for sub in sstate]
430 return [sub for sub in sstate]
431
431
432 symbols = {
432 symbols = {
433 'added': added,
433 'added': added,
434 'binary': binary,
434 'binary': binary,
435 'clean': clean,
435 'clean': clean,
436 'copied': copied,
436 'copied': copied,
437 'deleted': deleted,
437 'deleted': deleted,
438 'encoding': encoding,
438 'encoding': encoding,
439 'eol': eol,
439 'eol': eol,
440 'exec': exec_,
440 'exec': exec_,
441 'grep': grep,
441 'grep': grep,
442 'ignored': ignored,
442 'ignored': ignored,
443 'hgignore': hgignore,
443 'hgignore': hgignore,
444 'modified': modified,
444 'modified': modified,
445 'portable': portable,
445 'portable': portable,
446 'removed': removed,
446 'removed': removed,
447 'resolved': resolved,
447 'resolved': resolved,
448 'size': size,
448 'size': size,
449 'symlink': symlink,
449 'symlink': symlink,
450 'unknown': unknown,
450 'unknown': unknown,
451 'unresolved': unresolved,
451 'unresolved': unresolved,
452 'subrepo': subrepo,
452 'subrepo': subrepo,
453 }
453 }
454
454
455 methods = {
455 methods = {
456 'string': stringset,
456 'string': stringset,
457 'symbol': stringset,
457 'symbol': stringset,
458 'and': andset,
458 'and': andset,
459 'or': orset,
459 'or': orset,
460 'minus': minusset,
460 'minus': minusset,
461 'list': listset,
461 'list': listset,
462 'group': getset,
462 'group': getset,
463 'not': notset,
463 'not': notset,
464 'func': func,
464 'func': func,
465 }
465 }
466
466
467 class matchctx(object):
467 class matchctx(object):
468 def __init__(self, ctx, subset=None, status=None):
468 def __init__(self, ctx, subset=None, status=None):
469 self.ctx = ctx
469 self.ctx = ctx
470 self.subset = subset
470 self.subset = subset
471 self._status = status
471 self._status = status
472 def status(self):
472 def status(self):
473 return self._status
473 return self._status
474 def matcher(self, patterns):
474 def matcher(self, patterns):
475 return self.ctx.match(patterns)
475 return self.ctx.match(patterns)
476 def filter(self, files):
476 def filter(self, files):
477 return [f for f in files if f in self.subset]
477 return [f for f in files if f in self.subset]
478 def existing(self):
478 def existing(self):
479 if self._status is not None:
479 if self._status is not None:
480 removed = set(self._status[3])
480 removed = set(self._status[3])
481 unknown = set(self._status[4] + self._status[5])
481 unknown = set(self._status[4] + self._status[5])
482 else:
482 else:
483 removed = set()
483 removed = set()
484 unknown = set()
484 unknown = set()
485 return (f for f in self.subset
485 return (f for f in self.subset
486 if (f in self.ctx and f not in removed) or f in unknown)
486 if (f in self.ctx and f not in removed) or f in unknown)
487 def narrow(self, files):
487 def narrow(self, files):
488 return matchctx(self.ctx, self.filter(files), self._status)
488 return matchctx(self.ctx, self.filter(files), self._status)
489
489
490 def _intree(funcs, tree):
490 def _intree(funcs, tree):
491 if isinstance(tree, tuple):
491 if isinstance(tree, tuple):
492 if tree[0] == 'func' and tree[1][0] == 'symbol':
492 if tree[0] == 'func' and tree[1][0] == 'symbol':
493 if tree[1][1] in funcs:
493 if tree[1][1] in funcs:
494 return True
494 return True
495 for s in tree[1:]:
495 for s in tree[1:]:
496 if _intree(funcs, s):
496 if _intree(funcs, s):
497 return True
497 return True
498 return False
498 return False
499
499
500 # filesets using matchctx.existing()
500 # filesets using matchctx.existing()
501 _existingcallers = [
501 _existingcallers = [
502 'binary',
502 'binary',
503 'exec',
503 'exec',
504 'grep',
504 'grep',
505 'size',
505 'size',
506 'symlink',
506 'symlink',
507 ]
507 ]
508
508
509 def getfileset(ctx, expr):
509 def getfileset(ctx, expr):
510 tree = parse(expr)
510 tree = parse(expr)
511
511
512 # do we need status info?
512 # do we need status info?
513 if (_intree(['modified', 'added', 'removed', 'deleted',
513 if (_intree(['modified', 'added', 'removed', 'deleted',
514 'unknown', 'ignored', 'clean'], tree) or
514 'unknown', 'ignored', 'clean'], tree) or
515 # Using matchctx.existing() on a workingctx requires us to check
515 # Using matchctx.existing() on a workingctx requires us to check
516 # for deleted files.
516 # for deleted files.
517 (ctx.rev() is None and _intree(_existingcallers, tree))):
517 (ctx.rev() is None and _intree(_existingcallers, tree))):
518 unknown = _intree(['unknown'], tree)
518 unknown = _intree(['unknown'], tree)
519 ignored = _intree(['ignored'], tree)
519 ignored = _intree(['ignored'], tree)
520
520
521 r = ctx.repo()
521 r = ctx.repo()
522 status = r.status(ctx.p1(), ctx,
522 status = r.status(ctx.p1(), ctx,
523 unknown=unknown, ignored=ignored, clean=True)
523 unknown=unknown, ignored=ignored, clean=True)
524 subset = []
524 subset = []
525 for c in status:
525 for c in status:
526 subset.extend(c)
526 subset.extend(c)
527 else:
527 else:
528 status = None
528 status = None
529 subset = list(ctx.walk(ctx.match([])))
529 subset = list(ctx.walk(ctx.match([])))
530
530
531 return getset(matchctx(ctx, subset, status), tree)
531 return getset(matchctx(ctx, subset, status), tree)
532
532
533 def prettyformat(tree):
533 def prettyformat(tree):
534 return parser.prettyformat(tree, ('string', 'symbol'))
534 return parser.prettyformat(tree, ('string', 'symbol'))
535
535
536 # tell hggettext to extract docstrings from these functions:
536 # tell hggettext to extract docstrings from these functions:
537 i18nfunctions = symbols.values()
537 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now