contexts: add working dir and working file contexts
authorMatt Mackall <mpm@selenic.com>
Mon, 02 Oct 2006 22:03:14 -0500
changeset 3217 6d98149d70fe
parent 3216 d865390c1781
child 3218 8d4855fd9d7b
contexts: add working dir and working file contexts add workingctx add workingfilectx extend filectx.annotate for rev=None extend filectx.ancestor
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 []
+