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