##// END OF EJS Templates
revset: add id() and rev() to allow explicitly referring to changes by hash or rev
Augie Fackler -
r12716:c7e619e3 default
parent child Browse files
Show More
@@ -1,197 +1,203 b''
1 Mercurial supports a functional language for selecting a set of
1 Mercurial supports a functional language for selecting a set of
2 revisions.
2 revisions.
3
3
4 The language supports a number of predicates which are joined by infix
4 The language supports a number of predicates which are joined by infix
5 operators. Parenthesis can be used for grouping.
5 operators. Parenthesis can be used for grouping.
6
6
7 Identifiers such as branch names must be quoted with single or double
7 Identifiers such as branch names must be quoted with single or double
8 quotes if they contain characters outside of
8 quotes if they contain characters outside of
9 ``[._a-zA-Z0-9\x80-\xff]`` or if they match one of the predefined
9 ``[._a-zA-Z0-9\x80-\xff]`` or if they match one of the predefined
10 predicates.
10 predicates.
11
11
12 Special characters can be used in quoted identifiers by escaping them,
12 Special characters can be used in quoted identifiers by escaping them,
13 e.g., ``\n`` is interpreted as a newline. To prevent them from being
13 e.g., ``\n`` is interpreted as a newline. To prevent them from being
14 interpreted, strings can be prefixed with ``r``, e.g. ``r'...'``.
14 interpreted, strings can be prefixed with ``r``, e.g. ``r'...'``.
15
15
16 There is a single prefix operator:
16 There is a single prefix operator:
17
17
18 ``not x``
18 ``not x``
19 Changesets not in x. Short form is ``! x``.
19 Changesets not in x. Short form is ``! x``.
20
20
21 These are the supported infix operators:
21 These are the supported infix operators:
22
22
23 ``x::y``
23 ``x::y``
24 A DAG range, meaning all changesets that are descendants of x and
24 A DAG range, meaning all changesets that are descendants of x and
25 ancestors of y, including x and y themselves. If the first endpoint
25 ancestors of y, including x and y themselves. If the first endpoint
26 is left out, this is equivalent to ``ancestors(y)``, if the second
26 is left out, this is equivalent to ``ancestors(y)``, if the second
27 is left out it is equivalent to ``descendants(x)``.
27 is left out it is equivalent to ``descendants(x)``.
28
28
29 An alternative syntax is ``x..y``.
29 An alternative syntax is ``x..y``.
30
30
31 ``x:y``
31 ``x:y``
32 All changesets with revision numbers between x and y, both
32 All changesets with revision numbers between x and y, both
33 inclusive. Either endpoint can be left out, they default to 0 and
33 inclusive. Either endpoint can be left out, they default to 0 and
34 tip.
34 tip.
35
35
36 ``x and y``
36 ``x and y``
37 The intersection of changesets in x and y. Short form is ``x & y``.
37 The intersection of changesets in x and y. Short form is ``x & y``.
38
38
39 ``x or y``
39 ``x or y``
40 The union of changesets in x and y. There are two alternative short
40 The union of changesets in x and y. There are two alternative short
41 forms: ``x | y`` and ``x + y``.
41 forms: ``x | y`` and ``x + y``.
42
42
43 ``x - y``
43 ``x - y``
44 Changesets in x but not in y.
44 Changesets in x but not in y.
45
45
46 The following predicates are supported:
46 The following predicates are supported:
47
47
48 ``adds(pattern)``
48 ``adds(pattern)``
49 Changesets that add a file matching pattern.
49 Changesets that add a file matching pattern.
50
50
51 ``all()``
51 ``all()``
52 All changesets, the same as ``0:tip``.
52 All changesets, the same as ``0:tip``.
53
53
54 ``ancestor(single, single)``
54 ``ancestor(single, single)``
55 Greatest common ancestor of the two changesets.
55 Greatest common ancestor of the two changesets.
56
56
57 ``ancestors(set)``
57 ``ancestors(set)``
58 Changesets that are ancestors of a changeset in set.
58 Changesets that are ancestors of a changeset in set.
59
59
60 ``author(string)``
60 ``author(string)``
61 Alias for ``user(string)``.
61 Alias for ``user(string)``.
62
62
63 ``branch(set)``
63 ``branch(set)``
64 All changesets belonging to the branches of changesets in set.
64 All changesets belonging to the branches of changesets in set.
65
65
66 ``children(set)``
66 ``children(set)``
67 Child changesets of changesets in set.
67 Child changesets of changesets in set.
68
68
69 ``closed()``
69 ``closed()``
70 Changeset is closed.
70 Changeset is closed.
71
71
72 ``contains(pattern)``
72 ``contains(pattern)``
73 Revision contains pattern.
73 Revision contains pattern.
74
74
75 ``date(interval)``
75 ``date(interval)``
76 Changesets within the interval, see :hg:`help dates`.
76 Changesets within the interval, see :hg:`help dates`.
77
77
78 ``descendants(set)``
78 ``descendants(set)``
79 Changesets which are descendants of changesets in set.
79 Changesets which are descendants of changesets in set.
80
80
81 ``file(pattern)``
81 ``file(pattern)``
82 Changesets affecting files matched by pattern.
82 Changesets affecting files matched by pattern.
83
83
84 ``follow()``
84 ``follow()``
85 An alias for ``::.`` (ancestors of the working copy's first parent).
85 An alias for ``::.`` (ancestors of the working copy's first parent).
86
86
87 ``grep(regex)``
87 ``grep(regex)``
88 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
88 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
89 to ensure special escape characters are handled correctly.
89 to ensure special escape characters are handled correctly.
90
90
91 ``head()``
91 ``head()``
92 Changeset is a named branch head.
92 Changeset is a named branch head.
93
93
94 ``heads(set)``
94 ``heads(set)``
95 Members of set with no children in set.
95 Members of set with no children in set.
96
96
97 ``id(string)``
98 Revision non-ambiguously specified by the given hex string prefix
99
97 ``keyword(string)``
100 ``keyword(string)``
98 Search commit message, user name, and names of changed files for
101 Search commit message, user name, and names of changed files for
99 string.
102 string.
100
103
101 ``limit(set, n)``
104 ``limit(set, n)``
102 First n members of set.
105 First n members of set.
103
106
104 ``max(set)``
107 ``max(set)``
105 Changeset with highest revision number in set.
108 Changeset with highest revision number in set.
106
109
107 ``min(set)``
110 ``min(set)``
108 Changeset with lowest revision number in set.
111 Changeset with lowest revision number in set.
109
112
110 ``merge()``
113 ``merge()``
111 Changeset is a merge changeset.
114 Changeset is a merge changeset.
112
115
113 ``modifies(pattern)``
116 ``modifies(pattern)``
114 Changesets modifying files matched by pattern.
117 Changesets modifying files matched by pattern.
115
118
116 ``outgoing([path])``
119 ``outgoing([path])``
117 Changesets not found in the specified destination repository, or the
120 Changesets not found in the specified destination repository, or the
118 default push location.
121 default push location.
119
122
120 ``p1(set)``
123 ``p1(set)``
121 First parent of changesets in set.
124 First parent of changesets in set.
122
125
123 ``p2(set)``
126 ``p2(set)``
124 Second parent of changesets in set.
127 Second parent of changesets in set.
125
128
126 ``parents(set)``
129 ``parents(set)``
127 The set of all parents for all changesets in set.
130 The set of all parents for all changesets in set.
128
131
129 ``present(set)``
132 ``present(set)``
130 An empty set, if any revision in set isn't found; otherwise,
133 An empty set, if any revision in set isn't found; otherwise,
131 all revisions in set.
134 all revisions in set.
132
135
133 ``removes(pattern)``
136 ``removes(pattern)``
134 Changesets which remove files matching pattern.
137 Changesets which remove files matching pattern.
135
138
139 ``rev(number)``
140 Revision with the given numeric identifier.
141
136 ``reverse(set)``
142 ``reverse(set)``
137 Reverse order of set.
143 Reverse order of set.
138
144
139 ``roots(set)``
145 ``roots(set)``
140 Changesets with no parent changeset in set.
146 Changesets with no parent changeset in set.
141
147
142 ``sort(set[, [-]key...])``
148 ``sort(set[, [-]key...])``
143 Sort set by keys. The default sort order is ascending, specify a key
149 Sort set by keys. The default sort order is ascending, specify a key
144 as ``-key`` to sort in descending order.
150 as ``-key`` to sort in descending order.
145
151
146 The keys can be:
152 The keys can be:
147
153
148 - ``rev`` for the revision number,
154 - ``rev`` for the revision number,
149 - ``branch`` for the branch name,
155 - ``branch`` for the branch name,
150 - ``desc`` for the commit message (description),
156 - ``desc`` for the commit message (description),
151 - ``user`` for user name (``author`` can be used as an alias),
157 - ``user`` for user name (``author`` can be used as an alias),
152 - ``date`` for the commit date
158 - ``date`` for the commit date
153
159
154 ``tag(name)``
160 ``tag(name)``
155 The specified tag by name, or all tagged revisions if no name is given.
161 The specified tag by name, or all tagged revisions if no name is given.
156
162
157 ``user(string)``
163 ``user(string)``
158 User name is string.
164 User name is string.
159
165
160 Command line equivalents for :hg:`log`::
166 Command line equivalents for :hg:`log`::
161
167
162 -f -> ::.
168 -f -> ::.
163 -d x -> date(x)
169 -d x -> date(x)
164 -k x -> keyword(x)
170 -k x -> keyword(x)
165 -m -> merge()
171 -m -> merge()
166 -u x -> user(x)
172 -u x -> user(x)
167 -b x -> branch(x)
173 -b x -> branch(x)
168 -P x -> !::x
174 -P x -> !::x
169 -l x -> limit(expr, x)
175 -l x -> limit(expr, x)
170
176
171 Some sample queries:
177 Some sample queries:
172
178
173 - Changesets on the default branch::
179 - Changesets on the default branch::
174
180
175 hg log -r 'branch(default)'
181 hg log -r 'branch(default)'
176
182
177 - Changesets on the default branch since tag 1.5 (excluding merges)::
183 - Changesets on the default branch since tag 1.5 (excluding merges)::
178
184
179 hg log -r 'branch(default) and 1.5:: and not merge()'
185 hg log -r 'branch(default) and 1.5:: and not merge()'
180
186
181 - Open branch heads::
187 - Open branch heads::
182
188
183 hg log -r 'head() and not closed()'
189 hg log -r 'head() and not closed()'
184
190
185 - Changesets between tags 1.3 and 1.5 mentioning "bug" that affect
191 - Changesets between tags 1.3 and 1.5 mentioning "bug" that affect
186 ``hgext/*``::
192 ``hgext/*``::
187
193
188 hg log -r '1.3::1.5 and keyword(bug) and file("hgext/*")'
194 hg log -r '1.3::1.5 and keyword(bug) and file("hgext/*")'
189
195
190 - Changesets in committed May 2008, sorted by user::
196 - Changesets in committed May 2008, sorted by user::
191
197
192 hg log -r 'sort(date("May 2008"), user)'
198 hg log -r 'sort(date("May 2008"), user)'
193
199
194 - Changesets mentioning "bug" or "issue" that are not in a tagged
200 - Changesets mentioning "bug" or "issue" that are not in a tagged
195 release::
201 release::
196
202
197 hg log -r '(keyword(bug) or keyword(issue)) and not ancestors(tagged())'
203 hg log -r '(keyword(bug) or keyword(issue)) and not ancestors(tagged())'
@@ -1,607 +1,626 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
9 import parser, util, error, discovery
10 import match as matchmod
10 import match as matchmod
11 from i18n import _
11 from i18n import _
12
12
13 elements = {
13 elements = {
14 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
14 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "-": (5, ("negate", 19), ("minus", 5)),
15 "-": (5, ("negate", 19), ("minus", 5)),
16 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
16 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
17 ("dagrangepost", 17)),
17 ("dagrangepost", 17)),
18 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
18 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
19 ("dagrangepost", 17)),
19 ("dagrangepost", 17)),
20 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
20 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
21 "not": (10, ("not", 10)),
21 "not": (10, ("not", 10)),
22 "!": (10, ("not", 10)),
22 "!": (10, ("not", 10)),
23 "and": (5, None, ("and", 5)),
23 "and": (5, None, ("and", 5)),
24 "&": (5, None, ("and", 5)),
24 "&": (5, None, ("and", 5)),
25 "or": (4, None, ("or", 4)),
25 "or": (4, None, ("or", 4)),
26 "|": (4, None, ("or", 4)),
26 "|": (4, None, ("or", 4)),
27 "+": (4, None, ("or", 4)),
27 "+": (4, None, ("or", 4)),
28 ",": (2, None, ("list", 2)),
28 ",": (2, None, ("list", 2)),
29 ")": (0, None, None),
29 ")": (0, None, None),
30 "symbol": (0, ("symbol",), None),
30 "symbol": (0, ("symbol",), None),
31 "string": (0, ("string",), None),
31 "string": (0, ("string",), None),
32 "end": (0, None, None),
32 "end": (0, None, None),
33 }
33 }
34
34
35 keywords = set(['and', 'or', 'not'])
35 keywords = set(['and', 'or', 'not'])
36
36
37 def tokenize(program):
37 def tokenize(program):
38 pos, l = 0, len(program)
38 pos, l = 0, len(program)
39 while pos < l:
39 while pos < l:
40 c = program[pos]
40 c = program[pos]
41 if c.isspace(): # skip inter-token whitespace
41 if c.isspace(): # skip inter-token whitespace
42 pass
42 pass
43 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
43 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
44 yield ('::', None, pos)
44 yield ('::', None, pos)
45 pos += 1 # skip ahead
45 pos += 1 # skip ahead
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 in "():,-|&+!": # handle simple operators
49 elif c in "():,-|&+!": # handle simple operators
50 yield (c, None, pos)
50 yield (c, None, pos)
51 elif (c in '"\'' or c == 'r' and
51 elif (c in '"\'' or c == 'r' and
52 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
52 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
53 if c == 'r':
53 if c == 'r':
54 pos += 1
54 pos += 1
55 c = program[pos]
55 c = program[pos]
56 decode = lambda x: x
56 decode = lambda x: x
57 else:
57 else:
58 decode = lambda x: x.decode('string-escape')
58 decode = lambda x: x.decode('string-escape')
59 pos += 1
59 pos += 1
60 s = pos
60 s = pos
61 while pos < l: # find closing quote
61 while pos < l: # find closing quote
62 d = program[pos]
62 d = program[pos]
63 if d == '\\': # skip over escaped characters
63 if d == '\\': # skip over escaped characters
64 pos += 2
64 pos += 2
65 continue
65 continue
66 if d == c:
66 if d == c:
67 yield ('string', decode(program[s:pos]), s)
67 yield ('string', decode(program[s:pos]), s)
68 break
68 break
69 pos += 1
69 pos += 1
70 else:
70 else:
71 raise error.ParseError(_("unterminated string"), s)
71 raise error.ParseError(_("unterminated string"), s)
72 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
72 elif c.isalnum() or c in '._' or ord(c) > 127: # 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 "._" or ord(d) > 127):
77 if not (d.isalnum() or d in "._" or ord(d) > 127):
78 break
78 break
79 if d == '.' and program[pos - 1] == '.': # special case for ..
79 if d == '.' and program[pos - 1] == '.': # special case for ..
80 pos -= 1
80 pos -= 1
81 break
81 break
82 pos += 1
82 pos += 1
83 sym = program[s:pos]
83 sym = program[s:pos]
84 if sym in keywords: # operator keywords
84 if sym in keywords: # operator keywords
85 yield (sym, None, s)
85 yield (sym, None, s)
86 else:
86 else:
87 yield ('symbol', sym, s)
87 yield ('symbol', sym, s)
88 pos -= 1
88 pos -= 1
89 else:
89 else:
90 raise error.ParseError(_("syntax error"), pos)
90 raise error.ParseError(_("syntax error"), pos)
91 pos += 1
91 pos += 1
92 yield ('end', None, pos)
92 yield ('end', None, pos)
93
93
94 # helpers
94 # helpers
95
95
96 def getstring(x, err):
96 def getstring(x, err):
97 if x and (x[0] == 'string' or x[0] == 'symbol'):
97 if x and (x[0] == 'string' or x[0] == 'symbol'):
98 return x[1]
98 return x[1]
99 raise error.ParseError(err)
99 raise error.ParseError(err)
100
100
101 def getlist(x):
101 def getlist(x):
102 if not x:
102 if not x:
103 return []
103 return []
104 if x[0] == 'list':
104 if x[0] == 'list':
105 return getlist(x[1]) + [x[2]]
105 return getlist(x[1]) + [x[2]]
106 return [x]
106 return [x]
107
107
108 def getargs(x, min, max, err):
108 def getargs(x, min, max, err):
109 l = getlist(x)
109 l = getlist(x)
110 if len(l) < min or len(l) > max:
110 if len(l) < min or len(l) > max:
111 raise error.ParseError(err)
111 raise error.ParseError(err)
112 return l
112 return l
113
113
114 def getset(repo, subset, x):
114 def getset(repo, subset, x):
115 if not x:
115 if not x:
116 raise error.ParseError(_("missing argument"))
116 raise error.ParseError(_("missing argument"))
117 return methods[x[0]](repo, subset, *x[1:])
117 return methods[x[0]](repo, subset, *x[1:])
118
118
119 # operator methods
119 # operator methods
120
120
121 def stringset(repo, subset, x):
121 def stringset(repo, subset, x):
122 x = repo[x].rev()
122 x = repo[x].rev()
123 if x == -1 and len(subset) == len(repo):
123 if x == -1 and len(subset) == len(repo):
124 return [-1]
124 return [-1]
125 if x in subset:
125 if x in subset:
126 return [x]
126 return [x]
127 return []
127 return []
128
128
129 def symbolset(repo, subset, x):
129 def symbolset(repo, subset, x):
130 if x in symbols:
130 if x in symbols:
131 raise error.ParseError(_("can't use %s here") % x)
131 raise error.ParseError(_("can't use %s here") % x)
132 return stringset(repo, subset, x)
132 return stringset(repo, subset, x)
133
133
134 def rangeset(repo, subset, x, y):
134 def rangeset(repo, subset, x, y):
135 m = getset(repo, subset, x)
135 m = getset(repo, subset, x)
136 if not m:
136 if not m:
137 m = getset(repo, range(len(repo)), x)
137 m = getset(repo, range(len(repo)), x)
138
138
139 n = getset(repo, subset, y)
139 n = getset(repo, subset, y)
140 if not n:
140 if not n:
141 n = getset(repo, range(len(repo)), y)
141 n = getset(repo, range(len(repo)), y)
142
142
143 if not m or not n:
143 if not m or not n:
144 return []
144 return []
145 m, n = m[0], n[-1]
145 m, n = m[0], n[-1]
146
146
147 if m < n:
147 if m < n:
148 r = range(m, n + 1)
148 r = range(m, n + 1)
149 else:
149 else:
150 r = range(m, n - 1, -1)
150 r = range(m, n - 1, -1)
151 s = set(subset)
151 s = set(subset)
152 return [x for x in r if x in s]
152 return [x for x in r if x in s]
153
153
154 def andset(repo, subset, x, y):
154 def andset(repo, subset, x, y):
155 return getset(repo, getset(repo, subset, x), y)
155 return getset(repo, getset(repo, subset, x), y)
156
156
157 def orset(repo, subset, x, y):
157 def orset(repo, subset, x, y):
158 s = set(getset(repo, subset, x))
158 s = set(getset(repo, subset, x))
159 s |= set(getset(repo, [r for r in subset if r not in s], y))
159 s |= set(getset(repo, [r for r in subset if r not in s], y))
160 return [r for r in subset if r in s]
160 return [r for r in subset if r in s]
161
161
162 def notset(repo, subset, x):
162 def notset(repo, subset, x):
163 s = set(getset(repo, subset, x))
163 s = set(getset(repo, subset, x))
164 return [r for r in subset if r not in s]
164 return [r for r in subset if r not in s]
165
165
166 def listset(repo, subset, a, b):
166 def listset(repo, subset, a, b):
167 raise error.ParseError(_("can't use a list in this context"))
167 raise error.ParseError(_("can't use a list in this context"))
168
168
169 def func(repo, subset, a, b):
169 def func(repo, subset, a, b):
170 if a[0] == 'symbol' and a[1] in symbols:
170 if a[0] == 'symbol' and a[1] in symbols:
171 return symbols[a[1]](repo, subset, b)
171 return symbols[a[1]](repo, subset, b)
172 raise error.ParseError(_("not a function: %s") % a[1])
172 raise error.ParseError(_("not a function: %s") % a[1])
173
173
174 # functions
174 # functions
175
175
176 def node(repo, subset, x):
177 l = getargs(x, 1, 1, _("rev wants one argument"))
178 n = getstring(l[0], _("rev wants a string"))
179 if len(n) == 40:
180 rn = repo[n].rev()
181 else:
182 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
183 return [r for r in subset if r == rn]
184
185 def rev(repo, subset, x):
186 l = getargs(x, 1, 1, _("rev wants one argument"))
187 try:
188 l = int(getstring(l[0], _("rev wants a number")))
189 except ValueError:
190 raise error.ParseError(_("rev expects a number"))
191 return [r for r in subset if r == l]
192
176 def p1(repo, subset, x):
193 def p1(repo, subset, x):
177 ps = set()
194 ps = set()
178 cl = repo.changelog
195 cl = repo.changelog
179 for r in getset(repo, subset, x):
196 for r in getset(repo, subset, x):
180 ps.add(cl.parentrevs(r)[0])
197 ps.add(cl.parentrevs(r)[0])
181 return [r for r in subset if r in ps]
198 return [r for r in subset if r in ps]
182
199
183 def p2(repo, subset, x):
200 def p2(repo, subset, x):
184 ps = set()
201 ps = set()
185 cl = repo.changelog
202 cl = repo.changelog
186 for r in getset(repo, subset, x):
203 for r in getset(repo, subset, x):
187 ps.add(cl.parentrevs(r)[1])
204 ps.add(cl.parentrevs(r)[1])
188 return [r for r in subset if r in ps]
205 return [r for r in subset if r in ps]
189
206
190 def parents(repo, subset, x):
207 def parents(repo, subset, x):
191 ps = set()
208 ps = set()
192 cl = repo.changelog
209 cl = repo.changelog
193 for r in getset(repo, subset, x):
210 for r in getset(repo, subset, x):
194 ps.update(cl.parentrevs(r))
211 ps.update(cl.parentrevs(r))
195 return [r for r in subset if r in ps]
212 return [r for r in subset if r in ps]
196
213
197 def maxrev(repo, subset, x):
214 def maxrev(repo, subset, x):
198 s = getset(repo, subset, x)
215 s = getset(repo, subset, x)
199 if s:
216 if s:
200 m = max(s)
217 m = max(s)
201 if m in subset:
218 if m in subset:
202 return [m]
219 return [m]
203 return []
220 return []
204
221
205 def minrev(repo, subset, x):
222 def minrev(repo, subset, x):
206 s = getset(repo, subset, x)
223 s = getset(repo, subset, x)
207 if s:
224 if s:
208 m = min(s)
225 m = min(s)
209 if m in subset:
226 if m in subset:
210 return [m]
227 return [m]
211 return []
228 return []
212
229
213 def limit(repo, subset, x):
230 def limit(repo, subset, x):
214 l = getargs(x, 2, 2, _("limit wants two arguments"))
231 l = getargs(x, 2, 2, _("limit wants two arguments"))
215 try:
232 try:
216 lim = int(getstring(l[1], _("limit wants a number")))
233 lim = int(getstring(l[1], _("limit wants a number")))
217 except ValueError:
234 except ValueError:
218 raise error.ParseError(_("limit expects a number"))
235 raise error.ParseError(_("limit expects a number"))
219 return getset(repo, subset, l[0])[:lim]
236 return getset(repo, subset, l[0])[:lim]
220
237
221 def children(repo, subset, x):
238 def children(repo, subset, x):
222 cs = set()
239 cs = set()
223 cl = repo.changelog
240 cl = repo.changelog
224 s = set(getset(repo, subset, x))
241 s = set(getset(repo, subset, x))
225 for r in xrange(0, len(repo)):
242 for r in xrange(0, len(repo)):
226 for p in cl.parentrevs(r):
243 for p in cl.parentrevs(r):
227 if p in s:
244 if p in s:
228 cs.add(r)
245 cs.add(r)
229 return [r for r in subset if r in cs]
246 return [r for r in subset if r in cs]
230
247
231 def branch(repo, subset, x):
248 def branch(repo, subset, x):
232 s = getset(repo, range(len(repo)), x)
249 s = getset(repo, range(len(repo)), x)
233 b = set()
250 b = set()
234 for r in s:
251 for r in s:
235 b.add(repo[r].branch())
252 b.add(repo[r].branch())
236 s = set(s)
253 s = set(s)
237 return [r for r in subset if r in s or repo[r].branch() in b]
254 return [r for r in subset if r in s or repo[r].branch() in b]
238
255
239 def ancestor(repo, subset, x):
256 def ancestor(repo, subset, x):
240 l = getargs(x, 2, 2, _("ancestor wants two arguments"))
257 l = getargs(x, 2, 2, _("ancestor wants two arguments"))
241 r = range(len(repo))
258 r = range(len(repo))
242 a = getset(repo, r, l[0])
259 a = getset(repo, r, l[0])
243 b = getset(repo, r, l[1])
260 b = getset(repo, r, l[1])
244 if len(a) != 1 or len(b) != 1:
261 if len(a) != 1 or len(b) != 1:
245 raise error.ParseError(_("ancestor arguments must be single revisions"))
262 raise error.ParseError(_("ancestor arguments must be single revisions"))
246 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
263 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
247
264
248 return [r for r in an if r in subset]
265 return [r for r in an if r in subset]
249
266
250 def ancestors(repo, subset, x):
267 def ancestors(repo, subset, x):
251 args = getset(repo, range(len(repo)), x)
268 args = getset(repo, range(len(repo)), x)
252 if not args:
269 if not args:
253 return []
270 return []
254 s = set(repo.changelog.ancestors(*args)) | set(args)
271 s = set(repo.changelog.ancestors(*args)) | set(args)
255 return [r for r in subset if r in s]
272 return [r for r in subset if r in s]
256
273
257 def descendants(repo, subset, x):
274 def descendants(repo, subset, x):
258 args = getset(repo, range(len(repo)), x)
275 args = getset(repo, range(len(repo)), x)
259 if not args:
276 if not args:
260 return []
277 return []
261 s = set(repo.changelog.descendants(*args)) | set(args)
278 s = set(repo.changelog.descendants(*args)) | set(args)
262 return [r for r in subset if r in s]
279 return [r for r in subset if r in s]
263
280
264 def follow(repo, subset, x):
281 def follow(repo, subset, x):
265 getargs(x, 0, 0, _("follow takes no arguments"))
282 getargs(x, 0, 0, _("follow takes no arguments"))
266 p = repo['.'].rev()
283 p = repo['.'].rev()
267 s = set(repo.changelog.ancestors(p)) | set([p])
284 s = set(repo.changelog.ancestors(p)) | set([p])
268 return [r for r in subset if r in s]
285 return [r for r in subset if r in s]
269
286
270 def date(repo, subset, x):
287 def date(repo, subset, x):
271 ds = getstring(x, _("date wants a string"))
288 ds = getstring(x, _("date wants a string"))
272 dm = util.matchdate(ds)
289 dm = util.matchdate(ds)
273 return [r for r in subset if dm(repo[r].date()[0])]
290 return [r for r in subset if dm(repo[r].date()[0])]
274
291
275 def keyword(repo, subset, x):
292 def keyword(repo, subset, x):
276 kw = getstring(x, _("keyword wants a string")).lower()
293 kw = getstring(x, _("keyword wants a string")).lower()
277 l = []
294 l = []
278 for r in subset:
295 for r in subset:
279 c = repo[r]
296 c = repo[r]
280 t = " ".join(c.files() + [c.user(), c.description()])
297 t = " ".join(c.files() + [c.user(), c.description()])
281 if kw in t.lower():
298 if kw in t.lower():
282 l.append(r)
299 l.append(r)
283 return l
300 return l
284
301
285 def grep(repo, subset, x):
302 def grep(repo, subset, x):
286 try:
303 try:
287 gr = re.compile(getstring(x, _("grep wants a string")))
304 gr = re.compile(getstring(x, _("grep wants a string")))
288 except re.error, e:
305 except re.error, e:
289 raise error.ParseError(_('invalid match pattern: %s') % e)
306 raise error.ParseError(_('invalid match pattern: %s') % e)
290 l = []
307 l = []
291 for r in subset:
308 for r in subset:
292 c = repo[r]
309 c = repo[r]
293 for e in c.files() + [c.user(), c.description()]:
310 for e in c.files() + [c.user(), c.description()]:
294 if gr.search(e):
311 if gr.search(e):
295 l.append(r)
312 l.append(r)
296 continue
313 continue
297 return l
314 return l
298
315
299 def author(repo, subset, x):
316 def author(repo, subset, x):
300 n = getstring(x, _("author wants a string")).lower()
317 n = getstring(x, _("author wants a string")).lower()
301 return [r for r in subset if n in repo[r].user().lower()]
318 return [r for r in subset if n in repo[r].user().lower()]
302
319
303 def hasfile(repo, subset, x):
320 def hasfile(repo, subset, x):
304 pat = getstring(x, _("file wants a pattern"))
321 pat = getstring(x, _("file wants a pattern"))
305 m = matchmod.match(repo.root, repo.getcwd(), [pat])
322 m = matchmod.match(repo.root, repo.getcwd(), [pat])
306 s = []
323 s = []
307 for r in subset:
324 for r in subset:
308 for f in repo[r].files():
325 for f in repo[r].files():
309 if m(f):
326 if m(f):
310 s.append(r)
327 s.append(r)
311 continue
328 continue
312 return s
329 return s
313
330
314 def contains(repo, subset, x):
331 def contains(repo, subset, x):
315 pat = getstring(x, _("contains wants a pattern"))
332 pat = getstring(x, _("contains wants a pattern"))
316 m = matchmod.match(repo.root, repo.getcwd(), [pat])
333 m = matchmod.match(repo.root, repo.getcwd(), [pat])
317 s = []
334 s = []
318 if m.files() == [pat]:
335 if m.files() == [pat]:
319 for r in subset:
336 for r in subset:
320 if pat in repo[r]:
337 if pat in repo[r]:
321 s.append(r)
338 s.append(r)
322 continue
339 continue
323 else:
340 else:
324 for r in subset:
341 for r in subset:
325 for f in repo[r].manifest():
342 for f in repo[r].manifest():
326 if m(f):
343 if m(f):
327 s.append(r)
344 s.append(r)
328 continue
345 continue
329 return s
346 return s
330
347
331 def checkstatus(repo, subset, pat, field):
348 def checkstatus(repo, subset, pat, field):
332 m = matchmod.match(repo.root, repo.getcwd(), [pat])
349 m = matchmod.match(repo.root, repo.getcwd(), [pat])
333 s = []
350 s = []
334 fast = (m.files() == [pat])
351 fast = (m.files() == [pat])
335 for r in subset:
352 for r in subset:
336 c = repo[r]
353 c = repo[r]
337 if fast:
354 if fast:
338 if pat not in c.files():
355 if pat not in c.files():
339 continue
356 continue
340 else:
357 else:
341 for f in c.files():
358 for f in c.files():
342 if m(f):
359 if m(f):
343 break
360 break
344 else:
361 else:
345 continue
362 continue
346 files = repo.status(c.p1().node(), c.node())[field]
363 files = repo.status(c.p1().node(), c.node())[field]
347 if fast:
364 if fast:
348 if pat in files:
365 if pat in files:
349 s.append(r)
366 s.append(r)
350 continue
367 continue
351 else:
368 else:
352 for f in files:
369 for f in files:
353 if m(f):
370 if m(f):
354 s.append(r)
371 s.append(r)
355 continue
372 continue
356 return s
373 return s
357
374
358 def modifies(repo, subset, x):
375 def modifies(repo, subset, x):
359 pat = getstring(x, _("modifies wants a pattern"))
376 pat = getstring(x, _("modifies wants a pattern"))
360 return checkstatus(repo, subset, pat, 0)
377 return checkstatus(repo, subset, pat, 0)
361
378
362 def adds(repo, subset, x):
379 def adds(repo, subset, x):
363 pat = getstring(x, _("adds wants a pattern"))
380 pat = getstring(x, _("adds wants a pattern"))
364 return checkstatus(repo, subset, pat, 1)
381 return checkstatus(repo, subset, pat, 1)
365
382
366 def removes(repo, subset, x):
383 def removes(repo, subset, x):
367 pat = getstring(x, _("removes wants a pattern"))
384 pat = getstring(x, _("removes wants a pattern"))
368 return checkstatus(repo, subset, pat, 2)
385 return checkstatus(repo, subset, pat, 2)
369
386
370 def merge(repo, subset, x):
387 def merge(repo, subset, x):
371 getargs(x, 0, 0, _("merge takes no arguments"))
388 getargs(x, 0, 0, _("merge takes no arguments"))
372 cl = repo.changelog
389 cl = repo.changelog
373 return [r for r in subset if cl.parentrevs(r)[1] != -1]
390 return [r for r in subset if cl.parentrevs(r)[1] != -1]
374
391
375 def closed(repo, subset, x):
392 def closed(repo, subset, x):
376 getargs(x, 0, 0, _("closed takes no arguments"))
393 getargs(x, 0, 0, _("closed takes no arguments"))
377 return [r for r in subset if repo[r].extra().get('close')]
394 return [r for r in subset if repo[r].extra().get('close')]
378
395
379 def head(repo, subset, x):
396 def head(repo, subset, x):
380 getargs(x, 0, 0, _("head takes no arguments"))
397 getargs(x, 0, 0, _("head takes no arguments"))
381 hs = set()
398 hs = set()
382 for b, ls in repo.branchmap().iteritems():
399 for b, ls in repo.branchmap().iteritems():
383 hs.update(repo[h].rev() for h in ls)
400 hs.update(repo[h].rev() for h in ls)
384 return [r for r in subset if r in hs]
401 return [r for r in subset if r in hs]
385
402
386 def reverse(repo, subset, x):
403 def reverse(repo, subset, x):
387 l = getset(repo, subset, x)
404 l = getset(repo, subset, x)
388 l.reverse()
405 l.reverse()
389 return l
406 return l
390
407
391 def present(repo, subset, x):
408 def present(repo, subset, x):
392 try:
409 try:
393 return getset(repo, subset, x)
410 return getset(repo, subset, x)
394 except error.RepoLookupError:
411 except error.RepoLookupError:
395 return []
412 return []
396
413
397 def sort(repo, subset, x):
414 def sort(repo, subset, x):
398 l = getargs(x, 1, 2, _("sort wants one or two arguments"))
415 l = getargs(x, 1, 2, _("sort wants one or two arguments"))
399 keys = "rev"
416 keys = "rev"
400 if len(l) == 2:
417 if len(l) == 2:
401 keys = getstring(l[1], _("sort spec must be a string"))
418 keys = getstring(l[1], _("sort spec must be a string"))
402
419
403 s = l[0]
420 s = l[0]
404 keys = keys.split()
421 keys = keys.split()
405 l = []
422 l = []
406 def invert(s):
423 def invert(s):
407 return "".join(chr(255 - ord(c)) for c in s)
424 return "".join(chr(255 - ord(c)) for c in s)
408 for r in getset(repo, subset, s):
425 for r in getset(repo, subset, s):
409 c = repo[r]
426 c = repo[r]
410 e = []
427 e = []
411 for k in keys:
428 for k in keys:
412 if k == 'rev':
429 if k == 'rev':
413 e.append(r)
430 e.append(r)
414 elif k == '-rev':
431 elif k == '-rev':
415 e.append(-r)
432 e.append(-r)
416 elif k == 'branch':
433 elif k == 'branch':
417 e.append(c.branch())
434 e.append(c.branch())
418 elif k == '-branch':
435 elif k == '-branch':
419 e.append(invert(c.branch()))
436 e.append(invert(c.branch()))
420 elif k == 'desc':
437 elif k == 'desc':
421 e.append(c.description())
438 e.append(c.description())
422 elif k == '-desc':
439 elif k == '-desc':
423 e.append(invert(c.description()))
440 e.append(invert(c.description()))
424 elif k in 'user author':
441 elif k in 'user author':
425 e.append(c.user())
442 e.append(c.user())
426 elif k in '-user -author':
443 elif k in '-user -author':
427 e.append(invert(c.user()))
444 e.append(invert(c.user()))
428 elif k == 'date':
445 elif k == 'date':
429 e.append(c.date()[0])
446 e.append(c.date()[0])
430 elif k == '-date':
447 elif k == '-date':
431 e.append(-c.date()[0])
448 e.append(-c.date()[0])
432 else:
449 else:
433 raise error.ParseError(_("unknown sort key %r") % k)
450 raise error.ParseError(_("unknown sort key %r") % k)
434 e.append(r)
451 e.append(r)
435 l.append(e)
452 l.append(e)
436 l.sort()
453 l.sort()
437 return [e[-1] for e in l]
454 return [e[-1] for e in l]
438
455
439 def getall(repo, subset, x):
456 def getall(repo, subset, x):
440 getargs(x, 0, 0, _("all takes no arguments"))
457 getargs(x, 0, 0, _("all takes no arguments"))
441 return subset
458 return subset
442
459
443 def heads(repo, subset, x):
460 def heads(repo, subset, x):
444 s = getset(repo, subset, x)
461 s = getset(repo, subset, x)
445 ps = set(parents(repo, subset, x))
462 ps = set(parents(repo, subset, x))
446 return [r for r in s if r not in ps]
463 return [r for r in s if r not in ps]
447
464
448 def roots(repo, subset, x):
465 def roots(repo, subset, x):
449 s = getset(repo, subset, x)
466 s = getset(repo, subset, x)
450 cs = set(children(repo, subset, x))
467 cs = set(children(repo, subset, x))
451 return [r for r in s if r not in cs]
468 return [r for r in s if r not in cs]
452
469
453 def outgoing(repo, subset, x):
470 def outgoing(repo, subset, x):
454 import hg # avoid start-up nasties
471 import hg # avoid start-up nasties
455 l = getargs(x, 0, 1, _("outgoing wants a repository path"))
472 l = getargs(x, 0, 1, _("outgoing wants a repository path"))
456 dest = l and getstring(l[0], _("outgoing wants a repository path")) or ''
473 dest = l and getstring(l[0], _("outgoing wants a repository path")) or ''
457 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
474 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
458 dest, branches = hg.parseurl(dest)
475 dest, branches = hg.parseurl(dest)
459 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
476 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
460 if revs:
477 if revs:
461 revs = [repo.lookup(rev) for rev in revs]
478 revs = [repo.lookup(rev) for rev in revs]
462 other = hg.repository(hg.remoteui(repo, {}), dest)
479 other = hg.repository(hg.remoteui(repo, {}), dest)
463 repo.ui.pushbuffer()
480 repo.ui.pushbuffer()
464 o = discovery.findoutgoing(repo, other)
481 o = discovery.findoutgoing(repo, other)
465 repo.ui.popbuffer()
482 repo.ui.popbuffer()
466 cl = repo.changelog
483 cl = repo.changelog
467 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
484 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, revs)[0]])
468 return [r for r in subset if r in o]
485 return [r for r in subset if r in o]
469
486
470 def tag(repo, subset, x):
487 def tag(repo, subset, x):
471 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
488 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
472 cl = repo.changelog
489 cl = repo.changelog
473 if args:
490 if args:
474 tn = getstring(args[0],
491 tn = getstring(args[0],
475 _('the argument to tag must be a string'))
492 _('the argument to tag must be a string'))
476 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
493 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
477 else:
494 else:
478 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
495 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
479 return [r for r in subset if r in s]
496 return [r for r in subset if r in s]
480
497
481 symbols = {
498 symbols = {
482 "adds": adds,
499 "adds": adds,
483 "all": getall,
500 "all": getall,
484 "ancestor": ancestor,
501 "ancestor": ancestor,
485 "ancestors": ancestors,
502 "ancestors": ancestors,
486 "author": author,
503 "author": author,
487 "branch": branch,
504 "branch": branch,
488 "children": children,
505 "children": children,
489 "closed": closed,
506 "closed": closed,
490 "contains": contains,
507 "contains": contains,
491 "date": date,
508 "date": date,
492 "descendants": descendants,
509 "descendants": descendants,
493 "file": hasfile,
510 "file": hasfile,
494 "follow": follow,
511 "follow": follow,
495 "grep": grep,
512 "grep": grep,
496 "head": head,
513 "head": head,
497 "heads": heads,
514 "heads": heads,
498 "keyword": keyword,
515 "keyword": keyword,
499 "limit": limit,
516 "limit": limit,
500 "max": maxrev,
517 "max": maxrev,
501 "min": minrev,
518 "min": minrev,
502 "merge": merge,
519 "merge": merge,
503 "modifies": modifies,
520 "modifies": modifies,
521 "id": node,
504 "outgoing": outgoing,
522 "outgoing": outgoing,
505 "p1": p1,
523 "p1": p1,
506 "p2": p2,
524 "p2": p2,
507 "parents": parents,
525 "parents": parents,
508 "present": present,
526 "present": present,
509 "removes": removes,
527 "removes": removes,
510 "reverse": reverse,
528 "reverse": reverse,
529 "rev": rev,
511 "roots": roots,
530 "roots": roots,
512 "sort": sort,
531 "sort": sort,
513 "tag": tag,
532 "tag": tag,
514 "tagged": tag,
533 "tagged": tag,
515 "user": author,
534 "user": author,
516 }
535 }
517
536
518 methods = {
537 methods = {
519 "range": rangeset,
538 "range": rangeset,
520 "string": stringset,
539 "string": stringset,
521 "symbol": symbolset,
540 "symbol": symbolset,
522 "and": andset,
541 "and": andset,
523 "or": orset,
542 "or": orset,
524 "not": notset,
543 "not": notset,
525 "list": listset,
544 "list": listset,
526 "func": func,
545 "func": func,
527 }
546 }
528
547
529 def optimize(x, small):
548 def optimize(x, small):
530 if x == None:
549 if x == None:
531 return 0, x
550 return 0, x
532
551
533 smallbonus = 1
552 smallbonus = 1
534 if small:
553 if small:
535 smallbonus = .5
554 smallbonus = .5
536
555
537 op = x[0]
556 op = x[0]
538 if op == 'minus':
557 if op == 'minus':
539 return optimize(('and', x[1], ('not', x[2])), small)
558 return optimize(('and', x[1], ('not', x[2])), small)
540 elif op == 'dagrange':
559 elif op == 'dagrange':
541 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
560 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
542 ('func', ('symbol', 'ancestors'), x[2])), small)
561 ('func', ('symbol', 'ancestors'), x[2])), small)
543 elif op == 'dagrangepre':
562 elif op == 'dagrangepre':
544 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
563 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
545 elif op == 'dagrangepost':
564 elif op == 'dagrangepost':
546 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
565 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
547 elif op == 'rangepre':
566 elif op == 'rangepre':
548 return optimize(('range', ('string', '0'), x[1]), small)
567 return optimize(('range', ('string', '0'), x[1]), small)
549 elif op == 'rangepost':
568 elif op == 'rangepost':
550 return optimize(('range', x[1], ('string', 'tip')), small)
569 return optimize(('range', x[1], ('string', 'tip')), small)
551 elif op == 'negate':
570 elif op == 'negate':
552 return optimize(('string',
571 return optimize(('string',
553 '-' + getstring(x[1], _("can't negate that"))), small)
572 '-' + getstring(x[1], _("can't negate that"))), small)
554 elif op in 'string symbol negate':
573 elif op in 'string symbol negate':
555 return smallbonus, x # single revisions are small
574 return smallbonus, x # single revisions are small
556 elif op == 'and' or op == 'dagrange':
575 elif op == 'and' or op == 'dagrange':
557 wa, ta = optimize(x[1], True)
576 wa, ta = optimize(x[1], True)
558 wb, tb = optimize(x[2], True)
577 wb, tb = optimize(x[2], True)
559 w = min(wa, wb)
578 w = min(wa, wb)
560 if wa > wb:
579 if wa > wb:
561 return w, (op, tb, ta)
580 return w, (op, tb, ta)
562 return w, (op, ta, tb)
581 return w, (op, ta, tb)
563 elif op == 'or':
582 elif op == 'or':
564 wa, ta = optimize(x[1], False)
583 wa, ta = optimize(x[1], False)
565 wb, tb = optimize(x[2], False)
584 wb, tb = optimize(x[2], False)
566 if wb < wa:
585 if wb < wa:
567 wb, wa = wa, wb
586 wb, wa = wa, wb
568 return max(wa, wb), (op, ta, tb)
587 return max(wa, wb), (op, ta, tb)
569 elif op == 'not':
588 elif op == 'not':
570 o = optimize(x[1], not small)
589 o = optimize(x[1], not small)
571 return o[0], (op, o[1])
590 return o[0], (op, o[1])
572 elif op == 'group':
591 elif op == 'group':
573 return optimize(x[1], small)
592 return optimize(x[1], small)
574 elif op in 'range list':
593 elif op in 'range list':
575 wa, ta = optimize(x[1], small)
594 wa, ta = optimize(x[1], small)
576 wb, tb = optimize(x[2], small)
595 wb, tb = optimize(x[2], small)
577 return wa + wb, (op, ta, tb)
596 return wa + wb, (op, ta, tb)
578 elif op == 'func':
597 elif op == 'func':
579 f = getstring(x[1], _("not a symbol"))
598 f = getstring(x[1], _("not a symbol"))
580 wa, ta = optimize(x[2], small)
599 wa, ta = optimize(x[2], small)
581 if f in "grep date user author keyword branch file outgoing":
600 if f in "grep date user author keyword branch file outgoing":
582 w = 10 # slow
601 w = 10 # slow
583 elif f in "modifies adds removes":
602 elif f in "modifies adds removes":
584 w = 30 # slower
603 w = 30 # slower
585 elif f == "contains":
604 elif f == "contains":
586 w = 100 # very slow
605 w = 100 # very slow
587 elif f == "ancestor":
606 elif f == "ancestor":
588 w = 1 * smallbonus
607 w = 1 * smallbonus
589 elif f == "reverse limit":
608 elif f == "reverse limit":
590 w = 0
609 w = 0
591 elif f in "sort":
610 elif f in "sort":
592 w = 10 # assume most sorts look at changelog
611 w = 10 # assume most sorts look at changelog
593 else:
612 else:
594 w = 1
613 w = 1
595 return w + wa, (op, x[1], ta)
614 return w + wa, (op, x[1], ta)
596 return 1, x
615 return 1, x
597
616
598 parse = parser.parser(tokenize, elements).parse
617 parse = parser.parser(tokenize, elements).parse
599
618
600 def match(spec):
619 def match(spec):
601 if not spec:
620 if not spec:
602 raise error.ParseError(_("empty query"))
621 raise error.ParseError(_("empty query"))
603 tree = parse(spec)
622 tree = parse(spec)
604 weight, tree = optimize(tree, True)
623 weight, tree = optimize(tree, True)
605 def mfunc(repo, subset):
624 def mfunc(repo, subset):
606 return getset(repo, subset, tree)
625 return getset(repo, subset, tree)
607 return mfunc
626 return mfunc
@@ -1,337 +1,341 b''
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3
3
4 $ try() {
4 $ try() {
5 > hg debugrevspec --debug $@
5 > hg debugrevspec --debug $@
6 > }
6 > }
7
7
8 $ log() {
8 $ log() {
9 > hg log --template '{rev}\n' -r "$1"
9 > hg log --template '{rev}\n' -r "$1"
10 > }
10 > }
11
11
12 $ hg init repo
12 $ hg init repo
13 $ cd repo
13 $ cd repo
14
14
15 $ echo a > a
15 $ echo a > a
16 $ hg branch a
16 $ hg branch a
17 marked working directory as branch a
17 marked working directory as branch a
18 $ hg ci -Aqm0
18 $ hg ci -Aqm0
19
19
20 $ echo b > b
20 $ echo b > b
21 $ hg branch b
21 $ hg branch b
22 marked working directory as branch b
22 marked working directory as branch b
23 $ hg ci -Aqm1
23 $ hg ci -Aqm1
24
24
25 $ rm a
25 $ rm a
26 $ hg branch a-b-c-
26 $ hg branch a-b-c-
27 marked working directory as branch a-b-c-
27 marked working directory as branch a-b-c-
28 $ hg ci -Aqm2 -u Bob
28 $ hg ci -Aqm2 -u Bob
29
29
30 $ hg co 1
30 $ hg co 1
31 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 $ hg branch +a+b+c+
32 $ hg branch +a+b+c+
33 marked working directory as branch +a+b+c+
33 marked working directory as branch +a+b+c+
34 $ hg ci -Aqm3
34 $ hg ci -Aqm3
35
35
36 $ hg co 2 # interleave
36 $ hg co 2 # interleave
37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
38 $ echo bb > b
38 $ echo bb > b
39 $ hg branch -- -a-b-c-
39 $ hg branch -- -a-b-c-
40 marked working directory as branch -a-b-c-
40 marked working directory as branch -a-b-c-
41 $ hg ci -Aqm4 -d "May 12 2005"
41 $ hg ci -Aqm4 -d "May 12 2005"
42
42
43 $ hg co 3
43 $ hg co 3
44 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 $ hg branch /a/b/c/
45 $ hg branch /a/b/c/
46 marked working directory as branch /a/b/c/
46 marked working directory as branch /a/b/c/
47 $ hg ci -Aqm"5 bug"
47 $ hg ci -Aqm"5 bug"
48
48
49 $ hg merge 4
49 $ hg merge 4
50 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
50 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
51 (branch merge, don't forget to commit)
51 (branch merge, don't forget to commit)
52 $ hg branch _a_b_c_
52 $ hg branch _a_b_c_
53 marked working directory as branch _a_b_c_
53 marked working directory as branch _a_b_c_
54 $ hg ci -Aqm"6 issue619"
54 $ hg ci -Aqm"6 issue619"
55
55
56 $ hg branch .a.b.c.
56 $ hg branch .a.b.c.
57 marked working directory as branch .a.b.c.
57 marked working directory as branch .a.b.c.
58 $ hg ci -Aqm7
58 $ hg ci -Aqm7
59
59
60 $ hg branch all
60 $ hg branch all
61 marked working directory as branch all
61 marked working directory as branch all
62 $ hg ci --close-branch -Aqm8
62 $ hg ci --close-branch -Aqm8
63 abort: can only close branch heads
63 abort: can only close branch heads
64 [255]
64 [255]
65
65
66 $ hg co 4
66 $ hg co 4
67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 $ hg branch Γ©
68 $ hg branch Γ©
69 marked working directory as branch Γ©
69 marked working directory as branch Γ©
70 $ hg ci -Aqm9
70 $ hg ci -Aqm9
71
71
72 $ hg tag -r6 1.0
72 $ hg tag -r6 1.0
73
73
74 $ hg clone --quiet -U -r 7 . ../remote1
74 $ hg clone --quiet -U -r 7 . ../remote1
75 $ hg clone --quiet -U -r 8 . ../remote2
75 $ hg clone --quiet -U -r 8 . ../remote2
76 $ echo "[paths]" >> .hg/hgrc
76 $ echo "[paths]" >> .hg/hgrc
77 $ echo "default = ../remote1" >> .hg/hgrc
77 $ echo "default = ../remote1" >> .hg/hgrc
78
78
79 names that should work without quoting
79 names that should work without quoting
80
80
81 $ try a
81 $ try a
82 ('symbol', 'a')
82 ('symbol', 'a')
83 0
83 0
84 $ try b-a
84 $ try b-a
85 ('minus', ('symbol', 'b'), ('symbol', 'a'))
85 ('minus', ('symbol', 'b'), ('symbol', 'a'))
86 1
86 1
87 $ try _a_b_c_
87 $ try _a_b_c_
88 ('symbol', '_a_b_c_')
88 ('symbol', '_a_b_c_')
89 6
89 6
90 $ try _a_b_c_-a
90 $ try _a_b_c_-a
91 ('minus', ('symbol', '_a_b_c_'), ('symbol', 'a'))
91 ('minus', ('symbol', '_a_b_c_'), ('symbol', 'a'))
92 6
92 6
93 $ try .a.b.c.
93 $ try .a.b.c.
94 ('symbol', '.a.b.c.')
94 ('symbol', '.a.b.c.')
95 7
95 7
96 $ try .a.b.c.-a
96 $ try .a.b.c.-a
97 ('minus', ('symbol', '.a.b.c.'), ('symbol', 'a'))
97 ('minus', ('symbol', '.a.b.c.'), ('symbol', 'a'))
98 7
98 7
99 $ try -- '-a-b-c-' # complains
99 $ try -- '-a-b-c-' # complains
100 hg: parse error at 7: not a prefix: end
100 hg: parse error at 7: not a prefix: end
101 [255]
101 [255]
102 $ log -a-b-c- # succeeds with fallback
102 $ log -a-b-c- # succeeds with fallback
103 4
103 4
104 $ try -- -a-b-c--a # complains
104 $ try -- -a-b-c--a # complains
105 ('minus', ('minus', ('minus', ('negate', ('symbol', 'a')), ('symbol', 'b')), ('symbol', 'c')), ('negate', ('symbol', 'a')))
105 ('minus', ('minus', ('minus', ('negate', ('symbol', 'a')), ('symbol', 'b')), ('symbol', 'c')), ('negate', ('symbol', 'a')))
106 abort: unknown revision '-a'!
106 abort: unknown revision '-a'!
107 [255]
107 [255]
108 $ try Γ©
108 $ try Γ©
109 ('symbol', '\xc3\xa9')
109 ('symbol', '\xc3\xa9')
110 9
110 9
111
111
112 quoting needed
112 quoting needed
113
113
114 $ try '"-a-b-c-"-a'
114 $ try '"-a-b-c-"-a'
115 ('minus', ('string', '-a-b-c-'), ('symbol', 'a'))
115 ('minus', ('string', '-a-b-c-'), ('symbol', 'a'))
116 4
116 4
117
117
118 $ log '1 or 2'
118 $ log '1 or 2'
119 1
119 1
120 2
120 2
121 $ log '1|2'
121 $ log '1|2'
122 1
122 1
123 2
123 2
124 $ log '1 and 2'
124 $ log '1 and 2'
125 $ log '1&2'
125 $ log '1&2'
126 $ try '1&2|3' # precedence - and is higher
126 $ try '1&2|3' # precedence - and is higher
127 ('or', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
127 ('or', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
128 3
128 3
129 $ try '1|2&3'
129 $ try '1|2&3'
130 ('or', ('symbol', '1'), ('and', ('symbol', '2'), ('symbol', '3')))
130 ('or', ('symbol', '1'), ('and', ('symbol', '2'), ('symbol', '3')))
131 1
131 1
132 $ try '1&2&3' # associativity
132 $ try '1&2&3' # associativity
133 ('and', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
133 ('and', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
134 $ try '1|(2|3)'
134 $ try '1|(2|3)'
135 ('or', ('symbol', '1'), ('group', ('or', ('symbol', '2'), ('symbol', '3'))))
135 ('or', ('symbol', '1'), ('group', ('or', ('symbol', '2'), ('symbol', '3'))))
136 1
136 1
137 2
137 2
138 3
138 3
139 $ log '1.0' # tag
139 $ log '1.0' # tag
140 6
140 6
141 $ log 'a' # branch
141 $ log 'a' # branch
142 0
142 0
143 $ log '2785f51ee'
143 $ log '2785f51ee'
144 0
144 0
145 $ log 'date(2005)'
145 $ log 'date(2005)'
146 4
146 4
147 $ log 'date(this is a test)'
147 $ log 'date(this is a test)'
148 hg: parse error at 10: unexpected token: symbol
148 hg: parse error at 10: unexpected token: symbol
149 [255]
149 [255]
150 $ log 'date()'
150 $ log 'date()'
151 hg: parse error: date wants a string
151 hg: parse error: date wants a string
152 [255]
152 [255]
153 $ log 'date'
153 $ log 'date'
154 hg: parse error: can't use date here
154 hg: parse error: can't use date here
155 [255]
155 [255]
156 $ log 'date('
156 $ log 'date('
157 hg: parse error at 5: not a prefix: end
157 hg: parse error at 5: not a prefix: end
158 [255]
158 [255]
159 $ log 'date(tip)'
159 $ log 'date(tip)'
160 abort: invalid date: 'tip'
160 abort: invalid date: 'tip'
161 [255]
161 [255]
162 $ log '"date"'
162 $ log '"date"'
163 abort: unknown revision 'date'!
163 abort: unknown revision 'date'!
164 [255]
164 [255]
165 $ log 'date(2005) and 1::'
165 $ log 'date(2005) and 1::'
166 4
166 4
167
167
168 $ log 'ancestor(1)'
168 $ log 'ancestor(1)'
169 hg: parse error: ancestor wants two arguments
169 hg: parse error: ancestor wants two arguments
170 [255]
170 [255]
171 $ log 'ancestor(4,5)'
171 $ log 'ancestor(4,5)'
172 1
172 1
173 $ log 'ancestor(4,5) and 4'
173 $ log 'ancestor(4,5) and 4'
174 $ log 'ancestors(5)'
174 $ log 'ancestors(5)'
175 0
175 0
176 1
176 1
177 3
177 3
178 5
178 5
179 $ log 'author(bob)'
179 $ log 'author(bob)'
180 2
180 2
181 $ log 'branch(Γ©)'
181 $ log 'branch(Γ©)'
182 8
182 8
183 9
183 9
184 $ log 'children(ancestor(4,5))'
184 $ log 'children(ancestor(4,5))'
185 2
185 2
186 3
186 3
187 $ log 'closed()'
187 $ log 'closed()'
188 $ log 'contains(a)'
188 $ log 'contains(a)'
189 0
189 0
190 1
190 1
191 3
191 3
192 5
192 5
193 $ log 'descendants(2 or 3)'
193 $ log 'descendants(2 or 3)'
194 2
194 2
195 3
195 3
196 4
196 4
197 5
197 5
198 6
198 6
199 7
199 7
200 8
200 8
201 9
201 9
202 $ log 'file(b)'
202 $ log 'file(b)'
203 1
203 1
204 4
204 4
205 $ log 'follow()'
205 $ log 'follow()'
206 0
206 0
207 1
207 1
208 2
208 2
209 4
209 4
210 8
210 8
211 9
211 9
212 $ log 'grep("issue\d+")'
212 $ log 'grep("issue\d+")'
213 6
213 6
214 $ try 'grep("(")' # invalid regular expression
214 $ try 'grep("(")' # invalid regular expression
215 ('func', ('symbol', 'grep'), ('string', '('))
215 ('func', ('symbol', 'grep'), ('string', '('))
216 hg: parse error: invalid match pattern: unbalanced parenthesis
216 hg: parse error: invalid match pattern: unbalanced parenthesis
217 [255]
217 [255]
218 $ try 'grep("\bissue\d+")'
218 $ try 'grep("\bissue\d+")'
219 ('func', ('symbol', 'grep'), ('string', '\x08issue\\d+'))
219 ('func', ('symbol', 'grep'), ('string', '\x08issue\\d+'))
220 $ try 'grep(r"\bissue\d+")'
220 $ try 'grep(r"\bissue\d+")'
221 ('func', ('symbol', 'grep'), ('string', '\\bissue\\d+'))
221 ('func', ('symbol', 'grep'), ('string', '\\bissue\\d+'))
222 6
222 6
223 $ try 'grep(r"\")'
223 $ try 'grep(r"\")'
224 hg: parse error at 7: unterminated string
224 hg: parse error at 7: unterminated string
225 [255]
225 [255]
226 $ log 'head()'
226 $ log 'head()'
227 0
227 0
228 1
228 1
229 2
229 2
230 3
230 3
231 4
231 4
232 5
232 5
233 6
233 6
234 7
234 7
235 9
235 9
236 $ log 'heads(6::)'
236 $ log 'heads(6::)'
237 7
237 7
238 $ log 'keyword(issue)'
238 $ log 'keyword(issue)'
239 6
239 6
240 $ log 'limit(head(), 1)'
240 $ log 'limit(head(), 1)'
241 0
241 0
242 $ log 'max(contains(a))'
242 $ log 'max(contains(a))'
243 5
243 5
244 $ log 'min(contains(a))'
244 $ log 'min(contains(a))'
245 0
245 0
246 $ log 'merge()'
246 $ log 'merge()'
247 6
247 6
248 $ log 'modifies(b)'
248 $ log 'modifies(b)'
249 4
249 4
250 $ log 'id(5)'
251 2
250 $ log 'outgoing()'
252 $ log 'outgoing()'
251 8
253 8
252 9
254 9
253 $ log 'outgoing("../remote1")'
255 $ log 'outgoing("../remote1")'
254 8
256 8
255 9
257 9
256 $ log 'outgoing("../remote2")'
258 $ log 'outgoing("../remote2")'
257 3
259 3
258 5
260 5
259 6
261 6
260 7
262 7
261 9
263 9
262 $ log 'p1(merge())'
264 $ log 'p1(merge())'
263 5
265 5
264 $ log 'p2(merge())'
266 $ log 'p2(merge())'
265 4
267 4
266 $ log 'parents(merge())'
268 $ log 'parents(merge())'
267 4
269 4
268 5
270 5
269 $ log 'removes(a)'
271 $ log 'removes(a)'
270 2
272 2
271 6
273 6
272 $ log 'roots(all())'
274 $ log 'roots(all())'
273 0
275 0
274 $ log 'reverse(2 or 3 or 4 or 5)'
276 $ log 'reverse(2 or 3 or 4 or 5)'
275 5
277 5
276 4
278 4
277 3
279 3
278 2
280 2
281 $ log 'rev(5)'
282 5
279 $ log 'sort(limit(reverse(all()), 3))'
283 $ log 'sort(limit(reverse(all()), 3))'
280 7
284 7
281 8
285 8
282 9
286 9
283 $ log 'sort(2 or 3 or 4 or 5, date)'
287 $ log 'sort(2 or 3 or 4 or 5, date)'
284 2
288 2
285 3
289 3
286 5
290 5
287 4
291 4
288 $ log 'tagged()'
292 $ log 'tagged()'
289 6
293 6
290 $ log 'tag()'
294 $ log 'tag()'
291 6
295 6
292 $ log 'tag(1.0)'
296 $ log 'tag(1.0)'
293 6
297 6
294 $ log 'tag(tip)'
298 $ log 'tag(tip)'
295 9
299 9
296 $ log 'user(bob)'
300 $ log 'user(bob)'
297 2
301 2
298
302
299 $ log '4::8'
303 $ log '4::8'
300 4
304 4
301 8
305 8
302 $ log '4:8'
306 $ log '4:8'
303 4
307 4
304 5
308 5
305 6
309 6
306 7
310 7
307 8
311 8
308
312
309 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
313 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
310 4
314 4
311 2
315 2
312 5
316 5
313
317
314 $ log 'not 0 and 0:2'
318 $ log 'not 0 and 0:2'
315 1
319 1
316 2
320 2
317 $ log 'not 1 and 0:2'
321 $ log 'not 1 and 0:2'
318 0
322 0
319 2
323 2
320 $ log 'not 2 and 0:2'
324 $ log 'not 2 and 0:2'
321 0
325 0
322 1
326 1
323 $ log '(1 and 2)::'
327 $ log '(1 and 2)::'
324 $ log '(1 and 2):'
328 $ log '(1 and 2):'
325 $ log '(1 and 2):3'
329 $ log '(1 and 2):3'
326 $ log 'sort(head(), -rev)'
330 $ log 'sort(head(), -rev)'
327 9
331 9
328 7
332 7
329 6
333 6
330 5
334 5
331 4
335 4
332 3
336 3
333 2
337 2
334 1
338 1
335 0
339 0
336 $ log '4::8 - 8'
340 $ log '4::8 - 8'
337 4
341 4
General Comments 0
You need to be logged in to leave comments. Login now