mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 08:47:12 +03:00
f4e623cf8f
When handling OSError while visiting subdirectories, we're checking errno, but we never imported this module. This small patch fixes this.
239 lines
8.5 KiB
Python
239 lines
8.5 KiB
Python
import os, stat, re, errno
|
|
|
|
from mercurial import dirstate
|
|
from mercurial import hg
|
|
from mercurial import ignore
|
|
from mercurial import match as matchmod
|
|
from mercurial import osutil
|
|
from mercurial import scmutil
|
|
# pathauditor moved to pathutil in 2.8
|
|
try:
|
|
from mercurial import pathutil
|
|
pathutil.pathauditor
|
|
except:
|
|
pathutil = scmutil
|
|
from mercurial import util
|
|
from mercurial.i18n import _
|
|
|
|
def gignorepats(orig, lines, root = None):
|
|
'''parse lines (iterable) of .gitignore text, returning a tuple of
|
|
(patterns, parse errors). These patterns should be given to compile()
|
|
to be validated and converted into a match function.'''
|
|
syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
|
|
syntax = 'glob:'
|
|
|
|
patterns = []
|
|
warnings = []
|
|
|
|
for line in lines:
|
|
if "#" in line:
|
|
_commentre = re.compile(r'((^|[^\\])(\\\\)*)#.*')
|
|
# remove comments prefixed by an even number of escapes
|
|
line = _commentre.sub(r'\1', line)
|
|
# fixup properly escaped comments that survived the above
|
|
line = line.replace("\\#", "#")
|
|
line = line.rstrip()
|
|
if not line:
|
|
continue
|
|
|
|
if line.startswith('!'):
|
|
warnings.append(_("unsupported ignore pattern '%s'") % line)
|
|
continue
|
|
rootprefix = '%s/' % root if root else ''
|
|
if line.startswith('/'):
|
|
line = line[1:]
|
|
rootsuffixes = ['']
|
|
else:
|
|
rootsuffixes = ['','**/']
|
|
for rootsuffix in rootsuffixes:
|
|
pat = syntax + rootprefix + rootsuffix + line
|
|
for s, rels in syntaxes.iteritems():
|
|
if line.startswith(rels):
|
|
pat = line
|
|
break
|
|
elif line.startswith(s+':'):
|
|
pat = rels + line[len(s) + 1:]
|
|
break
|
|
patterns.append(pat)
|
|
|
|
return patterns, warnings
|
|
|
|
def gignore(orig, root, files, warn, extrapatterns=None):
|
|
pats = ignore.readpats(root, files, warn)
|
|
allpats = []
|
|
if extrapatterns:
|
|
allpats.extend(extrapatterns)
|
|
for f, patlist in pats:
|
|
allpats.extend(patlist)
|
|
if not allpats:
|
|
return util.never
|
|
try:
|
|
ignorefunc = matchmod.match(root, '', [], allpats)
|
|
except util.Abort:
|
|
for f, patlist in pats:
|
|
try:
|
|
matchmod.match(root, '', [], patlist)
|
|
except util.Abort, inst:
|
|
raise util.Abort('%s: %s' % (f, inst[0]))
|
|
if extrapatterns:
|
|
try:
|
|
matchmod.match(root, '', [], extrapatterns)
|
|
except util.Abort, inst:
|
|
raise util.Abort('%s: %s' % ('extra patterns', inst[0]))
|
|
return ignorefunc
|
|
|
|
class gitdirstate(dirstate.dirstate):
|
|
@dirstate.rootcache('.hgignore')
|
|
def _ignore(self):
|
|
files = [self._join('.hgignore')]
|
|
for name, path in self._ui.configitems("ui"):
|
|
if name == 'ignore' or name.startswith('ignore.'):
|
|
files.append(util.expandpath(path))
|
|
patterns = []
|
|
# Only use .gitignore if there's no .hgignore
|
|
try:
|
|
fp = open(files[0])
|
|
fp.close()
|
|
except:
|
|
fns = self._finddotgitignores()
|
|
for fn in fns:
|
|
d = os.path.dirname(fn)
|
|
fn = self.pathto(fn)
|
|
fp = open(fn)
|
|
pats, warnings = gignorepats(None,fp,root=d)
|
|
for warning in warnings:
|
|
self._ui.warn("%s: %s\n" % (fn, warning))
|
|
patterns.extend(pats)
|
|
return ignore.ignore(self._root, files, self._ui.warn, extrapatterns=patterns)
|
|
|
|
def _finddotgitignores(self):
|
|
"""A copy of dirstate.walk. This is called from the new _ignore method,
|
|
which is called by dirstate.walk, which would cause infinite recursion,
|
|
except _finddotgitignores calls the superclass _ignore directly."""
|
|
match = matchmod.match(self._root, self.getcwd(), ['relglob:.gitignore'])
|
|
#TODO: need subrepos?
|
|
subrepos = []
|
|
unknown = True
|
|
ignored = False
|
|
full=True
|
|
|
|
def fwarn(f, msg):
|
|
self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
|
|
return False
|
|
|
|
ignore = super(gitdirstate,self)._ignore
|
|
dirignore = self._dirignore
|
|
if ignored:
|
|
ignore = util.never
|
|
dirignore = util.never
|
|
elif not unknown:
|
|
# if unknown and ignored are False, skip step 2
|
|
ignore = util.always
|
|
dirignore = util.always
|
|
|
|
matchfn = match.matchfn
|
|
matchalways = match.always()
|
|
matchtdir = match.traversedir
|
|
dmap = self._map
|
|
listdir = osutil.listdir
|
|
lstat = os.lstat
|
|
dirkind = stat.S_IFDIR
|
|
regkind = stat.S_IFREG
|
|
lnkkind = stat.S_IFLNK
|
|
join = self._join
|
|
|
|
exact = skipstep3 = False
|
|
if matchfn == match.exact: # match.exact
|
|
exact = True
|
|
dirignore = util.always # skip step 2
|
|
elif match.files() and not match.anypats(): # match.match, no patterns
|
|
skipstep3 = True
|
|
|
|
if not exact and self._checkcase:
|
|
normalize = self._normalize
|
|
skipstep3 = False
|
|
else:
|
|
normalize = None
|
|
|
|
# step 1: find all explicit files
|
|
results, work, dirsnotfound = self._walkexplicit(match, subrepos)
|
|
|
|
skipstep3 = skipstep3 and not (work or dirsnotfound)
|
|
work = [d for d in work if not dirignore(d)]
|
|
wadd = work.append
|
|
|
|
# step 2: visit subdirectories
|
|
while work:
|
|
nd = work.pop()
|
|
skip = None
|
|
if nd == '.':
|
|
nd = ''
|
|
else:
|
|
skip = '.hg'
|
|
try:
|
|
entries = listdir(join(nd), stat=True, skip=skip)
|
|
except OSError, inst:
|
|
if inst.errno in (errno.EACCES, errno.ENOENT):
|
|
fwarn(nd, inst.strerror)
|
|
continue
|
|
raise
|
|
for f, kind, st in entries:
|
|
if normalize:
|
|
nf = normalize(nd and (nd + "/" + f) or f, True, True)
|
|
else:
|
|
nf = nd and (nd + "/" + f) or f
|
|
if nf not in results:
|
|
if kind == dirkind:
|
|
if not ignore(nf):
|
|
if matchtdir:
|
|
matchtdir(nf)
|
|
wadd(nf)
|
|
if nf in dmap and (matchalways or matchfn(nf)):
|
|
results[nf] = None
|
|
elif kind == regkind or kind == lnkkind:
|
|
if nf in dmap:
|
|
if matchalways or matchfn(nf):
|
|
results[nf] = st
|
|
elif (matchalways or matchfn(nf)) and not ignore(nf):
|
|
results[nf] = st
|
|
elif nf in dmap and (matchalways or matchfn(nf)):
|
|
results[nf] = None
|
|
|
|
for s in subrepos:
|
|
del results[s]
|
|
del results['.hg']
|
|
|
|
# step 3: report unseen items in the dmap hash
|
|
if not skipstep3 and not exact:
|
|
if not results and matchalways:
|
|
visit = dmap.keys()
|
|
else:
|
|
visit = [f for f in dmap if f not in results and matchfn(f)]
|
|
visit.sort()
|
|
|
|
if unknown:
|
|
# unknown == True means we walked the full directory tree above.
|
|
# So if a file is not seen it was either a) not matching matchfn
|
|
# b) ignored, c) missing, or d) under a symlink directory.
|
|
audit_path = pathutil.pathauditor(self._root)
|
|
|
|
for nf in iter(visit):
|
|
# Report ignored items in the dmap as long as they are not
|
|
# under a symlink directory.
|
|
if audit_path.check(nf):
|
|
try:
|
|
results[nf] = lstat(join(nf))
|
|
except OSError:
|
|
# file doesn't exist
|
|
results[nf] = None
|
|
else:
|
|
# It's either missing or under a symlink directory
|
|
results[nf] = None
|
|
else:
|
|
# We may not have walked the full directory tree above,
|
|
# so stat everything we missed.
|
|
nf = iter(visit).next
|
|
for st in util.statfiles([join(i) for i in visit]):
|
|
results[nf()] = st
|
|
return results.keys()
|