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