##// END OF EJS Templates
util._matcher: unify pattern normalization...
Alexis S. L. Carvalho -
r4192:9814d600 default
parent child Browse files
Show More
@@ -1,1377 +1,1379 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import gettext as _
15 from i18n import gettext as _
16 from demandload import *
16 from demandload import *
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time calendar ConfigParser locale glob")
18 demandload(globals(), "os threading time calendar ConfigParser locale glob")
19
19
20 try:
20 try:
21 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
21 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
22 or "ascii"
22 or "ascii"
23 except locale.Error:
23 except locale.Error:
24 _encoding = 'ascii'
24 _encoding = 'ascii'
25 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
25 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
26 _fallbackencoding = 'ISO-8859-1'
26 _fallbackencoding = 'ISO-8859-1'
27
27
28 def tolocal(s):
28 def tolocal(s):
29 """
29 """
30 Convert a string from internal UTF-8 to local encoding
30 Convert a string from internal UTF-8 to local encoding
31
31
32 All internal strings should be UTF-8 but some repos before the
32 All internal strings should be UTF-8 but some repos before the
33 implementation of locale support may contain latin1 or possibly
33 implementation of locale support may contain latin1 or possibly
34 other character sets. We attempt to decode everything strictly
34 other character sets. We attempt to decode everything strictly
35 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
35 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
36 replace unknown characters.
36 replace unknown characters.
37 """
37 """
38 for e in ('UTF-8', _fallbackencoding):
38 for e in ('UTF-8', _fallbackencoding):
39 try:
39 try:
40 u = s.decode(e) # attempt strict decoding
40 u = s.decode(e) # attempt strict decoding
41 return u.encode(_encoding, "replace")
41 return u.encode(_encoding, "replace")
42 except LookupError, k:
42 except LookupError, k:
43 raise Abort(_("%s, please check your locale settings") % k)
43 raise Abort(_("%s, please check your locale settings") % k)
44 except UnicodeDecodeError:
44 except UnicodeDecodeError:
45 pass
45 pass
46 u = s.decode("utf-8", "replace") # last ditch
46 u = s.decode("utf-8", "replace") # last ditch
47 return u.encode(_encoding, "replace")
47 return u.encode(_encoding, "replace")
48
48
49 def fromlocal(s):
49 def fromlocal(s):
50 """
50 """
51 Convert a string from the local character encoding to UTF-8
51 Convert a string from the local character encoding to UTF-8
52
52
53 We attempt to decode strings using the encoding mode set by
53 We attempt to decode strings using the encoding mode set by
54 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
54 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
55 characters will cause an error message. Other modes include
55 characters will cause an error message. Other modes include
56 'replace', which replaces unknown characters with a special
56 'replace', which replaces unknown characters with a special
57 Unicode character, and 'ignore', which drops the character.
57 Unicode character, and 'ignore', which drops the character.
58 """
58 """
59 try:
59 try:
60 return s.decode(_encoding, _encodingmode).encode("utf-8")
60 return s.decode(_encoding, _encodingmode).encode("utf-8")
61 except UnicodeDecodeError, inst:
61 except UnicodeDecodeError, inst:
62 sub = s[max(0, inst.start-10):inst.start+10]
62 sub = s[max(0, inst.start-10):inst.start+10]
63 raise Abort("decoding near '%s': %s!" % (sub, inst))
63 raise Abort("decoding near '%s': %s!" % (sub, inst))
64 except LookupError, k:
64 except LookupError, k:
65 raise Abort(_("%s, please check your locale settings") % k)
65 raise Abort(_("%s, please check your locale settings") % k)
66
66
67 def locallen(s):
67 def locallen(s):
68 """Find the length in characters of a local string"""
68 """Find the length in characters of a local string"""
69 return len(s.decode(_encoding, "replace"))
69 return len(s.decode(_encoding, "replace"))
70
70
71 def localsub(s, a, b=None):
71 def localsub(s, a, b=None):
72 try:
72 try:
73 u = s.decode(_encoding, _encodingmode)
73 u = s.decode(_encoding, _encodingmode)
74 if b is not None:
74 if b is not None:
75 u = u[a:b]
75 u = u[a:b]
76 else:
76 else:
77 u = u[:a]
77 u = u[:a]
78 return u.encode(_encoding, _encodingmode)
78 return u.encode(_encoding, _encodingmode)
79 except UnicodeDecodeError, inst:
79 except UnicodeDecodeError, inst:
80 sub = s[max(0, inst.start-10), inst.start+10]
80 sub = s[max(0, inst.start-10), inst.start+10]
81 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
81 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
82
82
83 # used by parsedate
83 # used by parsedate
84 defaultdateformats = (
84 defaultdateformats = (
85 '%Y-%m-%d %H:%M:%S',
85 '%Y-%m-%d %H:%M:%S',
86 '%Y-%m-%d %I:%M:%S%p',
86 '%Y-%m-%d %I:%M:%S%p',
87 '%Y-%m-%d %H:%M',
87 '%Y-%m-%d %H:%M',
88 '%Y-%m-%d %I:%M%p',
88 '%Y-%m-%d %I:%M%p',
89 '%Y-%m-%d',
89 '%Y-%m-%d',
90 '%m-%d',
90 '%m-%d',
91 '%m/%d',
91 '%m/%d',
92 '%m/%d/%y',
92 '%m/%d/%y',
93 '%m/%d/%Y',
93 '%m/%d/%Y',
94 '%a %b %d %H:%M:%S %Y',
94 '%a %b %d %H:%M:%S %Y',
95 '%a %b %d %I:%M:%S%p %Y',
95 '%a %b %d %I:%M:%S%p %Y',
96 '%b %d %H:%M:%S %Y',
96 '%b %d %H:%M:%S %Y',
97 '%b %d %I:%M:%S%p %Y',
97 '%b %d %I:%M:%S%p %Y',
98 '%b %d %H:%M:%S',
98 '%b %d %H:%M:%S',
99 '%b %d %I:%M:%S%p',
99 '%b %d %I:%M:%S%p',
100 '%b %d %H:%M',
100 '%b %d %H:%M',
101 '%b %d %I:%M%p',
101 '%b %d %I:%M%p',
102 '%b %d %Y',
102 '%b %d %Y',
103 '%b %d',
103 '%b %d',
104 '%H:%M:%S',
104 '%H:%M:%S',
105 '%I:%M:%SP',
105 '%I:%M:%SP',
106 '%H:%M',
106 '%H:%M',
107 '%I:%M%p',
107 '%I:%M%p',
108 )
108 )
109
109
110 extendeddateformats = defaultdateformats + (
110 extendeddateformats = defaultdateformats + (
111 "%Y",
111 "%Y",
112 "%Y-%m",
112 "%Y-%m",
113 "%b",
113 "%b",
114 "%b %Y",
114 "%b %Y",
115 )
115 )
116
116
117 class SignalInterrupt(Exception):
117 class SignalInterrupt(Exception):
118 """Exception raised on SIGTERM and SIGHUP."""
118 """Exception raised on SIGTERM and SIGHUP."""
119
119
120 # like SafeConfigParser but with case-sensitive keys
120 # like SafeConfigParser but with case-sensitive keys
121 class configparser(ConfigParser.SafeConfigParser):
121 class configparser(ConfigParser.SafeConfigParser):
122 def optionxform(self, optionstr):
122 def optionxform(self, optionstr):
123 return optionstr
123 return optionstr
124
124
125 def cachefunc(func):
125 def cachefunc(func):
126 '''cache the result of function calls'''
126 '''cache the result of function calls'''
127 # XXX doesn't handle keywords args
127 # XXX doesn't handle keywords args
128 cache = {}
128 cache = {}
129 if func.func_code.co_argcount == 1:
129 if func.func_code.co_argcount == 1:
130 # we gain a small amount of time because
130 # we gain a small amount of time because
131 # we don't need to pack/unpack the list
131 # we don't need to pack/unpack the list
132 def f(arg):
132 def f(arg):
133 if arg not in cache:
133 if arg not in cache:
134 cache[arg] = func(arg)
134 cache[arg] = func(arg)
135 return cache[arg]
135 return cache[arg]
136 else:
136 else:
137 def f(*args):
137 def f(*args):
138 if args not in cache:
138 if args not in cache:
139 cache[args] = func(*args)
139 cache[args] = func(*args)
140 return cache[args]
140 return cache[args]
141
141
142 return f
142 return f
143
143
144 def pipefilter(s, cmd):
144 def pipefilter(s, cmd):
145 '''filter string S through command CMD, returning its output'''
145 '''filter string S through command CMD, returning its output'''
146 (pout, pin) = popen2.popen2(cmd, -1, 'b')
146 (pout, pin) = popen2.popen2(cmd, -1, 'b')
147 def writer():
147 def writer():
148 try:
148 try:
149 pin.write(s)
149 pin.write(s)
150 pin.close()
150 pin.close()
151 except IOError, inst:
151 except IOError, inst:
152 if inst.errno != errno.EPIPE:
152 if inst.errno != errno.EPIPE:
153 raise
153 raise
154
154
155 # we should use select instead on UNIX, but this will work on most
155 # we should use select instead on UNIX, but this will work on most
156 # systems, including Windows
156 # systems, including Windows
157 w = threading.Thread(target=writer)
157 w = threading.Thread(target=writer)
158 w.start()
158 w.start()
159 f = pout.read()
159 f = pout.read()
160 pout.close()
160 pout.close()
161 w.join()
161 w.join()
162 return f
162 return f
163
163
164 def tempfilter(s, cmd):
164 def tempfilter(s, cmd):
165 '''filter string S through a pair of temporary files with CMD.
165 '''filter string S through a pair of temporary files with CMD.
166 CMD is used as a template to create the real command to be run,
166 CMD is used as a template to create the real command to be run,
167 with the strings INFILE and OUTFILE replaced by the real names of
167 with the strings INFILE and OUTFILE replaced by the real names of
168 the temporary files generated.'''
168 the temporary files generated.'''
169 inname, outname = None, None
169 inname, outname = None, None
170 try:
170 try:
171 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
171 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
172 fp = os.fdopen(infd, 'wb')
172 fp = os.fdopen(infd, 'wb')
173 fp.write(s)
173 fp.write(s)
174 fp.close()
174 fp.close()
175 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
175 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
176 os.close(outfd)
176 os.close(outfd)
177 cmd = cmd.replace('INFILE', inname)
177 cmd = cmd.replace('INFILE', inname)
178 cmd = cmd.replace('OUTFILE', outname)
178 cmd = cmd.replace('OUTFILE', outname)
179 code = os.system(cmd)
179 code = os.system(cmd)
180 if code: raise Abort(_("command '%s' failed: %s") %
180 if code: raise Abort(_("command '%s' failed: %s") %
181 (cmd, explain_exit(code)))
181 (cmd, explain_exit(code)))
182 return open(outname, 'rb').read()
182 return open(outname, 'rb').read()
183 finally:
183 finally:
184 try:
184 try:
185 if inname: os.unlink(inname)
185 if inname: os.unlink(inname)
186 except: pass
186 except: pass
187 try:
187 try:
188 if outname: os.unlink(outname)
188 if outname: os.unlink(outname)
189 except: pass
189 except: pass
190
190
191 filtertable = {
191 filtertable = {
192 'tempfile:': tempfilter,
192 'tempfile:': tempfilter,
193 'pipe:': pipefilter,
193 'pipe:': pipefilter,
194 }
194 }
195
195
196 def filter(s, cmd):
196 def filter(s, cmd):
197 "filter a string through a command that transforms its input to its output"
197 "filter a string through a command that transforms its input to its output"
198 for name, fn in filtertable.iteritems():
198 for name, fn in filtertable.iteritems():
199 if cmd.startswith(name):
199 if cmd.startswith(name):
200 return fn(s, cmd[len(name):].lstrip())
200 return fn(s, cmd[len(name):].lstrip())
201 return pipefilter(s, cmd)
201 return pipefilter(s, cmd)
202
202
203 def find_in_path(name, path, default=None):
203 def find_in_path(name, path, default=None):
204 '''find name in search path. path can be string (will be split
204 '''find name in search path. path can be string (will be split
205 with os.pathsep), or iterable thing that returns strings. if name
205 with os.pathsep), or iterable thing that returns strings. if name
206 found, return path to name. else return default.'''
206 found, return path to name. else return default.'''
207 if isinstance(path, str):
207 if isinstance(path, str):
208 path = path.split(os.pathsep)
208 path = path.split(os.pathsep)
209 for p in path:
209 for p in path:
210 p_name = os.path.join(p, name)
210 p_name = os.path.join(p, name)
211 if os.path.exists(p_name):
211 if os.path.exists(p_name):
212 return p_name
212 return p_name
213 return default
213 return default
214
214
215 def binary(s):
215 def binary(s):
216 """return true if a string is binary data using diff's heuristic"""
216 """return true if a string is binary data using diff's heuristic"""
217 if s and '\0' in s[:4096]:
217 if s and '\0' in s[:4096]:
218 return True
218 return True
219 return False
219 return False
220
220
221 def unique(g):
221 def unique(g):
222 """return the uniq elements of iterable g"""
222 """return the uniq elements of iterable g"""
223 seen = {}
223 seen = {}
224 l = []
224 l = []
225 for f in g:
225 for f in g:
226 if f not in seen:
226 if f not in seen:
227 seen[f] = 1
227 seen[f] = 1
228 l.append(f)
228 l.append(f)
229 return l
229 return l
230
230
231 class Abort(Exception):
231 class Abort(Exception):
232 """Raised if a command needs to print an error and exit."""
232 """Raised if a command needs to print an error and exit."""
233
233
234 class UnexpectedOutput(Abort):
234 class UnexpectedOutput(Abort):
235 """Raised to print an error with part of output and exit."""
235 """Raised to print an error with part of output and exit."""
236
236
237 def always(fn): return True
237 def always(fn): return True
238 def never(fn): return False
238 def never(fn): return False
239
239
240 def expand_glob(pats):
240 def expand_glob(pats):
241 '''On Windows, expand the implicit globs in a list of patterns'''
241 '''On Windows, expand the implicit globs in a list of patterns'''
242 if os.name != 'nt':
242 if os.name != 'nt':
243 return list(pats)
243 return list(pats)
244 ret = []
244 ret = []
245 for p in pats:
245 for p in pats:
246 kind, name = patkind(p, None)
246 kind, name = patkind(p, None)
247 if kind is None:
247 if kind is None:
248 globbed = glob.glob(name)
248 globbed = glob.glob(name)
249 if globbed:
249 if globbed:
250 ret.extend(globbed)
250 ret.extend(globbed)
251 continue
251 continue
252 # if we couldn't expand the glob, just keep it around
252 # if we couldn't expand the glob, just keep it around
253 ret.append(p)
253 ret.append(p)
254 return ret
254 return ret
255
255
256 def patkind(name, dflt_pat='glob'):
256 def patkind(name, dflt_pat='glob'):
257 """Split a string into an optional pattern kind prefix and the
257 """Split a string into an optional pattern kind prefix and the
258 actual pattern."""
258 actual pattern."""
259 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
259 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
260 if name.startswith(prefix + ':'): return name.split(':', 1)
260 if name.startswith(prefix + ':'): return name.split(':', 1)
261 return dflt_pat, name
261 return dflt_pat, name
262
262
263 def globre(pat, head='^', tail='$'):
263 def globre(pat, head='^', tail='$'):
264 "convert a glob pattern into a regexp"
264 "convert a glob pattern into a regexp"
265 i, n = 0, len(pat)
265 i, n = 0, len(pat)
266 res = ''
266 res = ''
267 group = False
267 group = False
268 def peek(): return i < n and pat[i]
268 def peek(): return i < n and pat[i]
269 while i < n:
269 while i < n:
270 c = pat[i]
270 c = pat[i]
271 i = i+1
271 i = i+1
272 if c == '*':
272 if c == '*':
273 if peek() == '*':
273 if peek() == '*':
274 i += 1
274 i += 1
275 res += '.*'
275 res += '.*'
276 else:
276 else:
277 res += '[^/]*'
277 res += '[^/]*'
278 elif c == '?':
278 elif c == '?':
279 res += '.'
279 res += '.'
280 elif c == '[':
280 elif c == '[':
281 j = i
281 j = i
282 if j < n and pat[j] in '!]':
282 if j < n and pat[j] in '!]':
283 j += 1
283 j += 1
284 while j < n and pat[j] != ']':
284 while j < n and pat[j] != ']':
285 j += 1
285 j += 1
286 if j >= n:
286 if j >= n:
287 res += '\\['
287 res += '\\['
288 else:
288 else:
289 stuff = pat[i:j].replace('\\','\\\\')
289 stuff = pat[i:j].replace('\\','\\\\')
290 i = j + 1
290 i = j + 1
291 if stuff[0] == '!':
291 if stuff[0] == '!':
292 stuff = '^' + stuff[1:]
292 stuff = '^' + stuff[1:]
293 elif stuff[0] == '^':
293 elif stuff[0] == '^':
294 stuff = '\\' + stuff
294 stuff = '\\' + stuff
295 res = '%s[%s]' % (res, stuff)
295 res = '%s[%s]' % (res, stuff)
296 elif c == '{':
296 elif c == '{':
297 group = True
297 group = True
298 res += '(?:'
298 res += '(?:'
299 elif c == '}' and group:
299 elif c == '}' and group:
300 res += ')'
300 res += ')'
301 group = False
301 group = False
302 elif c == ',' and group:
302 elif c == ',' and group:
303 res += '|'
303 res += '|'
304 elif c == '\\':
304 elif c == '\\':
305 p = peek()
305 p = peek()
306 if p:
306 if p:
307 i += 1
307 i += 1
308 res += re.escape(p)
308 res += re.escape(p)
309 else:
309 else:
310 res += re.escape(c)
310 res += re.escape(c)
311 else:
311 else:
312 res += re.escape(c)
312 res += re.escape(c)
313 return head + res + tail
313 return head + res + tail
314
314
315 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
315 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
316
316
317 def pathto(n1, n2):
317 def pathto(n1, n2):
318 '''return the relative path from one place to another.
318 '''return the relative path from one place to another.
319 n1 should use os.sep to separate directories
319 n1 should use os.sep to separate directories
320 n2 should use "/" to separate directories
320 n2 should use "/" to separate directories
321 returns an os.sep-separated path.
321 returns an os.sep-separated path.
322 '''
322 '''
323 if not n1: return localpath(n2)
323 if not n1: return localpath(n2)
324 a, b = n1.split(os.sep), n2.split('/')
324 a, b = n1.split(os.sep), n2.split('/')
325 a.reverse()
325 a.reverse()
326 b.reverse()
326 b.reverse()
327 while a and b and a[-1] == b[-1]:
327 while a and b and a[-1] == b[-1]:
328 a.pop()
328 a.pop()
329 b.pop()
329 b.pop()
330 b.reverse()
330 b.reverse()
331 return os.sep.join((['..'] * len(a)) + b)
331 return os.sep.join((['..'] * len(a)) + b)
332
332
333 def canonpath(root, cwd, myname):
333 def canonpath(root, cwd, myname):
334 """return the canonical path of myname, given cwd and root"""
334 """return the canonical path of myname, given cwd and root"""
335 if root == os.sep:
335 if root == os.sep:
336 rootsep = os.sep
336 rootsep = os.sep
337 elif root.endswith(os.sep):
337 elif root.endswith(os.sep):
338 rootsep = root
338 rootsep = root
339 else:
339 else:
340 rootsep = root + os.sep
340 rootsep = root + os.sep
341 name = myname
341 name = myname
342 if not os.path.isabs(name):
342 if not os.path.isabs(name):
343 name = os.path.join(root, cwd, name)
343 name = os.path.join(root, cwd, name)
344 name = os.path.normpath(name)
344 name = os.path.normpath(name)
345 if name != rootsep and name.startswith(rootsep):
345 if name != rootsep and name.startswith(rootsep):
346 name = name[len(rootsep):]
346 name = name[len(rootsep):]
347 audit_path(name)
347 audit_path(name)
348 return pconvert(name)
348 return pconvert(name)
349 elif name == root:
349 elif name == root:
350 return ''
350 return ''
351 else:
351 else:
352 # Determine whether `name' is in the hierarchy at or beneath `root',
352 # Determine whether `name' is in the hierarchy at or beneath `root',
353 # by iterating name=dirname(name) until that causes no change (can't
353 # by iterating name=dirname(name) until that causes no change (can't
354 # check name == '/', because that doesn't work on windows). For each
354 # check name == '/', because that doesn't work on windows). For each
355 # `name', compare dev/inode numbers. If they match, the list `rel'
355 # `name', compare dev/inode numbers. If they match, the list `rel'
356 # holds the reversed list of components making up the relative file
356 # holds the reversed list of components making up the relative file
357 # name we want.
357 # name we want.
358 root_st = os.stat(root)
358 root_st = os.stat(root)
359 rel = []
359 rel = []
360 while True:
360 while True:
361 try:
361 try:
362 name_st = os.stat(name)
362 name_st = os.stat(name)
363 except OSError:
363 except OSError:
364 break
364 break
365 if samestat(name_st, root_st):
365 if samestat(name_st, root_st):
366 if not rel:
366 if not rel:
367 # name was actually the same as root (maybe a symlink)
367 # name was actually the same as root (maybe a symlink)
368 return ''
368 return ''
369 rel.reverse()
369 rel.reverse()
370 name = os.path.join(*rel)
370 name = os.path.join(*rel)
371 audit_path(name)
371 audit_path(name)
372 return pconvert(name)
372 return pconvert(name)
373 dirname, basename = os.path.split(name)
373 dirname, basename = os.path.split(name)
374 rel.append(basename)
374 rel.append(basename)
375 if dirname == name:
375 if dirname == name:
376 break
376 break
377 name = dirname
377 name = dirname
378
378
379 raise Abort('%s not under root' % myname)
379 raise Abort('%s not under root' % myname)
380
380
381 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], head='', src=None):
381 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], head='', src=None):
382 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
382 return _matcher(canonroot, cwd, names, inc, exc, head, 'glob', src)
383
383
384 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], head='',
384 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], head='',
385 src=None, globbed=False):
385 src=None, globbed=False):
386 if not globbed:
386 if not globbed:
387 names = expand_glob(names)
387 names = expand_glob(names)
388 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src)
388 return _matcher(canonroot, cwd, names, inc, exc, head, 'relpath', src)
389
389
390 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
390 def _matcher(canonroot, cwd, names, inc, exc, head, dflt_pat, src):
391 """build a function to match a set of file patterns
391 """build a function to match a set of file patterns
392
392
393 arguments:
393 arguments:
394 canonroot - the canonical root of the tree you're matching against
394 canonroot - the canonical root of the tree you're matching against
395 cwd - the current working directory, if relevant
395 cwd - the current working directory, if relevant
396 names - patterns to find
396 names - patterns to find
397 inc - patterns to include
397 inc - patterns to include
398 exc - patterns to exclude
398 exc - patterns to exclude
399 head - a regex to prepend to patterns to control whether a match is rooted
399 head - a regex to prepend to patterns to control whether a match is rooted
400 dflt_pat - if a pattern in names has no explicit type, assume this one
400 dflt_pat - if a pattern in names has no explicit type, assume this one
401 src - where these patterns came from (e.g. .hgignore)
401 src - where these patterns came from (e.g. .hgignore)
402
402
403 a pattern is one of:
403 a pattern is one of:
404 'glob:<glob>' - a glob relative to cwd
404 'glob:<glob>' - a glob relative to cwd
405 're:<regexp>' - a regular expression
405 're:<regexp>' - a regular expression
406 'path:<path>' - a path relative to canonroot
406 'path:<path>' - a path relative to canonroot
407 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
407 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
408 'relpath:<path>' - a path relative to cwd
408 'relpath:<path>' - a path relative to cwd
409 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
409 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
410 '<something>' - one of the cases above, selected by the dflt_pat argument
410 '<something>' - one of the cases above, selected by the dflt_pat argument
411
411
412 returns:
412 returns:
413 a 3-tuple containing
413 a 3-tuple containing
414 - list of roots (places where one should start a recursive walk of the fs);
414 - list of roots (places where one should start a recursive walk of the fs);
415 this often matches the explicit non-pattern names passed in, but also
415 this often matches the explicit non-pattern names passed in, but also
416 includes the initial part of glob: patterns that has no glob characters
416 includes the initial part of glob: patterns that has no glob characters
417 - a bool match(filename) function
417 - a bool match(filename) function
418 - a bool indicating if any patterns were passed in
418 - a bool indicating if any patterns were passed in
419
419
420 todo:
420 todo:
421 make head regex a rooted bool
421 make head regex a rooted bool
422 """
422 """
423
423
424 def contains_glob(name):
424 def contains_glob(name):
425 for c in name:
425 for c in name:
426 if c in _globchars: return True
426 if c in _globchars: return True
427 return False
427 return False
428
428
429 def regex(kind, name, tail):
429 def regex(kind, name, tail):
430 '''convert a pattern into a regular expression'''
430 '''convert a pattern into a regular expression'''
431 if not name:
431 if not name:
432 return ''
432 return ''
433 if kind == 're':
433 if kind == 're':
434 return name
434 return name
435 elif kind == 'path':
435 elif kind == 'path':
436 return '^' + re.escape(name) + '(?:/|$)'
436 return '^' + re.escape(name) + '(?:/|$)'
437 elif kind == 'relglob':
437 elif kind == 'relglob':
438 return head + globre(name, '(?:|.*/)', '(?:/|$)')
438 return head + globre(name, '(?:|.*/)', '(?:/|$)')
439 elif kind == 'relpath':
439 elif kind == 'relpath':
440 return head + re.escape(name) + tail
440 return head + re.escape(name) + '(?:/|$)'
441 elif kind == 'relre':
441 elif kind == 'relre':
442 if name.startswith('^'):
442 if name.startswith('^'):
443 return name
443 return name
444 return '.*' + name
444 return '.*' + name
445 return head + globre(name, '', tail)
445 return head + globre(name, '', tail)
446
446
447 def matchfn(pats, tail):
447 def matchfn(pats, tail):
448 """build a matching function from a set of patterns"""
448 """build a matching function from a set of patterns"""
449 if not pats:
449 if not pats:
450 return
450 return
451 matches = []
451 matches = []
452 for k, p in pats:
452 for k, p in pats:
453 try:
453 try:
454 pat = '(?:%s)' % regex(k, p, tail)
454 pat = '(?:%s)' % regex(k, p, tail)
455 matches.append(re.compile(pat).match)
455 matches.append(re.compile(pat).match)
456 except re.error:
456 except re.error:
457 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
457 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
458 else: raise Abort("invalid pattern (%s): %s" % (k, p))
458 else: raise Abort("invalid pattern (%s): %s" % (k, p))
459
459
460 def buildfn(text):
460 def buildfn(text):
461 for m in matches:
461 for m in matches:
462 r = m(text)
462 r = m(text)
463 if r:
463 if r:
464 return r
464 return r
465
465
466 return buildfn
466 return buildfn
467
467
468 def globprefix(pat):
468 def globprefix(pat):
469 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
469 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
470 root = []
470 root = []
471 for p in pat.split('/'):
471 for p in pat.split('/'):
472 if contains_glob(p): break
472 if contains_glob(p): break
473 root.append(p)
473 root.append(p)
474 return '/'.join(root) or '.'
474 return '/'.join(root) or '.'
475
475
476 pats = []
476 def normalizepats(names, default):
477 files = []
477 pats = []
478 roots = []
478 files = []
479 for kind, name in [patkind(p, dflt_pat) for p in names]:
479 roots = []
480 if kind in ('glob', 'relpath'):
480 anypats = False
481 name = canonpath(canonroot, cwd, name)
481 for kind, name in [patkind(p, default) for p in names]:
482 elif kind in ('relglob', 'path'):
482 if kind in ('glob', 'relpath'):
483 name = normpath(name)
483 name = canonpath(canonroot, cwd, name)
484 if kind in ('glob', 're', 'relglob'):
484 elif kind in ('relglob', 'path'):
485 pats.append((kind, name))
485 name = normpath(name)
486 if kind == 'glob':
486 if kind in ('glob', 're', 'relglob', 'relre'):
487 root = globprefix(name)
487 pats.append((kind, name))
488 roots.append(root)
488 anypats = True
489 elif kind in ('relpath', 'path'):
489 if kind == 'glob':
490 files.append((kind, name))
490 root = globprefix(name)
491 roots.append(name)
491 roots.append(root)
492 elif kind == 'relglob':
492 elif kind in ('relpath', 'path'):
493 roots.append('.')
493 files.append((kind, name))
494 roots.append(name)
495 elif kind == 'relglob':
496 roots.append('.')
497 return roots, pats + files, anypats
498
499 roots, pats, anypats = normalizepats(names, dflt_pat)
494
500
495 patmatch = matchfn(pats, '$') or always
501 patmatch = matchfn(pats, '$') or always
496 filematch = matchfn(files, '(?:/|$)') or always
497 incmatch = always
502 incmatch = always
498 if inc:
503 if inc:
499 inckinds = [patkind(canonpath(canonroot, cwd, i)) for i in inc]
504 dummy, inckinds, dummy = normalizepats(inc, 'glob')
500 incmatch = matchfn(inckinds, '(?:/|$)')
505 incmatch = matchfn(inckinds, '(?:/|$)')
501 excmatch = lambda fn: False
506 excmatch = lambda fn: False
502 if exc:
507 if exc:
503 exckinds = [patkind(canonpath(canonroot, cwd, x)) for x in exc]
508 dummy, exckinds, dummy = normalizepats(exc, 'glob')
504 excmatch = matchfn(exckinds, '(?:/|$)')
509 excmatch = matchfn(exckinds, '(?:/|$)')
505
510
506 return (roots,
511 return (roots,
507 lambda fn: (incmatch(fn) and not excmatch(fn) and
512 lambda fn: (incmatch(fn) and not excmatch(fn) and
508 (fn.endswith('/') or
513 (fn.endswith('/') or patmatch(fn))),
509 (not pats and not files) or
514 (inc or exc or anypats) and True)
510 (pats and patmatch(fn)) or
511 (files and filematch(fn)))),
512 (inc or exc or pats) and True)
513
515
514 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
516 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
515 '''enhanced shell command execution.
517 '''enhanced shell command execution.
516 run with environment maybe modified, maybe in different dir.
518 run with environment maybe modified, maybe in different dir.
517
519
518 if command fails and onerr is None, return status. if ui object,
520 if command fails and onerr is None, return status. if ui object,
519 print error message and return status, else raise onerr object as
521 print error message and return status, else raise onerr object as
520 exception.'''
522 exception.'''
521 def py2shell(val):
523 def py2shell(val):
522 'convert python object into string that is useful to shell'
524 'convert python object into string that is useful to shell'
523 if val in (None, False):
525 if val in (None, False):
524 return '0'
526 return '0'
525 if val == True:
527 if val == True:
526 return '1'
528 return '1'
527 return str(val)
529 return str(val)
528 oldenv = {}
530 oldenv = {}
529 for k in environ:
531 for k in environ:
530 oldenv[k] = os.environ.get(k)
532 oldenv[k] = os.environ.get(k)
531 if cwd is not None:
533 if cwd is not None:
532 oldcwd = os.getcwd()
534 oldcwd = os.getcwd()
533 origcmd = cmd
535 origcmd = cmd
534 if os.name == 'nt':
536 if os.name == 'nt':
535 cmd = '"%s"' % cmd
537 cmd = '"%s"' % cmd
536 try:
538 try:
537 for k, v in environ.iteritems():
539 for k, v in environ.iteritems():
538 os.environ[k] = py2shell(v)
540 os.environ[k] = py2shell(v)
539 if cwd is not None and oldcwd != cwd:
541 if cwd is not None and oldcwd != cwd:
540 os.chdir(cwd)
542 os.chdir(cwd)
541 rc = os.system(cmd)
543 rc = os.system(cmd)
542 if rc and onerr:
544 if rc and onerr:
543 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
545 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
544 explain_exit(rc)[0])
546 explain_exit(rc)[0])
545 if errprefix:
547 if errprefix:
546 errmsg = '%s: %s' % (errprefix, errmsg)
548 errmsg = '%s: %s' % (errprefix, errmsg)
547 try:
549 try:
548 onerr.warn(errmsg + '\n')
550 onerr.warn(errmsg + '\n')
549 except AttributeError:
551 except AttributeError:
550 raise onerr(errmsg)
552 raise onerr(errmsg)
551 return rc
553 return rc
552 finally:
554 finally:
553 for k, v in oldenv.iteritems():
555 for k, v in oldenv.iteritems():
554 if v is None:
556 if v is None:
555 del os.environ[k]
557 del os.environ[k]
556 else:
558 else:
557 os.environ[k] = v
559 os.environ[k] = v
558 if cwd is not None and oldcwd != cwd:
560 if cwd is not None and oldcwd != cwd:
559 os.chdir(oldcwd)
561 os.chdir(oldcwd)
560
562
561 def rename(src, dst):
563 def rename(src, dst):
562 """forcibly rename a file"""
564 """forcibly rename a file"""
563 try:
565 try:
564 os.rename(src, dst)
566 os.rename(src, dst)
565 except OSError, err:
567 except OSError, err:
566 # on windows, rename to existing file is not allowed, so we
568 # on windows, rename to existing file is not allowed, so we
567 # must delete destination first. but if file is open, unlink
569 # must delete destination first. but if file is open, unlink
568 # schedules it for delete but does not delete it. rename
570 # schedules it for delete but does not delete it. rename
569 # happens immediately even for open files, so we create
571 # happens immediately even for open files, so we create
570 # temporary file, delete it, rename destination to that name,
572 # temporary file, delete it, rename destination to that name,
571 # then delete that. then rename is safe to do.
573 # then delete that. then rename is safe to do.
572 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
574 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
573 os.close(fd)
575 os.close(fd)
574 os.unlink(temp)
576 os.unlink(temp)
575 os.rename(dst, temp)
577 os.rename(dst, temp)
576 os.unlink(temp)
578 os.unlink(temp)
577 os.rename(src, dst)
579 os.rename(src, dst)
578
580
579 def unlink(f):
581 def unlink(f):
580 """unlink and remove the directory if it is empty"""
582 """unlink and remove the directory if it is empty"""
581 os.unlink(f)
583 os.unlink(f)
582 # try removing directories that might now be empty
584 # try removing directories that might now be empty
583 try:
585 try:
584 os.removedirs(os.path.dirname(f))
586 os.removedirs(os.path.dirname(f))
585 except OSError:
587 except OSError:
586 pass
588 pass
587
589
588 def copyfile(src, dest):
590 def copyfile(src, dest):
589 "copy a file, preserving mode"
591 "copy a file, preserving mode"
590 try:
592 try:
591 shutil.copyfile(src, dest)
593 shutil.copyfile(src, dest)
592 shutil.copymode(src, dest)
594 shutil.copymode(src, dest)
593 except shutil.Error, inst:
595 except shutil.Error, inst:
594 raise Abort(str(inst))
596 raise Abort(str(inst))
595
597
596 def copyfiles(src, dst, hardlink=None):
598 def copyfiles(src, dst, hardlink=None):
597 """Copy a directory tree using hardlinks if possible"""
599 """Copy a directory tree using hardlinks if possible"""
598
600
599 if hardlink is None:
601 if hardlink is None:
600 hardlink = (os.stat(src).st_dev ==
602 hardlink = (os.stat(src).st_dev ==
601 os.stat(os.path.dirname(dst)).st_dev)
603 os.stat(os.path.dirname(dst)).st_dev)
602
604
603 if os.path.isdir(src):
605 if os.path.isdir(src):
604 os.mkdir(dst)
606 os.mkdir(dst)
605 for name in os.listdir(src):
607 for name in os.listdir(src):
606 srcname = os.path.join(src, name)
608 srcname = os.path.join(src, name)
607 dstname = os.path.join(dst, name)
609 dstname = os.path.join(dst, name)
608 copyfiles(srcname, dstname, hardlink)
610 copyfiles(srcname, dstname, hardlink)
609 else:
611 else:
610 if hardlink:
612 if hardlink:
611 try:
613 try:
612 os_link(src, dst)
614 os_link(src, dst)
613 except (IOError, OSError):
615 except (IOError, OSError):
614 hardlink = False
616 hardlink = False
615 shutil.copy(src, dst)
617 shutil.copy(src, dst)
616 else:
618 else:
617 shutil.copy(src, dst)
619 shutil.copy(src, dst)
618
620
619 def audit_path(path):
621 def audit_path(path):
620 """Abort if path contains dangerous components"""
622 """Abort if path contains dangerous components"""
621 parts = os.path.normcase(path).split(os.sep)
623 parts = os.path.normcase(path).split(os.sep)
622 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
624 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
623 or os.pardir in parts):
625 or os.pardir in parts):
624 raise Abort(_("path contains illegal component: %s\n") % path)
626 raise Abort(_("path contains illegal component: %s\n") % path)
625
627
626 def _makelock_file(info, pathname):
628 def _makelock_file(info, pathname):
627 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
629 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
628 os.write(ld, info)
630 os.write(ld, info)
629 os.close(ld)
631 os.close(ld)
630
632
631 def _readlock_file(pathname):
633 def _readlock_file(pathname):
632 return posixfile(pathname).read()
634 return posixfile(pathname).read()
633
635
634 def nlinks(pathname):
636 def nlinks(pathname):
635 """Return number of hardlinks for the given file."""
637 """Return number of hardlinks for the given file."""
636 return os.lstat(pathname).st_nlink
638 return os.lstat(pathname).st_nlink
637
639
638 if hasattr(os, 'link'):
640 if hasattr(os, 'link'):
639 os_link = os.link
641 os_link = os.link
640 else:
642 else:
641 def os_link(src, dst):
643 def os_link(src, dst):
642 raise OSError(0, _("Hardlinks not supported"))
644 raise OSError(0, _("Hardlinks not supported"))
643
645
644 def fstat(fp):
646 def fstat(fp):
645 '''stat file object that may not have fileno method.'''
647 '''stat file object that may not have fileno method.'''
646 try:
648 try:
647 return os.fstat(fp.fileno())
649 return os.fstat(fp.fileno())
648 except AttributeError:
650 except AttributeError:
649 return os.stat(fp.name)
651 return os.stat(fp.name)
650
652
651 posixfile = file
653 posixfile = file
652
654
653 def is_win_9x():
655 def is_win_9x():
654 '''return true if run on windows 95, 98 or me.'''
656 '''return true if run on windows 95, 98 or me.'''
655 try:
657 try:
656 return sys.getwindowsversion()[3] == 1
658 return sys.getwindowsversion()[3] == 1
657 except AttributeError:
659 except AttributeError:
658 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
660 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
659
661
660 getuser_fallback = None
662 getuser_fallback = None
661
663
662 def getuser():
664 def getuser():
663 '''return name of current user'''
665 '''return name of current user'''
664 try:
666 try:
665 return getpass.getuser()
667 return getpass.getuser()
666 except ImportError:
668 except ImportError:
667 # import of pwd will fail on windows - try fallback
669 # import of pwd will fail on windows - try fallback
668 if getuser_fallback:
670 if getuser_fallback:
669 return getuser_fallback()
671 return getuser_fallback()
670 # raised if win32api not available
672 # raised if win32api not available
671 raise Abort(_('user name not available - set USERNAME '
673 raise Abort(_('user name not available - set USERNAME '
672 'environment variable'))
674 'environment variable'))
673
675
674 def username(uid=None):
676 def username(uid=None):
675 """Return the name of the user with the given uid.
677 """Return the name of the user with the given uid.
676
678
677 If uid is None, return the name of the current user."""
679 If uid is None, return the name of the current user."""
678 try:
680 try:
679 import pwd
681 import pwd
680 if uid is None:
682 if uid is None:
681 uid = os.getuid()
683 uid = os.getuid()
682 try:
684 try:
683 return pwd.getpwuid(uid)[0]
685 return pwd.getpwuid(uid)[0]
684 except KeyError:
686 except KeyError:
685 return str(uid)
687 return str(uid)
686 except ImportError:
688 except ImportError:
687 return None
689 return None
688
690
689 def groupname(gid=None):
691 def groupname(gid=None):
690 """Return the name of the group with the given gid.
692 """Return the name of the group with the given gid.
691
693
692 If gid is None, return the name of the current group."""
694 If gid is None, return the name of the current group."""
693 try:
695 try:
694 import grp
696 import grp
695 if gid is None:
697 if gid is None:
696 gid = os.getgid()
698 gid = os.getgid()
697 try:
699 try:
698 return grp.getgrgid(gid)[0]
700 return grp.getgrgid(gid)[0]
699 except KeyError:
701 except KeyError:
700 return str(gid)
702 return str(gid)
701 except ImportError:
703 except ImportError:
702 return None
704 return None
703
705
704 # File system features
706 # File system features
705
707
706 def checkfolding(path):
708 def checkfolding(path):
707 """
709 """
708 Check whether the given path is on a case-sensitive filesystem
710 Check whether the given path is on a case-sensitive filesystem
709
711
710 Requires a path (like /foo/.hg) ending with a foldable final
712 Requires a path (like /foo/.hg) ending with a foldable final
711 directory component.
713 directory component.
712 """
714 """
713 s1 = os.stat(path)
715 s1 = os.stat(path)
714 d, b = os.path.split(path)
716 d, b = os.path.split(path)
715 p2 = os.path.join(d, b.upper())
717 p2 = os.path.join(d, b.upper())
716 if path == p2:
718 if path == p2:
717 p2 = os.path.join(d, b.lower())
719 p2 = os.path.join(d, b.lower())
718 try:
720 try:
719 s2 = os.stat(p2)
721 s2 = os.stat(p2)
720 if s2 == s1:
722 if s2 == s1:
721 return False
723 return False
722 return True
724 return True
723 except:
725 except:
724 return True
726 return True
725
727
726 # Platform specific variants
728 # Platform specific variants
727 if os.name == 'nt':
729 if os.name == 'nt':
728 demandload(globals(), "msvcrt")
730 demandload(globals(), "msvcrt")
729 nulldev = 'NUL:'
731 nulldev = 'NUL:'
730
732
731 class winstdout:
733 class winstdout:
732 '''stdout on windows misbehaves if sent through a pipe'''
734 '''stdout on windows misbehaves if sent through a pipe'''
733
735
734 def __init__(self, fp):
736 def __init__(self, fp):
735 self.fp = fp
737 self.fp = fp
736
738
737 def __getattr__(self, key):
739 def __getattr__(self, key):
738 return getattr(self.fp, key)
740 return getattr(self.fp, key)
739
741
740 def close(self):
742 def close(self):
741 try:
743 try:
742 self.fp.close()
744 self.fp.close()
743 except: pass
745 except: pass
744
746
745 def write(self, s):
747 def write(self, s):
746 try:
748 try:
747 return self.fp.write(s)
749 return self.fp.write(s)
748 except IOError, inst:
750 except IOError, inst:
749 if inst.errno != 0: raise
751 if inst.errno != 0: raise
750 self.close()
752 self.close()
751 raise IOError(errno.EPIPE, 'Broken pipe')
753 raise IOError(errno.EPIPE, 'Broken pipe')
752
754
753 def flush(self):
755 def flush(self):
754 try:
756 try:
755 return self.fp.flush()
757 return self.fp.flush()
756 except IOError, inst:
758 except IOError, inst:
757 if inst.errno != errno.EINVAL: raise
759 if inst.errno != errno.EINVAL: raise
758 self.close()
760 self.close()
759 raise IOError(errno.EPIPE, 'Broken pipe')
761 raise IOError(errno.EPIPE, 'Broken pipe')
760
762
761 sys.stdout = winstdout(sys.stdout)
763 sys.stdout = winstdout(sys.stdout)
762
764
763 def system_rcpath():
765 def system_rcpath():
764 try:
766 try:
765 return system_rcpath_win32()
767 return system_rcpath_win32()
766 except:
768 except:
767 return [r'c:\mercurial\mercurial.ini']
769 return [r'c:\mercurial\mercurial.ini']
768
770
769 def os_rcpath():
771 def os_rcpath():
770 '''return default os-specific hgrc search path'''
772 '''return default os-specific hgrc search path'''
771 path = system_rcpath()
773 path = system_rcpath()
772 path.append(user_rcpath())
774 path.append(user_rcpath())
773 userprofile = os.environ.get('USERPROFILE')
775 userprofile = os.environ.get('USERPROFILE')
774 if userprofile:
776 if userprofile:
775 path.append(os.path.join(userprofile, 'mercurial.ini'))
777 path.append(os.path.join(userprofile, 'mercurial.ini'))
776 return path
778 return path
777
779
778 def user_rcpath():
780 def user_rcpath():
779 '''return os-specific hgrc search path to the user dir'''
781 '''return os-specific hgrc search path to the user dir'''
780 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
782 return os.path.join(os.path.expanduser('~'), 'mercurial.ini')
781
783
782 def parse_patch_output(output_line):
784 def parse_patch_output(output_line):
783 """parses the output produced by patch and returns the file name"""
785 """parses the output produced by patch and returns the file name"""
784 pf = output_line[14:]
786 pf = output_line[14:]
785 if pf[0] == '`':
787 if pf[0] == '`':
786 pf = pf[1:-1] # Remove the quotes
788 pf = pf[1:-1] # Remove the quotes
787 return pf
789 return pf
788
790
789 def testpid(pid):
791 def testpid(pid):
790 '''return False if pid dead, True if running or not known'''
792 '''return False if pid dead, True if running or not known'''
791 return True
793 return True
792
794
793 def is_exec(f, last):
795 def is_exec(f, last):
794 return last
796 return last
795
797
796 def set_exec(f, mode):
798 def set_exec(f, mode):
797 pass
799 pass
798
800
799 def set_binary(fd):
801 def set_binary(fd):
800 msvcrt.setmode(fd.fileno(), os.O_BINARY)
802 msvcrt.setmode(fd.fileno(), os.O_BINARY)
801
803
802 def pconvert(path):
804 def pconvert(path):
803 return path.replace("\\", "/")
805 return path.replace("\\", "/")
804
806
805 def localpath(path):
807 def localpath(path):
806 return path.replace('/', '\\')
808 return path.replace('/', '\\')
807
809
808 def normpath(path):
810 def normpath(path):
809 return pconvert(os.path.normpath(path))
811 return pconvert(os.path.normpath(path))
810
812
811 makelock = _makelock_file
813 makelock = _makelock_file
812 readlock = _readlock_file
814 readlock = _readlock_file
813
815
814 def samestat(s1, s2):
816 def samestat(s1, s2):
815 return False
817 return False
816
818
817 # A sequence of backslashes is special iff it precedes a double quote:
819 # A sequence of backslashes is special iff it precedes a double quote:
818 # - if there's an even number of backslashes, the double quote is not
820 # - if there's an even number of backslashes, the double quote is not
819 # quoted (i.e. it ends the quoted region)
821 # quoted (i.e. it ends the quoted region)
820 # - if there's an odd number of backslashes, the double quote is quoted
822 # - if there's an odd number of backslashes, the double quote is quoted
821 # - in both cases, every pair of backslashes is unquoted into a single
823 # - in both cases, every pair of backslashes is unquoted into a single
822 # backslash
824 # backslash
823 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
825 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
824 # So, to quote a string, we must surround it in double quotes, double
826 # So, to quote a string, we must surround it in double quotes, double
825 # the number of backslashes that preceed double quotes and add another
827 # the number of backslashes that preceed double quotes and add another
826 # backslash before every double quote (being careful with the double
828 # backslash before every double quote (being careful with the double
827 # quote we've appended to the end)
829 # quote we've appended to the end)
828 _quotere = None
830 _quotere = None
829 def shellquote(s):
831 def shellquote(s):
830 global _quotere
832 global _quotere
831 if _quotere is None:
833 if _quotere is None:
832 _quotere = re.compile(r'(\\*)("|\\$)')
834 _quotere = re.compile(r'(\\*)("|\\$)')
833 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
835 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
834
836
835 def explain_exit(code):
837 def explain_exit(code):
836 return _("exited with status %d") % code, code
838 return _("exited with status %d") % code, code
837
839
838 # if you change this stub into a real check, please try to implement the
840 # if you change this stub into a real check, please try to implement the
839 # username and groupname functions above, too.
841 # username and groupname functions above, too.
840 def isowner(fp, st=None):
842 def isowner(fp, st=None):
841 return True
843 return True
842
844
843 try:
845 try:
844 # override functions with win32 versions if possible
846 # override functions with win32 versions if possible
845 from util_win32 import *
847 from util_win32 import *
846 if not is_win_9x():
848 if not is_win_9x():
847 posixfile = posixfile_nt
849 posixfile = posixfile_nt
848 except ImportError:
850 except ImportError:
849 pass
851 pass
850
852
851 else:
853 else:
852 nulldev = '/dev/null'
854 nulldev = '/dev/null'
853
855
854 def rcfiles(path):
856 def rcfiles(path):
855 rcs = [os.path.join(path, 'hgrc')]
857 rcs = [os.path.join(path, 'hgrc')]
856 rcdir = os.path.join(path, 'hgrc.d')
858 rcdir = os.path.join(path, 'hgrc.d')
857 try:
859 try:
858 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
860 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
859 if f.endswith(".rc")])
861 if f.endswith(".rc")])
860 except OSError:
862 except OSError:
861 pass
863 pass
862 return rcs
864 return rcs
863
865
864 def os_rcpath():
866 def os_rcpath():
865 '''return default os-specific hgrc search path'''
867 '''return default os-specific hgrc search path'''
866 path = []
868 path = []
867 # old mod_python does not set sys.argv
869 # old mod_python does not set sys.argv
868 if len(getattr(sys, 'argv', [])) > 0:
870 if len(getattr(sys, 'argv', [])) > 0:
869 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
871 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
870 '/../etc/mercurial'))
872 '/../etc/mercurial'))
871 path.extend(rcfiles('/etc/mercurial'))
873 path.extend(rcfiles('/etc/mercurial'))
872 path.append(os.path.expanduser('~/.hgrc'))
874 path.append(os.path.expanduser('~/.hgrc'))
873 path = [os.path.normpath(f) for f in path]
875 path = [os.path.normpath(f) for f in path]
874 return path
876 return path
875
877
876 def parse_patch_output(output_line):
878 def parse_patch_output(output_line):
877 """parses the output produced by patch and returns the file name"""
879 """parses the output produced by patch and returns the file name"""
878 pf = output_line[14:]
880 pf = output_line[14:]
879 if pf.startswith("'") and pf.endswith("'") and " " in pf:
881 if pf.startswith("'") and pf.endswith("'") and " " in pf:
880 pf = pf[1:-1] # Remove the quotes
882 pf = pf[1:-1] # Remove the quotes
881 return pf
883 return pf
882
884
883 def is_exec(f, last):
885 def is_exec(f, last):
884 """check whether a file is executable"""
886 """check whether a file is executable"""
885 return (os.lstat(f).st_mode & 0100 != 0)
887 return (os.lstat(f).st_mode & 0100 != 0)
886
888
887 def set_exec(f, mode):
889 def set_exec(f, mode):
888 s = os.lstat(f).st_mode
890 s = os.lstat(f).st_mode
889 if (s & 0100 != 0) == mode:
891 if (s & 0100 != 0) == mode:
890 return
892 return
891 if mode:
893 if mode:
892 # Turn on +x for every +r bit when making a file executable
894 # Turn on +x for every +r bit when making a file executable
893 # and obey umask.
895 # and obey umask.
894 umask = os.umask(0)
896 umask = os.umask(0)
895 os.umask(umask)
897 os.umask(umask)
896 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
898 os.chmod(f, s | (s & 0444) >> 2 & ~umask)
897 else:
899 else:
898 os.chmod(f, s & 0666)
900 os.chmod(f, s & 0666)
899
901
900 def set_binary(fd):
902 def set_binary(fd):
901 pass
903 pass
902
904
903 def pconvert(path):
905 def pconvert(path):
904 return path
906 return path
905
907
906 def localpath(path):
908 def localpath(path):
907 return path
909 return path
908
910
909 normpath = os.path.normpath
911 normpath = os.path.normpath
910 samestat = os.path.samestat
912 samestat = os.path.samestat
911
913
912 def makelock(info, pathname):
914 def makelock(info, pathname):
913 try:
915 try:
914 os.symlink(info, pathname)
916 os.symlink(info, pathname)
915 except OSError, why:
917 except OSError, why:
916 if why.errno == errno.EEXIST:
918 if why.errno == errno.EEXIST:
917 raise
919 raise
918 else:
920 else:
919 _makelock_file(info, pathname)
921 _makelock_file(info, pathname)
920
922
921 def readlock(pathname):
923 def readlock(pathname):
922 try:
924 try:
923 return os.readlink(pathname)
925 return os.readlink(pathname)
924 except OSError, why:
926 except OSError, why:
925 if why.errno == errno.EINVAL:
927 if why.errno == errno.EINVAL:
926 return _readlock_file(pathname)
928 return _readlock_file(pathname)
927 else:
929 else:
928 raise
930 raise
929
931
930 def shellquote(s):
932 def shellquote(s):
931 return "'%s'" % s.replace("'", "'\\''")
933 return "'%s'" % s.replace("'", "'\\''")
932
934
933 def testpid(pid):
935 def testpid(pid):
934 '''return False if pid dead, True if running or not sure'''
936 '''return False if pid dead, True if running or not sure'''
935 try:
937 try:
936 os.kill(pid, 0)
938 os.kill(pid, 0)
937 return True
939 return True
938 except OSError, inst:
940 except OSError, inst:
939 return inst.errno != errno.ESRCH
941 return inst.errno != errno.ESRCH
940
942
941 def explain_exit(code):
943 def explain_exit(code):
942 """return a 2-tuple (desc, code) describing a process's status"""
944 """return a 2-tuple (desc, code) describing a process's status"""
943 if os.WIFEXITED(code):
945 if os.WIFEXITED(code):
944 val = os.WEXITSTATUS(code)
946 val = os.WEXITSTATUS(code)
945 return _("exited with status %d") % val, val
947 return _("exited with status %d") % val, val
946 elif os.WIFSIGNALED(code):
948 elif os.WIFSIGNALED(code):
947 val = os.WTERMSIG(code)
949 val = os.WTERMSIG(code)
948 return _("killed by signal %d") % val, val
950 return _("killed by signal %d") % val, val
949 elif os.WIFSTOPPED(code):
951 elif os.WIFSTOPPED(code):
950 val = os.WSTOPSIG(code)
952 val = os.WSTOPSIG(code)
951 return _("stopped by signal %d") % val, val
953 return _("stopped by signal %d") % val, val
952 raise ValueError(_("invalid exit code"))
954 raise ValueError(_("invalid exit code"))
953
955
954 def isowner(fp, st=None):
956 def isowner(fp, st=None):
955 """Return True if the file object f belongs to the current user.
957 """Return True if the file object f belongs to the current user.
956
958
957 The return value of a util.fstat(f) may be passed as the st argument.
959 The return value of a util.fstat(f) may be passed as the st argument.
958 """
960 """
959 if st is None:
961 if st is None:
960 st = fstat(fp)
962 st = fstat(fp)
961 return st.st_uid == os.getuid()
963 return st.st_uid == os.getuid()
962
964
963 def _buildencodefun():
965 def _buildencodefun():
964 e = '_'
966 e = '_'
965 win_reserved = [ord(x) for x in '\\:*?"<>|']
967 win_reserved = [ord(x) for x in '\\:*?"<>|']
966 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
968 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
967 for x in (range(32) + range(126, 256) + win_reserved):
969 for x in (range(32) + range(126, 256) + win_reserved):
968 cmap[chr(x)] = "~%02x" % x
970 cmap[chr(x)] = "~%02x" % x
969 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
971 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
970 cmap[chr(x)] = e + chr(x).lower()
972 cmap[chr(x)] = e + chr(x).lower()
971 dmap = {}
973 dmap = {}
972 for k, v in cmap.iteritems():
974 for k, v in cmap.iteritems():
973 dmap[v] = k
975 dmap[v] = k
974 def decode(s):
976 def decode(s):
975 i = 0
977 i = 0
976 while i < len(s):
978 while i < len(s):
977 for l in xrange(1, 4):
979 for l in xrange(1, 4):
978 try:
980 try:
979 yield dmap[s[i:i+l]]
981 yield dmap[s[i:i+l]]
980 i += l
982 i += l
981 break
983 break
982 except KeyError:
984 except KeyError:
983 pass
985 pass
984 else:
986 else:
985 raise KeyError
987 raise KeyError
986 return (lambda s: "".join([cmap[c] for c in s]),
988 return (lambda s: "".join([cmap[c] for c in s]),
987 lambda s: "".join(list(decode(s))))
989 lambda s: "".join(list(decode(s))))
988
990
989 encodefilename, decodefilename = _buildencodefun()
991 encodefilename, decodefilename = _buildencodefun()
990
992
991 def encodedopener(openerfn, fn):
993 def encodedopener(openerfn, fn):
992 def o(path, *args, **kw):
994 def o(path, *args, **kw):
993 return openerfn(fn(path), *args, **kw)
995 return openerfn(fn(path), *args, **kw)
994 return o
996 return o
995
997
996 def opener(base, audit=True):
998 def opener(base, audit=True):
997 """
999 """
998 return a function that opens files relative to base
1000 return a function that opens files relative to base
999
1001
1000 this function is used to hide the details of COW semantics and
1002 this function is used to hide the details of COW semantics and
1001 remote file access from higher level code.
1003 remote file access from higher level code.
1002 """
1004 """
1003 p = base
1005 p = base
1004 audit_p = audit
1006 audit_p = audit
1005
1007
1006 def mktempcopy(name):
1008 def mktempcopy(name):
1007 d, fn = os.path.split(name)
1009 d, fn = os.path.split(name)
1008 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1010 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1009 os.close(fd)
1011 os.close(fd)
1010 ofp = posixfile(temp, "wb")
1012 ofp = posixfile(temp, "wb")
1011 try:
1013 try:
1012 try:
1014 try:
1013 ifp = posixfile(name, "rb")
1015 ifp = posixfile(name, "rb")
1014 except IOError, inst:
1016 except IOError, inst:
1015 if not getattr(inst, 'filename', None):
1017 if not getattr(inst, 'filename', None):
1016 inst.filename = name
1018 inst.filename = name
1017 raise
1019 raise
1018 for chunk in filechunkiter(ifp):
1020 for chunk in filechunkiter(ifp):
1019 ofp.write(chunk)
1021 ofp.write(chunk)
1020 ifp.close()
1022 ifp.close()
1021 ofp.close()
1023 ofp.close()
1022 except:
1024 except:
1023 try: os.unlink(temp)
1025 try: os.unlink(temp)
1024 except: pass
1026 except: pass
1025 raise
1027 raise
1026 st = os.lstat(name)
1028 st = os.lstat(name)
1027 os.chmod(temp, st.st_mode)
1029 os.chmod(temp, st.st_mode)
1028 return temp
1030 return temp
1029
1031
1030 class atomictempfile(posixfile):
1032 class atomictempfile(posixfile):
1031 """the file will only be copied when rename is called"""
1033 """the file will only be copied when rename is called"""
1032 def __init__(self, name, mode):
1034 def __init__(self, name, mode):
1033 self.__name = name
1035 self.__name = name
1034 self.temp = mktempcopy(name)
1036 self.temp = mktempcopy(name)
1035 posixfile.__init__(self, self.temp, mode)
1037 posixfile.__init__(self, self.temp, mode)
1036 def rename(self):
1038 def rename(self):
1037 if not self.closed:
1039 if not self.closed:
1038 posixfile.close(self)
1040 posixfile.close(self)
1039 rename(self.temp, localpath(self.__name))
1041 rename(self.temp, localpath(self.__name))
1040 def __del__(self):
1042 def __del__(self):
1041 if not self.closed:
1043 if not self.closed:
1042 try:
1044 try:
1043 os.unlink(self.temp)
1045 os.unlink(self.temp)
1044 except: pass
1046 except: pass
1045 posixfile.close(self)
1047 posixfile.close(self)
1046
1048
1047 class atomicfile(atomictempfile):
1049 class atomicfile(atomictempfile):
1048 """the file will only be copied on close"""
1050 """the file will only be copied on close"""
1049 def __init__(self, name, mode):
1051 def __init__(self, name, mode):
1050 atomictempfile.__init__(self, name, mode)
1052 atomictempfile.__init__(self, name, mode)
1051 def close(self):
1053 def close(self):
1052 self.rename()
1054 self.rename()
1053 def __del__(self):
1055 def __del__(self):
1054 self.rename()
1056 self.rename()
1055
1057
1056 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1058 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1057 if audit_p:
1059 if audit_p:
1058 audit_path(path)
1060 audit_path(path)
1059 f = os.path.join(p, path)
1061 f = os.path.join(p, path)
1060
1062
1061 if not text:
1063 if not text:
1062 mode += "b" # for that other OS
1064 mode += "b" # for that other OS
1063
1065
1064 if mode[0] != "r":
1066 if mode[0] != "r":
1065 try:
1067 try:
1066 nlink = nlinks(f)
1068 nlink = nlinks(f)
1067 except OSError:
1069 except OSError:
1068 d = os.path.dirname(f)
1070 d = os.path.dirname(f)
1069 if not os.path.isdir(d):
1071 if not os.path.isdir(d):
1070 os.makedirs(d)
1072 os.makedirs(d)
1071 else:
1073 else:
1072 if atomic:
1074 if atomic:
1073 return atomicfile(f, mode)
1075 return atomicfile(f, mode)
1074 elif atomictemp:
1076 elif atomictemp:
1075 return atomictempfile(f, mode)
1077 return atomictempfile(f, mode)
1076 if nlink > 1:
1078 if nlink > 1:
1077 rename(mktempcopy(f), f)
1079 rename(mktempcopy(f), f)
1078 return posixfile(f, mode)
1080 return posixfile(f, mode)
1079
1081
1080 return o
1082 return o
1081
1083
1082 class chunkbuffer(object):
1084 class chunkbuffer(object):
1083 """Allow arbitrary sized chunks of data to be efficiently read from an
1085 """Allow arbitrary sized chunks of data to be efficiently read from an
1084 iterator over chunks of arbitrary size."""
1086 iterator over chunks of arbitrary size."""
1085
1087
1086 def __init__(self, in_iter, targetsize = 2**16):
1088 def __init__(self, in_iter, targetsize = 2**16):
1087 """in_iter is the iterator that's iterating over the input chunks.
1089 """in_iter is the iterator that's iterating over the input chunks.
1088 targetsize is how big a buffer to try to maintain."""
1090 targetsize is how big a buffer to try to maintain."""
1089 self.in_iter = iter(in_iter)
1091 self.in_iter = iter(in_iter)
1090 self.buf = ''
1092 self.buf = ''
1091 self.targetsize = int(targetsize)
1093 self.targetsize = int(targetsize)
1092 if self.targetsize <= 0:
1094 if self.targetsize <= 0:
1093 raise ValueError(_("targetsize must be greater than 0, was %d") %
1095 raise ValueError(_("targetsize must be greater than 0, was %d") %
1094 targetsize)
1096 targetsize)
1095 self.iterempty = False
1097 self.iterempty = False
1096
1098
1097 def fillbuf(self):
1099 def fillbuf(self):
1098 """Ignore target size; read every chunk from iterator until empty."""
1100 """Ignore target size; read every chunk from iterator until empty."""
1099 if not self.iterempty:
1101 if not self.iterempty:
1100 collector = cStringIO.StringIO()
1102 collector = cStringIO.StringIO()
1101 collector.write(self.buf)
1103 collector.write(self.buf)
1102 for ch in self.in_iter:
1104 for ch in self.in_iter:
1103 collector.write(ch)
1105 collector.write(ch)
1104 self.buf = collector.getvalue()
1106 self.buf = collector.getvalue()
1105 self.iterempty = True
1107 self.iterempty = True
1106
1108
1107 def read(self, l):
1109 def read(self, l):
1108 """Read L bytes of data from the iterator of chunks of data.
1110 """Read L bytes of data from the iterator of chunks of data.
1109 Returns less than L bytes if the iterator runs dry."""
1111 Returns less than L bytes if the iterator runs dry."""
1110 if l > len(self.buf) and not self.iterempty:
1112 if l > len(self.buf) and not self.iterempty:
1111 # Clamp to a multiple of self.targetsize
1113 # Clamp to a multiple of self.targetsize
1112 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1114 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1113 collector = cStringIO.StringIO()
1115 collector = cStringIO.StringIO()
1114 collector.write(self.buf)
1116 collector.write(self.buf)
1115 collected = len(self.buf)
1117 collected = len(self.buf)
1116 for chunk in self.in_iter:
1118 for chunk in self.in_iter:
1117 collector.write(chunk)
1119 collector.write(chunk)
1118 collected += len(chunk)
1120 collected += len(chunk)
1119 if collected >= targetsize:
1121 if collected >= targetsize:
1120 break
1122 break
1121 if collected < targetsize:
1123 if collected < targetsize:
1122 self.iterempty = True
1124 self.iterempty = True
1123 self.buf = collector.getvalue()
1125 self.buf = collector.getvalue()
1124 s, self.buf = self.buf[:l], buffer(self.buf, l)
1126 s, self.buf = self.buf[:l], buffer(self.buf, l)
1125 return s
1127 return s
1126
1128
1127 def filechunkiter(f, size=65536, limit=None):
1129 def filechunkiter(f, size=65536, limit=None):
1128 """Create a generator that produces the data in the file size
1130 """Create a generator that produces the data in the file size
1129 (default 65536) bytes at a time, up to optional limit (default is
1131 (default 65536) bytes at a time, up to optional limit (default is
1130 to read all data). Chunks may be less than size bytes if the
1132 to read all data). Chunks may be less than size bytes if the
1131 chunk is the last chunk in the file, or the file is a socket or
1133 chunk is the last chunk in the file, or the file is a socket or
1132 some other type of file that sometimes reads less data than is
1134 some other type of file that sometimes reads less data than is
1133 requested."""
1135 requested."""
1134 assert size >= 0
1136 assert size >= 0
1135 assert limit is None or limit >= 0
1137 assert limit is None or limit >= 0
1136 while True:
1138 while True:
1137 if limit is None: nbytes = size
1139 if limit is None: nbytes = size
1138 else: nbytes = min(limit, size)
1140 else: nbytes = min(limit, size)
1139 s = nbytes and f.read(nbytes)
1141 s = nbytes and f.read(nbytes)
1140 if not s: break
1142 if not s: break
1141 if limit: limit -= len(s)
1143 if limit: limit -= len(s)
1142 yield s
1144 yield s
1143
1145
1144 def makedate():
1146 def makedate():
1145 lt = time.localtime()
1147 lt = time.localtime()
1146 if lt[8] == 1 and time.daylight:
1148 if lt[8] == 1 and time.daylight:
1147 tz = time.altzone
1149 tz = time.altzone
1148 else:
1150 else:
1149 tz = time.timezone
1151 tz = time.timezone
1150 return time.mktime(lt), tz
1152 return time.mktime(lt), tz
1151
1153
1152 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1154 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1153 """represent a (unixtime, offset) tuple as a localized time.
1155 """represent a (unixtime, offset) tuple as a localized time.
1154 unixtime is seconds since the epoch, and offset is the time zone's
1156 unixtime is seconds since the epoch, and offset is the time zone's
1155 number of seconds away from UTC. if timezone is false, do not
1157 number of seconds away from UTC. if timezone is false, do not
1156 append time zone to string."""
1158 append time zone to string."""
1157 t, tz = date or makedate()
1159 t, tz = date or makedate()
1158 s = time.strftime(format, time.gmtime(float(t) - tz))
1160 s = time.strftime(format, time.gmtime(float(t) - tz))
1159 if timezone:
1161 if timezone:
1160 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1162 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1161 return s
1163 return s
1162
1164
1163 def strdate(string, format, defaults):
1165 def strdate(string, format, defaults):
1164 """parse a localized time string and return a (unixtime, offset) tuple.
1166 """parse a localized time string and return a (unixtime, offset) tuple.
1165 if the string cannot be parsed, ValueError is raised."""
1167 if the string cannot be parsed, ValueError is raised."""
1166 def timezone(string):
1168 def timezone(string):
1167 tz = string.split()[-1]
1169 tz = string.split()[-1]
1168 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1170 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1169 tz = int(tz)
1171 tz = int(tz)
1170 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1172 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1171 return offset
1173 return offset
1172 if tz == "GMT" or tz == "UTC":
1174 if tz == "GMT" or tz == "UTC":
1173 return 0
1175 return 0
1174 return None
1176 return None
1175
1177
1176 # NOTE: unixtime = localunixtime + offset
1178 # NOTE: unixtime = localunixtime + offset
1177 offset, date = timezone(string), string
1179 offset, date = timezone(string), string
1178 if offset != None:
1180 if offset != None:
1179 date = " ".join(string.split()[:-1])
1181 date = " ".join(string.split()[:-1])
1180
1182
1181 # add missing elements from defaults
1183 # add missing elements from defaults
1182 for part in defaults:
1184 for part in defaults:
1183 found = [True for p in part if ("%"+p) in format]
1185 found = [True for p in part if ("%"+p) in format]
1184 if not found:
1186 if not found:
1185 date += "@" + defaults[part]
1187 date += "@" + defaults[part]
1186 format += "@%" + part[0]
1188 format += "@%" + part[0]
1187
1189
1188 timetuple = time.strptime(date, format)
1190 timetuple = time.strptime(date, format)
1189 localunixtime = int(calendar.timegm(timetuple))
1191 localunixtime = int(calendar.timegm(timetuple))
1190 if offset is None:
1192 if offset is None:
1191 # local timezone
1193 # local timezone
1192 unixtime = int(time.mktime(timetuple))
1194 unixtime = int(time.mktime(timetuple))
1193 offset = unixtime - localunixtime
1195 offset = unixtime - localunixtime
1194 else:
1196 else:
1195 unixtime = localunixtime + offset
1197 unixtime = localunixtime + offset
1196 return unixtime, offset
1198 return unixtime, offset
1197
1199
1198 def parsedate(string, formats=None, defaults=None):
1200 def parsedate(string, formats=None, defaults=None):
1199 """parse a localized time string and return a (unixtime, offset) tuple.
1201 """parse a localized time string and return a (unixtime, offset) tuple.
1200 The date may be a "unixtime offset" string or in one of the specified
1202 The date may be a "unixtime offset" string or in one of the specified
1201 formats."""
1203 formats."""
1202 if not string:
1204 if not string:
1203 return 0, 0
1205 return 0, 0
1204 if not formats:
1206 if not formats:
1205 formats = defaultdateformats
1207 formats = defaultdateformats
1206 string = string.strip()
1208 string = string.strip()
1207 try:
1209 try:
1208 when, offset = map(int, string.split(' '))
1210 when, offset = map(int, string.split(' '))
1209 except ValueError:
1211 except ValueError:
1210 # fill out defaults
1212 # fill out defaults
1211 if not defaults:
1213 if not defaults:
1212 defaults = {}
1214 defaults = {}
1213 now = makedate()
1215 now = makedate()
1214 for part in "d mb yY HI M S".split():
1216 for part in "d mb yY HI M S".split():
1215 if part not in defaults:
1217 if part not in defaults:
1216 if part[0] in "HMS":
1218 if part[0] in "HMS":
1217 defaults[part] = "00"
1219 defaults[part] = "00"
1218 elif part[0] in "dm":
1220 elif part[0] in "dm":
1219 defaults[part] = "1"
1221 defaults[part] = "1"
1220 else:
1222 else:
1221 defaults[part] = datestr(now, "%" + part[0], False)
1223 defaults[part] = datestr(now, "%" + part[0], False)
1222
1224
1223 for format in formats:
1225 for format in formats:
1224 try:
1226 try:
1225 when, offset = strdate(string, format, defaults)
1227 when, offset = strdate(string, format, defaults)
1226 except ValueError:
1228 except ValueError:
1227 pass
1229 pass
1228 else:
1230 else:
1229 break
1231 break
1230 else:
1232 else:
1231 raise Abort(_('invalid date: %r ') % string)
1233 raise Abort(_('invalid date: %r ') % string)
1232 # validate explicit (probably user-specified) date and
1234 # validate explicit (probably user-specified) date and
1233 # time zone offset. values must fit in signed 32 bits for
1235 # time zone offset. values must fit in signed 32 bits for
1234 # current 32-bit linux runtimes. timezones go from UTC-12
1236 # current 32-bit linux runtimes. timezones go from UTC-12
1235 # to UTC+14
1237 # to UTC+14
1236 if abs(when) > 0x7fffffff:
1238 if abs(when) > 0x7fffffff:
1237 raise Abort(_('date exceeds 32 bits: %d') % when)
1239 raise Abort(_('date exceeds 32 bits: %d') % when)
1238 if offset < -50400 or offset > 43200:
1240 if offset < -50400 or offset > 43200:
1239 raise Abort(_('impossible time zone offset: %d') % offset)
1241 raise Abort(_('impossible time zone offset: %d') % offset)
1240 return when, offset
1242 return when, offset
1241
1243
1242 def matchdate(date):
1244 def matchdate(date):
1243 """Return a function that matches a given date match specifier
1245 """Return a function that matches a given date match specifier
1244
1246
1245 Formats include:
1247 Formats include:
1246
1248
1247 '{date}' match a given date to the accuracy provided
1249 '{date}' match a given date to the accuracy provided
1248
1250
1249 '<{date}' on or before a given date
1251 '<{date}' on or before a given date
1250
1252
1251 '>{date}' on or after a given date
1253 '>{date}' on or after a given date
1252
1254
1253 """
1255 """
1254
1256
1255 def lower(date):
1257 def lower(date):
1256 return parsedate(date, extendeddateformats)[0]
1258 return parsedate(date, extendeddateformats)[0]
1257
1259
1258 def upper(date):
1260 def upper(date):
1259 d = dict(mb="12", HI="23", M="59", S="59")
1261 d = dict(mb="12", HI="23", M="59", S="59")
1260 for days in "31 30 29".split():
1262 for days in "31 30 29".split():
1261 try:
1263 try:
1262 d["d"] = days
1264 d["d"] = days
1263 return parsedate(date, extendeddateformats, d)[0]
1265 return parsedate(date, extendeddateformats, d)[0]
1264 except:
1266 except:
1265 pass
1267 pass
1266 d["d"] = "28"
1268 d["d"] = "28"
1267 return parsedate(date, extendeddateformats, d)[0]
1269 return parsedate(date, extendeddateformats, d)[0]
1268
1270
1269 if date[0] == "<":
1271 if date[0] == "<":
1270 when = upper(date[1:])
1272 when = upper(date[1:])
1271 return lambda x: x <= when
1273 return lambda x: x <= when
1272 elif date[0] == ">":
1274 elif date[0] == ">":
1273 when = lower(date[1:])
1275 when = lower(date[1:])
1274 return lambda x: x >= when
1276 return lambda x: x >= when
1275 elif date[0] == "-":
1277 elif date[0] == "-":
1276 try:
1278 try:
1277 days = int(date[1:])
1279 days = int(date[1:])
1278 except ValueError:
1280 except ValueError:
1279 raise Abort(_("invalid day spec: %s") % date[1:])
1281 raise Abort(_("invalid day spec: %s") % date[1:])
1280 when = makedate()[0] - days * 3600 * 24
1282 when = makedate()[0] - days * 3600 * 24
1281 return lambda x: x >= when
1283 return lambda x: x >= when
1282 elif " to " in date:
1284 elif " to " in date:
1283 a, b = date.split(" to ")
1285 a, b = date.split(" to ")
1284 start, stop = lower(a), upper(b)
1286 start, stop = lower(a), upper(b)
1285 return lambda x: x >= start and x <= stop
1287 return lambda x: x >= start and x <= stop
1286 else:
1288 else:
1287 start, stop = lower(date), upper(date)
1289 start, stop = lower(date), upper(date)
1288 return lambda x: x >= start and x <= stop
1290 return lambda x: x >= start and x <= stop
1289
1291
1290 def shortuser(user):
1292 def shortuser(user):
1291 """Return a short representation of a user name or email address."""
1293 """Return a short representation of a user name or email address."""
1292 f = user.find('@')
1294 f = user.find('@')
1293 if f >= 0:
1295 if f >= 0:
1294 user = user[:f]
1296 user = user[:f]
1295 f = user.find('<')
1297 f = user.find('<')
1296 if f >= 0:
1298 if f >= 0:
1297 user = user[f+1:]
1299 user = user[f+1:]
1298 f = user.find(' ')
1300 f = user.find(' ')
1299 if f >= 0:
1301 if f >= 0:
1300 user = user[:f]
1302 user = user[:f]
1301 f = user.find('.')
1303 f = user.find('.')
1302 if f >= 0:
1304 if f >= 0:
1303 user = user[:f]
1305 user = user[:f]
1304 return user
1306 return user
1305
1307
1306 def ellipsis(text, maxlength=400):
1308 def ellipsis(text, maxlength=400):
1307 """Trim string to at most maxlength (default: 400) characters."""
1309 """Trim string to at most maxlength (default: 400) characters."""
1308 if len(text) <= maxlength:
1310 if len(text) <= maxlength:
1309 return text
1311 return text
1310 else:
1312 else:
1311 return "%s..." % (text[:maxlength-3])
1313 return "%s..." % (text[:maxlength-3])
1312
1314
1313 def walkrepos(path):
1315 def walkrepos(path):
1314 '''yield every hg repository under path, recursively.'''
1316 '''yield every hg repository under path, recursively.'''
1315 def errhandler(err):
1317 def errhandler(err):
1316 if err.filename == path:
1318 if err.filename == path:
1317 raise err
1319 raise err
1318
1320
1319 for root, dirs, files in os.walk(path, onerror=errhandler):
1321 for root, dirs, files in os.walk(path, onerror=errhandler):
1320 for d in dirs:
1322 for d in dirs:
1321 if d == '.hg':
1323 if d == '.hg':
1322 yield root
1324 yield root
1323 dirs[:] = []
1325 dirs[:] = []
1324 break
1326 break
1325
1327
1326 _rcpath = None
1328 _rcpath = None
1327
1329
1328 def rcpath():
1330 def rcpath():
1329 '''return hgrc search path. if env var HGRCPATH is set, use it.
1331 '''return hgrc search path. if env var HGRCPATH is set, use it.
1330 for each item in path, if directory, use files ending in .rc,
1332 for each item in path, if directory, use files ending in .rc,
1331 else use item.
1333 else use item.
1332 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1334 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1333 if no HGRCPATH, use default os-specific path.'''
1335 if no HGRCPATH, use default os-specific path.'''
1334 global _rcpath
1336 global _rcpath
1335 if _rcpath is None:
1337 if _rcpath is None:
1336 if 'HGRCPATH' in os.environ:
1338 if 'HGRCPATH' in os.environ:
1337 _rcpath = []
1339 _rcpath = []
1338 for p in os.environ['HGRCPATH'].split(os.pathsep):
1340 for p in os.environ['HGRCPATH'].split(os.pathsep):
1339 if not p: continue
1341 if not p: continue
1340 if os.path.isdir(p):
1342 if os.path.isdir(p):
1341 for f in os.listdir(p):
1343 for f in os.listdir(p):
1342 if f.endswith('.rc'):
1344 if f.endswith('.rc'):
1343 _rcpath.append(os.path.join(p, f))
1345 _rcpath.append(os.path.join(p, f))
1344 else:
1346 else:
1345 _rcpath.append(p)
1347 _rcpath.append(p)
1346 else:
1348 else:
1347 _rcpath = os_rcpath()
1349 _rcpath = os_rcpath()
1348 return _rcpath
1350 return _rcpath
1349
1351
1350 def bytecount(nbytes):
1352 def bytecount(nbytes):
1351 '''return byte count formatted as readable string, with units'''
1353 '''return byte count formatted as readable string, with units'''
1352
1354
1353 units = (
1355 units = (
1354 (100, 1<<30, _('%.0f GB')),
1356 (100, 1<<30, _('%.0f GB')),
1355 (10, 1<<30, _('%.1f GB')),
1357 (10, 1<<30, _('%.1f GB')),
1356 (1, 1<<30, _('%.2f GB')),
1358 (1, 1<<30, _('%.2f GB')),
1357 (100, 1<<20, _('%.0f MB')),
1359 (100, 1<<20, _('%.0f MB')),
1358 (10, 1<<20, _('%.1f MB')),
1360 (10, 1<<20, _('%.1f MB')),
1359 (1, 1<<20, _('%.2f MB')),
1361 (1, 1<<20, _('%.2f MB')),
1360 (100, 1<<10, _('%.0f KB')),
1362 (100, 1<<10, _('%.0f KB')),
1361 (10, 1<<10, _('%.1f KB')),
1363 (10, 1<<10, _('%.1f KB')),
1362 (1, 1<<10, _('%.2f KB')),
1364 (1, 1<<10, _('%.2f KB')),
1363 (1, 1, _('%.0f bytes')),
1365 (1, 1, _('%.0f bytes')),
1364 )
1366 )
1365
1367
1366 for multiplier, divisor, format in units:
1368 for multiplier, divisor, format in units:
1367 if nbytes >= divisor * multiplier:
1369 if nbytes >= divisor * multiplier:
1368 return format % (nbytes / float(divisor))
1370 return format % (nbytes / float(divisor))
1369 return units[-1][2] % nbytes
1371 return units[-1][2] % nbytes
1370
1372
1371 def drop_scheme(scheme, path):
1373 def drop_scheme(scheme, path):
1372 sc = scheme + ':'
1374 sc = scheme + ':'
1373 if path.startswith(sc):
1375 if path.startswith(sc):
1374 path = path[len(sc):]
1376 path = path[len(sc):]
1375 if path.startswith('//'):
1377 if path.startswith('//'):
1376 path = path[2:]
1378 path = path[2:]
1377 return path
1379 return path
@@ -1,82 +1,92 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 debugwalk()
3 debugwalk()
4 {
4 {
5 echo "hg debugwalk $@"
5 echo "hg debugwalk $@"
6 hg debugwalk "$@"
6 hg debugwalk "$@"
7 echo
7 echo
8 }
8 }
9
9
10 chdir()
10 chdir()
11 {
11 {
12 echo "cd $@"
12 echo "cd $@"
13 cd "$@"
13 cd "$@"
14 echo
14 echo
15 }
15 }
16
16
17 mkdir t
17 mkdir t
18 cd t
18 cd t
19 hg init
19 hg init
20 mkdir -p beans
20 mkdir -p beans
21 for b in kidney navy turtle borlotti black pinto; do
21 for b in kidney navy turtle borlotti black pinto; do
22 echo $b > beans/$b
22 echo $b > beans/$b
23 done
23 done
24 mkdir -p mammals/Procyonidae
24 mkdir -p mammals/Procyonidae
25 for m in cacomistle coatimundi raccoon; do
25 for m in cacomistle coatimundi raccoon; do
26 echo $m > mammals/Procyonidae/$m
26 echo $m > mammals/Procyonidae/$m
27 done
27 done
28 echo skunk > mammals/skunk
28 echo skunk > mammals/skunk
29 echo fennel > fennel
29 echo fennel > fennel
30 echo fenugreek > fenugreek
30 echo fenugreek > fenugreek
31 echo fiddlehead > fiddlehead
31 echo fiddlehead > fiddlehead
32 echo glob:glob > glob:glob
32 echo glob:glob > glob:glob
33 hg addremove
33 hg addremove
34 hg commit -m "commit #0" -d "1000000 0"
34 hg commit -m "commit #0" -d "1000000 0"
35 debugwalk
35 debugwalk
36 debugwalk -I.
36 debugwalk -I.
37 chdir mammals
37 chdir mammals
38 debugwalk
39 debugwalk -X ../beans
40 debugwalk -I '*k'
41 debugwalk -I 'glob:*k'
42 debugwalk -I 'relglob:*k'
43 debugwalk -I 'relglob:*k' .
44 debugwalk -I 're:.*k$'
45 debugwalk -I 'relre:.*k$'
46 debugwalk -I 'path:beans'
47 debugwalk -I 'relpath:../beans'
38 debugwalk .
48 debugwalk .
39 debugwalk -I.
49 debugwalk -I.
40 debugwalk Procyonidae
50 debugwalk Procyonidae
41 chdir Procyonidae
51 chdir Procyonidae
42 debugwalk .
52 debugwalk .
43 debugwalk ..
53 debugwalk ..
44 chdir ..
54 chdir ..
45 debugwalk ../beans
55 debugwalk ../beans
46 debugwalk .
56 debugwalk .
47 debugwalk .hg
57 debugwalk .hg
48 debugwalk ../.hg
58 debugwalk ../.hg
49 chdir ..
59 chdir ..
50 debugwalk -Ibeans
60 debugwalk -Ibeans
51 debugwalk 'glob:mammals/../beans/b*'
61 debugwalk 'glob:mammals/../beans/b*'
52 debugwalk '-X*/Procyonidae' mammals
62 debugwalk '-X*/Procyonidae' mammals
53 debugwalk path:mammals
63 debugwalk path:mammals
54 debugwalk ..
64 debugwalk ..
55 debugwalk beans/../..
65 debugwalk beans/../..
56 debugwalk .hg
66 debugwalk .hg
57 debugwalk beans/../.hg
67 debugwalk beans/../.hg
58 debugwalk beans/../.hg/data
68 debugwalk beans/../.hg/data
59 debugwalk beans/.hg
69 debugwalk beans/.hg
60 # Don't know how to test absolute paths without always getting a false
70 # Don't know how to test absolute paths without always getting a false
61 # error.
71 # error.
62 #debugwalk `pwd`/beans
72 #debugwalk `pwd`/beans
63 #debugwalk `pwd`/..
73 #debugwalk `pwd`/..
64 debugwalk glob:\*
74 debugwalk glob:\*
65 debugwalk 're:.*[kb]$'
75 debugwalk 're:.*[kb]$'
66 debugwalk path:beans/black
76 debugwalk path:beans/black
67 debugwalk path:beans//black
77 debugwalk path:beans//black
68 debugwalk relglob:Procyonidae
78 debugwalk relglob:Procyonidae
69 debugwalk relglob:Procyonidae/ fennel
79 debugwalk relglob:Procyonidae/ fennel
70 debugwalk beans 'glob:beans/*'
80 debugwalk beans 'glob:beans/*'
71 debugwalk 'glob:mamm**'
81 debugwalk 'glob:mamm**'
72 debugwalk 'glob:mamm**' fennel
82 debugwalk 'glob:mamm**' fennel
73 debugwalk 'glob:j*'
83 debugwalk 'glob:j*'
74 debugwalk NOEXIST
84 debugwalk NOEXIST
75 mkfifo fifo
85 mkfifo fifo
76 debugwalk fifo
86 debugwalk fifo
77 rm fenugreek
87 rm fenugreek
78 debugwalk fenugreek
88 debugwalk fenugreek
79 hg rm fenugreek
89 hg rm fenugreek
80 debugwalk fenugreek
90 debugwalk fenugreek
81 touch new
91 touch new
82 debugwalk new
92 debugwalk new
@@ -1,212 +1,278 b''
1 adding beans/black
1 adding beans/black
2 adding beans/borlotti
2 adding beans/borlotti
3 adding beans/kidney
3 adding beans/kidney
4 adding beans/navy
4 adding beans/navy
5 adding beans/pinto
5 adding beans/pinto
6 adding beans/turtle
6 adding beans/turtle
7 adding fennel
7 adding fennel
8 adding fenugreek
8 adding fenugreek
9 adding fiddlehead
9 adding fiddlehead
10 adding glob:glob
10 adding glob:glob
11 adding mammals/Procyonidae/cacomistle
11 adding mammals/Procyonidae/cacomistle
12 adding mammals/Procyonidae/coatimundi
12 adding mammals/Procyonidae/coatimundi
13 adding mammals/Procyonidae/raccoon
13 adding mammals/Procyonidae/raccoon
14 adding mammals/skunk
14 adding mammals/skunk
15 hg debugwalk
15 hg debugwalk
16 f beans/black beans/black
16 f beans/black beans/black
17 f beans/borlotti beans/borlotti
17 f beans/borlotti beans/borlotti
18 f beans/kidney beans/kidney
18 f beans/kidney beans/kidney
19 f beans/navy beans/navy
19 f beans/navy beans/navy
20 f beans/pinto beans/pinto
20 f beans/pinto beans/pinto
21 f beans/turtle beans/turtle
21 f beans/turtle beans/turtle
22 f fennel fennel
22 f fennel fennel
23 f fenugreek fenugreek
23 f fenugreek fenugreek
24 f fiddlehead fiddlehead
24 f fiddlehead fiddlehead
25 f glob:glob glob:glob
25 f glob:glob glob:glob
26 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
26 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
27 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
27 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
28 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
28 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
29 f mammals/skunk mammals/skunk
29 f mammals/skunk mammals/skunk
30
30
31 hg debugwalk -I.
31 hg debugwalk -I.
32 f beans/black beans/black
32 f beans/black beans/black
33 f beans/borlotti beans/borlotti
33 f beans/borlotti beans/borlotti
34 f beans/kidney beans/kidney
34 f beans/kidney beans/kidney
35 f beans/navy beans/navy
35 f beans/navy beans/navy
36 f beans/pinto beans/pinto
36 f beans/pinto beans/pinto
37 f beans/turtle beans/turtle
37 f beans/turtle beans/turtle
38 f fennel fennel
38 f fennel fennel
39 f fenugreek fenugreek
39 f fenugreek fenugreek
40 f fiddlehead fiddlehead
40 f fiddlehead fiddlehead
41 f glob:glob glob:glob
41 f glob:glob glob:glob
42 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
42 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
43 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
43 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
44 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
44 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
45 f mammals/skunk mammals/skunk
45 f mammals/skunk mammals/skunk
46
46
47 cd mammals
47 cd mammals
48
48
49 hg debugwalk
50 f beans/black ../beans/black
51 f beans/borlotti ../beans/borlotti
52 f beans/kidney ../beans/kidney
53 f beans/navy ../beans/navy
54 f beans/pinto ../beans/pinto
55 f beans/turtle ../beans/turtle
56 f fennel ../fennel
57 f fenugreek ../fenugreek
58 f fiddlehead ../fiddlehead
59 f glob:glob ../glob:glob
60 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
61 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
62 f mammals/Procyonidae/raccoon Procyonidae/raccoon
63 f mammals/skunk skunk
64
65 hg debugwalk -X ../beans
66 f fennel ../fennel
67 f fenugreek ../fenugreek
68 f fiddlehead ../fiddlehead
69 f glob:glob ../glob:glob
70 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
71 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
72 f mammals/Procyonidae/raccoon Procyonidae/raccoon
73 f mammals/skunk skunk
74
75 hg debugwalk -I *k
76 m mammals/skunk skunk
77
78 hg debugwalk -I glob:*k
79 m mammals/skunk skunk
80
81 hg debugwalk -I relglob:*k
82 f fenugreek ../fenugreek
83 m beans/black ../beans/black
84 m mammals/skunk skunk
85
86 hg debugwalk -I relglob:*k .
87 f mammals/skunk skunk
88
89 hg debugwalk -I re:.*k$
90 f fenugreek ../fenugreek
91 m beans/black ../beans/black
92 m mammals/skunk skunk
93
94 hg debugwalk -I relre:.*k$
95 f fenugreek ../fenugreek
96 m beans/black ../beans/black
97 m mammals/skunk skunk
98
99 hg debugwalk -I path:beans
100 f beans/black ../beans/black
101 f beans/borlotti ../beans/borlotti
102 f beans/kidney ../beans/kidney
103 f beans/navy ../beans/navy
104 f beans/pinto ../beans/pinto
105 f beans/turtle ../beans/turtle
106
107 hg debugwalk -I relpath:../beans
108 f beans/black ../beans/black
109 f beans/borlotti ../beans/borlotti
110 f beans/kidney ../beans/kidney
111 f beans/navy ../beans/navy
112 f beans/pinto ../beans/pinto
113 f beans/turtle ../beans/turtle
114
49 hg debugwalk .
115 hg debugwalk .
50 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
116 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
51 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
117 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
52 f mammals/Procyonidae/raccoon Procyonidae/raccoon
118 f mammals/Procyonidae/raccoon Procyonidae/raccoon
53 f mammals/skunk skunk
119 f mammals/skunk skunk
54
120
55 hg debugwalk -I.
121 hg debugwalk -I.
56 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
122 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
57 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
123 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
58 f mammals/Procyonidae/raccoon Procyonidae/raccoon
124 f mammals/Procyonidae/raccoon Procyonidae/raccoon
59 f mammals/skunk skunk
125 f mammals/skunk skunk
60
126
61 hg debugwalk Procyonidae
127 hg debugwalk Procyonidae
62 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
128 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
63 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
129 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
64 f mammals/Procyonidae/raccoon Procyonidae/raccoon
130 f mammals/Procyonidae/raccoon Procyonidae/raccoon
65
131
66 cd Procyonidae
132 cd Procyonidae
67
133
68 hg debugwalk .
134 hg debugwalk .
69 f mammals/Procyonidae/cacomistle cacomistle
135 f mammals/Procyonidae/cacomistle cacomistle
70 f mammals/Procyonidae/coatimundi coatimundi
136 f mammals/Procyonidae/coatimundi coatimundi
71 f mammals/Procyonidae/raccoon raccoon
137 f mammals/Procyonidae/raccoon raccoon
72
138
73 hg debugwalk ..
139 hg debugwalk ..
74 f mammals/Procyonidae/cacomistle cacomistle
140 f mammals/Procyonidae/cacomistle cacomistle
75 f mammals/Procyonidae/coatimundi coatimundi
141 f mammals/Procyonidae/coatimundi coatimundi
76 f mammals/Procyonidae/raccoon raccoon
142 f mammals/Procyonidae/raccoon raccoon
77 f mammals/skunk ../skunk
143 f mammals/skunk ../skunk
78
144
79 cd ..
145 cd ..
80
146
81 hg debugwalk ../beans
147 hg debugwalk ../beans
82 f beans/black ../beans/black
148 f beans/black ../beans/black
83 f beans/borlotti ../beans/borlotti
149 f beans/borlotti ../beans/borlotti
84 f beans/kidney ../beans/kidney
150 f beans/kidney ../beans/kidney
85 f beans/navy ../beans/navy
151 f beans/navy ../beans/navy
86 f beans/pinto ../beans/pinto
152 f beans/pinto ../beans/pinto
87 f beans/turtle ../beans/turtle
153 f beans/turtle ../beans/turtle
88
154
89 hg debugwalk .
155 hg debugwalk .
90 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
156 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
91 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
157 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
92 f mammals/Procyonidae/raccoon Procyonidae/raccoon
158 f mammals/Procyonidae/raccoon Procyonidae/raccoon
93 f mammals/skunk skunk
159 f mammals/skunk skunk
94
160
95 hg debugwalk .hg
161 hg debugwalk .hg
96 .hg: No such file or directory
162 .hg: No such file or directory
97
163
98 hg debugwalk ../.hg
164 hg debugwalk ../.hg
99 abort: path contains illegal component: .hg
165 abort: path contains illegal component: .hg
100
166
101
167
102 cd ..
168 cd ..
103
169
104 hg debugwalk -Ibeans
170 hg debugwalk -Ibeans
105 f beans/black beans/black
171 f beans/black beans/black
106 f beans/borlotti beans/borlotti
172 f beans/borlotti beans/borlotti
107 f beans/kidney beans/kidney
173 f beans/kidney beans/kidney
108 f beans/navy beans/navy
174 f beans/navy beans/navy
109 f beans/pinto beans/pinto
175 f beans/pinto beans/pinto
110 f beans/turtle beans/turtle
176 f beans/turtle beans/turtle
111
177
112 hg debugwalk glob:mammals/../beans/b*
178 hg debugwalk glob:mammals/../beans/b*
113 f beans/black beans/black
179 f beans/black beans/black
114 f beans/borlotti beans/borlotti
180 f beans/borlotti beans/borlotti
115
181
116 hg debugwalk -X*/Procyonidae mammals
182 hg debugwalk -X*/Procyonidae mammals
117 f mammals/skunk mammals/skunk
183 f mammals/skunk mammals/skunk
118
184
119 hg debugwalk path:mammals
185 hg debugwalk path:mammals
120 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
186 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
121 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
187 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
122 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
188 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
123 f mammals/skunk mammals/skunk
189 f mammals/skunk mammals/skunk
124
190
125 hg debugwalk ..
191 hg debugwalk ..
126 abort: .. not under root
192 abort: .. not under root
127
193
128 hg debugwalk beans/../..
194 hg debugwalk beans/../..
129 abort: beans/../.. not under root
195 abort: beans/../.. not under root
130
196
131 hg debugwalk .hg
197 hg debugwalk .hg
132 abort: path contains illegal component: .hg
198 abort: path contains illegal component: .hg
133
199
134
200
135 hg debugwalk beans/../.hg
201 hg debugwalk beans/../.hg
136 abort: path contains illegal component: .hg
202 abort: path contains illegal component: .hg
137
203
138
204
139 hg debugwalk beans/../.hg/data
205 hg debugwalk beans/../.hg/data
140 abort: path contains illegal component: .hg/data
206 abort: path contains illegal component: .hg/data
141
207
142
208
143 hg debugwalk beans/.hg
209 hg debugwalk beans/.hg
144 beans/.hg: No such file or directory
210 beans/.hg: No such file or directory
145
211
146 hg debugwalk glob:*
212 hg debugwalk glob:*
147 f fennel fennel
213 f fennel fennel
148 f fenugreek fenugreek
214 f fenugreek fenugreek
149 f fiddlehead fiddlehead
215 f fiddlehead fiddlehead
150 f glob:glob glob:glob
216 f glob:glob glob:glob
151
217
152 hg debugwalk re:.*[kb]$
218 hg debugwalk re:.*[kb]$
153 f beans/black beans/black
219 f beans/black beans/black
154 f fenugreek fenugreek
220 f fenugreek fenugreek
155 f glob:glob glob:glob
221 f glob:glob glob:glob
156 f mammals/skunk mammals/skunk
222 f mammals/skunk mammals/skunk
157
223
158 hg debugwalk path:beans/black
224 hg debugwalk path:beans/black
159 f beans/black beans/black exact
225 f beans/black beans/black exact
160
226
161 hg debugwalk path:beans//black
227 hg debugwalk path:beans//black
162 f beans/black beans/black exact
228 f beans/black beans/black exact
163
229
164 hg debugwalk relglob:Procyonidae
230 hg debugwalk relglob:Procyonidae
165 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
231 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
166 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
232 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
167 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
233 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
168
234
169 hg debugwalk relglob:Procyonidae/ fennel
235 hg debugwalk relglob:Procyonidae/ fennel
170 f fennel fennel exact
236 f fennel fennel exact
171 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
237 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
172 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
238 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
173 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
239 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
174
240
175 hg debugwalk beans glob:beans/*
241 hg debugwalk beans glob:beans/*
176 f beans/black beans/black
242 f beans/black beans/black
177 f beans/borlotti beans/borlotti
243 f beans/borlotti beans/borlotti
178 f beans/kidney beans/kidney
244 f beans/kidney beans/kidney
179 f beans/navy beans/navy
245 f beans/navy beans/navy
180 f beans/pinto beans/pinto
246 f beans/pinto beans/pinto
181 f beans/turtle beans/turtle
247 f beans/turtle beans/turtle
182
248
183 hg debugwalk glob:mamm**
249 hg debugwalk glob:mamm**
184 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
250 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
185 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
251 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
186 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
252 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
187 f mammals/skunk mammals/skunk
253 f mammals/skunk mammals/skunk
188
254
189 hg debugwalk glob:mamm** fennel
255 hg debugwalk glob:mamm** fennel
190 f fennel fennel exact
256 f fennel fennel exact
191 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
257 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
192 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
258 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
193 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
259 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
194 f mammals/skunk mammals/skunk
260 f mammals/skunk mammals/skunk
195
261
196 hg debugwalk glob:j*
262 hg debugwalk glob:j*
197
263
198 hg debugwalk NOEXIST
264 hg debugwalk NOEXIST
199 NOEXIST: No such file or directory
265 NOEXIST: No such file or directory
200
266
201 hg debugwalk fifo
267 hg debugwalk fifo
202 fifo: unsupported file type (type is fifo)
268 fifo: unsupported file type (type is fifo)
203
269
204 hg debugwalk fenugreek
270 hg debugwalk fenugreek
205 m fenugreek fenugreek exact
271 m fenugreek fenugreek exact
206
272
207 hg debugwalk fenugreek
273 hg debugwalk fenugreek
208 m fenugreek fenugreek exact
274 m fenugreek fenugreek exact
209
275
210 hg debugwalk new
276 hg debugwalk new
211 f new new exact
277 f new new exact
212
278
General Comments 0
You need to be logged in to leave comments. Login now