# HG changeset patch # User Matt Mackall # Date 1159844594 18000 # Node ID 6d98149d70fe59ee2183bbb227c211ed4b4c6f87 # Parent d865390c1781ea61a599eebe27804503b138efe6 contexts: add working dir and working file contexts add workingctx add workingfilectx extend filectx.annotate for rev=None extend filectx.ancestor diff -r d865390c1781 -r 6d98149d70fe mercurial/context.py --- a/mercurial/context.py Mon Oct 02 15:28:44 2006 -0500 +++ b/mercurial/context.py Mon Oct 02 22:03:14 2006 -0500 @@ -226,7 +226,10 @@ def parents(f): # we want to reuse filectx objects as much as possible p = f._path - pl = [ (p, f._filelog.rev(n)) for n in f._filelog.parents(f._filenode) ] + if f._filerev is None: # working dir + pl = [ (n.path(), n.filerev()) for n in f.parents() ] + else: + pl = [ (p, n) for n in f._filelog.parentrevs(f._filerev) ] if follow: r = f.renamed() @@ -234,7 +237,7 @@ pl[0] = (r[0], getlog(r[0]).rev(r[1])) return [ getctx(p, n) for p, n in pl if n != -1 ] - + # find all ancestors needed = {self: 1} visit = [self] @@ -279,6 +282,13 @@ """ acache = {} + + # prime the ancestor cache for the working directory + for c in (self, fc2): + if c._filerev == None: + pl = [ (n.path(), n.filenode()) for n in c.parents() ] + acache[(c._path, None)] = pl + flcache = {self._path:self._filelog, fc2._path:fc2._filelog} def parents(vertex): if vertex in acache: @@ -301,3 +311,149 @@ return filectx(self._repo, f, fileid=n, filelog=flcache[f]) return None + +class workingctx(changectx): + """A workingctx object makes access to data related to + the current working directory convenient.""" + def __init__(self, repo): + self._repo = repo + self._rev = None + self._node = None + + def __str__(self): + return "." + + def __nonzero__(self): + return True + + def __getattr__(self, name): + if name == '_parents': + self._parents = self._repo.parents() + return self._parents + if name == '_status': + self._status = self._repo.status() + return self._status + if name == '_manifest': + self._buildmanifest() + return self._manifest + else: + raise AttributeError, name + + def _buildmanifest(self): + """generate a manifest corresponding to the working directory""" + + man = self._parents[0].manifest().coy() + copied = self._repo.dirstate.copies() + modified, added, removed, deleted, unknown = self._status[:5] + for i,l in (("a", added), ("m", modified), ("u", unknown)): + for f in l: + man[f] = man.get(copied.get(f, f), nullid) + i + man.set(f, util.is_exec(self._repo.wjoin(f), man.execf(f))) + + for f in deleted + removed: + del man[f] + + self._manifest = man + + def manifest(self): return self._manifest + + def user(self): return self._repo.ui.username() + def date(self): return util.makedate() + def description(self): return "" + def files(self): + f = self.modified() + self.added() + self.removed() + f.sort() + return f + + def modified(self): return self._status[0] + def added(self): return self._status[1] + def removed(self): return self._status[2] + def deleted(self): return self._status[3] + def unknown(self): return self._status[4] + def clean(self): return self._status[5] + + def parents(self): + """return contexts for each parent changeset""" + return self._parents + + def children(self): + return [] + + def filectx(self, path): + """get a file context from the working directory""" + return workingfilectx(self._repo, path, workingctx=self) + + def ancestor(self, c2): + """return the ancestor context of self and c2""" + return self._parents[0].ancestor(c2) # punt on two parents for now + +class workingfilectx(filectx): + """A workingfilectx object makes access to data related to a particular + file in the working directory convenient.""" + def __init__(self, repo, path, filelog=None, workingctx=None): + """changeid can be a changeset revision, node, or tag. + fileid can be a file revision or node.""" + self._repo = repo + self._path = path + self._changeid = None + self._filerev = self._filenode = None + + if filelog: + self._filelog = filelog + if workingctx: + self._changectx = workingctx + + def __getattr__(self, name): + if name == '_changectx': + self._changectx = workingctx(repo) + return self._changectx + elif name == '_repopath': + self._repopath = self._repo.dirstate.copied(p) or self._path + elif name == '_filelog': + self._filelog = self._repo.file(self._repopath) + return self._filelog + else: + raise AttributeError, name + + def __nonzero__(self): + return True + + def __str__(self): + return "%s@." % self.path() + + def filectx(self, fileid): + '''opens an arbitrary revision of the file without + opening a new filelog''' + return filectx(self._repo, self._repopath, fileid=fileid, + filelog=self._filelog) + + def rev(self): + if hasattr(self, "_changectx"): + return self._changectx.rev() + return self._filelog.linkrev(self._filenode) + + def data(self): return self._repo.wread(self._path) + def renamed(self): + rp = self._repopath + if rp == self._path: + return None + return rp, self._workingctx._parents._manifest.get(rp, nullid) + + def parents(self): + '''return parent filectxs, following copies if necessary''' + p = self._path + rp = self._repopath + pcl = self._workingctx._parents + fl = self._filelog + pl = [ (rp, pcl[0]._manifest.get(rp, nullid), fl) ] + if len(pcl) > 1: + if rp != p: + fl = None + pl.append((p, pcl[1]._manifest.get(p, nullid), fl)) + + return [ filectx(self._repo, p, fileid=n, filelog=l) + for p,n,l in pl if n != nullid ] + + def children(self): + return [] +