mercurial/changelog.py
changeset 51106 d83d788590a8
parent 51104 1c0f3994d733
child 51181 dcaa2df1f688
equal deleted inserted replaced
51105:af96fbb8f739 51106:d83d788590a8
    25     stringutil,
    25     stringutil,
    26 )
    26 )
    27 from .revlogutils import (
    27 from .revlogutils import (
    28     constants as revlog_constants,
    28     constants as revlog_constants,
    29     flagutil,
    29     flagutil,
    30     randomaccessfile,
       
    31 )
    30 )
    32 
    31 
    33 _defaultextra = {b'branch': b'default'}
    32 _defaultextra = {b'branch': b'default'}
    34 
    33 
    35 
    34 
    90 def stripdesc(desc):
    89 def stripdesc(desc):
    91     """strip trailing whitespace and leading and trailing empty lines"""
    90     """strip trailing whitespace and leading and trailing empty lines"""
    92     return b'\n'.join([l.rstrip() for l in desc.splitlines()]).strip(b'\n')
    91     return b'\n'.join([l.rstrip() for l in desc.splitlines()]).strip(b'\n')
    93 
    92 
    94 
    93 
    95 class _divertopener:
       
    96     def __init__(self, opener, target):
       
    97         self._opener = opener
       
    98         self._target = target
       
    99 
       
   100     def __call__(self, name, mode=b'r', checkambig=False, **kwargs):
       
   101         if name != self._target:
       
   102             return self._opener(name, mode, **kwargs)
       
   103         return self._opener(name + b".a", mode, **kwargs)
       
   104 
       
   105     def __getattr__(self, attr):
       
   106         return getattr(self._opener, attr)
       
   107 
       
   108 
       
   109 class _delayopener:
       
   110     """build an opener that stores chunks in 'buf' instead of 'target'"""
       
   111 
       
   112     def __init__(self, opener, target, buf):
       
   113         self._opener = opener
       
   114         self._target = target
       
   115         self._buf = buf
       
   116 
       
   117     def __call__(self, name, mode=b'r', checkambig=False, **kwargs):
       
   118         if name != self._target:
       
   119             return self._opener(name, mode, **kwargs)
       
   120         assert not kwargs
       
   121         return randomaccessfile.appender(self._opener, name, mode, self._buf)
       
   122 
       
   123     def __getattr__(self, attr):
       
   124         return getattr(self._opener, attr)
       
   125 
       
   126 
       
   127 @attr.s
    94 @attr.s
   128 class _changelogrevision:
    95 class _changelogrevision:
   129     # Extensions might modify _defaultextra, so let the constructor below pass
    96     # Extensions might modify _defaultextra, so let the constructor below pass
   130     # it in
    97     # it in
   131     extra = attr.ib()
    98     extra = attr.ib()
   352         # Delta chains for changelogs tend to be very small because entries
   319         # Delta chains for changelogs tend to be very small because entries
   353         # tend to be small and don't delta well with each. So disable delta
   320         # tend to be small and don't delta well with each. So disable delta
   354         # chains.
   321         # chains.
   355         self._storedeltachains = False
   322         self._storedeltachains = False
   356 
   323 
   357         self._realopener = opener
   324         self._v2_delayed = False
   358         self._delayed = False
       
   359         self._delaybuf = None
       
   360         self._divert = False
       
   361         self._filteredrevs = frozenset()
   325         self._filteredrevs = frozenset()
   362         self._filteredrevs_hashcache = {}
   326         self._filteredrevs_hashcache = {}
   363         self._copiesstorage = opener.options.get(b'copies-storage')
   327         self._copiesstorage = opener.options.get(b'copies-storage')
   364 
   328 
   365     @property
   329     @property
   372         assert isinstance(val, frozenset)
   336         assert isinstance(val, frozenset)
   373         self._filteredrevs = val
   337         self._filteredrevs = val
   374         self._filteredrevs_hashcache = {}
   338         self._filteredrevs_hashcache = {}
   375 
   339 
   376     def _write_docket(self, tr):
   340     def _write_docket(self, tr):
   377         if not self.is_delaying:
   341         if not self._v2_delayed:
   378             super(changelog, self)._write_docket(tr)
   342             super(changelog, self)._write_docket(tr)
   379 
       
   380     @property
       
   381     def is_delaying(self):
       
   382         return self._delayed
       
   383 
   343 
   384     def delayupdate(self, tr):
   344     def delayupdate(self, tr):
   385         """delay visibility of index updates to other readers"""
   345         """delay visibility of index updates to other readers"""
   386         assert not self._inner.is_open
   346         assert not self._inner.is_open
   387         if self._docket is None and not self.is_delaying:
   347         if self._docket is not None:
   388             if len(self) == 0:
   348             self._v2_delayed = True
   389                 self._divert = True
   349         else:
   390                 if self._realopener.exists(self._indexfile + b'.a'):
   350             new_index = self._inner.delay()
   391                     self._realopener.unlink(self._indexfile + b'.a')
   351             if new_index is not None:
   392                 self.opener = _divertopener(self._realopener, self._indexfile)
   352                 self._indexfile = new_index
   393             else:
   353                 tr.registertmp(new_index)
   394                 self._delaybuf = []
       
   395                 self.opener = _delayopener(
       
   396                     self._realopener, self._indexfile, self._delaybuf
       
   397                 )
       
   398             self._inner.opener = self.opener
       
   399             self._inner._segmentfile.opener = self.opener
       
   400             self._inner._segmentfile_sidedata.opener = self.opener
       
   401         self._delayed = True
       
   402         tr.addpending(b'cl-%i' % id(self), self._writepending)
   354         tr.addpending(b'cl-%i' % id(self), self._writepending)
   403         tr.addfinalize(b'cl-%i' % id(self), self._finalize)
   355         tr.addfinalize(b'cl-%i' % id(self), self._finalize)
   404 
   356 
   405     def _finalize(self, tr):
   357     def _finalize(self, tr):
   406         """finalize index updates"""
   358         """finalize index updates"""
   407         assert not self._inner.is_open
   359         assert not self._inner.is_open
   408         self._delayed = False
       
   409         self.opener = self._realopener
       
   410         self._inner.opener = self.opener
       
   411         self._inner._segmentfile.opener = self.opener
       
   412         self._inner._segmentfile_sidedata.opener = self.opener
       
   413         # move redirected index data back into place
       
   414         if self._docket is not None:
   360         if self._docket is not None:
   415             self._write_docket(tr)
   361             self._docket.write(tr)
   416         elif self._divert:
   362             self._v2_delayed = False
   417             assert not self._delaybuf
   363         else:
   418             tmpname = self._indexfile + b".a"
   364             new_index_file = self._inner.finalize_pending()
   419             nfile = self.opener.open(tmpname)
   365             self._indexfile = new_index_file
   420             nfile.close()
   366             # split when we're done
   421             self.opener.rename(tmpname, self._indexfile, checkambig=True)
   367             self._enforceinlinesize(tr, side_write=False)
   422         elif self._delaybuf:
       
   423             fp = self.opener(self._indexfile, b'a', checkambig=True)
       
   424             fp.write(b"".join(self._delaybuf))
       
   425             fp.close()
       
   426             self._delaybuf = None
       
   427         self._divert = False
       
   428         # split when we're done
       
   429         self._enforceinlinesize(tr, side_write=False)
       
   430 
   368 
   431     def _writepending(self, tr):
   369     def _writepending(self, tr):
   432         """create a file containing the unfinalized state for
   370         """create a file containing the unfinalized state for
   433         pretxnchangegroup"""
   371         pretxnchangegroup"""
   434         assert not self._inner.is_open
   372         assert not self._inner.is_open
   435         if self._docket:
   373         if self._docket:
   436             return self._docket.write(tr, pending=True)
   374             any_pending = self._docket.write(tr, pending=True)
   437         if self._delaybuf:
   375             self._v2_delayed = False
   438             # make a temporary copy of the index
   376         else:
   439             fp1 = self._realopener(self._indexfile)
   377             new_index, any_pending = self._inner.write_pending()
   440             pendingfilename = self._indexfile + b".a"
   378             if new_index is not None:
   441             # register as a temp file to ensure cleanup on failure
   379                 self._indexfile = new_index
   442             tr.registertmp(pendingfilename)
   380                 tr.registertmp(new_index)
   443             # write existing data
   381         return any_pending
   444             fp2 = self._realopener(pendingfilename, b"w")
       
   445             fp2.write(fp1.read())
       
   446             # add pending data
       
   447             fp2.write(b"".join(self._delaybuf))
       
   448             fp2.close()
       
   449             # switch modes so finalize can simply rename
       
   450             self._delaybuf = None
       
   451             self._divert = True
       
   452             self.opener = _divertopener(self._realopener, self._indexfile)
       
   453             self._inner.opener = self.opener
       
   454             self._inner._segmentfile.opener = self.opener
       
   455             self._inner._segmentfile_sidedata.opener = self.opener
       
   456 
       
   457         if self._divert:
       
   458             return True
       
   459 
       
   460         return False
       
   461 
   382 
   462     def _enforceinlinesize(self, tr, side_write=True):
   383     def _enforceinlinesize(self, tr, side_write=True):
   463         if not self.is_delaying:
   384         if not self.is_delaying:
   464             revlog.revlog._enforceinlinesize(self, tr, side_write=side_write)
   385             revlog.revlog._enforceinlinesize(self, tr, side_write=side_write)
   465 
   386