##// END OF EJS Templates
Reduce string duplication in compression code...
Reduce string duplication in compression code This cuts down on string copies and allows buffers (instead of strings) to be passed into the compression code

File last commit:

r1529:a208e86b default
r1533:3d11f81c default
Show More
dirstate.py
412 lines | 13.1 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 *
Benoit Boissinot
i18n first part: make '_' available for files who need it
r1400 from i18n import gettext as _
mpm@selenic.com
Break apart hg.py...
r1089 from demandload import *
Benoit Boissinot
fix dirstate.change: it should walk ignored files
r1476 demandload(globals(), "time bisect stat util re errno")
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:
Benoit Boissinot
i18n part2: use '_' for all strings who are part of the user interface
r1402 self.ui.warn(_("ignoring invalid syntax '%s'\n") % s)
Bryan O'Sullivan
Switch to new syntax for .hgignore files....
r1270 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:
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 self.lazyread()
mpm@selenic.com
Break apart hg.py...
r1089 return self[key]
def __contains__(self, key):
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 self.lazyread()
mpm@selenic.com
Break apart hg.py...
r1089 return key in self.map
def parents(self):
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 self.lazyread()
mpm@selenic.com
Break apart hg.py...
r1089 return self.pl
def markdirty(self):
if not self.dirty:
self.dirty = 1
def setparents(self, p1, p2=nullid):
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 self.lazyread()
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 "?"
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 def lazyread(self):
if self.map is None:
self.read()
mpm@selenic.com
Break apart hg.py...
r1089 def read(self):
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):
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 self.lazyread()
mpm@selenic.com
Break apart hg.py...
r1089 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
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 self.lazyread()
mpm@selenic.com
Break apart hg.py...
r1089 self.markdirty()
for f in files:
if state == "r":
self.map[f] = ('r', 0, 0, 0)
else:
Benoit Boissinot
use self.{w,}join when possible
r1510 s = os.lstat(self.wjoin(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
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 self.lazyread()
mpm@selenic.com
Break apart hg.py...
r1089 self.markdirty()
for f in files:
try:
del self.map[f]
except KeyError:
Benoit Boissinot
i18n part2: use '_' for all strings who are part of the user interface
r1402 self.ui.warn(_("not in dirstate: %s!\n") % f)
mpm@selenic.com
Break apart hg.py...
r1089 pass
def clear(self):
self.map = {}
self.markdirty()
def write(self):
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 st = self.opener("dirstate", "w", atomic=True)
mpm@selenic.com
Break apart hg.py...
r1089 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
Benoit Boissinot
don't print anything about file of unsupported type unless...
r1527 def supported_type(self, f, st, verbose=False):
Benoit Boissinot
fix handling of files of unsupported type in the walk code...
r1487 if stat.S_ISREG(st.st_mode):
return True
if verbose:
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
Benoit Boissinot
rewrote changes function in dirstate to use generic walk code...
r1471 def statwalk(self, files=None, match=util.always, dc=None):
Benoit Boissinot
add dirstate.lazyread, write atomically the dirstate...
r1529 self.lazyread()
mpm@selenic.com
Break apart hg.py...
r1089
# 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)
Benoit Boissinot
rewrote changes function in dirstate to use generic walk code...
r1471 def walk(self, files=None, match=util.always, dc=None):
# filter out the stat
for src, f, st in self.statwalk(files, match, dc):
yield src, f
mason@suse.com
Optimize dirstate walking...
r1183 # 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 #
Benoit Boissinot
rewrote changes function in dirstate to use generic walk code...
r1471 # results are yielded in a tuple (src, filename, st), where src
# is one of:
mason@suse.com
Optimize dirstate walking...
r1183 # 'f' the file was found in the directory tree
# 'm' the file was only in the dirstate and not in the tree
Benoit Boissinot
rewrote changes function in dirstate to use generic walk code...
r1471 # and st is the stat result if the file was found in the directory.
mason@suse.com
Optimize dirstate walking...
r1183 #
# 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):
# 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
fix handling of files of unsupported type in the walk code...
r1487 if statmatch(np, st) and np in dc:
yield 'm', util.pconvert(np), st
elif statmatch(np, st):
if self.supported_type(np, st):
yield 'f', util.pconvert(np), st
elif np in dc:
yield 'm', util.pconvert(np), st
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):
Benoit Boissinot
use self.{w,}join when possible
r1510 f = self.wjoin(ff)
mason@suse.com
Optimize dirstate walking...
r1183 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):
Benoit Boissinot
fix handling of files of unsupported type in the walk code...
r1487 cmp1 = (lambda x, y: cmp(x[1], y[1]))
mason@suse.com
Optimize dirstate walking...
r1183 sorted = [ x for x in findfiles(f) ]
Benoit Boissinot
fix handling of files of unsupported type in the walk code...
r1487 sorted.sort(cmp1)
for e in sorted:
yield e
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 self.blockignore = True
Benoit Boissinot
fix handling of files of unsupported type in the walk code...
r1487 if statmatch(ff, st):
Benoit Boissinot
don't print anything about file of unsupported type unless...
r1527 if self.supported_type(ff, st, verbose=True):
Benoit Boissinot
fix handling of files of unsupported type in the walk code...
r1487 yield 'f', ff, st
elif ff in dc:
yield 'm', ff, st
mason@suse.com
Optimize dirstate walking...
r1183 self.blockignore = False
mpm@selenic.com
Break apart hg.py...
r1089
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)):
Benoit Boissinot
rewrote changes function in dirstate to use generic walk code...
r1471 yield 'm', k, None
mpm@selenic.com
Break apart hg.py...
r1089
def changes(self, files=None, match=util.always):
lookup, modified, added, unknown = [], [], [], []
removed, deleted = [], []
Benoit Boissinot
rewrote changes function in dirstate to use generic walk code...
r1471 for src, fn, st in self.statwalk(files, match):
try:
type, mode, size, time = self[fn]
except KeyError:
Bryan O'Sullivan
Switch to new syntax for .hgignore files....
r1270 unknown.append(fn)
Benoit Boissinot
rewrote changes function in dirstate to use generic walk code...
r1471 continue
Benoit Boissinot
fix dirstate.change: it should walk ignored files
r1476 if src == 'm':
Benoit Boissinot
fix handling of files of unsupported type in the walk code...
r1487 nonexistent = True
if not st:
try:
Benoit Boissinot
use self.{w,}join when possible
r1510 f = self.wjoin(fn)
Benoit Boissinot
fix a bug in dirstate.changes when cwd != repo.root...
r1491 st = os.lstat(f)
Benoit Boissinot
fix handling of files of unsupported type in the walk code...
r1487 except OSError, inst:
if inst.errno != errno.ENOENT:
raise
st = None
# We need to re-check that it is a valid file
if st and self.supported_type(fn, st):
nonexistent = False
Benoit Boissinot
fix dirstate.change: it should walk ignored files
r1476 # XXX: what to do with file no longer present in the fs
# who are not removed in the dirstate ?
Benoit Boissinot
only files in normal state should be marked as deleted...
r1488 if nonexistent and type in "nm":
Benoit Boissinot
fix dirstate.change: it should walk ignored files
r1476 deleted.append(fn)
continue
Benoit Boissinot
rewrote changes function in dirstate to use generic walk code...
r1471 # check the common case first
if type == 'n':
if not st:
st = os.stat(fn)
if size != st.st_size or (mode ^ st.st_mode) & 0100:
modified.append(fn)
elif time != st.st_mtime:
lookup.append(fn)
elif type == 'm':
modified.append(fn)
elif type == 'a':
added.append(fn)
elif type == 'r':
removed.append(fn)
mason@suse.com
Optimize dirstate walking...
r1183
mpm@selenic.com
Break apart hg.py...
r1089 return (lookup, modified, added, removed + deleted, unknown)