Mercurial > hg
view mercurial/dirstate.py @ 1104:98988cc3723a
Fix dirstate imports
author | mpm@selenic.com |
---|---|
date | Sat, 27 Aug 2005 16:42:03 -0700 |
parents | 221b5252864c |
children | 30ab5b8ee8ec |
line wrap: on
line source
""" 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. """ import struct, os from node import * from demandload import * demandload(globals(), "time bisect stat util re") 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 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:] def ignore(self, f): if not self.ignorefunc: bigpat = [] try: l = file(self.wjoin(".hgignore")) for pat in l: p = pat.rstrip() if p: try: re.compile(p) except: self.ui.warn("ignoring invalid ignore" + " regular expression '%s'\n" % p) else: bigpat.append(p) except IOError: pass if bigpat: s = "(?:%s)" % (")|(?:".join(bigpat)) r = re.compile(s) self.ignorefunc = r.search else: self.ignorefunc = util.never return self.ignorefunc(f) 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): 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: s = os.stat(os.path.join(self.root, f)) 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) 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) known = {'.hg': 1} def seen(fn): if fn in known: return True known[fn] = 1 def traverse(): for ff in util.unique(files): f = os.path.join(self.root, ff) try: st = os.stat(f) 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): for dir, subdirs, fl in os.walk(f): d = dir[len(self.root) + 1:] nd = util.normpath(d) if nd == '.': nd = '' if seen(nd): subdirs[:] = [] continue for sd in subdirs: ds = os.path.join(nd, sd +'/') if self.ignore(ds) or not match(ds): subdirs.remove(sd) subdirs.sort() fl.sort() for fn in fl: fn = util.pconvert(os.path.join(d, fn)) yield 'f', fn elif stat.S_ISREG(st.st_mode): yield 'f', ff 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' self.ui.warn('%s: unsupported file type (type is %s)\n' % ( util.pathto(self.getcwd(), ff), kind)) ks = dc.keys() ks.sort() for k in ks: yield 'm', k # yield only files that match: all in dirstate, others only if # not in .hgignore for src, fn in util.unique(traverse()): fn = util.normpath(fn) if seen(fn): continue if fn not in dc and self.ignore(fn): continue if match(fn): yield src, fn def changes(self, files=None, match=util.always): self.read() if not files: dc = self.map.copy() else: dc = self.filterfiles(files) lookup, modified, added, unknown = [], [], [], [] removed, deleted = [], [] for src, fn in self.walk(files, match, dc=dc): try: s = os.stat(os.path.join(self.root, fn)) except OSError: continue if not stat.S_ISREG(s.st_mode): continue c = dc.get(fn) if c: del dc[fn] if c[0] == 'm': modified.append(fn) elif c[0] == 'a': added.append(fn) elif c[0] == 'r': unknown.append(fn) elif c[2] != s.st_size or (c[1] ^ s.st_mode) & 0100: modified.append(fn) elif c[3] != s.st_mtime: lookup.append(fn) else: unknown.append(fn) for fn, c in [(fn, c) for fn, c in dc.items() if match(fn)]: if c[0] == 'r': removed.append(fn) else: deleted.append(fn) return (lookup, modified, added, removed + deleted, unknown)