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