##// END OF EJS Templates
revbranchcache: return uncached branchinfo for nullrev (issue4683)...
Yuya Nishihara -
r25266:38117278 stable
parent child Browse files
Show More
@@ -1,439 +1,443 b''
1 1 # branchmap.py - logic to computes, maintain and stores branchmap for local repo
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from node import bin, hex, nullid, nullrev
9 9 import encoding
10 10 import scmutil
11 11 import util
12 12 import time
13 13 from array import array
14 14 from struct import calcsize, pack, unpack
15 15
16 16 def _filename(repo):
17 17 """name of a branchcache file for a given repo or repoview"""
18 18 filename = "cache/branch2"
19 19 if repo.filtername:
20 20 filename = '%s-%s' % (filename, repo.filtername)
21 21 return filename
22 22
23 23 def read(repo):
24 24 try:
25 25 f = repo.vfs(_filename(repo))
26 26 lines = f.read().split('\n')
27 27 f.close()
28 28 except (IOError, OSError):
29 29 return None
30 30
31 31 try:
32 32 cachekey = lines.pop(0).split(" ", 2)
33 33 last, lrev = cachekey[:2]
34 34 last, lrev = bin(last), int(lrev)
35 35 filteredhash = None
36 36 if len(cachekey) > 2:
37 37 filteredhash = bin(cachekey[2])
38 38 partial = branchcache(tipnode=last, tiprev=lrev,
39 39 filteredhash=filteredhash)
40 40 if not partial.validfor(repo):
41 41 # invalidate the cache
42 42 raise ValueError('tip differs')
43 43 for l in lines:
44 44 if not l:
45 45 continue
46 46 node, state, label = l.split(" ", 2)
47 47 if state not in 'oc':
48 48 raise ValueError('invalid branch state')
49 49 label = encoding.tolocal(label.strip())
50 50 if not node in repo:
51 51 raise ValueError('node %s does not exist' % node)
52 52 node = bin(node)
53 53 partial.setdefault(label, []).append(node)
54 54 if state == 'c':
55 55 partial._closednodes.add(node)
56 56 except KeyboardInterrupt:
57 57 raise
58 58 except Exception, inst:
59 59 if repo.ui.debugflag:
60 60 msg = 'invalid branchheads cache'
61 61 if repo.filtername is not None:
62 62 msg += ' (%s)' % repo.filtername
63 63 msg += ': %s\n'
64 64 repo.ui.debug(msg % inst)
65 65 partial = None
66 66 return partial
67 67
68 68 ### Nearest subset relation
69 69 # Nearest subset of filter X is a filter Y so that:
70 70 # * Y is included in X,
71 71 # * X - Y is as small as possible.
72 72 # This create and ordering used for branchmap purpose.
73 73 # the ordering may be partial
74 74 subsettable = {None: 'visible',
75 75 'visible': 'served',
76 76 'served': 'immutable',
77 77 'immutable': 'base'}
78 78
79 79 def updatecache(repo):
80 80 cl = repo.changelog
81 81 filtername = repo.filtername
82 82 partial = repo._branchcaches.get(filtername)
83 83
84 84 revs = []
85 85 if partial is None or not partial.validfor(repo):
86 86 partial = read(repo)
87 87 if partial is None:
88 88 subsetname = subsettable.get(filtername)
89 89 if subsetname is None:
90 90 partial = branchcache()
91 91 else:
92 92 subset = repo.filtered(subsetname)
93 93 partial = subset.branchmap().copy()
94 94 extrarevs = subset.changelog.filteredrevs - cl.filteredrevs
95 95 revs.extend(r for r in extrarevs if r <= partial.tiprev)
96 96 revs.extend(cl.revs(start=partial.tiprev + 1))
97 97 if revs:
98 98 partial.update(repo, revs)
99 99 partial.write(repo)
100 100
101 101 assert partial.validfor(repo), filtername
102 102 repo._branchcaches[repo.filtername] = partial
103 103
104 104 class branchcache(dict):
105 105 """A dict like object that hold branches heads cache.
106 106
107 107 This cache is used to avoid costly computations to determine all the
108 108 branch heads of a repo.
109 109
110 110 The cache is serialized on disk in the following format:
111 111
112 112 <tip hex node> <tip rev number> [optional filtered repo hex hash]
113 113 <branch head hex node> <open/closed state> <branch name>
114 114 <branch head hex node> <open/closed state> <branch name>
115 115 ...
116 116
117 117 The first line is used to check if the cache is still valid. If the
118 118 branch cache is for a filtered repo view, an optional third hash is
119 119 included that hashes the hashes of all filtered revisions.
120 120
121 121 The open/closed state is represented by a single letter 'o' or 'c'.
122 122 This field can be used to avoid changelog reads when determining if a
123 123 branch head closes a branch or not.
124 124 """
125 125
126 126 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
127 127 filteredhash=None, closednodes=None):
128 128 super(branchcache, self).__init__(entries)
129 129 self.tipnode = tipnode
130 130 self.tiprev = tiprev
131 131 self.filteredhash = filteredhash
132 132 # closednodes is a set of nodes that close their branch. If the branch
133 133 # cache has been updated, it may contain nodes that are no longer
134 134 # heads.
135 135 if closednodes is None:
136 136 self._closednodes = set()
137 137 else:
138 138 self._closednodes = closednodes
139 139
140 140 def validfor(self, repo):
141 141 """Is the cache content valid regarding a repo
142 142
143 143 - False when cached tipnode is unknown or if we detect a strip.
144 144 - True when cache is up to date or a subset of current repo."""
145 145 try:
146 146 return ((self.tipnode == repo.changelog.node(self.tiprev))
147 147 and (self.filteredhash == \
148 148 scmutil.filteredhash(repo, self.tiprev)))
149 149 except IndexError:
150 150 return False
151 151
152 152 def _branchtip(self, heads):
153 153 '''Return tuple with last open head in heads and false,
154 154 otherwise return last closed head and true.'''
155 155 tip = heads[-1]
156 156 closed = True
157 157 for h in reversed(heads):
158 158 if h not in self._closednodes:
159 159 tip = h
160 160 closed = False
161 161 break
162 162 return tip, closed
163 163
164 164 def branchtip(self, branch):
165 165 '''Return the tipmost open head on branch head, otherwise return the
166 166 tipmost closed head on branch.
167 167 Raise KeyError for unknown branch.'''
168 168 return self._branchtip(self[branch])[0]
169 169
170 170 def branchheads(self, branch, closed=False):
171 171 heads = self[branch]
172 172 if not closed:
173 173 heads = [h for h in heads if h not in self._closednodes]
174 174 return heads
175 175
176 176 def iterbranches(self):
177 177 for bn, heads in self.iteritems():
178 178 yield (bn, heads) + self._branchtip(heads)
179 179
180 180 def copy(self):
181 181 """return an deep copy of the branchcache object"""
182 182 return branchcache(self, self.tipnode, self.tiprev, self.filteredhash,
183 183 self._closednodes)
184 184
185 185 def write(self, repo):
186 186 try:
187 187 f = repo.vfs(_filename(repo), "w", atomictemp=True)
188 188 cachekey = [hex(self.tipnode), str(self.tiprev)]
189 189 if self.filteredhash is not None:
190 190 cachekey.append(hex(self.filteredhash))
191 191 f.write(" ".join(cachekey) + '\n')
192 192 nodecount = 0
193 193 for label, nodes in sorted(self.iteritems()):
194 194 for node in nodes:
195 195 nodecount += 1
196 196 if node in self._closednodes:
197 197 state = 'c'
198 198 else:
199 199 state = 'o'
200 200 f.write("%s %s %s\n" % (hex(node), state,
201 201 encoding.fromlocal(label)))
202 202 f.close()
203 203 repo.ui.log('branchcache',
204 204 'wrote %s branch cache with %d labels and %d nodes\n',
205 205 repo.filtername, len(self), nodecount)
206 206 except (IOError, OSError, util.Abort), inst:
207 207 repo.ui.debug("couldn't write branch cache: %s\n" % inst)
208 208 # Abort may be raise by read only opener
209 209 pass
210 210
211 211 def update(self, repo, revgen):
212 212 """Given a branchhead cache, self, that may have extra nodes or be
213 213 missing heads, and a generator of nodes that are strictly a superset of
214 214 heads missing, this function updates self to be correct.
215 215 """
216 216 starttime = time.time()
217 217 cl = repo.changelog
218 218 # collect new branch entries
219 219 newbranches = {}
220 220 getbranchinfo = repo.revbranchcache().branchinfo
221 221 for r in revgen:
222 222 branch, closesbranch = getbranchinfo(r)
223 223 newbranches.setdefault(branch, []).append(r)
224 224 if closesbranch:
225 225 self._closednodes.add(cl.node(r))
226 226
227 227 # fetch current topological heads to speed up filtering
228 228 topoheads = set(cl.headrevs())
229 229
230 230 # if older branchheads are reachable from new ones, they aren't
231 231 # really branchheads. Note checking parents is insufficient:
232 232 # 1 (branch a) -> 2 (branch b) -> 3 (branch a)
233 233 for branch, newheadrevs in newbranches.iteritems():
234 234 bheads = self.setdefault(branch, [])
235 235 bheadset = set(cl.rev(node) for node in bheads)
236 236
237 237 # This have been tested True on all internal usage of this function.
238 238 # run it again in case of doubt
239 239 # assert not (set(bheadrevs) & set(newheadrevs))
240 240 newheadrevs.sort()
241 241 bheadset.update(newheadrevs)
242 242
243 243 # This prunes out two kinds of heads - heads that are superseded by
244 244 # a head in newheadrevs, and newheadrevs that are not heads because
245 245 # an existing head is their descendant.
246 246 uncertain = bheadset - topoheads
247 247 if uncertain:
248 248 floorrev = min(uncertain)
249 249 ancestors = set(cl.ancestors(newheadrevs, floorrev))
250 250 bheadset -= ancestors
251 251 bheadrevs = sorted(bheadset)
252 252 self[branch] = [cl.node(rev) for rev in bheadrevs]
253 253 tiprev = bheadrevs[-1]
254 254 if tiprev > self.tiprev:
255 255 self.tipnode = cl.node(tiprev)
256 256 self.tiprev = tiprev
257 257
258 258 if not self.validfor(repo):
259 259 # cache key are not valid anymore
260 260 self.tipnode = nullid
261 261 self.tiprev = nullrev
262 262 for heads in self.values():
263 263 tiprev = max(cl.rev(node) for node in heads)
264 264 if tiprev > self.tiprev:
265 265 self.tipnode = cl.node(tiprev)
266 266 self.tiprev = tiprev
267 267 self.filteredhash = scmutil.filteredhash(repo, self.tiprev)
268 268
269 269 duration = time.time() - starttime
270 270 repo.ui.log('branchcache', 'updated %s branch cache in %.4f seconds\n',
271 271 repo.filtername, duration)
272 272
273 273 # Revision branch info cache
274 274
275 275 _rbcversion = '-v1'
276 276 _rbcnames = 'cache/rbc-names' + _rbcversion
277 277 _rbcrevs = 'cache/rbc-revs' + _rbcversion
278 278 # [4 byte hash prefix][4 byte branch name number with sign bit indicating open]
279 279 _rbcrecfmt = '>4sI'
280 280 _rbcrecsize = calcsize(_rbcrecfmt)
281 281 _rbcnodelen = 4
282 282 _rbcbranchidxmask = 0x7fffffff
283 283 _rbccloseflag = 0x80000000
284 284
285 285 class revbranchcache(object):
286 286 """Persistent cache, mapping from revision number to branch name and close.
287 287 This is a low level cache, independent of filtering.
288 288
289 289 Branch names are stored in rbc-names in internal encoding separated by 0.
290 290 rbc-names is append-only, and each branch name is only stored once and will
291 291 thus have a unique index.
292 292
293 293 The branch info for each revision is stored in rbc-revs as constant size
294 294 records. The whole file is read into memory, but it is only 'parsed' on
295 295 demand. The file is usually append-only but will be truncated if repo
296 296 modification is detected.
297 297 The record for each revision contains the first 4 bytes of the
298 298 corresponding node hash, and the record is only used if it still matches.
299 299 Even a completely trashed rbc-revs fill thus still give the right result
300 300 while converging towards full recovery ... assuming no incorrectly matching
301 301 node hashes.
302 302 The record also contains 4 bytes where 31 bits contains the index of the
303 303 branch and the last bit indicate that it is a branch close commit.
304 304 The usage pattern for rbc-revs is thus somewhat similar to 00changelog.i
305 305 and will grow with it but be 1/8th of its size.
306 306 """
307 307
308 308 def __init__(self, repo, readonly=True):
309 309 assert repo.filtername is None
310 310 self._repo = repo
311 311 self._names = [] # branch names in local encoding with static index
312 312 self._rbcrevs = array('c') # structs of type _rbcrecfmt
313 313 self._rbcsnameslen = 0
314 314 try:
315 315 bndata = repo.vfs.read(_rbcnames)
316 316 self._rbcsnameslen = len(bndata) # for verification before writing
317 317 self._names = [encoding.tolocal(bn) for bn in bndata.split('\0')]
318 318 except (IOError, OSError), inst:
319 319 if readonly:
320 320 # don't try to use cache - fall back to the slow path
321 321 self.branchinfo = self._branchinfo
322 322
323 323 if self._names:
324 324 try:
325 325 data = repo.vfs.read(_rbcrevs)
326 326 self._rbcrevs.fromstring(data)
327 327 except (IOError, OSError), inst:
328 328 repo.ui.debug("couldn't read revision branch cache: %s\n" %
329 329 inst)
330 330 # remember number of good records on disk
331 331 self._rbcrevslen = min(len(self._rbcrevs) // _rbcrecsize,
332 332 len(repo.changelog))
333 333 if self._rbcrevslen == 0:
334 334 self._names = []
335 335 self._rbcnamescount = len(self._names) # number of good names on disk
336 336 self._namesreverse = dict((b, r) for r, b in enumerate(self._names))
337 337
338 338 def branchinfo(self, rev):
339 339 """Return branch name and close flag for rev, using and updating
340 340 persistent cache."""
341 341 changelog = self._repo.changelog
342 342 rbcrevidx = rev * _rbcrecsize
343 343
344 # avoid negative index, changelog.read(nullrev) is fast without cache
345 if rev == nullrev:
346 return changelog.branchinfo(rev)
347
344 348 # if requested rev is missing, add and populate all missing revs
345 349 if len(self._rbcrevs) < rbcrevidx + _rbcrecsize:
346 350 self._rbcrevs.extend('\0' * (len(changelog) * _rbcrecsize -
347 351 len(self._rbcrevs)))
348 352
349 353 # fast path: extract data from cache, use it if node is matching
350 354 reponode = changelog.node(rev)[:_rbcnodelen]
351 355 cachenode, branchidx = unpack(
352 356 _rbcrecfmt, buffer(self._rbcrevs, rbcrevidx, _rbcrecsize))
353 357 close = bool(branchidx & _rbccloseflag)
354 358 if close:
355 359 branchidx &= _rbcbranchidxmask
356 360 if cachenode == '\0\0\0\0':
357 361 pass
358 362 elif cachenode == reponode:
359 363 return self._names[branchidx], close
360 364 else:
361 365 # rev/node map has changed, invalidate the cache from here up
362 366 truncate = rbcrevidx + _rbcrecsize
363 367 del self._rbcrevs[truncate:]
364 368 self._rbcrevslen = min(self._rbcrevslen, truncate)
365 369
366 370 # fall back to slow path and make sure it will be written to disk
367 371 return self._branchinfo(rev)
368 372
369 373 def _branchinfo(self, rev):
370 374 """Retrieve branch info from changelog and update _rbcrevs"""
371 375 changelog = self._repo.changelog
372 376 b, close = changelog.branchinfo(rev)
373 377 if b in self._namesreverse:
374 378 branchidx = self._namesreverse[b]
375 379 else:
376 380 branchidx = len(self._names)
377 381 self._names.append(b)
378 382 self._namesreverse[b] = branchidx
379 383 reponode = changelog.node(rev)
380 384 if close:
381 385 branchidx |= _rbccloseflag
382 386 self._setcachedata(rev, reponode, branchidx)
383 387 return b, close
384 388
385 389 def _setcachedata(self, rev, node, branchidx):
386 390 """Writes the node's branch data to the in-memory cache data."""
387 391 rbcrevidx = rev * _rbcrecsize
388 392 rec = array('c')
389 393 rec.fromstring(pack(_rbcrecfmt, node, branchidx))
390 394 self._rbcrevs[rbcrevidx:rbcrevidx + _rbcrecsize] = rec
391 395 self._rbcrevslen = min(self._rbcrevslen, rev)
392 396
393 397 tr = self._repo.currenttransaction()
394 398 if tr:
395 399 tr.addfinalize('write-revbranchcache', self.write)
396 400
397 401 def write(self, tr=None):
398 402 """Save branch cache if it is dirty."""
399 403 repo = self._repo
400 404 if self._rbcnamescount < len(self._names):
401 405 try:
402 406 if self._rbcnamescount != 0:
403 407 f = repo.vfs.open(_rbcnames, 'ab')
404 408 if f.tell() == self._rbcsnameslen:
405 409 f.write('\0')
406 410 else:
407 411 f.close()
408 412 repo.ui.debug("%s changed - rewriting it\n" % _rbcnames)
409 413 self._rbcnamescount = 0
410 414 self._rbcrevslen = 0
411 415 if self._rbcnamescount == 0:
412 416 f = repo.vfs.open(_rbcnames, 'wb')
413 417 f.write('\0'.join(encoding.fromlocal(b)
414 418 for b in self._names[self._rbcnamescount:]))
415 419 self._rbcsnameslen = f.tell()
416 420 f.close()
417 421 except (IOError, OSError, util.Abort), inst:
418 422 repo.ui.debug("couldn't write revision branch cache names: "
419 423 "%s\n" % inst)
420 424 return
421 425 self._rbcnamescount = len(self._names)
422 426
423 427 start = self._rbcrevslen * _rbcrecsize
424 428 if start != len(self._rbcrevs):
425 429 revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
426 430 try:
427 431 f = repo.vfs.open(_rbcrevs, 'ab')
428 432 if f.tell() != start:
429 433 repo.ui.debug("truncating %s to %s\n" % (_rbcrevs, start))
430 434 f.seek(start)
431 435 f.truncate()
432 436 end = revs * _rbcrecsize
433 437 f.write(self._rbcrevs[start:end])
434 438 f.close()
435 439 except (IOError, OSError, util.Abort), inst:
436 440 repo.ui.debug("couldn't write revision branch cache: %s\n" %
437 441 inst)
438 442 return
439 443 self._rbcrevslen = revs
@@ -1,1674 +1,1682 b''
1 1 $ HGENCODING=utf-8
2 2 $ export HGENCODING
3 3
4 4 $ try() {
5 5 > hg debugrevspec --debug "$@"
6 6 > }
7 7
8 8 $ log() {
9 9 > hg log --template '{rev}\n' -r "$1"
10 10 > }
11 11
12 12 $ hg init repo
13 13 $ cd repo
14 14
15 15 $ echo a > a
16 16 $ hg branch a
17 17 marked working directory as branch a
18 18 (branches are permanent and global, did you want a bookmark?)
19 19 $ hg ci -Aqm0
20 20
21 21 $ echo b > b
22 22 $ hg branch b
23 23 marked working directory as branch b
24 24 (branches are permanent and global, did you want a bookmark?)
25 25 $ hg ci -Aqm1
26 26
27 27 $ rm a
28 28 $ hg branch a-b-c-
29 29 marked working directory as branch a-b-c-
30 30 (branches are permanent and global, did you want a bookmark?)
31 31 $ hg ci -Aqm2 -u Bob
32 32
33 33 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
34 34 2
35 35 $ hg log -r "extra('branch')" --template '{rev}\n'
36 36 0
37 37 1
38 38 2
39 39 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
40 40 0 a
41 41 2 a-b-c-
42 42
43 43 $ hg co 1
44 44 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 45 $ hg branch +a+b+c+
46 46 marked working directory as branch +a+b+c+
47 47 (branches are permanent and global, did you want a bookmark?)
48 48 $ hg ci -Aqm3
49 49
50 50 $ hg co 2 # interleave
51 51 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
52 52 $ echo bb > b
53 53 $ hg branch -- -a-b-c-
54 54 marked working directory as branch -a-b-c-
55 55 (branches are permanent and global, did you want a bookmark?)
56 56 $ hg ci -Aqm4 -d "May 12 2005"
57 57
58 58 $ hg co 3
59 59 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 60 $ hg branch !a/b/c/
61 61 marked working directory as branch !a/b/c/
62 62 (branches are permanent and global, did you want a bookmark?)
63 63 $ hg ci -Aqm"5 bug"
64 64
65 65 $ hg merge 4
66 66 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
67 67 (branch merge, don't forget to commit)
68 68 $ hg branch _a_b_c_
69 69 marked working directory as branch _a_b_c_
70 70 (branches are permanent and global, did you want a bookmark?)
71 71 $ hg ci -Aqm"6 issue619"
72 72
73 73 $ hg branch .a.b.c.
74 74 marked working directory as branch .a.b.c.
75 75 (branches are permanent and global, did you want a bookmark?)
76 76 $ hg ci -Aqm7
77 77
78 78 $ hg branch all
79 79 marked working directory as branch all
80 80 (branches are permanent and global, did you want a bookmark?)
81 81
82 82 $ hg co 4
83 83 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 84 $ hg branch Γ©
85 85 marked working directory as branch \xc3\xa9 (esc)
86 86 (branches are permanent and global, did you want a bookmark?)
87 87 $ hg ci -Aqm9
88 88
89 89 $ hg tag -r6 1.0
90 90 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
91 91
92 92 $ hg clone --quiet -U -r 7 . ../remote1
93 93 $ hg clone --quiet -U -r 8 . ../remote2
94 94 $ echo "[paths]" >> .hg/hgrc
95 95 $ echo "default = ../remote1" >> .hg/hgrc
96 96
97 97 trivial
98 98
99 99 $ try 0:1
100 100 (range
101 101 ('symbol', '0')
102 102 ('symbol', '1'))
103 103 * set:
104 104 <spanset+ 0:1>
105 105 0
106 106 1
107 107 $ try 3::6
108 108 (dagrange
109 109 ('symbol', '3')
110 110 ('symbol', '6'))
111 111 * set:
112 112 <baseset [3, 5, 6]>
113 113 3
114 114 5
115 115 6
116 116 $ try '0|1|2'
117 117 (or
118 118 (or
119 119 ('symbol', '0')
120 120 ('symbol', '1'))
121 121 ('symbol', '2'))
122 122 * set:
123 123 <addset
124 124 <addset
125 125 <baseset [0]>,
126 126 <baseset [1]>>,
127 127 <baseset [2]>>
128 128 0
129 129 1
130 130 2
131 131
132 132 names that should work without quoting
133 133
134 134 $ try a
135 135 ('symbol', 'a')
136 136 * set:
137 137 <baseset [0]>
138 138 0
139 139 $ try b-a
140 140 (minus
141 141 ('symbol', 'b')
142 142 ('symbol', 'a'))
143 143 * set:
144 144 <filteredset
145 145 <baseset [1]>>
146 146 1
147 147 $ try _a_b_c_
148 148 ('symbol', '_a_b_c_')
149 149 * set:
150 150 <baseset [6]>
151 151 6
152 152 $ try _a_b_c_-a
153 153 (minus
154 154 ('symbol', '_a_b_c_')
155 155 ('symbol', 'a'))
156 156 * set:
157 157 <filteredset
158 158 <baseset [6]>>
159 159 6
160 160 $ try .a.b.c.
161 161 ('symbol', '.a.b.c.')
162 162 * set:
163 163 <baseset [7]>
164 164 7
165 165 $ try .a.b.c.-a
166 166 (minus
167 167 ('symbol', '.a.b.c.')
168 168 ('symbol', 'a'))
169 169 * set:
170 170 <filteredset
171 171 <baseset [7]>>
172 172 7
173 173 $ try -- '-a-b-c-' # complains
174 174 hg: parse error at 7: not a prefix: end
175 175 [255]
176 176 $ log -a-b-c- # succeeds with fallback
177 177 4
178 178
179 179 $ try -- -a-b-c--a # complains
180 180 (minus
181 181 (minus
182 182 (minus
183 183 (negate
184 184 ('symbol', 'a'))
185 185 ('symbol', 'b'))
186 186 ('symbol', 'c'))
187 187 (negate
188 188 ('symbol', 'a')))
189 189 abort: unknown revision '-a'!
190 190 [255]
191 191 $ try Γ©
192 192 ('symbol', '\xc3\xa9')
193 193 * set:
194 194 <baseset [9]>
195 195 9
196 196
197 197 no quoting needed
198 198
199 199 $ log ::a-b-c-
200 200 0
201 201 1
202 202 2
203 203
204 204 quoting needed
205 205
206 206 $ try '"-a-b-c-"-a'
207 207 (minus
208 208 ('string', '-a-b-c-')
209 209 ('symbol', 'a'))
210 210 * set:
211 211 <filteredset
212 212 <baseset [4]>>
213 213 4
214 214
215 215 $ log '1 or 2'
216 216 1
217 217 2
218 218 $ log '1|2'
219 219 1
220 220 2
221 221 $ log '1 and 2'
222 222 $ log '1&2'
223 223 $ try '1&2|3' # precedence - and is higher
224 224 (or
225 225 (and
226 226 ('symbol', '1')
227 227 ('symbol', '2'))
228 228 ('symbol', '3'))
229 229 * set:
230 230 <addset
231 231 <baseset []>,
232 232 <baseset [3]>>
233 233 3
234 234 $ try '1|2&3'
235 235 (or
236 236 ('symbol', '1')
237 237 (and
238 238 ('symbol', '2')
239 239 ('symbol', '3')))
240 240 * set:
241 241 <addset
242 242 <baseset [1]>,
243 243 <baseset []>>
244 244 1
245 245 $ try '1&2&3' # associativity
246 246 (and
247 247 (and
248 248 ('symbol', '1')
249 249 ('symbol', '2'))
250 250 ('symbol', '3'))
251 251 * set:
252 252 <baseset []>
253 253 $ try '1|(2|3)'
254 254 (or
255 255 ('symbol', '1')
256 256 (group
257 257 (or
258 258 ('symbol', '2')
259 259 ('symbol', '3'))))
260 260 * set:
261 261 <addset
262 262 <baseset [1]>,
263 263 <addset
264 264 <baseset [2]>,
265 265 <baseset [3]>>>
266 266 1
267 267 2
268 268 3
269 269 $ log '1.0' # tag
270 270 6
271 271 $ log 'a' # branch
272 272 0
273 273 $ log '2785f51ee'
274 274 0
275 275 $ log 'date(2005)'
276 276 4
277 277 $ log 'date(this is a test)'
278 278 hg: parse error at 10: unexpected token: symbol
279 279 [255]
280 280 $ log 'date()'
281 281 hg: parse error: date requires a string
282 282 [255]
283 283 $ log 'date'
284 284 hg: parse error: can't use date here
285 285 [255]
286 286 $ log 'date('
287 287 hg: parse error at 5: not a prefix: end
288 288 [255]
289 289 $ log 'date(tip)'
290 290 abort: invalid date: 'tip'
291 291 [255]
292 292 $ log '"date"'
293 293 abort: unknown revision 'date'!
294 294 [255]
295 295 $ log 'date(2005) and 1::'
296 296 4
297 297
298 298 ancestor can accept 0 or more arguments
299 299
300 300 $ log 'ancestor()'
301 301 $ log 'ancestor(1)'
302 302 1
303 303 $ log 'ancestor(4,5)'
304 304 1
305 305 $ log 'ancestor(4,5) and 4'
306 306 $ log 'ancestor(0,0,1,3)'
307 307 0
308 308 $ log 'ancestor(3,1,5,3,5,1)'
309 309 1
310 310 $ log 'ancestor(0,1,3,5)'
311 311 0
312 312 $ log 'ancestor(1,2,3,4,5)'
313 313 1
314 314 $ log 'ancestors(5)'
315 315 0
316 316 1
317 317 3
318 318 5
319 319 $ log 'ancestor(ancestors(5))'
320 320 0
321 321 $ log 'author(bob)'
322 322 2
323 323 $ log 'author("re:bob|test")'
324 324 0
325 325 1
326 326 2
327 327 3
328 328 4
329 329 5
330 330 6
331 331 7
332 332 8
333 333 9
334 334 $ log 'branch(Γ©)'
335 335 8
336 336 9
337 337 $ log 'branch(a)'
338 338 0
339 339 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
340 340 0 a
341 341 2 a-b-c-
342 342 3 +a+b+c+
343 343 4 -a-b-c-
344 344 5 !a/b/c/
345 345 6 _a_b_c_
346 346 7 .a.b.c.
347 347 $ log 'children(ancestor(4,5))'
348 348 2
349 349 3
350 350 $ log 'closed()'
351 351 $ log 'contains(a)'
352 352 0
353 353 1
354 354 3
355 355 5
356 356 $ log 'contains("../repo/a")'
357 357 0
358 358 1
359 359 3
360 360 5
361 361 $ log 'desc(B)'
362 362 5
363 363 $ log 'descendants(2 or 3)'
364 364 2
365 365 3
366 366 4
367 367 5
368 368 6
369 369 7
370 370 8
371 371 9
372 372 $ log 'file("b*")'
373 373 1
374 374 4
375 375 $ log 'filelog("b")'
376 376 1
377 377 4
378 378 $ log 'filelog("../repo/b")'
379 379 1
380 380 4
381 381 $ log 'follow()'
382 382 0
383 383 1
384 384 2
385 385 4
386 386 8
387 387 9
388 388 $ log 'grep("issue\d+")'
389 389 6
390 390 $ try 'grep("(")' # invalid regular expression
391 391 (func
392 392 ('symbol', 'grep')
393 393 ('string', '('))
394 394 hg: parse error: invalid match pattern: unbalanced parenthesis
395 395 [255]
396 396 $ try 'grep("\bissue\d+")'
397 397 (func
398 398 ('symbol', 'grep')
399 399 ('string', '\x08issue\\d+'))
400 400 * set:
401 401 <filteredset
402 402 <fullreposet+ 0:9>>
403 403 $ try 'grep(r"\bissue\d+")'
404 404 (func
405 405 ('symbol', 'grep')
406 406 ('string', '\\bissue\\d+'))
407 407 * set:
408 408 <filteredset
409 409 <fullreposet+ 0:9>>
410 410 6
411 411 $ try 'grep(r"\")'
412 412 hg: parse error at 7: unterminated string
413 413 [255]
414 414 $ log 'head()'
415 415 0
416 416 1
417 417 2
418 418 3
419 419 4
420 420 5
421 421 6
422 422 7
423 423 9
424 424 $ log 'heads(6::)'
425 425 7
426 426 $ log 'keyword(issue)'
427 427 6
428 428 $ log 'keyword("test a")'
429 429 $ log 'limit(head(), 1)'
430 430 0
431 431 $ log 'matching(6)'
432 432 6
433 433 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
434 434 6
435 435 7
436 436
437 437 Testing min and max
438 438
439 439 max: simple
440 440
441 441 $ log 'max(contains(a))'
442 442 5
443 443
444 444 max: simple on unordered set)
445 445
446 446 $ log 'max((4+0+2+5+7) and contains(a))'
447 447 5
448 448
449 449 max: no result
450 450
451 451 $ log 'max(contains(stringthatdoesnotappearanywhere))'
452 452
453 453 max: no result on unordered set
454 454
455 455 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
456 456
457 457 min: simple
458 458
459 459 $ log 'min(contains(a))'
460 460 0
461 461
462 462 min: simple on unordered set
463 463
464 464 $ log 'min((4+0+2+5+7) and contains(a))'
465 465 0
466 466
467 467 min: empty
468 468
469 469 $ log 'min(contains(stringthatdoesnotappearanywhere))'
470 470
471 471 min: empty on unordered set
472 472
473 473 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
474 474
475 475
476 476 $ log 'merge()'
477 477 6
478 478 $ log 'branchpoint()'
479 479 1
480 480 4
481 481 $ log 'modifies(b)'
482 482 4
483 483 $ log 'modifies("path:b")'
484 484 4
485 485 $ log 'modifies("*")'
486 486 4
487 487 6
488 488 $ log 'modifies("set:modified()")'
489 489 4
490 490 $ log 'id(5)'
491 491 2
492 492 $ log 'only(9)'
493 493 8
494 494 9
495 495 $ log 'only(8)'
496 496 8
497 497 $ log 'only(9, 5)'
498 498 2
499 499 4
500 500 8
501 501 9
502 502 $ log 'only(7 + 9, 5 + 2)'
503 503 4
504 504 6
505 505 7
506 506 8
507 507 9
508 508
509 509 Test empty set input
510 510 $ log 'only(p2())'
511 511 $ log 'only(p1(), p2())'
512 512 0
513 513 1
514 514 2
515 515 4
516 516 8
517 517 9
518 518
519 519 Test '%' operator
520 520
521 521 $ log '9%'
522 522 8
523 523 9
524 524 $ log '9%5'
525 525 2
526 526 4
527 527 8
528 528 9
529 529 $ log '(7 + 9)%(5 + 2)'
530 530 4
531 531 6
532 532 7
533 533 8
534 534 9
535 535
536 536 Test opreand of '%' is optimized recursively (issue4670)
537 537
538 538 $ try --optimize '8:9-8%'
539 539 (onlypost
540 540 (minus
541 541 (range
542 542 ('symbol', '8')
543 543 ('symbol', '9'))
544 544 ('symbol', '8')))
545 545 * optimized:
546 546 (func
547 547 ('symbol', 'only')
548 548 (and
549 549 (range
550 550 ('symbol', '8')
551 551 ('symbol', '9'))
552 552 (not
553 553 ('symbol', '8'))))
554 554 * set:
555 555 <baseset+ [8, 9]>
556 556 8
557 557 9
558 558
559 559 Test the order of operations
560 560
561 561 $ log '7 + 9%5 + 2'
562 562 7
563 563 2
564 564 4
565 565 8
566 566 9
567 567
568 568 Test explicit numeric revision
569 569 $ log 'rev(-2)'
570 570 $ log 'rev(-1)'
571 571 -1
572 572 $ log 'rev(0)'
573 573 0
574 574 $ log 'rev(9)'
575 575 9
576 576 $ log 'rev(10)'
577 577 $ log 'rev(tip)'
578 578 hg: parse error: rev expects a number
579 579 [255]
580 580
581 581 Test hexadecimal revision
582 582 $ log 'id(2)'
583 583 abort: 00changelog.i@2: ambiguous identifier!
584 584 [255]
585 585 $ log 'id(23268)'
586 586 4
587 587 $ log 'id(2785f51eece)'
588 588 0
589 589 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
590 590 8
591 591 $ log 'id(d5d0dcbdc4a)'
592 592 $ log 'id(d5d0dcbdc4w)'
593 593 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
594 594 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
595 595 $ log 'id(1.0)'
596 596 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
597 597
598 598 Test null revision
599 599 $ log '(null)'
600 600 -1
601 601 $ log '(null:0)'
602 602 -1
603 603 0
604 604 $ log '(0:null)'
605 605 0
606 606 -1
607 607 $ log 'null::0'
608 608 -1
609 609 0
610 610 $ log 'null:tip - 0:'
611 611 -1
612 612 $ log 'null: and null::' | head -1
613 613 -1
614 614 $ log 'null: or 0:' | head -2
615 615 -1
616 616 0
617 617 $ log 'ancestors(null)'
618 618 -1
619 619 $ log 'reverse(null:)' | tail -2
620 620 0
621 621 -1
622 622 BROKEN: should be '-1'
623 623 $ log 'first(null:)'
624 624 BROKEN: should be '-1'
625 625 $ log 'min(null:)'
626 626 $ log 'tip:null and all()' | tail -2
627 627 1
628 628 0
629 629
630 630 Test working-directory revision
631 631 $ hg debugrevspec 'wdir()'
632 632 None
633 633 BROKEN: should include 'None'
634 634 $ hg debugrevspec 'tip or wdir()'
635 635 9
636 636 $ hg debugrevspec '0:tip and wdir()'
637 637
638 638 $ log 'outgoing()'
639 639 8
640 640 9
641 641 $ log 'outgoing("../remote1")'
642 642 8
643 643 9
644 644 $ log 'outgoing("../remote2")'
645 645 3
646 646 5
647 647 6
648 648 7
649 649 9
650 650 $ log 'p1(merge())'
651 651 5
652 652 $ log 'p2(merge())'
653 653 4
654 654 $ log 'parents(merge())'
655 655 4
656 656 5
657 657 $ log 'p1(branchpoint())'
658 658 0
659 659 2
660 660 $ log 'p2(branchpoint())'
661 661 $ log 'parents(branchpoint())'
662 662 0
663 663 2
664 664 $ log 'removes(a)'
665 665 2
666 666 6
667 667 $ log 'roots(all())'
668 668 0
669 669 $ log 'reverse(2 or 3 or 4 or 5)'
670 670 5
671 671 4
672 672 3
673 673 2
674 674 $ log 'reverse(all())'
675 675 9
676 676 8
677 677 7
678 678 6
679 679 5
680 680 4
681 681 3
682 682 2
683 683 1
684 684 0
685 685 $ log 'reverse(all()) & filelog(b)'
686 686 4
687 687 1
688 688 $ log 'rev(5)'
689 689 5
690 690 $ log 'sort(limit(reverse(all()), 3))'
691 691 7
692 692 8
693 693 9
694 694 $ log 'sort(2 or 3 or 4 or 5, date)'
695 695 2
696 696 3
697 697 5
698 698 4
699 699 $ log 'tagged()'
700 700 6
701 701 $ log 'tag()'
702 702 6
703 703 $ log 'tag(1.0)'
704 704 6
705 705 $ log 'tag(tip)'
706 706 9
707 707
708 708 test sort revset
709 709 --------------------------------------------
710 710
711 711 test when adding two unordered revsets
712 712
713 713 $ log 'sort(keyword(issue) or modifies(b))'
714 714 4
715 715 6
716 716
717 717 test when sorting a reversed collection in the same way it is
718 718
719 719 $ log 'sort(reverse(all()), -rev)'
720 720 9
721 721 8
722 722 7
723 723 6
724 724 5
725 725 4
726 726 3
727 727 2
728 728 1
729 729 0
730 730
731 731 test when sorting a reversed collection
732 732
733 733 $ log 'sort(reverse(all()), rev)'
734 734 0
735 735 1
736 736 2
737 737 3
738 738 4
739 739 5
740 740 6
741 741 7
742 742 8
743 743 9
744 744
745 745
746 746 test sorting two sorted collections in different orders
747 747
748 748 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
749 749 2
750 750 6
751 751 8
752 752 9
753 753
754 754 test sorting two sorted collections in different orders backwards
755 755
756 756 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
757 757 9
758 758 8
759 759 6
760 760 2
761 761
762 762 test subtracting something from an addset
763 763
764 764 $ log '(outgoing() or removes(a)) - removes(a)'
765 765 8
766 766 9
767 767
768 768 test intersecting something with an addset
769 769
770 770 $ log 'parents(outgoing() or removes(a))'
771 771 1
772 772 4
773 773 5
774 774 8
775 775
776 776 test that `or` operation combines elements in the right order:
777 777
778 778 $ log '3:4 or 2:5'
779 779 3
780 780 4
781 781 2
782 782 5
783 783 $ log '3:4 or 5:2'
784 784 3
785 785 4
786 786 5
787 787 2
788 788 $ log 'sort(3:4 or 2:5)'
789 789 2
790 790 3
791 791 4
792 792 5
793 793 $ log 'sort(3:4 or 5:2)'
794 794 2
795 795 3
796 796 4
797 797 5
798 798
799 799 check that conversion to only works
800 800 $ try --optimize '::3 - ::1'
801 801 (minus
802 802 (dagrangepre
803 803 ('symbol', '3'))
804 804 (dagrangepre
805 805 ('symbol', '1')))
806 806 * optimized:
807 807 (func
808 808 ('symbol', 'only')
809 809 (list
810 810 ('symbol', '3')
811 811 ('symbol', '1')))
812 812 * set:
813 813 <baseset+ [3]>
814 814 3
815 815 $ try --optimize 'ancestors(1) - ancestors(3)'
816 816 (minus
817 817 (func
818 818 ('symbol', 'ancestors')
819 819 ('symbol', '1'))
820 820 (func
821 821 ('symbol', 'ancestors')
822 822 ('symbol', '3')))
823 823 * optimized:
824 824 (func
825 825 ('symbol', 'only')
826 826 (list
827 827 ('symbol', '1')
828 828 ('symbol', '3')))
829 829 * set:
830 830 <baseset+ []>
831 831 $ try --optimize 'not ::2 and ::6'
832 832 (and
833 833 (not
834 834 (dagrangepre
835 835 ('symbol', '2')))
836 836 (dagrangepre
837 837 ('symbol', '6')))
838 838 * optimized:
839 839 (func
840 840 ('symbol', 'only')
841 841 (list
842 842 ('symbol', '6')
843 843 ('symbol', '2')))
844 844 * set:
845 845 <baseset+ [3, 4, 5, 6]>
846 846 3
847 847 4
848 848 5
849 849 6
850 850 $ try --optimize 'ancestors(6) and not ancestors(4)'
851 851 (and
852 852 (func
853 853 ('symbol', 'ancestors')
854 854 ('symbol', '6'))
855 855 (not
856 856 (func
857 857 ('symbol', 'ancestors')
858 858 ('symbol', '4'))))
859 859 * optimized:
860 860 (func
861 861 ('symbol', 'only')
862 862 (list
863 863 ('symbol', '6')
864 864 ('symbol', '4')))
865 865 * set:
866 866 <baseset+ [3, 5, 6]>
867 867 3
868 868 5
869 869 6
870 870
871 871 we can use patterns when searching for tags
872 872
873 873 $ log 'tag("1..*")'
874 874 abort: tag '1..*' does not exist!
875 875 [255]
876 876 $ log 'tag("re:1..*")'
877 877 6
878 878 $ log 'tag("re:[0-9].[0-9]")'
879 879 6
880 880 $ log 'tag("literal:1.0")'
881 881 6
882 882 $ log 'tag("re:0..*")'
883 883
884 884 $ log 'tag(unknown)'
885 885 abort: tag 'unknown' does not exist!
886 886 [255]
887 887 $ log 'tag("re:unknown")'
888 888 $ log 'present(tag("unknown"))'
889 889 $ log 'present(tag("re:unknown"))'
890 890 $ log 'branch(unknown)'
891 891 abort: unknown revision 'unknown'!
892 892 [255]
893 893 $ log 'branch("re:unknown")'
894 894 $ log 'present(branch("unknown"))'
895 895 $ log 'present(branch("re:unknown"))'
896 896 $ log 'user(bob)'
897 897 2
898 898
899 899 $ log '4::8'
900 900 4
901 901 8
902 902 $ log '4:8'
903 903 4
904 904 5
905 905 6
906 906 7
907 907 8
908 908
909 909 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
910 910 4
911 911 2
912 912 5
913 913
914 914 $ log 'not 0 and 0:2'
915 915 1
916 916 2
917 917 $ log 'not 1 and 0:2'
918 918 0
919 919 2
920 920 $ log 'not 2 and 0:2'
921 921 0
922 922 1
923 923 $ log '(1 and 2)::'
924 924 $ log '(1 and 2):'
925 925 $ log '(1 and 2):3'
926 926 $ log 'sort(head(), -rev)'
927 927 9
928 928 7
929 929 6
930 930 5
931 931 4
932 932 3
933 933 2
934 934 1
935 935 0
936 936 $ log '4::8 - 8'
937 937 4
938 938 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
939 939 2
940 940 3
941 941 1
942 942
943 943 $ log 'named("unknown")'
944 944 abort: namespace 'unknown' does not exist!
945 945 [255]
946 946 $ log 'named("re:unknown")'
947 947 abort: no namespace exists that match 'unknown'!
948 948 [255]
949 949 $ log 'present(named("unknown"))'
950 950 $ log 'present(named("re:unknown"))'
951 951
952 952 $ log 'tag()'
953 953 6
954 954 $ log 'named("tags")'
955 955 6
956 956
957 957 issue2437
958 958
959 959 $ log '3 and p1(5)'
960 960 3
961 961 $ log '4 and p2(6)'
962 962 4
963 963 $ log '1 and parents(:2)'
964 964 1
965 965 $ log '2 and children(1:)'
966 966 2
967 967 $ log 'roots(all()) or roots(all())'
968 968 0
969 969 $ hg debugrevspec 'roots(all()) or roots(all())'
970 970 0
971 971 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
972 972 9
973 973 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
974 974 4
975 975
976 976 issue2654: report a parse error if the revset was not completely parsed
977 977
978 978 $ log '1 OR 2'
979 979 hg: parse error at 2: invalid token
980 980 [255]
981 981
982 982 or operator should preserve ordering:
983 983 $ log 'reverse(2::4) or tip'
984 984 4
985 985 2
986 986 9
987 987
988 988 parentrevspec
989 989
990 990 $ log 'merge()^0'
991 991 6
992 992 $ log 'merge()^'
993 993 5
994 994 $ log 'merge()^1'
995 995 5
996 996 $ log 'merge()^2'
997 997 4
998 998 $ log 'merge()^^'
999 999 3
1000 1000 $ log 'merge()^1^'
1001 1001 3
1002 1002 $ log 'merge()^^^'
1003 1003 1
1004 1004
1005 1005 $ log 'merge()~0'
1006 1006 6
1007 1007 $ log 'merge()~1'
1008 1008 5
1009 1009 $ log 'merge()~2'
1010 1010 3
1011 1011 $ log 'merge()~2^1'
1012 1012 1
1013 1013 $ log 'merge()~3'
1014 1014 1
1015 1015
1016 1016 $ log '(-3:tip)^'
1017 1017 4
1018 1018 6
1019 1019 8
1020 1020
1021 1021 $ log 'tip^foo'
1022 1022 hg: parse error: ^ expects a number 0, 1, or 2
1023 1023 [255]
1024 1024
1025 1025 Bogus function gets suggestions
1026 1026 $ log 'add()'
1027 1027 hg: parse error: unknown identifier: add
1028 1028 (did you mean 'adds'?)
1029 1029 [255]
1030 1030 $ log 'added()'
1031 1031 hg: parse error: unknown identifier: added
1032 1032 (did you mean 'adds'?)
1033 1033 [255]
1034 1034 $ log 'remo()'
1035 1035 hg: parse error: unknown identifier: remo
1036 1036 (did you mean one of remote, removes?)
1037 1037 [255]
1038 1038 $ log 'babar()'
1039 1039 hg: parse error: unknown identifier: babar
1040 1040 [255]
1041 1041
1042 1042 multiple revspecs
1043 1043
1044 1044 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
1045 1045 8
1046 1046 9
1047 1047 4
1048 1048 5
1049 1049 6
1050 1050 7
1051 1051
1052 1052 test usage in revpair (with "+")
1053 1053
1054 1054 (real pair)
1055 1055
1056 1056 $ hg diff -r 'tip^^' -r 'tip'
1057 1057 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1058 1058 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1059 1059 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1060 1060 @@ -0,0 +1,1 @@
1061 1061 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1062 1062 $ hg diff -r 'tip^^::tip'
1063 1063 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1064 1064 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1065 1065 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1066 1066 @@ -0,0 +1,1 @@
1067 1067 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1068 1068
1069 1069 (single rev)
1070 1070
1071 1071 $ hg diff -r 'tip^' -r 'tip^'
1072 1072 $ hg diff -r 'tip^::tip^ or tip^'
1073 1073
1074 1074 (single rev that does not looks like a range)
1075 1075
1076 1076 $ hg diff -r 'tip^ or tip^'
1077 1077 diff -r d5d0dcbdc4d9 .hgtags
1078 1078 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1079 1079 +++ b/.hgtags * (glob)
1080 1080 @@ -0,0 +1,1 @@
1081 1081 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1082 1082
1083 1083 (no rev)
1084 1084
1085 1085 $ hg diff -r 'author("babar") or author("celeste")'
1086 1086 abort: empty revision range
1087 1087 [255]
1088 1088
1089 1089 aliases:
1090 1090
1091 1091 $ echo '[revsetalias]' >> .hg/hgrc
1092 1092 $ echo 'm = merge()' >> .hg/hgrc
1093 1093 (revset aliases can override builtin revsets)
1094 1094 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
1095 1095 $ echo 'sincem = descendants(m)' >> .hg/hgrc
1096 1096 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
1097 1097 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1098 1098 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1099 1099
1100 1100 $ try m
1101 1101 ('symbol', 'm')
1102 1102 (func
1103 1103 ('symbol', 'merge')
1104 1104 None)
1105 1105 * set:
1106 1106 <filteredset
1107 1107 <fullreposet+ 0:9>>
1108 1108 6
1109 1109
1110 1110 $ HGPLAIN=1
1111 1111 $ export HGPLAIN
1112 1112 $ try m
1113 1113 ('symbol', 'm')
1114 1114 abort: unknown revision 'm'!
1115 1115 [255]
1116 1116
1117 1117 $ HGPLAINEXCEPT=revsetalias
1118 1118 $ export HGPLAINEXCEPT
1119 1119 $ try m
1120 1120 ('symbol', 'm')
1121 1121 (func
1122 1122 ('symbol', 'merge')
1123 1123 None)
1124 1124 * set:
1125 1125 <filteredset
1126 1126 <fullreposet+ 0:9>>
1127 1127 6
1128 1128
1129 1129 $ unset HGPLAIN
1130 1130 $ unset HGPLAINEXCEPT
1131 1131
1132 1132 $ try 'p2(.)'
1133 1133 (func
1134 1134 ('symbol', 'p2')
1135 1135 ('symbol', '.'))
1136 1136 (func
1137 1137 ('symbol', 'p1')
1138 1138 ('symbol', '.'))
1139 1139 * set:
1140 1140 <baseset+ [8]>
1141 1141 8
1142 1142
1143 1143 $ HGPLAIN=1
1144 1144 $ export HGPLAIN
1145 1145 $ try 'p2(.)'
1146 1146 (func
1147 1147 ('symbol', 'p2')
1148 1148 ('symbol', '.'))
1149 1149 * set:
1150 1150 <baseset+ []>
1151 1151
1152 1152 $ HGPLAINEXCEPT=revsetalias
1153 1153 $ export HGPLAINEXCEPT
1154 1154 $ try 'p2(.)'
1155 1155 (func
1156 1156 ('symbol', 'p2')
1157 1157 ('symbol', '.'))
1158 1158 (func
1159 1159 ('symbol', 'p1')
1160 1160 ('symbol', '.'))
1161 1161 * set:
1162 1162 <baseset+ [8]>
1163 1163 8
1164 1164
1165 1165 $ unset HGPLAIN
1166 1166 $ unset HGPLAINEXCEPT
1167 1167
1168 1168 test alias recursion
1169 1169
1170 1170 $ try sincem
1171 1171 ('symbol', 'sincem')
1172 1172 (func
1173 1173 ('symbol', 'descendants')
1174 1174 (func
1175 1175 ('symbol', 'merge')
1176 1176 None))
1177 1177 * set:
1178 1178 <addset+
1179 1179 <filteredset
1180 1180 <fullreposet+ 0:9>>,
1181 1181 <generatorset+>>
1182 1182 6
1183 1183 7
1184 1184
1185 1185 test infinite recursion
1186 1186
1187 1187 $ echo 'recurse1 = recurse2' >> .hg/hgrc
1188 1188 $ echo 'recurse2 = recurse1' >> .hg/hgrc
1189 1189 $ try recurse1
1190 1190 ('symbol', 'recurse1')
1191 1191 hg: parse error: infinite expansion of revset alias "recurse1" detected
1192 1192 [255]
1193 1193
1194 1194 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
1195 1195 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
1196 1196 $ try "level2(level1(1, 2), 3)"
1197 1197 (func
1198 1198 ('symbol', 'level2')
1199 1199 (list
1200 1200 (func
1201 1201 ('symbol', 'level1')
1202 1202 (list
1203 1203 ('symbol', '1')
1204 1204 ('symbol', '2')))
1205 1205 ('symbol', '3')))
1206 1206 (or
1207 1207 ('symbol', '3')
1208 1208 (or
1209 1209 ('symbol', '1')
1210 1210 ('symbol', '2')))
1211 1211 * set:
1212 1212 <addset
1213 1213 <baseset [3]>,
1214 1214 <addset
1215 1215 <baseset [1]>,
1216 1216 <baseset [2]>>>
1217 1217 3
1218 1218 1
1219 1219 2
1220 1220
1221 1221 test nesting and variable passing
1222 1222
1223 1223 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
1224 1224 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
1225 1225 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1226 1226 $ try 'nested(2:5)'
1227 1227 (func
1228 1228 ('symbol', 'nested')
1229 1229 (range
1230 1230 ('symbol', '2')
1231 1231 ('symbol', '5')))
1232 1232 (func
1233 1233 ('symbol', 'max')
1234 1234 (range
1235 1235 ('symbol', '2')
1236 1236 ('symbol', '5')))
1237 1237 * set:
1238 1238 <baseset [5]>
1239 1239 5
1240 1240
1241 1241 test variable isolation, variable placeholders are rewritten as string
1242 1242 then parsed and matched again as string. Check they do not leak too
1243 1243 far away.
1244 1244
1245 1245 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1246 1246 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1247 1247 $ try 'callinjection(2:5)'
1248 1248 (func
1249 1249 ('symbol', 'callinjection')
1250 1250 (range
1251 1251 ('symbol', '2')
1252 1252 ('symbol', '5')))
1253 1253 (func
1254 1254 ('symbol', 'descendants')
1255 1255 (func
1256 1256 ('symbol', 'max')
1257 1257 ('string', '$1')))
1258 1258 abort: unknown revision '$1'!
1259 1259 [255]
1260 1260
1261 1261 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1262 1262 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1263 1263 $ try 'callinjection2(2:5)'
1264 1264 (func
1265 1265 ('symbol', 'callinjection2')
1266 1266 (range
1267 1267 ('symbol', '2')
1268 1268 ('symbol', '5')))
1269 1269 abort: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1270 1270 [255]
1271 1271 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1272 1272 ('symbol', 'tip')
1273 1273 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1274 1274 warning: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1275 1275 * set:
1276 1276 <baseset [9]>
1277 1277 9
1278 1278 >>> data = file('.hg/hgrc', 'rb').read()
1279 1279 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1280 1280
1281 1281 $ try 'tip'
1282 1282 ('symbol', 'tip')
1283 1283 * set:
1284 1284 <baseset [9]>
1285 1285 9
1286 1286
1287 1287 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1288 1288 ('symbol', 'tip')
1289 1289 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1290 1290 * set:
1291 1291 <baseset [9]>
1292 1292 9
1293 1293 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1294 1294 $ try 'strictreplacing("foo", tip)'
1295 1295 (func
1296 1296 ('symbol', 'strictreplacing')
1297 1297 (list
1298 1298 ('string', 'foo')
1299 1299 ('symbol', 'tip')))
1300 1300 (or
1301 1301 ('symbol', 'tip')
1302 1302 (func
1303 1303 ('symbol', 'desc')
1304 1304 ('string', '$1')))
1305 1305 * set:
1306 1306 <addset
1307 1307 <baseset [9]>,
1308 1308 <filteredset
1309 1309 <filteredset
1310 1310 <fullreposet+ 0:9>>>>
1311 1311 9
1312 1312
1313 1313 $ try 'd(2:5)'
1314 1314 (func
1315 1315 ('symbol', 'd')
1316 1316 (range
1317 1317 ('symbol', '2')
1318 1318 ('symbol', '5')))
1319 1319 (func
1320 1320 ('symbol', 'reverse')
1321 1321 (func
1322 1322 ('symbol', 'sort')
1323 1323 (list
1324 1324 (range
1325 1325 ('symbol', '2')
1326 1326 ('symbol', '5'))
1327 1327 ('symbol', 'date'))))
1328 1328 * set:
1329 1329 <baseset [4, 5, 3, 2]>
1330 1330 4
1331 1331 5
1332 1332 3
1333 1333 2
1334 1334 $ try 'rs(2 or 3, date)'
1335 1335 (func
1336 1336 ('symbol', 'rs')
1337 1337 (list
1338 1338 (or
1339 1339 ('symbol', '2')
1340 1340 ('symbol', '3'))
1341 1341 ('symbol', 'date')))
1342 1342 (func
1343 1343 ('symbol', 'reverse')
1344 1344 (func
1345 1345 ('symbol', 'sort')
1346 1346 (list
1347 1347 (or
1348 1348 ('symbol', '2')
1349 1349 ('symbol', '3'))
1350 1350 ('symbol', 'date'))))
1351 1351 * set:
1352 1352 <baseset [3, 2]>
1353 1353 3
1354 1354 2
1355 1355 $ try 'rs()'
1356 1356 (func
1357 1357 ('symbol', 'rs')
1358 1358 None)
1359 1359 hg: parse error: invalid number of arguments: 0
1360 1360 [255]
1361 1361 $ try 'rs(2)'
1362 1362 (func
1363 1363 ('symbol', 'rs')
1364 1364 ('symbol', '2'))
1365 1365 hg: parse error: invalid number of arguments: 1
1366 1366 [255]
1367 1367 $ try 'rs(2, data, 7)'
1368 1368 (func
1369 1369 ('symbol', 'rs')
1370 1370 (list
1371 1371 (list
1372 1372 ('symbol', '2')
1373 1373 ('symbol', 'data'))
1374 1374 ('symbol', '7')))
1375 1375 hg: parse error: invalid number of arguments: 3
1376 1376 [255]
1377 1377 $ try 'rs4(2 or 3, x, x, date)'
1378 1378 (func
1379 1379 ('symbol', 'rs4')
1380 1380 (list
1381 1381 (list
1382 1382 (list
1383 1383 (or
1384 1384 ('symbol', '2')
1385 1385 ('symbol', '3'))
1386 1386 ('symbol', 'x'))
1387 1387 ('symbol', 'x'))
1388 1388 ('symbol', 'date')))
1389 1389 (func
1390 1390 ('symbol', 'reverse')
1391 1391 (func
1392 1392 ('symbol', 'sort')
1393 1393 (list
1394 1394 (or
1395 1395 ('symbol', '2')
1396 1396 ('symbol', '3'))
1397 1397 ('symbol', 'date'))))
1398 1398 * set:
1399 1399 <baseset [3, 2]>
1400 1400 3
1401 1401 2
1402 1402
1403 1403 issue4553: check that revset aliases override existing hash prefix
1404 1404
1405 1405 $ hg log -qr e
1406 1406 6:e0cc66ef77e8
1407 1407
1408 1408 $ hg log -qr e --config revsetalias.e="all()"
1409 1409 0:2785f51eece5
1410 1410 1:d75937da8da0
1411 1411 2:5ed5505e9f1c
1412 1412 3:8528aa5637f2
1413 1413 4:2326846efdab
1414 1414 5:904fa392b941
1415 1415 6:e0cc66ef77e8
1416 1416 7:013af1973af4
1417 1417 8:d5d0dcbdc4d9
1418 1418 9:24286f4ae135
1419 1419
1420 1420 $ hg log -qr e: --config revsetalias.e="0"
1421 1421 0:2785f51eece5
1422 1422 1:d75937da8da0
1423 1423 2:5ed5505e9f1c
1424 1424 3:8528aa5637f2
1425 1425 4:2326846efdab
1426 1426 5:904fa392b941
1427 1427 6:e0cc66ef77e8
1428 1428 7:013af1973af4
1429 1429 8:d5d0dcbdc4d9
1430 1430 9:24286f4ae135
1431 1431
1432 1432 $ hg log -qr :e --config revsetalias.e="9"
1433 1433 0:2785f51eece5
1434 1434 1:d75937da8da0
1435 1435 2:5ed5505e9f1c
1436 1436 3:8528aa5637f2
1437 1437 4:2326846efdab
1438 1438 5:904fa392b941
1439 1439 6:e0cc66ef77e8
1440 1440 7:013af1973af4
1441 1441 8:d5d0dcbdc4d9
1442 1442 9:24286f4ae135
1443 1443
1444 1444 $ hg log -qr e:
1445 1445 6:e0cc66ef77e8
1446 1446 7:013af1973af4
1447 1447 8:d5d0dcbdc4d9
1448 1448 9:24286f4ae135
1449 1449
1450 1450 $ hg log -qr :e
1451 1451 0:2785f51eece5
1452 1452 1:d75937da8da0
1453 1453 2:5ed5505e9f1c
1454 1454 3:8528aa5637f2
1455 1455 4:2326846efdab
1456 1456 5:904fa392b941
1457 1457 6:e0cc66ef77e8
1458 1458
1459 1459 issue2549 - correct optimizations
1460 1460
1461 1461 $ log 'limit(1 or 2 or 3, 2) and not 2'
1462 1462 1
1463 1463 $ log 'max(1 or 2) and not 2'
1464 1464 $ log 'min(1 or 2) and not 1'
1465 1465 $ log 'last(1 or 2, 1) and not 2'
1466 1466
1467 1467 issue4289 - ordering of built-ins
1468 1468 $ hg log -M -q -r 3:2
1469 1469 3:8528aa5637f2
1470 1470 2:5ed5505e9f1c
1471 1471
1472 1472 test revsets started with 40-chars hash (issue3669)
1473 1473
1474 1474 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1475 1475 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1476 1476 9
1477 1477 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1478 1478 8
1479 1479
1480 1480 test or-ed indirect predicates (issue3775)
1481 1481
1482 1482 $ log '6 or 6^1' | sort
1483 1483 5
1484 1484 6
1485 1485 $ log '6^1 or 6' | sort
1486 1486 5
1487 1487 6
1488 1488 $ log '4 or 4~1' | sort
1489 1489 2
1490 1490 4
1491 1491 $ log '4~1 or 4' | sort
1492 1492 2
1493 1493 4
1494 1494 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1495 1495 0
1496 1496 1
1497 1497 2
1498 1498 3
1499 1499 4
1500 1500 5
1501 1501 6
1502 1502 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1503 1503 0
1504 1504 1
1505 1505 2
1506 1506 3
1507 1507 4
1508 1508 5
1509 1509 6
1510 1510
1511 1511 tests for 'remote()' predicate:
1512 1512 #. (csets in remote) (id) (remote)
1513 1513 1. less than local current branch "default"
1514 1514 2. same with local specified "default"
1515 1515 3. more than local specified specified
1516 1516
1517 1517 $ hg clone --quiet -U . ../remote3
1518 1518 $ cd ../remote3
1519 1519 $ hg update -q 7
1520 1520 $ echo r > r
1521 1521 $ hg ci -Aqm 10
1522 1522 $ log 'remote()'
1523 1523 7
1524 1524 $ log 'remote("a-b-c-")'
1525 1525 2
1526 1526 $ cd ../repo
1527 1527 $ log 'remote(".a.b.c.", "../remote3")'
1528 1528
1529 1529 tests for concatenation of strings/symbols by "##"
1530 1530
1531 1531 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
1532 1532 (_concat
1533 1533 (_concat
1534 1534 (_concat
1535 1535 ('symbol', '278')
1536 1536 ('string', '5f5'))
1537 1537 ('symbol', '1ee'))
1538 1538 ('string', 'ce5'))
1539 1539 ('string', '2785f51eece5')
1540 1540 * set:
1541 1541 <baseset [0]>
1542 1542 0
1543 1543
1544 1544 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
1545 1545 $ try "cat4(278, '5f5', 1ee, 'ce5')"
1546 1546 (func
1547 1547 ('symbol', 'cat4')
1548 1548 (list
1549 1549 (list
1550 1550 (list
1551 1551 ('symbol', '278')
1552 1552 ('string', '5f5'))
1553 1553 ('symbol', '1ee'))
1554 1554 ('string', 'ce5')))
1555 1555 (_concat
1556 1556 (_concat
1557 1557 (_concat
1558 1558 ('symbol', '278')
1559 1559 ('string', '5f5'))
1560 1560 ('symbol', '1ee'))
1561 1561 ('string', 'ce5'))
1562 1562 ('string', '2785f51eece5')
1563 1563 * set:
1564 1564 <baseset [0]>
1565 1565 0
1566 1566
1567 1567 (check concatenation in alias nesting)
1568 1568
1569 1569 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
1570 1570 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
1571 1571 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
1572 1572 0
1573 1573
1574 1574 (check operator priority)
1575 1575
1576 1576 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
1577 1577 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
1578 1578 0
1579 1579 4
1580 1580
1581 1581 $ cd ..
1582 1582
1583 1583 prepare repository that has "default" branches of multiple roots
1584 1584
1585 1585 $ hg init namedbranch
1586 1586 $ cd namedbranch
1587 1587
1588 1588 $ echo default0 >> a
1589 1589 $ hg ci -Aqm0
1590 1590 $ echo default1 >> a
1591 1591 $ hg ci -m1
1592 1592
1593 1593 $ hg branch -q stable
1594 1594 $ echo stable2 >> a
1595 1595 $ hg ci -m2
1596 1596 $ echo stable3 >> a
1597 1597 $ hg ci -m3
1598 1598
1599 1599 $ hg update -q null
1600 1600 $ echo default4 >> a
1601 1601 $ hg ci -Aqm4
1602 1602 $ echo default5 >> a
1603 1603 $ hg ci -m5
1604 1604
1605 "null" revision belongs to "default" branch (issue4683)
1606
1607 $ log 'branch(null)'
1608 0
1609 1
1610 4
1611 5
1612
1605 1613 "null" revision belongs to "default" branch, but it shouldn't appear in set
1606 1614 unless explicitly specified (issue4682)
1607 1615
1608 1616 $ log 'children(branch(default))'
1609 1617 1
1610 1618 2
1611 1619 5
1612 1620
1613 1621 $ cd ..
1614 1622
1615 1623 test author/desc/keyword in problematic encoding
1616 1624 # unicode: cp932:
1617 1625 # u30A2 0x83 0x41(= 'A')
1618 1626 # u30C2 0x83 0x61(= 'a')
1619 1627
1620 1628 $ hg init problematicencoding
1621 1629 $ cd problematicencoding
1622 1630
1623 1631 $ python > setup.sh <<EOF
1624 1632 > print u'''
1625 1633 > echo a > text
1626 1634 > hg add text
1627 1635 > hg --encoding utf-8 commit -u '\u30A2' -m none
1628 1636 > echo b > text
1629 1637 > hg --encoding utf-8 commit -u '\u30C2' -m none
1630 1638 > echo c > text
1631 1639 > hg --encoding utf-8 commit -u none -m '\u30A2'
1632 1640 > echo d > text
1633 1641 > hg --encoding utf-8 commit -u none -m '\u30C2'
1634 1642 > '''.encode('utf-8')
1635 1643 > EOF
1636 1644 $ sh < setup.sh
1637 1645
1638 1646 test in problematic encoding
1639 1647 $ python > test.sh <<EOF
1640 1648 > print u'''
1641 1649 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
1642 1650 > echo ====
1643 1651 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
1644 1652 > echo ====
1645 1653 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
1646 1654 > echo ====
1647 1655 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
1648 1656 > echo ====
1649 1657 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
1650 1658 > echo ====
1651 1659 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
1652 1660 > '''.encode('cp932')
1653 1661 > EOF
1654 1662 $ sh < test.sh
1655 1663 0
1656 1664 ====
1657 1665 1
1658 1666 ====
1659 1667 2
1660 1668 ====
1661 1669 3
1662 1670 ====
1663 1671 0
1664 1672 2
1665 1673 ====
1666 1674 1
1667 1675 3
1668 1676
1669 1677 test error message of bad revset
1670 1678 $ hg log -r 'foo\\'
1671 1679 hg: parse error at 3: syntax error in revset 'foo\\'
1672 1680 [255]
1673 1681
1674 1682 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now