##// END OF EJS Templates
merge with stable
Martin Geisler -
r12321:11db6fa2 merge default
parent child Browse files
Show More
@@ -1,259 +1,266
1 # archival.py - revision archival for mercurial
1 # archival.py - revision archival for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 from node import hex
9 from node import hex
10 import cmdutil
10 import cmdutil
11 import util
11 import util
12 import cStringIO, os, stat, tarfile, time, zipfile
12 import cStringIO, os, stat, tarfile, time, zipfile
13 import zlib, gzip
13 import zlib, gzip
14
14
15 def tidyprefix(dest, kind, prefix):
15 def tidyprefix(dest, kind, prefix):
16 '''choose prefix to use for names in archive. make sure prefix is
16 '''choose prefix to use for names in archive. make sure prefix is
17 safe for consumers.'''
17 safe for consumers.'''
18
18
19 if prefix:
19 if prefix:
20 prefix = util.normpath(prefix)
20 prefix = util.normpath(prefix)
21 else:
21 else:
22 if not isinstance(dest, str):
22 if not isinstance(dest, str):
23 raise ValueError('dest must be string if no prefix')
23 raise ValueError('dest must be string if no prefix')
24 prefix = os.path.basename(dest)
24 prefix = os.path.basename(dest)
25 lower = prefix.lower()
25 lower = prefix.lower()
26 for sfx in exts.get(kind, []):
26 for sfx in exts.get(kind, []):
27 if lower.endswith(sfx):
27 if lower.endswith(sfx):
28 prefix = prefix[:-len(sfx)]
28 prefix = prefix[:-len(sfx)]
29 break
29 break
30 lpfx = os.path.normpath(util.localpath(prefix))
30 lpfx = os.path.normpath(util.localpath(prefix))
31 prefix = util.pconvert(lpfx)
31 prefix = util.pconvert(lpfx)
32 if not prefix.endswith('/'):
32 if not prefix.endswith('/'):
33 prefix += '/'
33 prefix += '/'
34 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
34 if prefix.startswith('../') or os.path.isabs(lpfx) or '/../' in prefix:
35 raise util.Abort(_('archive prefix contains illegal components'))
35 raise util.Abort(_('archive prefix contains illegal components'))
36 return prefix
36 return prefix
37
37
38 exts = {
38 exts = {
39 'tar': ['.tar'],
39 'tar': ['.tar'],
40 'tbz2': ['.tbz2', '.tar.bz2'],
40 'tbz2': ['.tbz2', '.tar.bz2'],
41 'tgz': ['.tgz', '.tar.gz'],
41 'tgz': ['.tgz', '.tar.gz'],
42 'zip': ['.zip'],
42 'zip': ['.zip'],
43 }
43 }
44
44
45 def guesskind(dest):
45 def guesskind(dest):
46 for kind, extensions in exts.iteritems():
46 for kind, extensions in exts.iteritems():
47 if util.any(dest.endswith(ext) for ext in extensions):
47 if util.any(dest.endswith(ext) for ext in extensions):
48 return kind
48 return kind
49 return None
49 return None
50
50
51
51
52 class tarit(object):
52 class tarit(object):
53 '''write archive to tar file or stream. can write uncompressed,
53 '''write archive to tar file or stream. can write uncompressed,
54 or compress with gzip or bzip2.'''
54 or compress with gzip or bzip2.'''
55
55
56 class GzipFileWithTime(gzip.GzipFile):
56 class GzipFileWithTime(gzip.GzipFile):
57
57
58 def __init__(self, *args, **kw):
58 def __init__(self, *args, **kw):
59 timestamp = None
59 timestamp = None
60 if 'timestamp' in kw:
60 if 'timestamp' in kw:
61 timestamp = kw.pop('timestamp')
61 timestamp = kw.pop('timestamp')
62 if timestamp is None:
62 if timestamp is None:
63 self.timestamp = time.time()
63 self.timestamp = time.time()
64 else:
64 else:
65 self.timestamp = timestamp
65 self.timestamp = timestamp
66 gzip.GzipFile.__init__(self, *args, **kw)
66 gzip.GzipFile.__init__(self, *args, **kw)
67
67
68 def _write_gzip_header(self):
68 def _write_gzip_header(self):
69 self.fileobj.write('\037\213') # magic header
69 self.fileobj.write('\037\213') # magic header
70 self.fileobj.write('\010') # compression method
70 self.fileobj.write('\010') # compression method
71 # Python 2.6 deprecates self.filename
71 # Python 2.6 deprecates self.filename
72 fname = getattr(self, 'name', None) or self.filename
72 fname = getattr(self, 'name', None) or self.filename
73 flags = 0
73 flags = 0
74 if fname:
74 if fname:
75 flags = gzip.FNAME
75 flags = gzip.FNAME
76 self.fileobj.write(chr(flags))
76 self.fileobj.write(chr(flags))
77 gzip.write32u(self.fileobj, long(self.timestamp))
77 gzip.write32u(self.fileobj, long(self.timestamp))
78 self.fileobj.write('\002')
78 self.fileobj.write('\002')
79 self.fileobj.write('\377')
79 self.fileobj.write('\377')
80 if fname:
80 if fname:
81 self.fileobj.write(fname + '\000')
81 self.fileobj.write(fname + '\000')
82
82
83 def __init__(self, dest, mtime, kind=''):
83 def __init__(self, dest, mtime, kind=''):
84 self.mtime = mtime
84 self.mtime = mtime
85
85
86 def taropen(name, mode, fileobj=None):
86 def taropen(name, mode, fileobj=None):
87 if kind == 'gz':
87 if kind == 'gz':
88 mode = mode[0]
88 mode = mode[0]
89 if not fileobj:
89 if not fileobj:
90 fileobj = open(name, mode + 'b')
90 fileobj = open(name, mode + 'b')
91 gzfileobj = self.GzipFileWithTime(name, mode + 'b',
91 gzfileobj = self.GzipFileWithTime(name, mode + 'b',
92 zlib.Z_BEST_COMPRESSION,
92 zlib.Z_BEST_COMPRESSION,
93 fileobj, timestamp=mtime)
93 fileobj, timestamp=mtime)
94 return tarfile.TarFile.taropen(name, mode, gzfileobj)
94 return tarfile.TarFile.taropen(name, mode, gzfileobj)
95 else:
95 else:
96 return tarfile.open(name, mode + kind, fileobj)
96 return tarfile.open(name, mode + kind, fileobj)
97
97
98 if isinstance(dest, str):
98 if isinstance(dest, str):
99 self.z = taropen(dest, mode='w:')
99 self.z = taropen(dest, mode='w:')
100 else:
100 else:
101 # Python 2.5-2.5.1 have a regression that requires a name arg
101 # Python 2.5-2.5.1 have a regression that requires a name arg
102 self.z = taropen(name='', mode='w|', fileobj=dest)
102 self.z = taropen(name='', mode='w|', fileobj=dest)
103
103
104 def addfile(self, name, mode, islink, data):
104 def addfile(self, name, mode, islink, data):
105 i = tarfile.TarInfo(name)
105 i = tarfile.TarInfo(name)
106 i.mtime = self.mtime
106 i.mtime = self.mtime
107 i.size = len(data)
107 i.size = len(data)
108 if islink:
108 if islink:
109 i.type = tarfile.SYMTYPE
109 i.type = tarfile.SYMTYPE
110 i.mode = 0777
110 i.mode = 0777
111 i.linkname = data
111 i.linkname = data
112 data = None
112 data = None
113 i.size = 0
113 i.size = 0
114 else:
114 else:
115 i.mode = mode
115 i.mode = mode
116 data = cStringIO.StringIO(data)
116 data = cStringIO.StringIO(data)
117 self.z.addfile(i, data)
117 self.z.addfile(i, data)
118
118
119 def done(self):
119 def done(self):
120 self.z.close()
120 self.z.close()
121
121
122 class tellable(object):
122 class tellable(object):
123 '''provide tell method for zipfile.ZipFile when writing to http
123 '''provide tell method for zipfile.ZipFile when writing to http
124 response file object.'''
124 response file object.'''
125
125
126 def __init__(self, fp):
126 def __init__(self, fp):
127 self.fp = fp
127 self.fp = fp
128 self.offset = 0
128 self.offset = 0
129
129
130 def __getattr__(self, key):
130 def __getattr__(self, key):
131 return getattr(self.fp, key)
131 return getattr(self.fp, key)
132
132
133 def write(self, s):
133 def write(self, s):
134 self.fp.write(s)
134 self.fp.write(s)
135 self.offset += len(s)
135 self.offset += len(s)
136
136
137 def tell(self):
137 def tell(self):
138 return self.offset
138 return self.offset
139
139
140 class zipit(object):
140 class zipit(object):
141 '''write archive to zip file or stream. can write uncompressed,
141 '''write archive to zip file or stream. can write uncompressed,
142 or compressed with deflate.'''
142 or compressed with deflate.'''
143
143
144 def __init__(self, dest, mtime, compress=True):
144 def __init__(self, dest, mtime, compress=True):
145 if not isinstance(dest, str):
145 if not isinstance(dest, str):
146 try:
146 try:
147 dest.tell()
147 dest.tell()
148 except (AttributeError, IOError):
148 except (AttributeError, IOError):
149 dest = tellable(dest)
149 dest = tellable(dest)
150 self.z = zipfile.ZipFile(dest, 'w',
150 self.z = zipfile.ZipFile(dest, 'w',
151 compress and zipfile.ZIP_DEFLATED or
151 compress and zipfile.ZIP_DEFLATED or
152 zipfile.ZIP_STORED)
152 zipfile.ZIP_STORED)
153
154 # Python's zipfile module emits deprecation warnings if we try
155 # to store files with a date before 1980.
156 epoch = 315532800 # calendar.timegm((1980, 1, 1, 0, 0, 0, 1, 1, 0))
157 if mtime < epoch:
158 mtime = epoch
159
153 self.date_time = time.gmtime(mtime)[:6]
160 self.date_time = time.gmtime(mtime)[:6]
154
161
155 def addfile(self, name, mode, islink, data):
162 def addfile(self, name, mode, islink, data):
156 i = zipfile.ZipInfo(name, self.date_time)
163 i = zipfile.ZipInfo(name, self.date_time)
157 i.compress_type = self.z.compression
164 i.compress_type = self.z.compression
158 # unzip will not honor unix file modes unless file creator is
165 # unzip will not honor unix file modes unless file creator is
159 # set to unix (id 3).
166 # set to unix (id 3).
160 i.create_system = 3
167 i.create_system = 3
161 ftype = stat.S_IFREG
168 ftype = stat.S_IFREG
162 if islink:
169 if islink:
163 mode = 0777
170 mode = 0777
164 ftype = stat.S_IFLNK
171 ftype = stat.S_IFLNK
165 i.external_attr = (mode | ftype) << 16L
172 i.external_attr = (mode | ftype) << 16L
166 self.z.writestr(i, data)
173 self.z.writestr(i, data)
167
174
168 def done(self):
175 def done(self):
169 self.z.close()
176 self.z.close()
170
177
171 class fileit(object):
178 class fileit(object):
172 '''write archive as files in directory.'''
179 '''write archive as files in directory.'''
173
180
174 def __init__(self, name, mtime):
181 def __init__(self, name, mtime):
175 self.basedir = name
182 self.basedir = name
176 self.opener = util.opener(self.basedir)
183 self.opener = util.opener(self.basedir)
177
184
178 def addfile(self, name, mode, islink, data):
185 def addfile(self, name, mode, islink, data):
179 if islink:
186 if islink:
180 self.opener.symlink(data, name)
187 self.opener.symlink(data, name)
181 return
188 return
182 f = self.opener(name, "w", atomictemp=True)
189 f = self.opener(name, "w", atomictemp=True)
183 f.write(data)
190 f.write(data)
184 f.rename()
191 f.rename()
185 destfile = os.path.join(self.basedir, name)
192 destfile = os.path.join(self.basedir, name)
186 os.chmod(destfile, mode)
193 os.chmod(destfile, mode)
187
194
188 def done(self):
195 def done(self):
189 pass
196 pass
190
197
191 archivers = {
198 archivers = {
192 'files': fileit,
199 'files': fileit,
193 'tar': tarit,
200 'tar': tarit,
194 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
201 'tbz2': lambda name, mtime: tarit(name, mtime, 'bz2'),
195 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
202 'tgz': lambda name, mtime: tarit(name, mtime, 'gz'),
196 'uzip': lambda name, mtime: zipit(name, mtime, False),
203 'uzip': lambda name, mtime: zipit(name, mtime, False),
197 'zip': zipit,
204 'zip': zipit,
198 }
205 }
199
206
200 def archive(repo, dest, node, kind, decode=True, matchfn=None,
207 def archive(repo, dest, node, kind, decode=True, matchfn=None,
201 prefix=None, mtime=None):
208 prefix=None, mtime=None):
202 '''create archive of repo as it was at node.
209 '''create archive of repo as it was at node.
203
210
204 dest can be name of directory, name of archive file, or file
211 dest can be name of directory, name of archive file, or file
205 object to write archive to.
212 object to write archive to.
206
213
207 kind is type of archive to create.
214 kind is type of archive to create.
208
215
209 decode tells whether to put files through decode filters from
216 decode tells whether to put files through decode filters from
210 hgrc.
217 hgrc.
211
218
212 matchfn is function to filter names of files to write to archive.
219 matchfn is function to filter names of files to write to archive.
213
220
214 prefix is name of path to put before every archive member.'''
221 prefix is name of path to put before every archive member.'''
215
222
216 if kind == 'files':
223 if kind == 'files':
217 if prefix:
224 if prefix:
218 raise util.Abort(_('cannot give prefix when archiving to files'))
225 raise util.Abort(_('cannot give prefix when archiving to files'))
219 else:
226 else:
220 prefix = tidyprefix(dest, kind, prefix)
227 prefix = tidyprefix(dest, kind, prefix)
221
228
222 def write(name, mode, islink, getdata):
229 def write(name, mode, islink, getdata):
223 if matchfn and not matchfn(name):
230 if matchfn and not matchfn(name):
224 return
231 return
225 data = getdata()
232 data = getdata()
226 if decode:
233 if decode:
227 data = repo.wwritedata(name, data)
234 data = repo.wwritedata(name, data)
228 archiver.addfile(prefix + name, mode, islink, data)
235 archiver.addfile(prefix + name, mode, islink, data)
229
236
230 if kind not in archivers:
237 if kind not in archivers:
231 raise util.Abort(_("unknown archive type '%s'") % kind)
238 raise util.Abort(_("unknown archive type '%s'") % kind)
232
239
233 ctx = repo[node]
240 ctx = repo[node]
234 archiver = archivers[kind](dest, mtime or ctx.date()[0])
241 archiver = archivers[kind](dest, mtime or ctx.date()[0])
235
242
236 if repo.ui.configbool("ui", "archivemeta", True):
243 if repo.ui.configbool("ui", "archivemeta", True):
237 def metadata():
244 def metadata():
238 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
245 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
239 repo[0].hex(), hex(node), ctx.branch())
246 repo[0].hex(), hex(node), ctx.branch())
240
247
241 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
248 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
242 if repo.tagtype(t) == 'global')
249 if repo.tagtype(t) == 'global')
243 if not tags:
250 if not tags:
244 repo.ui.pushbuffer()
251 repo.ui.pushbuffer()
245 opts = {'template': '{latesttag}\n{latesttagdistance}',
252 opts = {'template': '{latesttag}\n{latesttagdistance}',
246 'style': '', 'patch': None, 'git': None}
253 'style': '', 'patch': None, 'git': None}
247 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
254 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
248 ltags, dist = repo.ui.popbuffer().split('\n')
255 ltags, dist = repo.ui.popbuffer().split('\n')
249 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
256 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
250 tags += 'latesttagdistance: %s\n' % dist
257 tags += 'latesttagdistance: %s\n' % dist
251
258
252 return base + tags
259 return base + tags
253
260
254 write('.hg_archival.txt', 0644, False, metadata)
261 write('.hg_archival.txt', 0644, False, metadata)
255
262
256 for f in ctx:
263 for f in ctx:
257 ff = ctx.flags(f)
264 ff = ctx.flags(f)
258 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data)
265 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, ctx[f].data)
259 archiver.done()
266 archiver.done()
@@ -1,588 +1,591
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import re
8 import re
9 import parser, util, error, discovery
9 import parser, util, error, discovery
10 import match as matchmod
10 import match as matchmod
11 from i18n import _
11 from i18n import _
12
12
13 elements = {
13 elements = {
14 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
14 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "-": (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):
198 def minrev(repo, subset, x):
199 s = getset(repo, subset, x)
199 s = getset(repo, subset, x)
200 if s:
200 if s:
201 m = min(s)
201 m = min(s)
202 if m in subset:
202 if m in subset:
203 return [m]
203 return [m]
204 return []
204 return []
205
205
206 def limit(repo, subset, x):
206 def limit(repo, subset, x):
207 l = getargs(x, 2, 2, _("limit wants two arguments"))
207 l = getargs(x, 2, 2, _("limit wants two arguments"))
208 try:
208 try:
209 lim = int(getstring(l[1], _("limit wants a number")))
209 lim = int(getstring(l[1], _("limit wants a number")))
210 except ValueError:
210 except ValueError:
211 raise error.ParseError(_("limit expects a number"))
211 raise error.ParseError(_("limit expects a number"))
212 return getset(repo, subset, l[0])[:lim]
212 return getset(repo, subset, l[0])[:lim]
213
213
214 def children(repo, subset, x):
214 def children(repo, subset, x):
215 cs = set()
215 cs = set()
216 cl = repo.changelog
216 cl = repo.changelog
217 s = set(getset(repo, subset, x))
217 s = set(getset(repo, subset, x))
218 for r in xrange(0, len(repo)):
218 for r in xrange(0, len(repo)):
219 for p in cl.parentrevs(r):
219 for p in cl.parentrevs(r):
220 if p in s:
220 if p in s:
221 cs.add(r)
221 cs.add(r)
222 return [r for r in subset if r in cs]
222 return [r for r in subset if r in cs]
223
223
224 def branch(repo, subset, x):
224 def branch(repo, subset, x):
225 s = getset(repo, range(len(repo)), x)
225 s = getset(repo, range(len(repo)), x)
226 b = set()
226 b = set()
227 for r in s:
227 for r in s:
228 b.add(repo[r].branch())
228 b.add(repo[r].branch())
229 s = set(s)
229 s = set(s)
230 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]
231
231
232 def ancestor(repo, subset, x):
232 def ancestor(repo, subset, x):
233 l = getargs(x, 2, 2, _("ancestor wants two arguments"))
233 l = getargs(x, 2, 2, _("ancestor wants two arguments"))
234 r = range(len(repo))
234 r = range(len(repo))
235 a = getset(repo, r, l[0])
235 a = getset(repo, r, l[0])
236 b = getset(repo, r, l[1])
236 b = getset(repo, r, l[1])
237 if len(a) != 1 or len(b) != 1:
237 if len(a) != 1 or len(b) != 1:
238 raise error.ParseError(_("ancestor arguments must be single revisions"))
238 raise error.ParseError(_("ancestor arguments must be single revisions"))
239 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
239 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
240
240
241 return [r for r in an if r in subset]
241 return [r for r in an if r in subset]
242
242
243 def ancestors(repo, subset, x):
243 def ancestors(repo, subset, x):
244 args = getset(repo, range(len(repo)), x)
244 args = getset(repo, range(len(repo)), x)
245 if not args:
245 if not args:
246 return []
246 return []
247 s = set(repo.changelog.ancestors(*args)) | set(args)
247 s = set(repo.changelog.ancestors(*args)) | set(args)
248 return [r for r in subset if r in s]
248 return [r for r in subset if r in s]
249
249
250 def descendants(repo, subset, x):
250 def descendants(repo, subset, x):
251 args = getset(repo, range(len(repo)), x)
251 args = getset(repo, range(len(repo)), x)
252 if not args:
252 if not args:
253 return []
253 return []
254 s = set(repo.changelog.descendants(*args)) | set(args)
254 s = set(repo.changelog.descendants(*args)) | set(args)
255 return [r for r in subset if r in s]
255 return [r for r in subset if r in s]
256
256
257 def follow(repo, subset, x):
257 def follow(repo, subset, x):
258 getargs(x, 0, 0, _("follow takes no arguments"))
258 getargs(x, 0, 0, _("follow takes no arguments"))
259 p = repo['.'].rev()
259 p = repo['.'].rev()
260 s = set(repo.changelog.ancestors(p)) | set([p])
260 s = set(repo.changelog.ancestors(p)) | set([p])
261 return [r for r in subset if r in s]
261 return [r for r in subset if r in s]
262
262
263 def date(repo, subset, x):
263 def date(repo, subset, x):
264 ds = getstring(x, _("date wants a string"))
264 ds = getstring(x, _("date wants a string"))
265 dm = util.matchdate(ds)
265 dm = util.matchdate(ds)
266 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])]
267
267
268 def keyword(repo, subset, x):
268 def keyword(repo, subset, x):
269 kw = getstring(x, _("keyword wants a string")).lower()
269 kw = getstring(x, _("keyword wants a string")).lower()
270 l = []
270 l = []
271 for r in subset:
271 for r in subset:
272 c = repo[r]
272 c = repo[r]
273 t = " ".join(c.files() + [c.user(), c.description()])
273 t = " ".join(c.files() + [c.user(), c.description()])
274 if kw in t.lower():
274 if kw in t.lower():
275 l.append(r)
275 l.append(r)
276 return l
276 return l
277
277
278 def grep(repo, subset, x):
278 def grep(repo, subset, x):
279 gr = re.compile(getstring(x, _("grep wants a string")))
279 try:
280 gr = re.compile(getstring(x, _("grep wants a string")))
281 except re.error, e:
282 raise error.ParseError(_('invalid match pattern: %s') % e)
280 l = []
283 l = []
281 for r in subset:
284 for r in subset:
282 c = repo[r]
285 c = repo[r]
283 for e in c.files() + [c.user(), c.description()]:
286 for e in c.files() + [c.user(), c.description()]:
284 if gr.search(e):
287 if gr.search(e):
285 l.append(r)
288 l.append(r)
286 continue
289 continue
287 return l
290 return l
288
291
289 def author(repo, subset, x):
292 def author(repo, subset, x):
290 n = getstring(x, _("author wants a string")).lower()
293 n = getstring(x, _("author wants a string")).lower()
291 return [r for r in subset if n in repo[r].user().lower()]
294 return [r for r in subset if n in repo[r].user().lower()]
292
295
293 def hasfile(repo, subset, x):
296 def hasfile(repo, subset, x):
294 pat = getstring(x, _("file wants a pattern"))
297 pat = getstring(x, _("file wants a pattern"))
295 m = matchmod.match(repo.root, repo.getcwd(), [pat])
298 m = matchmod.match(repo.root, repo.getcwd(), [pat])
296 s = []
299 s = []
297 for r in subset:
300 for r in subset:
298 for f in repo[r].files():
301 for f in repo[r].files():
299 if m(f):
302 if m(f):
300 s.append(r)
303 s.append(r)
301 continue
304 continue
302 return s
305 return s
303
306
304 def contains(repo, subset, x):
307 def contains(repo, subset, x):
305 pat = getstring(x, _("contains wants a pattern"))
308 pat = getstring(x, _("contains wants a pattern"))
306 m = matchmod.match(repo.root, repo.getcwd(), [pat])
309 m = matchmod.match(repo.root, repo.getcwd(), [pat])
307 s = []
310 s = []
308 if m.files() == [pat]:
311 if m.files() == [pat]:
309 for r in subset:
312 for r in subset:
310 if pat in repo[r]:
313 if pat in repo[r]:
311 s.append(r)
314 s.append(r)
312 continue
315 continue
313 else:
316 else:
314 for r in subset:
317 for r in subset:
315 for f in repo[r].manifest():
318 for f in repo[r].manifest():
316 if m(f):
319 if m(f):
317 s.append(r)
320 s.append(r)
318 continue
321 continue
319 return s
322 return s
320
323
321 def checkstatus(repo, subset, pat, field):
324 def checkstatus(repo, subset, pat, field):
322 m = matchmod.match(repo.root, repo.getcwd(), [pat])
325 m = matchmod.match(repo.root, repo.getcwd(), [pat])
323 s = []
326 s = []
324 fast = (m.files() == [pat])
327 fast = (m.files() == [pat])
325 for r in subset:
328 for r in subset:
326 c = repo[r]
329 c = repo[r]
327 if fast:
330 if fast:
328 if pat not in c.files():
331 if pat not in c.files():
329 continue
332 continue
330 else:
333 else:
331 for f in c.files():
334 for f in c.files():
332 if m(f):
335 if m(f):
333 break
336 break
334 else:
337 else:
335 continue
338 continue
336 files = repo.status(c.p1().node(), c.node())[field]
339 files = repo.status(c.p1().node(), c.node())[field]
337 if fast:
340 if fast:
338 if pat in files:
341 if pat in files:
339 s.append(r)
342 s.append(r)
340 continue
343 continue
341 else:
344 else:
342 for f in files:
345 for f in files:
343 if m(f):
346 if m(f):
344 s.append(r)
347 s.append(r)
345 continue
348 continue
346 return s
349 return s
347
350
348 def modifies(repo, subset, x):
351 def modifies(repo, subset, x):
349 pat = getstring(x, _("modifies wants a pattern"))
352 pat = getstring(x, _("modifies wants a pattern"))
350 return checkstatus(repo, subset, pat, 0)
353 return checkstatus(repo, subset, pat, 0)
351
354
352 def adds(repo, subset, x):
355 def adds(repo, subset, x):
353 pat = getstring(x, _("adds wants a pattern"))
356 pat = getstring(x, _("adds wants a pattern"))
354 return checkstatus(repo, subset, pat, 1)
357 return checkstatus(repo, subset, pat, 1)
355
358
356 def removes(repo, subset, x):
359 def removes(repo, subset, x):
357 pat = getstring(x, _("removes wants a pattern"))
360 pat = getstring(x, _("removes wants a pattern"))
358 return checkstatus(repo, subset, pat, 2)
361 return checkstatus(repo, subset, pat, 2)
359
362
360 def merge(repo, subset, x):
363 def merge(repo, subset, x):
361 getargs(x, 0, 0, _("merge takes no arguments"))
364 getargs(x, 0, 0, _("merge takes no arguments"))
362 cl = repo.changelog
365 cl = repo.changelog
363 return [r for r in subset if cl.parentrevs(r)[1] != -1]
366 return [r for r in subset if cl.parentrevs(r)[1] != -1]
364
367
365 def closed(repo, subset, x):
368 def closed(repo, subset, x):
366 getargs(x, 0, 0, _("closed takes no arguments"))
369 getargs(x, 0, 0, _("closed takes no arguments"))
367 return [r for r in subset if repo[r].extra().get('close')]
370 return [r for r in subset if repo[r].extra().get('close')]
368
371
369 def head(repo, subset, x):
372 def head(repo, subset, x):
370 getargs(x, 0, 0, _("head takes no arguments"))
373 getargs(x, 0, 0, _("head takes no arguments"))
371 hs = set()
374 hs = set()
372 for b, ls in repo.branchmap().iteritems():
375 for b, ls in repo.branchmap().iteritems():
373 hs.update(repo[h].rev() for h in ls)
376 hs.update(repo[h].rev() for h in ls)
374 return [r for r in subset if r in hs]
377 return [r for r in subset if r in hs]
375
378
376 def reverse(repo, subset, x):
379 def reverse(repo, subset, x):
377 l = getset(repo, subset, x)
380 l = getset(repo, subset, x)
378 l.reverse()
381 l.reverse()
379 return l
382 return l
380
383
381 def present(repo, subset, x):
384 def present(repo, subset, x):
382 try:
385 try:
383 return getset(repo, subset, x)
386 return getset(repo, subset, x)
384 except error.RepoLookupError:
387 except error.RepoLookupError:
385 return []
388 return []
386
389
387 def sort(repo, subset, x):
390 def sort(repo, subset, x):
388 l = getargs(x, 1, 2, _("sort wants one or two arguments"))
391 l = getargs(x, 1, 2, _("sort wants one or two arguments"))
389 keys = "rev"
392 keys = "rev"
390 if len(l) == 2:
393 if len(l) == 2:
391 keys = getstring(l[1], _("sort spec must be a string"))
394 keys = getstring(l[1], _("sort spec must be a string"))
392
395
393 s = l[0]
396 s = l[0]
394 keys = keys.split()
397 keys = keys.split()
395 l = []
398 l = []
396 def invert(s):
399 def invert(s):
397 return "".join(chr(255 - ord(c)) for c in s)
400 return "".join(chr(255 - ord(c)) for c in s)
398 for r in getset(repo, subset, s):
401 for r in getset(repo, subset, s):
399 c = repo[r]
402 c = repo[r]
400 e = []
403 e = []
401 for k in keys:
404 for k in keys:
402 if k == 'rev':
405 if k == 'rev':
403 e.append(r)
406 e.append(r)
404 elif k == '-rev':
407 elif k == '-rev':
405 e.append(-r)
408 e.append(-r)
406 elif k == 'branch':
409 elif k == 'branch':
407 e.append(c.branch())
410 e.append(c.branch())
408 elif k == '-branch':
411 elif k == '-branch':
409 e.append(invert(c.branch()))
412 e.append(invert(c.branch()))
410 elif k == 'desc':
413 elif k == 'desc':
411 e.append(c.description())
414 e.append(c.description())
412 elif k == '-desc':
415 elif k == '-desc':
413 e.append(invert(c.description()))
416 e.append(invert(c.description()))
414 elif k in 'user author':
417 elif k in 'user author':
415 e.append(c.user())
418 e.append(c.user())
416 elif k in '-user -author':
419 elif k in '-user -author':
417 e.append(invert(c.user()))
420 e.append(invert(c.user()))
418 elif k == 'date':
421 elif k == 'date':
419 e.append(c.date()[0])
422 e.append(c.date()[0])
420 elif k == '-date':
423 elif k == '-date':
421 e.append(-c.date()[0])
424 e.append(-c.date()[0])
422 else:
425 else:
423 raise error.ParseError(_("unknown sort key %r") % k)
426 raise error.ParseError(_("unknown sort key %r") % k)
424 e.append(r)
427 e.append(r)
425 l.append(e)
428 l.append(e)
426 l.sort()
429 l.sort()
427 return [e[-1] for e in l]
430 return [e[-1] for e in l]
428
431
429 def getall(repo, subset, x):
432 def getall(repo, subset, x):
430 getargs(x, 0, 0, _("all takes no arguments"))
433 getargs(x, 0, 0, _("all takes no arguments"))
431 return subset
434 return subset
432
435
433 def heads(repo, subset, x):
436 def heads(repo, subset, x):
434 s = getset(repo, subset, x)
437 s = getset(repo, subset, x)
435 ps = set(parents(repo, subset, x))
438 ps = set(parents(repo, subset, x))
436 return [r for r in s if r not in ps]
439 return [r for r in s if r not in ps]
437
440
438 def roots(repo, subset, x):
441 def roots(repo, subset, x):
439 s = getset(repo, subset, x)
442 s = getset(repo, subset, x)
440 cs = set(children(repo, subset, x))
443 cs = set(children(repo, subset, x))
441 return [r for r in s if r not in cs]
444 return [r for r in s if r not in cs]
442
445
443 def outgoing(repo, subset, x):
446 def outgoing(repo, subset, x):
444 import hg # avoid start-up nasties
447 import hg # avoid start-up nasties
445 l = getargs(x, 0, 1, _("outgoing wants a repository path"))
448 l = getargs(x, 0, 1, _("outgoing wants a repository path"))
446 dest = l and getstring(l[0], _("outgoing wants a repository path")) or ''
449 dest = l and getstring(l[0], _("outgoing wants a repository path")) or ''
447 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
450 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
448 dest, branches = hg.parseurl(dest)
451 dest, branches = hg.parseurl(dest)
449 other = hg.repository(hg.remoteui(repo, {}), dest)
452 other = hg.repository(hg.remoteui(repo, {}), dest)
450 repo.ui.pushbuffer()
453 repo.ui.pushbuffer()
451 o = discovery.findoutgoing(repo, other)
454 o = discovery.findoutgoing(repo, other)
452 repo.ui.popbuffer()
455 repo.ui.popbuffer()
453 cl = repo.changelog
456 cl = repo.changelog
454 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, None)[0]])
457 o = set([cl.rev(r) for r in repo.changelog.nodesbetween(o, None)[0]])
455 return [r for r in subset if r in o]
458 return [r for r in subset if r in o]
456
459
457 def tagged(repo, subset, x):
460 def tagged(repo, subset, x):
458 getargs(x, 0, 0, _("tagged takes no arguments"))
461 getargs(x, 0, 0, _("tagged takes no arguments"))
459 cl = repo.changelog
462 cl = repo.changelog
460 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
463 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
461 return [r for r in subset if r in s]
464 return [r for r in subset if r in s]
462
465
463 symbols = {
466 symbols = {
464 "adds": adds,
467 "adds": adds,
465 "all": getall,
468 "all": getall,
466 "ancestor": ancestor,
469 "ancestor": ancestor,
467 "ancestors": ancestors,
470 "ancestors": ancestors,
468 "author": author,
471 "author": author,
469 "branch": branch,
472 "branch": branch,
470 "children": children,
473 "children": children,
471 "closed": closed,
474 "closed": closed,
472 "contains": contains,
475 "contains": contains,
473 "date": date,
476 "date": date,
474 "descendants": descendants,
477 "descendants": descendants,
475 "file": hasfile,
478 "file": hasfile,
476 "follow": follow,
479 "follow": follow,
477 "grep": grep,
480 "grep": grep,
478 "head": head,
481 "head": head,
479 "heads": heads,
482 "heads": heads,
480 "keyword": keyword,
483 "keyword": keyword,
481 "limit": limit,
484 "limit": limit,
482 "max": maxrev,
485 "max": maxrev,
483 "min": minrev,
486 "min": minrev,
484 "merge": merge,
487 "merge": merge,
485 "modifies": modifies,
488 "modifies": modifies,
486 "outgoing": outgoing,
489 "outgoing": outgoing,
487 "p1": p1,
490 "p1": p1,
488 "p2": p2,
491 "p2": p2,
489 "parents": parents,
492 "parents": parents,
490 "present": present,
493 "present": present,
491 "removes": removes,
494 "removes": removes,
492 "reverse": reverse,
495 "reverse": reverse,
493 "roots": roots,
496 "roots": roots,
494 "sort": sort,
497 "sort": sort,
495 "tagged": tagged,
498 "tagged": tagged,
496 "user": author,
499 "user": author,
497 }
500 }
498
501
499 methods = {
502 methods = {
500 "range": rangeset,
503 "range": rangeset,
501 "string": stringset,
504 "string": stringset,
502 "symbol": symbolset,
505 "symbol": symbolset,
503 "and": andset,
506 "and": andset,
504 "or": orset,
507 "or": orset,
505 "not": notset,
508 "not": notset,
506 "list": listset,
509 "list": listset,
507 "func": func,
510 "func": func,
508 }
511 }
509
512
510 def optimize(x, small):
513 def optimize(x, small):
511 if x == None:
514 if x == None:
512 return 0, x
515 return 0, x
513
516
514 smallbonus = 1
517 smallbonus = 1
515 if small:
518 if small:
516 smallbonus = .5
519 smallbonus = .5
517
520
518 op = x[0]
521 op = x[0]
519 if op == 'minus':
522 if op == 'minus':
520 return optimize(('and', x[1], ('not', x[2])), small)
523 return optimize(('and', x[1], ('not', x[2])), small)
521 elif op == 'dagrange':
524 elif op == 'dagrange':
522 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
525 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
523 ('func', ('symbol', 'ancestors'), x[2])), small)
526 ('func', ('symbol', 'ancestors'), x[2])), small)
524 elif op == 'dagrangepre':
527 elif op == 'dagrangepre':
525 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
528 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
526 elif op == 'dagrangepost':
529 elif op == 'dagrangepost':
527 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
530 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
528 elif op == 'rangepre':
531 elif op == 'rangepre':
529 return optimize(('range', ('string', '0'), x[1]), small)
532 return optimize(('range', ('string', '0'), x[1]), small)
530 elif op == 'rangepost':
533 elif op == 'rangepost':
531 return optimize(('range', x[1], ('string', 'tip')), small)
534 return optimize(('range', x[1], ('string', 'tip')), small)
532 elif op == 'negate':
535 elif op == 'negate':
533 return optimize(('string',
536 return optimize(('string',
534 '-' + getstring(x[1], _("can't negate that"))), small)
537 '-' + getstring(x[1], _("can't negate that"))), small)
535 elif op in 'string symbol negate':
538 elif op in 'string symbol negate':
536 return smallbonus, x # single revisions are small
539 return smallbonus, x # single revisions are small
537 elif op == 'and' or op == 'dagrange':
540 elif op == 'and' or op == 'dagrange':
538 wa, ta = optimize(x[1], True)
541 wa, ta = optimize(x[1], True)
539 wb, tb = optimize(x[2], True)
542 wb, tb = optimize(x[2], True)
540 w = min(wa, wb)
543 w = min(wa, wb)
541 if wa > wb:
544 if wa > wb:
542 return w, (op, tb, ta)
545 return w, (op, tb, ta)
543 return w, (op, ta, tb)
546 return w, (op, ta, tb)
544 elif op == 'or':
547 elif op == 'or':
545 wa, ta = optimize(x[1], False)
548 wa, ta = optimize(x[1], False)
546 wb, tb = optimize(x[2], False)
549 wb, tb = optimize(x[2], False)
547 if wb < wa:
550 if wb < wa:
548 wb, wa = wa, wb
551 wb, wa = wa, wb
549 return max(wa, wb), (op, ta, tb)
552 return max(wa, wb), (op, ta, tb)
550 elif op == 'not':
553 elif op == 'not':
551 o = optimize(x[1], not small)
554 o = optimize(x[1], not small)
552 return o[0], (op, o[1])
555 return o[0], (op, o[1])
553 elif op == 'group':
556 elif op == 'group':
554 return optimize(x[1], small)
557 return optimize(x[1], small)
555 elif op in 'range list':
558 elif op in 'range list':
556 wa, ta = optimize(x[1], small)
559 wa, ta = optimize(x[1], small)
557 wb, tb = optimize(x[2], small)
560 wb, tb = optimize(x[2], small)
558 return wa + wb, (op, ta, tb)
561 return wa + wb, (op, ta, tb)
559 elif op == 'func':
562 elif op == 'func':
560 f = getstring(x[1], _("not a symbol"))
563 f = getstring(x[1], _("not a symbol"))
561 wa, ta = optimize(x[2], small)
564 wa, ta = optimize(x[2], small)
562 if f in "grep date user author keyword branch file":
565 if f in "grep date user author keyword branch file":
563 w = 10 # slow
566 w = 10 # slow
564 elif f in "modifies adds removes outgoing":
567 elif f in "modifies adds removes outgoing":
565 w = 30 # slower
568 w = 30 # slower
566 elif f == "contains":
569 elif f == "contains":
567 w = 100 # very slow
570 w = 100 # very slow
568 elif f == "ancestor":
571 elif f == "ancestor":
569 w = 1 * smallbonus
572 w = 1 * smallbonus
570 elif f == "reverse limit":
573 elif f == "reverse limit":
571 w = 0
574 w = 0
572 elif f in "sort":
575 elif f in "sort":
573 w = 10 # assume most sorts look at changelog
576 w = 10 # assume most sorts look at changelog
574 else:
577 else:
575 w = 1
578 w = 1
576 return w + wa, (op, x[1], ta)
579 return w + wa, (op, x[1], ta)
577 return 1, x
580 return 1, x
578
581
579 parse = parser.parser(tokenize, elements).parse
582 parse = parser.parser(tokenize, elements).parse
580
583
581 def match(spec):
584 def match(spec):
582 if not spec:
585 if not spec:
583 raise error.ParseError(_("empty query"))
586 raise error.ParseError(_("empty query"))
584 tree = parse(spec)
587 tree = parse(spec)
585 weight, tree = optimize(tree, True)
588 weight, tree = optimize(tree, True)
586 def mfunc(repo, subset):
589 def mfunc(repo, subset):
587 return getset(repo, subset, tree)
590 return getset(repo, subset, tree)
588 return mfunc
591 return mfunc
@@ -1,223 +1,237
1 $ mkdir test
1 $ mkdir test
2 $ cd test
2 $ cd test
3 $ hg init
3 $ hg init
4 $ echo foo>foo
4 $ echo foo>foo
5 $ hg commit -Am 1 -d '1 0'
5 $ hg commit -Am 1 -d '1 0'
6 adding foo
6 adding foo
7 $ echo bar>bar
7 $ echo bar>bar
8 $ hg commit -Am 2 -d '2 0'
8 $ hg commit -Am 2 -d '2 0'
9 adding bar
9 adding bar
10 $ mkdir baz
10 $ mkdir baz
11 $ echo bletch>baz/bletch
11 $ echo bletch>baz/bletch
12 $ hg commit -Am 3 -d '1000000000 0'
12 $ hg commit -Am 3 -d '1000000000 0'
13 adding baz/bletch
13 adding baz/bletch
14 $ echo "[web]" >> .hg/hgrc
14 $ echo "[web]" >> .hg/hgrc
15 $ echo "name = test-archive" >> .hg/hgrc
15 $ echo "name = test-archive" >> .hg/hgrc
16 $ cp .hg/hgrc .hg/hgrc-base
16 $ cp .hg/hgrc .hg/hgrc-base
17 > test_archtype() {
17 > test_archtype() {
18 > echo "allow_archive = $1" >> .hg/hgrc
18 > echo "allow_archive = $1" >> .hg/hgrc
19 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
19 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
20 > cat hg.pid >> $DAEMON_PIDS
20 > cat hg.pid >> $DAEMON_PIDS
21 > echo % $1 allowed should give 200
21 > echo % $1 allowed should give 200
22 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$2" | head -n 1
22 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$2" | head -n 1
23 > echo % $3 and $4 disallowed should both give 403
23 > echo % $3 and $4 disallowed should both give 403
24 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$3" | head -n 1
24 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$3" | head -n 1
25 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$4" | head -n 1
25 > "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.$4" | head -n 1
26 > "$TESTDIR/killdaemons.py"
26 > "$TESTDIR/killdaemons.py"
27 > cat errors.log
27 > cat errors.log
28 > cp .hg/hgrc-base .hg/hgrc
28 > cp .hg/hgrc-base .hg/hgrc
29 > }
29 > }
30
30
31 check http return codes
31 check http return codes
32
32
33
33
34 $ test_archtype gz tar.gz tar.bz2 zip
34 $ test_archtype gz tar.gz tar.bz2 zip
35 % gz allowed should give 200
35 % gz allowed should give 200
36 200 Script output follows
36 200 Script output follows
37 % tar.bz2 and zip disallowed should both give 403
37 % tar.bz2 and zip disallowed should both give 403
38 403 Archive type not allowed: bz2
38 403 Archive type not allowed: bz2
39 403 Archive type not allowed: zip
39 403 Archive type not allowed: zip
40 $ test_archtype bz2 tar.bz2 zip tar.gz
40 $ test_archtype bz2 tar.bz2 zip tar.gz
41 % bz2 allowed should give 200
41 % bz2 allowed should give 200
42 200 Script output follows
42 200 Script output follows
43 % zip and tar.gz disallowed should both give 403
43 % zip and tar.gz disallowed should both give 403
44 403 Archive type not allowed: zip
44 403 Archive type not allowed: zip
45 403 Archive type not allowed: gz
45 403 Archive type not allowed: gz
46 $ test_archtype zip zip tar.gz tar.bz2
46 $ test_archtype zip zip tar.gz tar.bz2
47 % zip allowed should give 200
47 % zip allowed should give 200
48 200 Script output follows
48 200 Script output follows
49 % tar.gz and tar.bz2 disallowed should both give 403
49 % tar.gz and tar.bz2 disallowed should both give 403
50 403 Archive type not allowed: gz
50 403 Archive type not allowed: gz
51 403 Archive type not allowed: bz2
51 403 Archive type not allowed: bz2
52
52
53 $ echo "allow_archive = gz bz2 zip" >> .hg/hgrc
53 $ echo "allow_archive = gz bz2 zip" >> .hg/hgrc
54 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
54 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
55 $ cat hg.pid >> $DAEMON_PIDS
55 $ cat hg.pid >> $DAEMON_PIDS
56
56
57 invalid arch type should give 404
57 invalid arch type should give 404
58
58
59 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.invalid" | head -n 1
59 $ "$TESTDIR/get-with-headers.py" localhost:$HGPORT "/archive/tip.invalid" | head -n 1
60 404 Unsupported archive type: None
60 404 Unsupported archive type: None
61
61
62 $ TIP=`hg id -v | cut -f1 -d' '`
62 $ TIP=`hg id -v | cut -f1 -d' '`
63 $ QTIP=`hg id -q`
63 $ QTIP=`hg id -q`
64 $ cat > getarchive.py <<EOF
64 $ cat > getarchive.py <<EOF
65 > import os, sys, urllib2
65 > import os, sys, urllib2
66 > try:
66 > try:
67 > # Set stdout to binary mode for win32 platforms
67 > # Set stdout to binary mode for win32 platforms
68 > import msvcrt
68 > import msvcrt
69 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
69 > msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
70 > except ImportError:
70 > except ImportError:
71 > pass
71 > pass
72 > node, archive = sys.argv[1:]
72 > node, archive = sys.argv[1:]
73 > f = urllib2.urlopen('http://127.0.0.1:%s/?cmd=archive;node=%s;type=%s'
73 > f = urllib2.urlopen('http://127.0.0.1:%s/?cmd=archive;node=%s;type=%s'
74 > % (os.environ['HGPORT'], node, archive))
74 > % (os.environ['HGPORT'], node, archive))
75 > sys.stdout.write(f.read())
75 > sys.stdout.write(f.read())
76 > EOF
76 > EOF
77 $ python getarchive.py "$TIP" gz | gunzip | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
77 $ python getarchive.py "$TIP" gz | gunzip | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
78 test-archive-TIP/.hg_archival.txt
78 test-archive-TIP/.hg_archival.txt
79 test-archive-TIP/bar
79 test-archive-TIP/bar
80 test-archive-TIP/baz/bletch
80 test-archive-TIP/baz/bletch
81 test-archive-TIP/foo
81 test-archive-TIP/foo
82 $ python getarchive.py "$TIP" bz2 | bunzip2 | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
82 $ python getarchive.py "$TIP" bz2 | bunzip2 | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
83 test-archive-TIP/.hg_archival.txt
83 test-archive-TIP/.hg_archival.txt
84 test-archive-TIP/bar
84 test-archive-TIP/bar
85 test-archive-TIP/baz/bletch
85 test-archive-TIP/baz/bletch
86 test-archive-TIP/foo
86 test-archive-TIP/foo
87 $ python getarchive.py "$TIP" zip > archive.zip
87 $ python getarchive.py "$TIP" zip > archive.zip
88 $ unzip -t archive.zip | sed "s/$QTIP/TIP/"
88 $ unzip -t archive.zip | sed "s/$QTIP/TIP/"
89 Archive: archive.zip
89 Archive: archive.zip
90 testing: test-archive-TIP/.hg_archival.txt OK
90 testing: test-archive-TIP/.hg_archival.txt OK
91 testing: test-archive-TIP/bar OK
91 testing: test-archive-TIP/bar OK
92 testing: test-archive-TIP/baz/bletch OK
92 testing: test-archive-TIP/baz/bletch OK
93 testing: test-archive-TIP/foo OK
93 testing: test-archive-TIP/foo OK
94 No errors detected in compressed data of archive.zip.
94 No errors detected in compressed data of archive.zip.
95
95
96 $ "$TESTDIR/killdaemons.py"
96 $ "$TESTDIR/killdaemons.py"
97
97
98 $ hg archive -t tar test.tar
98 $ hg archive -t tar test.tar
99 $ tar tf test.tar
99 $ tar tf test.tar
100 test/.hg_archival.txt
100 test/.hg_archival.txt
101 test/bar
101 test/bar
102 test/baz/bletch
102 test/baz/bletch
103 test/foo
103 test/foo
104
104
105 $ hg archive -t tbz2 -X baz test.tar.bz2
105 $ hg archive -t tbz2 -X baz test.tar.bz2
106 $ bunzip2 -dc test.tar.bz2 | tar tf - 2>/dev/null
106 $ bunzip2 -dc test.tar.bz2 | tar tf - 2>/dev/null
107 test/.hg_archival.txt
107 test/.hg_archival.txt
108 test/bar
108 test/bar
109 test/foo
109 test/foo
110
110
111 $ hg archive -t tgz -p %b-%h test-%h.tar.gz
111 $ hg archive -t tgz -p %b-%h test-%h.tar.gz
112 $ gzip -dc test-$QTIP.tar.gz | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
112 $ gzip -dc test-$QTIP.tar.gz | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
113 test-TIP/.hg_archival.txt
113 test-TIP/.hg_archival.txt
114 test-TIP/bar
114 test-TIP/bar
115 test-TIP/baz/bletch
115 test-TIP/baz/bletch
116 test-TIP/foo
116 test-TIP/foo
117
117
118 $ hg archive autodetected_test.tar
118 $ hg archive autodetected_test.tar
119 $ tar tf autodetected_test.tar
119 $ tar tf autodetected_test.tar
120 autodetected_test/.hg_archival.txt
120 autodetected_test/.hg_archival.txt
121 autodetected_test/bar
121 autodetected_test/bar
122 autodetected_test/baz/bletch
122 autodetected_test/baz/bletch
123 autodetected_test/foo
123 autodetected_test/foo
124
124
125 The '-t' should override autodetection
125 The '-t' should override autodetection
126
126
127 $ hg archive -t tar autodetect_override_test.zip
127 $ hg archive -t tar autodetect_override_test.zip
128 $ tar tf autodetect_override_test.zip
128 $ tar tf autodetect_override_test.zip
129 autodetect_override_test.zip/.hg_archival.txt
129 autodetect_override_test.zip/.hg_archival.txt
130 autodetect_override_test.zip/bar
130 autodetect_override_test.zip/bar
131 autodetect_override_test.zip/baz/bletch
131 autodetect_override_test.zip/baz/bletch
132 autodetect_override_test.zip/foo
132 autodetect_override_test.zip/foo
133
133
134 $ for ext in tar tar.gz tgz tar.bz2 tbz2 zip; do
134 $ for ext in tar tar.gz tgz tar.bz2 tbz2 zip; do
135 > hg archive auto_test.$ext
135 > hg archive auto_test.$ext
136 > if [ -d auto_test.$ext ]; then
136 > if [ -d auto_test.$ext ]; then
137 > echo "extension $ext was not autodetected."
137 > echo "extension $ext was not autodetected."
138 > fi
138 > fi
139 > done
139 > done
140
140
141 $ cat > md5comp.py <<EOF
141 $ cat > md5comp.py <<EOF
142 > try:
142 > try:
143 > from hashlib import md5
143 > from hashlib import md5
144 > except ImportError:
144 > except ImportError:
145 > from md5 import md5
145 > from md5 import md5
146 > import sys
146 > import sys
147 > f1, f2 = sys.argv[1:3]
147 > f1, f2 = sys.argv[1:3]
148 > h1 = md5(file(f1, 'rb').read()).hexdigest()
148 > h1 = md5(file(f1, 'rb').read()).hexdigest()
149 > h2 = md5(file(f2, 'rb').read()).hexdigest()
149 > h2 = md5(file(f2, 'rb').read()).hexdigest()
150 > print h1 == h2 or "md5 differ: " + repr((h1, h2))
150 > print h1 == h2 or "md5 differ: " + repr((h1, h2))
151 > EOF
151 > EOF
152
152
153 archive name is stored in the archive, so create similar
153 archive name is stored in the archive, so create similar
154
154
155 archives and rename them afterwards.
155 archives and rename them afterwards.
156
156
157 $ hg archive -t tgz tip.tar.gz
157 $ hg archive -t tgz tip.tar.gz
158 $ mv tip.tar.gz tip1.tar.gz
158 $ mv tip.tar.gz tip1.tar.gz
159 $ sleep 1
159 $ sleep 1
160 $ hg archive -t tgz tip.tar.gz
160 $ hg archive -t tgz tip.tar.gz
161 $ mv tip.tar.gz tip2.tar.gz
161 $ mv tip.tar.gz tip2.tar.gz
162 $ python md5comp.py tip1.tar.gz tip2.tar.gz
162 $ python md5comp.py tip1.tar.gz tip2.tar.gz
163 True
163 True
164
164
165 $ hg archive -t zip -p /illegal test.zip
165 $ hg archive -t zip -p /illegal test.zip
166 abort: archive prefix contains illegal components
166 abort: archive prefix contains illegal components
167 [255]
167 [255]
168 $ hg archive -t zip -p very/../bad test.zip
168 $ hg archive -t zip -p very/../bad test.zip
169
169
170 $ hg archive --config ui.archivemeta=false -t zip -r 2 test.zip
170 $ hg archive --config ui.archivemeta=false -t zip -r 2 test.zip
171 $ unzip -t test.zip
171 $ unzip -t test.zip
172 Archive: test.zip
172 Archive: test.zip
173 testing: test/bar OK
173 testing: test/bar OK
174 testing: test/baz/bletch OK
174 testing: test/baz/bletch OK
175 testing: test/foo OK
175 testing: test/foo OK
176 No errors detected in compressed data of test.zip.
176 No errors detected in compressed data of test.zip.
177
177
178 $ hg archive -t tar - | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
178 $ hg archive -t tar - | tar tf - 2>/dev/null | sed "s/$QTIP/TIP/"
179 test-TIP/.hg_archival.txt
179 test-TIP/.hg_archival.txt
180 test-TIP/bar
180 test-TIP/bar
181 test-TIP/baz/bletch
181 test-TIP/baz/bletch
182 test-TIP/foo
182 test-TIP/foo
183
183
184 $ hg archive -r 0 -t tar rev-%r.tar
184 $ hg archive -r 0 -t tar rev-%r.tar
185 $ if [ -f rev-0.tar ]; then
185 $ if [ -f rev-0.tar ]; then
186 $ fi
186 $ fi
187
187
188 test .hg_archival.txt
188 test .hg_archival.txt
189
189
190 $ hg archive ../test-tags
190 $ hg archive ../test-tags
191 $ cat ../test-tags/.hg_archival.txt
191 $ cat ../test-tags/.hg_archival.txt
192 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
192 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
193 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
193 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
194 branch: default
194 branch: default
195 latesttag: null
195 latesttag: null
196 latesttagdistance: 3
196 latesttagdistance: 3
197 $ hg tag -r 2 mytag
197 $ hg tag -r 2 mytag
198 $ hg tag -r 2 anothertag
198 $ hg tag -r 2 anothertag
199 $ hg archive -r 2 ../test-lasttag
199 $ hg archive -r 2 ../test-lasttag
200 $ cat ../test-lasttag/.hg_archival.txt
200 $ cat ../test-lasttag/.hg_archival.txt
201 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
201 repo: daa7f7c60e0a224faa4ff77ca41b2760562af264
202 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
202 node: 2c0277f05ed49d1c8328fb9ba92fba7a5ebcb33e
203 branch: default
203 branch: default
204 tag: anothertag
204 tag: anothertag
205 tag: mytag
205 tag: mytag
206
206
207 $ hg archive -t bogus test.bogus
207 $ hg archive -t bogus test.bogus
208 abort: unknown archive type 'bogus'
208 abort: unknown archive type 'bogus'
209 [255]
209 [255]
210
210
211 server errors
211 server errors
212
212
213 $ cat errors.log
213 $ cat errors.log
214
214
215 empty repo
215 empty repo
216
216
217 $ hg init ../empty
217 $ hg init ../empty
218 $ cd ../empty
218 $ cd ../empty
219 $ hg archive ../test-empty
219 $ hg archive ../test-empty
220 abort: no working directory: please specify a revision
220 abort: no working directory: please specify a revision
221 [255]
221 [255]
222 old file -- date clamped to 1980
223
224 $ touch -d 1975-01-01 old
225 $ hg add old
226 $ hg commit -m old
227 $ hg archive ../old.zip
228 $ unzip -l ../old.zip
229 Archive: ../old.zip
230 Length Date Time Name
231 --------- ---------- ----- ----
232 147 1980-01-01 00:00 old/.hg_archival.txt
233 0 1980-01-01 00:00 old/old
234 --------- -------
235 147 2 files
222
236
223 $ exit 0
237 $ exit 0
@@ -1,317 +1,321
1 $ HGENCODING=utf-8
1 $ HGENCODING=utf-8
2 $ export HGENCODING
2 $ export HGENCODING
3
3
4 $ try() {
4 $ try() {
5 > hg debugrevspec --debug $@
5 > hg debugrevspec --debug $@
6 > }
6 > }
7
7
8 $ log() {
8 $ log() {
9 > hg log --template '{rev}\n' -r "$1"
9 > hg log --template '{rev}\n' -r "$1"
10 > }
10 > }
11
11
12 $ hg init repo
12 $ hg init repo
13 $ cd repo
13 $ cd repo
14
14
15 $ echo a > a
15 $ echo a > a
16 $ hg branch a
16 $ hg branch a
17 marked working directory as branch a
17 marked working directory as branch a
18 $ hg ci -Aqm0
18 $ hg ci -Aqm0
19
19
20 $ echo b > b
20 $ echo b > b
21 $ hg branch b
21 $ hg branch b
22 marked working directory as branch b
22 marked working directory as branch b
23 $ hg ci -Aqm1
23 $ hg ci -Aqm1
24
24
25 $ rm a
25 $ rm a
26 $ hg branch a-b-c-
26 $ hg branch a-b-c-
27 marked working directory as branch a-b-c-
27 marked working directory as branch a-b-c-
28 $ hg ci -Aqm2 -u Bob
28 $ hg ci -Aqm2 -u Bob
29
29
30 $ hg co 1
30 $ hg co 1
31 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
31 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
32 $ hg branch +a+b+c+
32 $ hg branch +a+b+c+
33 marked working directory as branch +a+b+c+
33 marked working directory as branch +a+b+c+
34 $ hg ci -Aqm3
34 $ hg ci -Aqm3
35
35
36 $ hg co 2 # interleave
36 $ hg co 2 # interleave
37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
38 $ echo bb > b
38 $ echo bb > b
39 $ hg branch -- -a-b-c-
39 $ hg branch -- -a-b-c-
40 marked working directory as branch -a-b-c-
40 marked working directory as branch -a-b-c-
41 $ hg ci -Aqm4 -d "May 12 2005"
41 $ hg ci -Aqm4 -d "May 12 2005"
42
42
43 $ hg co 3
43 $ hg co 3
44 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
44 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 $ hg branch /a/b/c/
45 $ hg branch /a/b/c/
46 marked working directory as branch /a/b/c/
46 marked working directory as branch /a/b/c/
47 $ hg ci -Aqm"5 bug"
47 $ hg ci -Aqm"5 bug"
48
48
49 $ hg merge 4
49 $ hg merge 4
50 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
50 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
51 (branch merge, don't forget to commit)
51 (branch merge, don't forget to commit)
52 $ hg branch _a_b_c_
52 $ hg branch _a_b_c_
53 marked working directory as branch _a_b_c_
53 marked working directory as branch _a_b_c_
54 $ hg ci -Aqm"6 issue619"
54 $ hg ci -Aqm"6 issue619"
55
55
56 $ hg branch .a.b.c.
56 $ hg branch .a.b.c.
57 marked working directory as branch .a.b.c.
57 marked working directory as branch .a.b.c.
58 $ hg ci -Aqm7
58 $ hg ci -Aqm7
59
59
60 $ hg branch all
60 $ hg branch all
61 marked working directory as branch all
61 marked working directory as branch all
62 $ hg ci --close-branch -Aqm8
62 $ hg ci --close-branch -Aqm8
63 abort: can only close branch heads
63 abort: can only close branch heads
64 [255]
64 [255]
65
65
66 $ hg co 4
66 $ hg co 4
67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
68 $ hg branch Γ©
68 $ hg branch Γ©
69 marked working directory as branch Γ©
69 marked working directory as branch Γ©
70 $ hg ci -Aqm9
70 $ hg ci -Aqm9
71
71
72 $ hg tag -r6 1.0
72 $ hg tag -r6 1.0
73
73
74 $ hg clone --quiet -U -r 7 . ../remote1
74 $ hg clone --quiet -U -r 7 . ../remote1
75 $ hg clone --quiet -U -r 8 . ../remote2
75 $ hg clone --quiet -U -r 8 . ../remote2
76 $ echo "[paths]" >> .hg/hgrc
76 $ echo "[paths]" >> .hg/hgrc
77 $ echo "default = ../remote1" >> .hg/hgrc
77 $ echo "default = ../remote1" >> .hg/hgrc
78
78
79 names that should work without quoting
79 names that should work without quoting
80
80
81 $ try a
81 $ try a
82 ('symbol', 'a')
82 ('symbol', 'a')
83 0
83 0
84 $ try b-a
84 $ try b-a
85 ('minus', ('symbol', 'b'), ('symbol', 'a'))
85 ('minus', ('symbol', 'b'), ('symbol', 'a'))
86 1
86 1
87 $ try _a_b_c_
87 $ try _a_b_c_
88 ('symbol', '_a_b_c_')
88 ('symbol', '_a_b_c_')
89 6
89 6
90 $ try _a_b_c_-a
90 $ try _a_b_c_-a
91 ('minus', ('symbol', '_a_b_c_'), ('symbol', 'a'))
91 ('minus', ('symbol', '_a_b_c_'), ('symbol', 'a'))
92 6
92 6
93 $ try .a.b.c.
93 $ try .a.b.c.
94 ('symbol', '.a.b.c.')
94 ('symbol', '.a.b.c.')
95 7
95 7
96 $ try .a.b.c.-a
96 $ try .a.b.c.-a
97 ('minus', ('symbol', '.a.b.c.'), ('symbol', 'a'))
97 ('minus', ('symbol', '.a.b.c.'), ('symbol', 'a'))
98 7
98 7
99 $ try -- '-a-b-c-' # complains
99 $ try -- '-a-b-c-' # complains
100 hg: parse error at 7: not a prefix: end
100 hg: parse error at 7: not a prefix: end
101 [255]
101 [255]
102 $ log -a-b-c- # succeeds with fallback
102 $ log -a-b-c- # succeeds with fallback
103 4
103 4
104 $ try -- -a-b-c--a # complains
104 $ try -- -a-b-c--a # complains
105 ('minus', ('minus', ('minus', ('negate', ('symbol', 'a')), ('symbol', 'b')), ('symbol', 'c')), ('negate', ('symbol', 'a')))
105 ('minus', ('minus', ('minus', ('negate', ('symbol', 'a')), ('symbol', 'b')), ('symbol', 'c')), ('negate', ('symbol', 'a')))
106 abort: unknown revision '-a'!
106 abort: unknown revision '-a'!
107 [255]
107 [255]
108 $ try Γ©
108 $ try Γ©
109 ('symbol', '\xc3\xa9')
109 ('symbol', '\xc3\xa9')
110 9
110 9
111
111
112 quoting needed
112 quoting needed
113
113
114 $ try '"-a-b-c-"-a'
114 $ try '"-a-b-c-"-a'
115 ('minus', ('string', '-a-b-c-'), ('symbol', 'a'))
115 ('minus', ('string', '-a-b-c-'), ('symbol', 'a'))
116 4
116 4
117
117
118 $ log '1 or 2'
118 $ log '1 or 2'
119 1
119 1
120 2
120 2
121 $ log '1|2'
121 $ log '1|2'
122 1
122 1
123 2
123 2
124 $ log '1 and 2'
124 $ log '1 and 2'
125 $ log '1&2'
125 $ log '1&2'
126 $ try '1&2|3' # precedence - and is higher
126 $ try '1&2|3' # precedence - and is higher
127 ('or', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
127 ('or', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
128 3
128 3
129 $ try '1|2&3'
129 $ try '1|2&3'
130 ('or', ('symbol', '1'), ('and', ('symbol', '2'), ('symbol', '3')))
130 ('or', ('symbol', '1'), ('and', ('symbol', '2'), ('symbol', '3')))
131 1
131 1
132 $ try '1&2&3' # associativity
132 $ try '1&2&3' # associativity
133 ('and', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
133 ('and', ('and', ('symbol', '1'), ('symbol', '2')), ('symbol', '3'))
134 $ try '1|(2|3)'
134 $ try '1|(2|3)'
135 ('or', ('symbol', '1'), ('group', ('or', ('symbol', '2'), ('symbol', '3'))))
135 ('or', ('symbol', '1'), ('group', ('or', ('symbol', '2'), ('symbol', '3'))))
136 1
136 1
137 2
137 2
138 3
138 3
139 $ log '1.0' # tag
139 $ log '1.0' # tag
140 6
140 6
141 $ log 'a' # branch
141 $ log 'a' # branch
142 0
142 0
143 $ log '2785f51ee'
143 $ log '2785f51ee'
144 0
144 0
145 $ log 'date(2005)'
145 $ log 'date(2005)'
146 4
146 4
147 $ log 'date(this is a test)'
147 $ log 'date(this is a test)'
148 hg: parse error at 10: unexpected token: symbol
148 hg: parse error at 10: unexpected token: symbol
149 [255]
149 [255]
150 $ log 'date()'
150 $ log 'date()'
151 hg: parse error: date wants a string
151 hg: parse error: date wants a string
152 [255]
152 [255]
153 $ log 'date'
153 $ log 'date'
154 hg: parse error: can't use date here
154 hg: parse error: can't use date here
155 [255]
155 [255]
156 $ log 'date('
156 $ log 'date('
157 hg: parse error at 5: not a prefix: end
157 hg: parse error at 5: not a prefix: end
158 [255]
158 [255]
159 $ log 'date(tip)'
159 $ log 'date(tip)'
160 abort: invalid date: 'tip'
160 abort: invalid date: 'tip'
161 [255]
161 [255]
162 $ log '"date"'
162 $ log '"date"'
163 abort: unknown revision 'date'!
163 abort: unknown revision 'date'!
164 [255]
164 [255]
165 $ log 'date(2005) and 1::'
165 $ log 'date(2005) and 1::'
166 4
166 4
167
167
168 $ log 'ancestor(1)'
168 $ log 'ancestor(1)'
169 hg: parse error: ancestor wants two arguments
169 hg: parse error: ancestor wants two arguments
170 [255]
170 [255]
171 $ log 'ancestor(4,5)'
171 $ log 'ancestor(4,5)'
172 1
172 1
173 $ log 'ancestor(4,5) and 4'
173 $ log 'ancestor(4,5) and 4'
174 $ log 'ancestors(5)'
174 $ log 'ancestors(5)'
175 0
175 0
176 1
176 1
177 3
177 3
178 5
178 5
179 $ log 'author(bob)'
179 $ log 'author(bob)'
180 2
180 2
181 $ log 'branch(Γ©)'
181 $ log 'branch(Γ©)'
182 8
182 8
183 9
183 9
184 $ log 'children(ancestor(4,5))'
184 $ log 'children(ancestor(4,5))'
185 2
185 2
186 3
186 3
187 $ log 'closed()'
187 $ log 'closed()'
188 $ log 'contains(a)'
188 $ log 'contains(a)'
189 0
189 0
190 1
190 1
191 3
191 3
192 5
192 5
193 $ log 'descendants(2 or 3)'
193 $ log 'descendants(2 or 3)'
194 2
194 2
195 3
195 3
196 4
196 4
197 5
197 5
198 6
198 6
199 7
199 7
200 8
200 8
201 9
201 9
202 $ log 'file(b)'
202 $ log 'file(b)'
203 1
203 1
204 4
204 4
205 $ log 'follow()'
205 $ log 'follow()'
206 0
206 0
207 1
207 1
208 2
208 2
209 4
209 4
210 8
210 8
211 9
211 9
212 $ log 'grep("issue\d+")'
212 $ log 'grep("issue\d+")'
213 6
213 6
214 $ try 'grep("(")' # invalid regular expression
215 ('func', ('symbol', 'grep'), ('string', '('))
216 hg: parse error: invalid match pattern: unbalanced parenthesis
217 [255]
214 $ log 'head()'
218 $ log 'head()'
215 0
219 0
216 1
220 1
217 2
221 2
218 3
222 3
219 4
223 4
220 5
224 5
221 6
225 6
222 7
226 7
223 9
227 9
224 $ log 'heads(6::)'
228 $ log 'heads(6::)'
225 7
229 7
226 $ log 'keyword(issue)'
230 $ log 'keyword(issue)'
227 6
231 6
228 $ log 'limit(head(), 1)'
232 $ log 'limit(head(), 1)'
229 0
233 0
230 $ log 'max(contains(a))'
234 $ log 'max(contains(a))'
231 5
235 5
232 $ log 'min(contains(a))'
236 $ log 'min(contains(a))'
233 0
237 0
234 $ log 'merge()'
238 $ log 'merge()'
235 6
239 6
236 $ log 'modifies(b)'
240 $ log 'modifies(b)'
237 4
241 4
238 $ log 'outgoing()'
242 $ log 'outgoing()'
239 8
243 8
240 9
244 9
241 $ log 'outgoing("../remote1")'
245 $ log 'outgoing("../remote1")'
242 8
246 8
243 9
247 9
244 $ log 'outgoing("../remote2")'
248 $ log 'outgoing("../remote2")'
245 3
249 3
246 5
250 5
247 6
251 6
248 7
252 7
249 9
253 9
250 $ log 'p1(merge())'
254 $ log 'p1(merge())'
251 5
255 5
252 $ log 'p2(merge())'
256 $ log 'p2(merge())'
253 4
257 4
254 $ log 'parents(merge())'
258 $ log 'parents(merge())'
255 4
259 4
256 5
260 5
257 $ log 'removes(a)'
261 $ log 'removes(a)'
258 2
262 2
259 6
263 6
260 $ log 'roots(all())'
264 $ log 'roots(all())'
261 0
265 0
262 $ log 'reverse(2 or 3 or 4 or 5)'
266 $ log 'reverse(2 or 3 or 4 or 5)'
263 5
267 5
264 4
268 4
265 3
269 3
266 2
270 2
267 $ log 'sort(limit(reverse(all()), 3))'
271 $ log 'sort(limit(reverse(all()), 3))'
268 7
272 7
269 8
273 8
270 9
274 9
271 $ log 'sort(2 or 3 or 4 or 5, date)'
275 $ log 'sort(2 or 3 or 4 or 5, date)'
272 2
276 2
273 3
277 3
274 5
278 5
275 4
279 4
276 $ log 'tagged()'
280 $ log 'tagged()'
277 6
281 6
278 $ log 'user(bob)'
282 $ log 'user(bob)'
279 2
283 2
280
284
281 $ log '4::8'
285 $ log '4::8'
282 4
286 4
283 8
287 8
284 $ log '4:8'
288 $ log '4:8'
285 4
289 4
286 5
290 5
287 6
291 6
288 7
292 7
289 8
293 8
290
294
291 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
295 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
292 4
296 4
293 2
297 2
294 5
298 5
295
299
296 $ log 'not 0 and 0:2'
300 $ log 'not 0 and 0:2'
297 1
301 1
298 2
302 2
299 $ log 'not 1 and 0:2'
303 $ log 'not 1 and 0:2'
300 0
304 0
301 2
305 2
302 $ log 'not 2 and 0:2'
306 $ log 'not 2 and 0:2'
303 0
307 0
304 1
308 1
305 $ log '(1 and 2)::'
309 $ log '(1 and 2)::'
306 $ log '(1 and 2):'
310 $ log '(1 and 2):'
307 $ log '(1 and 2):3'
311 $ log '(1 and 2):3'
308 $ log 'sort(head(), -rev)'
312 $ log 'sort(head(), -rev)'
309 9
313 9
310 7
314 7
311 6
315 6
312 5
316 5
313 4
317 4
314 3
318 3
315 2
319 2
316 1
320 1
317 0
321 0
General Comments 0
You need to be logged in to leave comments. Login now