##// END OF EJS Templates
merge with stable
Matt Mackall -
r14844:7aaae546 merge default
parent child Browse files
Show More
@@ -1,159 +1,160 b''
1 # win32mbcs.py -- MBCS filename support for Mercurial
1 # win32mbcs.py -- MBCS filename support for Mercurial
2 #
2 #
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
4 #
4 #
5 # Version: 0.3
5 # Version: 0.3
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
7 #
7 #
8 # This software may be used and distributed according to the terms of the
8 # This software may be used and distributed according to the terms of the
9 # GNU General Public License version 2 or any later version.
9 # GNU General Public License version 2 or any later version.
10 #
10 #
11
11
12 '''allow the use of MBCS paths with problematic encodings
12 '''allow the use of MBCS paths with problematic encodings
13
13
14 Some MBCS encodings are not good for some path operations (i.e.
14 Some MBCS encodings are not good for some path operations (i.e.
15 splitting path, case conversion, etc.) with its encoded bytes. We call
15 splitting path, case conversion, etc.) with its encoded bytes. We call
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
17 This extension can be used to fix the issue with those encodings by
17 This extension can be used to fix the issue with those encodings by
18 wrapping some functions to convert to Unicode string before path
18 wrapping some functions to convert to Unicode string before path
19 operation.
19 operation.
20
20
21 This extension is useful for:
21 This extension is useful for:
22
22
23 - Japanese Windows users using shift_jis encoding.
23 - Japanese Windows users using shift_jis encoding.
24 - Chinese Windows users using big5 encoding.
24 - Chinese Windows users using big5 encoding.
25 - All users who use a repository with one of problematic encodings on
25 - All users who use a repository with one of problematic encodings on
26 case-insensitive file system.
26 case-insensitive file system.
27
27
28 This extension is not needed for:
28 This extension is not needed for:
29
29
30 - Any user who use only ASCII chars in path.
30 - Any user who use only ASCII chars in path.
31 - Any user who do not use any of problematic encodings.
31 - Any user who do not use any of problematic encodings.
32
32
33 Note that there are some limitations on using this extension:
33 Note that there are some limitations on using this extension:
34
34
35 - You should use single encoding in one repository.
35 - You should use single encoding in one repository.
36 - If the repository path ends with 0x5c, .hg/hgrc cannot be read.
36 - If the repository path ends with 0x5c, .hg/hgrc cannot be read.
37 - win32mbcs is not compatible with fixutf8 extension.
37 - win32mbcs is not compatible with fixutf8 extension.
38
38
39 By default, win32mbcs uses encoding.encoding decided by Mercurial.
39 By default, win32mbcs uses encoding.encoding decided by Mercurial.
40 You can specify the encoding by config option::
40 You can specify the encoding by config option::
41
41
42 [win32mbcs]
42 [win32mbcs]
43 encoding = sjis
43 encoding = sjis
44
44
45 It is useful for the users who want to commit with UTF-8 log message.
45 It is useful for the users who want to commit with UTF-8 log message.
46 '''
46 '''
47
47
48 import os, sys
48 import os, sys
49 from mercurial.i18n import _
49 from mercurial.i18n import _
50 from mercurial import util, encoding
50 from mercurial import util, encoding
51
51
52 _encoding = None # see extsetup
52 _encoding = None # see extsetup
53
53
54 def decode(arg):
54 def decode(arg):
55 if isinstance(arg, str):
55 if isinstance(arg, str):
56 uarg = arg.decode(_encoding)
56 uarg = arg.decode(_encoding)
57 if arg == uarg.encode(_encoding):
57 if arg == uarg.encode(_encoding):
58 return uarg
58 return uarg
59 raise UnicodeError("Not local encoding")
59 raise UnicodeError("Not local encoding")
60 elif isinstance(arg, tuple):
60 elif isinstance(arg, tuple):
61 return tuple(map(decode, arg))
61 return tuple(map(decode, arg))
62 elif isinstance(arg, list):
62 elif isinstance(arg, list):
63 return map(decode, arg)
63 return map(decode, arg)
64 elif isinstance(arg, dict):
64 elif isinstance(arg, dict):
65 for k, v in arg.items():
65 for k, v in arg.items():
66 arg[k] = decode(v)
66 arg[k] = decode(v)
67 return arg
67 return arg
68
68
69 def encode(arg):
69 def encode(arg):
70 if isinstance(arg, unicode):
70 if isinstance(arg, unicode):
71 return arg.encode(_encoding)
71 return arg.encode(_encoding)
72 elif isinstance(arg, tuple):
72 elif isinstance(arg, tuple):
73 return tuple(map(encode, arg))
73 return tuple(map(encode, arg))
74 elif isinstance(arg, list):
74 elif isinstance(arg, list):
75 return map(encode, arg)
75 return map(encode, arg)
76 elif isinstance(arg, dict):
76 elif isinstance(arg, dict):
77 for k, v in arg.items():
77 for k, v in arg.items():
78 arg[k] = encode(v)
78 arg[k] = encode(v)
79 return arg
79 return arg
80
80
81 def appendsep(s):
81 def appendsep(s):
82 # ensure the path ends with os.sep, appending it if necessary.
82 # ensure the path ends with os.sep, appending it if necessary.
83 try:
83 try:
84 us = decode(s)
84 us = decode(s)
85 except UnicodeError:
85 except UnicodeError:
86 us = s
86 us = s
87 if us and us[-1] not in ':/\\':
87 if us and us[-1] not in ':/\\':
88 s += os.sep
88 s += os.sep
89 return s
89 return s
90
90
91 def wrapper(func, args, kwds):
91 def wrapper(func, args, kwds):
92 # check argument is unicode, then call original
92 # check argument is unicode, then call original
93 for arg in args:
93 for arg in args:
94 if isinstance(arg, unicode):
94 if isinstance(arg, unicode):
95 return func(*args, **kwds)
95 return func(*args, **kwds)
96
96
97 try:
97 try:
98 # convert arguments to unicode, call func, then convert back
98 # convert arguments to unicode, call func, then convert back
99 return encode(func(*decode(args), **decode(kwds)))
99 return encode(func(*decode(args), **decode(kwds)))
100 except UnicodeError:
100 except UnicodeError:
101 raise util.Abort(_("[win32mbcs] filename conversion failed with"
101 raise util.Abort(_("[win32mbcs] filename conversion failed with"
102 " %s encoding\n") % (_encoding))
102 " %s encoding\n") % (_encoding))
103
103
104 def wrapperforlistdir(func, args, kwds):
104 def wrapperforlistdir(func, args, kwds):
105 # Ensure 'path' argument ends with os.sep to avoids
105 # Ensure 'path' argument ends with os.sep to avoids
106 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
106 # misinterpreting last 0x5c of MBCS 2nd byte as path separator.
107 if args:
107 if args:
108 args = list(args)
108 args = list(args)
109 args[0] = appendsep(args[0])
109 args[0] = appendsep(args[0])
110 if 'path' in kwds:
110 if 'path' in kwds:
111 kwds['path'] = appendsep(kwds['path'])
111 kwds['path'] = appendsep(kwds['path'])
112 return func(*args, **kwds)
112 return func(*args, **kwds)
113
113
114 def wrapname(name, wrapper):
114 def wrapname(name, wrapper):
115 module, name = name.rsplit('.', 1)
115 module, name = name.rsplit('.', 1)
116 module = sys.modules[module]
116 module = sys.modules[module]
117 func = getattr(module, name)
117 func = getattr(module, name)
118 def f(*args, **kwds):
118 def f(*args, **kwds):
119 return wrapper(func, args, kwds)
119 return wrapper(func, args, kwds)
120 try:
120 try:
121 f.__name__ = func.__name__ # fail with python23
121 f.__name__ = func.__name__ # fail with python23
122 except Exception:
122 except Exception:
123 pass
123 pass
124 setattr(module, name, f)
124 setattr(module, name, f)
125
125
126 # List of functions to be wrapped.
126 # List of functions to be wrapped.
127 # NOTE: os.path.dirname() and os.path.basename() are safe because
127 # NOTE: os.path.dirname() and os.path.basename() are safe because
128 # they use result of os.path.split()
128 # they use result of os.path.split()
129 funcs = '''os.path.join os.path.split os.path.splitext
129 funcs = '''os.path.join os.path.split os.path.splitext
130 os.path.splitunc os.path.normpath os.path.normcase os.makedirs
130 os.path.splitunc os.path.normpath os.path.normcase os.makedirs
131 mercurial.util.endswithsep mercurial.util.splitpath mercurial.util.checkcase
131 mercurial.util.endswithsep mercurial.util.splitpath mercurial.util.checkcase
132 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath'''
132 mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath
133 mercurial.util.checkwinfilename mercurial.util.checkosfilename'''
133
134
134 # codec and alias names of sjis and big5 to be faked.
135 # codec and alias names of sjis and big5 to be faked.
135 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
136 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
136 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
137 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
137 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
138 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
138 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
139 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
139
140
140 def extsetup(ui):
141 def extsetup(ui):
141 # TODO: decide use of config section for this extension
142 # TODO: decide use of config section for this extension
142 if not os.path.supports_unicode_filenames:
143 if not os.path.supports_unicode_filenames:
143 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
144 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
144 return
145 return
145 # determine encoding for filename
146 # determine encoding for filename
146 global _encoding
147 global _encoding
147 _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding)
148 _encoding = ui.config('win32mbcs', 'encoding', encoding.encoding)
148 # fake is only for relevant environment.
149 # fake is only for relevant environment.
149 if _encoding.lower() in problematic_encodings.split():
150 if _encoding.lower() in problematic_encodings.split():
150 for f in funcs.split():
151 for f in funcs.split():
151 wrapname(f, wrapper)
152 wrapname(f, wrapper)
152 wrapname("mercurial.osutil.listdir", wrapperforlistdir)
153 wrapname("mercurial.osutil.listdir", wrapperforlistdir)
153 # Check sys.args manually instead of using ui.debug() because
154 # Check sys.args manually instead of using ui.debug() because
154 # command line options is not yet applied when
155 # command line options is not yet applied when
155 # extensions.loadall() is called.
156 # extensions.loadall() is called.
156 if '--debug' in sys.argv:
157 if '--debug' in sys.argv:
157 ui.write("[win32mbcs] activated with encoding: %s\n"
158 ui.write("[win32mbcs] activated with encoding: %s\n"
158 % _encoding)
159 % _encoding)
159
160
@@ -1,1021 +1,1029 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 import re
8 import re
9 import parser, util, error, discovery, hbisect
9 import parser, util, error, discovery, hbisect
10 import bookmarks as bookmarksmod
10 import bookmarks as bookmarksmod
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13
13
14 elements = {
14 elements = {
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 "~": (18, None, ("ancestor", 18)),
16 "~": (18, None, ("ancestor", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
18 "-": (5, ("negate", 19), ("minus", 5)),
18 "-": (5, ("negate", 19), ("minus", 5)),
19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
20 ("dagrangepost", 17)),
20 ("dagrangepost", 17)),
21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
22 ("dagrangepost", 17)),
22 ("dagrangepost", 17)),
23 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
23 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
24 "not": (10, ("not", 10)),
24 "not": (10, ("not", 10)),
25 "!": (10, ("not", 10)),
25 "!": (10, ("not", 10)),
26 "and": (5, None, ("and", 5)),
26 "and": (5, None, ("and", 5)),
27 "&": (5, None, ("and", 5)),
27 "&": (5, None, ("and", 5)),
28 "or": (4, None, ("or", 4)),
28 "or": (4, None, ("or", 4)),
29 "|": (4, None, ("or", 4)),
29 "|": (4, None, ("or", 4)),
30 "+": (4, None, ("or", 4)),
30 "+": (4, None, ("or", 4)),
31 ",": (2, None, ("list", 2)),
31 ",": (2, None, ("list", 2)),
32 ")": (0, None, None),
32 ")": (0, None, None),
33 "symbol": (0, ("symbol",), None),
33 "symbol": (0, ("symbol",), None),
34 "string": (0, ("string",), None),
34 "string": (0, ("string",), None),
35 "end": (0, None, None),
35 "end": (0, None, None),
36 }
36 }
37
37
38 keywords = set(['and', 'or', 'not'])
38 keywords = set(['and', 'or', 'not'])
39
39
40 def tokenize(program):
40 def tokenize(program):
41 pos, l = 0, len(program)
41 pos, l = 0, len(program)
42 while pos < l:
42 while pos < l:
43 c = program[pos]
43 c = program[pos]
44 if c.isspace(): # skip inter-token whitespace
44 if c.isspace(): # skip inter-token whitespace
45 pass
45 pass
46 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
46 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
47 yield ('::', None, pos)
47 yield ('::', None, pos)
48 pos += 1 # skip ahead
48 pos += 1 # skip ahead
49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
50 yield ('..', None, pos)
50 yield ('..', None, pos)
51 pos += 1 # skip ahead
51 pos += 1 # skip ahead
52 elif c in "():,-|&+!~^": # handle simple operators
52 elif c in "():,-|&+!~^": # handle simple operators
53 yield (c, None, pos)
53 yield (c, None, pos)
54 elif (c in '"\'' or c == 'r' and
54 elif (c in '"\'' or c == 'r' and
55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
56 if c == 'r':
56 if c == 'r':
57 pos += 1
57 pos += 1
58 c = program[pos]
58 c = program[pos]
59 decode = lambda x: x
59 decode = lambda x: x
60 else:
60 else:
61 decode = lambda x: x.decode('string-escape')
61 decode = lambda x: x.decode('string-escape')
62 pos += 1
62 pos += 1
63 s = pos
63 s = pos
64 while pos < l: # find closing quote
64 while pos < l: # find closing quote
65 d = program[pos]
65 d = program[pos]
66 if d == '\\': # skip over escaped characters
66 if d == '\\': # skip over escaped characters
67 pos += 2
67 pos += 2
68 continue
68 continue
69 if d == c:
69 if d == c:
70 yield ('string', decode(program[s:pos]), s)
70 yield ('string', decode(program[s:pos]), s)
71 break
71 break
72 pos += 1
72 pos += 1
73 else:
73 else:
74 raise error.ParseError(_("unterminated string"), s)
74 raise error.ParseError(_("unterminated string"), s)
75 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
75 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
76 s = pos
76 s = pos
77 pos += 1
77 pos += 1
78 while pos < l: # find end of symbol
78 while pos < l: # find end of symbol
79 d = program[pos]
79 d = program[pos]
80 if not (d.isalnum() or d in "._" or ord(d) > 127):
80 if not (d.isalnum() or d in "._" or ord(d) > 127):
81 break
81 break
82 if d == '.' and program[pos - 1] == '.': # special case for ..
82 if d == '.' and program[pos - 1] == '.': # special case for ..
83 pos -= 1
83 pos -= 1
84 break
84 break
85 pos += 1
85 pos += 1
86 sym = program[s:pos]
86 sym = program[s:pos]
87 if sym in keywords: # operator keywords
87 if sym in keywords: # operator keywords
88 yield (sym, None, s)
88 yield (sym, None, s)
89 else:
89 else:
90 yield ('symbol', sym, s)
90 yield ('symbol', sym, s)
91 pos -= 1
91 pos -= 1
92 else:
92 else:
93 raise error.ParseError(_("syntax error"), pos)
93 raise error.ParseError(_("syntax error"), pos)
94 pos += 1
94 pos += 1
95 yield ('end', None, pos)
95 yield ('end', None, pos)
96
96
97 # helpers
97 # helpers
98
98
99 def getstring(x, err):
99 def getstring(x, err):
100 if x and (x[0] == 'string' or x[0] == 'symbol'):
100 if x and (x[0] == 'string' or x[0] == 'symbol'):
101 return x[1]
101 return x[1]
102 raise error.ParseError(err)
102 raise error.ParseError(err)
103
103
104 def getlist(x):
104 def getlist(x):
105 if not x:
105 if not x:
106 return []
106 return []
107 if x[0] == 'list':
107 if x[0] == 'list':
108 return getlist(x[1]) + [x[2]]
108 return getlist(x[1]) + [x[2]]
109 return [x]
109 return [x]
110
110
111 def getargs(x, min, max, err):
111 def getargs(x, min, max, err):
112 l = getlist(x)
112 l = getlist(x)
113 if len(l) < min or len(l) > max:
113 if len(l) < min or len(l) > max:
114 raise error.ParseError(err)
114 raise error.ParseError(err)
115 return l
115 return l
116
116
117 def getset(repo, subset, x):
117 def getset(repo, subset, x):
118 if not x:
118 if not x:
119 raise error.ParseError(_("missing argument"))
119 raise error.ParseError(_("missing argument"))
120 return methods[x[0]](repo, subset, *x[1:])
120 return methods[x[0]](repo, subset, *x[1:])
121
121
122 # operator methods
122 # operator methods
123
123
124 def stringset(repo, subset, x):
124 def stringset(repo, subset, x):
125 x = repo[x].rev()
125 x = repo[x].rev()
126 if x == -1 and len(subset) == len(repo):
126 if x == -1 and len(subset) == len(repo):
127 return [-1]
127 return [-1]
128 if len(subset) == len(repo) or x in subset:
128 if len(subset) == len(repo) or x in subset:
129 return [x]
129 return [x]
130 return []
130 return []
131
131
132 def symbolset(repo, subset, x):
132 def symbolset(repo, subset, x):
133 if x in symbols:
133 if x in symbols:
134 raise error.ParseError(_("can't use %s here") % x)
134 raise error.ParseError(_("can't use %s here") % x)
135 return stringset(repo, subset, x)
135 return stringset(repo, subset, x)
136
136
137 def rangeset(repo, subset, x, y):
137 def rangeset(repo, subset, x, y):
138 m = getset(repo, subset, x)
138 m = getset(repo, subset, x)
139 if not m:
139 if not m:
140 m = getset(repo, range(len(repo)), x)
140 m = getset(repo, range(len(repo)), x)
141
141
142 n = getset(repo, subset, y)
142 n = getset(repo, subset, y)
143 if not n:
143 if not n:
144 n = getset(repo, range(len(repo)), y)
144 n = getset(repo, range(len(repo)), y)
145
145
146 if not m or not n:
146 if not m or not n:
147 return []
147 return []
148 m, n = m[0], n[-1]
148 m, n = m[0], n[-1]
149
149
150 if m < n:
150 if m < n:
151 r = range(m, n + 1)
151 r = range(m, n + 1)
152 else:
152 else:
153 r = range(m, n - 1, -1)
153 r = range(m, n - 1, -1)
154 s = set(subset)
154 s = set(subset)
155 return [x for x in r if x in s]
155 return [x for x in r if x in s]
156
156
157 def andset(repo, subset, x, y):
157 def andset(repo, subset, x, y):
158 return getset(repo, getset(repo, subset, x), y)
158 return getset(repo, getset(repo, subset, x), y)
159
159
160 def orset(repo, subset, x, y):
160 def orset(repo, subset, x, y):
161 xl = getset(repo, subset, x)
161 xl = getset(repo, subset, x)
162 s = set(xl)
162 s = set(xl)
163 yl = getset(repo, [r for r in subset if r not in s], y)
163 yl = getset(repo, [r for r in subset if r not in s], y)
164 return xl + yl
164 return xl + yl
165
165
166 def notset(repo, subset, x):
166 def notset(repo, subset, x):
167 s = set(getset(repo, subset, x))
167 s = set(getset(repo, subset, x))
168 return [r for r in subset if r not in s]
168 return [r for r in subset if r not in s]
169
169
170 def listset(repo, subset, a, b):
170 def listset(repo, subset, a, b):
171 raise error.ParseError(_("can't use a list in this context"))
171 raise error.ParseError(_("can't use a list in this context"))
172
172
173 def func(repo, subset, a, b):
173 def func(repo, subset, a, b):
174 if a[0] == 'symbol' and a[1] in symbols:
174 if a[0] == 'symbol' and a[1] in symbols:
175 return symbols[a[1]](repo, subset, b)
175 return symbols[a[1]](repo, subset, b)
176 raise error.ParseError(_("not a function: %s") % a[1])
176 raise error.ParseError(_("not a function: %s") % a[1])
177
177
178 # functions
178 # functions
179
179
180 def adds(repo, subset, x):
180 def adds(repo, subset, x):
181 """``adds(pattern)``
181 """``adds(pattern)``
182 Changesets that add a file matching pattern.
182 Changesets that add a file matching pattern.
183 """
183 """
184 # i18n: "adds" is a keyword
184 # i18n: "adds" is a keyword
185 pat = getstring(x, _("adds requires a pattern"))
185 pat = getstring(x, _("adds requires a pattern"))
186 return checkstatus(repo, subset, pat, 1)
186 return checkstatus(repo, subset, pat, 1)
187
187
188 def ancestor(repo, subset, x):
188 def ancestor(repo, subset, x):
189 """``ancestor(single, single)``
189 """``ancestor(single, single)``
190 Greatest common ancestor of the two changesets.
190 Greatest common ancestor of the two changesets.
191 """
191 """
192 # i18n: "ancestor" is a keyword
192 # i18n: "ancestor" is a keyword
193 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
193 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
194 r = range(len(repo))
194 r = range(len(repo))
195 a = getset(repo, r, l[0])
195 a = getset(repo, r, l[0])
196 b = getset(repo, r, l[1])
196 b = getset(repo, r, l[1])
197 if len(a) != 1 or len(b) != 1:
197 if len(a) != 1 or len(b) != 1:
198 # i18n: "ancestor" is a keyword
198 # i18n: "ancestor" is a keyword
199 raise error.ParseError(_("ancestor arguments must be single revisions"))
199 raise error.ParseError(_("ancestor arguments must be single revisions"))
200 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
200 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
201
201
202 return [r for r in an if r in subset]
202 return [r for r in an if r in subset]
203
203
204 def ancestors(repo, subset, x):
204 def ancestors(repo, subset, x):
205 """``ancestors(set)``
205 """``ancestors(set)``
206 Changesets that are ancestors of a changeset in set.
206 Changesets that are ancestors of a changeset in set.
207 """
207 """
208 args = getset(repo, range(len(repo)), x)
208 args = getset(repo, range(len(repo)), x)
209 if not args:
209 if not args:
210 return []
210 return []
211 s = set(repo.changelog.ancestors(*args)) | set(args)
211 s = set(repo.changelog.ancestors(*args)) | set(args)
212 return [r for r in subset if r in s]
212 return [r for r in subset if r in s]
213
213
214 def ancestorspec(repo, subset, x, n):
214 def ancestorspec(repo, subset, x, n):
215 """``set~n``
215 """``set~n``
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
217 """
217 """
218 try:
218 try:
219 n = int(n[1])
219 n = int(n[1])
220 except ValueError:
220 except ValueError:
221 raise error.ParseError(_("~ expects a number"))
221 raise error.ParseError(_("~ expects a number"))
222 ps = set()
222 ps = set()
223 cl = repo.changelog
223 cl = repo.changelog
224 for r in getset(repo, subset, x):
224 for r in getset(repo, subset, x):
225 for i in range(n):
225 for i in range(n):
226 r = cl.parentrevs(r)[0]
226 r = cl.parentrevs(r)[0]
227 ps.add(r)
227 ps.add(r)
228 return [r for r in subset if r in ps]
228 return [r for r in subset if r in ps]
229
229
230 def author(repo, subset, x):
230 def author(repo, subset, x):
231 """``author(string)``
231 """``author(string)``
232 Alias for ``user(string)``.
232 Alias for ``user(string)``.
233 """
233 """
234 # i18n: "author" is a keyword
234 # i18n: "author" is a keyword
235 n = getstring(x, _("author requires a string")).lower()
235 n = getstring(x, _("author requires a string")).lower()
236 return [r for r in subset if n in repo[r].user().lower()]
236 return [r for r in subset if n in repo[r].user().lower()]
237
237
238 def bisected(repo, subset, x):
238 def bisected(repo, subset, x):
239 """``bisected(string)``
239 """``bisected(string)``
240 Changesets marked in the specified bisect state (good, bad, skip).
240 Changesets marked in the specified bisect state (good, bad, skip).
241 """
241 """
242 state = getstring(x, _("bisect requires a string")).lower()
242 state = getstring(x, _("bisect requires a string")).lower()
243 if state not in ('good', 'bad', 'skip', 'unknown'):
243 if state not in ('good', 'bad', 'skip', 'unknown'):
244 raise error.ParseError(_('invalid bisect state'))
244 raise error.ParseError(_('invalid bisect state'))
245 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
245 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
246 return [r for r in subset if r in marked]
246 return [r for r in subset if r in marked]
247
247
248 def bookmark(repo, subset, x):
248 def bookmark(repo, subset, x):
249 """``bookmark([name])``
249 """``bookmark([name])``
250 The named bookmark or all bookmarks.
250 The named bookmark or all bookmarks.
251 """
251 """
252 # i18n: "bookmark" is a keyword
252 # i18n: "bookmark" is a keyword
253 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
253 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
254 if args:
254 if args:
255 bm = getstring(args[0],
255 bm = getstring(args[0],
256 # i18n: "bookmark" is a keyword
256 # i18n: "bookmark" is a keyword
257 _('the argument to bookmark must be a string'))
257 _('the argument to bookmark must be a string'))
258 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
258 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
259 if not bmrev:
259 if not bmrev:
260 raise util.Abort(_("bookmark '%s' does not exist") % bm)
260 raise util.Abort(_("bookmark '%s' does not exist") % bm)
261 bmrev = repo[bmrev].rev()
261 bmrev = repo[bmrev].rev()
262 return [r for r in subset if r == bmrev]
262 return [r for r in subset if r == bmrev]
263 bms = set([repo[r].rev()
263 bms = set([repo[r].rev()
264 for r in bookmarksmod.listbookmarks(repo).values()])
264 for r in bookmarksmod.listbookmarks(repo).values()])
265 return [r for r in subset if r in bms]
265 return [r for r in subset if r in bms]
266
266
267 def branch(repo, subset, x):
267 def branch(repo, subset, x):
268 """``branch(string or set)``
268 """``branch(string or set)``
269 All changesets belonging to the given branch or the branches of the given
269 All changesets belonging to the given branch or the branches of the given
270 changesets.
270 changesets.
271 """
271 """
272 try:
272 try:
273 b = getstring(x, '')
273 b = getstring(x, '')
274 if b in repo.branchmap():
274 if b in repo.branchmap():
275 return [r for r in subset if repo[r].branch() == b]
275 return [r for r in subset if repo[r].branch() == b]
276 except error.ParseError:
276 except error.ParseError:
277 # not a string, but another revspec, e.g. tip()
277 # not a string, but another revspec, e.g. tip()
278 pass
278 pass
279
279
280 s = getset(repo, range(len(repo)), x)
280 s = getset(repo, range(len(repo)), x)
281 b = set()
281 b = set()
282 for r in s:
282 for r in s:
283 b.add(repo[r].branch())
283 b.add(repo[r].branch())
284 s = set(s)
284 s = set(s)
285 return [r for r in subset if r in s or repo[r].branch() in b]
285 return [r for r in subset if r in s or repo[r].branch() in b]
286
286
287 def checkstatus(repo, subset, pat, field):
287 def checkstatus(repo, subset, pat, field):
288 m = matchmod.match(repo.root, repo.getcwd(), [pat])
288 m = matchmod.match(repo.root, repo.getcwd(), [pat])
289 s = []
289 s = []
290 fast = (m.files() == [pat])
290 fast = (m.files() == [pat])
291 for r in subset:
291 for r in subset:
292 c = repo[r]
292 c = repo[r]
293 if fast:
293 if fast:
294 if pat not in c.files():
294 if pat not in c.files():
295 continue
295 continue
296 else:
296 else:
297 for f in c.files():
297 for f in c.files():
298 if m(f):
298 if m(f):
299 break
299 break
300 else:
300 else:
301 continue
301 continue
302 files = repo.status(c.p1().node(), c.node())[field]
302 files = repo.status(c.p1().node(), c.node())[field]
303 if fast:
303 if fast:
304 if pat in files:
304 if pat in files:
305 s.append(r)
305 s.append(r)
306 else:
306 else:
307 for f in files:
307 for f in files:
308 if m(f):
308 if m(f):
309 s.append(r)
309 s.append(r)
310 break
310 break
311 return s
311 return s
312
312
313 def children(repo, subset, x):
313 def children(repo, subset, x):
314 """``children(set)``
314 """``children(set)``
315 Child changesets of changesets in set.
315 Child changesets of changesets in set.
316 """
316 """
317 cs = set()
317 cs = set()
318 cl = repo.changelog
318 cl = repo.changelog
319 s = set(getset(repo, range(len(repo)), x))
319 s = set(getset(repo, range(len(repo)), x))
320 for r in xrange(0, len(repo)):
320 for r in xrange(0, len(repo)):
321 for p in cl.parentrevs(r):
321 for p in cl.parentrevs(r):
322 if p in s:
322 if p in s:
323 cs.add(r)
323 cs.add(r)
324 return [r for r in subset if r in cs]
324 return [r for r in subset if r in cs]
325
325
326 def closed(repo, subset, x):
326 def closed(repo, subset, x):
327 """``closed()``
327 """``closed()``
328 Changeset is closed.
328 Changeset is closed.
329 """
329 """
330 # i18n: "closed" is a keyword
330 # i18n: "closed" is a keyword
331 getargs(x, 0, 0, _("closed takes no arguments"))
331 getargs(x, 0, 0, _("closed takes no arguments"))
332 return [r for r in subset if repo[r].extra().get('close')]
332 return [r for r in subset if repo[r].extra().get('close')]
333
333
334 def contains(repo, subset, x):
334 def contains(repo, subset, x):
335 """``contains(pattern)``
335 """``contains(pattern)``
336 Revision contains a file matching pattern. See :hg:`help patterns`
336 Revision contains a file matching pattern. See :hg:`help patterns`
337 for information about file patterns.
337 for information about file patterns.
338 """
338 """
339 # i18n: "contains" is a keyword
339 # i18n: "contains" is a keyword
340 pat = getstring(x, _("contains requires a pattern"))
340 pat = getstring(x, _("contains requires a pattern"))
341 m = matchmod.match(repo.root, repo.getcwd(), [pat])
341 m = matchmod.match(repo.root, repo.getcwd(), [pat])
342 s = []
342 s = []
343 if m.files() == [pat]:
343 if m.files() == [pat]:
344 for r in subset:
344 for r in subset:
345 if pat in repo[r]:
345 if pat in repo[r]:
346 s.append(r)
346 s.append(r)
347 else:
347 else:
348 for r in subset:
348 for r in subset:
349 for f in repo[r].manifest():
349 for f in repo[r].manifest():
350 if m(f):
350 if m(f):
351 s.append(r)
351 s.append(r)
352 break
352 break
353 return s
353 return s
354
354
355 def date(repo, subset, x):
355 def date(repo, subset, x):
356 """``date(interval)``
356 """``date(interval)``
357 Changesets within the interval, see :hg:`help dates`.
357 Changesets within the interval, see :hg:`help dates`.
358 """
358 """
359 # i18n: "date" is a keyword
359 # i18n: "date" is a keyword
360 ds = getstring(x, _("date requires a string"))
360 ds = getstring(x, _("date requires a string"))
361 dm = util.matchdate(ds)
361 dm = util.matchdate(ds)
362 return [r for r in subset if dm(repo[r].date()[0])]
362 return [r for r in subset if dm(repo[r].date()[0])]
363
363
364 def desc(repo, subset, x):
364 def desc(repo, subset, x):
365 """``desc(string)``
365 """``desc(string)``
366 Search commit message for string. The match is case-insensitive.
366 Search commit message for string. The match is case-insensitive.
367 """
367 """
368 # i18n: "desc" is a keyword
368 # i18n: "desc" is a keyword
369 ds = getstring(x, _("desc requires a string")).lower()
369 ds = getstring(x, _("desc requires a string")).lower()
370 l = []
370 l = []
371 for r in subset:
371 for r in subset:
372 c = repo[r]
372 c = repo[r]
373 if ds in c.description().lower():
373 if ds in c.description().lower():
374 l.append(r)
374 l.append(r)
375 return l
375 return l
376
376
377 def descendants(repo, subset, x):
377 def descendants(repo, subset, x):
378 """``descendants(set)``
378 """``descendants(set)``
379 Changesets which are descendants of changesets in set.
379 Changesets which are descendants of changesets in set.
380 """
380 """
381 args = getset(repo, range(len(repo)), x)
381 args = getset(repo, range(len(repo)), x)
382 if not args:
382 if not args:
383 return []
383 return []
384 s = set(repo.changelog.descendants(*args)) | set(args)
384 s = set(repo.changelog.descendants(*args)) | set(args)
385 return [r for r in subset if r in s]
385 return [r for r in subset if r in s]
386
386
387 def filelog(repo, subset, x):
387 def filelog(repo, subset, x):
388 """``filelog(pattern)``
388 """``filelog(pattern)``
389 Changesets connected to the specified filelog.
389 Changesets connected to the specified filelog.
390 """
390 """
391
391
392 pat = getstring(x, _("filelog requires a pattern"))
392 pat = getstring(x, _("filelog requires a pattern"))
393 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath')
393 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath')
394 s = set()
394 s = set()
395
395
396 if not m.anypats():
396 if not m.anypats():
397 for f in m.files():
397 for f in m.files():
398 fl = repo.file(f)
398 fl = repo.file(f)
399 for fr in fl:
399 for fr in fl:
400 s.add(fl.linkrev(fr))
400 s.add(fl.linkrev(fr))
401 else:
401 else:
402 for f in repo[None]:
402 for f in repo[None]:
403 if m(f):
403 if m(f):
404 fl = repo.file(f)
404 fl = repo.file(f)
405 for fr in fl:
405 for fr in fl:
406 s.add(fl.linkrev(fr))
406 s.add(fl.linkrev(fr))
407
407
408 return [r for r in subset if r in s]
408 return [r for r in subset if r in s]
409
409
410 def follow(repo, subset, x):
410 def follow(repo, subset, x):
411 """``follow([file])``
411 """``follow([file])``
412 An alias for ``::.`` (ancestors of the working copy's first parent).
412 An alias for ``::.`` (ancestors of the working copy's first parent).
413 If a filename is specified, the history of the given file is followed,
413 If a filename is specified, the history of the given file is followed,
414 including copies.
414 including copies.
415 """
415 """
416 # i18n: "follow" is a keyword
416 # i18n: "follow" is a keyword
417 l = getargs(x, 0, 1, _("follow takes no arguments or a filename"))
417 l = getargs(x, 0, 1, _("follow takes no arguments or a filename"))
418 p = repo['.'].rev()
418 p = repo['.'].rev()
419 if l:
419 if l:
420 x = getstring(l[0], _("follow expected a filename"))
420 x = getstring(l[0], _("follow expected a filename"))
421 s = set(ctx.rev() for ctx in repo['.'][x].ancestors())
421 s = set(ctx.rev() for ctx in repo['.'][x].ancestors())
422 else:
422 else:
423 s = set(repo.changelog.ancestors(p))
423 s = set(repo.changelog.ancestors(p))
424
424
425 s |= set([p])
425 s |= set([p])
426 return [r for r in subset if r in s]
426 return [r for r in subset if r in s]
427
427
428 def followfile(repo, subset, x):
428 def followfile(repo, subset, x):
429 """``follow()``
429 """``follow()``
430 An alias for ``::.`` (ancestors of the working copy's first parent).
430 An alias for ``::.`` (ancestors of the working copy's first parent).
431 """
431 """
432 # i18n: "follow" is a keyword
432 # i18n: "follow" is a keyword
433 getargs(x, 0, 0, _("follow takes no arguments"))
433 getargs(x, 0, 0, _("follow takes no arguments"))
434 p = repo['.'].rev()
434 p = repo['.'].rev()
435 s = set(repo.changelog.ancestors(p)) | set([p])
435 s = set(repo.changelog.ancestors(p)) | set([p])
436 return [r for r in subset if r in s]
436 return [r for r in subset if r in s]
437
437
438 def getall(repo, subset, x):
438 def getall(repo, subset, x):
439 """``all()``
439 """``all()``
440 All changesets, the same as ``0:tip``.
440 All changesets, the same as ``0:tip``.
441 """
441 """
442 # i18n: "all" is a keyword
442 # i18n: "all" is a keyword
443 getargs(x, 0, 0, _("all takes no arguments"))
443 getargs(x, 0, 0, _("all takes no arguments"))
444 return subset
444 return subset
445
445
446 def grep(repo, subset, x):
446 def grep(repo, subset, x):
447 """``grep(regex)``
447 """``grep(regex)``
448 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
448 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
449 to ensure special escape characters are handled correctly. Unlike
449 to ensure special escape characters are handled correctly. Unlike
450 ``keyword(string)``, the match is case-sensitive.
450 ``keyword(string)``, the match is case-sensitive.
451 """
451 """
452 try:
452 try:
453 # i18n: "grep" is a keyword
453 # i18n: "grep" is a keyword
454 gr = re.compile(getstring(x, _("grep requires a string")))
454 gr = re.compile(getstring(x, _("grep requires a string")))
455 except re.error, e:
455 except re.error, e:
456 raise error.ParseError(_('invalid match pattern: %s') % e)
456 raise error.ParseError(_('invalid match pattern: %s') % e)
457 l = []
457 l = []
458 for r in subset:
458 for r in subset:
459 c = repo[r]
459 c = repo[r]
460 for e in c.files() + [c.user(), c.description()]:
460 for e in c.files() + [c.user(), c.description()]:
461 if gr.search(e):
461 if gr.search(e):
462 l.append(r)
462 l.append(r)
463 break
463 break
464 return l
464 return l
465
465
466 def hasfile(repo, subset, x):
466 def hasfile(repo, subset, x):
467 """``file(pattern)``
467 """``file(pattern)``
468 Changesets affecting files matched by pattern.
468 Changesets affecting files matched by pattern.
469 """
469 """
470 # i18n: "file" is a keyword
470 # i18n: "file" is a keyword
471 pat = getstring(x, _("file requires a pattern"))
471 pat = getstring(x, _("file requires a pattern"))
472 m = matchmod.match(repo.root, repo.getcwd(), [pat])
472 m = matchmod.match(repo.root, repo.getcwd(), [pat])
473 s = []
473 s = []
474 for r in subset:
474 for r in subset:
475 for f in repo[r].files():
475 for f in repo[r].files():
476 if m(f):
476 if m(f):
477 s.append(r)
477 s.append(r)
478 break
478 break
479 return s
479 return s
480
480
481 def head(repo, subset, x):
481 def head(repo, subset, x):
482 """``head()``
482 """``head()``
483 Changeset is a named branch head.
483 Changeset is a named branch head.
484 """
484 """
485 # i18n: "head" is a keyword
485 # i18n: "head" is a keyword
486 getargs(x, 0, 0, _("head takes no arguments"))
486 getargs(x, 0, 0, _("head takes no arguments"))
487 hs = set()
487 hs = set()
488 for b, ls in repo.branchmap().iteritems():
488 for b, ls in repo.branchmap().iteritems():
489 hs.update(repo[h].rev() for h in ls)
489 hs.update(repo[h].rev() for h in ls)
490 return [r for r in subset if r in hs]
490 return [r for r in subset if r in hs]
491
491
492 def heads(repo, subset, x):
492 def heads(repo, subset, x):
493 """``heads(set)``
493 """``heads(set)``
494 Members of set with no children in set.
494 Members of set with no children in set.
495 """
495 """
496 s = getset(repo, subset, x)
496 s = getset(repo, subset, x)
497 ps = set(parents(repo, subset, x))
497 ps = set(parents(repo, subset, x))
498 return [r for r in s if r not in ps]
498 return [r for r in s if r not in ps]
499
499
500 def keyword(repo, subset, x):
500 def keyword(repo, subset, x):
501 """``keyword(string)``
501 """``keyword(string)``
502 Search commit message, user name, and names of changed files for
502 Search commit message, user name, and names of changed files for
503 string. The match is case-insensitive.
503 string. The match is case-insensitive.
504 """
504 """
505 # i18n: "keyword" is a keyword
505 # i18n: "keyword" is a keyword
506 kw = getstring(x, _("keyword requires a string")).lower()
506 kw = getstring(x, _("keyword requires a string")).lower()
507 l = []
507 l = []
508 for r in subset:
508 for r in subset:
509 c = repo[r]
509 c = repo[r]
510 t = " ".join(c.files() + [c.user(), c.description()])
510 t = " ".join(c.files() + [c.user(), c.description()])
511 if kw in t.lower():
511 if kw in t.lower():
512 l.append(r)
512 l.append(r)
513 return l
513 return l
514
514
515 def limit(repo, subset, x):
515 def limit(repo, subset, x):
516 """``limit(set, n)``
516 """``limit(set, n)``
517 First n members of set.
517 First n members of set.
518 """
518 """
519 # i18n: "limit" is a keyword
519 # i18n: "limit" is a keyword
520 l = getargs(x, 2, 2, _("limit requires two arguments"))
520 l = getargs(x, 2, 2, _("limit requires two arguments"))
521 try:
521 try:
522 # i18n: "limit" is a keyword
522 # i18n: "limit" is a keyword
523 lim = int(getstring(l[1], _("limit requires a number")))
523 lim = int(getstring(l[1], _("limit requires a number")))
524 except ValueError:
524 except ValueError:
525 # i18n: "limit" is a keyword
525 # i18n: "limit" is a keyword
526 raise error.ParseError(_("limit expects a number"))
526 raise error.ParseError(_("limit expects a number"))
527 ss = set(subset)
527 ss = set(subset)
528 os = getset(repo, range(len(repo)), l[0])[:lim]
528 os = getset(repo, range(len(repo)), l[0])[:lim]
529 return [r for r in os if r in ss]
529 return [r for r in os if r in ss]
530
530
531 def last(repo, subset, x):
531 def last(repo, subset, x):
532 """``last(set, n)``
532 """``last(set, n)``
533 Last n members of set.
533 Last n members of set.
534 """
534 """
535 # i18n: "last" is a keyword
535 # i18n: "last" is a keyword
536 l = getargs(x, 2, 2, _("last requires two arguments"))
536 l = getargs(x, 2, 2, _("last requires two arguments"))
537 try:
537 try:
538 # i18n: "last" is a keyword
538 # i18n: "last" is a keyword
539 lim = int(getstring(l[1], _("last requires a number")))
539 lim = int(getstring(l[1], _("last requires a number")))
540 except ValueError:
540 except ValueError:
541 # i18n: "last" is a keyword
541 # i18n: "last" is a keyword
542 raise error.ParseError(_("last expects a number"))
542 raise error.ParseError(_("last expects a number"))
543 ss = set(subset)
543 ss = set(subset)
544 os = getset(repo, range(len(repo)), l[0])[-lim:]
544 os = getset(repo, range(len(repo)), l[0])[-lim:]
545 return [r for r in os if r in ss]
545 return [r for r in os if r in ss]
546
546
547 def maxrev(repo, subset, x):
547 def maxrev(repo, subset, x):
548 """``max(set)``
548 """``max(set)``
549 Changeset with highest revision number in set.
549 Changeset with highest revision number in set.
550 """
550 """
551 os = getset(repo, range(len(repo)), x)
551 os = getset(repo, range(len(repo)), x)
552 if os:
552 if os:
553 m = max(os)
553 m = max(os)
554 if m in subset:
554 if m in subset:
555 return [m]
555 return [m]
556 return []
556 return []
557
557
558 def merge(repo, subset, x):
558 def merge(repo, subset, x):
559 """``merge()``
559 """``merge()``
560 Changeset is a merge changeset.
560 Changeset is a merge changeset.
561 """
561 """
562 # i18n: "merge" is a keyword
562 # i18n: "merge" is a keyword
563 getargs(x, 0, 0, _("merge takes no arguments"))
563 getargs(x, 0, 0, _("merge takes no arguments"))
564 cl = repo.changelog
564 cl = repo.changelog
565 return [r for r in subset if cl.parentrevs(r)[1] != -1]
565 return [r for r in subset if cl.parentrevs(r)[1] != -1]
566
566
567 def minrev(repo, subset, x):
567 def minrev(repo, subset, x):
568 """``min(set)``
568 """``min(set)``
569 Changeset with lowest revision number in set.
569 Changeset with lowest revision number in set.
570 """
570 """
571 os = getset(repo, range(len(repo)), x)
571 os = getset(repo, range(len(repo)), x)
572 if os:
572 if os:
573 m = min(os)
573 m = min(os)
574 if m in subset:
574 if m in subset:
575 return [m]
575 return [m]
576 return []
576 return []
577
577
578 def modifies(repo, subset, x):
578 def modifies(repo, subset, x):
579 """``modifies(pattern)``
579 """``modifies(pattern)``
580 Changesets modifying files matched by pattern.
580 Changesets modifying files matched by pattern.
581 """
581 """
582 # i18n: "modifies" is a keyword
582 # i18n: "modifies" is a keyword
583 pat = getstring(x, _("modifies requires a pattern"))
583 pat = getstring(x, _("modifies requires a pattern"))
584 return checkstatus(repo, subset, pat, 0)
584 return checkstatus(repo, subset, pat, 0)
585
585
586 def node(repo, subset, x):
586 def node(repo, subset, x):
587 """``id(string)``
587 """``id(string)``
588 Revision non-ambiguously specified by the given hex string prefix.
588 Revision non-ambiguously specified by the given hex string prefix.
589 """
589 """
590 # i18n: "id" is a keyword
590 # i18n: "id" is a keyword
591 l = getargs(x, 1, 1, _("id requires one argument"))
591 l = getargs(x, 1, 1, _("id requires one argument"))
592 # i18n: "id" is a keyword
592 # i18n: "id" is a keyword
593 n = getstring(l[0], _("id requires a string"))
593 n = getstring(l[0], _("id requires a string"))
594 if len(n) == 40:
594 if len(n) == 40:
595 rn = repo[n].rev()
595 rn = repo[n].rev()
596 else:
596 else:
597 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
597 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
598 return [r for r in subset if r == rn]
598 return [r for r in subset if r == rn]
599
599
600 def outgoing(repo, subset, x):
600 def outgoing(repo, subset, x):
601 """``outgoing([path])``
601 """``outgoing([path])``
602 Changesets not found in the specified destination repository, or the
602 Changesets not found in the specified destination repository, or the
603 default push location.
603 default push location.
604 """
604 """
605 import hg # avoid start-up nasties
605 import hg # avoid start-up nasties
606 # i18n: "outgoing" is a keyword
606 # i18n: "outgoing" is a keyword
607 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
607 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
608 # i18n: "outgoing" is a keyword
608 # i18n: "outgoing" is a keyword
609 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
609 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
610 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
610 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
611 dest, branches = hg.parseurl(dest)
611 dest, branches = hg.parseurl(dest)
612 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
612 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
613 if revs:
613 if revs:
614 revs = [repo.lookup(rev) for rev in revs]
614 revs = [repo.lookup(rev) for rev in revs]
615 other = hg.peer(repo, {}, dest)
615 other = hg.peer(repo, {}, dest)
616 repo.ui.pushbuffer()
616 repo.ui.pushbuffer()
617 common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
617 common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
618 repo.ui.popbuffer()
618 repo.ui.popbuffer()
619 cl = repo.changelog
619 cl = repo.changelog
620 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
620 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
621 return [r for r in subset if r in o]
621 return [r for r in subset if r in o]
622
622
623 def p1(repo, subset, x):
623 def p1(repo, subset, x):
624 """``p1([set])``
624 """``p1([set])``
625 First parent of changesets in set, or the working directory.
625 First parent of changesets in set, or the working directory.
626 """
626 """
627 if x is None:
627 if x is None:
628 p = repo[x].p1().rev()
628 p = repo[x].p1().rev()
629 return [r for r in subset if r == p]
629 return [r for r in subset if r == p]
630
630
631 ps = set()
631 ps = set()
632 cl = repo.changelog
632 cl = repo.changelog
633 for r in getset(repo, range(len(repo)), x):
633 for r in getset(repo, range(len(repo)), x):
634 ps.add(cl.parentrevs(r)[0])
634 ps.add(cl.parentrevs(r)[0])
635 return [r for r in subset if r in ps]
635 return [r for r in subset if r in ps]
636
636
637 def p2(repo, subset, x):
637 def p2(repo, subset, x):
638 """``p2([set])``
638 """``p2([set])``
639 Second parent of changesets in set, or the working directory.
639 Second parent of changesets in set, or the working directory.
640 """
640 """
641 if x is None:
641 if x is None:
642 ps = repo[x].parents()
642 ps = repo[x].parents()
643 try:
643 try:
644 p = ps[1].rev()
644 p = ps[1].rev()
645 return [r for r in subset if r == p]
645 return [r for r in subset if r == p]
646 except IndexError:
646 except IndexError:
647 return []
647 return []
648
648
649 ps = set()
649 ps = set()
650 cl = repo.changelog
650 cl = repo.changelog
651 for r in getset(repo, range(len(repo)), x):
651 for r in getset(repo, range(len(repo)), x):
652 ps.add(cl.parentrevs(r)[1])
652 ps.add(cl.parentrevs(r)[1])
653 return [r for r in subset if r in ps]
653 return [r for r in subset if r in ps]
654
654
655 def parents(repo, subset, x):
655 def parents(repo, subset, x):
656 """``parents([set])``
656 """``parents([set])``
657 The set of all parents for all changesets in set, or the working directory.
657 The set of all parents for all changesets in set, or the working directory.
658 """
658 """
659 if x is None:
659 if x is None:
660 ps = tuple(p.rev() for p in repo[x].parents())
660 ps = tuple(p.rev() for p in repo[x].parents())
661 return [r for r in subset if r in ps]
661 return [r for r in subset if r in ps]
662
662
663 ps = set()
663 ps = set()
664 cl = repo.changelog
664 cl = repo.changelog
665 for r in getset(repo, range(len(repo)), x):
665 for r in getset(repo, range(len(repo)), x):
666 ps.update(cl.parentrevs(r))
666 ps.update(cl.parentrevs(r))
667 return [r for r in subset if r in ps]
667 return [r for r in subset if r in ps]
668
668
669 def parentspec(repo, subset, x, n):
669 def parentspec(repo, subset, x, n):
670 """``set^0``
670 """``set^0``
671 The set.
671 The set.
672 ``set^1`` (or ``set^``), ``set^2``
672 ``set^1`` (or ``set^``), ``set^2``
673 First or second parent, respectively, of all changesets in set.
673 First or second parent, respectively, of all changesets in set.
674 """
674 """
675 try:
675 try:
676 n = int(n[1])
676 n = int(n[1])
677 if n not in (0, 1, 2):
677 if n not in (0, 1, 2):
678 raise ValueError
678 raise ValueError
679 except ValueError:
679 except ValueError:
680 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
680 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
681 ps = set()
681 ps = set()
682 cl = repo.changelog
682 cl = repo.changelog
683 for r in getset(repo, subset, x):
683 for r in getset(repo, subset, x):
684 if n == 0:
684 if n == 0:
685 ps.add(r)
685 ps.add(r)
686 elif n == 1:
686 elif n == 1:
687 ps.add(cl.parentrevs(r)[0])
687 ps.add(cl.parentrevs(r)[0])
688 elif n == 2:
688 elif n == 2:
689 parents = cl.parentrevs(r)
689 parents = cl.parentrevs(r)
690 if len(parents) > 1:
690 if len(parents) > 1:
691 ps.add(parents[1])
691 ps.add(parents[1])
692 return [r for r in subset if r in ps]
692 return [r for r in subset if r in ps]
693
693
694 def present(repo, subset, x):
694 def present(repo, subset, x):
695 """``present(set)``
695 """``present(set)``
696 An empty set, if any revision in set isn't found; otherwise,
696 An empty set, if any revision in set isn't found; otherwise,
697 all revisions in set.
697 all revisions in set.
698 """
698 """
699 try:
699 try:
700 return getset(repo, subset, x)
700 return getset(repo, subset, x)
701 except error.RepoLookupError:
701 except error.RepoLookupError:
702 return []
702 return []
703
703
704 def removes(repo, subset, x):
704 def removes(repo, subset, x):
705 """``removes(pattern)``
705 """``removes(pattern)``
706 Changesets which remove files matching pattern.
706 Changesets which remove files matching pattern.
707 """
707 """
708 # i18n: "removes" is a keyword
708 # i18n: "removes" is a keyword
709 pat = getstring(x, _("removes requires a pattern"))
709 pat = getstring(x, _("removes requires a pattern"))
710 return checkstatus(repo, subset, pat, 2)
710 return checkstatus(repo, subset, pat, 2)
711
711
712 def rev(repo, subset, x):
712 def rev(repo, subset, x):
713 """``rev(number)``
713 """``rev(number)``
714 Revision with the given numeric identifier.
714 Revision with the given numeric identifier.
715 """
715 """
716 # i18n: "rev" is a keyword
716 # i18n: "rev" is a keyword
717 l = getargs(x, 1, 1, _("rev requires one argument"))
717 l = getargs(x, 1, 1, _("rev requires one argument"))
718 try:
718 try:
719 # i18n: "rev" is a keyword
719 # i18n: "rev" is a keyword
720 l = int(getstring(l[0], _("rev requires a number")))
720 l = int(getstring(l[0], _("rev requires a number")))
721 except ValueError:
721 except ValueError:
722 # i18n: "rev" is a keyword
722 # i18n: "rev" is a keyword
723 raise error.ParseError(_("rev expects a number"))
723 raise error.ParseError(_("rev expects a number"))
724 return [r for r in subset if r == l]
724 return [r for r in subset if r == l]
725
725
726 def reverse(repo, subset, x):
726 def reverse(repo, subset, x):
727 """``reverse(set)``
727 """``reverse(set)``
728 Reverse order of set.
728 Reverse order of set.
729 """
729 """
730 l = getset(repo, subset, x)
730 l = getset(repo, subset, x)
731 l.reverse()
731 l.reverse()
732 return l
732 return l
733
733
734 def roots(repo, subset, x):
734 def roots(repo, subset, x):
735 """``roots(set)``
735 """``roots(set)``
736 Changesets with no parent changeset in set.
736 Changesets with no parent changeset in set.
737 """
737 """
738 s = getset(repo, subset, x)
738 s = getset(repo, subset, x)
739 cs = set(children(repo, subset, x))
739 cs = set(children(repo, subset, x))
740 return [r for r in s if r not in cs]
740 return [r for r in s if r not in cs]
741
741
742 def sort(repo, subset, x):
742 def sort(repo, subset, x):
743 """``sort(set[, [-]key...])``
743 """``sort(set[, [-]key...])``
744 Sort set by keys. The default sort order is ascending, specify a key
744 Sort set by keys. The default sort order is ascending, specify a key
745 as ``-key`` to sort in descending order.
745 as ``-key`` to sort in descending order.
746
746
747 The keys can be:
747 The keys can be:
748
748
749 - ``rev`` for the revision number,
749 - ``rev`` for the revision number,
750 - ``branch`` for the branch name,
750 - ``branch`` for the branch name,
751 - ``desc`` for the commit message (description),
751 - ``desc`` for the commit message (description),
752 - ``user`` for user name (``author`` can be used as an alias),
752 - ``user`` for user name (``author`` can be used as an alias),
753 - ``date`` for the commit date
753 - ``date`` for the commit date
754 """
754 """
755 # i18n: "sort" is a keyword
755 # i18n: "sort" is a keyword
756 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
756 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
757 keys = "rev"
757 keys = "rev"
758 if len(l) == 2:
758 if len(l) == 2:
759 keys = getstring(l[1], _("sort spec must be a string"))
759 keys = getstring(l[1], _("sort spec must be a string"))
760
760
761 s = l[0]
761 s = l[0]
762 keys = keys.split()
762 keys = keys.split()
763 l = []
763 l = []
764 def invert(s):
764 def invert(s):
765 return "".join(chr(255 - ord(c)) for c in s)
765 return "".join(chr(255 - ord(c)) for c in s)
766 for r in getset(repo, subset, s):
766 for r in getset(repo, subset, s):
767 c = repo[r]
767 c = repo[r]
768 e = []
768 e = []
769 for k in keys:
769 for k in keys:
770 if k == 'rev':
770 if k == 'rev':
771 e.append(r)
771 e.append(r)
772 elif k == '-rev':
772 elif k == '-rev':
773 e.append(-r)
773 e.append(-r)
774 elif k == 'branch':
774 elif k == 'branch':
775 e.append(c.branch())
775 e.append(c.branch())
776 elif k == '-branch':
776 elif k == '-branch':
777 e.append(invert(c.branch()))
777 e.append(invert(c.branch()))
778 elif k == 'desc':
778 elif k == 'desc':
779 e.append(c.description())
779 e.append(c.description())
780 elif k == '-desc':
780 elif k == '-desc':
781 e.append(invert(c.description()))
781 e.append(invert(c.description()))
782 elif k in 'user author':
782 elif k in 'user author':
783 e.append(c.user())
783 e.append(c.user())
784 elif k in '-user -author':
784 elif k in '-user -author':
785 e.append(invert(c.user()))
785 e.append(invert(c.user()))
786 elif k == 'date':
786 elif k == 'date':
787 e.append(c.date()[0])
787 e.append(c.date()[0])
788 elif k == '-date':
788 elif k == '-date':
789 e.append(-c.date()[0])
789 e.append(-c.date()[0])
790 else:
790 else:
791 raise error.ParseError(_("unknown sort key %r") % k)
791 raise error.ParseError(_("unknown sort key %r") % k)
792 e.append(r)
792 e.append(r)
793 l.append(e)
793 l.append(e)
794 l.sort()
794 l.sort()
795 return [e[-1] for e in l]
795 return [e[-1] for e in l]
796
796
797 def tag(repo, subset, x):
797 def tag(repo, subset, x):
798 """``tag([name])``
798 """``tag([name])``
799 The specified tag by name, or all tagged revisions if no name is given.
799 The specified tag by name, or all tagged revisions if no name is given.
800 """
800 """
801 # i18n: "tag" is a keyword
801 # i18n: "tag" is a keyword
802 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
802 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
803 cl = repo.changelog
803 cl = repo.changelog
804 if args:
804 if args:
805 tn = getstring(args[0],
805 tn = getstring(args[0],
806 # i18n: "tag" is a keyword
806 # i18n: "tag" is a keyword
807 _('the argument to tag must be a string'))
807 _('the argument to tag must be a string'))
808 if not repo.tags().get(tn, None):
808 if not repo.tags().get(tn, None):
809 raise util.Abort(_("tag '%s' does not exist") % tn)
809 raise util.Abort(_("tag '%s' does not exist") % tn)
810 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
810 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
811 else:
811 else:
812 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
812 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
813 return [r for r in subset if r in s]
813 return [r for r in subset if r in s]
814
814
815 def tagged(repo, subset, x):
815 def tagged(repo, subset, x):
816 return tag(repo, subset, x)
816 return tag(repo, subset, x)
817
817
818 def user(repo, subset, x):
818 def user(repo, subset, x):
819 """``user(string)``
819 """``user(string)``
820 User name contains string. The match is case-insensitive.
820 User name contains string. The match is case-insensitive.
821 """
821 """
822 return author(repo, subset, x)
822 return author(repo, subset, x)
823
823
824 symbols = {
824 symbols = {
825 "adds": adds,
825 "adds": adds,
826 "all": getall,
826 "all": getall,
827 "ancestor": ancestor,
827 "ancestor": ancestor,
828 "ancestors": ancestors,
828 "ancestors": ancestors,
829 "author": author,
829 "author": author,
830 "bisected": bisected,
830 "bisected": bisected,
831 "bookmark": bookmark,
831 "bookmark": bookmark,
832 "branch": branch,
832 "branch": branch,
833 "children": children,
833 "children": children,
834 "closed": closed,
834 "closed": closed,
835 "contains": contains,
835 "contains": contains,
836 "date": date,
836 "date": date,
837 "desc": desc,
837 "desc": desc,
838 "descendants": descendants,
838 "descendants": descendants,
839 "file": hasfile,
839 "file": hasfile,
840 "filelog": filelog,
840 "filelog": filelog,
841 "follow": follow,
841 "follow": follow,
842 "grep": grep,
842 "grep": grep,
843 "head": head,
843 "head": head,
844 "heads": heads,
844 "heads": heads,
845 "id": node,
845 "id": node,
846 "keyword": keyword,
846 "keyword": keyword,
847 "last": last,
847 "last": last,
848 "limit": limit,
848 "limit": limit,
849 "max": maxrev,
849 "max": maxrev,
850 "merge": merge,
850 "merge": merge,
851 "min": minrev,
851 "min": minrev,
852 "modifies": modifies,
852 "modifies": modifies,
853 "outgoing": outgoing,
853 "outgoing": outgoing,
854 "p1": p1,
854 "p1": p1,
855 "p2": p2,
855 "p2": p2,
856 "parents": parents,
856 "parents": parents,
857 "present": present,
857 "present": present,
858 "removes": removes,
858 "removes": removes,
859 "rev": rev,
859 "rev": rev,
860 "reverse": reverse,
860 "reverse": reverse,
861 "roots": roots,
861 "roots": roots,
862 "sort": sort,
862 "sort": sort,
863 "tag": tag,
863 "tag": tag,
864 "tagged": tagged,
864 "tagged": tagged,
865 "user": user,
865 "user": user,
866 }
866 }
867
867
868 methods = {
868 methods = {
869 "range": rangeset,
869 "range": rangeset,
870 "string": stringset,
870 "string": stringset,
871 "symbol": symbolset,
871 "symbol": symbolset,
872 "and": andset,
872 "and": andset,
873 "or": orset,
873 "or": orset,
874 "not": notset,
874 "not": notset,
875 "list": listset,
875 "list": listset,
876 "func": func,
876 "func": func,
877 "ancestor": ancestorspec,
877 "ancestor": ancestorspec,
878 "parent": parentspec,
878 "parent": parentspec,
879 "parentpost": p1,
879 "parentpost": p1,
880 }
880 }
881
881
882 def optimize(x, small):
882 def optimize(x, small):
883 if x is None:
883 if x is None:
884 return 0, x
884 return 0, x
885
885
886 smallbonus = 1
886 smallbonus = 1
887 if small:
887 if small:
888 smallbonus = .5
888 smallbonus = .5
889
889
890 op = x[0]
890 op = x[0]
891 if op == 'minus':
891 if op == 'minus':
892 return optimize(('and', x[1], ('not', x[2])), small)
892 return optimize(('and', x[1], ('not', x[2])), small)
893 elif op == 'dagrange':
893 elif op == 'dagrange':
894 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
894 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
895 ('func', ('symbol', 'ancestors'), x[2])), small)
895 ('func', ('symbol', 'ancestors'), x[2])), small)
896 elif op == 'dagrangepre':
896 elif op == 'dagrangepre':
897 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
897 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
898 elif op == 'dagrangepost':
898 elif op == 'dagrangepost':
899 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
899 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
900 elif op == 'rangepre':
900 elif op == 'rangepre':
901 return optimize(('range', ('string', '0'), x[1]), small)
901 return optimize(('range', ('string', '0'), x[1]), small)
902 elif op == 'rangepost':
902 elif op == 'rangepost':
903 return optimize(('range', x[1], ('string', 'tip')), small)
903 return optimize(('range', x[1], ('string', 'tip')), small)
904 elif op == 'negate':
904 elif op == 'negate':
905 return optimize(('string',
905 return optimize(('string',
906 '-' + getstring(x[1], _("can't negate that"))), small)
906 '-' + getstring(x[1], _("can't negate that"))), small)
907 elif op in 'string symbol negate':
907 elif op in 'string symbol negate':
908 return smallbonus, x # single revisions are small
908 return smallbonus, x # single revisions are small
909 elif op == 'and' or op == 'dagrange':
909 elif op == 'and' or op == 'dagrange':
910 wa, ta = optimize(x[1], True)
910 wa, ta = optimize(x[1], True)
911 wb, tb = optimize(x[2], True)
911 wb, tb = optimize(x[2], True)
912 w = min(wa, wb)
912 w = min(wa, wb)
913 if wa > wb:
913 if wa > wb:
914 return w, (op, tb, ta)
914 return w, (op, tb, ta)
915 return w, (op, ta, tb)
915 return w, (op, ta, tb)
916 elif op == 'or':
916 elif op == 'or':
917 wa, ta = optimize(x[1], False)
917 wa, ta = optimize(x[1], False)
918 wb, tb = optimize(x[2], False)
918 wb, tb = optimize(x[2], False)
919 if wb < wa:
919 if wb < wa:
920 wb, wa = wa, wb
920 wb, wa = wa, wb
921 return max(wa, wb), (op, ta, tb)
921 return max(wa, wb), (op, ta, tb)
922 elif op == 'not':
922 elif op == 'not':
923 o = optimize(x[1], not small)
923 o = optimize(x[1], not small)
924 return o[0], (op, o[1])
924 return o[0], (op, o[1])
925 elif op == 'parentpost':
925 elif op == 'parentpost':
926 o = optimize(x[1], small)
926 o = optimize(x[1], small)
927 return o[0], (op, o[1])
927 return o[0], (op, o[1])
928 elif op == 'group':
928 elif op == 'group':
929 return optimize(x[1], small)
929 return optimize(x[1], small)
930 elif op in 'range list parent ancestorspec':
930 elif op in 'range list parent ancestorspec':
931 if op == 'parent':
932 # x^:y means (x^) : y, not x ^ (:y)
933 post = ('parentpost', x[1])
934 if x[2][0] == 'dagrangepre':
935 return optimize(('dagrange', post, x[2][1]), small)
936 elif x[2][0] == 'rangepre':
937 return optimize(('range', post, x[2][1]), small)
938
931 wa, ta = optimize(x[1], small)
939 wa, ta = optimize(x[1], small)
932 wb, tb = optimize(x[2], small)
940 wb, tb = optimize(x[2], small)
933 return wa + wb, (op, ta, tb)
941 return wa + wb, (op, ta, tb)
934 elif op == 'func':
942 elif op == 'func':
935 f = getstring(x[1], _("not a symbol"))
943 f = getstring(x[1], _("not a symbol"))
936 wa, ta = optimize(x[2], small)
944 wa, ta = optimize(x[2], small)
937 if f in ("author branch closed date desc file grep keyword "
945 if f in ("author branch closed date desc file grep keyword "
938 "outgoing user"):
946 "outgoing user"):
939 w = 10 # slow
947 w = 10 # slow
940 elif f in "modifies adds removes":
948 elif f in "modifies adds removes":
941 w = 30 # slower
949 w = 30 # slower
942 elif f == "contains":
950 elif f == "contains":
943 w = 100 # very slow
951 w = 100 # very slow
944 elif f == "ancestor":
952 elif f == "ancestor":
945 w = 1 * smallbonus
953 w = 1 * smallbonus
946 elif f in "reverse limit":
954 elif f in "reverse limit":
947 w = 0
955 w = 0
948 elif f in "sort":
956 elif f in "sort":
949 w = 10 # assume most sorts look at changelog
957 w = 10 # assume most sorts look at changelog
950 else:
958 else:
951 w = 1
959 w = 1
952 return w + wa, (op, x[1], ta)
960 return w + wa, (op, x[1], ta)
953 return 1, x
961 return 1, x
954
962
955 class revsetalias(object):
963 class revsetalias(object):
956 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
964 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
957 args = None
965 args = None
958
966
959 def __init__(self, name, value):
967 def __init__(self, name, value):
960 '''Aliases like:
968 '''Aliases like:
961
969
962 h = heads(default)
970 h = heads(default)
963 b($1) = ancestors($1) - ancestors(default)
971 b($1) = ancestors($1) - ancestors(default)
964 '''
972 '''
965 if isinstance(name, tuple): # parameter substitution
973 if isinstance(name, tuple): # parameter substitution
966 self.tree = name
974 self.tree = name
967 self.replacement = value
975 self.replacement = value
968 else: # alias definition
976 else: # alias definition
969 m = self.funcre.search(name)
977 m = self.funcre.search(name)
970 if m:
978 if m:
971 self.tree = ('func', ('symbol', m.group(1)))
979 self.tree = ('func', ('symbol', m.group(1)))
972 self.args = [x.strip() for x in m.group(2).split(',')]
980 self.args = [x.strip() for x in m.group(2).split(',')]
973 for arg in self.args:
981 for arg in self.args:
974 value = value.replace(arg, repr(arg))
982 value = value.replace(arg, repr(arg))
975 else:
983 else:
976 self.tree = ('symbol', name)
984 self.tree = ('symbol', name)
977
985
978 self.replacement, pos = parse(value)
986 self.replacement, pos = parse(value)
979 if pos != len(value):
987 if pos != len(value):
980 raise error.ParseError(_('invalid token'), pos)
988 raise error.ParseError(_('invalid token'), pos)
981
989
982 def process(self, tree):
990 def process(self, tree):
983 if isinstance(tree, tuple):
991 if isinstance(tree, tuple):
984 if self.args is None:
992 if self.args is None:
985 if tree == self.tree:
993 if tree == self.tree:
986 return self.replacement
994 return self.replacement
987 elif tree[:2] == self.tree:
995 elif tree[:2] == self.tree:
988 l = getlist(tree[2])
996 l = getlist(tree[2])
989 if len(l) != len(self.args):
997 if len(l) != len(self.args):
990 raise error.ParseError(
998 raise error.ParseError(
991 _('invalid number of arguments: %s') % len(l))
999 _('invalid number of arguments: %s') % len(l))
992 result = self.replacement
1000 result = self.replacement
993 for a, v in zip(self.args, l):
1001 for a, v in zip(self.args, l):
994 valalias = revsetalias(('string', a), v)
1002 valalias = revsetalias(('string', a), v)
995 result = valalias.process(result)
1003 result = valalias.process(result)
996 return result
1004 return result
997 return tuple(map(self.process, tree))
1005 return tuple(map(self.process, tree))
998 return tree
1006 return tree
999
1007
1000 def findaliases(ui, tree):
1008 def findaliases(ui, tree):
1001 for k, v in ui.configitems('revsetalias'):
1009 for k, v in ui.configitems('revsetalias'):
1002 alias = revsetalias(k, v)
1010 alias = revsetalias(k, v)
1003 tree = alias.process(tree)
1011 tree = alias.process(tree)
1004 return tree
1012 return tree
1005
1013
1006 parse = parser.parser(tokenize, elements).parse
1014 parse = parser.parser(tokenize, elements).parse
1007
1015
1008 def match(ui, spec):
1016 def match(ui, spec):
1009 if not spec:
1017 if not spec:
1010 raise error.ParseError(_("empty query"))
1018 raise error.ParseError(_("empty query"))
1011 tree, pos = parse(spec)
1019 tree, pos = parse(spec)
1012 if (pos != len(spec)):
1020 if (pos != len(spec)):
1013 raise error.ParseError(_("invalid token"), pos)
1021 raise error.ParseError(_("invalid token"), pos)
1014 tree = findaliases(ui, tree)
1022 tree = findaliases(ui, tree)
1015 weight, tree = optimize(tree, True)
1023 weight, tree = optimize(tree, True)
1016 def mfunc(repo, subset):
1024 def mfunc(repo, subset):
1017 return getset(repo, subset, tree)
1025 return getset(repo, subset, tree)
1018 return mfunc
1026 return mfunc
1019
1027
1020 # tell hggettext to extract docstrings from these functions:
1028 # tell hggettext to extract docstrings from these functions:
1021 i18nfunctions = symbols.values()
1029 i18nfunctions = symbols.values()
General Comments 0
You need to be logged in to leave comments. Login now