mercurial/context.py
changeset 42318 a13b30555ffb
parent 42317 e79aeb518aa1
child 42319 491855ea9d62
equal deleted inserted replaced
42317:e79aeb518aa1 42318:a13b30555ffb
   270         try:
   270         try:
   271             return self._fileinfo(path)[1]
   271             return self._fileinfo(path)[1]
   272         except error.LookupError:
   272         except error.LookupError:
   273             return ''
   273             return ''
   274 
   274 
   275     def sub(self, path, allowcreate=True):
       
   276         '''return a subrepo for the stored revision of path, never wdir()'''
       
   277         return subrepo.subrepo(self, path, allowcreate=allowcreate)
       
   278 
       
   279     def nullsub(self, path, pctx):
       
   280         return subrepo.nullsubrepo(self, path, pctx)
       
   281 
       
   282     def workingsub(self, path):
       
   283         '''return a subrepo for the stored revision, or wdir if this is a wdir
       
   284         context.
       
   285         '''
       
   286         return subrepo.subrepo(self, path, allowwdir=True)
       
   287 
       
   288     def match(self, pats=None, include=None, exclude=None, default='glob',
       
   289               listsubrepos=False, badfn=None):
       
   290         r = self._repo
       
   291         return matchmod.match(r.root, r.getcwd(), pats,
       
   292                               include, exclude, default,
       
   293                               auditor=r.nofsauditor, ctx=self,
       
   294                               listsubrepos=listsubrepos, badfn=badfn)
       
   295 
       
   296     def diff(self, ctx2=None, match=None, changes=None, opts=None,
       
   297              losedatafn=None, pathfn=None, copy=None,
       
   298              copysourcematch=None, hunksfilterfn=None):
       
   299         """Returns a diff generator for the given contexts and matcher"""
       
   300         if ctx2 is None:
       
   301             ctx2 = self.p1()
       
   302         if ctx2 is not None:
       
   303             ctx2 = self._repo[ctx2]
       
   304         return patch.diff(self._repo, ctx2, self, match=match, changes=changes,
       
   305                           opts=opts, losedatafn=losedatafn, pathfn=pathfn,
       
   306                           copy=copy, copysourcematch=copysourcematch,
       
   307                           hunksfilterfn=hunksfilterfn)
       
   308 
       
   309     def dirs(self):
       
   310         return self._manifest.dirs()
       
   311 
       
   312     def hasdir(self, dir):
       
   313         return self._manifest.hasdir(dir)
       
   314 
       
   315     def status(self, other=None, match=None, listignored=False,
       
   316                listclean=False, listunknown=False, listsubrepos=False):
       
   317         """return status of files between two nodes or node and working
       
   318         directory.
       
   319 
       
   320         If other is None, compare this node with working directory.
       
   321 
       
   322         returns (modified, added, removed, deleted, unknown, ignored, clean)
       
   323         """
       
   324 
       
   325         ctx1 = self
       
   326         ctx2 = self._repo[other]
       
   327 
       
   328         # This next code block is, admittedly, fragile logic that tests for
       
   329         # reversing the contexts and wouldn't need to exist if it weren't for
       
   330         # the fast (and common) code path of comparing the working directory
       
   331         # with its first parent.
       
   332         #
       
   333         # What we're aiming for here is the ability to call:
       
   334         #
       
   335         # workingctx.status(parentctx)
       
   336         #
       
   337         # If we always built the manifest for each context and compared those,
       
   338         # then we'd be done. But the special case of the above call means we
       
   339         # just copy the manifest of the parent.
       
   340         reversed = False
       
   341         if (not isinstance(ctx1, changectx)
       
   342             and isinstance(ctx2, changectx)):
       
   343             reversed = True
       
   344             ctx1, ctx2 = ctx2, ctx1
       
   345 
       
   346         match = self._repo.narrowmatch(match)
       
   347         match = ctx2._matchstatus(ctx1, match)
       
   348         r = scmutil.status([], [], [], [], [], [], [])
       
   349         r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
       
   350                               listunknown)
       
   351 
       
   352         if reversed:
       
   353             # Reverse added and removed. Clear deleted, unknown and ignored as
       
   354             # these make no sense to reverse.
       
   355             r = scmutil.status(r.modified, r.removed, r.added, [], [], [],
       
   356                                r.clean)
       
   357 
       
   358         if listsubrepos:
       
   359             for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
       
   360                 try:
       
   361                     rev2 = ctx2.subrev(subpath)
       
   362                 except KeyError:
       
   363                     # A subrepo that existed in node1 was deleted between
       
   364                     # node1 and node2 (inclusive). Thus, ctx2's substate
       
   365                     # won't contain that subpath. The best we can do ignore it.
       
   366                     rev2 = None
       
   367                 submatch = matchmod.subdirmatcher(subpath, match)
       
   368                 s = sub.status(rev2, match=submatch, ignored=listignored,
       
   369                                clean=listclean, unknown=listunknown,
       
   370                                listsubrepos=True)
       
   371                 for rfiles, sfiles in zip(r, s):
       
   372                     rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
       
   373 
       
   374         for l in r:
       
   375             l.sort()
       
   376 
       
   377         return r
       
   378 
       
   379 class changectx(basectx):
       
   380     """A changecontext object makes access to data related to a particular
       
   381     changeset convenient. It represents a read-only context already present in
       
   382     the repo."""
       
   383     def __init__(self, repo, rev, node):
       
   384         super(changectx, self).__init__(repo)
       
   385         self._rev = rev
       
   386         self._node = node
       
   387 
       
   388     def __hash__(self):
       
   389         try:
       
   390             return hash(self._rev)
       
   391         except AttributeError:
       
   392             return id(self)
       
   393 
       
   394     def __nonzero__(self):
       
   395         return self._rev != nullrev
       
   396 
       
   397     __bool__ = __nonzero__
       
   398 
       
   399     @propertycache
       
   400     def _changeset(self):
       
   401         return self._repo.changelog.changelogrevision(self.rev())
       
   402 
       
   403     @propertycache
       
   404     def _manifest(self):
       
   405         return self._manifestctx.read()
       
   406 
       
   407     @property
       
   408     def _manifestctx(self):
       
   409         return self._repo.manifestlog[self._changeset.manifest]
       
   410 
       
   411     @propertycache
       
   412     def _manifestdelta(self):
       
   413         return self._manifestctx.readdelta()
       
   414 
       
   415     @propertycache
       
   416     def _parents(self):
       
   417         repo = self._repo
       
   418         p1, p2 = repo.changelog.parentrevs(self._rev)
       
   419         if p2 == nullrev:
       
   420             return [repo[p1]]
       
   421         return [repo[p1], repo[p2]]
       
   422 
       
   423     def changeset(self):
       
   424         c = self._changeset
       
   425         return (
       
   426             c.manifest,
       
   427             c.user,
       
   428             c.date,
       
   429             c.files,
       
   430             c.description,
       
   431             c.extra,
       
   432         )
       
   433     def manifestnode(self):
       
   434         return self._changeset.manifest
       
   435 
       
   436     def user(self):
       
   437         return self._changeset.user
       
   438     def date(self):
       
   439         return self._changeset.date
       
   440     def files(self):
       
   441         return self._changeset.files
       
   442     @propertycache
   275     @propertycache
   443     def _copies(self):
   276     def _copies(self):
   444         source = self._repo.ui.config('experimental', 'copies.read-from')
       
   445         p1copies = self._changeset.p1copies
       
   446         p2copies = self._changeset.p2copies
       
   447         # If config says to get copy metadata only from changeset, then return
       
   448         # that, defaulting to {} if there was no copy metadata.
       
   449         # In compatibility mode, we return copy data from the changeset if
       
   450         # it was recorded there, and otherwise we fall back to getting it from
       
   451         # the filelogs (below).
       
   452         if (source == 'changeset-only' or
       
   453             (source == 'compatibility' and p1copies is not None)):
       
   454             return p1copies or {}, p2copies or {}
       
   455 
       
   456         # Otherwise (config said to read only from filelog, or we are in
       
   457         # compatiblity mode and there is not data in the changeset), we get
       
   458         # the copy metadata from the filelogs.
       
   459         p1copies = {}
   277         p1copies = {}
   460         p2copies = {}
   278         p2copies = {}
   461         p1 = self.p1()
   279         p1 = self.p1()
   462         p2 = self.p2()
   280         p2 = self.p2()
   463         narrowmatch = self._repo.narrowmatch()
   281         narrowmatch = self._repo.narrowmatch()
   475         return p1copies, p2copies
   293         return p1copies, p2copies
   476     def p1copies(self):
   294     def p1copies(self):
   477         return self._copies[0]
   295         return self._copies[0]
   478     def p2copies(self):
   296     def p2copies(self):
   479         return self._copies[1]
   297         return self._copies[1]
       
   298 
       
   299     def sub(self, path, allowcreate=True):
       
   300         '''return a subrepo for the stored revision of path, never wdir()'''
       
   301         return subrepo.subrepo(self, path, allowcreate=allowcreate)
       
   302 
       
   303     def nullsub(self, path, pctx):
       
   304         return subrepo.nullsubrepo(self, path, pctx)
       
   305 
       
   306     def workingsub(self, path):
       
   307         '''return a subrepo for the stored revision, or wdir if this is a wdir
       
   308         context.
       
   309         '''
       
   310         return subrepo.subrepo(self, path, allowwdir=True)
       
   311 
       
   312     def match(self, pats=None, include=None, exclude=None, default='glob',
       
   313               listsubrepos=False, badfn=None):
       
   314         r = self._repo
       
   315         return matchmod.match(r.root, r.getcwd(), pats,
       
   316                               include, exclude, default,
       
   317                               auditor=r.nofsauditor, ctx=self,
       
   318                               listsubrepos=listsubrepos, badfn=badfn)
       
   319 
       
   320     def diff(self, ctx2=None, match=None, changes=None, opts=None,
       
   321              losedatafn=None, pathfn=None, copy=None,
       
   322              copysourcematch=None, hunksfilterfn=None):
       
   323         """Returns a diff generator for the given contexts and matcher"""
       
   324         if ctx2 is None:
       
   325             ctx2 = self.p1()
       
   326         if ctx2 is not None:
       
   327             ctx2 = self._repo[ctx2]
       
   328         return patch.diff(self._repo, ctx2, self, match=match, changes=changes,
       
   329                           opts=opts, losedatafn=losedatafn, pathfn=pathfn,
       
   330                           copy=copy, copysourcematch=copysourcematch,
       
   331                           hunksfilterfn=hunksfilterfn)
       
   332 
       
   333     def dirs(self):
       
   334         return self._manifest.dirs()
       
   335 
       
   336     def hasdir(self, dir):
       
   337         return self._manifest.hasdir(dir)
       
   338 
       
   339     def status(self, other=None, match=None, listignored=False,
       
   340                listclean=False, listunknown=False, listsubrepos=False):
       
   341         """return status of files between two nodes or node and working
       
   342         directory.
       
   343 
       
   344         If other is None, compare this node with working directory.
       
   345 
       
   346         returns (modified, added, removed, deleted, unknown, ignored, clean)
       
   347         """
       
   348 
       
   349         ctx1 = self
       
   350         ctx2 = self._repo[other]
       
   351 
       
   352         # This next code block is, admittedly, fragile logic that tests for
       
   353         # reversing the contexts and wouldn't need to exist if it weren't for
       
   354         # the fast (and common) code path of comparing the working directory
       
   355         # with its first parent.
       
   356         #
       
   357         # What we're aiming for here is the ability to call:
       
   358         #
       
   359         # workingctx.status(parentctx)
       
   360         #
       
   361         # If we always built the manifest for each context and compared those,
       
   362         # then we'd be done. But the special case of the above call means we
       
   363         # just copy the manifest of the parent.
       
   364         reversed = False
       
   365         if (not isinstance(ctx1, changectx)
       
   366             and isinstance(ctx2, changectx)):
       
   367             reversed = True
       
   368             ctx1, ctx2 = ctx2, ctx1
       
   369 
       
   370         match = self._repo.narrowmatch(match)
       
   371         match = ctx2._matchstatus(ctx1, match)
       
   372         r = scmutil.status([], [], [], [], [], [], [])
       
   373         r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
       
   374                               listunknown)
       
   375 
       
   376         if reversed:
       
   377             # Reverse added and removed. Clear deleted, unknown and ignored as
       
   378             # these make no sense to reverse.
       
   379             r = scmutil.status(r.modified, r.removed, r.added, [], [], [],
       
   380                                r.clean)
       
   381 
       
   382         if listsubrepos:
       
   383             for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
       
   384                 try:
       
   385                     rev2 = ctx2.subrev(subpath)
       
   386                 except KeyError:
       
   387                     # A subrepo that existed in node1 was deleted between
       
   388                     # node1 and node2 (inclusive). Thus, ctx2's substate
       
   389                     # won't contain that subpath. The best we can do ignore it.
       
   390                     rev2 = None
       
   391                 submatch = matchmod.subdirmatcher(subpath, match)
       
   392                 s = sub.status(rev2, match=submatch, ignored=listignored,
       
   393                                clean=listclean, unknown=listunknown,
       
   394                                listsubrepos=True)
       
   395                 for rfiles, sfiles in zip(r, s):
       
   396                     rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
       
   397 
       
   398         for l in r:
       
   399             l.sort()
       
   400 
       
   401         return r
       
   402 
       
   403 class changectx(basectx):
       
   404     """A changecontext object makes access to data related to a particular
       
   405     changeset convenient. It represents a read-only context already present in
       
   406     the repo."""
       
   407     def __init__(self, repo, rev, node):
       
   408         super(changectx, self).__init__(repo)
       
   409         self._rev = rev
       
   410         self._node = node
       
   411 
       
   412     def __hash__(self):
       
   413         try:
       
   414             return hash(self._rev)
       
   415         except AttributeError:
       
   416             return id(self)
       
   417 
       
   418     def __nonzero__(self):
       
   419         return self._rev != nullrev
       
   420 
       
   421     __bool__ = __nonzero__
       
   422 
       
   423     @propertycache
       
   424     def _changeset(self):
       
   425         return self._repo.changelog.changelogrevision(self.rev())
       
   426 
       
   427     @propertycache
       
   428     def _manifest(self):
       
   429         return self._manifestctx.read()
       
   430 
       
   431     @property
       
   432     def _manifestctx(self):
       
   433         return self._repo.manifestlog[self._changeset.manifest]
       
   434 
       
   435     @propertycache
       
   436     def _manifestdelta(self):
       
   437         return self._manifestctx.readdelta()
       
   438 
       
   439     @propertycache
       
   440     def _parents(self):
       
   441         repo = self._repo
       
   442         p1, p2 = repo.changelog.parentrevs(self._rev)
       
   443         if p2 == nullrev:
       
   444             return [repo[p1]]
       
   445         return [repo[p1], repo[p2]]
       
   446 
       
   447     def changeset(self):
       
   448         c = self._changeset
       
   449         return (
       
   450             c.manifest,
       
   451             c.user,
       
   452             c.date,
       
   453             c.files,
       
   454             c.description,
       
   455             c.extra,
       
   456         )
       
   457     def manifestnode(self):
       
   458         return self._changeset.manifest
       
   459 
       
   460     def user(self):
       
   461         return self._changeset.user
       
   462     def date(self):
       
   463         return self._changeset.date
       
   464     def files(self):
       
   465         return self._changeset.files
       
   466     @propertycache
       
   467     def _copies(self):
       
   468         source = self._repo.ui.config('experimental', 'copies.read-from')
       
   469         p1copies = self._changeset.p1copies
       
   470         p2copies = self._changeset.p2copies
       
   471         # If config says to get copy metadata only from changeset, then return
       
   472         # that, defaulting to {} if there was no copy metadata.
       
   473         # In compatibility mode, we return copy data from the changeset if
       
   474         # it was recorded there, and otherwise we fall back to getting it from
       
   475         # the filelogs (below).
       
   476         if (source == 'changeset-only' or
       
   477             (source == 'compatibility' and p1copies is not None)):
       
   478             return p1copies or {}, p2copies or {}
       
   479 
       
   480         # Otherwise (config said to read only from filelog, or we are in
       
   481         # compatiblity mode and there is not data in the changeset), we get
       
   482         # the copy metadata from the filelogs.
       
   483         return super(changectx, self)._copies
   480     def description(self):
   484     def description(self):
   481         return self._changeset.description
   485         return self._changeset.description
   482     def branch(self):
   486     def branch(self):
   483         return encoding.tolocal(self._changeset.extra.get("branch"))
   487         return encoding.tolocal(self._changeset.extra.get("branch"))
   484     def closesbranch(self):
   488     def closesbranch(self):
  1204         return self._status.added
  1208         return self._status.added
  1205     def removed(self):
  1209     def removed(self):
  1206         return self._status.removed
  1210         return self._status.removed
  1207     def deleted(self):
  1211     def deleted(self):
  1208         return self._status.deleted
  1212         return self._status.deleted
  1209     @propertycache
       
  1210     def _copies(self):
       
  1211         p1copies = {}
       
  1212         p2copies = {}
       
  1213         parents = self._repo.dirstate.parents()
       
  1214         p1manifest = self._repo[parents[0]].manifest()
       
  1215         p2manifest = self._repo[parents[1]].manifest()
       
  1216         narrowmatch = self._repo.narrowmatch()
       
  1217         for dst, src in self._repo.dirstate.copies().items():
       
  1218             if not narrowmatch(dst):
       
  1219                 continue
       
  1220             if src in p1manifest:
       
  1221                 p1copies[dst] = src
       
  1222             elif src in p2manifest:
       
  1223                 p2copies[dst] = src
       
  1224         return p1copies, p2copies
       
  1225     def p1copies(self):
       
  1226         return self._copies[0]
       
  1227     def p2copies(self):
       
  1228         return self._copies[1]
       
  1229     def branch(self):
  1213     def branch(self):
  1230         return encoding.tolocal(self._extra['branch'])
  1214         return encoding.tolocal(self._extra['branch'])
  1231     def closesbranch(self):
  1215     def closesbranch(self):
  1232         return 'close' in self._extra
  1216         return 'close' in self._extra
  1233     def extra(self):
  1217     def extra(self):
  1577                 self._status = s
  1561                 self._status = s
  1578 
  1562 
  1579         return s
  1563         return s
  1580 
  1564 
  1581     @propertycache
  1565     @propertycache
       
  1566     def _copies(self):
       
  1567         p1copies = {}
       
  1568         p2copies = {}
       
  1569         parents = self._repo.dirstate.parents()
       
  1570         p1manifest = self._repo[parents[0]].manifest()
       
  1571         p2manifest = self._repo[parents[1]].manifest()
       
  1572         narrowmatch = self._repo.narrowmatch()
       
  1573         for dst, src in self._repo.dirstate.copies().items():
       
  1574             if not narrowmatch(dst):
       
  1575                 continue
       
  1576             if src in p1manifest:
       
  1577                 p1copies[dst] = src
       
  1578             elif src in p2manifest:
       
  1579                 p2copies[dst] = src
       
  1580         return p1copies, p2copies
       
  1581     def p1copies(self):
       
  1582         return self._copies[0]
       
  1583     def p2copies(self):
       
  1584         return self._copies[1]
       
  1585 
       
  1586     @propertycache
  1582     def _manifest(self):
  1587     def _manifest(self):
  1583         """generate a manifest corresponding to the values in self._status
  1588         """generate a manifest corresponding to the values in self._status
  1584 
  1589 
  1585         This reuse the file nodeid from parent, but we use special node
  1590         This reuse the file nodeid from parent, but we use special node
  1586         identifiers for added and modified files. This is used by manifests
  1591         identifiers for added and modified files. This is used by manifests