add hg and python
This commit is contained in:
parent
3a742c699f
commit
458120dd40
3709 changed files with 1244309 additions and 1 deletions
601
sys/lib/python/mercurial/dirstate.py
Normal file
601
sys/lib/python/mercurial/dirstate.py
Normal file
|
@ -0,0 +1,601 @@
|
|||
# dirstate.py - working directory tracking for mercurial
|
||||
#
|
||||
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
|
||||
#
|
||||
# This software may be used and distributed according to the terms of the
|
||||
# GNU General Public License version 2, incorporated herein by reference.
|
||||
|
||||
from node import nullid
|
||||
from i18n import _
|
||||
import util, ignore, osutil, parsers
|
||||
import struct, os, stat, errno
|
||||
import cStringIO, sys
|
||||
|
||||
_unknown = ('?', 0, 0, 0)
|
||||
_format = ">cllll"
|
||||
propertycache = util.propertycache
|
||||
|
||||
def _finddirs(path):
|
||||
pos = path.rfind('/')
|
||||
while pos != -1:
|
||||
yield path[:pos]
|
||||
pos = path.rfind('/', 0, pos)
|
||||
|
||||
def _incdirs(dirs, path):
|
||||
for base in _finddirs(path):
|
||||
if base in dirs:
|
||||
dirs[base] += 1
|
||||
return
|
||||
dirs[base] = 1
|
||||
|
||||
def _decdirs(dirs, path):
|
||||
for base in _finddirs(path):
|
||||
if dirs[base] > 1:
|
||||
dirs[base] -= 1
|
||||
return
|
||||
del dirs[base]
|
||||
|
||||
class dirstate(object):
|
||||
|
||||
def __init__(self, opener, ui, root):
|
||||
self._opener = opener
|
||||
self._root = root
|
||||
self._rootdir = os.path.join(root, '')
|
||||
self._dirty = False
|
||||
self._dirtypl = False
|
||||
self._ui = ui
|
||||
|
||||
@propertycache
|
||||
def _map(self):
|
||||
self._read()
|
||||
return self._map
|
||||
|
||||
@propertycache
|
||||
def _copymap(self):
|
||||
self._read()
|
||||
return self._copymap
|
||||
|
||||
@propertycache
|
||||
def _foldmap(self):
|
||||
f = {}
|
||||
for name in self._map:
|
||||
f[os.path.normcase(name)] = name
|
||||
return f
|
||||
|
||||
@propertycache
|
||||
def _branch(self):
|
||||
try:
|
||||
return self._opener("branch").read().strip() or "default"
|
||||
except IOError:
|
||||
return "default"
|
||||
|
||||
@propertycache
|
||||
def _pl(self):
|
||||
try:
|
||||
st = self._opener("dirstate").read(40)
|
||||
l = len(st)
|
||||
if l == 40:
|
||||
return st[:20], st[20:40]
|
||||
elif l > 0 and l < 40:
|
||||
raise util.Abort(_('working directory state appears damaged!'))
|
||||
except IOError, err:
|
||||
if err.errno != errno.ENOENT: raise
|
||||
return [nullid, nullid]
|
||||
|
||||
@propertycache
|
||||
def _dirs(self):
|
||||
dirs = {}
|
||||
for f,s in self._map.iteritems():
|
||||
if s[0] != 'r':
|
||||
_incdirs(dirs, f)
|
||||
return dirs
|
||||
|
||||
@propertycache
|
||||
def _ignore(self):
|
||||
files = [self._join('.hgignore')]
|
||||
for name, path in self._ui.configitems("ui"):
|
||||
if name == 'ignore' or name.startswith('ignore.'):
|
||||
files.append(os.path.expanduser(path))
|
||||
return ignore.ignore(self._root, files, self._ui.warn)
|
||||
|
||||
@propertycache
|
||||
def _slash(self):
|
||||
return self._ui.configbool('ui', 'slash') and os.sep != '/'
|
||||
|
||||
@propertycache
|
||||
def _checklink(self):
|
||||
return util.checklink(self._root)
|
||||
|
||||
@propertycache
|
||||
def _checkexec(self):
|
||||
return util.checkexec(self._root)
|
||||
|
||||
@propertycache
|
||||
def _checkcase(self):
|
||||
return not util.checkcase(self._join('.hg'))
|
||||
|
||||
def _join(self, f):
|
||||
# much faster than os.path.join()
|
||||
# it's safe because f is always a relative path
|
||||
return self._rootdir + f
|
||||
|
||||
def flagfunc(self, fallback):
|
||||
if self._checklink:
|
||||
if self._checkexec:
|
||||
def f(x):
|
||||
p = self._join(x)
|
||||
if os.path.islink(p):
|
||||
return 'l'
|
||||
if util.is_exec(p):
|
||||
return 'x'
|
||||
return ''
|
||||
return f
|
||||
def f(x):
|
||||
if os.path.islink(self._join(x)):
|
||||
return 'l'
|
||||
if 'x' in fallback(x):
|
||||
return 'x'
|
||||
return ''
|
||||
return f
|
||||
if self._checkexec:
|
||||
def f(x):
|
||||
if 'l' in fallback(x):
|
||||
return 'l'
|
||||
if util.is_exec(self._join(x)):
|
||||
return 'x'
|
||||
return ''
|
||||
return f
|
||||
return fallback
|
||||
|
||||
def getcwd(self):
|
||||
cwd = os.getcwd()
|
||||
if cwd == self._root: return ''
|
||||
# self._root ends with a path separator if self._root is '/' or 'C:\'
|
||||
rootsep = self._root
|
||||
if not util.endswithsep(rootsep):
|
||||
rootsep += os.sep
|
||||
if cwd.startswith(rootsep):
|
||||
return cwd[len(rootsep):]
|
||||
else:
|
||||
# we're outside the repo. return an absolute path.
|
||||
return cwd
|
||||
|
||||
def pathto(self, f, cwd=None):
|
||||
if cwd is None:
|
||||
cwd = self.getcwd()
|
||||
path = util.pathto(self._root, cwd, f)
|
||||
if self._slash:
|
||||
return util.normpath(path)
|
||||
return path
|
||||
|
||||
def __getitem__(self, key):
|
||||
''' current states:
|
||||
n normal
|
||||
m needs merging
|
||||
r marked for removal
|
||||
a marked for addition
|
||||
? not tracked'''
|
||||
return self._map.get(key, ("?",))[0]
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._map
|
||||
|
||||
def __iter__(self):
|
||||
for x in sorted(self._map):
|
||||
yield x
|
||||
|
||||
def parents(self):
|
||||
return self._pl
|
||||
|
||||
def branch(self):
|
||||
return self._branch
|
||||
|
||||
def setparents(self, p1, p2=nullid):
|
||||
self._dirty = self._dirtypl = True
|
||||
self._pl = p1, p2
|
||||
|
||||
def setbranch(self, branch):
|
||||
self._branch = branch
|
||||
self._opener("branch", "w").write(branch + '\n')
|
||||
|
||||
def _read(self):
|
||||
self._map = {}
|
||||
self._copymap = {}
|
||||
try:
|
||||
st = self._opener("dirstate").read()
|
||||
except IOError, err:
|
||||
if err.errno != errno.ENOENT: raise
|
||||
return
|
||||
if not st:
|
||||
return
|
||||
|
||||
p = parsers.parse_dirstate(self._map, self._copymap, st)
|
||||
if not self._dirtypl:
|
||||
self._pl = p
|
||||
|
||||
def invalidate(self):
|
||||
for a in "_map _copymap _foldmap _branch _pl _dirs _ignore".split():
|
||||
if a in self.__dict__:
|
||||
delattr(self, a)
|
||||
self._dirty = False
|
||||
|
||||
def copy(self, source, dest):
|
||||
"""Mark dest as a copy of source. Unmark dest if source is None.
|
||||
"""
|
||||
if source == dest:
|
||||
return
|
||||
self._dirty = True
|
||||
if source is not None:
|
||||
self._copymap[dest] = source
|
||||
elif dest in self._copymap:
|
||||
del self._copymap[dest]
|
||||
|
||||
def copied(self, file):
|
||||
return self._copymap.get(file, None)
|
||||
|
||||
def copies(self):
|
||||
return self._copymap
|
||||
|
||||
def _droppath(self, f):
|
||||
if self[f] not in "?r" and "_dirs" in self.__dict__:
|
||||
_decdirs(self._dirs, f)
|
||||
|
||||
def _addpath(self, f, check=False):
|
||||
oldstate = self[f]
|
||||
if check or oldstate == "r":
|
||||
if '\r' in f or '\n' in f:
|
||||
raise util.Abort(
|
||||
_("'\\n' and '\\r' disallowed in filenames: %r") % f)
|
||||
if f in self._dirs:
|
||||
raise util.Abort(_('directory %r already in dirstate') % f)
|
||||
# shadows
|
||||
for d in _finddirs(f):
|
||||
if d in self._dirs:
|
||||
break
|
||||
if d in self._map and self[d] != 'r':
|
||||
raise util.Abort(
|
||||
_('file %r in dirstate clashes with %r') % (d, f))
|
||||
if oldstate in "?r" and "_dirs" in self.__dict__:
|
||||
_incdirs(self._dirs, f)
|
||||
|
||||
def normal(self, f):
|
||||
'mark a file normal and clean'
|
||||
self._dirty = True
|
||||
self._addpath(f)
|
||||
s = os.lstat(self._join(f))
|
||||
self._map[f] = ('n', s.st_mode, s.st_size, int(s.st_mtime))
|
||||
if f in self._copymap:
|
||||
del self._copymap[f]
|
||||
|
||||
def normallookup(self, f):
|
||||
'mark a file normal, but possibly dirty'
|
||||
if self._pl[1] != nullid and f in self._map:
|
||||
# if there is a merge going on and the file was either
|
||||
# in state 'm' or dirty before being removed, restore that state.
|
||||
entry = self._map[f]
|
||||
if entry[0] == 'r' and entry[2] in (-1, -2):
|
||||
source = self._copymap.get(f)
|
||||
if entry[2] == -1:
|
||||
self.merge(f)
|
||||
elif entry[2] == -2:
|
||||
self.normaldirty(f)
|
||||
if source:
|
||||
self.copy(source, f)
|
||||
return
|
||||
if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
|
||||
return
|
||||
self._dirty = True
|
||||
self._addpath(f)
|
||||
self._map[f] = ('n', 0, -1, -1)
|
||||
if f in self._copymap:
|
||||
del self._copymap[f]
|
||||
|
||||
def normaldirty(self, f):
|
||||
'mark a file normal, but dirty'
|
||||
self._dirty = True
|
||||
self._addpath(f)
|
||||
self._map[f] = ('n', 0, -2, -1)
|
||||
if f in self._copymap:
|
||||
del self._copymap[f]
|
||||
|
||||
def add(self, f):
|
||||
'mark a file added'
|
||||
self._dirty = True
|
||||
self._addpath(f, True)
|
||||
self._map[f] = ('a', 0, -1, -1)
|
||||
if f in self._copymap:
|
||||
del self._copymap[f]
|
||||
|
||||
def remove(self, f):
|
||||
'mark a file removed'
|
||||
self._dirty = True
|
||||
self._droppath(f)
|
||||
size = 0
|
||||
if self._pl[1] != nullid and f in self._map:
|
||||
entry = self._map[f]
|
||||
if entry[0] == 'm':
|
||||
size = -1
|
||||
elif entry[0] == 'n' and entry[2] == -2:
|
||||
size = -2
|
||||
self._map[f] = ('r', 0, size, 0)
|
||||
if size == 0 and f in self._copymap:
|
||||
del self._copymap[f]
|
||||
|
||||
def merge(self, f):
|
||||
'mark a file merged'
|
||||
self._dirty = True
|
||||
s = os.lstat(self._join(f))
|
||||
self._addpath(f)
|
||||
self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime))
|
||||
if f in self._copymap:
|
||||
del self._copymap[f]
|
||||
|
||||
def forget(self, f):
|
||||
'forget a file'
|
||||
self._dirty = True
|
||||
try:
|
||||
self._droppath(f)
|
||||
del self._map[f]
|
||||
except KeyError:
|
||||
self._ui.warn(_("not in dirstate: %s\n") % f)
|
||||
|
||||
def _normalize(self, path, knownpath):
|
||||
norm_path = os.path.normcase(path)
|
||||
fold_path = self._foldmap.get(norm_path, None)
|
||||
if fold_path is None:
|
||||
if knownpath or not os.path.exists(os.path.join(self._root, path)):
|
||||
fold_path = path
|
||||
else:
|
||||
fold_path = self._foldmap.setdefault(norm_path,
|
||||
util.fspath(path, self._root))
|
||||
return fold_path
|
||||
|
||||
def clear(self):
|
||||
self._map = {}
|
||||
if "_dirs" in self.__dict__:
|
||||
delattr(self, "_dirs");
|
||||
self._copymap = {}
|
||||
self._pl = [nullid, nullid]
|
||||
self._dirty = True
|
||||
|
||||
def rebuild(self, parent, files):
|
||||
self.clear()
|
||||
for f in files:
|
||||
if 'x' in files.flags(f):
|
||||
self._map[f] = ('n', 0777, -1, 0)
|
||||
else:
|
||||
self._map[f] = ('n', 0666, -1, 0)
|
||||
self._pl = (parent, nullid)
|
||||
self._dirty = True
|
||||
|
||||
def write(self):
|
||||
if not self._dirty:
|
||||
return
|
||||
st = self._opener("dirstate", "w", atomictemp=True)
|
||||
|
||||
try:
|
||||
gran = int(self._ui.config('dirstate', 'granularity', 1))
|
||||
except ValueError:
|
||||
gran = 1
|
||||
limit = sys.maxint
|
||||
if gran > 0:
|
||||
limit = util.fstat(st).st_mtime - gran
|
||||
|
||||
cs = cStringIO.StringIO()
|
||||
copymap = self._copymap
|
||||
pack = struct.pack
|
||||
write = cs.write
|
||||
write("".join(self._pl))
|
||||
for f, e in self._map.iteritems():
|
||||
if f in copymap:
|
||||
f = "%s\0%s" % (f, copymap[f])
|
||||
if e[3] > limit and e[0] == 'n':
|
||||
e = (e[0], 0, -1, -1)
|
||||
e = pack(_format, e[0], e[1], e[2], e[3], len(f))
|
||||
write(e)
|
||||
write(f)
|
||||
st.write(cs.getvalue())
|
||||
st.rename()
|
||||
self._dirty = self._dirtypl = False
|
||||
|
||||
def _dirignore(self, f):
|
||||
if f == '.':
|
||||
return False
|
||||
if self._ignore(f):
|
||||
return True
|
||||
for p in _finddirs(f):
|
||||
if self._ignore(p):
|
||||
return True
|
||||
return False
|
||||
|
||||
def walk(self, match, unknown, ignored):
|
||||
'''
|
||||
walk recursively through the directory tree, finding all files
|
||||
matched by the match function
|
||||
|
||||
results are yielded in a tuple (filename, stat), where stat
|
||||
and st is the stat result if the file was found in the directory.
|
||||
'''
|
||||
|
||||
def fwarn(f, msg):
|
||||
self._ui.warn('%s: %s\n' % (self.pathto(f), msg))
|
||||
return False
|
||||
|
||||
def badtype(mode):
|
||||
kind = _('unknown')
|
||||
if stat.S_ISCHR(mode): kind = _('character device')
|
||||
elif stat.S_ISBLK(mode): kind = _('block device')
|
||||
elif stat.S_ISFIFO(mode): kind = _('fifo')
|
||||
elif stat.S_ISSOCK(mode): kind = _('socket')
|
||||
elif stat.S_ISDIR(mode): kind = _('directory')
|
||||
return _('unsupported file type (type is %s)') % kind
|
||||
|
||||
ignore = 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
|
||||
badfn = match.bad
|
||||
dmap = self._map
|
||||
normpath = util.normpath
|
||||
listdir = osutil.listdir
|
||||
lstat = os.lstat
|
||||
getkind = stat.S_IFMT
|
||||
dirkind = stat.S_IFDIR
|
||||
regkind = stat.S_IFREG
|
||||
lnkkind = stat.S_IFLNK
|
||||
join = self._join
|
||||
work = []
|
||||
wadd = work.append
|
||||
|
||||
if self._checkcase:
|
||||
normalize = self._normalize
|
||||
else:
|
||||
normalize = lambda x, y: x
|
||||
|
||||
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
|
||||
|
||||
files = set(match.files())
|
||||
if not files or '.' in files:
|
||||
files = ['']
|
||||
results = {'.hg': None}
|
||||
|
||||
# step 1: find all explicit files
|
||||
for ff in sorted(files):
|
||||
nf = normalize(normpath(ff), False)
|
||||
if nf in results:
|
||||
continue
|
||||
|
||||
try:
|
||||
st = lstat(join(nf))
|
||||
kind = getkind(st.st_mode)
|
||||
if kind == dirkind:
|
||||
skipstep3 = False
|
||||
if nf in dmap:
|
||||
#file deleted on disk but still in dirstate
|
||||
results[nf] = None
|
||||
match.dir(nf)
|
||||
if not dirignore(nf):
|
||||
wadd(nf)
|
||||
elif kind == regkind or kind == lnkkind:
|
||||
results[nf] = st
|
||||
else:
|
||||
badfn(ff, badtype(kind))
|
||||
if nf in dmap:
|
||||
results[nf] = None
|
||||
except OSError, inst:
|
||||
if nf in dmap: # does it exactly match a file?
|
||||
results[nf] = None
|
||||
else: # does it match a directory?
|
||||
prefix = nf + "/"
|
||||
for fn in dmap:
|
||||
if fn.startswith(prefix):
|
||||
match.dir(nf)
|
||||
skipstep3 = False
|
||||
break
|
||||
else:
|
||||
badfn(ff, inst.strerror)
|
||||
|
||||
# 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 == errno.EACCES:
|
||||
fwarn(nd, inst.strerror)
|
||||
continue
|
||||
raise
|
||||
for f, kind, st in entries:
|
||||
nf = normalize(nd and (nd + "/" + f) or f, True)
|
||||
if nf not in results:
|
||||
if kind == dirkind:
|
||||
if not ignore(nf):
|
||||
match.dir(nf)
|
||||
wadd(nf)
|
||||
if nf in dmap and matchfn(nf):
|
||||
results[nf] = None
|
||||
elif kind == regkind or kind == lnkkind:
|
||||
if nf in dmap:
|
||||
if matchfn(nf):
|
||||
results[nf] = st
|
||||
elif matchfn(nf) and not ignore(nf):
|
||||
results[nf] = st
|
||||
elif nf in dmap and matchfn(nf):
|
||||
results[nf] = None
|
||||
|
||||
# step 3: report unseen items in the dmap hash
|
||||
if not skipstep3 and not exact:
|
||||
visit = sorted([f for f in dmap if f not in results and matchfn(f)])
|
||||
for nf, st in zip(visit, util.statfiles([join(i) for i in visit])):
|
||||
if not st is None and not getkind(st.st_mode) in (regkind, lnkkind):
|
||||
st = None
|
||||
results[nf] = st
|
||||
|
||||
del results['.hg']
|
||||
return results
|
||||
|
||||
def status(self, match, ignored, clean, unknown):
|
||||
listignored, listclean, listunknown = ignored, clean, unknown
|
||||
lookup, modified, added, unknown, ignored = [], [], [], [], []
|
||||
removed, deleted, clean = [], [], []
|
||||
|
||||
dmap = self._map
|
||||
ladd = lookup.append
|
||||
madd = modified.append
|
||||
aadd = added.append
|
||||
uadd = unknown.append
|
||||
iadd = ignored.append
|
||||
radd = removed.append
|
||||
dadd = deleted.append
|
||||
cadd = clean.append
|
||||
|
||||
for fn, st in self.walk(match, listunknown, listignored).iteritems():
|
||||
if fn not in dmap:
|
||||
if (listignored or match.exact(fn)) and self._dirignore(fn):
|
||||
if listignored:
|
||||
iadd(fn)
|
||||
elif listunknown:
|
||||
uadd(fn)
|
||||
continue
|
||||
|
||||
state, mode, size, time = dmap[fn]
|
||||
|
||||
if not st and state in "nma":
|
||||
dadd(fn)
|
||||
elif state == 'n':
|
||||
if (size >= 0 and
|
||||
(size != st.st_size
|
||||
or ((mode ^ st.st_mode) & 0100 and self._checkexec))
|
||||
or size == -2
|
||||
or fn in self._copymap):
|
||||
madd(fn)
|
||||
elif time != int(st.st_mtime):
|
||||
ladd(fn)
|
||||
elif listclean:
|
||||
cadd(fn)
|
||||
elif state == 'm':
|
||||
madd(fn)
|
||||
elif state == 'a':
|
||||
aadd(fn)
|
||||
elif state == 'r':
|
||||
radd(fn)
|
||||
|
||||
return (lookup, modified, added, removed, deleted, unknown, ignored,
|
||||
clean)
|
Loading…
Add table
Add a link
Reference in a new issue