##// END OF EJS Templates
fileset: treat encoding and eol as the predicate calling _existing...
FUJIWARA Katsunori -
r27459:2f15253e default
parent child Browse files
Show More
@@ -1,547 +1,549
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 = parser.unescapestr
57 decode = parser.unescapestr
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 :hg:`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 :hg:`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 :hg:`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 Alias for ``missing()``.
162 Alias for ``missing()``.
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 missing(mctx, x):
169 def missing(mctx, x):
170 """``missing()``
170 """``missing()``
171 File that is missing according to :hg:`status`.
171 File that is missing according to :hg:`status`.
172 """
172 """
173 # i18n: "missing" is a keyword
173 # i18n: "missing" is a keyword
174 getargs(x, 0, 0, _("missing takes no arguments"))
174 getargs(x, 0, 0, _("missing takes no arguments"))
175 s = mctx.status().deleted
175 s = mctx.status().deleted
176 return [f for f in mctx.subset if f in s]
176 return [f for f in mctx.subset if f in s]
177
177
178 def unknown(mctx, x):
178 def unknown(mctx, x):
179 """``unknown()``
179 """``unknown()``
180 File that is unknown according to :hg:`status`. These files will only be
180 File that is unknown according to :hg:`status`. These files will only be
181 considered if this predicate is used.
181 considered if this predicate is used.
182 """
182 """
183 # i18n: "unknown" is a keyword
183 # i18n: "unknown" is a keyword
184 getargs(x, 0, 0, _("unknown takes no arguments"))
184 getargs(x, 0, 0, _("unknown takes no arguments"))
185 s = mctx.status().unknown
185 s = mctx.status().unknown
186 return [f for f in mctx.subset if f in s]
186 return [f for f in mctx.subset if f in s]
187
187
188 def ignored(mctx, x):
188 def ignored(mctx, x):
189 """``ignored()``
189 """``ignored()``
190 File that is ignored according to :hg:`status`. These files will only be
190 File that is ignored according to :hg:`status`. These files will only be
191 considered if this predicate is used.
191 considered if this predicate is used.
192 """
192 """
193 # i18n: "ignored" is a keyword
193 # i18n: "ignored" is a keyword
194 getargs(x, 0, 0, _("ignored takes no arguments"))
194 getargs(x, 0, 0, _("ignored takes no arguments"))
195 s = mctx.status().ignored
195 s = mctx.status().ignored
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 clean(mctx, x):
198 def clean(mctx, x):
199 """``clean()``
199 """``clean()``
200 File that is clean according to :hg:`status`.
200 File that is clean according to :hg:`status`.
201 """
201 """
202 # i18n: "clean" is a keyword
202 # i18n: "clean" is a keyword
203 getargs(x, 0, 0, _("clean takes no arguments"))
203 getargs(x, 0, 0, _("clean takes no arguments"))
204 s = mctx.status().clean
204 s = mctx.status().clean
205 return [f for f in mctx.subset if f in s]
205 return [f for f in mctx.subset if f in s]
206
206
207 def func(mctx, a, b):
207 def func(mctx, a, b):
208 if a[0] == 'symbol' and a[1] in symbols:
208 if a[0] == 'symbol' and a[1] in symbols:
209 return symbols[a[1]](mctx, b)
209 return symbols[a[1]](mctx, b)
210
210
211 keep = lambda fn: getattr(fn, '__doc__', None) is not None
211 keep = lambda fn: getattr(fn, '__doc__', None) is not None
212
212
213 syms = [s for (s, fn) in symbols.items() if keep(fn)]
213 syms = [s for (s, fn) in symbols.items() if keep(fn)]
214 raise error.UnknownIdentifier(a[1], syms)
214 raise error.UnknownIdentifier(a[1], syms)
215
215
216 def getlist(x):
216 def getlist(x):
217 if not x:
217 if not x:
218 return []
218 return []
219 if x[0] == 'list':
219 if x[0] == 'list':
220 return getlist(x[1]) + [x[2]]
220 return getlist(x[1]) + [x[2]]
221 return [x]
221 return [x]
222
222
223 def getargs(x, min, max, err):
223 def getargs(x, min, max, err):
224 l = getlist(x)
224 l = getlist(x)
225 if len(l) < min or len(l) > max:
225 if len(l) < min or len(l) > max:
226 raise error.ParseError(err)
226 raise error.ParseError(err)
227 return l
227 return l
228
228
229 def binary(mctx, x):
229 def binary(mctx, x):
230 """``binary()``
230 """``binary()``
231 File that appears to be binary (contains NUL bytes).
231 File that appears to be binary (contains NUL bytes).
232 """
232 """
233 # i18n: "binary" is a keyword
233 # i18n: "binary" is a keyword
234 getargs(x, 0, 0, _("binary takes no arguments"))
234 getargs(x, 0, 0, _("binary takes no arguments"))
235 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
235 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
236
236
237 def exec_(mctx, x):
237 def exec_(mctx, x):
238 """``exec()``
238 """``exec()``
239 File that is marked as executable.
239 File that is marked as executable.
240 """
240 """
241 # i18n: "exec" is a keyword
241 # i18n: "exec" is a keyword
242 getargs(x, 0, 0, _("exec takes no arguments"))
242 getargs(x, 0, 0, _("exec takes no arguments"))
243 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
243 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
244
244
245 def symlink(mctx, x):
245 def symlink(mctx, x):
246 """``symlink()``
246 """``symlink()``
247 File that is marked as a symlink.
247 File that is marked as a symlink.
248 """
248 """
249 # i18n: "symlink" is a keyword
249 # i18n: "symlink" is a keyword
250 getargs(x, 0, 0, _("symlink takes no arguments"))
250 getargs(x, 0, 0, _("symlink takes no arguments"))
251 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
251 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
252
252
253 def resolved(mctx, x):
253 def resolved(mctx, x):
254 """``resolved()``
254 """``resolved()``
255 File that is marked resolved according to :hg:`resolve -l`.
255 File that is marked resolved according to :hg:`resolve -l`.
256 """
256 """
257 # i18n: "resolved" is a keyword
257 # i18n: "resolved" is a keyword
258 getargs(x, 0, 0, _("resolved takes no arguments"))
258 getargs(x, 0, 0, _("resolved takes no arguments"))
259 if mctx.ctx.rev() is not None:
259 if mctx.ctx.rev() is not None:
260 return []
260 return []
261 ms = merge.mergestate.read(mctx.ctx.repo())
261 ms = merge.mergestate.read(mctx.ctx.repo())
262 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
262 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
263
263
264 def unresolved(mctx, x):
264 def unresolved(mctx, x):
265 """``unresolved()``
265 """``unresolved()``
266 File that is marked unresolved according to :hg:`resolve -l`.
266 File that is marked unresolved according to :hg:`resolve -l`.
267 """
267 """
268 # i18n: "unresolved" is a keyword
268 # i18n: "unresolved" is a keyword
269 getargs(x, 0, 0, _("unresolved takes no arguments"))
269 getargs(x, 0, 0, _("unresolved takes no arguments"))
270 if mctx.ctx.rev() is not None:
270 if mctx.ctx.rev() is not None:
271 return []
271 return []
272 ms = merge.mergestate.read(mctx.ctx.repo())
272 ms = merge.mergestate.read(mctx.ctx.repo())
273 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
273 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
274
274
275 def hgignore(mctx, x):
275 def hgignore(mctx, x):
276 """``hgignore()``
276 """``hgignore()``
277 File that matches the active .hgignore pattern.
277 File that matches the active .hgignore pattern.
278 """
278 """
279 # i18n: "hgignore" is a keyword
279 # i18n: "hgignore" is a keyword
280 getargs(x, 0, 0, _("hgignore takes no arguments"))
280 getargs(x, 0, 0, _("hgignore takes no arguments"))
281 ignore = mctx.ctx.repo().dirstate._ignore
281 ignore = mctx.ctx.repo().dirstate._ignore
282 return [f for f in mctx.subset if ignore(f)]
282 return [f for f in mctx.subset if ignore(f)]
283
283
284 def portable(mctx, x):
284 def portable(mctx, x):
285 """``portable()``
285 """``portable()``
286 File that has a portable name. (This doesn't include filenames with case
286 File that has a portable name. (This doesn't include filenames with case
287 collisions.)
287 collisions.)
288 """
288 """
289 # i18n: "portable" is a keyword
289 # i18n: "portable" is a keyword
290 getargs(x, 0, 0, _("portable takes no arguments"))
290 getargs(x, 0, 0, _("portable takes no arguments"))
291 checkwinfilename = util.checkwinfilename
291 checkwinfilename = util.checkwinfilename
292 return [f for f in mctx.subset if checkwinfilename(f) is None]
292 return [f for f in mctx.subset if checkwinfilename(f) is None]
293
293
294 def grep(mctx, x):
294 def grep(mctx, x):
295 """``grep(regex)``
295 """``grep(regex)``
296 File contains the given regular expression.
296 File contains the given regular expression.
297 """
297 """
298 try:
298 try:
299 # i18n: "grep" is a keyword
299 # i18n: "grep" is a keyword
300 r = re.compile(getstring(x, _("grep requires a pattern")))
300 r = re.compile(getstring(x, _("grep requires a pattern")))
301 except re.error as e:
301 except re.error as e:
302 raise error.ParseError(_('invalid match pattern: %s') % e)
302 raise error.ParseError(_('invalid match pattern: %s') % e)
303 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
303 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
304
304
305 def _sizetomax(s):
305 def _sizetomax(s):
306 try:
306 try:
307 s = s.strip().lower()
307 s = s.strip().lower()
308 for k, v in util._sizeunits:
308 for k, v in util._sizeunits:
309 if s.endswith(k):
309 if s.endswith(k):
310 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
310 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
311 n = s[:-len(k)]
311 n = s[:-len(k)]
312 inc = 1.0
312 inc = 1.0
313 if "." in n:
313 if "." in n:
314 inc /= 10 ** len(n.split(".")[1])
314 inc /= 10 ** len(n.split(".")[1])
315 return int((float(n) + inc) * v) - 1
315 return int((float(n) + inc) * v) - 1
316 # no extension, this is a precise value
316 # no extension, this is a precise value
317 return int(s)
317 return int(s)
318 except ValueError:
318 except ValueError:
319 raise error.ParseError(_("couldn't parse size: %s") % s)
319 raise error.ParseError(_("couldn't parse size: %s") % s)
320
320
321 def size(mctx, x):
321 def size(mctx, x):
322 """``size(expression)``
322 """``size(expression)``
323 File size matches the given expression. Examples:
323 File size matches the given expression. Examples:
324
324
325 - 1k (files from 1024 to 2047 bytes)
325 - 1k (files from 1024 to 2047 bytes)
326 - < 20k (files less than 20480 bytes)
326 - < 20k (files less than 20480 bytes)
327 - >= .5MB (files at least 524288 bytes)
327 - >= .5MB (files at least 524288 bytes)
328 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
328 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
329 """
329 """
330
330
331 # i18n: "size" is a keyword
331 # i18n: "size" is a keyword
332 expr = getstring(x, _("size requires an expression")).strip()
332 expr = getstring(x, _("size requires an expression")).strip()
333 if '-' in expr: # do we have a range?
333 if '-' in expr: # do we have a range?
334 a, b = expr.split('-', 1)
334 a, b = expr.split('-', 1)
335 a = util.sizetoint(a)
335 a = util.sizetoint(a)
336 b = util.sizetoint(b)
336 b = util.sizetoint(b)
337 m = lambda x: x >= a and x <= b
337 m = lambda x: x >= a and x <= b
338 elif expr.startswith("<="):
338 elif expr.startswith("<="):
339 a = util.sizetoint(expr[2:])
339 a = util.sizetoint(expr[2:])
340 m = lambda x: x <= a
340 m = lambda x: x <= a
341 elif expr.startswith("<"):
341 elif expr.startswith("<"):
342 a = util.sizetoint(expr[1:])
342 a = util.sizetoint(expr[1:])
343 m = lambda x: x < a
343 m = lambda x: x < a
344 elif expr.startswith(">="):
344 elif expr.startswith(">="):
345 a = util.sizetoint(expr[2:])
345 a = util.sizetoint(expr[2:])
346 m = lambda x: x >= a
346 m = lambda x: x >= a
347 elif expr.startswith(">"):
347 elif expr.startswith(">"):
348 a = util.sizetoint(expr[1:])
348 a = util.sizetoint(expr[1:])
349 m = lambda x: x > a
349 m = lambda x: x > a
350 elif expr[0].isdigit or expr[0] == '.':
350 elif expr[0].isdigit or expr[0] == '.':
351 a = util.sizetoint(expr)
351 a = util.sizetoint(expr)
352 b = _sizetomax(expr)
352 b = _sizetomax(expr)
353 m = lambda x: x >= a and x <= b
353 m = lambda x: x >= a and x <= b
354 else:
354 else:
355 raise error.ParseError(_("couldn't parse size: %s") % expr)
355 raise error.ParseError(_("couldn't parse size: %s") % expr)
356
356
357 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
357 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
358
358
359 def encoding(mctx, x):
359 def encoding(mctx, x):
360 """``encoding(name)``
360 """``encoding(name)``
361 File can be successfully decoded with the given character
361 File can be successfully decoded with the given character
362 encoding. May not be useful for encodings other than ASCII and
362 encoding. May not be useful for encodings other than ASCII and
363 UTF-8.
363 UTF-8.
364 """
364 """
365
365
366 # i18n: "encoding" is a keyword
366 # i18n: "encoding" is a keyword
367 enc = getstring(x, _("encoding requires an encoding name"))
367 enc = getstring(x, _("encoding requires an encoding name"))
368
368
369 s = []
369 s = []
370 for f in mctx.existing():
370 for f in mctx.existing():
371 d = mctx.ctx[f].data()
371 d = mctx.ctx[f].data()
372 try:
372 try:
373 d.decode(enc)
373 d.decode(enc)
374 except LookupError:
374 except LookupError:
375 raise error.Abort(_("unknown encoding '%s'") % enc)
375 raise error.Abort(_("unknown encoding '%s'") % enc)
376 except UnicodeDecodeError:
376 except UnicodeDecodeError:
377 continue
377 continue
378 s.append(f)
378 s.append(f)
379
379
380 return s
380 return s
381
381
382 def eol(mctx, x):
382 def eol(mctx, x):
383 """``eol(style)``
383 """``eol(style)``
384 File contains newlines of the given style (dos, unix, mac). Binary
384 File contains newlines of the given style (dos, unix, mac). Binary
385 files are excluded, files with mixed line endings match multiple
385 files are excluded, files with mixed line endings match multiple
386 styles.
386 styles.
387 """
387 """
388
388
389 # i18n: "encoding" is a keyword
389 # i18n: "encoding" is a keyword
390 enc = getstring(x, _("encoding requires an encoding name"))
390 enc = getstring(x, _("encoding requires an encoding name"))
391
391
392 s = []
392 s = []
393 for f in mctx.existing():
393 for f in mctx.existing():
394 d = mctx.ctx[f].data()
394 d = mctx.ctx[f].data()
395 if util.binary(d):
395 if util.binary(d):
396 continue
396 continue
397 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
397 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
398 s.append(f)
398 s.append(f)
399 elif enc == 'unix' and re.search('(?<!\r)\n', d):
399 elif enc == 'unix' and re.search('(?<!\r)\n', d):
400 s.append(f)
400 s.append(f)
401 elif enc == 'mac' and re.search('\r(?!\n)', d):
401 elif enc == 'mac' and re.search('\r(?!\n)', d):
402 s.append(f)
402 s.append(f)
403 return s
403 return s
404
404
405 def copied(mctx, x):
405 def copied(mctx, x):
406 """``copied()``
406 """``copied()``
407 File that is recorded as being copied.
407 File that is recorded as being copied.
408 """
408 """
409 # i18n: "copied" is a keyword
409 # i18n: "copied" is a keyword
410 getargs(x, 0, 0, _("copied takes no arguments"))
410 getargs(x, 0, 0, _("copied takes no arguments"))
411 s = []
411 s = []
412 for f in mctx.subset:
412 for f in mctx.subset:
413 p = mctx.ctx[f].parents()
413 p = mctx.ctx[f].parents()
414 if p and p[0].path() != f:
414 if p and p[0].path() != f:
415 s.append(f)
415 s.append(f)
416 return s
416 return s
417
417
418 def subrepo(mctx, x):
418 def subrepo(mctx, x):
419 """``subrepo([pattern])``
419 """``subrepo([pattern])``
420 Subrepositories whose paths match the given pattern.
420 Subrepositories whose paths match the given pattern.
421 """
421 """
422 # i18n: "subrepo" is a keyword
422 # i18n: "subrepo" is a keyword
423 getargs(x, 0, 1, _("subrepo takes at most one argument"))
423 getargs(x, 0, 1, _("subrepo takes at most one argument"))
424 ctx = mctx.ctx
424 ctx = mctx.ctx
425 sstate = sorted(ctx.substate)
425 sstate = sorted(ctx.substate)
426 if x:
426 if x:
427 # i18n: "subrepo" is a keyword
427 # i18n: "subrepo" is a keyword
428 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
428 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
429
429
430 from . import match as matchmod # avoid circular import issues
430 from . import match as matchmod # avoid circular import issues
431 fast = not matchmod.patkind(pat)
431 fast = not matchmod.patkind(pat)
432 if fast:
432 if fast:
433 def m(s):
433 def m(s):
434 return (s == pat)
434 return (s == pat)
435 else:
435 else:
436 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
436 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
437 return [sub for sub in sstate if m(sub)]
437 return [sub for sub in sstate if m(sub)]
438 else:
438 else:
439 return [sub for sub in sstate]
439 return [sub for sub in sstate]
440
440
441 symbols = {
441 symbols = {
442 'added': added,
442 'added': added,
443 'binary': binary,
443 'binary': binary,
444 'clean': clean,
444 'clean': clean,
445 'copied': copied,
445 'copied': copied,
446 'deleted': deleted,
446 'deleted': deleted,
447 'encoding': encoding,
447 'encoding': encoding,
448 'eol': eol,
448 'eol': eol,
449 'exec': exec_,
449 'exec': exec_,
450 'grep': grep,
450 'grep': grep,
451 'ignored': ignored,
451 'ignored': ignored,
452 'hgignore': hgignore,
452 'hgignore': hgignore,
453 'missing': missing,
453 'missing': missing,
454 'modified': modified,
454 'modified': modified,
455 'portable': portable,
455 'portable': portable,
456 'removed': removed,
456 'removed': removed,
457 'resolved': resolved,
457 'resolved': resolved,
458 'size': size,
458 'size': size,
459 'symlink': symlink,
459 'symlink': symlink,
460 'unknown': unknown,
460 'unknown': unknown,
461 'unresolved': unresolved,
461 'unresolved': unresolved,
462 'subrepo': subrepo,
462 'subrepo': subrepo,
463 }
463 }
464
464
465 methods = {
465 methods = {
466 'string': stringset,
466 'string': stringset,
467 'symbol': stringset,
467 'symbol': stringset,
468 'and': andset,
468 'and': andset,
469 'or': orset,
469 'or': orset,
470 'minus': minusset,
470 'minus': minusset,
471 'list': listset,
471 'list': listset,
472 'group': getset,
472 'group': getset,
473 'not': notset,
473 'not': notset,
474 'func': func,
474 'func': func,
475 }
475 }
476
476
477 class matchctx(object):
477 class matchctx(object):
478 def __init__(self, ctx, subset=None, status=None):
478 def __init__(self, ctx, subset=None, status=None):
479 self.ctx = ctx
479 self.ctx = ctx
480 self.subset = subset
480 self.subset = subset
481 self._status = status
481 self._status = status
482 def status(self):
482 def status(self):
483 return self._status
483 return self._status
484 def matcher(self, patterns):
484 def matcher(self, patterns):
485 return self.ctx.match(patterns)
485 return self.ctx.match(patterns)
486 def filter(self, files):
486 def filter(self, files):
487 return [f for f in files if f in self.subset]
487 return [f for f in files if f in self.subset]
488 def existing(self):
488 def existing(self):
489 if self._status is not None:
489 if self._status is not None:
490 removed = set(self._status[3])
490 removed = set(self._status[3])
491 unknown = set(self._status[4] + self._status[5])
491 unknown = set(self._status[4] + self._status[5])
492 else:
492 else:
493 removed = set()
493 removed = set()
494 unknown = set()
494 unknown = set()
495 return (f for f in self.subset
495 return (f for f in self.subset
496 if (f in self.ctx and f not in removed) or f in unknown)
496 if (f in self.ctx and f not in removed) or f in unknown)
497 def narrow(self, files):
497 def narrow(self, files):
498 return matchctx(self.ctx, self.filter(files), self._status)
498 return matchctx(self.ctx, self.filter(files), self._status)
499
499
500 def _intree(funcs, tree):
500 def _intree(funcs, tree):
501 if isinstance(tree, tuple):
501 if isinstance(tree, tuple):
502 if tree[0] == 'func' and tree[1][0] == 'symbol':
502 if tree[0] == 'func' and tree[1][0] == 'symbol':
503 if tree[1][1] in funcs:
503 if tree[1][1] in funcs:
504 return True
504 return True
505 for s in tree[1:]:
505 for s in tree[1:]:
506 if _intree(funcs, s):
506 if _intree(funcs, s):
507 return True
507 return True
508 return False
508 return False
509
509
510 # filesets using matchctx.existing()
510 # filesets using matchctx.existing()
511 _existingcallers = [
511 _existingcallers = [
512 'binary',
512 'binary',
513 'encoding',
514 'eol',
513 'exec',
515 'exec',
514 'grep',
516 'grep',
515 'size',
517 'size',
516 'symlink',
518 'symlink',
517 ]
519 ]
518
520
519 def getfileset(ctx, expr):
521 def getfileset(ctx, expr):
520 tree = parse(expr)
522 tree = parse(expr)
521
523
522 # do we need status info?
524 # do we need status info?
523 if (_intree(['modified', 'added', 'removed', 'deleted',
525 if (_intree(['modified', 'added', 'removed', 'deleted',
524 'missing', 'unknown', 'ignored', 'clean'], tree) or
526 'missing', 'unknown', 'ignored', 'clean'], tree) or
525 # Using matchctx.existing() on a workingctx requires us to check
527 # Using matchctx.existing() on a workingctx requires us to check
526 # for deleted files.
528 # for deleted files.
527 (ctx.rev() is None and _intree(_existingcallers, tree))):
529 (ctx.rev() is None and _intree(_existingcallers, tree))):
528 unknown = _intree(['unknown'], tree)
530 unknown = _intree(['unknown'], tree)
529 ignored = _intree(['ignored'], tree)
531 ignored = _intree(['ignored'], tree)
530
532
531 r = ctx.repo()
533 r = ctx.repo()
532 status = r.status(ctx.p1(), ctx,
534 status = r.status(ctx.p1(), ctx,
533 unknown=unknown, ignored=ignored, clean=True)
535 unknown=unknown, ignored=ignored, clean=True)
534 subset = []
536 subset = []
535 for c in status:
537 for c in status:
536 subset.extend(c)
538 subset.extend(c)
537 else:
539 else:
538 status = None
540 status = None
539 subset = list(ctx.walk(ctx.match([])))
541 subset = list(ctx.walk(ctx.match([])))
540
542
541 return getset(matchctx(ctx, subset, status), tree)
543 return getset(matchctx(ctx, subset, status), tree)
542
544
543 def prettyformat(tree):
545 def prettyformat(tree):
544 return parser.prettyformat(tree, ('string', 'symbol'))
546 return parser.prettyformat(tree, ('string', 'symbol'))
545
547
546 # tell hggettext to extract docstrings from these functions:
548 # tell hggettext to extract docstrings from these functions:
547 i18nfunctions = symbols.values()
549 i18nfunctions = symbols.values()
@@ -1,312 +1,330
1 $ fileset() {
1 $ fileset() {
2 > hg debugfileset "$@"
2 > hg debugfileset "$@"
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 a1
21 a1
22 $ fileset -v 'a*'
22 $ fileset -v 'a*'
23 ('symbol', 'a*')
23 ('symbol', 'a*')
24 a1
24 a1
25 a2
25 a2
26 $ fileset -v '"re:a\d"'
26 $ fileset -v '"re:a\d"'
27 ('string', 're:a\\d')
27 ('string', 're:a\\d')
28 a1
28 a1
29 a2
29 a2
30 $ fileset -v 'a1 or a2'
30 $ fileset -v 'a1 or a2'
31 (or
31 (or
32 ('symbol', 'a1')
32 ('symbol', 'a1')
33 ('symbol', 'a2'))
33 ('symbol', 'a2'))
34 a1
34 a1
35 a2
35 a2
36 $ fileset 'a1 | a2'
36 $ fileset 'a1 | a2'
37 a1
37 a1
38 a2
38 a2
39 $ fileset 'a* and "*1"'
39 $ fileset 'a* and "*1"'
40 a1
40 a1
41 $ fileset 'a* & "*1"'
41 $ fileset 'a* & "*1"'
42 a1
42 a1
43 $ fileset 'not (r"a*")'
43 $ fileset 'not (r"a*")'
44 b1
44 b1
45 b2
45 b2
46 $ fileset '! ("a*")'
46 $ fileset '! ("a*")'
47 b1
47 b1
48 b2
48 b2
49 $ fileset 'a* - a1'
49 $ fileset 'a* - a1'
50 a2
50 a2
51 $ fileset 'a_b'
51 $ fileset 'a_b'
52 $ fileset '"\xy"'
52 $ fileset '"\xy"'
53 hg: parse error: invalid \x escape
53 hg: parse error: invalid \x escape
54 [255]
54 [255]
55
55
56 Test files status
56 Test files status
57
57
58 $ rm a1
58 $ rm a1
59 $ hg rm a2
59 $ hg rm a2
60 $ echo b >> b2
60 $ echo b >> b2
61 $ hg cp b1 c1
61 $ hg cp b1 c1
62 $ echo c > c2
62 $ echo c > c2
63 $ echo c > c3
63 $ echo c > c3
64 $ cat > .hgignore <<EOF
64 $ cat > .hgignore <<EOF
65 > \.hgignore
65 > \.hgignore
66 > 2$
66 > 2$
67 > EOF
67 > EOF
68 $ fileset 'modified()'
68 $ fileset 'modified()'
69 b2
69 b2
70 $ fileset 'added()'
70 $ fileset 'added()'
71 c1
71 c1
72 $ fileset 'removed()'
72 $ fileset 'removed()'
73 a2
73 a2
74 $ fileset 'deleted()'
74 $ fileset 'deleted()'
75 a1
75 a1
76 $ fileset 'missing()'
76 $ fileset 'missing()'
77 a1
77 a1
78 $ fileset 'unknown()'
78 $ fileset 'unknown()'
79 c3
79 c3
80 $ fileset 'ignored()'
80 $ fileset 'ignored()'
81 .hgignore
81 .hgignore
82 c2
82 c2
83 $ fileset 'hgignore()'
83 $ fileset 'hgignore()'
84 a2
84 a2
85 b2
85 b2
86 $ fileset 'clean()'
86 $ fileset 'clean()'
87 b1
87 b1
88 $ fileset 'copied()'
88 $ fileset 'copied()'
89 c1
89 c1
90
90
91 Test files properties
91 Test files properties
92
92
93 >>> file('bin', 'wb').write('\0a')
93 >>> file('bin', 'wb').write('\0a')
94 $ fileset 'binary()'
94 $ fileset 'binary()'
95 $ fileset 'binary() and unknown()'
95 $ fileset 'binary() and unknown()'
96 bin
96 bin
97 $ echo '^bin$' >> .hgignore
97 $ echo '^bin$' >> .hgignore
98 $ fileset 'binary() and ignored()'
98 $ fileset 'binary() and ignored()'
99 bin
99 bin
100 $ hg add bin
100 $ hg add bin
101 $ fileset 'binary()'
101 $ fileset 'binary()'
102 bin
102 bin
103
103
104 $ fileset 'grep("b{1}")'
104 $ fileset 'grep("b{1}")'
105 b2
105 b2
106 c1
106 c1
107 b1
107 b1
108 $ fileset 'grep("missingparens(")'
108 $ fileset 'grep("missingparens(")'
109 hg: parse error: invalid match pattern: unbalanced parenthesis
109 hg: parse error: invalid match pattern: unbalanced parenthesis
110 [255]
110 [255]
111
111
112 #if execbit
112 #if execbit
113 $ chmod +x b2
113 $ chmod +x b2
114 $ fileset 'exec()'
114 $ fileset 'exec()'
115 b2
115 b2
116 #endif
116 #endif
117
117
118 #if symlink
118 #if symlink
119 $ ln -s b2 b2link
119 $ ln -s b2 b2link
120 $ fileset 'symlink() and unknown()'
120 $ fileset 'symlink() and unknown()'
121 b2link
121 b2link
122 $ hg add b2link
122 $ hg add b2link
123 #endif
123 #endif
124
124
125 #if no-windows
125 #if no-windows
126 $ echo foo > con.xml
126 $ echo foo > con.xml
127 $ fileset 'not portable()'
127 $ fileset 'not portable()'
128 con.xml
128 con.xml
129 $ hg --config ui.portablefilenames=ignore add con.xml
129 $ hg --config ui.portablefilenames=ignore add con.xml
130 #endif
130 #endif
131
131
132 >>> file('1k', 'wb').write(' '*1024)
132 >>> file('1k', 'wb').write(' '*1024)
133 >>> file('2k', 'wb').write(' '*2048)
133 >>> file('2k', 'wb').write(' '*2048)
134 $ hg add 1k 2k
134 $ hg add 1k 2k
135 $ fileset 'size("bar")'
135 $ fileset 'size("bar")'
136 hg: parse error: couldn't parse size: bar
136 hg: parse error: couldn't parse size: bar
137 [255]
137 [255]
138 $ fileset 'size(1k)'
138 $ fileset 'size(1k)'
139 1k
139 1k
140 $ fileset '(1k or 2k) and size("< 2k")'
140 $ fileset '(1k or 2k) and size("< 2k")'
141 1k
141 1k
142 $ fileset '(1k or 2k) and size("<=2k")'
142 $ fileset '(1k or 2k) and size("<=2k")'
143 1k
143 1k
144 2k
144 2k
145 $ fileset '(1k or 2k) and size("> 1k")'
145 $ fileset '(1k or 2k) and size("> 1k")'
146 2k
146 2k
147 $ fileset '(1k or 2k) and size(">=1K")'
147 $ fileset '(1k or 2k) and size(">=1K")'
148 1k
148 1k
149 2k
149 2k
150 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
150 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
151 1k
151 1k
152 $ fileset 'size("1M")'
152 $ fileset 'size("1M")'
153 $ fileset 'size("1 GB")'
153 $ fileset 'size("1 GB")'
154
154
155 Test merge states
155 Test merge states
156
156
157 $ hg ci -m manychanges
157 $ hg ci -m manychanges
158 $ hg up -C 0
158 $ hg up -C 0
159 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
159 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
160 $ echo c >> b2
160 $ echo c >> b2
161 $ hg ci -m diverging b2
161 $ hg ci -m diverging b2
162 created new head
162 created new head
163 $ fileset 'resolved()'
163 $ fileset 'resolved()'
164 $ fileset 'unresolved()'
164 $ fileset 'unresolved()'
165 $ hg merge
165 $ hg merge
166 merging b2
166 merging b2
167 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
167 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
168 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
168 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
169 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
169 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
170 [1]
170 [1]
171 $ fileset 'resolved()'
171 $ fileset 'resolved()'
172 $ fileset 'unresolved()'
172 $ fileset 'unresolved()'
173 b2
173 b2
174 $ echo e > b2
174 $ echo e > b2
175 $ hg resolve -m b2
175 $ hg resolve -m b2
176 (no more unresolved files)
176 (no more unresolved files)
177 $ fileset 'resolved()'
177 $ fileset 'resolved()'
178 b2
178 b2
179 $ fileset 'unresolved()'
179 $ fileset 'unresolved()'
180 $ hg ci -m merge
180 $ hg ci -m merge
181
181
182 Test subrepo predicate
182 Test subrepo predicate
183
183
184 $ hg init sub
184 $ hg init sub
185 $ echo a > sub/suba
185 $ echo a > sub/suba
186 $ hg -R sub add sub/suba
186 $ hg -R sub add sub/suba
187 $ hg -R sub ci -m sub
187 $ hg -R sub ci -m sub
188 $ echo 'sub = sub' > .hgsub
188 $ echo 'sub = sub' > .hgsub
189 $ hg init sub2
189 $ hg init sub2
190 $ echo b > sub2/b
190 $ echo b > sub2/b
191 $ hg -R sub2 ci -Am sub2
191 $ hg -R sub2 ci -Am sub2
192 adding b
192 adding b
193 $ echo 'sub2 = sub2' >> .hgsub
193 $ echo 'sub2 = sub2' >> .hgsub
194 $ fileset 'subrepo()'
194 $ fileset 'subrepo()'
195 $ hg add .hgsub
195 $ hg add .hgsub
196 $ fileset 'subrepo()'
196 $ fileset 'subrepo()'
197 sub
197 sub
198 sub2
198 sub2
199 $ fileset 'subrepo("sub")'
199 $ fileset 'subrepo("sub")'
200 sub
200 sub
201 $ fileset 'subrepo("glob:*")'
201 $ fileset 'subrepo("glob:*")'
202 sub
202 sub
203 sub2
203 sub2
204 $ hg ci -m subrepo
204 $ hg ci -m subrepo
205
205
206 Test that .hgsubstate is updated as appropriate during a conversion. The
206 Test that .hgsubstate is updated as appropriate during a conversion. The
207 saverev property is enough to alter the hashes of the subrepo.
207 saverev property is enough to alter the hashes of the subrepo.
208
208
209 $ hg init ../converted
209 $ hg init ../converted
210 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
210 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
211 > sub ../converted/sub
211 > sub ../converted/sub
212 initializing destination ../converted/sub repository
212 initializing destination ../converted/sub repository
213 scanning source...
213 scanning source...
214 sorting...
214 sorting...
215 converting...
215 converting...
216 0 sub
216 0 sub
217 $ hg clone -U sub2 ../converted/sub2
217 $ hg clone -U sub2 ../converted/sub2
218 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
218 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
219 > . ../converted
219 > . ../converted
220 scanning source...
220 scanning source...
221 sorting...
221 sorting...
222 converting...
222 converting...
223 4 addfiles
223 4 addfiles
224 3 manychanges
224 3 manychanges
225 2 diverging
225 2 diverging
226 1 merge
226 1 merge
227 0 subrepo
227 0 subrepo
228 no ".hgsubstate" updates will be made for "sub2"
228 no ".hgsubstate" updates will be made for "sub2"
229 $ hg up -q -R ../converted -r tip
229 $ hg up -q -R ../converted -r tip
230 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
230 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
231 a
231 a
232 b
232 b
233 $ oldnode=`hg log -r tip -T "{node}\n"`
233 $ oldnode=`hg log -r tip -T "{node}\n"`
234 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
234 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
235 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
235 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
236
236
237 Test with a revision
237 Test with a revision
238
238
239 $ hg log -G --template '{rev} {desc}\n'
239 $ hg log -G --template '{rev} {desc}\n'
240 @ 4 subrepo
240 @ 4 subrepo
241 |
241 |
242 o 3 merge
242 o 3 merge
243 |\
243 |\
244 | o 2 diverging
244 | o 2 diverging
245 | |
245 | |
246 o | 1 manychanges
246 o | 1 manychanges
247 |/
247 |/
248 o 0 addfiles
248 o 0 addfiles
249
249
250 $ echo unknown > unknown
250 $ echo unknown > unknown
251 $ fileset -r1 'modified()'
251 $ fileset -r1 'modified()'
252 b2
252 b2
253 $ fileset -r1 'added() and c1'
253 $ fileset -r1 'added() and c1'
254 c1
254 c1
255 $ fileset -r1 'removed()'
255 $ fileset -r1 'removed()'
256 a2
256 a2
257 $ fileset -r1 'deleted()'
257 $ fileset -r1 'deleted()'
258 $ fileset -r1 'unknown()'
258 $ fileset -r1 'unknown()'
259 $ fileset -r1 'ignored()'
259 $ fileset -r1 'ignored()'
260 $ fileset -r1 'hgignore()'
260 $ fileset -r1 'hgignore()'
261 b2
261 b2
262 bin
262 bin
263 $ fileset -r1 'binary()'
263 $ fileset -r1 'binary()'
264 bin
264 bin
265 $ fileset -r1 'size(1k)'
265 $ fileset -r1 'size(1k)'
266 1k
266 1k
267 $ fileset -r3 'resolved()'
267 $ fileset -r3 'resolved()'
268 $ fileset -r3 'unresolved()'
268 $ fileset -r3 'unresolved()'
269
269
270 #if execbit
270 #if execbit
271 $ fileset -r1 'exec()'
271 $ fileset -r1 'exec()'
272 b2
272 b2
273 #endif
273 #endif
274
274
275 #if symlink
275 #if symlink
276 $ fileset -r1 'symlink()'
276 $ fileset -r1 'symlink()'
277 b2link
277 b2link
278 #endif
278 #endif
279
279
280 #if no-windows
280 #if no-windows
281 $ fileset -r1 'not portable()'
281 $ fileset -r1 'not portable()'
282 con.xml
282 con.xml
283 $ hg forget 'con.xml'
283 $ hg forget 'con.xml'
284 #endif
284 #endif
285
285
286 $ fileset -r4 'subrepo("re:su.*")'
286 $ fileset -r4 'subrepo("re:su.*")'
287 sub
287 sub
288 sub2
288 sub2
289 $ fileset -r4 'subrepo("sub")'
289 $ fileset -r4 'subrepo("sub")'
290 sub
290 sub
291 $ fileset -r4 'b2 or c1'
291 $ fileset -r4 'b2 or c1'
292 b2
292 b2
293 c1
293 c1
294
294
295 >>> open('dos', 'wb').write("dos\r\n")
295 >>> open('dos', 'wb').write("dos\r\n")
296 >>> open('mixed', 'wb').write("dos\r\nunix\n")
296 >>> open('mixed', 'wb').write("dos\r\nunix\n")
297 >>> open('mac', 'wb').write("mac\r")
297 >>> open('mac', 'wb').write("mac\r")
298 $ hg add dos mixed mac
298 $ hg add dos mixed mac
299
299
300 (remove a1, to examine safety of 'eol' on removed files)
301 $ rm a1
302
300 $ fileset 'eol(dos)'
303 $ fileset 'eol(dos)'
301 dos
304 dos
302 mixed
305 mixed
303 $ fileset 'eol(unix)'
306 $ fileset 'eol(unix)'
307 mixed
304 .hgsub
308 .hgsub
305 .hgsubstate
309 .hgsubstate
306 a1
307 b1
310 b1
308 b2
311 b2
309 c1
312 c1
310 mixed
311 $ fileset 'eol(mac)'
313 $ fileset 'eol(mac)'
312 mac
314 mac
315
316 Test safety of 'encoding' on removed files
317
318 $ fileset 'encoding("ascii")'
319 dos
320 mac
321 mixed
322 .hgsub
323 .hgsubstate
324 1k
325 2k
326 b1
327 b2
328 b2link
329 bin
330 c1
General Comments 0
You need to be logged in to leave comments. Login now