##// END OF EJS Templates
audit: be even pickier (issue1450)
Matt Mackall -
r7820:346fafc1 default
parent child Browse files
Show More
@@ -1,2020 +1,2020
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-2007 Matt Mackall <mpm@selenic.com>
5 Copyright 2005-2007 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 _
15 from i18n import _
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback, error
16 import cStringIO, errno, getpass, re, shutil, sys, tempfile, traceback, error
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
17 import os, stat, threading, time, calendar, ConfigParser, locale, glob, osutil
18 import imp, unicodedata
18 import imp, unicodedata
19
19
20 # Python compatibility
20 # Python compatibility
21
21
22 try:
22 try:
23 set = set
23 set = set
24 frozenset = frozenset
24 frozenset = frozenset
25 except NameError:
25 except NameError:
26 from sets import Set as set, ImmutableSet as frozenset
26 from sets import Set as set, ImmutableSet as frozenset
27
27
28 _md5 = None
28 _md5 = None
29 def md5(s):
29 def md5(s):
30 global _md5
30 global _md5
31 if _md5 is None:
31 if _md5 is None:
32 try:
32 try:
33 import hashlib
33 import hashlib
34 _md5 = hashlib.md5
34 _md5 = hashlib.md5
35 except ImportError:
35 except ImportError:
36 import md5
36 import md5
37 _md5 = md5.md5
37 _md5 = md5.md5
38 return _md5(s)
38 return _md5(s)
39
39
40 _sha1 = None
40 _sha1 = None
41 def sha1(s):
41 def sha1(s):
42 global _sha1
42 global _sha1
43 if _sha1 is None:
43 if _sha1 is None:
44 try:
44 try:
45 import hashlib
45 import hashlib
46 _sha1 = hashlib.sha1
46 _sha1 = hashlib.sha1
47 except ImportError:
47 except ImportError:
48 import sha
48 import sha
49 _sha1 = sha.sha
49 _sha1 = sha.sha
50 return _sha1(s)
50 return _sha1(s)
51
51
52 try:
52 try:
53 import subprocess
53 import subprocess
54 subprocess.Popen # trigger ImportError early
54 subprocess.Popen # trigger ImportError early
55 closefds = os.name == 'posix'
55 closefds = os.name == 'posix'
56 def popen2(cmd, mode='t', bufsize=-1):
56 def popen2(cmd, mode='t', bufsize=-1):
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
57 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
58 close_fds=closefds,
58 close_fds=closefds,
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
59 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
60 return p.stdin, p.stdout
60 return p.stdin, p.stdout
61 def popen3(cmd, mode='t', bufsize=-1):
61 def popen3(cmd, mode='t', bufsize=-1):
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
62 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
63 close_fds=closefds,
63 close_fds=closefds,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
64 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
65 stderr=subprocess.PIPE)
65 stderr=subprocess.PIPE)
66 return p.stdin, p.stdout, p.stderr
66 return p.stdin, p.stdout, p.stderr
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
67 def Popen3(cmd, capturestderr=False, bufsize=-1):
68 stderr = capturestderr and subprocess.PIPE or None
68 stderr = capturestderr and subprocess.PIPE or None
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
69 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
70 close_fds=closefds,
70 close_fds=closefds,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
71 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
72 stderr=stderr)
72 stderr=stderr)
73 p.fromchild = p.stdout
73 p.fromchild = p.stdout
74 p.tochild = p.stdin
74 p.tochild = p.stdin
75 p.childerr = p.stderr
75 p.childerr = p.stderr
76 return p
76 return p
77 except ImportError:
77 except ImportError:
78 subprocess = None
78 subprocess = None
79 from popen2 import Popen3
79 from popen2 import Popen3
80 popen2 = os.popen2
80 popen2 = os.popen2
81 popen3 = os.popen3
81 popen3 = os.popen3
82
82
83
83
84 _encodingfixup = {'646': 'ascii', 'ANSI_X3.4-1968': 'ascii'}
84 _encodingfixup = {'646': 'ascii', 'ANSI_X3.4-1968': 'ascii'}
85
85
86 try:
86 try:
87 _encoding = os.environ.get("HGENCODING")
87 _encoding = os.environ.get("HGENCODING")
88 if sys.platform == 'darwin' and not _encoding:
88 if sys.platform == 'darwin' and not _encoding:
89 # On darwin, getpreferredencoding ignores the locale environment and
89 # On darwin, getpreferredencoding ignores the locale environment and
90 # always returns mac-roman. We override this if the environment is
90 # always returns mac-roman. We override this if the environment is
91 # not C (has been customized by the user).
91 # not C (has been customized by the user).
92 locale.setlocale(locale.LC_CTYPE, '')
92 locale.setlocale(locale.LC_CTYPE, '')
93 _encoding = locale.getlocale()[1]
93 _encoding = locale.getlocale()[1]
94 if not _encoding:
94 if not _encoding:
95 _encoding = locale.getpreferredencoding() or 'ascii'
95 _encoding = locale.getpreferredencoding() or 'ascii'
96 _encoding = _encodingfixup.get(_encoding, _encoding)
96 _encoding = _encodingfixup.get(_encoding, _encoding)
97 except locale.Error:
97 except locale.Error:
98 _encoding = 'ascii'
98 _encoding = 'ascii'
99 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
99 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
100 _fallbackencoding = 'ISO-8859-1'
100 _fallbackencoding = 'ISO-8859-1'
101
101
102 def tolocal(s):
102 def tolocal(s):
103 """
103 """
104 Convert a string from internal UTF-8 to local encoding
104 Convert a string from internal UTF-8 to local encoding
105
105
106 All internal strings should be UTF-8 but some repos before the
106 All internal strings should be UTF-8 but some repos before the
107 implementation of locale support may contain latin1 or possibly
107 implementation of locale support may contain latin1 or possibly
108 other character sets. We attempt to decode everything strictly
108 other character sets. We attempt to decode everything strictly
109 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
109 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
110 replace unknown characters.
110 replace unknown characters.
111 """
111 """
112 for e in ('UTF-8', _fallbackencoding):
112 for e in ('UTF-8', _fallbackencoding):
113 try:
113 try:
114 u = s.decode(e) # attempt strict decoding
114 u = s.decode(e) # attempt strict decoding
115 return u.encode(_encoding, "replace")
115 return u.encode(_encoding, "replace")
116 except LookupError, k:
116 except LookupError, k:
117 raise Abort(_("%s, please check your locale settings") % k)
117 raise Abort(_("%s, please check your locale settings") % k)
118 except UnicodeDecodeError:
118 except UnicodeDecodeError:
119 pass
119 pass
120 u = s.decode("utf-8", "replace") # last ditch
120 u = s.decode("utf-8", "replace") # last ditch
121 return u.encode(_encoding, "replace")
121 return u.encode(_encoding, "replace")
122
122
123 def fromlocal(s):
123 def fromlocal(s):
124 """
124 """
125 Convert a string from the local character encoding to UTF-8
125 Convert a string from the local character encoding to UTF-8
126
126
127 We attempt to decode strings using the encoding mode set by
127 We attempt to decode strings using the encoding mode set by
128 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
128 HGENCODINGMODE, which defaults to 'strict'. In this mode, unknown
129 characters will cause an error message. Other modes include
129 characters will cause an error message. Other modes include
130 'replace', which replaces unknown characters with a special
130 'replace', which replaces unknown characters with a special
131 Unicode character, and 'ignore', which drops the character.
131 Unicode character, and 'ignore', which drops the character.
132 """
132 """
133 try:
133 try:
134 return s.decode(_encoding, _encodingmode).encode("utf-8")
134 return s.decode(_encoding, _encodingmode).encode("utf-8")
135 except UnicodeDecodeError, inst:
135 except UnicodeDecodeError, inst:
136 sub = s[max(0, inst.start-10):inst.start+10]
136 sub = s[max(0, inst.start-10):inst.start+10]
137 raise Abort("decoding near '%s': %s!" % (sub, inst))
137 raise Abort("decoding near '%s': %s!" % (sub, inst))
138 except LookupError, k:
138 except LookupError, k:
139 raise Abort(_("%s, please check your locale settings") % k)
139 raise Abort(_("%s, please check your locale settings") % k)
140
140
141 def colwidth(s):
141 def colwidth(s):
142 "Find the column width of a UTF-8 string for display"
142 "Find the column width of a UTF-8 string for display"
143 d = s.decode(_encoding, 'replace')
143 d = s.decode(_encoding, 'replace')
144 if hasattr(unicodedata, 'east_asian_width'):
144 if hasattr(unicodedata, 'east_asian_width'):
145 w = unicodedata.east_asian_width
145 w = unicodedata.east_asian_width
146 return sum([w(c) in 'WF' and 2 or 1 for c in d])
146 return sum([w(c) in 'WF' and 2 or 1 for c in d])
147 return len(d)
147 return len(d)
148
148
149 def version():
149 def version():
150 """Return version information if available."""
150 """Return version information if available."""
151 try:
151 try:
152 import __version__
152 import __version__
153 return __version__.version
153 return __version__.version
154 except ImportError:
154 except ImportError:
155 return 'unknown'
155 return 'unknown'
156
156
157 # used by parsedate
157 # used by parsedate
158 defaultdateformats = (
158 defaultdateformats = (
159 '%Y-%m-%d %H:%M:%S',
159 '%Y-%m-%d %H:%M:%S',
160 '%Y-%m-%d %I:%M:%S%p',
160 '%Y-%m-%d %I:%M:%S%p',
161 '%Y-%m-%d %H:%M',
161 '%Y-%m-%d %H:%M',
162 '%Y-%m-%d %I:%M%p',
162 '%Y-%m-%d %I:%M%p',
163 '%Y-%m-%d',
163 '%Y-%m-%d',
164 '%m-%d',
164 '%m-%d',
165 '%m/%d',
165 '%m/%d',
166 '%m/%d/%y',
166 '%m/%d/%y',
167 '%m/%d/%Y',
167 '%m/%d/%Y',
168 '%a %b %d %H:%M:%S %Y',
168 '%a %b %d %H:%M:%S %Y',
169 '%a %b %d %I:%M:%S%p %Y',
169 '%a %b %d %I:%M:%S%p %Y',
170 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
170 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
171 '%b %d %H:%M:%S %Y',
171 '%b %d %H:%M:%S %Y',
172 '%b %d %I:%M:%S%p %Y',
172 '%b %d %I:%M:%S%p %Y',
173 '%b %d %H:%M:%S',
173 '%b %d %H:%M:%S',
174 '%b %d %I:%M:%S%p',
174 '%b %d %I:%M:%S%p',
175 '%b %d %H:%M',
175 '%b %d %H:%M',
176 '%b %d %I:%M%p',
176 '%b %d %I:%M%p',
177 '%b %d %Y',
177 '%b %d %Y',
178 '%b %d',
178 '%b %d',
179 '%H:%M:%S',
179 '%H:%M:%S',
180 '%I:%M:%SP',
180 '%I:%M:%SP',
181 '%H:%M',
181 '%H:%M',
182 '%I:%M%p',
182 '%I:%M%p',
183 )
183 )
184
184
185 extendeddateformats = defaultdateformats + (
185 extendeddateformats = defaultdateformats + (
186 "%Y",
186 "%Y",
187 "%Y-%m",
187 "%Y-%m",
188 "%b",
188 "%b",
189 "%b %Y",
189 "%b %Y",
190 )
190 )
191
191
192 # differences from SafeConfigParser:
192 # differences from SafeConfigParser:
193 # - case-sensitive keys
193 # - case-sensitive keys
194 # - allows values that are not strings (this means that you may not
194 # - allows values that are not strings (this means that you may not
195 # be able to save the configuration to a file)
195 # be able to save the configuration to a file)
196 class configparser(ConfigParser.SafeConfigParser):
196 class configparser(ConfigParser.SafeConfigParser):
197 def optionxform(self, optionstr):
197 def optionxform(self, optionstr):
198 return optionstr
198 return optionstr
199
199
200 def set(self, section, option, value):
200 def set(self, section, option, value):
201 return ConfigParser.ConfigParser.set(self, section, option, value)
201 return ConfigParser.ConfigParser.set(self, section, option, value)
202
202
203 def _interpolate(self, section, option, rawval, vars):
203 def _interpolate(self, section, option, rawval, vars):
204 if not isinstance(rawval, basestring):
204 if not isinstance(rawval, basestring):
205 return rawval
205 return rawval
206 return ConfigParser.SafeConfigParser._interpolate(self, section,
206 return ConfigParser.SafeConfigParser._interpolate(self, section,
207 option, rawval, vars)
207 option, rawval, vars)
208
208
209 def cachefunc(func):
209 def cachefunc(func):
210 '''cache the result of function calls'''
210 '''cache the result of function calls'''
211 # XXX doesn't handle keywords args
211 # XXX doesn't handle keywords args
212 cache = {}
212 cache = {}
213 if func.func_code.co_argcount == 1:
213 if func.func_code.co_argcount == 1:
214 # we gain a small amount of time because
214 # we gain a small amount of time because
215 # we don't need to pack/unpack the list
215 # we don't need to pack/unpack the list
216 def f(arg):
216 def f(arg):
217 if arg not in cache:
217 if arg not in cache:
218 cache[arg] = func(arg)
218 cache[arg] = func(arg)
219 return cache[arg]
219 return cache[arg]
220 else:
220 else:
221 def f(*args):
221 def f(*args):
222 if args not in cache:
222 if args not in cache:
223 cache[args] = func(*args)
223 cache[args] = func(*args)
224 return cache[args]
224 return cache[args]
225
225
226 return f
226 return f
227
227
228 def pipefilter(s, cmd):
228 def pipefilter(s, cmd):
229 '''filter string S through command CMD, returning its output'''
229 '''filter string S through command CMD, returning its output'''
230 (pin, pout) = popen2(cmd, 'b')
230 (pin, pout) = popen2(cmd, 'b')
231 def writer():
231 def writer():
232 try:
232 try:
233 pin.write(s)
233 pin.write(s)
234 pin.close()
234 pin.close()
235 except IOError, inst:
235 except IOError, inst:
236 if inst.errno != errno.EPIPE:
236 if inst.errno != errno.EPIPE:
237 raise
237 raise
238
238
239 # we should use select instead on UNIX, but this will work on most
239 # we should use select instead on UNIX, but this will work on most
240 # systems, including Windows
240 # systems, including Windows
241 w = threading.Thread(target=writer)
241 w = threading.Thread(target=writer)
242 w.start()
242 w.start()
243 f = pout.read()
243 f = pout.read()
244 pout.close()
244 pout.close()
245 w.join()
245 w.join()
246 return f
246 return f
247
247
248 def tempfilter(s, cmd):
248 def tempfilter(s, cmd):
249 '''filter string S through a pair of temporary files with CMD.
249 '''filter string S through a pair of temporary files with CMD.
250 CMD is used as a template to create the real command to be run,
250 CMD is used as a template to create the real command to be run,
251 with the strings INFILE and OUTFILE replaced by the real names of
251 with the strings INFILE and OUTFILE replaced by the real names of
252 the temporary files generated.'''
252 the temporary files generated.'''
253 inname, outname = None, None
253 inname, outname = None, None
254 try:
254 try:
255 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
255 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
256 fp = os.fdopen(infd, 'wb')
256 fp = os.fdopen(infd, 'wb')
257 fp.write(s)
257 fp.write(s)
258 fp.close()
258 fp.close()
259 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
259 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
260 os.close(outfd)
260 os.close(outfd)
261 cmd = cmd.replace('INFILE', inname)
261 cmd = cmd.replace('INFILE', inname)
262 cmd = cmd.replace('OUTFILE', outname)
262 cmd = cmd.replace('OUTFILE', outname)
263 code = os.system(cmd)
263 code = os.system(cmd)
264 if sys.platform == 'OpenVMS' and code & 1:
264 if sys.platform == 'OpenVMS' and code & 1:
265 code = 0
265 code = 0
266 if code: raise Abort(_("command '%s' failed: %s") %
266 if code: raise Abort(_("command '%s' failed: %s") %
267 (cmd, explain_exit(code)))
267 (cmd, explain_exit(code)))
268 return open(outname, 'rb').read()
268 return open(outname, 'rb').read()
269 finally:
269 finally:
270 try:
270 try:
271 if inname: os.unlink(inname)
271 if inname: os.unlink(inname)
272 except: pass
272 except: pass
273 try:
273 try:
274 if outname: os.unlink(outname)
274 if outname: os.unlink(outname)
275 except: pass
275 except: pass
276
276
277 filtertable = {
277 filtertable = {
278 'tempfile:': tempfilter,
278 'tempfile:': tempfilter,
279 'pipe:': pipefilter,
279 'pipe:': pipefilter,
280 }
280 }
281
281
282 def filter(s, cmd):
282 def filter(s, cmd):
283 "filter a string through a command that transforms its input to its output"
283 "filter a string through a command that transforms its input to its output"
284 for name, fn in filtertable.iteritems():
284 for name, fn in filtertable.iteritems():
285 if cmd.startswith(name):
285 if cmd.startswith(name):
286 return fn(s, cmd[len(name):].lstrip())
286 return fn(s, cmd[len(name):].lstrip())
287 return pipefilter(s, cmd)
287 return pipefilter(s, cmd)
288
288
289 def binary(s):
289 def binary(s):
290 """return true if a string is binary data"""
290 """return true if a string is binary data"""
291 if s and '\0' in s:
291 if s and '\0' in s:
292 return True
292 return True
293 return False
293 return False
294
294
295 def unique(g):
295 def unique(g):
296 """return the uniq elements of iterable g"""
296 """return the uniq elements of iterable g"""
297 return dict.fromkeys(g).keys()
297 return dict.fromkeys(g).keys()
298
298
299 def sort(l):
299 def sort(l):
300 if not isinstance(l, list):
300 if not isinstance(l, list):
301 l = list(l)
301 l = list(l)
302 l.sort()
302 l.sort()
303 return l
303 return l
304
304
305 def increasingchunks(source, min=1024, max=65536):
305 def increasingchunks(source, min=1024, max=65536):
306 '''return no less than min bytes per chunk while data remains,
306 '''return no less than min bytes per chunk while data remains,
307 doubling min after each chunk until it reaches max'''
307 doubling min after each chunk until it reaches max'''
308 def log2(x):
308 def log2(x):
309 if not x:
309 if not x:
310 return 0
310 return 0
311 i = 0
311 i = 0
312 while x:
312 while x:
313 x >>= 1
313 x >>= 1
314 i += 1
314 i += 1
315 return i - 1
315 return i - 1
316
316
317 buf = []
317 buf = []
318 blen = 0
318 blen = 0
319 for chunk in source:
319 for chunk in source:
320 buf.append(chunk)
320 buf.append(chunk)
321 blen += len(chunk)
321 blen += len(chunk)
322 if blen >= min:
322 if blen >= min:
323 if min < max:
323 if min < max:
324 min = min << 1
324 min = min << 1
325 nmin = 1 << log2(blen)
325 nmin = 1 << log2(blen)
326 if nmin > min:
326 if nmin > min:
327 min = nmin
327 min = nmin
328 if min > max:
328 if min > max:
329 min = max
329 min = max
330 yield ''.join(buf)
330 yield ''.join(buf)
331 blen = 0
331 blen = 0
332 buf = []
332 buf = []
333 if buf:
333 if buf:
334 yield ''.join(buf)
334 yield ''.join(buf)
335
335
336 class Abort(Exception):
336 class Abort(Exception):
337 """Raised if a command needs to print an error and exit."""
337 """Raised if a command needs to print an error and exit."""
338
338
339 def always(fn): return True
339 def always(fn): return True
340 def never(fn): return False
340 def never(fn): return False
341
341
342 def expand_glob(pats):
342 def expand_glob(pats):
343 '''On Windows, expand the implicit globs in a list of patterns'''
343 '''On Windows, expand the implicit globs in a list of patterns'''
344 if os.name != 'nt':
344 if os.name != 'nt':
345 return list(pats)
345 return list(pats)
346 ret = []
346 ret = []
347 for p in pats:
347 for p in pats:
348 kind, name = patkind(p, None)
348 kind, name = patkind(p, None)
349 if kind is None:
349 if kind is None:
350 globbed = glob.glob(name)
350 globbed = glob.glob(name)
351 if globbed:
351 if globbed:
352 ret.extend(globbed)
352 ret.extend(globbed)
353 continue
353 continue
354 # if we couldn't expand the glob, just keep it around
354 # if we couldn't expand the glob, just keep it around
355 ret.append(p)
355 ret.append(p)
356 return ret
356 return ret
357
357
358 def patkind(name, default):
358 def patkind(name, default):
359 """Split a string into an optional pattern kind prefix and the
359 """Split a string into an optional pattern kind prefix and the
360 actual pattern."""
360 actual pattern."""
361 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
361 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
362 if name.startswith(prefix + ':'): return name.split(':', 1)
362 if name.startswith(prefix + ':'): return name.split(':', 1)
363 return default, name
363 return default, name
364
364
365 def globre(pat, head='^', tail='$'):
365 def globre(pat, head='^', tail='$'):
366 "convert a glob pattern into a regexp"
366 "convert a glob pattern into a regexp"
367 i, n = 0, len(pat)
367 i, n = 0, len(pat)
368 res = ''
368 res = ''
369 group = 0
369 group = 0
370 def peek(): return i < n and pat[i]
370 def peek(): return i < n and pat[i]
371 while i < n:
371 while i < n:
372 c = pat[i]
372 c = pat[i]
373 i = i+1
373 i = i+1
374 if c == '*':
374 if c == '*':
375 if peek() == '*':
375 if peek() == '*':
376 i += 1
376 i += 1
377 res += '.*'
377 res += '.*'
378 else:
378 else:
379 res += '[^/]*'
379 res += '[^/]*'
380 elif c == '?':
380 elif c == '?':
381 res += '.'
381 res += '.'
382 elif c == '[':
382 elif c == '[':
383 j = i
383 j = i
384 if j < n and pat[j] in '!]':
384 if j < n and pat[j] in '!]':
385 j += 1
385 j += 1
386 while j < n and pat[j] != ']':
386 while j < n and pat[j] != ']':
387 j += 1
387 j += 1
388 if j >= n:
388 if j >= n:
389 res += '\\['
389 res += '\\['
390 else:
390 else:
391 stuff = pat[i:j].replace('\\','\\\\')
391 stuff = pat[i:j].replace('\\','\\\\')
392 i = j + 1
392 i = j + 1
393 if stuff[0] == '!':
393 if stuff[0] == '!':
394 stuff = '^' + stuff[1:]
394 stuff = '^' + stuff[1:]
395 elif stuff[0] == '^':
395 elif stuff[0] == '^':
396 stuff = '\\' + stuff
396 stuff = '\\' + stuff
397 res = '%s[%s]' % (res, stuff)
397 res = '%s[%s]' % (res, stuff)
398 elif c == '{':
398 elif c == '{':
399 group += 1
399 group += 1
400 res += '(?:'
400 res += '(?:'
401 elif c == '}' and group:
401 elif c == '}' and group:
402 res += ')'
402 res += ')'
403 group -= 1
403 group -= 1
404 elif c == ',' and group:
404 elif c == ',' and group:
405 res += '|'
405 res += '|'
406 elif c == '\\':
406 elif c == '\\':
407 p = peek()
407 p = peek()
408 if p:
408 if p:
409 i += 1
409 i += 1
410 res += re.escape(p)
410 res += re.escape(p)
411 else:
411 else:
412 res += re.escape(c)
412 res += re.escape(c)
413 else:
413 else:
414 res += re.escape(c)
414 res += re.escape(c)
415 return head + res + tail
415 return head + res + tail
416
416
417 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
417 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
418
418
419 def pathto(root, n1, n2):
419 def pathto(root, n1, n2):
420 '''return the relative path from one place to another.
420 '''return the relative path from one place to another.
421 root should use os.sep to separate directories
421 root should use os.sep to separate directories
422 n1 should use os.sep to separate directories
422 n1 should use os.sep to separate directories
423 n2 should use "/" to separate directories
423 n2 should use "/" to separate directories
424 returns an os.sep-separated path.
424 returns an os.sep-separated path.
425
425
426 If n1 is a relative path, it's assumed it's
426 If n1 is a relative path, it's assumed it's
427 relative to root.
427 relative to root.
428 n2 should always be relative to root.
428 n2 should always be relative to root.
429 '''
429 '''
430 if not n1: return localpath(n2)
430 if not n1: return localpath(n2)
431 if os.path.isabs(n1):
431 if os.path.isabs(n1):
432 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
432 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
433 return os.path.join(root, localpath(n2))
433 return os.path.join(root, localpath(n2))
434 n2 = '/'.join((pconvert(root), n2))
434 n2 = '/'.join((pconvert(root), n2))
435 a, b = splitpath(n1), n2.split('/')
435 a, b = splitpath(n1), n2.split('/')
436 a.reverse()
436 a.reverse()
437 b.reverse()
437 b.reverse()
438 while a and b and a[-1] == b[-1]:
438 while a and b and a[-1] == b[-1]:
439 a.pop()
439 a.pop()
440 b.pop()
440 b.pop()
441 b.reverse()
441 b.reverse()
442 return os.sep.join((['..'] * len(a)) + b) or '.'
442 return os.sep.join((['..'] * len(a)) + b) or '.'
443
443
444 def canonpath(root, cwd, myname):
444 def canonpath(root, cwd, myname):
445 """return the canonical path of myname, given cwd and root"""
445 """return the canonical path of myname, given cwd and root"""
446 if root == os.sep:
446 if root == os.sep:
447 rootsep = os.sep
447 rootsep = os.sep
448 elif endswithsep(root):
448 elif endswithsep(root):
449 rootsep = root
449 rootsep = root
450 else:
450 else:
451 rootsep = root + os.sep
451 rootsep = root + os.sep
452 name = myname
452 name = myname
453 if not os.path.isabs(name):
453 if not os.path.isabs(name):
454 name = os.path.join(root, cwd, name)
454 name = os.path.join(root, cwd, name)
455 name = os.path.normpath(name)
455 name = os.path.normpath(name)
456 audit_path = path_auditor(root)
456 audit_path = path_auditor(root)
457 if name != rootsep and name.startswith(rootsep):
457 if name != rootsep and name.startswith(rootsep):
458 name = name[len(rootsep):]
458 name = name[len(rootsep):]
459 audit_path(name)
459 audit_path(name)
460 return pconvert(name)
460 return pconvert(name)
461 elif name == root:
461 elif name == root:
462 return ''
462 return ''
463 else:
463 else:
464 # Determine whether `name' is in the hierarchy at or beneath `root',
464 # Determine whether `name' is in the hierarchy at or beneath `root',
465 # by iterating name=dirname(name) until that causes no change (can't
465 # by iterating name=dirname(name) until that causes no change (can't
466 # check name == '/', because that doesn't work on windows). For each
466 # check name == '/', because that doesn't work on windows). For each
467 # `name', compare dev/inode numbers. If they match, the list `rel'
467 # `name', compare dev/inode numbers. If they match, the list `rel'
468 # holds the reversed list of components making up the relative file
468 # holds the reversed list of components making up the relative file
469 # name we want.
469 # name we want.
470 root_st = os.stat(root)
470 root_st = os.stat(root)
471 rel = []
471 rel = []
472 while True:
472 while True:
473 try:
473 try:
474 name_st = os.stat(name)
474 name_st = os.stat(name)
475 except OSError:
475 except OSError:
476 break
476 break
477 if samestat(name_st, root_st):
477 if samestat(name_st, root_st):
478 if not rel:
478 if not rel:
479 # name was actually the same as root (maybe a symlink)
479 # name was actually the same as root (maybe a symlink)
480 return ''
480 return ''
481 rel.reverse()
481 rel.reverse()
482 name = os.path.join(*rel)
482 name = os.path.join(*rel)
483 audit_path(name)
483 audit_path(name)
484 return pconvert(name)
484 return pconvert(name)
485 dirname, basename = os.path.split(name)
485 dirname, basename = os.path.split(name)
486 rel.append(basename)
486 rel.append(basename)
487 if dirname == name:
487 if dirname == name:
488 break
488 break
489 name = dirname
489 name = dirname
490
490
491 raise Abort('%s not under root' % myname)
491 raise Abort('%s not under root' % myname)
492
492
493 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
493 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None, dflt_pat='glob'):
494 """build a function to match a set of file patterns
494 """build a function to match a set of file patterns
495
495
496 arguments:
496 arguments:
497 canonroot - the canonical root of the tree you're matching against
497 canonroot - the canonical root of the tree you're matching against
498 cwd - the current working directory, if relevant
498 cwd - the current working directory, if relevant
499 names - patterns to find
499 names - patterns to find
500 inc - patterns to include
500 inc - patterns to include
501 exc - patterns to exclude
501 exc - patterns to exclude
502 dflt_pat - if a pattern in names has no explicit type, assume this one
502 dflt_pat - if a pattern in names has no explicit type, assume this one
503 src - where these patterns came from (e.g. .hgignore)
503 src - where these patterns came from (e.g. .hgignore)
504
504
505 a pattern is one of:
505 a pattern is one of:
506 'glob:<glob>' - a glob relative to cwd
506 'glob:<glob>' - a glob relative to cwd
507 're:<regexp>' - a regular expression
507 're:<regexp>' - a regular expression
508 'path:<path>' - a path relative to canonroot
508 'path:<path>' - a path relative to canonroot
509 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
509 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
510 'relpath:<path>' - a path relative to cwd
510 'relpath:<path>' - a path relative to cwd
511 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
511 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
512 '<something>' - one of the cases above, selected by the dflt_pat argument
512 '<something>' - one of the cases above, selected by the dflt_pat argument
513
513
514 returns:
514 returns:
515 a 3-tuple containing
515 a 3-tuple containing
516 - list of roots (places where one should start a recursive walk of the fs);
516 - list of roots (places where one should start a recursive walk of the fs);
517 this often matches the explicit non-pattern names passed in, but also
517 this often matches the explicit non-pattern names passed in, but also
518 includes the initial part of glob: patterns that has no glob characters
518 includes the initial part of glob: patterns that has no glob characters
519 - a bool match(filename) function
519 - a bool match(filename) function
520 - a bool indicating if any patterns were passed in
520 - a bool indicating if any patterns were passed in
521 """
521 """
522
522
523 # a common case: no patterns at all
523 # a common case: no patterns at all
524 if not names and not inc and not exc:
524 if not names and not inc and not exc:
525 return [], always, False
525 return [], always, False
526
526
527 def contains_glob(name):
527 def contains_glob(name):
528 for c in name:
528 for c in name:
529 if c in _globchars: return True
529 if c in _globchars: return True
530 return False
530 return False
531
531
532 def regex(kind, name, tail):
532 def regex(kind, name, tail):
533 '''convert a pattern into a regular expression'''
533 '''convert a pattern into a regular expression'''
534 if not name:
534 if not name:
535 return ''
535 return ''
536 if kind == 're':
536 if kind == 're':
537 return name
537 return name
538 elif kind == 'path':
538 elif kind == 'path':
539 return '^' + re.escape(name) + '(?:/|$)'
539 return '^' + re.escape(name) + '(?:/|$)'
540 elif kind == 'relglob':
540 elif kind == 'relglob':
541 return globre(name, '(?:|.*/)', tail)
541 return globre(name, '(?:|.*/)', tail)
542 elif kind == 'relpath':
542 elif kind == 'relpath':
543 return re.escape(name) + '(?:/|$)'
543 return re.escape(name) + '(?:/|$)'
544 elif kind == 'relre':
544 elif kind == 'relre':
545 if name.startswith('^'):
545 if name.startswith('^'):
546 return name
546 return name
547 return '.*' + name
547 return '.*' + name
548 return globre(name, '', tail)
548 return globre(name, '', tail)
549
549
550 def matchfn(pats, tail):
550 def matchfn(pats, tail):
551 """build a matching function from a set of patterns"""
551 """build a matching function from a set of patterns"""
552 if not pats:
552 if not pats:
553 return
553 return
554 try:
554 try:
555 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
555 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
556 if len(pat) > 20000:
556 if len(pat) > 20000:
557 raise OverflowError()
557 raise OverflowError()
558 return re.compile(pat).match
558 return re.compile(pat).match
559 except OverflowError:
559 except OverflowError:
560 # We're using a Python with a tiny regex engine and we
560 # We're using a Python with a tiny regex engine and we
561 # made it explode, so we'll divide the pattern list in two
561 # made it explode, so we'll divide the pattern list in two
562 # until it works
562 # until it works
563 l = len(pats)
563 l = len(pats)
564 if l < 2:
564 if l < 2:
565 raise
565 raise
566 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
566 a, b = matchfn(pats[:l//2], tail), matchfn(pats[l//2:], tail)
567 return lambda s: a(s) or b(s)
567 return lambda s: a(s) or b(s)
568 except re.error:
568 except re.error:
569 for k, p in pats:
569 for k, p in pats:
570 try:
570 try:
571 re.compile('(?:%s)' % regex(k, p, tail))
571 re.compile('(?:%s)' % regex(k, p, tail))
572 except re.error:
572 except re.error:
573 if src:
573 if src:
574 raise Abort("%s: invalid pattern (%s): %s" %
574 raise Abort("%s: invalid pattern (%s): %s" %
575 (src, k, p))
575 (src, k, p))
576 else:
576 else:
577 raise Abort("invalid pattern (%s): %s" % (k, p))
577 raise Abort("invalid pattern (%s): %s" % (k, p))
578 raise Abort("invalid pattern")
578 raise Abort("invalid pattern")
579
579
580 def globprefix(pat):
580 def globprefix(pat):
581 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
581 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
582 root = []
582 root = []
583 for p in pat.split('/'):
583 for p in pat.split('/'):
584 if contains_glob(p): break
584 if contains_glob(p): break
585 root.append(p)
585 root.append(p)
586 return '/'.join(root) or '.'
586 return '/'.join(root) or '.'
587
587
588 def normalizepats(names, default):
588 def normalizepats(names, default):
589 pats = []
589 pats = []
590 roots = []
590 roots = []
591 anypats = False
591 anypats = False
592 for kind, name in [patkind(p, default) for p in names]:
592 for kind, name in [patkind(p, default) for p in names]:
593 if kind in ('glob', 'relpath'):
593 if kind in ('glob', 'relpath'):
594 name = canonpath(canonroot, cwd, name)
594 name = canonpath(canonroot, cwd, name)
595 elif kind in ('relglob', 'path'):
595 elif kind in ('relglob', 'path'):
596 name = normpath(name)
596 name = normpath(name)
597
597
598 pats.append((kind, name))
598 pats.append((kind, name))
599
599
600 if kind in ('glob', 're', 'relglob', 'relre'):
600 if kind in ('glob', 're', 'relglob', 'relre'):
601 anypats = True
601 anypats = True
602
602
603 if kind == 'glob':
603 if kind == 'glob':
604 root = globprefix(name)
604 root = globprefix(name)
605 roots.append(root)
605 roots.append(root)
606 elif kind in ('relpath', 'path'):
606 elif kind in ('relpath', 'path'):
607 roots.append(name or '.')
607 roots.append(name or '.')
608 elif kind == 'relglob':
608 elif kind == 'relglob':
609 roots.append('.')
609 roots.append('.')
610 return roots, pats, anypats
610 return roots, pats, anypats
611
611
612 roots, pats, anypats = normalizepats(names, dflt_pat)
612 roots, pats, anypats = normalizepats(names, dflt_pat)
613
613
614 patmatch = matchfn(pats, '$') or always
614 patmatch = matchfn(pats, '$') or always
615 incmatch = always
615 incmatch = always
616 if inc:
616 if inc:
617 dummy, inckinds, dummy = normalizepats(inc, 'glob')
617 dummy, inckinds, dummy = normalizepats(inc, 'glob')
618 incmatch = matchfn(inckinds, '(?:/|$)')
618 incmatch = matchfn(inckinds, '(?:/|$)')
619 excmatch = never
619 excmatch = never
620 if exc:
620 if exc:
621 dummy, exckinds, dummy = normalizepats(exc, 'glob')
621 dummy, exckinds, dummy = normalizepats(exc, 'glob')
622 excmatch = matchfn(exckinds, '(?:/|$)')
622 excmatch = matchfn(exckinds, '(?:/|$)')
623
623
624 if not names and inc and not exc:
624 if not names and inc and not exc:
625 # common case: hgignore patterns
625 # common case: hgignore patterns
626 match = incmatch
626 match = incmatch
627 else:
627 else:
628 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
628 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
629
629
630 return (roots, match, (inc or exc or anypats) and True)
630 return (roots, match, (inc or exc or anypats) and True)
631
631
632 _hgexecutable = None
632 _hgexecutable = None
633
633
634 def main_is_frozen():
634 def main_is_frozen():
635 """return True if we are a frozen executable.
635 """return True if we are a frozen executable.
636
636
637 The code supports py2exe (most common, Windows only) and tools/freeze
637 The code supports py2exe (most common, Windows only) and tools/freeze
638 (portable, not much used).
638 (portable, not much used).
639 """
639 """
640 return (hasattr(sys, "frozen") or # new py2exe
640 return (hasattr(sys, "frozen") or # new py2exe
641 hasattr(sys, "importers") or # old py2exe
641 hasattr(sys, "importers") or # old py2exe
642 imp.is_frozen("__main__")) # tools/freeze
642 imp.is_frozen("__main__")) # tools/freeze
643
643
644 def hgexecutable():
644 def hgexecutable():
645 """return location of the 'hg' executable.
645 """return location of the 'hg' executable.
646
646
647 Defaults to $HG or 'hg' in the search path.
647 Defaults to $HG or 'hg' in the search path.
648 """
648 """
649 if _hgexecutable is None:
649 if _hgexecutable is None:
650 hg = os.environ.get('HG')
650 hg = os.environ.get('HG')
651 if hg:
651 if hg:
652 set_hgexecutable(hg)
652 set_hgexecutable(hg)
653 elif main_is_frozen():
653 elif main_is_frozen():
654 set_hgexecutable(sys.executable)
654 set_hgexecutable(sys.executable)
655 else:
655 else:
656 set_hgexecutable(find_exe('hg') or 'hg')
656 set_hgexecutable(find_exe('hg') or 'hg')
657 return _hgexecutable
657 return _hgexecutable
658
658
659 def set_hgexecutable(path):
659 def set_hgexecutable(path):
660 """set location of the 'hg' executable"""
660 """set location of the 'hg' executable"""
661 global _hgexecutable
661 global _hgexecutable
662 _hgexecutable = path
662 _hgexecutable = path
663
663
664 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
664 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
665 '''enhanced shell command execution.
665 '''enhanced shell command execution.
666 run with environment maybe modified, maybe in different dir.
666 run with environment maybe modified, maybe in different dir.
667
667
668 if command fails and onerr is None, return status. if ui object,
668 if command fails and onerr is None, return status. if ui object,
669 print error message and return status, else raise onerr object as
669 print error message and return status, else raise onerr object as
670 exception.'''
670 exception.'''
671 def py2shell(val):
671 def py2shell(val):
672 'convert python object into string that is useful to shell'
672 'convert python object into string that is useful to shell'
673 if val in (None, False):
673 if val in (None, False):
674 return '0'
674 return '0'
675 if val == True:
675 if val == True:
676 return '1'
676 return '1'
677 return str(val)
677 return str(val)
678 oldenv = {}
678 oldenv = {}
679 for k in environ:
679 for k in environ:
680 oldenv[k] = os.environ.get(k)
680 oldenv[k] = os.environ.get(k)
681 if cwd is not None:
681 if cwd is not None:
682 oldcwd = os.getcwd()
682 oldcwd = os.getcwd()
683 origcmd = cmd
683 origcmd = cmd
684 if os.name == 'nt':
684 if os.name == 'nt':
685 cmd = '"%s"' % cmd
685 cmd = '"%s"' % cmd
686 try:
686 try:
687 for k, v in environ.iteritems():
687 for k, v in environ.iteritems():
688 os.environ[k] = py2shell(v)
688 os.environ[k] = py2shell(v)
689 os.environ['HG'] = hgexecutable()
689 os.environ['HG'] = hgexecutable()
690 if cwd is not None and oldcwd != cwd:
690 if cwd is not None and oldcwd != cwd:
691 os.chdir(cwd)
691 os.chdir(cwd)
692 rc = os.system(cmd)
692 rc = os.system(cmd)
693 if sys.platform == 'OpenVMS' and rc & 1:
693 if sys.platform == 'OpenVMS' and rc & 1:
694 rc = 0
694 rc = 0
695 if rc and onerr:
695 if rc and onerr:
696 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
696 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
697 explain_exit(rc)[0])
697 explain_exit(rc)[0])
698 if errprefix:
698 if errprefix:
699 errmsg = '%s: %s' % (errprefix, errmsg)
699 errmsg = '%s: %s' % (errprefix, errmsg)
700 try:
700 try:
701 onerr.warn(errmsg + '\n')
701 onerr.warn(errmsg + '\n')
702 except AttributeError:
702 except AttributeError:
703 raise onerr(errmsg)
703 raise onerr(errmsg)
704 return rc
704 return rc
705 finally:
705 finally:
706 for k, v in oldenv.iteritems():
706 for k, v in oldenv.iteritems():
707 if v is None:
707 if v is None:
708 del os.environ[k]
708 del os.environ[k]
709 else:
709 else:
710 os.environ[k] = v
710 os.environ[k] = v
711 if cwd is not None and oldcwd != cwd:
711 if cwd is not None and oldcwd != cwd:
712 os.chdir(oldcwd)
712 os.chdir(oldcwd)
713
713
714 def checksignature(func):
714 def checksignature(func):
715 '''wrap a function with code to check for calling errors'''
715 '''wrap a function with code to check for calling errors'''
716 def check(*args, **kwargs):
716 def check(*args, **kwargs):
717 try:
717 try:
718 return func(*args, **kwargs)
718 return func(*args, **kwargs)
719 except TypeError:
719 except TypeError:
720 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
720 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
721 raise error.SignatureError
721 raise error.SignatureError
722 raise
722 raise
723
723
724 return check
724 return check
725
725
726 # os.path.lexists is not available on python2.3
726 # os.path.lexists is not available on python2.3
727 def lexists(filename):
727 def lexists(filename):
728 "test whether a file with this name exists. does not follow symlinks"
728 "test whether a file with this name exists. does not follow symlinks"
729 try:
729 try:
730 os.lstat(filename)
730 os.lstat(filename)
731 except:
731 except:
732 return False
732 return False
733 return True
733 return True
734
734
735 def rename(src, dst):
735 def rename(src, dst):
736 """forcibly rename a file"""
736 """forcibly rename a file"""
737 try:
737 try:
738 os.rename(src, dst)
738 os.rename(src, dst)
739 except OSError, err: # FIXME: check err (EEXIST ?)
739 except OSError, err: # FIXME: check err (EEXIST ?)
740 # on windows, rename to existing file is not allowed, so we
740 # on windows, rename to existing file is not allowed, so we
741 # must delete destination first. but if file is open, unlink
741 # must delete destination first. but if file is open, unlink
742 # schedules it for delete but does not delete it. rename
742 # schedules it for delete but does not delete it. rename
743 # happens immediately even for open files, so we rename
743 # happens immediately even for open files, so we rename
744 # destination to a temporary name, then delete that. then
744 # destination to a temporary name, then delete that. then
745 # rename is safe to do.
745 # rename is safe to do.
746 temp = dst + "-force-rename"
746 temp = dst + "-force-rename"
747 os.rename(dst, temp)
747 os.rename(dst, temp)
748 os.unlink(temp)
748 os.unlink(temp)
749 os.rename(src, dst)
749 os.rename(src, dst)
750
750
751 def unlink(f):
751 def unlink(f):
752 """unlink and remove the directory if it is empty"""
752 """unlink and remove the directory if it is empty"""
753 os.unlink(f)
753 os.unlink(f)
754 # try removing directories that might now be empty
754 # try removing directories that might now be empty
755 try:
755 try:
756 os.removedirs(os.path.dirname(f))
756 os.removedirs(os.path.dirname(f))
757 except OSError:
757 except OSError:
758 pass
758 pass
759
759
760 def copyfile(src, dest):
760 def copyfile(src, dest):
761 "copy a file, preserving mode and atime/mtime"
761 "copy a file, preserving mode and atime/mtime"
762 if os.path.islink(src):
762 if os.path.islink(src):
763 try:
763 try:
764 os.unlink(dest)
764 os.unlink(dest)
765 except:
765 except:
766 pass
766 pass
767 os.symlink(os.readlink(src), dest)
767 os.symlink(os.readlink(src), dest)
768 else:
768 else:
769 try:
769 try:
770 shutil.copyfile(src, dest)
770 shutil.copyfile(src, dest)
771 shutil.copystat(src, dest)
771 shutil.copystat(src, dest)
772 except shutil.Error, inst:
772 except shutil.Error, inst:
773 raise Abort(str(inst))
773 raise Abort(str(inst))
774
774
775 def copyfiles(src, dst, hardlink=None):
775 def copyfiles(src, dst, hardlink=None):
776 """Copy a directory tree using hardlinks if possible"""
776 """Copy a directory tree using hardlinks if possible"""
777
777
778 if hardlink is None:
778 if hardlink is None:
779 hardlink = (os.stat(src).st_dev ==
779 hardlink = (os.stat(src).st_dev ==
780 os.stat(os.path.dirname(dst)).st_dev)
780 os.stat(os.path.dirname(dst)).st_dev)
781
781
782 if os.path.isdir(src):
782 if os.path.isdir(src):
783 os.mkdir(dst)
783 os.mkdir(dst)
784 for name, kind in osutil.listdir(src):
784 for name, kind in osutil.listdir(src):
785 srcname = os.path.join(src, name)
785 srcname = os.path.join(src, name)
786 dstname = os.path.join(dst, name)
786 dstname = os.path.join(dst, name)
787 copyfiles(srcname, dstname, hardlink)
787 copyfiles(srcname, dstname, hardlink)
788 else:
788 else:
789 if hardlink:
789 if hardlink:
790 try:
790 try:
791 os_link(src, dst)
791 os_link(src, dst)
792 except (IOError, OSError):
792 except (IOError, OSError):
793 hardlink = False
793 hardlink = False
794 shutil.copy(src, dst)
794 shutil.copy(src, dst)
795 else:
795 else:
796 shutil.copy(src, dst)
796 shutil.copy(src, dst)
797
797
798 class path_auditor(object):
798 class path_auditor(object):
799 '''ensure that a filesystem path contains no banned components.
799 '''ensure that a filesystem path contains no banned components.
800 the following properties of a path are checked:
800 the following properties of a path are checked:
801
801
802 - under top-level .hg
802 - under top-level .hg
803 - starts at the root of a windows drive
803 - starts at the root of a windows drive
804 - contains ".."
804 - contains ".."
805 - traverses a symlink (e.g. a/symlink_here/b)
805 - traverses a symlink (e.g. a/symlink_here/b)
806 - inside a nested repository'''
806 - inside a nested repository'''
807
807
808 def __init__(self, root):
808 def __init__(self, root):
809 self.audited = set()
809 self.audited = set()
810 self.auditeddir = set()
810 self.auditeddir = set()
811 self.root = root
811 self.root = root
812
812
813 def __call__(self, path):
813 def __call__(self, path):
814 if path in self.audited:
814 if path in self.audited:
815 return
815 return
816 normpath = os.path.normcase(path)
816 normpath = os.path.normcase(path)
817 parts = splitpath(normpath)
817 parts = splitpath(normpath)
818 if (os.path.splitdrive(path)[0]
818 if (os.path.splitdrive(path)[0]
819 or parts[0].lower() in ('.hg', '.hg.', '')
819 or parts[0].lower() in ('.hg', '.hg.', '')
820 or os.pardir in parts):
820 or os.pardir in parts):
821 raise Abort(_("path contains illegal component: %s") % path)
821 raise Abort(_("path contains illegal component: %s") % path)
822 if '.hg' in path.lower():
822 if '.hg' in path.lower():
823 lparts = [p.lower() for p in parts]
823 lparts = [p.lower() for p in parts]
824 for p in '.hg', '.hg.':
824 for p in '.hg', '.hg.':
825 if p in lparts[1:-1]:
825 if p in lparts[1:]:
826 pos = lparts.index(p)
826 pos = lparts.index(p)
827 base = os.path.join(*parts[:pos])
827 base = os.path.join(*parts[:pos])
828 raise Abort(_('path %r is inside repo %r') % (path, base))
828 raise Abort(_('path %r is inside repo %r') % (path, base))
829 def check(prefix):
829 def check(prefix):
830 curpath = os.path.join(self.root, prefix)
830 curpath = os.path.join(self.root, prefix)
831 try:
831 try:
832 st = os.lstat(curpath)
832 st = os.lstat(curpath)
833 except OSError, err:
833 except OSError, err:
834 # EINVAL can be raised as invalid path syntax under win32.
834 # EINVAL can be raised as invalid path syntax under win32.
835 # They must be ignored for patterns can be checked too.
835 # They must be ignored for patterns can be checked too.
836 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
836 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
837 raise
837 raise
838 else:
838 else:
839 if stat.S_ISLNK(st.st_mode):
839 if stat.S_ISLNK(st.st_mode):
840 raise Abort(_('path %r traverses symbolic link %r') %
840 raise Abort(_('path %r traverses symbolic link %r') %
841 (path, prefix))
841 (path, prefix))
842 elif (stat.S_ISDIR(st.st_mode) and
842 elif (stat.S_ISDIR(st.st_mode) and
843 os.path.isdir(os.path.join(curpath, '.hg'))):
843 os.path.isdir(os.path.join(curpath, '.hg'))):
844 raise Abort(_('path %r is inside repo %r') %
844 raise Abort(_('path %r is inside repo %r') %
845 (path, prefix))
845 (path, prefix))
846 parts.pop()
846 parts.pop()
847 prefixes = []
847 prefixes = []
848 for n in range(len(parts)):
848 for n in range(len(parts)):
849 prefix = os.sep.join(parts)
849 prefix = os.sep.join(parts)
850 if prefix in self.auditeddir:
850 if prefix in self.auditeddir:
851 break
851 break
852 check(prefix)
852 check(prefix)
853 prefixes.append(prefix)
853 prefixes.append(prefix)
854 parts.pop()
854 parts.pop()
855
855
856 self.audited.add(path)
856 self.audited.add(path)
857 # only add prefixes to the cache after checking everything: we don't
857 # only add prefixes to the cache after checking everything: we don't
858 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
858 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
859 self.auditeddir.update(prefixes)
859 self.auditeddir.update(prefixes)
860
860
861 def _makelock_file(info, pathname):
861 def _makelock_file(info, pathname):
862 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
862 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
863 os.write(ld, info)
863 os.write(ld, info)
864 os.close(ld)
864 os.close(ld)
865
865
866 def _readlock_file(pathname):
866 def _readlock_file(pathname):
867 return posixfile(pathname).read()
867 return posixfile(pathname).read()
868
868
869 def nlinks(pathname):
869 def nlinks(pathname):
870 """Return number of hardlinks for the given file."""
870 """Return number of hardlinks for the given file."""
871 return os.lstat(pathname).st_nlink
871 return os.lstat(pathname).st_nlink
872
872
873 if hasattr(os, 'link'):
873 if hasattr(os, 'link'):
874 os_link = os.link
874 os_link = os.link
875 else:
875 else:
876 def os_link(src, dst):
876 def os_link(src, dst):
877 raise OSError(0, _("Hardlinks not supported"))
877 raise OSError(0, _("Hardlinks not supported"))
878
878
879 def fstat(fp):
879 def fstat(fp):
880 '''stat file object that may not have fileno method.'''
880 '''stat file object that may not have fileno method.'''
881 try:
881 try:
882 return os.fstat(fp.fileno())
882 return os.fstat(fp.fileno())
883 except AttributeError:
883 except AttributeError:
884 return os.stat(fp.name)
884 return os.stat(fp.name)
885
885
886 posixfile = file
886 posixfile = file
887
887
888 def openhardlinks():
888 def openhardlinks():
889 '''return true if it is safe to hold open file handles to hardlinks'''
889 '''return true if it is safe to hold open file handles to hardlinks'''
890 return True
890 return True
891
891
892 def _statfiles(files):
892 def _statfiles(files):
893 'Stat each file in files and yield stat or None if file does not exist.'
893 'Stat each file in files and yield stat or None if file does not exist.'
894 lstat = os.lstat
894 lstat = os.lstat
895 for nf in files:
895 for nf in files:
896 try:
896 try:
897 st = lstat(nf)
897 st = lstat(nf)
898 except OSError, err:
898 except OSError, err:
899 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
899 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
900 raise
900 raise
901 st = None
901 st = None
902 yield st
902 yield st
903
903
904 def _statfiles_clustered(files):
904 def _statfiles_clustered(files):
905 '''Stat each file in files and yield stat or None if file does not exist.
905 '''Stat each file in files and yield stat or None if file does not exist.
906 Cluster and cache stat per directory to minimize number of OS stat calls.'''
906 Cluster and cache stat per directory to minimize number of OS stat calls.'''
907 lstat = os.lstat
907 lstat = os.lstat
908 ncase = os.path.normcase
908 ncase = os.path.normcase
909 sep = os.sep
909 sep = os.sep
910 dircache = {} # dirname -> filename -> status | None if file does not exist
910 dircache = {} # dirname -> filename -> status | None if file does not exist
911 for nf in files:
911 for nf in files:
912 nf = ncase(nf)
912 nf = ncase(nf)
913 pos = nf.rfind(sep)
913 pos = nf.rfind(sep)
914 if pos == -1:
914 if pos == -1:
915 dir, base = '.', nf
915 dir, base = '.', nf
916 else:
916 else:
917 dir, base = nf[:pos+1], nf[pos+1:]
917 dir, base = nf[:pos+1], nf[pos+1:]
918 cache = dircache.get(dir, None)
918 cache = dircache.get(dir, None)
919 if cache is None:
919 if cache is None:
920 try:
920 try:
921 dmap = dict([(ncase(n), s)
921 dmap = dict([(ncase(n), s)
922 for n, k, s in osutil.listdir(dir, True)])
922 for n, k, s in osutil.listdir(dir, True)])
923 except OSError, err:
923 except OSError, err:
924 # handle directory not found in Python version prior to 2.5
924 # handle directory not found in Python version prior to 2.5
925 # Python <= 2.4 returns native Windows code 3 in errno
925 # Python <= 2.4 returns native Windows code 3 in errno
926 # Python >= 2.5 returns ENOENT and adds winerror field
926 # Python >= 2.5 returns ENOENT and adds winerror field
927 # EINVAL is raised if dir is not a directory.
927 # EINVAL is raised if dir is not a directory.
928 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
928 if err.errno not in (3, errno.ENOENT, errno.EINVAL,
929 errno.ENOTDIR):
929 errno.ENOTDIR):
930 raise
930 raise
931 dmap = {}
931 dmap = {}
932 cache = dircache.setdefault(dir, dmap)
932 cache = dircache.setdefault(dir, dmap)
933 yield cache.get(base, None)
933 yield cache.get(base, None)
934
934
935 if sys.platform == 'win32':
935 if sys.platform == 'win32':
936 statfiles = _statfiles_clustered
936 statfiles = _statfiles_clustered
937 else:
937 else:
938 statfiles = _statfiles
938 statfiles = _statfiles
939
939
940 getuser_fallback = None
940 getuser_fallback = None
941
941
942 def getuser():
942 def getuser():
943 '''return name of current user'''
943 '''return name of current user'''
944 try:
944 try:
945 return getpass.getuser()
945 return getpass.getuser()
946 except ImportError:
946 except ImportError:
947 # import of pwd will fail on windows - try fallback
947 # import of pwd will fail on windows - try fallback
948 if getuser_fallback:
948 if getuser_fallback:
949 return getuser_fallback()
949 return getuser_fallback()
950 # raised if win32api not available
950 # raised if win32api not available
951 raise Abort(_('user name not available - set USERNAME '
951 raise Abort(_('user name not available - set USERNAME '
952 'environment variable'))
952 'environment variable'))
953
953
954 def username(uid=None):
954 def username(uid=None):
955 """Return the name of the user with the given uid.
955 """Return the name of the user with the given uid.
956
956
957 If uid is None, return the name of the current user."""
957 If uid is None, return the name of the current user."""
958 try:
958 try:
959 import pwd
959 import pwd
960 if uid is None:
960 if uid is None:
961 uid = os.getuid()
961 uid = os.getuid()
962 try:
962 try:
963 return pwd.getpwuid(uid)[0]
963 return pwd.getpwuid(uid)[0]
964 except KeyError:
964 except KeyError:
965 return str(uid)
965 return str(uid)
966 except ImportError:
966 except ImportError:
967 return None
967 return None
968
968
969 def groupname(gid=None):
969 def groupname(gid=None):
970 """Return the name of the group with the given gid.
970 """Return the name of the group with the given gid.
971
971
972 If gid is None, return the name of the current group."""
972 If gid is None, return the name of the current group."""
973 try:
973 try:
974 import grp
974 import grp
975 if gid is None:
975 if gid is None:
976 gid = os.getgid()
976 gid = os.getgid()
977 try:
977 try:
978 return grp.getgrgid(gid)[0]
978 return grp.getgrgid(gid)[0]
979 except KeyError:
979 except KeyError:
980 return str(gid)
980 return str(gid)
981 except ImportError:
981 except ImportError:
982 return None
982 return None
983
983
984 # File system features
984 # File system features
985
985
986 def checkcase(path):
986 def checkcase(path):
987 """
987 """
988 Check whether the given path is on a case-sensitive filesystem
988 Check whether the given path is on a case-sensitive filesystem
989
989
990 Requires a path (like /foo/.hg) ending with a foldable final
990 Requires a path (like /foo/.hg) ending with a foldable final
991 directory component.
991 directory component.
992 """
992 """
993 s1 = os.stat(path)
993 s1 = os.stat(path)
994 d, b = os.path.split(path)
994 d, b = os.path.split(path)
995 p2 = os.path.join(d, b.upper())
995 p2 = os.path.join(d, b.upper())
996 if path == p2:
996 if path == p2:
997 p2 = os.path.join(d, b.lower())
997 p2 = os.path.join(d, b.lower())
998 try:
998 try:
999 s2 = os.stat(p2)
999 s2 = os.stat(p2)
1000 if s2 == s1:
1000 if s2 == s1:
1001 return False
1001 return False
1002 return True
1002 return True
1003 except:
1003 except:
1004 return True
1004 return True
1005
1005
1006 _fspathcache = {}
1006 _fspathcache = {}
1007 def fspath(name, root):
1007 def fspath(name, root):
1008 '''Get name in the case stored in the filesystem
1008 '''Get name in the case stored in the filesystem
1009
1009
1010 The name is either relative to root, or it is an absolute path starting
1010 The name is either relative to root, or it is an absolute path starting
1011 with root. Note that this function is unnecessary, and should not be
1011 with root. Note that this function is unnecessary, and should not be
1012 called, for case-sensitive filesystems (simply because it's expensive).
1012 called, for case-sensitive filesystems (simply because it's expensive).
1013 '''
1013 '''
1014 # If name is absolute, make it relative
1014 # If name is absolute, make it relative
1015 if name.lower().startswith(root.lower()):
1015 if name.lower().startswith(root.lower()):
1016 l = len(root)
1016 l = len(root)
1017 if name[l] == os.sep or name[l] == os.altsep:
1017 if name[l] == os.sep or name[l] == os.altsep:
1018 l = l + 1
1018 l = l + 1
1019 name = name[l:]
1019 name = name[l:]
1020
1020
1021 if not os.path.exists(os.path.join(root, name)):
1021 if not os.path.exists(os.path.join(root, name)):
1022 return None
1022 return None
1023
1023
1024 seps = os.sep
1024 seps = os.sep
1025 if os.altsep:
1025 if os.altsep:
1026 seps = seps + os.altsep
1026 seps = seps + os.altsep
1027 # Protect backslashes. This gets silly very quickly.
1027 # Protect backslashes. This gets silly very quickly.
1028 seps.replace('\\','\\\\')
1028 seps.replace('\\','\\\\')
1029 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1029 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
1030 dir = os.path.normcase(os.path.normpath(root))
1030 dir = os.path.normcase(os.path.normpath(root))
1031 result = []
1031 result = []
1032 for part, sep in pattern.findall(name):
1032 for part, sep in pattern.findall(name):
1033 if sep:
1033 if sep:
1034 result.append(sep)
1034 result.append(sep)
1035 continue
1035 continue
1036
1036
1037 if dir not in _fspathcache:
1037 if dir not in _fspathcache:
1038 _fspathcache[dir] = os.listdir(dir)
1038 _fspathcache[dir] = os.listdir(dir)
1039 contents = _fspathcache[dir]
1039 contents = _fspathcache[dir]
1040
1040
1041 lpart = part.lower()
1041 lpart = part.lower()
1042 for n in contents:
1042 for n in contents:
1043 if n.lower() == lpart:
1043 if n.lower() == lpart:
1044 result.append(n)
1044 result.append(n)
1045 break
1045 break
1046 else:
1046 else:
1047 # Cannot happen, as the file exists!
1047 # Cannot happen, as the file exists!
1048 result.append(part)
1048 result.append(part)
1049 dir = os.path.join(dir, lpart)
1049 dir = os.path.join(dir, lpart)
1050
1050
1051 return ''.join(result)
1051 return ''.join(result)
1052
1052
1053 def checkexec(path):
1053 def checkexec(path):
1054 """
1054 """
1055 Check whether the given path is on a filesystem with UNIX-like exec flags
1055 Check whether the given path is on a filesystem with UNIX-like exec flags
1056
1056
1057 Requires a directory (like /foo/.hg)
1057 Requires a directory (like /foo/.hg)
1058 """
1058 """
1059
1059
1060 # VFAT on some Linux versions can flip mode but it doesn't persist
1060 # VFAT on some Linux versions can flip mode but it doesn't persist
1061 # a FS remount. Frequently we can detect it if files are created
1061 # a FS remount. Frequently we can detect it if files are created
1062 # with exec bit on.
1062 # with exec bit on.
1063
1063
1064 try:
1064 try:
1065 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1065 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1066 fh, fn = tempfile.mkstemp("", "", path)
1066 fh, fn = tempfile.mkstemp("", "", path)
1067 try:
1067 try:
1068 os.close(fh)
1068 os.close(fh)
1069 m = os.stat(fn).st_mode & 0777
1069 m = os.stat(fn).st_mode & 0777
1070 new_file_has_exec = m & EXECFLAGS
1070 new_file_has_exec = m & EXECFLAGS
1071 os.chmod(fn, m ^ EXECFLAGS)
1071 os.chmod(fn, m ^ EXECFLAGS)
1072 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1072 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0777) == m)
1073 finally:
1073 finally:
1074 os.unlink(fn)
1074 os.unlink(fn)
1075 except (IOError, OSError):
1075 except (IOError, OSError):
1076 # we don't care, the user probably won't be able to commit anyway
1076 # we don't care, the user probably won't be able to commit anyway
1077 return False
1077 return False
1078 return not (new_file_has_exec or exec_flags_cannot_flip)
1078 return not (new_file_has_exec or exec_flags_cannot_flip)
1079
1079
1080 def checklink(path):
1080 def checklink(path):
1081 """check whether the given path is on a symlink-capable filesystem"""
1081 """check whether the given path is on a symlink-capable filesystem"""
1082 # mktemp is not racy because symlink creation will fail if the
1082 # mktemp is not racy because symlink creation will fail if the
1083 # file already exists
1083 # file already exists
1084 name = tempfile.mktemp(dir=path)
1084 name = tempfile.mktemp(dir=path)
1085 try:
1085 try:
1086 os.symlink(".", name)
1086 os.symlink(".", name)
1087 os.unlink(name)
1087 os.unlink(name)
1088 return True
1088 return True
1089 except (OSError, AttributeError):
1089 except (OSError, AttributeError):
1090 return False
1090 return False
1091
1091
1092 _umask = os.umask(0)
1092 _umask = os.umask(0)
1093 os.umask(_umask)
1093 os.umask(_umask)
1094
1094
1095 def needbinarypatch():
1095 def needbinarypatch():
1096 """return True if patches should be applied in binary mode by default."""
1096 """return True if patches should be applied in binary mode by default."""
1097 return os.name == 'nt'
1097 return os.name == 'nt'
1098
1098
1099 def endswithsep(path):
1099 def endswithsep(path):
1100 '''Check path ends with os.sep or os.altsep.'''
1100 '''Check path ends with os.sep or os.altsep.'''
1101 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1101 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
1102
1102
1103 def splitpath(path):
1103 def splitpath(path):
1104 '''Split path by os.sep.
1104 '''Split path by os.sep.
1105 Note that this function does not use os.altsep because this is
1105 Note that this function does not use os.altsep because this is
1106 an alternative of simple "xxx.split(os.sep)".
1106 an alternative of simple "xxx.split(os.sep)".
1107 It is recommended to use os.path.normpath() before using this
1107 It is recommended to use os.path.normpath() before using this
1108 function if need.'''
1108 function if need.'''
1109 return path.split(os.sep)
1109 return path.split(os.sep)
1110
1110
1111 def gui():
1111 def gui():
1112 '''Are we running in a GUI?'''
1112 '''Are we running in a GUI?'''
1113 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1113 return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
1114
1114
1115 def lookup_reg(key, name=None, scope=None):
1115 def lookup_reg(key, name=None, scope=None):
1116 return None
1116 return None
1117
1117
1118 # Platform specific variants
1118 # Platform specific variants
1119 if os.name == 'nt':
1119 if os.name == 'nt':
1120 import msvcrt
1120 import msvcrt
1121 nulldev = 'NUL:'
1121 nulldev = 'NUL:'
1122
1122
1123 class winstdout:
1123 class winstdout:
1124 '''stdout on windows misbehaves if sent through a pipe'''
1124 '''stdout on windows misbehaves if sent through a pipe'''
1125
1125
1126 def __init__(self, fp):
1126 def __init__(self, fp):
1127 self.fp = fp
1127 self.fp = fp
1128
1128
1129 def __getattr__(self, key):
1129 def __getattr__(self, key):
1130 return getattr(self.fp, key)
1130 return getattr(self.fp, key)
1131
1131
1132 def close(self):
1132 def close(self):
1133 try:
1133 try:
1134 self.fp.close()
1134 self.fp.close()
1135 except: pass
1135 except: pass
1136
1136
1137 def write(self, s):
1137 def write(self, s):
1138 try:
1138 try:
1139 # This is workaround for "Not enough space" error on
1139 # This is workaround for "Not enough space" error on
1140 # writing large size of data to console.
1140 # writing large size of data to console.
1141 limit = 16000
1141 limit = 16000
1142 l = len(s)
1142 l = len(s)
1143 start = 0
1143 start = 0
1144 while start < l:
1144 while start < l:
1145 end = start + limit
1145 end = start + limit
1146 self.fp.write(s[start:end])
1146 self.fp.write(s[start:end])
1147 start = end
1147 start = end
1148 except IOError, inst:
1148 except IOError, inst:
1149 if inst.errno != 0: raise
1149 if inst.errno != 0: raise
1150 self.close()
1150 self.close()
1151 raise IOError(errno.EPIPE, 'Broken pipe')
1151 raise IOError(errno.EPIPE, 'Broken pipe')
1152
1152
1153 def flush(self):
1153 def flush(self):
1154 try:
1154 try:
1155 return self.fp.flush()
1155 return self.fp.flush()
1156 except IOError, inst:
1156 except IOError, inst:
1157 if inst.errno != errno.EINVAL: raise
1157 if inst.errno != errno.EINVAL: raise
1158 self.close()
1158 self.close()
1159 raise IOError(errno.EPIPE, 'Broken pipe')
1159 raise IOError(errno.EPIPE, 'Broken pipe')
1160
1160
1161 sys.stdout = winstdout(sys.stdout)
1161 sys.stdout = winstdout(sys.stdout)
1162
1162
1163 def _is_win_9x():
1163 def _is_win_9x():
1164 '''return true if run on windows 95, 98 or me.'''
1164 '''return true if run on windows 95, 98 or me.'''
1165 try:
1165 try:
1166 return sys.getwindowsversion()[3] == 1
1166 return sys.getwindowsversion()[3] == 1
1167 except AttributeError:
1167 except AttributeError:
1168 return 'command' in os.environ.get('comspec', '')
1168 return 'command' in os.environ.get('comspec', '')
1169
1169
1170 def openhardlinks():
1170 def openhardlinks():
1171 return not _is_win_9x and "win32api" in locals()
1171 return not _is_win_9x and "win32api" in locals()
1172
1172
1173 def system_rcpath():
1173 def system_rcpath():
1174 try:
1174 try:
1175 return system_rcpath_win32()
1175 return system_rcpath_win32()
1176 except:
1176 except:
1177 return [r'c:\mercurial\mercurial.ini']
1177 return [r'c:\mercurial\mercurial.ini']
1178
1178
1179 def user_rcpath():
1179 def user_rcpath():
1180 '''return os-specific hgrc search path to the user dir'''
1180 '''return os-specific hgrc search path to the user dir'''
1181 try:
1181 try:
1182 path = user_rcpath_win32()
1182 path = user_rcpath_win32()
1183 except:
1183 except:
1184 home = os.path.expanduser('~')
1184 home = os.path.expanduser('~')
1185 path = [os.path.join(home, 'mercurial.ini'),
1185 path = [os.path.join(home, 'mercurial.ini'),
1186 os.path.join(home, '.hgrc')]
1186 os.path.join(home, '.hgrc')]
1187 userprofile = os.environ.get('USERPROFILE')
1187 userprofile = os.environ.get('USERPROFILE')
1188 if userprofile:
1188 if userprofile:
1189 path.append(os.path.join(userprofile, 'mercurial.ini'))
1189 path.append(os.path.join(userprofile, 'mercurial.ini'))
1190 path.append(os.path.join(userprofile, '.hgrc'))
1190 path.append(os.path.join(userprofile, '.hgrc'))
1191 return path
1191 return path
1192
1192
1193 def parse_patch_output(output_line):
1193 def parse_patch_output(output_line):
1194 """parses the output produced by patch and returns the file name"""
1194 """parses the output produced by patch and returns the file name"""
1195 pf = output_line[14:]
1195 pf = output_line[14:]
1196 if pf[0] == '`':
1196 if pf[0] == '`':
1197 pf = pf[1:-1] # Remove the quotes
1197 pf = pf[1:-1] # Remove the quotes
1198 return pf
1198 return pf
1199
1199
1200 def sshargs(sshcmd, host, user, port):
1200 def sshargs(sshcmd, host, user, port):
1201 '''Build argument list for ssh or Plink'''
1201 '''Build argument list for ssh or Plink'''
1202 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1202 pflag = 'plink' in sshcmd.lower() and '-P' or '-p'
1203 args = user and ("%s@%s" % (user, host)) or host
1203 args = user and ("%s@%s" % (user, host)) or host
1204 return port and ("%s %s %s" % (args, pflag, port)) or args
1204 return port and ("%s %s %s" % (args, pflag, port)) or args
1205
1205
1206 def testpid(pid):
1206 def testpid(pid):
1207 '''return False if pid dead, True if running or not known'''
1207 '''return False if pid dead, True if running or not known'''
1208 return True
1208 return True
1209
1209
1210 def set_flags(f, l, x):
1210 def set_flags(f, l, x):
1211 pass
1211 pass
1212
1212
1213 def set_binary(fd):
1213 def set_binary(fd):
1214 # When run without console, pipes may expose invalid
1214 # When run without console, pipes may expose invalid
1215 # fileno(), usually set to -1.
1215 # fileno(), usually set to -1.
1216 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1216 if hasattr(fd, 'fileno') and fd.fileno() >= 0:
1217 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1217 msvcrt.setmode(fd.fileno(), os.O_BINARY)
1218
1218
1219 def pconvert(path):
1219 def pconvert(path):
1220 return '/'.join(splitpath(path))
1220 return '/'.join(splitpath(path))
1221
1221
1222 def localpath(path):
1222 def localpath(path):
1223 return path.replace('/', '\\')
1223 return path.replace('/', '\\')
1224
1224
1225 def normpath(path):
1225 def normpath(path):
1226 return pconvert(os.path.normpath(path))
1226 return pconvert(os.path.normpath(path))
1227
1227
1228 makelock = _makelock_file
1228 makelock = _makelock_file
1229 readlock = _readlock_file
1229 readlock = _readlock_file
1230
1230
1231 def samestat(s1, s2):
1231 def samestat(s1, s2):
1232 return False
1232 return False
1233
1233
1234 # A sequence of backslashes is special iff it precedes a double quote:
1234 # A sequence of backslashes is special iff it precedes a double quote:
1235 # - if there's an even number of backslashes, the double quote is not
1235 # - if there's an even number of backslashes, the double quote is not
1236 # quoted (i.e. it ends the quoted region)
1236 # quoted (i.e. it ends the quoted region)
1237 # - if there's an odd number of backslashes, the double quote is quoted
1237 # - if there's an odd number of backslashes, the double quote is quoted
1238 # - in both cases, every pair of backslashes is unquoted into a single
1238 # - in both cases, every pair of backslashes is unquoted into a single
1239 # backslash
1239 # backslash
1240 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1240 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
1241 # So, to quote a string, we must surround it in double quotes, double
1241 # So, to quote a string, we must surround it in double quotes, double
1242 # the number of backslashes that preceed double quotes and add another
1242 # the number of backslashes that preceed double quotes and add another
1243 # backslash before every double quote (being careful with the double
1243 # backslash before every double quote (being careful with the double
1244 # quote we've appended to the end)
1244 # quote we've appended to the end)
1245 _quotere = None
1245 _quotere = None
1246 def shellquote(s):
1246 def shellquote(s):
1247 global _quotere
1247 global _quotere
1248 if _quotere is None:
1248 if _quotere is None:
1249 _quotere = re.compile(r'(\\*)("|\\$)')
1249 _quotere = re.compile(r'(\\*)("|\\$)')
1250 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1250 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
1251
1251
1252 def quotecommand(cmd):
1252 def quotecommand(cmd):
1253 """Build a command string suitable for os.popen* calls."""
1253 """Build a command string suitable for os.popen* calls."""
1254 # The extra quotes are needed because popen* runs the command
1254 # The extra quotes are needed because popen* runs the command
1255 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1255 # through the current COMSPEC. cmd.exe suppress enclosing quotes.
1256 return '"' + cmd + '"'
1256 return '"' + cmd + '"'
1257
1257
1258 def popen(command, mode='r'):
1258 def popen(command, mode='r'):
1259 # Work around "popen spawned process may not write to stdout
1259 # Work around "popen spawned process may not write to stdout
1260 # under windows"
1260 # under windows"
1261 # http://bugs.python.org/issue1366
1261 # http://bugs.python.org/issue1366
1262 command += " 2> %s" % nulldev
1262 command += " 2> %s" % nulldev
1263 return os.popen(quotecommand(command), mode)
1263 return os.popen(quotecommand(command), mode)
1264
1264
1265 def explain_exit(code):
1265 def explain_exit(code):
1266 return _("exited with status %d") % code, code
1266 return _("exited with status %d") % code, code
1267
1267
1268 # if you change this stub into a real check, please try to implement the
1268 # if you change this stub into a real check, please try to implement the
1269 # username and groupname functions above, too.
1269 # username and groupname functions above, too.
1270 def isowner(fp, st=None):
1270 def isowner(fp, st=None):
1271 return True
1271 return True
1272
1272
1273 def find_exe(command):
1273 def find_exe(command):
1274 '''Find executable for command searching like cmd.exe does.
1274 '''Find executable for command searching like cmd.exe does.
1275 If command is a basename then PATH is searched for command.
1275 If command is a basename then PATH is searched for command.
1276 PATH isn't searched if command is an absolute or relative path.
1276 PATH isn't searched if command is an absolute or relative path.
1277 An extension from PATHEXT is found and added if not present.
1277 An extension from PATHEXT is found and added if not present.
1278 If command isn't found None is returned.'''
1278 If command isn't found None is returned.'''
1279 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1279 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
1280 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
1280 pathexts = [ext for ext in pathext.lower().split(os.pathsep)]
1281 if os.path.splitext(command)[1].lower() in pathexts:
1281 if os.path.splitext(command)[1].lower() in pathexts:
1282 pathexts = ['']
1282 pathexts = ['']
1283
1283
1284 def findexisting(pathcommand):
1284 def findexisting(pathcommand):
1285 'Will append extension (if needed) and return existing file'
1285 'Will append extension (if needed) and return existing file'
1286 for ext in pathexts:
1286 for ext in pathexts:
1287 executable = pathcommand + ext
1287 executable = pathcommand + ext
1288 if os.path.exists(executable):
1288 if os.path.exists(executable):
1289 return executable
1289 return executable
1290 return None
1290 return None
1291
1291
1292 if os.sep in command:
1292 if os.sep in command:
1293 return findexisting(command)
1293 return findexisting(command)
1294
1294
1295 for path in os.environ.get('PATH', '').split(os.pathsep):
1295 for path in os.environ.get('PATH', '').split(os.pathsep):
1296 executable = findexisting(os.path.join(path, command))
1296 executable = findexisting(os.path.join(path, command))
1297 if executable is not None:
1297 if executable is not None:
1298 return executable
1298 return executable
1299 return None
1299 return None
1300
1300
1301 def set_signal_handler():
1301 def set_signal_handler():
1302 try:
1302 try:
1303 set_signal_handler_win32()
1303 set_signal_handler_win32()
1304 except NameError:
1304 except NameError:
1305 pass
1305 pass
1306
1306
1307 try:
1307 try:
1308 # override functions with win32 versions if possible
1308 # override functions with win32 versions if possible
1309 from util_win32 import *
1309 from util_win32 import *
1310 if not _is_win_9x():
1310 if not _is_win_9x():
1311 posixfile = posixfile_nt
1311 posixfile = posixfile_nt
1312 except ImportError:
1312 except ImportError:
1313 pass
1313 pass
1314
1314
1315 else:
1315 else:
1316 nulldev = '/dev/null'
1316 nulldev = '/dev/null'
1317
1317
1318 def rcfiles(path):
1318 def rcfiles(path):
1319 rcs = [os.path.join(path, 'hgrc')]
1319 rcs = [os.path.join(path, 'hgrc')]
1320 rcdir = os.path.join(path, 'hgrc.d')
1320 rcdir = os.path.join(path, 'hgrc.d')
1321 try:
1321 try:
1322 rcs.extend([os.path.join(rcdir, f)
1322 rcs.extend([os.path.join(rcdir, f)
1323 for f, kind in osutil.listdir(rcdir)
1323 for f, kind in osutil.listdir(rcdir)
1324 if f.endswith(".rc")])
1324 if f.endswith(".rc")])
1325 except OSError:
1325 except OSError:
1326 pass
1326 pass
1327 return rcs
1327 return rcs
1328
1328
1329 def system_rcpath():
1329 def system_rcpath():
1330 path = []
1330 path = []
1331 # old mod_python does not set sys.argv
1331 # old mod_python does not set sys.argv
1332 if len(getattr(sys, 'argv', [])) > 0:
1332 if len(getattr(sys, 'argv', [])) > 0:
1333 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1333 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1334 '/../etc/mercurial'))
1334 '/../etc/mercurial'))
1335 path.extend(rcfiles('/etc/mercurial'))
1335 path.extend(rcfiles('/etc/mercurial'))
1336 return path
1336 return path
1337
1337
1338 def user_rcpath():
1338 def user_rcpath():
1339 return [os.path.expanduser('~/.hgrc')]
1339 return [os.path.expanduser('~/.hgrc')]
1340
1340
1341 def parse_patch_output(output_line):
1341 def parse_patch_output(output_line):
1342 """parses the output produced by patch and returns the file name"""
1342 """parses the output produced by patch and returns the file name"""
1343 pf = output_line[14:]
1343 pf = output_line[14:]
1344 if os.sys.platform == 'OpenVMS':
1344 if os.sys.platform == 'OpenVMS':
1345 if pf[0] == '`':
1345 if pf[0] == '`':
1346 pf = pf[1:-1] # Remove the quotes
1346 pf = pf[1:-1] # Remove the quotes
1347 else:
1347 else:
1348 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1348 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1349 pf = pf[1:-1] # Remove the quotes
1349 pf = pf[1:-1] # Remove the quotes
1350 return pf
1350 return pf
1351
1351
1352 def sshargs(sshcmd, host, user, port):
1352 def sshargs(sshcmd, host, user, port):
1353 '''Build argument list for ssh'''
1353 '''Build argument list for ssh'''
1354 args = user and ("%s@%s" % (user, host)) or host
1354 args = user and ("%s@%s" % (user, host)) or host
1355 return port and ("%s -p %s" % (args, port)) or args
1355 return port and ("%s -p %s" % (args, port)) or args
1356
1356
1357 def is_exec(f):
1357 def is_exec(f):
1358 """check whether a file is executable"""
1358 """check whether a file is executable"""
1359 return (os.lstat(f).st_mode & 0100 != 0)
1359 return (os.lstat(f).st_mode & 0100 != 0)
1360
1360
1361 def set_flags(f, l, x):
1361 def set_flags(f, l, x):
1362 s = os.lstat(f).st_mode
1362 s = os.lstat(f).st_mode
1363 if l:
1363 if l:
1364 if not stat.S_ISLNK(s):
1364 if not stat.S_ISLNK(s):
1365 # switch file to link
1365 # switch file to link
1366 data = file(f).read()
1366 data = file(f).read()
1367 os.unlink(f)
1367 os.unlink(f)
1368 try:
1368 try:
1369 os.symlink(data, f)
1369 os.symlink(data, f)
1370 except:
1370 except:
1371 # failed to make a link, rewrite file
1371 # failed to make a link, rewrite file
1372 file(f, "w").write(data)
1372 file(f, "w").write(data)
1373 # no chmod needed at this point
1373 # no chmod needed at this point
1374 return
1374 return
1375 if stat.S_ISLNK(s):
1375 if stat.S_ISLNK(s):
1376 # switch link to file
1376 # switch link to file
1377 data = os.readlink(f)
1377 data = os.readlink(f)
1378 os.unlink(f)
1378 os.unlink(f)
1379 file(f, "w").write(data)
1379 file(f, "w").write(data)
1380 s = 0666 & ~_umask # avoid restatting for chmod
1380 s = 0666 & ~_umask # avoid restatting for chmod
1381
1381
1382 sx = s & 0100
1382 sx = s & 0100
1383 if x and not sx:
1383 if x and not sx:
1384 # Turn on +x for every +r bit when making a file executable
1384 # Turn on +x for every +r bit when making a file executable
1385 # and obey umask.
1385 # and obey umask.
1386 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1386 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1387 elif not x and sx:
1387 elif not x and sx:
1388 # Turn off all +x bits
1388 # Turn off all +x bits
1389 os.chmod(f, s & 0666)
1389 os.chmod(f, s & 0666)
1390
1390
1391 def set_binary(fd):
1391 def set_binary(fd):
1392 pass
1392 pass
1393
1393
1394 def pconvert(path):
1394 def pconvert(path):
1395 return path
1395 return path
1396
1396
1397 def localpath(path):
1397 def localpath(path):
1398 return path
1398 return path
1399
1399
1400 normpath = os.path.normpath
1400 normpath = os.path.normpath
1401 samestat = os.path.samestat
1401 samestat = os.path.samestat
1402
1402
1403 def makelock(info, pathname):
1403 def makelock(info, pathname):
1404 try:
1404 try:
1405 os.symlink(info, pathname)
1405 os.symlink(info, pathname)
1406 except OSError, why:
1406 except OSError, why:
1407 if why.errno == errno.EEXIST:
1407 if why.errno == errno.EEXIST:
1408 raise
1408 raise
1409 else:
1409 else:
1410 _makelock_file(info, pathname)
1410 _makelock_file(info, pathname)
1411
1411
1412 def readlock(pathname):
1412 def readlock(pathname):
1413 try:
1413 try:
1414 return os.readlink(pathname)
1414 return os.readlink(pathname)
1415 except OSError, why:
1415 except OSError, why:
1416 if why.errno in (errno.EINVAL, errno.ENOSYS):
1416 if why.errno in (errno.EINVAL, errno.ENOSYS):
1417 return _readlock_file(pathname)
1417 return _readlock_file(pathname)
1418 else:
1418 else:
1419 raise
1419 raise
1420
1420
1421 def shellquote(s):
1421 def shellquote(s):
1422 if os.sys.platform == 'OpenVMS':
1422 if os.sys.platform == 'OpenVMS':
1423 return '"%s"' % s
1423 return '"%s"' % s
1424 else:
1424 else:
1425 return "'%s'" % s.replace("'", "'\\''")
1425 return "'%s'" % s.replace("'", "'\\''")
1426
1426
1427 def quotecommand(cmd):
1427 def quotecommand(cmd):
1428 return cmd
1428 return cmd
1429
1429
1430 def popen(command, mode='r'):
1430 def popen(command, mode='r'):
1431 return os.popen(command, mode)
1431 return os.popen(command, mode)
1432
1432
1433 def testpid(pid):
1433 def testpid(pid):
1434 '''return False if pid dead, True if running or not sure'''
1434 '''return False if pid dead, True if running or not sure'''
1435 if os.sys.platform == 'OpenVMS':
1435 if os.sys.platform == 'OpenVMS':
1436 return True
1436 return True
1437 try:
1437 try:
1438 os.kill(pid, 0)
1438 os.kill(pid, 0)
1439 return True
1439 return True
1440 except OSError, inst:
1440 except OSError, inst:
1441 return inst.errno != errno.ESRCH
1441 return inst.errno != errno.ESRCH
1442
1442
1443 def explain_exit(code):
1443 def explain_exit(code):
1444 """return a 2-tuple (desc, code) describing a process's status"""
1444 """return a 2-tuple (desc, code) describing a process's status"""
1445 if os.WIFEXITED(code):
1445 if os.WIFEXITED(code):
1446 val = os.WEXITSTATUS(code)
1446 val = os.WEXITSTATUS(code)
1447 return _("exited with status %d") % val, val
1447 return _("exited with status %d") % val, val
1448 elif os.WIFSIGNALED(code):
1448 elif os.WIFSIGNALED(code):
1449 val = os.WTERMSIG(code)
1449 val = os.WTERMSIG(code)
1450 return _("killed by signal %d") % val, val
1450 return _("killed by signal %d") % val, val
1451 elif os.WIFSTOPPED(code):
1451 elif os.WIFSTOPPED(code):
1452 val = os.WSTOPSIG(code)
1452 val = os.WSTOPSIG(code)
1453 return _("stopped by signal %d") % val, val
1453 return _("stopped by signal %d") % val, val
1454 raise ValueError(_("invalid exit code"))
1454 raise ValueError(_("invalid exit code"))
1455
1455
1456 def isowner(fp, st=None):
1456 def isowner(fp, st=None):
1457 """Return True if the file object f belongs to the current user.
1457 """Return True if the file object f belongs to the current user.
1458
1458
1459 The return value of a util.fstat(f) may be passed as the st argument.
1459 The return value of a util.fstat(f) may be passed as the st argument.
1460 """
1460 """
1461 if st is None:
1461 if st is None:
1462 st = fstat(fp)
1462 st = fstat(fp)
1463 return st.st_uid == os.getuid()
1463 return st.st_uid == os.getuid()
1464
1464
1465 def find_exe(command):
1465 def find_exe(command):
1466 '''Find executable for command searching like which does.
1466 '''Find executable for command searching like which does.
1467 If command is a basename then PATH is searched for command.
1467 If command is a basename then PATH is searched for command.
1468 PATH isn't searched if command is an absolute or relative path.
1468 PATH isn't searched if command is an absolute or relative path.
1469 If command isn't found None is returned.'''
1469 If command isn't found None is returned.'''
1470 if sys.platform == 'OpenVMS':
1470 if sys.platform == 'OpenVMS':
1471 return command
1471 return command
1472
1472
1473 def findexisting(executable):
1473 def findexisting(executable):
1474 'Will return executable if existing file'
1474 'Will return executable if existing file'
1475 if os.path.exists(executable):
1475 if os.path.exists(executable):
1476 return executable
1476 return executable
1477 return None
1477 return None
1478
1478
1479 if os.sep in command:
1479 if os.sep in command:
1480 return findexisting(command)
1480 return findexisting(command)
1481
1481
1482 for path in os.environ.get('PATH', '').split(os.pathsep):
1482 for path in os.environ.get('PATH', '').split(os.pathsep):
1483 executable = findexisting(os.path.join(path, command))
1483 executable = findexisting(os.path.join(path, command))
1484 if executable is not None:
1484 if executable is not None:
1485 return executable
1485 return executable
1486 return None
1486 return None
1487
1487
1488 def set_signal_handler():
1488 def set_signal_handler():
1489 pass
1489 pass
1490
1490
1491 def mktempcopy(name, emptyok=False, createmode=None):
1491 def mktempcopy(name, emptyok=False, createmode=None):
1492 """Create a temporary file with the same contents from name
1492 """Create a temporary file with the same contents from name
1493
1493
1494 The permission bits are copied from the original file.
1494 The permission bits are copied from the original file.
1495
1495
1496 If the temporary file is going to be truncated immediately, you
1496 If the temporary file is going to be truncated immediately, you
1497 can use emptyok=True as an optimization.
1497 can use emptyok=True as an optimization.
1498
1498
1499 Returns the name of the temporary file.
1499 Returns the name of the temporary file.
1500 """
1500 """
1501 d, fn = os.path.split(name)
1501 d, fn = os.path.split(name)
1502 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1502 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1503 os.close(fd)
1503 os.close(fd)
1504 # Temporary files are created with mode 0600, which is usually not
1504 # Temporary files are created with mode 0600, which is usually not
1505 # what we want. If the original file already exists, just copy
1505 # what we want. If the original file already exists, just copy
1506 # its mode. Otherwise, manually obey umask.
1506 # its mode. Otherwise, manually obey umask.
1507 try:
1507 try:
1508 st_mode = os.lstat(name).st_mode & 0777
1508 st_mode = os.lstat(name).st_mode & 0777
1509 except OSError, inst:
1509 except OSError, inst:
1510 if inst.errno != errno.ENOENT:
1510 if inst.errno != errno.ENOENT:
1511 raise
1511 raise
1512 st_mode = createmode
1512 st_mode = createmode
1513 if st_mode is None:
1513 if st_mode is None:
1514 st_mode = ~_umask
1514 st_mode = ~_umask
1515 st_mode &= 0666
1515 st_mode &= 0666
1516 os.chmod(temp, st_mode)
1516 os.chmod(temp, st_mode)
1517 if emptyok:
1517 if emptyok:
1518 return temp
1518 return temp
1519 try:
1519 try:
1520 try:
1520 try:
1521 ifp = posixfile(name, "rb")
1521 ifp = posixfile(name, "rb")
1522 except IOError, inst:
1522 except IOError, inst:
1523 if inst.errno == errno.ENOENT:
1523 if inst.errno == errno.ENOENT:
1524 return temp
1524 return temp
1525 if not getattr(inst, 'filename', None):
1525 if not getattr(inst, 'filename', None):
1526 inst.filename = name
1526 inst.filename = name
1527 raise
1527 raise
1528 ofp = posixfile(temp, "wb")
1528 ofp = posixfile(temp, "wb")
1529 for chunk in filechunkiter(ifp):
1529 for chunk in filechunkiter(ifp):
1530 ofp.write(chunk)
1530 ofp.write(chunk)
1531 ifp.close()
1531 ifp.close()
1532 ofp.close()
1532 ofp.close()
1533 except:
1533 except:
1534 try: os.unlink(temp)
1534 try: os.unlink(temp)
1535 except: pass
1535 except: pass
1536 raise
1536 raise
1537 return temp
1537 return temp
1538
1538
1539 class atomictempfile(posixfile):
1539 class atomictempfile(posixfile):
1540 """file-like object that atomically updates a file
1540 """file-like object that atomically updates a file
1541
1541
1542 All writes will be redirected to a temporary copy of the original
1542 All writes will be redirected to a temporary copy of the original
1543 file. When rename is called, the copy is renamed to the original
1543 file. When rename is called, the copy is renamed to the original
1544 name, making the changes visible.
1544 name, making the changes visible.
1545 """
1545 """
1546 def __init__(self, name, mode, createmode):
1546 def __init__(self, name, mode, createmode):
1547 self.__name = name
1547 self.__name = name
1548 self.temp = mktempcopy(name, emptyok=('w' in mode),
1548 self.temp = mktempcopy(name, emptyok=('w' in mode),
1549 createmode=createmode)
1549 createmode=createmode)
1550 posixfile.__init__(self, self.temp, mode)
1550 posixfile.__init__(self, self.temp, mode)
1551
1551
1552 def rename(self):
1552 def rename(self):
1553 if not self.closed:
1553 if not self.closed:
1554 posixfile.close(self)
1554 posixfile.close(self)
1555 rename(self.temp, localpath(self.__name))
1555 rename(self.temp, localpath(self.__name))
1556
1556
1557 def __del__(self):
1557 def __del__(self):
1558 if not self.closed:
1558 if not self.closed:
1559 try:
1559 try:
1560 os.unlink(self.temp)
1560 os.unlink(self.temp)
1561 except: pass
1561 except: pass
1562 posixfile.close(self)
1562 posixfile.close(self)
1563
1563
1564 def makedirs(name, mode=None):
1564 def makedirs(name, mode=None):
1565 """recursive directory creation with parent mode inheritance"""
1565 """recursive directory creation with parent mode inheritance"""
1566 try:
1566 try:
1567 os.mkdir(name)
1567 os.mkdir(name)
1568 if mode is not None:
1568 if mode is not None:
1569 os.chmod(name, mode)
1569 os.chmod(name, mode)
1570 return
1570 return
1571 except OSError, err:
1571 except OSError, err:
1572 if err.errno == errno.EEXIST:
1572 if err.errno == errno.EEXIST:
1573 return
1573 return
1574 if err.errno != errno.ENOENT:
1574 if err.errno != errno.ENOENT:
1575 raise
1575 raise
1576 parent = os.path.abspath(os.path.dirname(name))
1576 parent = os.path.abspath(os.path.dirname(name))
1577 makedirs(parent, mode)
1577 makedirs(parent, mode)
1578 makedirs(name, mode)
1578 makedirs(name, mode)
1579
1579
1580 class opener(object):
1580 class opener(object):
1581 """Open files relative to a base directory
1581 """Open files relative to a base directory
1582
1582
1583 This class is used to hide the details of COW semantics and
1583 This class is used to hide the details of COW semantics and
1584 remote file access from higher level code.
1584 remote file access from higher level code.
1585 """
1585 """
1586 def __init__(self, base, audit=True):
1586 def __init__(self, base, audit=True):
1587 self.base = base
1587 self.base = base
1588 if audit:
1588 if audit:
1589 self.audit_path = path_auditor(base)
1589 self.audit_path = path_auditor(base)
1590 else:
1590 else:
1591 self.audit_path = always
1591 self.audit_path = always
1592 self.createmode = None
1592 self.createmode = None
1593
1593
1594 def __getattr__(self, name):
1594 def __getattr__(self, name):
1595 if name == '_can_symlink':
1595 if name == '_can_symlink':
1596 self._can_symlink = checklink(self.base)
1596 self._can_symlink = checklink(self.base)
1597 return self._can_symlink
1597 return self._can_symlink
1598 raise AttributeError(name)
1598 raise AttributeError(name)
1599
1599
1600 def _fixfilemode(self, name):
1600 def _fixfilemode(self, name):
1601 if self.createmode is None:
1601 if self.createmode is None:
1602 return
1602 return
1603 os.chmod(name, self.createmode & 0666)
1603 os.chmod(name, self.createmode & 0666)
1604
1604
1605 def __call__(self, path, mode="r", text=False, atomictemp=False):
1605 def __call__(self, path, mode="r", text=False, atomictemp=False):
1606 self.audit_path(path)
1606 self.audit_path(path)
1607 f = os.path.join(self.base, path)
1607 f = os.path.join(self.base, path)
1608
1608
1609 if not text and "b" not in mode:
1609 if not text and "b" not in mode:
1610 mode += "b" # for that other OS
1610 mode += "b" # for that other OS
1611
1611
1612 nlink = -1
1612 nlink = -1
1613 if mode not in ("r", "rb"):
1613 if mode not in ("r", "rb"):
1614 try:
1614 try:
1615 nlink = nlinks(f)
1615 nlink = nlinks(f)
1616 except OSError:
1616 except OSError:
1617 nlink = 0
1617 nlink = 0
1618 d = os.path.dirname(f)
1618 d = os.path.dirname(f)
1619 if not os.path.isdir(d):
1619 if not os.path.isdir(d):
1620 makedirs(d, self.createmode)
1620 makedirs(d, self.createmode)
1621 if atomictemp:
1621 if atomictemp:
1622 return atomictempfile(f, mode, self.createmode)
1622 return atomictempfile(f, mode, self.createmode)
1623 if nlink > 1:
1623 if nlink > 1:
1624 rename(mktempcopy(f), f)
1624 rename(mktempcopy(f), f)
1625 fp = posixfile(f, mode)
1625 fp = posixfile(f, mode)
1626 if nlink == 0:
1626 if nlink == 0:
1627 self._fixfilemode(f)
1627 self._fixfilemode(f)
1628 return fp
1628 return fp
1629
1629
1630 def symlink(self, src, dst):
1630 def symlink(self, src, dst):
1631 self.audit_path(dst)
1631 self.audit_path(dst)
1632 linkname = os.path.join(self.base, dst)
1632 linkname = os.path.join(self.base, dst)
1633 try:
1633 try:
1634 os.unlink(linkname)
1634 os.unlink(linkname)
1635 except OSError:
1635 except OSError:
1636 pass
1636 pass
1637
1637
1638 dirname = os.path.dirname(linkname)
1638 dirname = os.path.dirname(linkname)
1639 if not os.path.exists(dirname):
1639 if not os.path.exists(dirname):
1640 makedirs(dirname, self.createmode)
1640 makedirs(dirname, self.createmode)
1641
1641
1642 if self._can_symlink:
1642 if self._can_symlink:
1643 try:
1643 try:
1644 os.symlink(src, linkname)
1644 os.symlink(src, linkname)
1645 except OSError, err:
1645 except OSError, err:
1646 raise OSError(err.errno, _('could not symlink to %r: %s') %
1646 raise OSError(err.errno, _('could not symlink to %r: %s') %
1647 (src, err.strerror), linkname)
1647 (src, err.strerror), linkname)
1648 else:
1648 else:
1649 f = self(dst, "w")
1649 f = self(dst, "w")
1650 f.write(src)
1650 f.write(src)
1651 f.close()
1651 f.close()
1652 self._fixfilemode(dst)
1652 self._fixfilemode(dst)
1653
1653
1654 class chunkbuffer(object):
1654 class chunkbuffer(object):
1655 """Allow arbitrary sized chunks of data to be efficiently read from an
1655 """Allow arbitrary sized chunks of data to be efficiently read from an
1656 iterator over chunks of arbitrary size."""
1656 iterator over chunks of arbitrary size."""
1657
1657
1658 def __init__(self, in_iter):
1658 def __init__(self, in_iter):
1659 """in_iter is the iterator that's iterating over the input chunks.
1659 """in_iter is the iterator that's iterating over the input chunks.
1660 targetsize is how big a buffer to try to maintain."""
1660 targetsize is how big a buffer to try to maintain."""
1661 self.iter = iter(in_iter)
1661 self.iter = iter(in_iter)
1662 self.buf = ''
1662 self.buf = ''
1663 self.targetsize = 2**16
1663 self.targetsize = 2**16
1664
1664
1665 def read(self, l):
1665 def read(self, l):
1666 """Read L bytes of data from the iterator of chunks of data.
1666 """Read L bytes of data from the iterator of chunks of data.
1667 Returns less than L bytes if the iterator runs dry."""
1667 Returns less than L bytes if the iterator runs dry."""
1668 if l > len(self.buf) and self.iter:
1668 if l > len(self.buf) and self.iter:
1669 # Clamp to a multiple of self.targetsize
1669 # Clamp to a multiple of self.targetsize
1670 targetsize = max(l, self.targetsize)
1670 targetsize = max(l, self.targetsize)
1671 collector = cStringIO.StringIO()
1671 collector = cStringIO.StringIO()
1672 collector.write(self.buf)
1672 collector.write(self.buf)
1673 collected = len(self.buf)
1673 collected = len(self.buf)
1674 for chunk in self.iter:
1674 for chunk in self.iter:
1675 collector.write(chunk)
1675 collector.write(chunk)
1676 collected += len(chunk)
1676 collected += len(chunk)
1677 if collected >= targetsize:
1677 if collected >= targetsize:
1678 break
1678 break
1679 if collected < targetsize:
1679 if collected < targetsize:
1680 self.iter = False
1680 self.iter = False
1681 self.buf = collector.getvalue()
1681 self.buf = collector.getvalue()
1682 if len(self.buf) == l:
1682 if len(self.buf) == l:
1683 s, self.buf = str(self.buf), ''
1683 s, self.buf = str(self.buf), ''
1684 else:
1684 else:
1685 s, self.buf = self.buf[:l], buffer(self.buf, l)
1685 s, self.buf = self.buf[:l], buffer(self.buf, l)
1686 return s
1686 return s
1687
1687
1688 def filechunkiter(f, size=65536, limit=None):
1688 def filechunkiter(f, size=65536, limit=None):
1689 """Create a generator that produces the data in the file size
1689 """Create a generator that produces the data in the file size
1690 (default 65536) bytes at a time, up to optional limit (default is
1690 (default 65536) bytes at a time, up to optional limit (default is
1691 to read all data). Chunks may be less than size bytes if the
1691 to read all data). Chunks may be less than size bytes if the
1692 chunk is the last chunk in the file, or the file is a socket or
1692 chunk is the last chunk in the file, or the file is a socket or
1693 some other type of file that sometimes reads less data than is
1693 some other type of file that sometimes reads less data than is
1694 requested."""
1694 requested."""
1695 assert size >= 0
1695 assert size >= 0
1696 assert limit is None or limit >= 0
1696 assert limit is None or limit >= 0
1697 while True:
1697 while True:
1698 if limit is None: nbytes = size
1698 if limit is None: nbytes = size
1699 else: nbytes = min(limit, size)
1699 else: nbytes = min(limit, size)
1700 s = nbytes and f.read(nbytes)
1700 s = nbytes and f.read(nbytes)
1701 if not s: break
1701 if not s: break
1702 if limit: limit -= len(s)
1702 if limit: limit -= len(s)
1703 yield s
1703 yield s
1704
1704
1705 def makedate():
1705 def makedate():
1706 lt = time.localtime()
1706 lt = time.localtime()
1707 if lt[8] == 1 and time.daylight:
1707 if lt[8] == 1 and time.daylight:
1708 tz = time.altzone
1708 tz = time.altzone
1709 else:
1709 else:
1710 tz = time.timezone
1710 tz = time.timezone
1711 return time.mktime(lt), tz
1711 return time.mktime(lt), tz
1712
1712
1713 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1713 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
1714 """represent a (unixtime, offset) tuple as a localized time.
1714 """represent a (unixtime, offset) tuple as a localized time.
1715 unixtime is seconds since the epoch, and offset is the time zone's
1715 unixtime is seconds since the epoch, and offset is the time zone's
1716 number of seconds away from UTC. if timezone is false, do not
1716 number of seconds away from UTC. if timezone is false, do not
1717 append time zone to string."""
1717 append time zone to string."""
1718 t, tz = date or makedate()
1718 t, tz = date or makedate()
1719 if "%1" in format or "%2" in format:
1719 if "%1" in format or "%2" in format:
1720 sign = (tz > 0) and "-" or "+"
1720 sign = (tz > 0) and "-" or "+"
1721 minutes = abs(tz) / 60
1721 minutes = abs(tz) / 60
1722 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1722 format = format.replace("%1", "%c%02d" % (sign, minutes / 60))
1723 format = format.replace("%2", "%02d" % (minutes % 60))
1723 format = format.replace("%2", "%02d" % (minutes % 60))
1724 s = time.strftime(format, time.gmtime(float(t) - tz))
1724 s = time.strftime(format, time.gmtime(float(t) - tz))
1725 return s
1725 return s
1726
1726
1727 def shortdate(date=None):
1727 def shortdate(date=None):
1728 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1728 """turn (timestamp, tzoff) tuple into iso 8631 date."""
1729 return datestr(date, format='%Y-%m-%d')
1729 return datestr(date, format='%Y-%m-%d')
1730
1730
1731 def strdate(string, format, defaults=[]):
1731 def strdate(string, format, defaults=[]):
1732 """parse a localized time string and return a (unixtime, offset) tuple.
1732 """parse a localized time string and return a (unixtime, offset) tuple.
1733 if the string cannot be parsed, ValueError is raised."""
1733 if the string cannot be parsed, ValueError is raised."""
1734 def timezone(string):
1734 def timezone(string):
1735 tz = string.split()[-1]
1735 tz = string.split()[-1]
1736 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1736 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1737 sign = (tz[0] == "+") and 1 or -1
1737 sign = (tz[0] == "+") and 1 or -1
1738 hours = int(tz[1:3])
1738 hours = int(tz[1:3])
1739 minutes = int(tz[3:5])
1739 minutes = int(tz[3:5])
1740 return -sign * (hours * 60 + minutes) * 60
1740 return -sign * (hours * 60 + minutes) * 60
1741 if tz == "GMT" or tz == "UTC":
1741 if tz == "GMT" or tz == "UTC":
1742 return 0
1742 return 0
1743 return None
1743 return None
1744
1744
1745 # NOTE: unixtime = localunixtime + offset
1745 # NOTE: unixtime = localunixtime + offset
1746 offset, date = timezone(string), string
1746 offset, date = timezone(string), string
1747 if offset != None:
1747 if offset != None:
1748 date = " ".join(string.split()[:-1])
1748 date = " ".join(string.split()[:-1])
1749
1749
1750 # add missing elements from defaults
1750 # add missing elements from defaults
1751 for part in defaults:
1751 for part in defaults:
1752 found = [True for p in part if ("%"+p) in format]
1752 found = [True for p in part if ("%"+p) in format]
1753 if not found:
1753 if not found:
1754 date += "@" + defaults[part]
1754 date += "@" + defaults[part]
1755 format += "@%" + part[0]
1755 format += "@%" + part[0]
1756
1756
1757 timetuple = time.strptime(date, format)
1757 timetuple = time.strptime(date, format)
1758 localunixtime = int(calendar.timegm(timetuple))
1758 localunixtime = int(calendar.timegm(timetuple))
1759 if offset is None:
1759 if offset is None:
1760 # local timezone
1760 # local timezone
1761 unixtime = int(time.mktime(timetuple))
1761 unixtime = int(time.mktime(timetuple))
1762 offset = unixtime - localunixtime
1762 offset = unixtime - localunixtime
1763 else:
1763 else:
1764 unixtime = localunixtime + offset
1764 unixtime = localunixtime + offset
1765 return unixtime, offset
1765 return unixtime, offset
1766
1766
1767 def parsedate(date, formats=None, defaults=None):
1767 def parsedate(date, formats=None, defaults=None):
1768 """parse a localized date/time string and return a (unixtime, offset) tuple.
1768 """parse a localized date/time string and return a (unixtime, offset) tuple.
1769
1769
1770 The date may be a "unixtime offset" string or in one of the specified
1770 The date may be a "unixtime offset" string or in one of the specified
1771 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1771 formats. If the date already is a (unixtime, offset) tuple, it is returned.
1772 """
1772 """
1773 if not date:
1773 if not date:
1774 return 0, 0
1774 return 0, 0
1775 if isinstance(date, tuple) and len(date) == 2:
1775 if isinstance(date, tuple) and len(date) == 2:
1776 return date
1776 return date
1777 if not formats:
1777 if not formats:
1778 formats = defaultdateformats
1778 formats = defaultdateformats
1779 date = date.strip()
1779 date = date.strip()
1780 try:
1780 try:
1781 when, offset = map(int, date.split(' '))
1781 when, offset = map(int, date.split(' '))
1782 except ValueError:
1782 except ValueError:
1783 # fill out defaults
1783 # fill out defaults
1784 if not defaults:
1784 if not defaults:
1785 defaults = {}
1785 defaults = {}
1786 now = makedate()
1786 now = makedate()
1787 for part in "d mb yY HI M S".split():
1787 for part in "d mb yY HI M S".split():
1788 if part not in defaults:
1788 if part not in defaults:
1789 if part[0] in "HMS":
1789 if part[0] in "HMS":
1790 defaults[part] = "00"
1790 defaults[part] = "00"
1791 else:
1791 else:
1792 defaults[part] = datestr(now, "%" + part[0])
1792 defaults[part] = datestr(now, "%" + part[0])
1793
1793
1794 for format in formats:
1794 for format in formats:
1795 try:
1795 try:
1796 when, offset = strdate(date, format, defaults)
1796 when, offset = strdate(date, format, defaults)
1797 except (ValueError, OverflowError):
1797 except (ValueError, OverflowError):
1798 pass
1798 pass
1799 else:
1799 else:
1800 break
1800 break
1801 else:
1801 else:
1802 raise Abort(_('invalid date: %r ') % date)
1802 raise Abort(_('invalid date: %r ') % date)
1803 # validate explicit (probably user-specified) date and
1803 # validate explicit (probably user-specified) date and
1804 # time zone offset. values must fit in signed 32 bits for
1804 # time zone offset. values must fit in signed 32 bits for
1805 # current 32-bit linux runtimes. timezones go from UTC-12
1805 # current 32-bit linux runtimes. timezones go from UTC-12
1806 # to UTC+14
1806 # to UTC+14
1807 if abs(when) > 0x7fffffff:
1807 if abs(when) > 0x7fffffff:
1808 raise Abort(_('date exceeds 32 bits: %d') % when)
1808 raise Abort(_('date exceeds 32 bits: %d') % when)
1809 if offset < -50400 or offset > 43200:
1809 if offset < -50400 or offset > 43200:
1810 raise Abort(_('impossible time zone offset: %d') % offset)
1810 raise Abort(_('impossible time zone offset: %d') % offset)
1811 return when, offset
1811 return when, offset
1812
1812
1813 def matchdate(date):
1813 def matchdate(date):
1814 """Return a function that matches a given date match specifier
1814 """Return a function that matches a given date match specifier
1815
1815
1816 Formats include:
1816 Formats include:
1817
1817
1818 '{date}' match a given date to the accuracy provided
1818 '{date}' match a given date to the accuracy provided
1819
1819
1820 '<{date}' on or before a given date
1820 '<{date}' on or before a given date
1821
1821
1822 '>{date}' on or after a given date
1822 '>{date}' on or after a given date
1823
1823
1824 """
1824 """
1825
1825
1826 def lower(date):
1826 def lower(date):
1827 d = dict(mb="1", d="1")
1827 d = dict(mb="1", d="1")
1828 return parsedate(date, extendeddateformats, d)[0]
1828 return parsedate(date, extendeddateformats, d)[0]
1829
1829
1830 def upper(date):
1830 def upper(date):
1831 d = dict(mb="12", HI="23", M="59", S="59")
1831 d = dict(mb="12", HI="23", M="59", S="59")
1832 for days in "31 30 29".split():
1832 for days in "31 30 29".split():
1833 try:
1833 try:
1834 d["d"] = days
1834 d["d"] = days
1835 return parsedate(date, extendeddateformats, d)[0]
1835 return parsedate(date, extendeddateformats, d)[0]
1836 except:
1836 except:
1837 pass
1837 pass
1838 d["d"] = "28"
1838 d["d"] = "28"
1839 return parsedate(date, extendeddateformats, d)[0]
1839 return parsedate(date, extendeddateformats, d)[0]
1840
1840
1841 if date[0] == "<":
1841 if date[0] == "<":
1842 when = upper(date[1:])
1842 when = upper(date[1:])
1843 return lambda x: x <= when
1843 return lambda x: x <= when
1844 elif date[0] == ">":
1844 elif date[0] == ">":
1845 when = lower(date[1:])
1845 when = lower(date[1:])
1846 return lambda x: x >= when
1846 return lambda x: x >= when
1847 elif date[0] == "-":
1847 elif date[0] == "-":
1848 try:
1848 try:
1849 days = int(date[1:])
1849 days = int(date[1:])
1850 except ValueError:
1850 except ValueError:
1851 raise Abort(_("invalid day spec: %s") % date[1:])
1851 raise Abort(_("invalid day spec: %s") % date[1:])
1852 when = makedate()[0] - days * 3600 * 24
1852 when = makedate()[0] - days * 3600 * 24
1853 return lambda x: x >= when
1853 return lambda x: x >= when
1854 elif " to " in date:
1854 elif " to " in date:
1855 a, b = date.split(" to ")
1855 a, b = date.split(" to ")
1856 start, stop = lower(a), upper(b)
1856 start, stop = lower(a), upper(b)
1857 return lambda x: x >= start and x <= stop
1857 return lambda x: x >= start and x <= stop
1858 else:
1858 else:
1859 start, stop = lower(date), upper(date)
1859 start, stop = lower(date), upper(date)
1860 return lambda x: x >= start and x <= stop
1860 return lambda x: x >= start and x <= stop
1861
1861
1862 def shortuser(user):
1862 def shortuser(user):
1863 """Return a short representation of a user name or email address."""
1863 """Return a short representation of a user name or email address."""
1864 f = user.find('@')
1864 f = user.find('@')
1865 if f >= 0:
1865 if f >= 0:
1866 user = user[:f]
1866 user = user[:f]
1867 f = user.find('<')
1867 f = user.find('<')
1868 if f >= 0:
1868 if f >= 0:
1869 user = user[f+1:]
1869 user = user[f+1:]
1870 f = user.find(' ')
1870 f = user.find(' ')
1871 if f >= 0:
1871 if f >= 0:
1872 user = user[:f]
1872 user = user[:f]
1873 f = user.find('.')
1873 f = user.find('.')
1874 if f >= 0:
1874 if f >= 0:
1875 user = user[:f]
1875 user = user[:f]
1876 return user
1876 return user
1877
1877
1878 def email(author):
1878 def email(author):
1879 '''get email of author.'''
1879 '''get email of author.'''
1880 r = author.find('>')
1880 r = author.find('>')
1881 if r == -1: r = None
1881 if r == -1: r = None
1882 return author[author.find('<')+1:r]
1882 return author[author.find('<')+1:r]
1883
1883
1884 def ellipsis(text, maxlength=400):
1884 def ellipsis(text, maxlength=400):
1885 """Trim string to at most maxlength (default: 400) characters."""
1885 """Trim string to at most maxlength (default: 400) characters."""
1886 if len(text) <= maxlength:
1886 if len(text) <= maxlength:
1887 return text
1887 return text
1888 else:
1888 else:
1889 return "%s..." % (text[:maxlength-3])
1889 return "%s..." % (text[:maxlength-3])
1890
1890
1891 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1891 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
1892 '''yield every hg repository under path, recursively.'''
1892 '''yield every hg repository under path, recursively.'''
1893 def errhandler(err):
1893 def errhandler(err):
1894 if err.filename == path:
1894 if err.filename == path:
1895 raise err
1895 raise err
1896 if followsym and hasattr(os.path, 'samestat'):
1896 if followsym and hasattr(os.path, 'samestat'):
1897 def _add_dir_if_not_there(dirlst, dirname):
1897 def _add_dir_if_not_there(dirlst, dirname):
1898 match = False
1898 match = False
1899 samestat = os.path.samestat
1899 samestat = os.path.samestat
1900 dirstat = os.stat(dirname)
1900 dirstat = os.stat(dirname)
1901 for lstdirstat in dirlst:
1901 for lstdirstat in dirlst:
1902 if samestat(dirstat, lstdirstat):
1902 if samestat(dirstat, lstdirstat):
1903 match = True
1903 match = True
1904 break
1904 break
1905 if not match:
1905 if not match:
1906 dirlst.append(dirstat)
1906 dirlst.append(dirstat)
1907 return not match
1907 return not match
1908 else:
1908 else:
1909 followsym = False
1909 followsym = False
1910
1910
1911 if (seen_dirs is None) and followsym:
1911 if (seen_dirs is None) and followsym:
1912 seen_dirs = []
1912 seen_dirs = []
1913 _add_dir_if_not_there(seen_dirs, path)
1913 _add_dir_if_not_there(seen_dirs, path)
1914 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1914 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
1915 if '.hg' in dirs:
1915 if '.hg' in dirs:
1916 yield root # found a repository
1916 yield root # found a repository
1917 qroot = os.path.join(root, '.hg', 'patches')
1917 qroot = os.path.join(root, '.hg', 'patches')
1918 if os.path.isdir(os.path.join(qroot, '.hg')):
1918 if os.path.isdir(os.path.join(qroot, '.hg')):
1919 yield qroot # we have a patch queue repo here
1919 yield qroot # we have a patch queue repo here
1920 if recurse:
1920 if recurse:
1921 # avoid recursing inside the .hg directory
1921 # avoid recursing inside the .hg directory
1922 dirs.remove('.hg')
1922 dirs.remove('.hg')
1923 else:
1923 else:
1924 dirs[:] = [] # don't descend further
1924 dirs[:] = [] # don't descend further
1925 elif followsym:
1925 elif followsym:
1926 newdirs = []
1926 newdirs = []
1927 for d in dirs:
1927 for d in dirs:
1928 fname = os.path.join(root, d)
1928 fname = os.path.join(root, d)
1929 if _add_dir_if_not_there(seen_dirs, fname):
1929 if _add_dir_if_not_there(seen_dirs, fname):
1930 if os.path.islink(fname):
1930 if os.path.islink(fname):
1931 for hgname in walkrepos(fname, True, seen_dirs):
1931 for hgname in walkrepos(fname, True, seen_dirs):
1932 yield hgname
1932 yield hgname
1933 else:
1933 else:
1934 newdirs.append(d)
1934 newdirs.append(d)
1935 dirs[:] = newdirs
1935 dirs[:] = newdirs
1936
1936
1937 _rcpath = None
1937 _rcpath = None
1938
1938
1939 def os_rcpath():
1939 def os_rcpath():
1940 '''return default os-specific hgrc search path'''
1940 '''return default os-specific hgrc search path'''
1941 path = system_rcpath()
1941 path = system_rcpath()
1942 path.extend(user_rcpath())
1942 path.extend(user_rcpath())
1943 path = [os.path.normpath(f) for f in path]
1943 path = [os.path.normpath(f) for f in path]
1944 return path
1944 return path
1945
1945
1946 def rcpath():
1946 def rcpath():
1947 '''return hgrc search path. if env var HGRCPATH is set, use it.
1947 '''return hgrc search path. if env var HGRCPATH is set, use it.
1948 for each item in path, if directory, use files ending in .rc,
1948 for each item in path, if directory, use files ending in .rc,
1949 else use item.
1949 else use item.
1950 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1950 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1951 if no HGRCPATH, use default os-specific path.'''
1951 if no HGRCPATH, use default os-specific path.'''
1952 global _rcpath
1952 global _rcpath
1953 if _rcpath is None:
1953 if _rcpath is None:
1954 if 'HGRCPATH' in os.environ:
1954 if 'HGRCPATH' in os.environ:
1955 _rcpath = []
1955 _rcpath = []
1956 for p in os.environ['HGRCPATH'].split(os.pathsep):
1956 for p in os.environ['HGRCPATH'].split(os.pathsep):
1957 if not p: continue
1957 if not p: continue
1958 if os.path.isdir(p):
1958 if os.path.isdir(p):
1959 for f, kind in osutil.listdir(p):
1959 for f, kind in osutil.listdir(p):
1960 if f.endswith('.rc'):
1960 if f.endswith('.rc'):
1961 _rcpath.append(os.path.join(p, f))
1961 _rcpath.append(os.path.join(p, f))
1962 else:
1962 else:
1963 _rcpath.append(p)
1963 _rcpath.append(p)
1964 else:
1964 else:
1965 _rcpath = os_rcpath()
1965 _rcpath = os_rcpath()
1966 return _rcpath
1966 return _rcpath
1967
1967
1968 def bytecount(nbytes):
1968 def bytecount(nbytes):
1969 '''return byte count formatted as readable string, with units'''
1969 '''return byte count formatted as readable string, with units'''
1970
1970
1971 units = (
1971 units = (
1972 (100, 1<<30, _('%.0f GB')),
1972 (100, 1<<30, _('%.0f GB')),
1973 (10, 1<<30, _('%.1f GB')),
1973 (10, 1<<30, _('%.1f GB')),
1974 (1, 1<<30, _('%.2f GB')),
1974 (1, 1<<30, _('%.2f GB')),
1975 (100, 1<<20, _('%.0f MB')),
1975 (100, 1<<20, _('%.0f MB')),
1976 (10, 1<<20, _('%.1f MB')),
1976 (10, 1<<20, _('%.1f MB')),
1977 (1, 1<<20, _('%.2f MB')),
1977 (1, 1<<20, _('%.2f MB')),
1978 (100, 1<<10, _('%.0f KB')),
1978 (100, 1<<10, _('%.0f KB')),
1979 (10, 1<<10, _('%.1f KB')),
1979 (10, 1<<10, _('%.1f KB')),
1980 (1, 1<<10, _('%.2f KB')),
1980 (1, 1<<10, _('%.2f KB')),
1981 (1, 1, _('%.0f bytes')),
1981 (1, 1, _('%.0f bytes')),
1982 )
1982 )
1983
1983
1984 for multiplier, divisor, format in units:
1984 for multiplier, divisor, format in units:
1985 if nbytes >= divisor * multiplier:
1985 if nbytes >= divisor * multiplier:
1986 return format % (nbytes / float(divisor))
1986 return format % (nbytes / float(divisor))
1987 return units[-1][2] % nbytes
1987 return units[-1][2] % nbytes
1988
1988
1989 def drop_scheme(scheme, path):
1989 def drop_scheme(scheme, path):
1990 sc = scheme + ':'
1990 sc = scheme + ':'
1991 if path.startswith(sc):
1991 if path.startswith(sc):
1992 path = path[len(sc):]
1992 path = path[len(sc):]
1993 if path.startswith('//'):
1993 if path.startswith('//'):
1994 path = path[2:]
1994 path = path[2:]
1995 return path
1995 return path
1996
1996
1997 def uirepr(s):
1997 def uirepr(s):
1998 # Avoid double backslash in Windows path repr()
1998 # Avoid double backslash in Windows path repr()
1999 return repr(s).replace('\\\\', '\\')
1999 return repr(s).replace('\\\\', '\\')
2000
2000
2001 def termwidth():
2001 def termwidth():
2002 if 'COLUMNS' in os.environ:
2002 if 'COLUMNS' in os.environ:
2003 try:
2003 try:
2004 return int(os.environ['COLUMNS'])
2004 return int(os.environ['COLUMNS'])
2005 except ValueError:
2005 except ValueError:
2006 pass
2006 pass
2007 try:
2007 try:
2008 import termios, array, fcntl
2008 import termios, array, fcntl
2009 for dev in (sys.stdout, sys.stdin):
2009 for dev in (sys.stdout, sys.stdin):
2010 try:
2010 try:
2011 fd = dev.fileno()
2011 fd = dev.fileno()
2012 if not os.isatty(fd):
2012 if not os.isatty(fd):
2013 continue
2013 continue
2014 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
2014 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
2015 return array.array('h', arri)[1]
2015 return array.array('h', arri)[1]
2016 except ValueError:
2016 except ValueError:
2017 pass
2017 pass
2018 except ImportError:
2018 except ImportError:
2019 pass
2019 pass
2020 return 80
2020 return 80
@@ -1,303 +1,303
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 abort: path 'mammals/.hg' is inside repo 'mammals'
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 cd ..
167 cd ..
168
168
169 hg debugwalk -Ibeans
169 hg debugwalk -Ibeans
170 f beans/black beans/black
170 f beans/black beans/black
171 f beans/borlotti beans/borlotti
171 f beans/borlotti beans/borlotti
172 f beans/kidney beans/kidney
172 f beans/kidney beans/kidney
173 f beans/navy beans/navy
173 f beans/navy beans/navy
174 f beans/pinto beans/pinto
174 f beans/pinto beans/pinto
175 f beans/turtle beans/turtle
175 f beans/turtle beans/turtle
176
176
177 hg debugwalk -I {*,{b,m}*/*}k
177 hg debugwalk -I {*,{b,m}*/*}k
178 f beans/black beans/black
178 f beans/black beans/black
179 f fenugreek fenugreek
179 f fenugreek fenugreek
180 f mammals/skunk mammals/skunk
180 f mammals/skunk mammals/skunk
181
181
182 hg debugwalk glob:mammals/../beans/b*
182 hg debugwalk glob:mammals/../beans/b*
183 f beans/black beans/black
183 f beans/black beans/black
184 f beans/borlotti beans/borlotti
184 f beans/borlotti beans/borlotti
185
185
186 hg debugwalk -X*/Procyonidae mammals
186 hg debugwalk -X*/Procyonidae mammals
187 f mammals/skunk mammals/skunk
187 f mammals/skunk mammals/skunk
188
188
189 hg debugwalk path:mammals
189 hg debugwalk path:mammals
190 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
190 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
191 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
191 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
192 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
192 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
193 f mammals/skunk mammals/skunk
193 f mammals/skunk mammals/skunk
194
194
195 hg debugwalk ..
195 hg debugwalk ..
196 abort: .. not under root
196 abort: .. not under root
197
197
198 hg debugwalk beans/../..
198 hg debugwalk beans/../..
199 abort: beans/../.. not under root
199 abort: beans/../.. not under root
200
200
201 hg debugwalk .hg
201 hg debugwalk .hg
202 abort: path contains illegal component: .hg
202 abort: path contains illegal component: .hg
203
203
204 hg debugwalk beans/../.hg
204 hg debugwalk beans/../.hg
205 abort: path contains illegal component: .hg
205 abort: path contains illegal component: .hg
206
206
207 hg debugwalk beans/../.hg/data
207 hg debugwalk beans/../.hg/data
208 abort: path contains illegal component: .hg/data
208 abort: path contains illegal component: .hg/data
209
209
210 hg debugwalk beans/.hg
210 hg debugwalk beans/.hg
211 beans/.hg: No such file or directory
211 abort: path 'beans/.hg' is inside repo 'beans'
212
212
213 hg debugwalk glob:*
213 hg debugwalk glob:*
214 f fennel fennel
214 f fennel fennel
215 f fenugreek fenugreek
215 f fenugreek fenugreek
216 f fiddlehead fiddlehead
216 f fiddlehead fiddlehead
217 f glob:glob glob:glob
217 f glob:glob glob:glob
218
218
219 hg debugwalk glob:**e
219 hg debugwalk glob:**e
220 f beans/turtle beans/turtle
220 f beans/turtle beans/turtle
221 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
221 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
222
222
223 hg debugwalk re:.*[kb]$
223 hg debugwalk re:.*[kb]$
224 f beans/black beans/black
224 f beans/black beans/black
225 f fenugreek fenugreek
225 f fenugreek fenugreek
226 f glob:glob glob:glob
226 f glob:glob glob:glob
227 f mammals/skunk mammals/skunk
227 f mammals/skunk mammals/skunk
228
228
229 hg debugwalk path:beans/black
229 hg debugwalk path:beans/black
230 f beans/black beans/black exact
230 f beans/black beans/black exact
231
231
232 hg debugwalk path:beans//black
232 hg debugwalk path:beans//black
233 f beans/black beans/black exact
233 f beans/black beans/black exact
234
234
235 hg debugwalk relglob:Procyonidae
235 hg debugwalk relglob:Procyonidae
236
236
237 hg debugwalk relglob:Procyonidae/**
237 hg debugwalk relglob:Procyonidae/**
238 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
238 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
239 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
239 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
240 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
240 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
241
241
242 hg debugwalk relglob:Procyonidae/** fennel
242 hg debugwalk relglob:Procyonidae/** fennel
243 f fennel fennel exact
243 f fennel fennel exact
244 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
244 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
245 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
245 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
246 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
246 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
247
247
248 hg debugwalk beans glob:beans/*
248 hg debugwalk beans glob:beans/*
249 f beans/black beans/black
249 f beans/black beans/black
250 f beans/borlotti beans/borlotti
250 f beans/borlotti beans/borlotti
251 f beans/kidney beans/kidney
251 f beans/kidney beans/kidney
252 f beans/navy beans/navy
252 f beans/navy beans/navy
253 f beans/pinto beans/pinto
253 f beans/pinto beans/pinto
254 f beans/turtle beans/turtle
254 f beans/turtle beans/turtle
255
255
256 hg debugwalk glob:mamm**
256 hg debugwalk glob:mamm**
257 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
257 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
258 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
258 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
259 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
259 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
260 f mammals/skunk mammals/skunk
260 f mammals/skunk mammals/skunk
261
261
262 hg debugwalk glob:mamm** fennel
262 hg debugwalk glob:mamm** fennel
263 f fennel fennel exact
263 f fennel fennel exact
264 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
264 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
265 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
265 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
266 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
266 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
267 f mammals/skunk mammals/skunk
267 f mammals/skunk mammals/skunk
268
268
269 hg debugwalk glob:j*
269 hg debugwalk glob:j*
270
270
271 hg debugwalk NOEXIST
271 hg debugwalk NOEXIST
272 NOEXIST: No such file or directory
272 NOEXIST: No such file or directory
273
273
274 hg debugwalk fifo
274 hg debugwalk fifo
275 fifo: unsupported file type (type is fifo)
275 fifo: unsupported file type (type is fifo)
276
276
277 hg debugwalk fenugreek
277 hg debugwalk fenugreek
278 f fenugreek fenugreek exact
278 f fenugreek fenugreek exact
279
279
280 hg debugwalk fenugreek
280 hg debugwalk fenugreek
281 f fenugreek fenugreek exact
281 f fenugreek fenugreek exact
282
282
283 hg debugwalk new
283 hg debugwalk new
284 f new new exact
284 f new new exact
285
285
286 hg debugwalk ignored
286 hg debugwalk ignored
287
287
288 hg debugwalk ignored/file
288 hg debugwalk ignored/file
289 f ignored/file ignored/file exact
289 f ignored/file ignored/file exact
290
290
291 cd ..
291 cd ..
292
292
293 hg debugwalk -R t t/mammals/skunk
293 hg debugwalk -R t t/mammals/skunk
294 f mammals/skunk t/mammals/skunk exact
294 f mammals/skunk t/mammals/skunk exact
295
295
296 cd t2
296 cd t2
297
297
298 hg debugwalk -R ../t ../t/mammals/skunk
298 hg debugwalk -R ../t ../t/mammals/skunk
299 f mammals/skunk ../t/mammals/skunk exact
299 f mammals/skunk ../t/mammals/skunk exact
300
300
301 hg debugwalk --cwd ../t mammals/skunk
301 hg debugwalk --cwd ../t mammals/skunk
302 f mammals/skunk mammals/skunk exact
302 f mammals/skunk mammals/skunk exact
303
303
General Comments 0
You need to be logged in to leave comments. Login now