mercurial/context.py
changeset 6876 077f1e637cd8
parent 6846 54b7c79575fa
child 7008 8fee8ff13d37
equal deleted inserted replaced
6875:0d714a48ab53 6876:077f1e637cd8
     3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
     3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
     4 #
     4 #
     5 # This software may be used and distributed according to the terms
     5 # This software may be used and distributed according to the terms
     6 # of the GNU General Public License, incorporated herein by reference.
     6 # of the GNU General Public License, incorporated herein by reference.
     7 
     7 
     8 from node import nullid, nullrev, short
     8 from node import nullid, nullrev, short, hex
     9 from i18n import _
     9 from i18n import _
    10 import ancestor, bdiff, revlog, util, os, errno
    10 import ancestor, bdiff, revlog, util, os, errno
    11 
    11 
    12 class changectx(object):
    12 class changectx(object):
    13     """A changecontext object makes access to data related to a particular
    13     """A changecontext object makes access to data related to a particular
    14     changeset convenient."""
    14     changeset convenient."""
    15     def __init__(self, repo, changeid=None):
    15     def __init__(self, repo, changeid=''):
    16         """changeid is a revision number, node, or tag"""
    16         """changeid is a revision number, node, or tag"""
       
    17         if changeid == '':
       
    18             changeid = '.'
    17         self._repo = repo
    19         self._repo = repo
    18 
       
    19         if not changeid and changeid != 0:
       
    20             p1, p2 = self._repo.dirstate.parents()
       
    21             self._rev = self._repo.changelog.rev(p1)
       
    22             if self._rev == -1:
       
    23                 changeid = 'tip'
       
    24             else:
       
    25                 self._node = p1
       
    26                 return
       
    27 
       
    28         self._node = self._repo.lookup(changeid)
    20         self._node = self._repo.lookup(changeid)
    29         self._rev = self._repo.changelog.rev(self._node)
    21         self._rev = self._repo.changelog.rev(self._node)
    30 
    22 
    31     def __str__(self):
    23     def __str__(self):
    32         return short(self.node())
    24         return short(self.node())
    33 
    25 
       
    26     def __int__(self):
       
    27         return self.rev()
       
    28 
    34     def __repr__(self):
    29     def __repr__(self):
    35         return "<changectx %s>" % str(self)
    30         return "<changectx %s>" % str(self)
       
    31 
       
    32     def __hash__(self):
       
    33         try:
       
    34             return hash(self._rev)
       
    35         except AttributeError:
       
    36             return id(self)
    36 
    37 
    37     def __eq__(self, other):
    38     def __eq__(self, other):
    38         try:
    39         try:
    39             return self._rev == other._rev
    40             return self._rev == other._rev
    40         except AttributeError:
    41         except AttributeError:
    55             return self._manifest
    56             return self._manifest
    56         elif name == '_manifestdelta':
    57         elif name == '_manifestdelta':
    57             md = self._repo.manifest.readdelta(self._changeset[0])
    58             md = self._repo.manifest.readdelta(self._changeset[0])
    58             self._manifestdelta = md
    59             self._manifestdelta = md
    59             return self._manifestdelta
    60             return self._manifestdelta
       
    61         elif name == '_parents':
       
    62             p = self._repo.changelog.parents(self._node)
       
    63             if p[1] == nullid:
       
    64                 p = p[:-1]
       
    65             self._parents = [changectx(self._repo, x) for x in p]
       
    66             return self._parents
    60         else:
    67         else:
    61             raise AttributeError, name
    68             raise AttributeError, name
    62 
    69 
    63     def __contains__(self, key):
    70     def __contains__(self, key):
    64         return key in self._manifest
    71         return key in self._manifest
    65 
    72 
    66     def __getitem__(self, key):
    73     def __getitem__(self, key):
    67         return self.filectx(key)
    74         return self.filectx(key)
    68 
    75 
    69     def __iter__(self):
    76     def __iter__(self):
    70         a = self._manifest.keys()
    77         for f in util.sort(self._manifest):
    71         a.sort()
       
    72         for f in a:
       
    73             yield f
    78             yield f
    74 
    79 
    75     def changeset(self): return self._changeset
    80     def changeset(self): return self._changeset
    76     def manifest(self): return self._manifest
    81     def manifest(self): return self._manifest
    77 
    82 
    78     def rev(self): return self._rev
    83     def rev(self): return self._rev
    79     def node(self): return self._node
    84     def node(self): return self._node
       
    85     def hex(self): return hex(self._node)
    80     def user(self): return self._changeset[1]
    86     def user(self): return self._changeset[1]
    81     def date(self): return self._changeset[2]
    87     def date(self): return self._changeset[2]
    82     def files(self): return self._changeset[3]
    88     def files(self): return self._changeset[3]
    83     def description(self): return self._changeset[4]
    89     def description(self): return self._changeset[4]
    84     def branch(self): return self._changeset[5].get("branch")
    90     def branch(self): return self._changeset[5].get("branch")
    85     def extra(self): return self._changeset[5]
    91     def extra(self): return self._changeset[5]
    86     def tags(self): return self._repo.nodetags(self._node)
    92     def tags(self): return self._repo.nodetags(self._node)
    87 
    93 
    88     def parents(self):
    94     def parents(self):
    89         """return contexts for each parent changeset"""
    95         """return contexts for each parent changeset"""
    90         p = self._repo.changelog.parents(self._node)
    96         return self._parents
    91         return [changectx(self._repo, x) for x in p]
       
    92 
    97 
    93     def children(self):
    98     def children(self):
    94         """return contexts for each child changeset"""
    99         """return contexts for each child changeset"""
    95         c = self._repo.changelog.children(self._node)
   100         c = self._repo.changelog.children(self._node)
    96         return [changectx(self._repo, x) for x in c]
   101         return [changectx(self._repo, x) for x in c]
       
   102 
       
   103     def ancestors(self):
       
   104         for a in self._repo.changelog.ancestors(self._rev):
       
   105             yield changectx(self._repo, a)
       
   106 
       
   107     def descendants(self):
       
   108         for d in self._repo.changelog.descendants(self._rev):
       
   109             yield changectx(self._repo, d)
    97 
   110 
    98     def _fileinfo(self, path):
   111     def _fileinfo(self, path):
    99         if '_manifest' in self.__dict__:
   112         if '_manifest' in self.__dict__:
   100             try:
   113             try:
   101                 return self._manifest[path], self._manifest.flags(path)
   114                 return self._manifest[path], self._manifest.flags(path)
   113         return node, flag
   126         return node, flag
   114 
   127 
   115     def filenode(self, path):
   128     def filenode(self, path):
   116         return self._fileinfo(path)[0]
   129         return self._fileinfo(path)[0]
   117 
   130 
   118     def fileflags(self, path):
   131     def flags(self, path):
   119         try:
   132         try:
   120             return self._fileinfo(path)[1]
   133             return self._fileinfo(path)[1]
   121         except revlog.LookupError:
   134         except revlog.LookupError:
   122             return ''
   135             return ''
   123 
   136 
   126         if fileid is None:
   139         if fileid is None:
   127             fileid = self.filenode(path)
   140             fileid = self.filenode(path)
   128         return filectx(self._repo, path, fileid=fileid,
   141         return filectx(self._repo, path, fileid=fileid,
   129                        changectx=self, filelog=filelog)
   142                        changectx=self, filelog=filelog)
   130 
   143 
   131     def filectxs(self):
       
   132         """generate a file context for each file in this changeset's
       
   133            manifest"""
       
   134         mf = self.manifest()
       
   135         m = mf.keys()
       
   136         m.sort()
       
   137         for f in m:
       
   138             yield self.filectx(f, fileid=mf[f])
       
   139 
       
   140     def ancestor(self, c2):
   144     def ancestor(self, c2):
   141         """
   145         """
   142         return the ancestor context of self and c2
   146         return the ancestor context of self and c2
   143         """
   147         """
   144         n = self._repo.changelog.ancestor(self._node, c2._node)
   148         n = self._repo.changelog.ancestor(self._node, c2._node)
   145         return changectx(self._repo, n)
   149         return changectx(self._repo, n)
       
   150 
       
   151     def walk(self, match):
       
   152         fdict = dict.fromkeys(match.files())
       
   153         # for dirstate.walk, files=['.'] means "walk the whole tree".
       
   154         # follow that here, too
       
   155         fdict.pop('.', None)
       
   156         for fn in self:
       
   157             for ffn in fdict:
       
   158                 # match if the file is the exact name or a directory
       
   159                 if ffn == fn or fn.startswith("%s/" % ffn):
       
   160                     del fdict[ffn]
       
   161                     break
       
   162             if match(fn):
       
   163                 yield fn
       
   164         for fn in util.sort(fdict):
       
   165             if match.bad(fn, 'No such file in rev ' + str(self)) and match(fn):
       
   166                 yield fn
   146 
   167 
   147 class filectx(object):
   168 class filectx(object):
   148     """A filecontext object makes access to data related to a particular
   169     """A filecontext object makes access to data related to a particular
   149        filerevision convenient."""
   170        filerevision convenient."""
   150     def __init__(self, repo, path, changeid=None, fileid=None,
   171     def __init__(self, repo, path, changeid=None, fileid=None,
   208         return "%s@%s" % (self.path(), short(self.node()))
   229         return "%s@%s" % (self.path(), short(self.node()))
   209 
   230 
   210     def __repr__(self):
   231     def __repr__(self):
   211         return "<filectx %s>" % str(self)
   232         return "<filectx %s>" % str(self)
   212 
   233 
       
   234     def __hash__(self):
       
   235         try:
       
   236             return hash((self._path, self._fileid))
       
   237         except AttributeError:
       
   238             return id(self)
       
   239 
   213     def __eq__(self, other):
   240     def __eq__(self, other):
   214         try:
   241         try:
   215             return (self._path == other._path
   242             return (self._path == other._path
   216                     and self._fileid == other._fileid)
   243                     and self._fileid == other._fileid)
   217         except AttributeError:
   244         except AttributeError:
   226         return filectx(self._repo, self._path, fileid=fileid,
   253         return filectx(self._repo, self._path, fileid=fileid,
   227                        filelog=self._filelog)
   254                        filelog=self._filelog)
   228 
   255 
   229     def filerev(self): return self._filerev
   256     def filerev(self): return self._filerev
   230     def filenode(self): return self._filenode
   257     def filenode(self): return self._filenode
   231     def fileflags(self): return self._changectx.fileflags(self._path)
   258     def flags(self): return self._changectx.flags(self._path)
   232     def isexec(self): return 'x' in self.fileflags()
       
   233     def islink(self): return 'l' in self.fileflags()
       
   234     def filelog(self): return self._filelog
   259     def filelog(self): return self._filelog
   235 
   260 
   236     def rev(self):
   261     def rev(self):
   237         if '_changectx' in self.__dict__:
   262         if '_changectx' in self.__dict__:
   238             return self._changectx.rev()
   263             return self._changectx.rev()
   374                     needed[p] += 1
   399                     needed[p] += 1
   375 
   400 
   376         # sort by revision (per file) which is a topological order
   401         # sort by revision (per file) which is a topological order
   377         visit = []
   402         visit = []
   378         for f in files:
   403         for f in files:
   379             fn = [(n.rev(), n) for n in needed.keys() if n._path == f]
   404             fn = [(n.rev(), n) for n in needed if n._path == f]
   380             visit.extend(fn)
   405             visit.extend(fn)
   381         visit.sort()
   406 
   382         hist = {}
   407         hist = {}
   383 
   408         for r, f in util.sort(visit):
   384         for r, f in visit:
       
   385             curr = decorate(f.data(), f)
   409             curr = decorate(f.data(), f)
   386             for p in parents(f):
   410             for p in parents(f):
   387                 if p != nullid:
   411                 if p != nullid:
   388                     curr = pair(hist[p], curr)
   412                     curr = pair(hist[p], curr)
   389                     # trim the history of unneeded revs
   413                     # trim the history of unneeded revs
   430 
   454 
   431         return None
   455         return None
   432 
   456 
   433 class workingctx(changectx):
   457 class workingctx(changectx):
   434     """A workingctx object makes access to data related to
   458     """A workingctx object makes access to data related to
   435     the current working directory convenient."""
   459     the current working directory convenient.
   436     def __init__(self, repo):
   460     parents - a pair of parent nodeids, or None to use the dirstate.
       
   461     date - any valid date string or (unixtime, offset), or None.
       
   462     user - username string, or None.
       
   463     extra - a dictionary of extra values, or None.
       
   464     changes - a list of file lists as returned by localrepo.status()
       
   465                or None to use the repository status.
       
   466     """
       
   467     def __init__(self, repo, parents=None, text="", user=None, date=None,
       
   468                  extra=None, changes=None):
   437         self._repo = repo
   469         self._repo = repo
   438         self._rev = None
   470         self._rev = None
   439         self._node = None
   471         self._node = None
       
   472         self._text = text
       
   473         if date:
       
   474             self._date = util.parsedate(date)
       
   475         if user:
       
   476             self._user = user
       
   477         if parents:
       
   478             self._parents = [changectx(self._repo, p) for p in parents]
       
   479         if changes:
       
   480             self._status = list(changes)
       
   481 
       
   482         self._extra = {}
       
   483         if extra:
       
   484             self._extra = extra.copy()
       
   485         if 'branch' not in self._extra:
       
   486             branch = self._repo.dirstate.branch()
       
   487             try:
       
   488                 branch = branch.decode('UTF-8').encode('UTF-8')
       
   489             except UnicodeDecodeError:
       
   490                 raise util.Abort(_('branch name not in UTF-8!'))
       
   491             self._extra['branch'] = branch
       
   492         if self._extra['branch'] == '':
       
   493             self._extra['branch'] = 'default'
   440 
   494 
   441     def __str__(self):
   495     def __str__(self):
   442         return str(self._parents[0]) + "+"
   496         return str(self._parents[0]) + "+"
   443 
   497 
   444     def __nonzero__(self):
   498     def __nonzero__(self):
   445         return True
   499         return True
   446 
   500 
       
   501     def __contains__(self, key):
       
   502         return self._dirstate[key] not in "?r"
       
   503 
   447     def __getattr__(self, name):
   504     def __getattr__(self, name):
   448         if name == '_parents':
       
   449             self._parents = self._repo.parents()
       
   450             return self._parents
       
   451         if name == '_status':
   505         if name == '_status':
   452             self._status = self._repo.status()
   506             self._status = self._repo.status(unknown=True)
   453             return self._status
   507             return self._status
       
   508         elif name == '_user':
       
   509             self._user = self._repo.ui.username()
       
   510             return self._user
       
   511         elif name == '_date':
       
   512             self._date = util.makedate()
       
   513             return self._date
   454         if name == '_manifest':
   514         if name == '_manifest':
   455             self._buildmanifest()
   515             self._buildmanifest()
   456             return self._manifest
   516             return self._manifest
       
   517         elif name == '_parents':
       
   518             p = self._repo.dirstate.parents()
       
   519             if p[1] == nullid:
       
   520                 p = p[:-1]
       
   521             self._parents = [changectx(self._repo, x) for x in p]
       
   522             return self._parents
   457         else:
   523         else:
   458             raise AttributeError, name
   524             raise AttributeError, name
   459 
   525 
   460     def _buildmanifest(self):
   526     def _buildmanifest(self):
   461         """generate a manifest corresponding to the working directory"""
   527         """generate a manifest corresponding to the working directory"""
   462 
   528 
   463         man = self._parents[0].manifest().copy()
   529         man = self._parents[0].manifest().copy()
   464         copied = self._repo.dirstate.copies()
   530         copied = self._repo.dirstate.copies()
   465         is_exec = util.execfunc(self._repo.root,
   531         cf = lambda x: man.flags(copied.get(x, x))
   466                                 lambda p: man.execf(copied.get(p,p)))
   532         ff = self._repo.dirstate.flagfunc(cf)
   467         is_link = util.linkfunc(self._repo.root,
       
   468                                 lambda p: man.linkf(copied.get(p,p)))
       
   469         modified, added, removed, deleted, unknown = self._status[:5]
   533         modified, added, removed, deleted, unknown = self._status[:5]
   470         for i, l in (("a", added), ("m", modified), ("u", unknown)):
   534         for i, l in (("a", added), ("m", modified), ("u", unknown)):
   471             for f in l:
   535             for f in l:
   472                 man[f] = man.get(copied.get(f, f), nullid) + i
   536                 man[f] = man.get(copied.get(f, f), nullid) + i
   473                 try:
   537                 try:
   474                     man.set(f, is_exec(f), is_link(f))
   538                     man.set(f, ff(f))
   475                 except OSError:
   539                 except OSError:
   476                     pass
   540                     pass
   477 
   541 
   478         for f in deleted + removed:
   542         for f in deleted + removed:
   479             if f in man:
   543             if f in man:
   481 
   545 
   482         self._manifest = man
   546         self._manifest = man
   483 
   547 
   484     def manifest(self): return self._manifest
   548     def manifest(self): return self._manifest
   485 
   549 
   486     def user(self): return self._repo.ui.username()
   550     def user(self): return self._user or self._repo.ui.username()
   487     def date(self): return util.makedate()
   551     def date(self): return self._date
   488     def description(self): return ""
   552     def description(self): return self._text
   489     def files(self):
   553     def files(self):
   490         f = self.modified() + self.added() + self.removed()
   554         return util.sort(self._status[0] + self._status[1] + self._status[2])
   491         f.sort()
       
   492         return f
       
   493 
   555 
   494     def modified(self): return self._status[0]
   556     def modified(self): return self._status[0]
   495     def added(self): return self._status[1]
   557     def added(self): return self._status[1]
   496     def removed(self): return self._status[2]
   558     def removed(self): return self._status[2]
   497     def deleted(self): return self._status[3]
   559     def deleted(self): return self._status[3]
   498     def unknown(self): return self._status[4]
   560     def unknown(self): return self._status[4]
   499     def clean(self): return self._status[5]
   561     def clean(self): return self._status[5]
   500     def branch(self): return self._repo.dirstate.branch()
   562     def branch(self): return self._extra['branch']
       
   563     def extra(self): return self._extra
   501 
   564 
   502     def tags(self):
   565     def tags(self):
   503         t = []
   566         t = []
   504         [t.extend(p.tags()) for p in self.parents()]
   567         [t.extend(p.tags()) for p in self.parents()]
   505         return t
   568         return t
   506 
   569 
   507     def parents(self):
       
   508         """return contexts for each parent changeset"""
       
   509         return self._parents
       
   510 
       
   511     def children(self):
   570     def children(self):
   512         return []
   571         return []
   513 
   572 
   514     def fileflags(self, path):
   573     def flags(self, path):
   515         if '_manifest' in self.__dict__:
   574         if '_manifest' in self.__dict__:
   516             try:
   575             try:
   517                 return self._manifest.flags(path)
   576                 return self._manifest.flags(path)
   518             except KeyError:
   577             except KeyError:
   519                 return ''
   578                 return ''
   520 
   579 
   521         pnode = self._parents[0].changeset()[0]
   580         pnode = self._parents[0].changeset()[0]
   522         orig = self._repo.dirstate.copies().get(path, path)
   581         orig = self._repo.dirstate.copies().get(path, path)
   523         node, flag = self._repo.manifest.find(pnode, orig)
   582         node, flag = self._repo.manifest.find(pnode, orig)
   524         is_link = util.linkfunc(self._repo.root,
       
   525                                 lambda p: flag and 'l' in flag)
       
   526         is_exec = util.execfunc(self._repo.root,
       
   527                                 lambda p: flag and 'x' in flag)
       
   528         try:
   583         try:
   529             return (is_link(path) and 'l' or '') + (is_exec(path) and 'x' or '')
   584             ff = self._repo.dirstate.flagfunc(lambda x: flag or '')
       
   585             return ff(path)
   530         except OSError:
   586         except OSError:
   531             pass
   587             pass
   532 
   588 
   533         if not node or path in self.deleted() or path in self.removed():
   589         if not node or path in self.deleted() or path in self.removed():
   534             return ''
   590             return ''
   540                               filelog=filelog)
   596                               filelog=filelog)
   541 
   597 
   542     def ancestor(self, c2):
   598     def ancestor(self, c2):
   543         """return the ancestor context of self and c2"""
   599         """return the ancestor context of self and c2"""
   544         return self._parents[0].ancestor(c2) # punt on two parents for now
   600         return self._parents[0].ancestor(c2) # punt on two parents for now
       
   601 
       
   602     def walk(self, match):
       
   603         return util.sort(self._repo.dirstate.walk(match, True, False).keys())
   545 
   604 
   546 class workingfilectx(filectx):
   605 class workingfilectx(filectx):
   547     """A workingfilectx object makes access to data related to a particular
   606     """A workingfilectx object makes access to data related to a particular
   548        file in the working directory convenient."""
   607        file in the working directory convenient."""
   549     def __init__(self, repo, path, filelog=None, workingctx=None):
   608     def __init__(self, repo, path, filelog=None, workingctx=None):
   623         except OSError, err:
   682         except OSError, err:
   624             if err.errno != errno.ENOENT: raise
   683             if err.errno != errno.ENOENT: raise
   625             return (t, tz)
   684             return (t, tz)
   626 
   685 
   627     def cmp(self, text): return self._repo.wread(self._path) == text
   686     def cmp(self, text): return self._repo.wread(self._path) == text
       
   687 
       
   688 class memctx(object):
       
   689     """A memctx is a subset of changectx supposed to be built on memory
       
   690     and passed to commit functions.
       
   691 
       
   692     NOTE: this interface and the related memfilectx are experimental and
       
   693     may change without notice.
       
   694 
       
   695     parents - a pair of parent nodeids.
       
   696     filectxfn - a callable taking (repo, memctx, path) arguments and
       
   697     returning a memctx object.
       
   698     date - any valid date string or (unixtime, offset), or None.
       
   699     user - username string, or None.
       
   700     extra - a dictionary of extra values, or None.
       
   701     """
       
   702     def __init__(self, repo, parents, text, files, filectxfn, user=None,
       
   703                  date=None, extra=None):
       
   704         self._repo = repo
       
   705         self._rev = None
       
   706         self._node = None
       
   707         self._text = text
       
   708         self._date = date and util.parsedate(date) or util.makedate()
       
   709         self._user = user
       
   710         parents = [(p or nullid) for p in parents]
       
   711         p1, p2 = parents
       
   712         self._parents = [changectx(self._repo, p) for p in (p1, p2)]
       
   713         files = util.sort(list(files))
       
   714         self._status = [files, [], [], [], []]
       
   715         self._filectxfn = filectxfn
       
   716 
       
   717         self._extra = extra and extra.copy() or {}
       
   718         if 'branch' not in self._extra:
       
   719             self._extra['branch'] = 'default'
       
   720         elif self._extra.get('branch') == '':
       
   721             self._extra['branch'] = 'default'
       
   722 
       
   723     def __str__(self):
       
   724         return str(self._parents[0]) + "+"
       
   725 
       
   726     def __int__(self):
       
   727         return self._rev
       
   728 
       
   729     def __nonzero__(self):
       
   730         return True
       
   731 
       
   732     def user(self): return self._user or self._repo.ui.username()
       
   733     def date(self): return self._date
       
   734     def description(self): return self._text
       
   735     def files(self): return self.modified()
       
   736     def modified(self): return self._status[0]
       
   737     def added(self): return self._status[1]
       
   738     def removed(self): return self._status[2]
       
   739     def deleted(self): return self._status[3]
       
   740     def unknown(self): return self._status[4]
       
   741     def clean(self): return self._status[5]
       
   742     def branch(self): return self._extra['branch']
       
   743     def extra(self): return self._extra
       
   744     def flags(self, f): return self[f].flags()
       
   745 
       
   746     def parents(self):
       
   747         """return contexts for each parent changeset"""
       
   748         return self._parents
       
   749 
       
   750     def filectx(self, path, filelog=None):
       
   751         """get a file context from the working directory"""
       
   752         return self._filectxfn(self._repo, self, path)
       
   753 
       
   754 class memfilectx(object):
       
   755     """A memfilectx is a subset of filectx supposed to be built by client
       
   756     code and passed to commit functions.
       
   757     """
       
   758     def __init__(self, path, data, islink, isexec, copied):
       
   759         """copied is the source file path, or None."""
       
   760         self._path = path
       
   761         self._data = data
       
   762         self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
       
   763         self._copied = None
       
   764         if copied:
       
   765             self._copied = (copied, nullid)
       
   766 
       
   767     def __nonzero__(self): return True
       
   768     def __str__(self): return "%s@%s" % (self.path(), self._changectx)
       
   769     def path(self): return self._path
       
   770     def data(self): return self._data
       
   771     def flags(self): return self._flags
       
   772     def isexec(self): return 'x' in self._flags
       
   773     def islink(self): return 'l' in self._flags
       
   774     def renamed(self): return self._copied
       
   775