##// END OF EJS Templates
Try to use ini-file in the same directory as the exe as the default on NT.
Try to use ini-file in the same directory as the exe as the default on NT.

File last commit:

r1396:8c3e2a25 default
r1399:9a70776e default
Show More
dirstate.py
425 lines | 13.5 KiB | text/x-python | PythonLexer
mpm@selenic.com
Break apart hg.py...
r1089 """
dirstate.py - working directory tracking for mercurial
Copyright 2005 Matt Mackall <mpm@selenic.com>
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
"""
mpm@selenic.com
Adjust some imports
r1094 import struct, os
from node import *
mpm@selenic.com
Break apart hg.py...
r1089 from demandload import *
mpm@selenic.com
Fix dirstate imports
r1104 demandload(globals(), "time bisect stat util re")
mpm@selenic.com
Break apart hg.py...
r1089
class dirstate:
def __init__(self, opener, ui, root):
self.opener = opener
self.root = root
self.dirty = 0
self.ui = ui
self.map = None
self.pl = None
self.copies = {}
self.ignorefunc = None
mason@suse.com
Optimize dirstate walking...
r1183 self.blockignore = False
mpm@selenic.com
Break apart hg.py...
r1089
def wjoin(self, f):
return os.path.join(self.root, f)
def getcwd(self):
cwd = os.getcwd()
if cwd == self.root: return ''
return cwd[len(self.root) + 1:]
Bryan O'Sullivan
Switch to new syntax for .hgignore files....
r1270 def hgignore(self):
'''return the contents of .hgignore as a list of patterns.
trailing white space is dropped.
the escape character is backslash.
comments start with #.
empty lines are skipped.
lines can be of the following formats:
syntax: regexp # defaults following lines to non-rooted regexps
syntax: glob # defaults following lines to non-rooted globs
re:pattern # non-rooted regular expression
glob:pattern # non-rooted glob
pattern # pattern of the current default type'''
syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
def parselines(fp):
for line in fp:
escape = False
for i in xrange(len(line)):
if escape: escape = False
elif line[i] == '\\': escape = True
elif line[i] == '#': break
line = line[:i].rstrip()
if line: yield line
pats = []
try:
fp = open(self.wjoin('.hgignore'))
syntax = 'relre:'
for line in parselines(fp):
if line.startswith('syntax:'):
s = line[7:].strip()
try:
syntax = syntaxes[s]
except KeyError:
self.ui.warn("ignoring invalid syntax '%s'\n" % s)
continue
pat = syntax + line
for s in syntaxes.values():
if line.startswith(s):
pat = line
break
pats.append(pat)
except IOError: pass
return pats
def ignore(self, fn):
'''default match function used by dirstate and localrepository.
this honours the .hgignore file, and nothing more.'''
mason@suse.com
Optimize dirstate walking...
r1183 if self.blockignore:
return False
mpm@selenic.com
Break apart hg.py...
r1089 if not self.ignorefunc:
Bryan O'Sullivan
Fix ignore regression....
r1271 ignore = self.hgignore()
if ignore:
files, self.ignorefunc, anypats = util.matcher(self.root,
inc=ignore)
else:
self.ignorefunc = util.never
Bryan O'Sullivan
Switch to new syntax for .hgignore files....
r1270 return self.ignorefunc(fn)
mpm@selenic.com
Break apart hg.py...
r1089
def __del__(self):
if self.dirty:
self.write()
def __getitem__(self, key):
try:
return self.map[key]
except TypeError:
self.read()
return self[key]
def __contains__(self, key):
if not self.map: self.read()
return key in self.map
def parents(self):
if not self.pl:
self.read()
return self.pl
def markdirty(self):
if not self.dirty:
self.dirty = 1
def setparents(self, p1, p2=nullid):
Matt Mackall
dirstate: make sure we read the dirstate before setting parents
r1394 if not self.pl:
self.read()
mpm@selenic.com
Break apart hg.py...
r1089 self.markdirty()
self.pl = p1, p2
def state(self, key):
try:
return self[key][0]
except KeyError:
return "?"
def read(self):
if self.map is not None: return self.map
self.map = {}
self.pl = [nullid, nullid]
try:
st = self.opener("dirstate").read()
if not st: return
except: return
self.pl = [st[:20], st[20: 40]]
pos = 40
while pos < len(st):
e = struct.unpack(">cllll", st[pos:pos+17])
l = e[4]
pos += 17
f = st[pos:pos + l]
if '\0' in f:
f, c = f.split('\0')
self.copies[f] = c
self.map[f] = e[:4]
pos += l
def copy(self, source, dest):
self.read()
self.markdirty()
self.copies[dest] = source
def copied(self, file):
return self.copies.get(file, None)
def update(self, files, state, **kw):
''' current states:
n normal
m needs merging
r marked for removal
a marked for addition'''
if not files: return
self.read()
self.markdirty()
for f in files:
if state == "r":
self.map[f] = ('r', 0, 0, 0)
else:
mpm@selenic.com
dirstate: two more stat -> lstat changes
r1230 s = os.lstat(os.path.join(self.root, f))
mpm@selenic.com
Break apart hg.py...
r1089 st_size = kw.get('st_size', s.st_size)
st_mtime = kw.get('st_mtime', s.st_mtime)
self.map[f] = (state, s.st_mode, st_size, st_mtime)
mpm@selenic.com
fix some rename/copy bugs...
r1117 if self.copies.has_key(f):
del self.copies[f]
mpm@selenic.com
Break apart hg.py...
r1089
def forget(self, files):
if not files: return
self.read()
self.markdirty()
for f in files:
try:
del self.map[f]
except KeyError:
self.ui.warn("not in dirstate: %s!\n" % f)
pass
def clear(self):
self.map = {}
self.markdirty()
def write(self):
st = self.opener("dirstate", "w")
st.write("".join(self.pl))
for f, e in self.map.items():
c = self.copied(f)
if c:
f = f + "\0" + c
e = struct.pack(">cllll", e[0], e[1], e[2], e[3], len(f))
st.write(e + f)
self.dirty = 0
def filterfiles(self, files):
ret = {}
unknown = []
for x in files:
if x is '.':
return self.map.copy()
if x not in self.map:
unknown.append(x)
else:
ret[x] = self.map[x]
if not unknown:
return ret
b = self.map.keys()
b.sort()
blen = len(b)
for x in unknown:
bs = bisect.bisect(b, x)
if bs != 0 and b[bs-1] == x:
ret[x] = self.map[x]
continue
while bs < blen:
s = b[bs]
if len(s) > len(x) and s.startswith(x) and s[len(x)] == '/':
ret[s] = self.map[s]
else:
break
bs += 1
return ret
def walk(self, files=None, match=util.always, dc=None):
self.read()
# walk all files by default
if not files:
files = [self.root]
if not dc:
dc = self.map.copy()
elif not dc:
dc = self.filterfiles(files)
mason@suse.com
Optimize dirstate walking...
r1183 def statmatch(file, stat):
mpm@selenic.com
Fix Windows status problem from new dirstate walk code
r1224 file = util.pconvert(file)
mason@suse.com
Optimize dirstate walking...
r1183 if file not in dc and self.ignore(file):
return False
return match(file)
mpm@selenic.com
Fix Windows status problem from new dirstate walk code
r1224
mason@suse.com
Optimize dirstate walking...
r1183 return self.walkhelper(files=files, statmatch=statmatch, dc=dc)
# walk recursively through the directory tree, finding all files
# matched by the statmatch function
mpm@selenic.com
Fix Windows status problem from new dirstate walk code
r1224 #
mason@suse.com
Optimize dirstate walking...
r1183 # results are yielded in a tuple (src, filename), where src is one of:
# 'f' the file was found in the directory tree
# 'm' the file was only in the dirstate and not in the tree
#
# dc is an optional arg for the current dirstate. dc is not modified
# directly by this function, but might be modified by your statmatch call.
#
def walkhelper(self, files, statmatch, dc):
Benoit Boissinot
add a check for filetype when walking
r1392 def supported_type(f, st):
if stat.S_ISREG(st.st_mode):
return True
else:
kind = 'unknown'
if stat.S_ISCHR(st.st_mode): kind = 'character device'
elif stat.S_ISBLK(st.st_mode): kind = 'block device'
elif stat.S_ISFIFO(st.st_mode): kind = 'fifo'
elif stat.S_ISLNK(st.st_mode): kind = 'symbolic link'
elif stat.S_ISSOCK(st.st_mode): kind = 'socket'
elif stat.S_ISDIR(st.st_mode): kind = 'directory'
self.ui.warn('%s: unsupported file type (type is %s)\n' % (
util.pathto(self.getcwd(), f),
kind))
return False
mason@suse.com
Optimize dirstate walking...
r1183 # recursion free walker, faster than os.walk.
def findfiles(s):
retfiles = []
work = [s]
while work:
top = work.pop()
names = os.listdir(top)
names.sort()
# nd is the top of the repository dir tree
nd = util.normpath(top[len(self.root) + 1:])
if nd == '.': nd = ''
for f in names:
np = os.path.join(nd, f)
if seen(np):
continue
p = os.path.join(top, f)
mpm@selenic.com
Fix dangling symlink bug in dirstate walk code
r1228 # don't trip over symlinks
st = os.lstat(p)
mason@suse.com
Optimize dirstate walking...
r1183 if stat.S_ISDIR(st.st_mode):
ds = os.path.join(nd, f +'/')
if statmatch(ds, st):
work.append(p)
Benoit Boissinot
check if a file is ignored before complaining if it is an unsupported type
r1396 elif statmatch(np, st) and supported_type(np, st):
yield util.pconvert(np)
mason@suse.com
Optimize dirstate walking...
r1183
Benoit Boissinot
add a check for filetype when walking
r1392
mpm@selenic.com
Break apart hg.py...
r1089 known = {'.hg': 1}
def seen(fn):
if fn in known: return True
known[fn] = 1
mason@suse.com
Optimize dirstate walking...
r1183
# step one, find all files that match our criteria
files.sort()
for ff in util.unique(files):
f = os.path.join(self.root, ff)
try:
mpm@selenic.com
dirstate: two more stat -> lstat changes
r1230 st = os.lstat(f)
mason@suse.com
Optimize dirstate walking...
r1183 except OSError, inst:
if ff not in dc: self.ui.warn('%s: %s\n' % (
util.pathto(self.getcwd(), ff),
inst.strerror))
continue
if stat.S_ISDIR(st.st_mode):
sorted = [ x for x in findfiles(f) ]
sorted.sort()
for fl in sorted:
yield 'f', fl
Benoit Boissinot
add a check for filetype when walking
r1392 else:
mason@suse.com
Optimize dirstate walking...
r1183 ff = util.normpath(ff)
if seen(ff):
mpm@selenic.com
Break apart hg.py...
r1089 continue
mason@suse.com
Optimize dirstate walking...
r1183 found = False
self.blockignore = True
Benoit Boissinot
check if a file is ignored before complaining if it is an unsupported type
r1396 if statmatch(ff, st) and supported_type(ff, st):
mason@suse.com
Optimize dirstate walking...
r1183 found = True
self.blockignore = False
if found:
mpm@selenic.com
Break apart hg.py...
r1089 yield 'f', ff
mason@suse.com
Optimize dirstate walking...
r1183 # step two run through anything left in the dc hash and yield
# if we haven't already seen it
ks = dc.keys()
ks.sort()
for k in ks:
if not seen(k) and (statmatch(k, None)):
mpm@selenic.com
Break apart hg.py...
r1089 yield 'm', k
def changes(self, files=None, match=util.always):
self.read()
if not files:
mason@suse.com
Optimize dirstate walking...
r1183 files = [self.root]
mpm@selenic.com
Break apart hg.py...
r1089 dc = self.map.copy()
else:
dc = self.filterfiles(files)
lookup, modified, added, unknown = [], [], [], []
removed, deleted = [], []
mason@suse.com
Optimize dirstate walking...
r1183 # statmatch function to eliminate entries from the dirstate copy
# and put files into the appropriate array. This gets passed
# to the walking code
def statmatch(fn, s):
mpm@selenic.com
Fix Windows status problem from new dirstate walk code
r1224 fn = util.pconvert(fn)
mason@suse.com
Optimize dirstate walking...
r1183 def checkappend(l, fn):
if match is util.always or match(fn):
l.append(fn)
mpm@selenic.com
Fix Windows status problem from new dirstate walk code
r1224
mason@suse.com
Optimize dirstate walking...
r1183 if not s or stat.S_ISDIR(s.st_mode):
Bryan O'Sullivan
Fix performance of dirstate.changes with ignored directories.
r1268 if self.ignore(fn): return False
return match(fn)
mason@suse.com
Optimize dirstate walking...
r1183
c = dc.pop(fn, None)
mpm@selenic.com
Break apart hg.py...
r1089 if c:
mason@suse.com
Optimize dirstate walking...
r1183 type, mode, size, time = c
# check the common case first
if type == 'n':
if size != s.st_size or (mode ^ s.st_mode) & 0100:
checkappend(modified, fn)
elif time != s.st_mtime:
checkappend(lookup, fn)
elif type == 'm':
checkappend(modified, fn)
elif type == 'a':
checkappend(added, fn)
elif type == 'r':
checkappend(unknown, fn)
Bryan O'Sullivan
Switch to new syntax for .hgignore files....
r1270 elif not self.ignore(fn) and match(fn):
unknown.append(fn)
mason@suse.com
Optimize dirstate walking...
r1183 # return false because we've already handled all cases above.
# there's no need for the walking code to process the file
# any further.
return False
mpm@selenic.com
Break apart hg.py...
r1089
mason@suse.com
Optimize dirstate walking...
r1183 # because our statmatch always returns false, self.walk will only
# return files in the dirstate map that are not present in the FS.
# But, we still need to iterate through the results to force the
# walk to complete
for src, fn in self.walkhelper(files, statmatch, dc):
pass
Bryan O'Sullivan
Fix dirstate.changes for ignored directories....
r1276 # there may be patterns in the .hgignore file that prevent us
# from examining entire directories in the dirstate map, so we
# go back and explicitly examine any matching files we've
# ignored
unexamined = [fn for fn in dc.iterkeys()
if self.ignore(fn) and match(fn)]
for src, fn in self.walkhelper(unexamined, statmatch, dc):
pass
mason@suse.com
Optimize dirstate walking...
r1183 # anything left in dc didn't exist in the filesystem
Bryan O'Sullivan
Fix dirstate.changes for ignored directories....
r1276 for fn, c in dc.iteritems():
if not match(fn): continue
mpm@selenic.com
Break apart hg.py...
r1089 if c[0] == 'r':
removed.append(fn)
else:
deleted.append(fn)
return (lookup, modified, added, removed + deleted, unknown)