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