mercurial/context.py
changeset 3241 a184cd0c2db9
parent 3229 f7a8228fde17
parent 3240 8d4855fd9d7b
child 3242 1539f788e913
equal deleted inserted replaced
3232:e0069e7fe419 3241:a184cd0c2db9
    31 
    31 
    32     def __str__(self):
    32     def __str__(self):
    33         return short(self.node())
    33         return short(self.node())
    34 
    34 
    35     def __repr__(self):
    35     def __repr__(self):
    36         return "<changectx %s>" % short(self.node())
    36         return "<changectx %s>" % str(self)
    37 
    37 
    38     def __eq__(self, other):
    38     def __eq__(self, other):
    39         return self._rev == other._rev
    39         return self._rev == other._rev
    40 
    40 
    41     def __nonzero__(self):
    41     def __nonzero__(self):
    42         return self._rev != -1
    42         return self._rev != -1
    43 
    43 
    44     def changeset(self):
    44     def __getattr__(self, name):
    45         try:
    45         if name == '_changeset':
    46             return self._changeset
       
    47         except AttributeError:
       
    48             self._changeset = self._repo.changelog.read(self.node())
    46             self._changeset = self._repo.changelog.read(self.node())
    49             return self._changeset
    47             return self._changeset
    50 
    48         elif name == '_manifest':
    51     def manifest(self):
    49             self._manifest = self._repo.manifest.read(self._changeset[0])
    52         try:
       
    53             return self._manifest
    50             return self._manifest
    54         except AttributeError:
    51         else:
    55             self._manifest = self._repo.manifest.read(self.changeset()[0])
    52             raise AttributeError, name
    56             return self._manifest
    53 
       
    54     def changeset(self): return self._changeset
       
    55     def manifest(self): return self._manifest
    57 
    56 
    58     def rev(self): return self._rev
    57     def rev(self): return self._rev
    59     def node(self): return self._node
    58     def node(self): return self._node
    60     def user(self): return self.changeset()[1]
    59     def user(self): return self._changeset[1]
    61     def date(self): return self.changeset()[2]
    60     def date(self): return self._changeset[2]
    62     def files(self): return self.changeset()[3]
    61     def files(self): return self._changeset[3]
    63     def description(self): return self.changeset()[4]
    62     def description(self): return self._changeset[4]
    64 
    63 
    65     def parents(self):
    64     def parents(self):
    66         """return contexts for each parent changeset"""
    65         """return contexts for each parent changeset"""
    67         p = self._repo.changelog.parents(self._node)
    66         p = self._repo.changelog.parents(self._node)
    68         return [ changectx(self._repo, x) for x in p ]
    67         return [ changectx(self._repo, x) for x in p ]
    71         """return contexts for each child changeset"""
    70         """return contexts for each child changeset"""
    72         c = self._repo.changelog.children(self._node)
    71         c = self._repo.changelog.children(self._node)
    73         return [ changectx(self._repo, x) for x in c ]
    72         return [ changectx(self._repo, x) for x in c ]
    74 
    73 
    75     def filenode(self, path):
    74     def filenode(self, path):
    76         node, flag = self._repo.manifest.find(self.changeset()[0], path)
    75         if hasattr(self, "_manifest"):
       
    76             return self._manifest[path]
       
    77         node, flag = self._repo.manifest.find(self._changeset[0], path)
    77         return node
    78         return node
    78 
    79 
    79     def filectx(self, path, fileid=None):
    80     def filectx(self, path, fileid=None):
    80         """get a file context from this changeset"""
    81         """get a file context from this changeset"""
    81         if fileid is None:
    82         if fileid is None:
    82             fileid = self.filenode(path)
    83             fileid = self.filenode(path)
    83             if not fileid:
    84         return filectx(self._repo, path, fileid=fileid, changectx=self)
    84                 raise repo.LookupError(_("'%s' does not exist in changeset %s") %
       
    85                                        (path, hex(self.node())))
       
    86         return filectx(self._repo, path, fileid=fileid)
       
    87 
    85 
    88     def filectxs(self):
    86     def filectxs(self):
    89         """generate a file context for each file in this changeset's
    87         """generate a file context for each file in this changeset's
    90            manifest"""
    88            manifest"""
    91         mf = self.manifest()
    89         mf = self.manifest()
   102         return changectx(self._repo, n)
   100         return changectx(self._repo, n)
   103 
   101 
   104 class filectx(object):
   102 class filectx(object):
   105     """A filecontext object makes access to data related to a particular
   103     """A filecontext object makes access to data related to a particular
   106        filerevision convenient."""
   104        filerevision convenient."""
   107     def __init__(self, repo_, path, changeid=None, fileid=None, filelog=None):
   105     def __init__(self, repo, path, changeid=None, fileid=None,
       
   106                  filelog=None, changectx=None):
   108         """changeid can be a changeset revision, node, or tag.
   107         """changeid can be a changeset revision, node, or tag.
   109            fileid can be a file revision or node."""
   108            fileid can be a file revision or node."""
   110         self._repo = repo_
   109         self._repo = repo
   111         self._path = path
   110         self._path = path
   112 
   111 
   113         assert changeid is not None or fileid is not None
   112         assert changeid is not None or fileid is not None
   114 
   113 
   115         if filelog:
   114         if filelog:
   116             self._filelog = filelog
   115             self._filelog = filelog
   117         else:
   116         if changectx:
   118             self._filelog = self._repo.file(self._path)
   117             self._changectx = changectx
       
   118             self._changeid = changectx.node()
   119 
   119 
   120         if fileid is None:
   120         if fileid is None:
   121             self._changeid = changeid
   121             self._changeid = changeid
   122         else:
   122         else:
   123             try:
   123             self._fileid = fileid
   124                 self._filenode = self._filelog.lookup(fileid)
       
   125             except revlog.RevlogError, inst:
       
   126                 raise repo.LookupError(str(inst))
       
   127             self._changeid = self._filelog.linkrev(self._filenode)
       
   128 
   124 
   129     def __getattr__(self, name):
   125     def __getattr__(self, name):
   130         if name == '_changectx':
   126         if name == '_changectx':
   131             self._changectx = changectx(self._repo, self._changeid)
   127             self._changectx = changectx(self._repo, self._changeid)
   132             return self._changectx
   128             return self._changectx
       
   129         elif name == '_filelog':
       
   130             self._filelog = self._repo.file(self._path)
       
   131             return self._filelog
       
   132         elif name == '_changeid':
       
   133             self._changeid = self._filelog.linkrev(self._filenode)
       
   134             return self._changeid
   133         elif name == '_filenode':
   135         elif name == '_filenode':
   134             self._filenode = self._changectx.filenode(self._path)
   136             try:
       
   137                 if hasattr(self, "_fileid"):
       
   138                     self._filenode = self._filelog.lookup(self._fileid)
       
   139                 else:
       
   140                     self._filenode = self._changectx.filenode(self._path)
       
   141             except revlog.RevlogError, inst:
       
   142                 raise repo.LookupError(str(inst))
   135             return self._filenode
   143             return self._filenode
   136         elif name == '_filerev':
   144         elif name == '_filerev':
   137             self._filerev = self._filelog.rev(self._filenode)
   145             self._filerev = self._filelog.rev(self._filenode)
   138             return self._filerev
   146             return self._filerev
   139         else:
   147         else:
   144 
   152 
   145     def __str__(self):
   153     def __str__(self):
   146         return "%s@%s" % (self.path(), short(self.node()))
   154         return "%s@%s" % (self.path(), short(self.node()))
   147 
   155 
   148     def __repr__(self):
   156     def __repr__(self):
   149         return "<filectx %s@%s>" % (self.path(), short(self.node()))
   157         return "<filectx %s>" % str(self)
   150 
   158 
   151     def __eq__(self, other):
   159     def __eq__(self, other):
   152         return self._path == other._path and self._changeid == other._changeid
   160         return self._path == other._path and self._changeid == other._changeid
   153 
   161 
   154     def filectx(self, fileid):
   162     def filectx(self, fileid):
   216         getctx = util.cachefunc(getctx)
   224         getctx = util.cachefunc(getctx)
   217 
   225 
   218         def parents(f):
   226         def parents(f):
   219             # we want to reuse filectx objects as much as possible
   227             # we want to reuse filectx objects as much as possible
   220             p = f._path
   228             p = f._path
   221             pl = [ (p, r) for r in f._filelog.parentrevs(f._filerev) ]
   229             if f._filerev is None: # working dir
       
   230                 pl = [ (n.path(), n.filerev()) for n in f.parents() ]
       
   231             else:
       
   232                 pl = [ (p, n) for n in f._filelog.parentrevs(f._filerev) ]
   222 
   233 
   223             if follow:
   234             if follow:
   224                 r = f.renamed()
   235                 r = f.renamed()
   225                 if r:
   236                 if r:
   226                     pl[0] = (r[0], getlog(r[0]).rev(r[1]))
   237                     pl[0] = (r[0], getlog(r[0]).rev(r[1]))
   269         """
   280         """
   270         find the common ancestor file context, if any, of self, and fc2
   281         find the common ancestor file context, if any, of self, and fc2
   271         """
   282         """
   272 
   283 
   273         acache = {}
   284         acache = {}
       
   285 
       
   286         # prime the ancestor cache for the working directory
       
   287         for c in (self, fc2):
       
   288             if c._filerev == None:
       
   289                 pl = [ (n.path(), n.filenode()) for n in c.parents() ]
       
   290                 acache[(c._path, None)] = pl
       
   291 
   274         flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
   292         flcache = {self._path:self._filelog, fc2._path:fc2._filelog}
   275         def parents(vertex):
   293         def parents(vertex):
   276             if vertex in acache:
   294             if vertex in acache:
   277                 return acache[vertex]
   295                 return acache[vertex]
   278             f, n = vertex
   296             f, n = vertex
   291         if v:
   309         if v:
   292             f,n = v
   310             f,n = v
   293             return filectx(self._repo, f, fileid=n, filelog=flcache[f])
   311             return filectx(self._repo, f, fileid=n, filelog=flcache[f])
   294 
   312 
   295         return None
   313         return None
       
   314 
       
   315 class workingctx(changectx):
       
   316     """A workingctx object makes access to data related to
       
   317     the current working directory convenient."""
       
   318     def __init__(self, repo):
       
   319         self._repo = repo
       
   320         self._rev = None
       
   321         self._node = None
       
   322 
       
   323     def __str__(self):
       
   324         return "."
       
   325 
       
   326     def __nonzero__(self):
       
   327         return True
       
   328 
       
   329     def __getattr__(self, name):
       
   330         if name == '_parents':
       
   331             self._parents = self._repo.parents()
       
   332             return self._parents
       
   333         if name == '_status':
       
   334             self._status = self._repo.status()
       
   335             return self._status
       
   336         if name == '_manifest':
       
   337             self._buildmanifest()
       
   338             return self._manifest
       
   339         else:
       
   340             raise AttributeError, name
       
   341 
       
   342     def _buildmanifest(self):
       
   343         """generate a manifest corresponding to the working directory"""
       
   344 
       
   345         man = self._parents[0].manifest().copy()
       
   346         copied = self._repo.dirstate.copies()
       
   347         modified, added, removed, deleted, unknown = self._status[:5]
       
   348         for i,l in (("a", added), ("m", modified), ("u", unknown)):
       
   349             for f in l:
       
   350                 man[f] = man.get(copied.get(f, f), nullid) + i
       
   351                 man.set(f, util.is_exec(self._repo.wjoin(f), man.execf(f)))
       
   352 
       
   353         for f in deleted + removed:
       
   354             del man[f]
       
   355 
       
   356         self._manifest = man
       
   357 
       
   358     def manifest(self): return self._manifest
       
   359 
       
   360     def user(self): return self._repo.ui.username()
       
   361     def date(self): return util.makedate()
       
   362     def description(self): return ""
       
   363     def files(self):
       
   364         f = self.modified() + self.added() + self.removed()
       
   365         f.sort()
       
   366         return f
       
   367 
       
   368     def modified(self): return self._status[0]
       
   369     def added(self): return self._status[1]
       
   370     def removed(self): return self._status[2]
       
   371     def deleted(self): return self._status[3]
       
   372     def unknown(self): return self._status[4]
       
   373     def clean(self): return self._status[5]
       
   374 
       
   375     def parents(self):
       
   376         """return contexts for each parent changeset"""
       
   377         return self._parents
       
   378 
       
   379     def children(self):
       
   380         return []
       
   381 
       
   382     def filectx(self, path):
       
   383         """get a file context from the working directory"""
       
   384         return workingfilectx(self._repo, path, workingctx=self)
       
   385 
       
   386     def ancestor(self, c2):
       
   387         """return the ancestor context of self and c2"""
       
   388         return self._parents[0].ancestor(c2) # punt on two parents for now
       
   389 
       
   390 class workingfilectx(filectx):
       
   391     """A workingfilectx object makes access to data related to a particular
       
   392        file in the working directory convenient."""
       
   393     def __init__(self, repo, path, filelog=None, workingctx=None):
       
   394         """changeid can be a changeset revision, node, or tag.
       
   395            fileid can be a file revision or node."""
       
   396         self._repo = repo
       
   397         self._path = path
       
   398         self._changeid = None
       
   399         self._filerev = self._filenode = None
       
   400 
       
   401         if filelog:
       
   402             self._filelog = filelog
       
   403         if workingctx:
       
   404             self._changectx = workingctx
       
   405 
       
   406     def __getattr__(self, name):
       
   407         if name == '_changectx':
       
   408             self._changectx = workingctx(repo)
       
   409             return self._changectx
       
   410         elif name == '_repopath':
       
   411             self._repopath = self._repo.dirstate.copied(p) or self._path
       
   412         elif name == '_filelog':
       
   413             self._filelog = self._repo.file(self._repopath)
       
   414             return self._filelog
       
   415         else:
       
   416             raise AttributeError, name
       
   417 
       
   418     def __nonzero__(self):
       
   419         return True
       
   420 
       
   421     def __str__(self):
       
   422         return "%s@." % self.path()
       
   423 
       
   424     def filectx(self, fileid):
       
   425         '''opens an arbitrary revision of the file without
       
   426         opening a new filelog'''
       
   427         return filectx(self._repo, self._repopath, fileid=fileid,
       
   428                        filelog=self._filelog)
       
   429 
       
   430     def rev(self):
       
   431         if hasattr(self, "_changectx"):
       
   432             return self._changectx.rev()
       
   433         return self._filelog.linkrev(self._filenode)
       
   434 
       
   435     def data(self): return self._repo.wread(self._path)
       
   436     def renamed(self):
       
   437         rp = self._repopath
       
   438         if rp == self._path:
       
   439             return None
       
   440         return rp, self._workingctx._parents._manifest.get(rp, nullid)
       
   441 
       
   442     def parents(self):
       
   443         '''return parent filectxs, following copies if necessary'''
       
   444         p = self._path
       
   445         rp = self._repopath
       
   446         pcl = self._workingctx._parents
       
   447         fl = self._filelog
       
   448         pl = [ (rp, pcl[0]._manifest.get(rp, nullid), fl) ]
       
   449         if len(pcl) > 1:
       
   450             if rp != p:
       
   451                 fl = None
       
   452             pl.append((p, pcl[1]._manifest.get(p, nullid), fl))
       
   453 
       
   454         return [ filectx(self._repo, p, fileid=n, filelog=l)
       
   455                  for p,n,l in pl if n != nullid ]
       
   456 
       
   457     def children(self):
       
   458         return []
       
   459