mercurial/manifest.py
changeset 24401 e6e023d57e94
parent 24396 9e03602cd2d8
child 24402 c2287f203ec4
equal deleted inserted replaced
24400:03c84c966ef5 24401:e6e023d57e94
   326 
   326 
   327     deltatext = "".join(struct.pack(">lll", start, end, len(content))
   327     deltatext = "".join(struct.pack(">lll", start, end, len(content))
   328                    + content for start, end, content in x)
   328                    + content for start, end, content in x)
   329     return deltatext, newaddlist
   329     return deltatext, newaddlist
   330 
   330 
       
   331 def _splittopdir(f):
       
   332     if '/' in f:
       
   333         dir, subpath = f.split('/', 1)
       
   334         return dir + '/', subpath
       
   335     else:
       
   336         return '', f
       
   337 
       
   338 class treemanifest(object):
       
   339     def __init__(self, text=''):
       
   340         self._dirs = {}
       
   341         # Using _lazymanifest here is a little slower than plain old dicts
       
   342         self._files = {}
       
   343         self._flags = {}
       
   344         lm = _lazymanifest(text)
       
   345         for f, n, fl in lm.iterentries():
       
   346             self[f] = n
       
   347             if fl:
       
   348                 self.setflag(f, fl)
       
   349 
       
   350     def __len__(self):
       
   351         size = len(self._files)
       
   352         for m in self._dirs.values():
       
   353             size += m.__len__()
       
   354         return size
       
   355 
       
   356     def iteritems(self):
       
   357         for p, n in sorted(self._dirs.items() + self._files.items()):
       
   358             if p in self._files:
       
   359                 yield p, n
       
   360             else:
       
   361                 for sf, sn in n.iteritems():
       
   362                     yield p + sf, sn
       
   363 
       
   364     def iterkeys(self):
       
   365         for p in sorted(self._dirs.keys() + self._files.keys()):
       
   366             if p in self._files:
       
   367                 yield p
       
   368             else:
       
   369                 for f in self._dirs[p].iterkeys():
       
   370                     yield p + f
       
   371 
       
   372     def keys(self):
       
   373         return list(self.iterkeys())
       
   374 
       
   375     def __iter__(self):
       
   376         return self.iterkeys()
       
   377 
       
   378     def __contains__(self, f):
       
   379         if f is None:
       
   380             return False
       
   381         dir, subpath = _splittopdir(f)
       
   382         if dir:
       
   383             if dir not in self._dirs:
       
   384                 return False
       
   385             return self._dirs[dir].__contains__(subpath)
       
   386         else:
       
   387             return f in self._files
       
   388 
       
   389     def get(self, f, default=None):
       
   390         dir, subpath = _splittopdir(f)
       
   391         if dir:
       
   392             if dir not in self._dirs:
       
   393                 return default
       
   394             return self._dirs[dir].get(subpath, default)
       
   395         else:
       
   396             return self._files.get(f, default)
       
   397 
       
   398     def __getitem__(self, f):
       
   399         dir, subpath = _splittopdir(f)
       
   400         if dir:
       
   401             return self._dirs[dir].__getitem__(subpath)
       
   402         else:
       
   403             return self._files[f]
       
   404 
       
   405     def flags(self, f):
       
   406         dir, subpath = _splittopdir(f)
       
   407         if dir:
       
   408             if dir not in self._dirs:
       
   409                 return ''
       
   410             return self._dirs[dir].flags(subpath)
       
   411         else:
       
   412             if f in self._dirs:
       
   413                 return ''
       
   414             return self._flags.get(f, '')
       
   415 
       
   416     def find(self, f):
       
   417         dir, subpath = _splittopdir(f)
       
   418         if dir:
       
   419             return self._dirs[dir].find(subpath)
       
   420         else:
       
   421             return self._files[f], self._flags.get(f, '')
       
   422 
       
   423     def __delitem__(self, f):
       
   424         dir, subpath = _splittopdir(f)
       
   425         if dir:
       
   426             self._dirs[dir].__delitem__(subpath)
       
   427             # If the directory is now empty, remove it
       
   428             if not self._dirs[dir]._dirs and not self._dirs[dir]._files:
       
   429                 del self._dirs[dir]
       
   430         else:
       
   431             del self._files[f]
       
   432             if f in self._flags:
       
   433                 del self._flags[f]
       
   434 
       
   435     def __setitem__(self, f, n):
       
   436         assert n is not None
       
   437         dir, subpath = _splittopdir(f)
       
   438         if dir:
       
   439             if dir not in self._dirs:
       
   440                 self._dirs[dir] = treemanifest()
       
   441             self._dirs[dir].__setitem__(subpath, n)
       
   442         else:
       
   443             self._files[f] = n
       
   444 
       
   445     def setflag(self, f, flags):
       
   446         """Set the flags (symlink, executable) for path f."""
       
   447         dir, subpath = _splittopdir(f)
       
   448         if dir:
       
   449             if dir not in self._dirs:
       
   450                 self._dirs[dir] = treemanifest()
       
   451             self._dirs[dir].setflag(subpath, flags)
       
   452         else:
       
   453             self._flags[f] = flags
       
   454 
       
   455     def copy(self):
       
   456         copy = treemanifest()
       
   457         for d in self._dirs:
       
   458             copy._dirs[d] = self._dirs[d].copy()
       
   459         copy._files = dict.copy(self._files)
       
   460         copy._flags = dict.copy(self._flags)
       
   461         return copy
       
   462 
       
   463     def intersectfiles(self, files):
       
   464         '''make a new treemanifest with the intersection of self with files
       
   465 
       
   466         The algorithm assumes that files is much smaller than self.'''
       
   467         ret = treemanifest()
       
   468         for fn in files:
       
   469             if fn in self:
       
   470                 ret[fn] = self[fn]
       
   471                 flags = self.flags(fn)
       
   472                 if flags:
       
   473                     ret.setflag(fn, flags)
       
   474         return ret
       
   475 
       
   476     def filesnotin(self, m2):
       
   477         '''Set of files in this manifest that are not in the other'''
       
   478         files = set(self.iterkeys())
       
   479         files.difference_update(m2.iterkeys())
       
   480         return files
       
   481 
       
   482     @propertycache
       
   483     def _alldirs(self):
       
   484         return scmutil.dirs(self)
       
   485 
       
   486     def dirs(self):
       
   487         return self._alldirs
       
   488 
       
   489     def hasdir(self, dir):
       
   490         return dir in self._alldirs
       
   491 
       
   492     def matches(self, match):
       
   493         '''generate a new manifest filtered by the match argument'''
       
   494         if match.always():
       
   495             return self.copy()
       
   496 
       
   497         files = match.files()
       
   498         if (match.matchfn == match.exact or
       
   499             (not match.anypats() and util.all(fn in self for fn in files))):
       
   500             return self.intersectfiles(files)
       
   501 
       
   502         m = self.copy()
       
   503         for fn in m.keys():
       
   504             if not match(fn):
       
   505                 del m[fn]
       
   506         return m
       
   507 
       
   508     def diff(self, m2, clean=False):
       
   509         '''Finds changes between the current manifest and m2.
       
   510 
       
   511         Args:
       
   512           m2: the manifest to which this manifest should be compared.
       
   513           clean: if true, include files unchanged between these manifests
       
   514                  with a None value in the returned dictionary.
       
   515 
       
   516         The result is returned as a dict with filename as key and
       
   517         values of the form ((n1,fl1),(n2,fl2)), where n1/n2 is the
       
   518         nodeid in the current/other manifest and fl1/fl2 is the flag
       
   519         in the current/other manifest. Where the file does not exist,
       
   520         the nodeid will be None and the flags will be the empty
       
   521         string.
       
   522         '''
       
   523         diff = {}
       
   524 
       
   525         for fn, n1 in self.iteritems():
       
   526             fl1 = self.flags(fn)
       
   527             n2 = m2.get(fn, None)
       
   528             fl2 = m2.flags(fn)
       
   529             if n2 is None:
       
   530                 fl2 = ''
       
   531             if n1 != n2 or fl1 != fl2:
       
   532                 diff[fn] = ((n1, fl1), (n2, fl2))
       
   533             elif clean:
       
   534                 diff[fn] = None
       
   535 
       
   536         for fn, n2 in m2.iteritems():
       
   537             if fn not in self:
       
   538                 fl2 = m2.flags(fn)
       
   539                 diff[fn] = ((None, ''), (n2, fl2))
       
   540 
       
   541         return diff
       
   542 
       
   543     def text(self):
       
   544         """Get the full data of this manifest as a bytestring."""
       
   545         fl = self.keys()
       
   546         _checkforbidden(fl)
       
   547 
       
   548         hex, flags = revlog.hex, self.flags
       
   549         # if this is changed to support newlines in filenames,
       
   550         # be sure to check the templates/ dir again (especially *-raw.tmpl)
       
   551         return ''.join("%s\0%s%s\n" % (f, hex(self[f]), flags(f)) for f in fl)
       
   552 
   331 class manifest(revlog.revlog):
   553 class manifest(revlog.revlog):
   332     def __init__(self, opener):
   554     def __init__(self, opener):
   333         # During normal operations, we expect to deal with not more than four
   555         # During normal operations, we expect to deal with not more than four
   334         # revs at a time (such as during commit --amend). When rebasing large
   556         # revs at a time (such as during commit --amend). When rebasing large
   335         # stacks of commits, the number can go up, hence the config knob below.
   557         # stacks of commits, the number can go up, hence the config knob below.