mercurial/dirstatemap.py
changeset 50252 a6b8b1ab9116
parent 50177 2f60cd6442fd
parent 49703 dbe09fb038fc
child 50389 bf16ef96defe
child 50596 2b1cd454793a
equal deleted inserted replaced
50251:2fbc109fd58a 50252:a6b8b1ab9116
     8 
     8 
     9 from . import (
     9 from . import (
    10     error,
    10     error,
    11     pathutil,
    11     pathutil,
    12     policy,
    12     policy,
       
    13     testing,
    13     txnutil,
    14     txnutil,
    14     util,
    15     util,
    15 )
    16 )
    16 
    17 
    17 from .dirstateutils import (
    18 from .dirstateutils import (
    28     DirstateItem = parsers.DirstateItem
    29     DirstateItem = parsers.DirstateItem
    29 else:
    30 else:
    30     DirstateItem = rustmod.DirstateItem
    31     DirstateItem = rustmod.DirstateItem
    31 
    32 
    32 rangemask = 0x7FFFFFFF
    33 rangemask = 0x7FFFFFFF
       
    34 
       
    35 WRITE_MODE_AUTO = 0
       
    36 WRITE_MODE_FORCE_NEW = 1
       
    37 WRITE_MODE_FORCE_APPEND = 2
       
    38 
       
    39 
       
    40 V2_MAX_READ_ATTEMPTS = 5
    33 
    41 
    34 
    42 
    35 class _dirstatemapcommon:
    43 class _dirstatemapcommon:
    36     """
    44     """
    37     Methods that are identical for both implementations of the dirstatemap
    45     Methods that are identical for both implementations of the dirstatemap
    52         self._filename = b'dirstate'
    60         self._filename = b'dirstate'
    53         self._nodelen = 20  # Also update Rust code when changing this!
    61         self._nodelen = 20  # Also update Rust code when changing this!
    54         self._parents = None
    62         self._parents = None
    55         self._dirtyparents = False
    63         self._dirtyparents = False
    56         self._docket = None
    64         self._docket = None
       
    65         write_mode = ui.config(b"devel", b"dirstate.v2.data_update_mode")
       
    66         if write_mode == b"auto":
       
    67             self._write_mode = WRITE_MODE_AUTO
       
    68         elif write_mode == b"force-append":
       
    69             self._write_mode = WRITE_MODE_FORCE_APPEND
       
    70         elif write_mode == b"force-new":
       
    71             self._write_mode = WRITE_MODE_FORCE_NEW
       
    72         else:
       
    73             # unknown value, fallback to default
       
    74             self._write_mode = WRITE_MODE_AUTO
    57 
    75 
    58         # for consistent view between _pl() and _read() invocations
    76         # for consistent view between _pl() and _read() invocations
    59         self._pendingmode = None
    77         self._pendingmode = None
    60 
    78 
    61     def _set_identity(self):
    79     def _set_identity(self):
   130         if not self._docket:
   148         if not self._docket:
   131             if not self._use_dirstate_v2:
   149             if not self._use_dirstate_v2:
   132                 raise error.ProgrammingError(
   150                 raise error.ProgrammingError(
   133                     b'dirstate only has a docket in v2 format'
   151                     b'dirstate only has a docket in v2 format'
   134                 )
   152                 )
       
   153             self._set_identity()
   135             self._docket = docketmod.DirstateDocket.parse(
   154             self._docket = docketmod.DirstateDocket.parse(
   136                 self._readdirstatefile(), self._nodeconstants
   155                 self._readdirstatefile(), self._nodeconstants
   137             )
   156             )
   138         return self._docket
   157         return self._docket
       
   158 
       
   159     def _read_v2_data(self):
       
   160         data = None
       
   161         attempts = 0
       
   162         while attempts < V2_MAX_READ_ATTEMPTS:
       
   163             attempts += 1
       
   164             try:
       
   165                 # TODO: use mmap when possible
       
   166                 data = self._opener.read(self.docket.data_filename())
       
   167             except FileNotFoundError:
       
   168                 # read race detected between docket and data file
       
   169                 # reload the docket and retry
       
   170                 self._docket = None
       
   171         if data is None:
       
   172             assert attempts >= V2_MAX_READ_ATTEMPTS
       
   173             msg = b"dirstate read race happened %d times in a row"
       
   174             msg %= attempts
       
   175             raise error.Abort(msg)
       
   176         return self._opener.read(self.docket.data_filename())
   139 
   177 
   140     def write_v2_no_append(self, tr, st, meta, packed):
   178     def write_v2_no_append(self, tr, st, meta, packed):
   141         old_docket = self.docket
   179         old_docket = self.docket
   142         new_docket = docketmod.DirstateDocket.with_new_uuid(
   180         new_docket = docketmod.DirstateDocket.with_new_uuid(
   143             self.parents(), len(packed), meta
   181             self.parents(), len(packed), meta
   288         return copies
   326         return copies
   289 
   327 
   290     ### disk interaction
   328     ### disk interaction
   291 
   329 
   292     def read(self):
   330     def read(self):
   293         # ignore HG_PENDING because identity is used only for writing
   331         testing.wait_on_cfg(self._ui, b'dirstate.pre-read-file')
   294         self._set_identity()
       
   295 
       
   296         if self._use_dirstate_v2:
   332         if self._use_dirstate_v2:
       
   333 
   297             if not self.docket.uuid:
   334             if not self.docket.uuid:
   298                 return
   335                 return
   299             st = self._opener.read(self.docket.data_filename())
   336             testing.wait_on_cfg(self._ui, b'dirstate.post-docket-read-file')
       
   337             st = self._read_v2_data()
   300         else:
   338         else:
       
   339             self._set_identity()
   301             st = self._readdirstatefile()
   340             st = self._readdirstatefile()
   302 
   341 
   303         if not st:
   342         if not st:
   304             return
   343             return
   305 
   344 
   554             Fills the Dirstatemap when called.
   593             Fills the Dirstatemap when called.
   555             """
   594             """
   556             # ignore HG_PENDING because identity is used only for writing
   595             # ignore HG_PENDING because identity is used only for writing
   557             self._set_identity()
   596             self._set_identity()
   558 
   597 
       
   598             testing.wait_on_cfg(self._ui, b'dirstate.pre-read-file')
   559             if self._use_dirstate_v2:
   599             if self._use_dirstate_v2:
   560                 if self.docket.uuid:
   600                 self.docket  # load the data if needed
   561                     # TODO: use mmap when possible
   601                 inode = (
   562                     data = self._opener.read(self.docket.data_filename())
   602                     self.identity.stat.st_ino
       
   603                     if self.identity is not None
       
   604                     and self.identity.stat is not None
       
   605                     else None
       
   606                 )
       
   607                 testing.wait_on_cfg(self._ui, b'dirstate.post-docket-read-file')
       
   608                 if not self.docket.uuid:
       
   609                     data = b''
       
   610                     self._map = rustmod.DirstateMap.new_empty()
   563                 else:
   611                 else:
   564                     data = b''
   612                     data = self._read_v2_data()
   565                 self._map = rustmod.DirstateMap.new_v2(
   613                     self._map = rustmod.DirstateMap.new_v2(
   566                     data, self.docket.data_size, self.docket.tree_metadata
   614                         data,
   567                 )
   615                         self.docket.data_size,
       
   616                         self.docket.tree_metadata,
       
   617                         self.docket.uuid,
       
   618                         inode,
       
   619                     )
   568                 parents = self.docket.parents
   620                 parents = self.docket.parents
   569             else:
   621             else:
       
   622                 self._set_identity()
       
   623                 inode = (
       
   624                     self.identity.stat.st_ino
       
   625                     if self.identity is not None
       
   626                     and self.identity.stat is not None
       
   627                     else None
       
   628                 )
   570                 self._map, parents = rustmod.DirstateMap.new_v1(
   629                 self._map, parents = rustmod.DirstateMap.new_v1(
   571                     self._readdirstatefile()
   630                     self._readdirstatefile(), inode
   572                 )
   631                 )
   573 
   632 
   574             if parents and not self._dirtyparents:
   633             if parents and not self._dirtyparents:
   575                 self.setparents(*parents)
   634                 self.setparents(*parents)
   576 
   635 
   636                 st.close()
   695                 st.close()
   637                 self._dirtyparents = False
   696                 self._dirtyparents = False
   638                 return
   697                 return
   639 
   698 
   640             # We can only append to an existing data file if there is one
   699             # We can only append to an existing data file if there is one
   641             can_append = self.docket.uuid is not None
   700             write_mode = self._write_mode
   642             packed, meta, append = self._map.write_v2(can_append)
   701             if self.docket.uuid is None:
       
   702                 write_mode = WRITE_MODE_FORCE_NEW
       
   703             packed, meta, append = self._map.write_v2(write_mode)
   643             if append:
   704             if append:
   644                 docket = self.docket
   705                 docket = self.docket
   645                 data_filename = docket.data_filename()
   706                 data_filename = docket.data_filename()
   646                 # We mark it for backup to make sure a future `hg rollback` (or
   707                 # We mark it for backup to make sure a future `hg rollback` (or
   647                 # `hg recover`?) call find the data it needs to restore a
   708                 # `hg recover`?) call find the data it needs to restore a