##// END OF EJS Templates
scmutil: split platform-specific bits into their own modules...
Kevin Bullock -
r18690:4c6f7f0d default
parent child Browse files
Show More
@@ -0,0 +1,32 b''
1 import sys, os
2 import osutil
3
4 def _rcfiles(path):
5 rcs = [os.path.join(path, 'hgrc')]
6 rcdir = os.path.join(path, 'hgrc.d')
7 try:
8 rcs.extend([os.path.join(rcdir, f)
9 for f, kind in osutil.listdir(rcdir)
10 if f.endswith(".rc")])
11 except OSError:
12 pass
13 return rcs
14
15 def systemrcpath():
16 path = []
17 if sys.platform == 'plan9':
18 root = 'lib/mercurial'
19 else:
20 root = 'etc/mercurial'
21 # old mod_python does not set sys.argv
22 if len(getattr(sys, 'argv', [])) > 0:
23 p = os.path.dirname(os.path.dirname(sys.argv[0]))
24 path.extend(_rcfiles(os.path.join(p, root)))
25 path.extend(_rcfiles('/' + root))
26 return path
27
28 def userrcpath():
29 if sys.platform == 'plan9':
30 return [os.environ['home'] + '/lib/hgrc']
31 else:
32 return [os.path.expanduser('~/.hgrc')]
@@ -0,0 +1,45 b''
1 import os
2 import osutil
3 import _winreg
4
5 def systemrcpath():
6 '''return default os-specific hgrc search path'''
7 rcpath = []
8 filename = util.executablepath()
9 # Use mercurial.ini found in directory with hg.exe
10 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
11 if os.path.isfile(progrc):
12 rcpath.append(progrc)
13 return rcpath
14 # Use hgrc.d found in directory with hg.exe
15 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
16 if os.path.isdir(progrcd):
17 for f, kind in osutil.listdir(progrcd):
18 if f.endswith('.rc'):
19 rcpath.append(os.path.join(progrcd, f))
20 return rcpath
21 # else look for a system rcpath in the registry
22 value = util.lookupreg('SOFTWARE\\Mercurial', None,
23 _winreg.HKEY_LOCAL_MACHINE)
24 if not isinstance(value, str) or not value:
25 return rcpath
26 value = util.localpath(value)
27 for p in value.split(os.pathsep):
28 if p.lower().endswith('mercurial.ini'):
29 rcpath.append(p)
30 elif os.path.isdir(p):
31 for f, kind in osutil.listdir(p):
32 if f.endswith('.rc'):
33 rcpath.append(os.path.join(p, f))
34 return rcpath
35
36 def userrcpath():
37 '''return os-specific hgrc search path to the user dir'''
38 home = os.path.expanduser('~')
39 path = [os.path.join(home, 'mercurial.ini'),
40 os.path.join(home, '.hgrc')]
41 userprofile = os.environ.get('USERPROFILE')
42 if userprofile:
43 path.append(os.path.join(userprofile, 'mercurial.ini'))
44 path.append(os.path.join(userprofile, '.hgrc'))
45 return path
@@ -1,999 +1,929 b''
1 1 # scmutil.py - Mercurial core utility functions
2 2 #
3 3 # Copyright Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 from mercurial.node import nullrev
10 10 import util, error, osutil, revset, similar, encoding, phases
11 11 import match as matchmod
12 import os, errno, re, stat, sys, glob
12 import os, errno, re, stat, glob
13
14 if os.name == 'nt':
15 import scmwindows as scmplatform
16 else:
17 import scmposix as scmplatform
18
19 systemrcpath = scmplatform.systemrcpath
20 userrcpath = scmplatform.userrcpath
13 21
14 22 def nochangesfound(ui, repo, excluded=None):
15 23 '''Report no changes for push/pull, excluded is None or a list of
16 24 nodes excluded from the push/pull.
17 25 '''
18 26 secretlist = []
19 27 if excluded:
20 28 for n in excluded:
21 29 if n not in repo:
22 30 # discovery should not have included the filtered revision,
23 31 # we have to explicitly exclude it until discovery is cleanup.
24 32 continue
25 33 ctx = repo[n]
26 34 if ctx.phase() >= phases.secret and not ctx.extinct():
27 35 secretlist.append(n)
28 36
29 37 if secretlist:
30 38 ui.status(_("no changes found (ignored %d secret changesets)\n")
31 39 % len(secretlist))
32 40 else:
33 41 ui.status(_("no changes found\n"))
34 42
35 43 def checknewlabel(repo, lbl, kind):
36 44 if lbl in ['tip', '.', 'null']:
37 45 raise util.Abort(_("the name '%s' is reserved") % lbl)
38 46 for c in (':', '\0', '\n', '\r'):
39 47 if c in lbl:
40 48 raise util.Abort(_("%r cannot be used in a name") % c)
41 49 try:
42 50 int(lbl)
43 51 raise util.Abort(_("a %s cannot have an integer as its name") % kind)
44 52 except ValueError:
45 53 pass
46 54
47 55 def checkfilename(f):
48 56 '''Check that the filename f is an acceptable filename for a tracked file'''
49 57 if '\r' in f or '\n' in f:
50 58 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
51 59
52 60 def checkportable(ui, f):
53 61 '''Check if filename f is portable and warn or abort depending on config'''
54 62 checkfilename(f)
55 63 abort, warn = checkportabilityalert(ui)
56 64 if abort or warn:
57 65 msg = util.checkwinfilename(f)
58 66 if msg:
59 67 msg = "%s: %r" % (msg, f)
60 68 if abort:
61 69 raise util.Abort(msg)
62 70 ui.warn(_("warning: %s\n") % msg)
63 71
64 72 def checkportabilityalert(ui):
65 73 '''check if the user's config requests nothing, a warning, or abort for
66 74 non-portable filenames'''
67 75 val = ui.config('ui', 'portablefilenames', 'warn')
68 76 lval = val.lower()
69 77 bval = util.parsebool(val)
70 78 abort = os.name == 'nt' or lval == 'abort'
71 79 warn = bval or lval == 'warn'
72 80 if bval is None and not (warn or abort or lval == 'ignore'):
73 81 raise error.ConfigError(
74 82 _("ui.portablefilenames value is invalid ('%s')") % val)
75 83 return abort, warn
76 84
77 85 class casecollisionauditor(object):
78 86 def __init__(self, ui, abort, dirstate):
79 87 self._ui = ui
80 88 self._abort = abort
81 89 allfiles = '\0'.join(dirstate._map)
82 90 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
83 91 self._dirstate = dirstate
84 92 # The purpose of _newfiles is so that we don't complain about
85 93 # case collisions if someone were to call this object with the
86 94 # same filename twice.
87 95 self._newfiles = set()
88 96
89 97 def __call__(self, f):
90 98 fl = encoding.lower(f)
91 99 if (fl in self._loweredfiles and f not in self._dirstate and
92 100 f not in self._newfiles):
93 101 msg = _('possible case-folding collision for %s') % f
94 102 if self._abort:
95 103 raise util.Abort(msg)
96 104 self._ui.warn(_("warning: %s\n") % msg)
97 105 self._loweredfiles.add(fl)
98 106 self._newfiles.add(f)
99 107
100 108 class pathauditor(object):
101 109 '''ensure that a filesystem path contains no banned components.
102 110 the following properties of a path are checked:
103 111
104 112 - ends with a directory separator
105 113 - under top-level .hg
106 114 - starts at the root of a windows drive
107 115 - contains ".."
108 116 - traverses a symlink (e.g. a/symlink_here/b)
109 117 - inside a nested repository (a callback can be used to approve
110 118 some nested repositories, e.g., subrepositories)
111 119 '''
112 120
113 121 def __init__(self, root, callback=None):
114 122 self.audited = set()
115 123 self.auditeddir = set()
116 124 self.root = root
117 125 self.callback = callback
118 126 if os.path.lexists(root) and not util.checkcase(root):
119 127 self.normcase = util.normcase
120 128 else:
121 129 self.normcase = lambda x: x
122 130
123 131 def __call__(self, path):
124 132 '''Check the relative path.
125 133 path may contain a pattern (e.g. foodir/**.txt)'''
126 134
127 135 path = util.localpath(path)
128 136 normpath = self.normcase(path)
129 137 if normpath in self.audited:
130 138 return
131 139 # AIX ignores "/" at end of path, others raise EISDIR.
132 140 if util.endswithsep(path):
133 141 raise util.Abort(_("path ends in directory separator: %s") % path)
134 142 parts = util.splitpath(path)
135 143 if (os.path.splitdrive(path)[0]
136 144 or parts[0].lower() in ('.hg', '.hg.', '')
137 145 or os.pardir in parts):
138 146 raise util.Abort(_("path contains illegal component: %s") % path)
139 147 if '.hg' in path.lower():
140 148 lparts = [p.lower() for p in parts]
141 149 for p in '.hg', '.hg.':
142 150 if p in lparts[1:]:
143 151 pos = lparts.index(p)
144 152 base = os.path.join(*parts[:pos])
145 153 raise util.Abort(_("path '%s' is inside nested repo %r")
146 154 % (path, base))
147 155
148 156 normparts = util.splitpath(normpath)
149 157 assert len(parts) == len(normparts)
150 158
151 159 parts.pop()
152 160 normparts.pop()
153 161 prefixes = []
154 162 while parts:
155 163 prefix = os.sep.join(parts)
156 164 normprefix = os.sep.join(normparts)
157 165 if normprefix in self.auditeddir:
158 166 break
159 167 curpath = os.path.join(self.root, prefix)
160 168 try:
161 169 st = os.lstat(curpath)
162 170 except OSError, err:
163 171 # EINVAL can be raised as invalid path syntax under win32.
164 172 # They must be ignored for patterns can be checked too.
165 173 if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
166 174 raise
167 175 else:
168 176 if stat.S_ISLNK(st.st_mode):
169 177 raise util.Abort(
170 178 _('path %r traverses symbolic link %r')
171 179 % (path, prefix))
172 180 elif (stat.S_ISDIR(st.st_mode) and
173 181 os.path.isdir(os.path.join(curpath, '.hg'))):
174 182 if not self.callback or not self.callback(curpath):
175 183 raise util.Abort(_("path '%s' is inside nested "
176 184 "repo %r")
177 185 % (path, prefix))
178 186 prefixes.append(normprefix)
179 187 parts.pop()
180 188 normparts.pop()
181 189
182 190 self.audited.add(normpath)
183 191 # only add prefixes to the cache after checking everything: we don't
184 192 # want to add "foo/bar/baz" before checking if there's a "foo/.hg"
185 193 self.auditeddir.update(prefixes)
186 194
187 195 def check(self, path):
188 196 try:
189 197 self(path)
190 198 return True
191 199 except (OSError, util.Abort):
192 200 return False
193 201
194 202 class abstractvfs(object):
195 203 """Abstract base class; cannot be instantiated"""
196 204
197 205 def __init__(self, *args, **kwargs):
198 206 '''Prevent instantiation; don't call this from subclasses.'''
199 207 raise NotImplementedError('attempted instantiating ' + str(type(self)))
200 208
201 209 def tryread(self, path):
202 210 '''gracefully return an empty string for missing files'''
203 211 try:
204 212 return self.read(path)
205 213 except IOError, inst:
206 214 if inst.errno != errno.ENOENT:
207 215 raise
208 216 return ""
209 217
210 218 def read(self, path):
211 219 fp = self(path, 'rb')
212 220 try:
213 221 return fp.read()
214 222 finally:
215 223 fp.close()
216 224
217 225 def write(self, path, data):
218 226 fp = self(path, 'wb')
219 227 try:
220 228 return fp.write(data)
221 229 finally:
222 230 fp.close()
223 231
224 232 def append(self, path, data):
225 233 fp = self(path, 'ab')
226 234 try:
227 235 return fp.write(data)
228 236 finally:
229 237 fp.close()
230 238
231 239 def exists(self, path=None):
232 240 return os.path.exists(self.join(path))
233 241
234 242 def isdir(self, path=None):
235 243 return os.path.isdir(self.join(path))
236 244
237 245 def makedir(self, path=None, notindexed=True):
238 246 return util.makedir(self.join(path), notindexed)
239 247
240 248 def makedirs(self, path=None, mode=None):
241 249 return util.makedirs(self.join(path), mode)
242 250
243 251 def mkdir(self, path=None):
244 252 return os.mkdir(self.join(path))
245 253
246 254 def readdir(self, path=None, stat=None, skip=None):
247 255 return osutil.listdir(self.join(path), stat, skip)
248 256
249 257 def stat(self, path=None):
250 258 return os.stat(self.join(path))
251 259
252 260 class vfs(abstractvfs):
253 261 '''Operate files relative to a base directory
254 262
255 263 This class is used to hide the details of COW semantics and
256 264 remote file access from higher level code.
257 265 '''
258 266 def __init__(self, base, audit=True, expand=False):
259 267 if expand:
260 268 base = os.path.realpath(util.expandpath(base))
261 269 self.base = base
262 270 self._setmustaudit(audit)
263 271 self.createmode = None
264 272 self._trustnlink = None
265 273
266 274 def _getmustaudit(self):
267 275 return self._audit
268 276
269 277 def _setmustaudit(self, onoff):
270 278 self._audit = onoff
271 279 if onoff:
272 280 self.audit = pathauditor(self.base)
273 281 else:
274 282 self.audit = util.always
275 283
276 284 mustaudit = property(_getmustaudit, _setmustaudit)
277 285
278 286 @util.propertycache
279 287 def _cansymlink(self):
280 288 return util.checklink(self.base)
281 289
282 290 @util.propertycache
283 291 def _chmod(self):
284 292 return util.checkexec(self.base)
285 293
286 294 def _fixfilemode(self, name):
287 295 if self.createmode is None or not self._chmod:
288 296 return
289 297 os.chmod(name, self.createmode & 0666)
290 298
291 299 def __call__(self, path, mode="r", text=False, atomictemp=False):
292 300 if self._audit:
293 301 r = util.checkosfilename(path)
294 302 if r:
295 303 raise util.Abort("%s: %r" % (r, path))
296 304 self.audit(path)
297 305 f = self.join(path)
298 306
299 307 if not text and "b" not in mode:
300 308 mode += "b" # for that other OS
301 309
302 310 nlink = -1
303 311 if mode not in ('r', 'rb'):
304 312 dirname, basename = util.split(f)
305 313 # If basename is empty, then the path is malformed because it points
306 314 # to a directory. Let the posixfile() call below raise IOError.
307 315 if basename:
308 316 if atomictemp:
309 317 util.ensuredirs(dirname, self.createmode)
310 318 return util.atomictempfile(f, mode, self.createmode)
311 319 try:
312 320 if 'w' in mode:
313 321 util.unlink(f)
314 322 nlink = 0
315 323 else:
316 324 # nlinks() may behave differently for files on Windows
317 325 # shares if the file is open.
318 326 fd = util.posixfile(f)
319 327 nlink = util.nlinks(f)
320 328 if nlink < 1:
321 329 nlink = 2 # force mktempcopy (issue1922)
322 330 fd.close()
323 331 except (OSError, IOError), e:
324 332 if e.errno != errno.ENOENT:
325 333 raise
326 334 nlink = 0
327 335 util.ensuredirs(dirname, self.createmode)
328 336 if nlink > 0:
329 337 if self._trustnlink is None:
330 338 self._trustnlink = nlink > 1 or util.checknlink(f)
331 339 if nlink > 1 or not self._trustnlink:
332 340 util.rename(util.mktempcopy(f), f)
333 341 fp = util.posixfile(f, mode)
334 342 if nlink == 0:
335 343 self._fixfilemode(f)
336 344 return fp
337 345
338 346 def symlink(self, src, dst):
339 347 self.audit(dst)
340 348 linkname = self.join(dst)
341 349 try:
342 350 os.unlink(linkname)
343 351 except OSError:
344 352 pass
345 353
346 354 util.ensuredirs(os.path.dirname(linkname), self.createmode)
347 355
348 356 if self._cansymlink:
349 357 try:
350 358 os.symlink(src, linkname)
351 359 except OSError, err:
352 360 raise OSError(err.errno, _('could not symlink to %r: %s') %
353 361 (src, err.strerror), linkname)
354 362 else:
355 363 self.write(dst, src)
356 364
357 365 def join(self, path):
358 366 if path:
359 367 return os.path.join(self.base, path)
360 368 else:
361 369 return self.base
362 370
363 371 opener = vfs
364 372
365 373 class auditvfs(object):
366 374 def __init__(self, vfs):
367 375 self.vfs = vfs
368 376
369 377 def _getmustaudit(self):
370 378 return self.vfs.mustaudit
371 379
372 380 def _setmustaudit(self, onoff):
373 381 self.vfs.mustaudit = onoff
374 382
375 383 mustaudit = property(_getmustaudit, _setmustaudit)
376 384
377 385 class filtervfs(abstractvfs, auditvfs):
378 386 '''Wrapper vfs for filtering filenames with a function.'''
379 387
380 388 def __init__(self, vfs, filter):
381 389 auditvfs.__init__(self, vfs)
382 390 self._filter = filter
383 391
384 392 def __call__(self, path, *args, **kwargs):
385 393 return self.vfs(self._filter(path), *args, **kwargs)
386 394
387 395 def join(self, path):
388 396 if path:
389 397 return self.vfs.join(self._filter(path))
390 398 else:
391 399 return self.vfs.join(path)
392 400
393 401 filteropener = filtervfs
394 402
395 403 class readonlyvfs(abstractvfs, auditvfs):
396 404 '''Wrapper vfs preventing any writing.'''
397 405
398 406 def __init__(self, vfs):
399 407 auditvfs.__init__(self, vfs)
400 408
401 409 def __call__(self, path, mode='r', *args, **kw):
402 410 if mode not in ('r', 'rb'):
403 411 raise util.Abort('this vfs is read only')
404 412 return self.vfs(path, mode, *args, **kw)
405 413
406 414
407 415 def canonpath(root, cwd, myname, auditor=None):
408 416 '''return the canonical path of myname, given cwd and root'''
409 417 if util.endswithsep(root):
410 418 rootsep = root
411 419 else:
412 420 rootsep = root + os.sep
413 421 name = myname
414 422 if not os.path.isabs(name):
415 423 name = os.path.join(root, cwd, name)
416 424 name = os.path.normpath(name)
417 425 if auditor is None:
418 426 auditor = pathauditor(root)
419 427 if name != rootsep and name.startswith(rootsep):
420 428 name = name[len(rootsep):]
421 429 auditor(name)
422 430 return util.pconvert(name)
423 431 elif name == root:
424 432 return ''
425 433 else:
426 434 # Determine whether `name' is in the hierarchy at or beneath `root',
427 435 # by iterating name=dirname(name) until that causes no change (can't
428 436 # check name == '/', because that doesn't work on windows). The list
429 437 # `rel' holds the reversed list of components making up the relative
430 438 # file name we want.
431 439 rel = []
432 440 while True:
433 441 try:
434 442 s = util.samefile(name, root)
435 443 except OSError:
436 444 s = False
437 445 if s:
438 446 if not rel:
439 447 # name was actually the same as root (maybe a symlink)
440 448 return ''
441 449 rel.reverse()
442 450 name = os.path.join(*rel)
443 451 auditor(name)
444 452 return util.pconvert(name)
445 453 dirname, basename = util.split(name)
446 454 rel.append(basename)
447 455 if dirname == name:
448 456 break
449 457 name = dirname
450 458
451 459 raise util.Abort(_("%s not under root '%s'") % (myname, root))
452 460
453 461 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
454 462 '''yield every hg repository under path, always recursively.
455 463 The recurse flag will only control recursion into repo working dirs'''
456 464 def errhandler(err):
457 465 if err.filename == path:
458 466 raise err
459 467 samestat = getattr(os.path, 'samestat', None)
460 468 if followsym and samestat is not None:
461 469 def adddir(dirlst, dirname):
462 470 match = False
463 471 dirstat = os.stat(dirname)
464 472 for lstdirstat in dirlst:
465 473 if samestat(dirstat, lstdirstat):
466 474 match = True
467 475 break
468 476 if not match:
469 477 dirlst.append(dirstat)
470 478 return not match
471 479 else:
472 480 followsym = False
473 481
474 482 if (seen_dirs is None) and followsym:
475 483 seen_dirs = []
476 484 adddir(seen_dirs, path)
477 485 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
478 486 dirs.sort()
479 487 if '.hg' in dirs:
480 488 yield root # found a repository
481 489 qroot = os.path.join(root, '.hg', 'patches')
482 490 if os.path.isdir(os.path.join(qroot, '.hg')):
483 491 yield qroot # we have a patch queue repo here
484 492 if recurse:
485 493 # avoid recursing inside the .hg directory
486 494 dirs.remove('.hg')
487 495 else:
488 496 dirs[:] = [] # don't descend further
489 497 elif followsym:
490 498 newdirs = []
491 499 for d in dirs:
492 500 fname = os.path.join(root, d)
493 501 if adddir(seen_dirs, fname):
494 502 if os.path.islink(fname):
495 503 for hgname in walkrepos(fname, True, seen_dirs):
496 504 yield hgname
497 505 else:
498 506 newdirs.append(d)
499 507 dirs[:] = newdirs
500 508
501 509 def osrcpath():
502 510 '''return default os-specific hgrc search path'''
503 511 path = systemrcpath()
504 512 path.extend(userrcpath())
505 513 path = [os.path.normpath(f) for f in path]
506 514 return path
507 515
508 516 _rcpath = None
509 517
510 518 def rcpath():
511 519 '''return hgrc search path. if env var HGRCPATH is set, use it.
512 520 for each item in path, if directory, use files ending in .rc,
513 521 else use item.
514 522 make HGRCPATH empty to only look in .hg/hgrc of current repo.
515 523 if no HGRCPATH, use default os-specific path.'''
516 524 global _rcpath
517 525 if _rcpath is None:
518 526 if 'HGRCPATH' in os.environ:
519 527 _rcpath = []
520 528 for p in os.environ['HGRCPATH'].split(os.pathsep):
521 529 if not p:
522 530 continue
523 531 p = util.expandpath(p)
524 532 if os.path.isdir(p):
525 533 for f, kind in osutil.listdir(p):
526 534 if f.endswith('.rc'):
527 535 _rcpath.append(os.path.join(p, f))
528 536 else:
529 537 _rcpath.append(p)
530 538 else:
531 539 _rcpath = osrcpath()
532 540 return _rcpath
533 541
534 if os.name != 'nt':
535
536 def rcfiles(path):
537 rcs = [os.path.join(path, 'hgrc')]
538 rcdir = os.path.join(path, 'hgrc.d')
539 try:
540 rcs.extend([os.path.join(rcdir, f)
541 for f, kind in osutil.listdir(rcdir)
542 if f.endswith(".rc")])
543 except OSError:
544 pass
545 return rcs
546
547 def systemrcpath():
548 path = []
549 if sys.platform == 'plan9':
550 root = 'lib/mercurial'
551 else:
552 root = 'etc/mercurial'
553 # old mod_python does not set sys.argv
554 if len(getattr(sys, 'argv', [])) > 0:
555 p = os.path.dirname(os.path.dirname(sys.argv[0]))
556 path.extend(rcfiles(os.path.join(p, root)))
557 path.extend(rcfiles('/' + root))
558 return path
559
560 def userrcpath():
561 if sys.platform == 'plan9':
562 return [os.environ['home'] + '/lib/hgrc']
563 else:
564 return [os.path.expanduser('~/.hgrc')]
565
566 else:
567
568 import _winreg
569
570 def systemrcpath():
571 '''return default os-specific hgrc search path'''
572 rcpath = []
573 filename = util.executablepath()
574 # Use mercurial.ini found in directory with hg.exe
575 progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
576 if os.path.isfile(progrc):
577 rcpath.append(progrc)
578 return rcpath
579 # Use hgrc.d found in directory with hg.exe
580 progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
581 if os.path.isdir(progrcd):
582 for f, kind in osutil.listdir(progrcd):
583 if f.endswith('.rc'):
584 rcpath.append(os.path.join(progrcd, f))
585 return rcpath
586 # else look for a system rcpath in the registry
587 value = util.lookupreg('SOFTWARE\\Mercurial', None,
588 _winreg.HKEY_LOCAL_MACHINE)
589 if not isinstance(value, str) or not value:
590 return rcpath
591 value = util.localpath(value)
592 for p in value.split(os.pathsep):
593 if p.lower().endswith('mercurial.ini'):
594 rcpath.append(p)
595 elif os.path.isdir(p):
596 for f, kind in osutil.listdir(p):
597 if f.endswith('.rc'):
598 rcpath.append(os.path.join(p, f))
599 return rcpath
600
601 def userrcpath():
602 '''return os-specific hgrc search path to the user dir'''
603 home = os.path.expanduser('~')
604 path = [os.path.join(home, 'mercurial.ini'),
605 os.path.join(home, '.hgrc')]
606 userprofile = os.environ.get('USERPROFILE')
607 if userprofile:
608 path.append(os.path.join(userprofile, 'mercurial.ini'))
609 path.append(os.path.join(userprofile, '.hgrc'))
610 return path
611
612 542 def revsingle(repo, revspec, default='.'):
613 543 if not revspec:
614 544 return repo[default]
615 545
616 546 l = revrange(repo, [revspec])
617 547 if len(l) < 1:
618 548 raise util.Abort(_('empty revision set'))
619 549 return repo[l[-1]]
620 550
621 551 def revpair(repo, revs):
622 552 if not revs:
623 553 return repo.dirstate.p1(), None
624 554
625 555 l = revrange(repo, revs)
626 556
627 557 if len(l) == 0:
628 558 if revs:
629 559 raise util.Abort(_('empty revision range'))
630 560 return repo.dirstate.p1(), None
631 561
632 562 if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
633 563 return repo.lookup(l[0]), None
634 564
635 565 return repo.lookup(l[0]), repo.lookup(l[-1])
636 566
637 567 _revrangesep = ':'
638 568
639 569 def revrange(repo, revs):
640 570 """Yield revision as strings from a list of revision specifications."""
641 571
642 572 def revfix(repo, val, defval):
643 573 if not val and val != 0 and defval is not None:
644 574 return defval
645 575 return repo[val].rev()
646 576
647 577 seen, l = set(), []
648 578 for spec in revs:
649 579 if l and not seen:
650 580 seen = set(l)
651 581 # attempt to parse old-style ranges first to deal with
652 582 # things like old-tag which contain query metacharacters
653 583 try:
654 584 if isinstance(spec, int):
655 585 seen.add(spec)
656 586 l.append(spec)
657 587 continue
658 588
659 589 if _revrangesep in spec:
660 590 start, end = spec.split(_revrangesep, 1)
661 591 start = revfix(repo, start, 0)
662 592 end = revfix(repo, end, len(repo) - 1)
663 593 if end == nullrev and start <= 0:
664 594 start = nullrev
665 595 rangeiter = repo.changelog.revs(start, end)
666 596 if not seen and not l:
667 597 # by far the most common case: revs = ["-1:0"]
668 598 l = list(rangeiter)
669 599 # defer syncing seen until next iteration
670 600 continue
671 601 newrevs = set(rangeiter)
672 602 if seen:
673 603 newrevs.difference_update(seen)
674 604 seen.update(newrevs)
675 605 else:
676 606 seen = newrevs
677 607 l.extend(sorted(newrevs, reverse=start > end))
678 608 continue
679 609 elif spec and spec in repo: # single unquoted rev
680 610 rev = revfix(repo, spec, None)
681 611 if rev in seen:
682 612 continue
683 613 seen.add(rev)
684 614 l.append(rev)
685 615 continue
686 616 except error.RepoLookupError:
687 617 pass
688 618
689 619 # fall through to new-style queries if old-style fails
690 620 m = revset.match(repo.ui, spec)
691 621 dl = [r for r in m(repo, list(repo)) if r not in seen]
692 622 l.extend(dl)
693 623 seen.update(dl)
694 624
695 625 return l
696 626
697 627 def expandpats(pats):
698 628 if not util.expandglobs:
699 629 return list(pats)
700 630 ret = []
701 631 for p in pats:
702 632 kind, name = matchmod._patsplit(p, None)
703 633 if kind is None:
704 634 try:
705 635 globbed = glob.glob(name)
706 636 except re.error:
707 637 globbed = [name]
708 638 if globbed:
709 639 ret.extend(globbed)
710 640 continue
711 641 ret.append(p)
712 642 return ret
713 643
714 644 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
715 645 if pats == ("",):
716 646 pats = []
717 647 if not globbed and default == 'relpath':
718 648 pats = expandpats(pats or [])
719 649
720 650 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
721 651 default)
722 652 def badfn(f, msg):
723 653 ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
724 654 m.bad = badfn
725 655 return m, pats
726 656
727 657 def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
728 658 return matchandpats(ctx, pats, opts, globbed, default)[0]
729 659
730 660 def matchall(repo):
731 661 return matchmod.always(repo.root, repo.getcwd())
732 662
733 663 def matchfiles(repo, files):
734 664 return matchmod.exact(repo.root, repo.getcwd(), files)
735 665
736 666 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
737 667 if dry_run is None:
738 668 dry_run = opts.get('dry_run')
739 669 if similarity is None:
740 670 similarity = float(opts.get('similarity') or 0)
741 671 # we'd use status here, except handling of symlinks and ignore is tricky
742 672 added, unknown, deleted, removed = [], [], [], []
743 673 audit_path = pathauditor(repo.root)
744 674 m = match(repo[None], pats, opts)
745 675 rejected = []
746 676 m.bad = lambda x, y: rejected.append(x)
747 677
748 678 ctx = repo[None]
749 679 walkresults = repo.dirstate.walk(m, sorted(ctx.substate), True, False)
750 680 for abs in sorted(walkresults):
751 681 st = walkresults[abs]
752 682 dstate = repo.dirstate[abs]
753 683 if dstate == '?' and audit_path.check(abs):
754 684 unknown.append(abs)
755 685 if repo.ui.verbose or not m.exact(abs):
756 686 rel = m.rel(abs)
757 687 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
758 688 elif (dstate != 'r' and (not st or
759 689 (stat.S_ISDIR(st.st_mode) and not stat.S_ISLNK(st.st_mode)))):
760 690 deleted.append(abs)
761 691 if repo.ui.verbose or not m.exact(abs):
762 692 rel = m.rel(abs)
763 693 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
764 694 # for finding renames
765 695 elif dstate == 'r':
766 696 removed.append(abs)
767 697 elif dstate == 'a':
768 698 added.append(abs)
769 699 copies = {}
770 700 if similarity > 0:
771 701 for old, new, score in similar.findrenames(repo,
772 702 added + unknown, removed + deleted, similarity):
773 703 if repo.ui.verbose or not m.exact(old) or not m.exact(new):
774 704 repo.ui.status(_('recording removal of %s as rename to %s '
775 705 '(%d%% similar)\n') %
776 706 (m.rel(old), m.rel(new), score * 100))
777 707 copies[new] = old
778 708
779 709 if not dry_run:
780 710 wctx = repo[None]
781 711 wlock = repo.wlock()
782 712 try:
783 713 wctx.forget(deleted)
784 714 wctx.add(unknown)
785 715 for new, old in copies.iteritems():
786 716 wctx.copy(old, new)
787 717 finally:
788 718 wlock.release()
789 719
790 720 for f in rejected:
791 721 if f in m.files():
792 722 return 1
793 723 return 0
794 724
795 725 def updatedir(ui, repo, patches, similarity=0):
796 726 '''Update dirstate after patch application according to metadata'''
797 727 if not patches:
798 728 return []
799 729 copies = []
800 730 removes = set()
801 731 cfiles = patches.keys()
802 732 cwd = repo.getcwd()
803 733 if cwd:
804 734 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
805 735 for f in patches:
806 736 gp = patches[f]
807 737 if not gp:
808 738 continue
809 739 if gp.op == 'RENAME':
810 740 copies.append((gp.oldpath, gp.path))
811 741 removes.add(gp.oldpath)
812 742 elif gp.op == 'COPY':
813 743 copies.append((gp.oldpath, gp.path))
814 744 elif gp.op == 'DELETE':
815 745 removes.add(gp.path)
816 746
817 747 wctx = repo[None]
818 748 for src, dst in copies:
819 749 dirstatecopy(ui, repo, wctx, src, dst, cwd=cwd)
820 750 if (not similarity) and removes:
821 751 wctx.remove(sorted(removes), True)
822 752
823 753 for f in patches:
824 754 gp = patches[f]
825 755 if gp and gp.mode:
826 756 islink, isexec = gp.mode
827 757 dst = repo.wjoin(gp.path)
828 758 # patch won't create empty files
829 759 if gp.op == 'ADD' and not os.path.lexists(dst):
830 760 flags = (isexec and 'x' or '') + (islink and 'l' or '')
831 761 repo.wwrite(gp.path, '', flags)
832 762 util.setflags(dst, islink, isexec)
833 763 addremove(repo, cfiles, similarity=similarity)
834 764 files = patches.keys()
835 765 files.extend([r for r in removes if r not in files])
836 766 return sorted(files)
837 767
838 768 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
839 769 """Update the dirstate to reflect the intent of copying src to dst. For
840 770 different reasons it might not end with dst being marked as copied from src.
841 771 """
842 772 origsrc = repo.dirstate.copied(src) or src
843 773 if dst == origsrc: # copying back a copy?
844 774 if repo.dirstate[dst] not in 'mn' and not dryrun:
845 775 repo.dirstate.normallookup(dst)
846 776 else:
847 777 if repo.dirstate[origsrc] == 'a' and origsrc == src:
848 778 if not ui.quiet:
849 779 ui.warn(_("%s has not been committed yet, so no copy "
850 780 "data will be stored for %s.\n")
851 781 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
852 782 if repo.dirstate[dst] in '?r' and not dryrun:
853 783 wctx.add([dst])
854 784 elif not dryrun:
855 785 wctx.copy(origsrc, dst)
856 786
857 787 def readrequires(opener, supported):
858 788 '''Reads and parses .hg/requires and checks if all entries found
859 789 are in the list of supported features.'''
860 790 requirements = set(opener.read("requires").splitlines())
861 791 missings = []
862 792 for r in requirements:
863 793 if r not in supported:
864 794 if not r or not r[0].isalnum():
865 795 raise error.RequirementError(_(".hg/requires file is corrupt"))
866 796 missings.append(r)
867 797 missings.sort()
868 798 if missings:
869 799 raise error.RequirementError(
870 800 _("unknown repository format: requires features '%s' (upgrade "
871 801 "Mercurial)") % "', '".join(missings))
872 802 return requirements
873 803
874 804 class filecacheentry(object):
875 805 def __init__(self, path, stat=True):
876 806 self.path = path
877 807 self.cachestat = None
878 808 self._cacheable = None
879 809
880 810 if stat:
881 811 self.cachestat = filecacheentry.stat(self.path)
882 812
883 813 if self.cachestat:
884 814 self._cacheable = self.cachestat.cacheable()
885 815 else:
886 816 # None means we don't know yet
887 817 self._cacheable = None
888 818
889 819 def refresh(self):
890 820 if self.cacheable():
891 821 self.cachestat = filecacheentry.stat(self.path)
892 822
893 823 def cacheable(self):
894 824 if self._cacheable is not None:
895 825 return self._cacheable
896 826
897 827 # we don't know yet, assume it is for now
898 828 return True
899 829
900 830 def changed(self):
901 831 # no point in going further if we can't cache it
902 832 if not self.cacheable():
903 833 return True
904 834
905 835 newstat = filecacheentry.stat(self.path)
906 836
907 837 # we may not know if it's cacheable yet, check again now
908 838 if newstat and self._cacheable is None:
909 839 self._cacheable = newstat.cacheable()
910 840
911 841 # check again
912 842 if not self._cacheable:
913 843 return True
914 844
915 845 if self.cachestat != newstat:
916 846 self.cachestat = newstat
917 847 return True
918 848 else:
919 849 return False
920 850
921 851 @staticmethod
922 852 def stat(path):
923 853 try:
924 854 return util.cachestat(path)
925 855 except OSError, e:
926 856 if e.errno != errno.ENOENT:
927 857 raise
928 858
929 859 class filecache(object):
930 860 '''A property like decorator that tracks a file under .hg/ for updates.
931 861
932 862 Records stat info when called in _filecache.
933 863
934 864 On subsequent calls, compares old stat info with new info, and recreates
935 865 the object when needed, updating the new stat info in _filecache.
936 866
937 867 Mercurial either atomic renames or appends for files under .hg,
938 868 so to ensure the cache is reliable we need the filesystem to be able
939 869 to tell us if a file has been replaced. If it can't, we fallback to
940 870 recreating the object on every call (essentially the same behaviour as
941 871 propertycache).'''
942 872 def __init__(self, path):
943 873 self.path = path
944 874
945 875 def join(self, obj, fname):
946 876 """Used to compute the runtime path of the cached file.
947 877
948 878 Users should subclass filecache and provide their own version of this
949 879 function to call the appropriate join function on 'obj' (an instance
950 880 of the class that its member function was decorated).
951 881 """
952 882 return obj.join(fname)
953 883
954 884 def __call__(self, func):
955 885 self.func = func
956 886 self.name = func.__name__
957 887 return self
958 888
959 889 def __get__(self, obj, type=None):
960 890 # do we need to check if the file changed?
961 891 if self.name in obj.__dict__:
962 892 assert self.name in obj._filecache, self.name
963 893 return obj.__dict__[self.name]
964 894
965 895 entry = obj._filecache.get(self.name)
966 896
967 897 if entry:
968 898 if entry.changed():
969 899 entry.obj = self.func(obj)
970 900 else:
971 901 path = self.join(obj, self.path)
972 902
973 903 # We stat -before- creating the object so our cache doesn't lie if
974 904 # a writer modified between the time we read and stat
975 905 entry = filecacheentry(path)
976 906 entry.obj = self.func(obj)
977 907
978 908 obj._filecache[self.name] = entry
979 909
980 910 obj.__dict__[self.name] = entry.obj
981 911 return entry.obj
982 912
983 913 def __set__(self, obj, value):
984 914 if self.name not in obj._filecache:
985 915 # we add an entry for the missing value because X in __dict__
986 916 # implies X in _filecache
987 917 ce = filecacheentry(self.join(obj, self.path), False)
988 918 obj._filecache[self.name] = ce
989 919 else:
990 920 ce = obj._filecache[self.name]
991 921
992 922 ce.obj = value # update cached copy
993 923 obj.__dict__[self.name] = value # update copy returned by obj.x
994 924
995 925 def __delete__(self, obj):
996 926 try:
997 927 del obj.__dict__[self.name]
998 928 except KeyError:
999 929 raise AttributeError(self.name)
General Comments 0
You need to be logged in to leave comments. Login now