1
# scmutil.py - Mercurial core utility functions
3
# Copyright Matt Mackall <mpm@selenic.com>
5
# This software may be used and distributed according to the terms of the
6
# GNU General Public License version 2 or any later version.
10
from mercurial.node import nullrev
11
import util, error, osutil, revset, similar, encoding, phases, parsers
12
import match as matchmod
13
import os, errno, re, stat, glob
16
import scmwindows as scmplatform
18
import scmposix as scmplatform
20
systemrcpath = scmplatform.systemrcpath
21
userrcpath = scmplatform.userrcpath
24
return encoding.hfsignoreclean(s.lower())
26
def nochangesfound(ui, repo, excluded=None):
27
'''Report no changes for push/pull, excluded is None or a list of
28
nodes excluded from the push/pull.
34
# discovery should not have included the filtered revision,
35
# we have to explicitly exclude it until discovery is cleanup.
38
if ctx.phase() >= phases.secret and not ctx.extinct():
42
ui.status(_("no changes found (ignored %d secret changesets)\n")
45
ui.status(_("no changes found\n"))
47
def checknewlabel(repo, lbl, kind):
48
# Do not use the "kind" parameter in ui output.
49
# It makes strings difficult to translate.
50
if lbl in ['tip', '.', 'null']:
51
raise util.Abort(_("the name '%s' is reserved") % lbl)
52
for c in (':', '\0', '\n', '\r'):
54
raise util.Abort(_("%r cannot be used in a name") % c)
57
raise util.Abort(_("cannot use an integer as a name"))
62
'''Check that the filename f is an acceptable filename for a tracked file'''
63
if '\r' in f or '\n' in f:
64
raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
66
def checkportable(ui, f):
67
'''Check if filename f is portable and warn or abort depending on config'''
69
abort, warn = checkportabilityalert(ui)
71
msg = util.checkwinfilename(f)
73
msg = "%s: %r" % (msg, f)
76
ui.warn(_("warning: %s\n") % msg)
78
def checkportabilityalert(ui):
79
'''check if the user's config requests nothing, a warning, or abort for
80
non-portable filenames'''
81
val = ui.config('ui', 'portablefilenames', 'warn')
83
bval = util.parsebool(val)
84
abort = os.name == 'nt' or lval == 'abort'
85
warn = bval or lval == 'warn'
86
if bval is None and not (warn or abort or lval == 'ignore'):
87
raise error.ConfigError(
88
_("ui.portablefilenames value is invalid ('%s')") % val)
91
class casecollisionauditor(object):
92
def __init__(self, ui, abort, dirstate):
95
allfiles = '\0'.join(dirstate._map)
96
self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
97
self._dirstate = dirstate
98
# The purpose of _newfiles is so that we don't complain about
99
# case collisions if someone were to call this object with the
100
# same filename twice.
101
self._newfiles = set()
103
def __call__(self, f):
104
fl = encoding.lower(f)
105
if (fl in self._loweredfiles and f not in self._dirstate and
106
f not in self._newfiles):
107
msg = _('possible case-folding collision for %s') % f
109
raise util.Abort(msg)
110
self._ui.warn(_("warning: %s\n") % msg)
111
self._loweredfiles.add(fl)
112
self._newfiles.add(f)
114
class pathauditor(object):
115
'''ensure that a filesystem path contains no banned components.
116
the following properties of a path are checked:
118
- ends with a directory separator
119
- under top-level .hg
120
- starts at the root of a windows drive
122
- traverses a symlink (e.g. a/symlink_here/b)
123
- inside a nested repository (a callback can be used to approve
124
some nested repositories, e.g., subrepositories)
127
def __init__(self, root, callback=None):
129
self.auditeddir = set()
131
self.callback = callback
132
if os.path.lexists(root) and not util.checkcase(root):
133
self.normcase = util.normcase
135
self.normcase = lambda x: x
137
def __call__(self, path):
138
'''Check the relative path.
139
path may contain a pattern (e.g. foodir/**.txt)'''
141
path = util.localpath(path)
142
normpath = self.normcase(path)
143
if normpath in self.audited:
145
# AIX ignores "/" at end of path, others raise EISDIR.
146
if util.endswithsep(path):
147
raise util.Abort(_("path ends in directory separator: %s") % path)
148
parts = util.splitpath(path)
149
if (os.path.splitdrive(path)[0]
150
or _lowerclean(parts[0]) in ('.hg', '.hg.', '')
151
or os.pardir in parts):
152
raise util.Abort(_("path contains illegal component: %s") % path)
153
if '.hg' in _lowerclean(path):
154
lparts = [_lowerclean(p.lower()) for p in parts]
155
for p in '.hg', '.hg.':
157
pos = lparts.index(p)
158
base = os.path.join(*parts[:pos])
159
raise util.Abort(_("path '%s' is inside nested repo %r")
162
normparts = util.splitpath(normpath)
163
assert len(parts) == len(normparts)
169
prefix = os.sep.join(parts)
170
normprefix = os.sep.join(normparts)
171
if normprefix in self.auditeddir:
173
curpath = os.path.join(self.root, prefix)
175
st = os.lstat(curpath)
177
# EINVAL can be raised as invalid path syntax under win32.
178
# They must be ignored for patterns can be checked too.
179
if err.errno not in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
182
if stat.S_ISLNK(st.st_mode):
184
_('path %r traverses symbolic link %r')
186
elif (stat.S_ISDIR(st.st_mode) and
187
os.path.isdir(os.path.join(curpath, '.hg'))):
188
if not self.callback or not self.callback(curpath):
189
raise util.Abort(_("path '%s' is inside nested "
192
prefixes.append(normprefix)
196
self.audited.add(normpath)
197
# only add prefixes to the cache after checking everything: we don't
198
# want to add "foo/bar/baz" before checking if there's a "foo/.hg"
199
self.auditeddir.update(prefixes)
201
def check(self, path):
205
except (OSError, util.Abort):
208
class abstractvfs(object):
209
"""Abstract base class; cannot be instantiated"""
211
def __init__(self, *args, **kwargs):
212
'''Prevent instantiation; don't call this from subclasses.'''
213
raise NotImplementedError('attempted instantiating ' + str(type(self)))
215
def tryread(self, path):
216
'''gracefully return an empty string for missing files'''
218
return self.read(path)
219
except IOError, inst:
220
if inst.errno != errno.ENOENT:
224
def open(self, path, mode="r", text=False, atomictemp=False):
225
self.open = self.__call__
226
return self.__call__(path, mode, text, atomictemp)
228
def read(self, path):
229
fp = self(path, 'rb')
235
def write(self, path, data):
236
fp = self(path, 'wb')
238
return fp.write(data)
242
def append(self, path, data):
243
fp = self(path, 'ab')
245
return fp.write(data)
249
def exists(self, path=None):
250
return os.path.exists(self.join(path))
253
return util.fstat(fp)
255
def isdir(self, path=None):
256
return os.path.isdir(self.join(path))
258
def islink(self, path=None):
259
return os.path.islink(self.join(path))
261
def lstat(self, path=None):
262
return os.lstat(self.join(path))
264
def makedir(self, path=None, notindexed=True):
265
return util.makedir(self.join(path), notindexed)
267
def makedirs(self, path=None, mode=None):
268
return util.makedirs(self.join(path), mode)
270
def mkdir(self, path=None):
271
return os.mkdir(self.join(path))
273
def readdir(self, path=None, stat=None, skip=None):
274
return osutil.listdir(self.join(path), stat, skip)
276
def rename(self, src, dst):
277
return util.rename(self.join(src), self.join(dst))
279
def readlink(self, path):
280
return os.readlink(self.join(path))
282
def setflags(self, path, l, x):
283
return util.setflags(self.join(path), l, x)
285
def stat(self, path=None):
286
return os.stat(self.join(path))
288
def unlink(self, path=None):
289
return util.unlink(self.join(path))
291
def utime(self, path=None, t=None):
292
return os.utime(self.join(path), t)
294
class vfs(abstractvfs):
295
'''Operate files relative to a base directory
297
This class is used to hide the details of COW semantics and
298
remote file access from higher level code.
300
def __init__(self, base, audit=True, expandpath=False, realpath=False):
302
base = util.expandpath(base)
304
base = os.path.realpath(base)
306
self._setmustaudit(audit)
307
self.createmode = None
308
self._trustnlink = None
310
def _getmustaudit(self):
313
def _setmustaudit(self, onoff):
316
self.audit = pathauditor(self.base)
318
self.audit = util.always
320
mustaudit = property(_getmustaudit, _setmustaudit)
323
def _cansymlink(self):
324
return util.checklink(self.base)
328
return util.checkexec(self.base)
330
def _fixfilemode(self, name):
331
if self.createmode is None or not self._chmod:
333
os.chmod(name, self.createmode & 0666)
335
def __call__(self, path, mode="r", text=False, atomictemp=False):
337
r = util.checkosfilename(path)
339
raise util.Abort("%s: %r" % (r, path))
343
if not text and "b" not in mode:
344
mode += "b" # for that other OS
347
if mode not in ('r', 'rb'):
348
dirname, basename = util.split(f)
349
# If basename is empty, then the path is malformed because it points
350
# to a directory. Let the posixfile() call below raise IOError.
353
util.ensuredirs(dirname, self.createmode)
354
return util.atomictempfile(f, mode, self.createmode)
360
# nlinks() may behave differently for files on Windows
361
# shares if the file is open.
362
fd = util.posixfile(f)
363
nlink = util.nlinks(f)
365
nlink = 2 # force mktempcopy (issue1922)
367
except (OSError, IOError), e:
368
if e.errno != errno.ENOENT:
371
util.ensuredirs(dirname, self.createmode)
373
if self._trustnlink is None:
374
self._trustnlink = nlink > 1 or util.checknlink(f)
375
if nlink > 1 or not self._trustnlink:
376
util.rename(util.mktempcopy(f), f)
377
fp = util.posixfile(f, mode)
382
def symlink(self, src, dst):
384
linkname = self.join(dst)
390
util.ensuredirs(os.path.dirname(linkname), self.createmode)
394
os.symlink(src, linkname)
396
raise OSError(err.errno, _('could not symlink to %r: %s') %
397
(src, err.strerror), linkname)
401
def join(self, path):
403
return os.path.join(self.base, path)
409
class auditvfs(object):
410
def __init__(self, vfs):
413
def _getmustaudit(self):
414
return self.vfs.mustaudit
416
def _setmustaudit(self, onoff):
417
self.vfs.mustaudit = onoff
419
mustaudit = property(_getmustaudit, _setmustaudit)
421
class filtervfs(abstractvfs, auditvfs):
422
'''Wrapper vfs for filtering filenames with a function.'''
424
def __init__(self, vfs, filter):
425
auditvfs.__init__(self, vfs)
426
self._filter = filter
428
def __call__(self, path, *args, **kwargs):
429
return self.vfs(self._filter(path), *args, **kwargs)
431
def join(self, path):
433
return self.vfs.join(self._filter(path))
435
return self.vfs.join(path)
437
filteropener = filtervfs
439
class readonlyvfs(abstractvfs, auditvfs):
440
'''Wrapper vfs preventing any writing.'''
442
def __init__(self, vfs):
443
auditvfs.__init__(self, vfs)
445
def __call__(self, path, mode='r', *args, **kw):
446
if mode not in ('r', 'rb'):
447
raise util.Abort('this vfs is read only')
448
return self.vfs(path, mode, *args, **kw)
451
def canonpath(root, cwd, myname, auditor=None):
452
'''return the canonical path of myname, given cwd and root'''
453
if util.endswithsep(root):
456
rootsep = root + os.sep
458
if not os.path.isabs(name):
459
name = os.path.join(root, cwd, name)
460
name = os.path.normpath(name)
462
auditor = pathauditor(root)
463
if name != rootsep and name.startswith(rootsep):
464
name = name[len(rootsep):]
466
return util.pconvert(name)
470
# Determine whether `name' is in the hierarchy at or beneath `root',
471
# by iterating name=dirname(name) until that causes no change (can't
472
# check name == '/', because that doesn't work on windows). The list
473
# `rel' holds the reversed list of components making up the relative
478
s = util.samefile(name, root)
483
# name was actually the same as root (maybe a symlink)
486
name = os.path.join(*rel)
488
return util.pconvert(name)
489
dirname, basename = util.split(name)
495
raise util.Abort(_("%s not under root '%s'") % (myname, root))
497
def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
498
'''yield every hg repository under path, always recursively.
499
The recurse flag will only control recursion into repo working dirs'''
501
if err.filename == path:
503
samestat = getattr(os.path, 'samestat', None)
504
if followsym and samestat is not None:
505
def adddir(dirlst, dirname):
507
dirstat = os.stat(dirname)
508
for lstdirstat in dirlst:
509
if samestat(dirstat, lstdirstat):
513
dirlst.append(dirstat)
518
if (seen_dirs is None) and followsym:
520
adddir(seen_dirs, path)
521
for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
524
yield root # found a repository
525
qroot = os.path.join(root, '.hg', 'patches')
526
if os.path.isdir(os.path.join(qroot, '.hg')):
527
yield qroot # we have a patch queue repo here
529
# avoid recursing inside the .hg directory
532
dirs[:] = [] # don't descend further
536
fname = os.path.join(root, d)
537
if adddir(seen_dirs, fname):
538
if os.path.islink(fname):
539
for hgname in walkrepos(fname, True, seen_dirs):
546
'''return default os-specific hgrc search path'''
547
path = systemrcpath()
548
path.extend(userrcpath())
549
path = [os.path.normpath(f) for f in path]
555
'''return hgrc search path. if env var HGRCPATH is set, use it.
556
for each item in path, if directory, use files ending in .rc,
558
make HGRCPATH empty to only look in .hg/hgrc of current repo.
559
if no HGRCPATH, use default os-specific path.'''
562
if 'HGRCPATH' in os.environ:
564
for p in os.environ['HGRCPATH'].split(os.pathsep):
567
p = util.expandpath(p)
569
for f, kind in osutil.listdir(p):
570
if f.endswith('.rc'):
571
_rcpath.append(os.path.join(p, f))
578
def revsingle(repo, revspec, default='.'):
579
if not revspec and revspec != 0:
582
l = revrange(repo, [revspec])
584
raise util.Abort(_('empty revision set'))
587
def revpair(repo, revs):
589
return repo.dirstate.p1(), None
591
l = revrange(repo, revs)
595
raise util.Abort(_('empty revision range'))
596
return repo.dirstate.p1(), None
598
if len(l) == 1 and len(revs) == 1 and _revrangesep not in revs[0]:
599
return repo.lookup(l[0]), None
601
return repo.lookup(l[0]), repo.lookup(l[-1])
605
def revrange(repo, revs):
606
"""Yield revision as strings from a list of revision specifications."""
608
def revfix(repo, val, defval):
609
if not val and val != 0 and defval is not None:
611
return repo[val].rev()
617
# attempt to parse old-style ranges first to deal with
618
# things like old-tag which contain query metacharacters
620
if isinstance(spec, int):
625
if _revrangesep in spec:
626
start, end = spec.split(_revrangesep, 1)
627
start = revfix(repo, start, 0)
628
end = revfix(repo, end, len(repo) - 1)
629
if end == nullrev and start <= 0:
631
rangeiter = repo.changelog.revs(start, end)
632
if not seen and not l:
633
# by far the most common case: revs = ["-1:0"]
635
# defer syncing seen until next iteration
637
newrevs = set(rangeiter)
639
newrevs.difference_update(seen)
643
l.extend(sorted(newrevs, reverse=start > end))
645
elif spec and spec in repo: # single unquoted rev
646
rev = revfix(repo, spec, None)
652
except error.RepoLookupError:
655
# fall through to new-style queries if old-style fails
656
m = revset.match(repo.ui, spec)
657
dl = [r for r in m(repo, list(repo)) if r not in seen]
663
def expandpats(pats):
664
if not util.expandglobs:
668
kind, name = matchmod._patsplit(p, None)
671
globbed = glob.glob(name)
680
def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath'):
683
if not globbed and default == 'relpath':
684
pats = expandpats(pats or [])
686
m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
689
ctx._repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
693
def match(ctx, pats=[], opts={}, globbed=False, default='relpath'):
694
return matchandpats(ctx, pats, opts, globbed, default)[0]
697
return matchmod.always(repo.root, repo.getcwd())
699
def matchfiles(repo, files):
700
return matchmod.exact(repo.root, repo.getcwd(), files)
702
def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
704
dry_run = opts.get('dry_run')
705
if similarity is None:
706
similarity = float(opts.get('similarity') or 0)
707
# we'd use status here, except handling of symlinks and ignore is tricky
708
m = match(repo[None], pats, opts)
710
m.bad = lambda x, y: rejected.append(x)
712
added, unknown, deleted, removed = _interestingfiles(repo, m)
714
unknownset = set(unknown)
715
toprint = unknownset.copy()
716
toprint.update(deleted)
717
for abs in sorted(toprint):
718
if repo.ui.verbose or not m.exact(abs):
720
if abs in unknownset:
721
status = _('adding %s\n') % ((pats and rel) or abs)
723
status = _('removing %s\n') % ((pats and rel) or abs)
724
repo.ui.status(status)
726
renames = _findrenames(repo, m, added + unknown, removed + deleted,
730
_markchanges(repo, unknown, deleted, renames)
737
def marktouched(repo, files, similarity=0.0):
738
'''Assert that files have somehow been operated upon. files are relative to
740
m = matchfiles(repo, files)
742
m.bad = lambda x, y: rejected.append(x)
744
added, unknown, deleted, removed = _interestingfiles(repo, m)
747
unknownset = set(unknown)
748
toprint = unknownset.copy()
749
toprint.update(deleted)
750
for abs in sorted(toprint):
751
if abs in unknownset:
752
status = _('adding %s\n') % abs
754
status = _('removing %s\n') % abs
755
repo.ui.status(status)
757
renames = _findrenames(repo, m, added + unknown, removed + deleted,
760
_markchanges(repo, unknown, deleted, renames)
767
def _interestingfiles(repo, matcher):
768
'''Walk dirstate with matcher, looking for files that addremove would care
771
This is different from dirstate.status because it doesn't care about
772
whether files are modified or clean.'''
773
added, unknown, deleted, removed = [], [], [], []
774
audit_path = pathauditor(repo.root)
777
dirstate = repo.dirstate
778
walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
780
for abs, st in walkresults.iteritems():
781
dstate = dirstate[abs]
782
if dstate == '?' and audit_path.check(abs):
784
elif dstate != 'r' and not st:
786
# for finding renames
792
return added, unknown, deleted, removed
794
def _findrenames(repo, matcher, added, removed, similarity):
795
'''Find renames from removed files to added ones.'''
798
for old, new, score in similar.findrenames(repo, added, removed,
800
if (repo.ui.verbose or not matcher.exact(old)
801
or not matcher.exact(new)):
802
repo.ui.status(_('recording removal of %s as rename to %s '
803
'(%d%% similar)\n') %
804
(matcher.rel(old), matcher.rel(new),
809
def _markchanges(repo, unknown, deleted, renames):
810
'''Marks the files in unknown as added, the files in deleted as removed,
811
and the files in renames as copied.'''
817
for new, old in renames.iteritems():
822
def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
823
"""Update the dirstate to reflect the intent of copying src to dst. For
824
different reasons it might not end with dst being marked as copied from src.
826
origsrc = repo.dirstate.copied(src) or src
827
if dst == origsrc: # copying back a copy?
828
if repo.dirstate[dst] not in 'mn' and not dryrun:
829
repo.dirstate.normallookup(dst)
831
if repo.dirstate[origsrc] == 'a' and origsrc == src:
833
ui.warn(_("%s has not been committed yet, so no copy "
834
"data will be stored for %s.\n")
835
% (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
836
if repo.dirstate[dst] in '?r' and not dryrun:
839
wctx.copy(origsrc, dst)
841
def readrequires(opener, supported):
842
'''Reads and parses .hg/requires and checks if all entries found
843
are in the list of supported features.'''
844
requirements = set(opener.read("requires").splitlines())
846
for r in requirements:
847
if r not in supported:
848
if not r or not r[0].isalnum():
849
raise error.RequirementError(_(".hg/requires file is corrupt"))
853
raise error.RequirementError(
854
_("unknown repository format: requires features '%s' (upgrade "
855
"Mercurial)") % "', '".join(missings))
858
class filecacheentry(object):
859
def __init__(self, path, stat=True):
861
self.cachestat = None
862
self._cacheable = None
865
self.cachestat = filecacheentry.stat(self.path)
868
self._cacheable = self.cachestat.cacheable()
870
# None means we don't know yet
871
self._cacheable = None
875
self.cachestat = filecacheentry.stat(self.path)
878
if self._cacheable is not None:
879
return self._cacheable
881
# we don't know yet, assume it is for now
885
# no point in going further if we can't cache it
886
if not self.cacheable():
889
newstat = filecacheentry.stat(self.path)
891
# we may not know if it's cacheable yet, check again now
892
if newstat and self._cacheable is None:
893
self._cacheable = newstat.cacheable()
896
if not self._cacheable:
899
if self.cachestat != newstat:
900
self.cachestat = newstat
908
return util.cachestat(path)
910
if e.errno != errno.ENOENT:
913
class filecache(object):
914
'''A property like decorator that tracks a file under .hg/ for updates.
916
Records stat info when called in _filecache.
918
On subsequent calls, compares old stat info with new info, and recreates
919
the object when needed, updating the new stat info in _filecache.
921
Mercurial either atomic renames or appends for files under .hg,
922
so to ensure the cache is reliable we need the filesystem to be able
923
to tell us if a file has been replaced. If it can't, we fallback to
924
recreating the object on every call (essentially the same behaviour as
926
def __init__(self, path):
929
def join(self, obj, fname):
930
"""Used to compute the runtime path of the cached file.
932
Users should subclass filecache and provide their own version of this
933
function to call the appropriate join function on 'obj' (an instance
934
of the class that its member function was decorated).
936
return obj.join(fname)
938
def __call__(self, func):
940
self.name = func.__name__
943
def __get__(self, obj, type=None):
944
# do we need to check if the file changed?
945
if self.name in obj.__dict__:
946
assert self.name in obj._filecache, self.name
947
return obj.__dict__[self.name]
949
entry = obj._filecache.get(self.name)
953
entry.obj = self.func(obj)
955
path = self.join(obj, self.path)
957
# We stat -before- creating the object so our cache doesn't lie if
958
# a writer modified between the time we read and stat
959
entry = filecacheentry(path)
960
entry.obj = self.func(obj)
962
obj._filecache[self.name] = entry
964
obj.__dict__[self.name] = entry.obj
967
def __set__(self, obj, value):
968
if self.name not in obj._filecache:
969
# we add an entry for the missing value because X in __dict__
970
# implies X in _filecache
971
ce = filecacheentry(self.join(obj, self.path), False)
972
obj._filecache[self.name] = ce
974
ce = obj._filecache[self.name]
976
ce.obj = value # update cached copy
977
obj.__dict__[self.name] = value # update copy returned by obj.x
979
def __delete__(self, obj):
981
del obj.__dict__[self.name]
983
raise AttributeError(self.name)
986
'''a multiset of directory names from a dirstate or manifest'''
988
def __init__(self, map, skip=None):
990
addpath = self.addpath
991
if util.safehasattr(map, 'iteritems') and skip is not None:
992
for f, s in map.iteritems():
999
def addpath(self, path):
1001
for base in finddirs(path):
1007
def delpath(self, path):
1009
for base in finddirs(path):
1016
return self._dirs.iterkeys()
1018
def __contains__(self, d):
1019
return d in self._dirs
1021
if util.safehasattr(parsers, 'dirs'):
1025
pos = path.rfind('/')
1028
pos = path.rfind('/', 0, pos)