Show More
@@ -240,3 +240,4 b' c890d8b8bc59b18e5febf60caada629df5356ee2' | |||||
240 | 59466b13a3ae0e29a5d4f485393e516cfbb057d0 0 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 |
@@ -256,3 +256,4 b' c890d8b8bc59b18e5febf60caada629df5356ee2' | |||||
256 | 59466b13a3ae0e29a5d4f485393e516cfbb057d0 6.3.2 |
|
256 | 59466b13a3ae0e29a5d4f485393e516cfbb057d0 6.3.2 | |
257 | 8830004967ad865ead89c28a410405a6e71e0796 6.3.3 |
|
257 | 8830004967ad865ead89c28a410405a6e71e0796 6.3.3 | |
258 | 05de4896508e8ec387b33eb30d8aab78d1c8e9e4 6.4rc0 |
|
258 | 05de4896508e8ec387b33eb30d8aab78d1c8e9e4 6.4rc0 | |
|
259 | f14864fffdcab725d9eac6d4f4c07be05a35f59a 6.4 |
@@ -481,7 +481,7 b' class changelog(revlog.revlog):' | |||||
481 | self._delaybuf = None |
|
481 | self._delaybuf = None | |
482 | self._divert = False |
|
482 | self._divert = False | |
483 | # split when we're done |
|
483 | # split when we're done | |
484 | self._enforceinlinesize(tr) |
|
484 | self._enforceinlinesize(tr, side_write=False) | |
485 |
|
485 | |||
486 | def _writepending(self, tr): |
|
486 | def _writepending(self, tr): | |
487 | """create a file containing the unfinalized state for |
|
487 | """create a file containing the unfinalized state for | |
@@ -512,9 +512,9 b' class changelog(revlog.revlog):' | |||||
512 |
|
512 | |||
513 | return False |
|
513 | return False | |
514 |
|
514 | |||
515 | def _enforceinlinesize(self, tr): |
|
515 | def _enforceinlinesize(self, tr, side_write=True): | |
516 | if not self._delayed: |
|
516 | if not self._delayed: | |
517 | revlog.revlog._enforceinlinesize(self, tr) |
|
517 | revlog.revlog._enforceinlinesize(self, tr, side_write=side_write) | |
518 |
|
518 | |||
519 | def read(self, nodeorrev): |
|
519 | def read(self, nodeorrev): | |
520 | """Obtain data from a parsed changelog revision. |
|
520 | """Obtain data from a parsed changelog revision. |
@@ -803,11 +803,12 b' def debugdeltachain(ui, repo, file_=None' | |||||
803 | # security to avoid crash on corrupted revlogs |
|
803 | # security to avoid crash on corrupted revlogs | |
804 | total_revs = len(index) |
|
804 | total_revs = len(index) | |
805 |
|
805 | |||
|
806 | chain_size_cache = {} | |||
|
807 | ||||
806 | def revinfo(rev): |
|
808 | def revinfo(rev): | |
807 | e = index[rev] |
|
809 | e = index[rev] | |
808 | compsize = e[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH] |
|
810 | compsize = e[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH] | |
809 | uncompsize = e[revlog_constants.ENTRY_DATA_UNCOMPRESSED_LENGTH] |
|
811 | uncompsize = e[revlog_constants.ENTRY_DATA_UNCOMPRESSED_LENGTH] | |
810 | chainsize = 0 |
|
|||
811 |
|
812 | |||
812 | base = e[revlog_constants.ENTRY_DELTA_BASE] |
|
813 | base = e[revlog_constants.ENTRY_DELTA_BASE] | |
813 | p1 = e[revlog_constants.ENTRY_PARENT_1] |
|
814 | p1 = e[revlog_constants.ENTRY_PARENT_1] | |
@@ -870,11 +871,17 b' def debugdeltachain(ui, repo, file_=None' | |||||
870 | deltatype = b'prev' |
|
871 | deltatype = b'prev' | |
871 |
|
872 | |||
872 | chain = r._deltachain(rev)[0] |
|
873 | chain = r._deltachain(rev)[0] | |
873 | for iterrev in chain: |
|
874 | chain_size = 0 | |
874 | e = index[iterrev] |
|
875 | for iter_rev in reversed(chain): | |
875 | chainsize += e[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH] |
|
876 | cached = chain_size_cache.get(iter_rev) | |
876 |
|
877 | if cached is not None: | ||
877 | return p1, p2, compsize, uncompsize, deltatype, chain, chainsize |
|
878 | chain_size += cached | |
|
879 | break | |||
|
880 | e = index[iter_rev] | |||
|
881 | chain_size += e[revlog_constants.ENTRY_DATA_COMPRESSED_LENGTH] | |||
|
882 | chain_size_cache[rev] = chain_size | |||
|
883 | ||||
|
884 | return p1, p2, compsize, uncompsize, deltatype, chain, chain_size | |||
878 |
|
885 | |||
879 | fm = ui.formatter(b'debugdeltachain', opts) |
|
886 | fm = ui.formatter(b'debugdeltachain', opts) | |
880 |
|
887 |
@@ -25,7 +25,7 b' from .revlogutils import (' | |||||
25 |
|
25 | |||
26 | @interfaceutil.implementer(repository.ifilestorage) |
|
26 | @interfaceutil.implementer(repository.ifilestorage) | |
27 | class filelog: |
|
27 | class filelog: | |
28 | def __init__(self, opener, path): |
|
28 | def __init__(self, opener, path, try_split=False): | |
29 | self._revlog = revlog.revlog( |
|
29 | self._revlog = revlog.revlog( | |
30 | opener, |
|
30 | opener, | |
31 | # XXX should use the unencoded path |
|
31 | # XXX should use the unencoded path | |
@@ -33,6 +33,7 b' class filelog:' | |||||
33 | radix=b'/'.join((b'data', path)), |
|
33 | radix=b'/'.join((b'data', path)), | |
34 | censorable=True, |
|
34 | censorable=True, | |
35 | canonical_parent_order=False, # see comment in revlog.py |
|
35 | canonical_parent_order=False, # see comment in revlog.py | |
|
36 | try_split=try_split, | |||
36 | ) |
|
37 | ) | |
37 | # Full name of the user visible file, relative to the repository root. |
|
38 | # Full name of the user visible file, relative to the repository root. | |
38 | # Used by LFS. |
|
39 | # Used by LFS. | |
@@ -256,8 +257,8 b' class filelog:' | |||||
256 | class narrowfilelog(filelog): |
|
257 | class narrowfilelog(filelog): | |
257 | """Filelog variation to be used with narrow stores.""" |
|
258 | """Filelog variation to be used with narrow stores.""" | |
258 |
|
259 | |||
259 | def __init__(self, opener, path, narrowmatch): |
|
260 | def __init__(self, opener, path, narrowmatch, try_split=False): | |
260 | super(narrowfilelog, self).__init__(opener, path) |
|
261 | super(narrowfilelog, self).__init__(opener, path, try_split=try_split) | |
261 | self._narrowmatch = narrowmatch |
|
262 | self._narrowmatch = narrowmatch | |
262 |
|
263 | |||
263 | def renamed(self, node): |
|
264 | def renamed(self, node): |
@@ -191,6 +191,11 b' def _exthook(ui, repo, htype, name, cmd,' | |||||
191 | cwd = encoding.getcwd() |
|
191 | cwd = encoding.getcwd() | |
192 | r = ui.system(cmd, environ=env, cwd=cwd, blockedtag=b'exthook-%s' % (name,)) |
|
192 | r = ui.system(cmd, environ=env, cwd=cwd, blockedtag=b'exthook-%s' % (name,)) | |
193 |
|
193 | |||
|
194 | if repo is not None and repo.currentwlock() is None: | |||
|
195 | repo.invalidatedirstate() | |||
|
196 | if repo is not None and repo.currentlock() is None: | |||
|
197 | repo.invalidate() | |||
|
198 | ||||
194 | duration = util.timer() - starttime |
|
199 | duration = util.timer() - starttime | |
195 | ui.log( |
|
200 | ui.log( | |
196 | b'exthook', |
|
201 | b'exthook', |
@@ -1810,6 +1810,9 b' class ilocalrepositorymain(interfaceutil' | |||||
1810 | def lock(wait=True): |
|
1810 | def lock(wait=True): | |
1811 | """Lock the repository store and return a lock instance.""" |
|
1811 | """Lock the repository store and return a lock instance.""" | |
1812 |
|
1812 | |||
|
1813 | def currentlock(): | |||
|
1814 | """Return the lock if it's held or None.""" | |||
|
1815 | ||||
1813 | def wlock(wait=True): |
|
1816 | def wlock(wait=True): | |
1814 | """Lock the non-store parts of the repository.""" |
|
1817 | """Lock the non-store parts of the repository.""" | |
1815 |
|
1818 |
@@ -1240,7 +1240,12 b' class revlogfilestorage:' | |||||
1240 | if path.startswith(b'/'): |
|
1240 | if path.startswith(b'/'): | |
1241 | path = path[1:] |
|
1241 | path = path[1:] | |
1242 |
|
1242 | |||
1243 | return filelog.filelog(self.svfs, path) |
|
1243 | try_split = ( | |
|
1244 | self.currenttransaction() is not None | |||
|
1245 | or txnutil.mayhavepending(self.root) | |||
|
1246 | ) | |||
|
1247 | ||||
|
1248 | return filelog.filelog(self.svfs, path, try_split=try_split) | |||
1244 |
|
1249 | |||
1245 |
|
1250 | |||
1246 | @interfaceutil.implementer(repository.ilocalrepositoryfilestorage) |
|
1251 | @interfaceutil.implementer(repository.ilocalrepositoryfilestorage) | |
@@ -1251,7 +1256,13 b' class revlognarrowfilestorage:' | |||||
1251 | if path.startswith(b'/'): |
|
1256 | if path.startswith(b'/'): | |
1252 | path = path[1:] |
|
1257 | path = path[1:] | |
1253 |
|
1258 | |||
1254 | return filelog.narrowfilelog(self.svfs, path, self._storenarrowmatch) |
|
1259 | try_split = ( | |
|
1260 | self.currenttransaction() is not None | |||
|
1261 | or txnutil.mayhavepending(self.root) | |||
|
1262 | ) | |||
|
1263 | return filelog.narrowfilelog( | |||
|
1264 | self.svfs, path, self._storenarrowmatch, try_split=try_split | |||
|
1265 | ) | |||
1255 |
|
1266 | |||
1256 |
|
1267 | |||
1257 | def makefilestorage(requirements, features, **kwargs): |
|
1268 | def makefilestorage(requirements, features, **kwargs): | |
@@ -1794,10 +1805,29 b' class localrepository:' | |||||
1794 | ) |
|
1805 | ) | |
1795 |
|
1806 | |||
1796 | def _dirstatevalidate(self, node): |
|
1807 | def _dirstatevalidate(self, node): | |
|
1808 | okay = True | |||
1797 | try: |
|
1809 | try: | |
1798 | self.changelog.rev(node) |
|
1810 | self.changelog.rev(node) | |
|
1811 | except error.LookupError: | |||
|
1812 | # If the parent are unknown it might just be because the changelog | |||
|
1813 | # in memory is lagging behind the dirstate in memory. So try to | |||
|
1814 | # refresh the changelog first. | |||
|
1815 | # | |||
|
1816 | # We only do so if we don't hold the lock, if we do hold the lock | |||
|
1817 | # the invalidation at that time should have taken care of this and | |||
|
1818 | # something is very fishy. | |||
|
1819 | if self.currentlock() is None: | |||
|
1820 | self.invalidate() | |||
|
1821 | try: | |||
|
1822 | self.changelog.rev(node) | |||
|
1823 | except error.LookupError: | |||
|
1824 | okay = False | |||
|
1825 | else: | |||
|
1826 | # XXX we should consider raising an error here. | |||
|
1827 | okay = False | |||
|
1828 | if okay: | |||
1799 | return node |
|
1829 | return node | |
1800 | except error.LookupError: |
|
1830 | else: | |
1801 | if not self._dirstatevalidatewarned: |
|
1831 | if not self._dirstatevalidatewarned: | |
1802 | self._dirstatevalidatewarned = True |
|
1832 | self._dirstatevalidatewarned = True | |
1803 | self.ui.warn( |
|
1833 | self.ui.warn( | |
@@ -3130,6 +3160,10 b' class localrepository:' | |||||
3130 | """Returns the wlock if it's held, or None if it's not.""" |
|
3160 | """Returns the wlock if it's held, or None if it's not.""" | |
3131 | return self._currentlock(self._wlockref) |
|
3161 | return self._currentlock(self._wlockref) | |
3132 |
|
3162 | |||
|
3163 | def currentlock(self): | |||
|
3164 | """Returns the lock if it's held, or None if it's not.""" | |||
|
3165 | return self._currentlock(self._lockref) | |||
|
3166 | ||||
3133 | def checkcommitpatterns(self, wctx, match, status, fail): |
|
3167 | def checkcommitpatterns(self, wctx, match, status, fail): | |
3134 | """check for commit arguments that aren't committable""" |
|
3168 | """check for commit arguments that aren't committable""" | |
3135 | if match.isexact() or match.prefix(): |
|
3169 | if match.isexact() or match.prefix(): |
@@ -302,6 +302,7 b' class revlog:' | |||||
302 | persistentnodemap=False, |
|
302 | persistentnodemap=False, | |
303 | concurrencychecker=None, |
|
303 | concurrencychecker=None, | |
304 | trypending=False, |
|
304 | trypending=False, | |
|
305 | try_split=False, | |||
305 | canonical_parent_order=True, |
|
306 | canonical_parent_order=True, | |
306 | ): |
|
307 | ): | |
307 | """ |
|
308 | """ | |
@@ -328,6 +329,7 b' class revlog:' | |||||
328 | self._nodemap_file = None |
|
329 | self._nodemap_file = None | |
329 | self.postfix = postfix |
|
330 | self.postfix = postfix | |
330 | self._trypending = trypending |
|
331 | self._trypending = trypending | |
|
332 | self._try_split = try_split | |||
331 | self.opener = opener |
|
333 | self.opener = opener | |
332 | if persistentnodemap: |
|
334 | if persistentnodemap: | |
333 | self._nodemap_file = nodemaputil.get_nodemap_file(self) |
|
335 | self._nodemap_file = nodemaputil.get_nodemap_file(self) | |
@@ -511,6 +513,8 b' class revlog:' | |||||
511 | entry_point = b'%s.i.%s' % (self.radix, self.postfix) |
|
513 | entry_point = b'%s.i.%s' % (self.radix, self.postfix) | |
512 | elif self._trypending and self.opener.exists(b'%s.i.a' % self.radix): |
|
514 | elif self._trypending and self.opener.exists(b'%s.i.a' % self.radix): | |
513 | entry_point = b'%s.i.a' % self.radix |
|
515 | entry_point = b'%s.i.a' % self.radix | |
|
516 | elif self._try_split and self.opener.exists(b'%s.i.s' % self.radix): | |||
|
517 | entry_point = b'%s.i.s' % self.radix | |||
514 | else: |
|
518 | else: | |
515 | entry_point = b'%s.i' % self.radix |
|
519 | entry_point = b'%s.i' % self.radix | |
516 |
|
520 | |||
@@ -2015,7 +2019,7 b' class revlog:' | |||||
2015 | raise error.CensoredNodeError(self.display_id, node, text) |
|
2019 | raise error.CensoredNodeError(self.display_id, node, text) | |
2016 | raise |
|
2020 | raise | |
2017 |
|
2021 | |||
2018 | def _enforceinlinesize(self, tr): |
|
2022 | def _enforceinlinesize(self, tr, side_write=True): | |
2019 | """Check if the revlog is too big for inline and convert if so. |
|
2023 | """Check if the revlog is too big for inline and convert if so. | |
2020 |
|
2024 | |||
2021 | This should be called after revisions are added to the revlog. If the |
|
2025 | This should be called after revisions are added to the revlog. If the | |
@@ -2032,7 +2036,8 b' class revlog:' | |||||
2032 | raise error.RevlogError( |
|
2036 | raise error.RevlogError( | |
2033 | _(b"%s not found in the transaction") % self._indexfile |
|
2037 | _(b"%s not found in the transaction") % self._indexfile | |
2034 | ) |
|
2038 | ) | |
2035 | trindex = None |
|
2039 | if troffset: | |
|
2040 | tr.addbackup(self._indexfile, for_offset=True) | |||
2036 | tr.add(self._datafile, 0) |
|
2041 | tr.add(self._datafile, 0) | |
2037 |
|
2042 | |||
2038 | existing_handles = False |
|
2043 | existing_handles = False | |
@@ -2048,6 +2053,29 b' class revlog:' | |||||
2048 | # No need to deal with sidedata writing handle as it is only |
|
2053 | # No need to deal with sidedata writing handle as it is only | |
2049 | # relevant with revlog-v2 which is never inline, not reaching |
|
2054 | # relevant with revlog-v2 which is never inline, not reaching | |
2050 | # this code |
|
2055 | # this code | |
|
2056 | if side_write: | |||
|
2057 | old_index_file_path = self._indexfile | |||
|
2058 | new_index_file_path = self._indexfile + b'.s' | |||
|
2059 | opener = self.opener | |||
|
2060 | ||||
|
2061 | fncache = getattr(opener, 'fncache', None) | |||
|
2062 | if fncache is not None: | |||
|
2063 | fncache.addignore(new_index_file_path) | |||
|
2064 | ||||
|
2065 | # the "split" index replace the real index when the transaction is finalized | |||
|
2066 | def finalize_callback(tr): | |||
|
2067 | opener.rename( | |||
|
2068 | new_index_file_path, | |||
|
2069 | old_index_file_path, | |||
|
2070 | checkambig=True, | |||
|
2071 | ) | |||
|
2072 | ||||
|
2073 | tr.registertmp(new_index_file_path) | |||
|
2074 | if self.target[1] is not None: | |||
|
2075 | finalize_id = b'000-revlog-split-%d-%s' % self.target | |||
|
2076 | else: | |||
|
2077 | finalize_id = b'000-revlog-split-%d' % self.target[0] | |||
|
2078 | tr.addfinalize(finalize_id, finalize_callback) | |||
2051 |
|
2079 | |||
2052 | new_dfh = self._datafp(b'w+') |
|
2080 | new_dfh = self._datafp(b'w+') | |
2053 | new_dfh.truncate(0) # drop any potentially existing data |
|
2081 | new_dfh.truncate(0) # drop any potentially existing data | |
@@ -2055,17 +2083,10 b' class revlog:' | |||||
2055 | with self._indexfp() as read_ifh: |
|
2083 | with self._indexfp() as read_ifh: | |
2056 | for r in self: |
|
2084 | for r in self: | |
2057 | new_dfh.write(self._getsegmentforrevs(r, r, df=read_ifh)[1]) |
|
2085 | new_dfh.write(self._getsegmentforrevs(r, r, df=read_ifh)[1]) | |
2058 | if ( |
|
|||
2059 | trindex is None |
|
|||
2060 | and troffset |
|
|||
2061 | <= self.start(r) + r * self.index.entry_size |
|
|||
2062 | ): |
|
|||
2063 | trindex = r |
|
|||
2064 | new_dfh.flush() |
|
2086 | new_dfh.flush() | |
2065 |
|
2087 | |||
2066 |
if |
|
2088 | if side_write: | |
2067 |
|
|
2089 | self._indexfile = new_index_file_path | |
2068 |
|
||||
2069 | with self.__index_new_fp() as fp: |
|
2090 | with self.__index_new_fp() as fp: | |
2070 | self._format_flags &= ~FLAG_INLINE_DATA |
|
2091 | self._format_flags &= ~FLAG_INLINE_DATA | |
2071 | self._inline = False |
|
2092 | self._inline = False | |
@@ -2079,16 +2100,9 b' class revlog:' | |||||
2079 | if self._docket is not None: |
|
2100 | if self._docket is not None: | |
2080 | self._docket.index_end = fp.tell() |
|
2101 | self._docket.index_end = fp.tell() | |
2081 |
|
2102 | |||
2082 | # There is a small transactional race here. If the rename of |
|
2103 | # If we don't use side-write, the temp file replace the real | |
2083 |
# |
|
2104 | # index when we exit the context manager | |
2084 | # important to ensure that the data file is not truncated |
|
2105 | ||
2085 | # when the index is replaced as otherwise data is lost. |
|
|||
2086 | tr.replace(self._datafile, self.start(trindex)) |
|
|||
2087 |
|
||||
2088 | # the temp file replace the real index when we exit the context |
|
|||
2089 | # manager |
|
|||
2090 |
|
||||
2091 | tr.replace(self._indexfile, trindex * self.index.entry_size) |
|
|||
2092 | nodemaputil.setup_persistent_nodemap(tr, self) |
|
2106 | nodemaputil.setup_persistent_nodemap(tr, self) | |
2093 | self._segmentfile = randomaccessfile.randomaccessfile( |
|
2107 | self._segmentfile = randomaccessfile.randomaccessfile( | |
2094 | self.opener, |
|
2108 | self.opener, |
@@ -403,7 +403,7 b" REVLOG_FILES_VOLATILE_EXT = (b'.n', b'.n" | |||||
403 | # some exception to the above matching |
|
403 | # some exception to the above matching | |
404 | # |
|
404 | # | |
405 | # XXX This is currently not in use because of issue6542 |
|
405 | # XXX This is currently not in use because of issue6542 | |
406 | EXCLUDED = re.compile(b'.*undo\.[^/]+\.(nd?|i)$') |
|
406 | EXCLUDED = re.compile(br'.*undo\.[^/]+\.(nd?|i)$') | |
407 |
|
407 | |||
408 |
|
408 | |||
409 | def is_revlog(f, kind, st): |
|
409 | def is_revlog(f, kind, st): | |
@@ -603,6 +603,7 b' class fncache:' | |||||
603 | # hence the encodedir/decodedir dance |
|
603 | # hence the encodedir/decodedir dance | |
604 | def __init__(self, vfs): |
|
604 | def __init__(self, vfs): | |
605 | self.vfs = vfs |
|
605 | self.vfs = vfs | |
|
606 | self._ignores = set() | |||
606 | self.entries = None |
|
607 | self.entries = None | |
607 | self._dirty = False |
|
608 | self._dirty = False | |
608 | # set of new additions to fncache |
|
609 | # set of new additions to fncache | |
@@ -687,7 +688,12 b' class fncache:' | |||||
687 | self.entries = None |
|
688 | self.entries = None | |
688 | self.addls = set() |
|
689 | self.addls = set() | |
689 |
|
690 | |||
|
691 | def addignore(self, fn): | |||
|
692 | self._ignores.add(fn) | |||
|
693 | ||||
690 | def add(self, fn): |
|
694 | def add(self, fn): | |
|
695 | if fn in self._ignores: | |||
|
696 | return | |||
691 | if self.entries is None: |
|
697 | if self.entries is None: | |
692 | self._load() |
|
698 | self._load() | |
693 | if fn not in self.entries: |
|
699 | if fn not in self.entries: |
@@ -105,7 +105,48 b' def _playback(' | |||||
105 | unlink=True, |
|
105 | unlink=True, | |
106 | checkambigfiles=None, |
|
106 | checkambigfiles=None, | |
107 | ): |
|
107 | ): | |
|
108 | """rollback a transaction : | |||
|
109 | - truncate files that have been appended to | |||
|
110 | - restore file backups | |||
|
111 | - delete temporary files | |||
|
112 | """ | |||
|
113 | backupfiles = [] | |||
|
114 | ||||
|
115 | def restore_one_backup(vfs, f, b, checkambig): | |||
|
116 | filepath = vfs.join(f) | |||
|
117 | backuppath = vfs.join(b) | |||
|
118 | try: | |||
|
119 | util.copyfile(backuppath, filepath, checkambig=checkambig) | |||
|
120 | backupfiles.append((vfs, b)) | |||
|
121 | except IOError as exc: | |||
|
122 | e_msg = stringutil.forcebytestr(exc) | |||
|
123 | report(_(b"failed to recover %s (%s)\n") % (f, e_msg)) | |||
|
124 | raise | |||
|
125 | ||||
|
126 | # gather all backup files that impact the store | |||
|
127 | # (we need this to detect files that are both backed up and truncated) | |||
|
128 | store_backup = {} | |||
|
129 | for entry in backupentries: | |||
|
130 | location, file_path, backup_path, cache = entry | |||
|
131 | vfs = vfsmap[location] | |||
|
132 | is_store = vfs.join(b'') == opener.join(b'') | |||
|
133 | if is_store and file_path and backup_path: | |||
|
134 | store_backup[file_path] = entry | |||
|
135 | copy_done = set() | |||
|
136 | ||||
|
137 | # truncate all file `f` to offset `o` | |||
108 | for f, o in sorted(dict(entries).items()): |
|
138 | for f, o in sorted(dict(entries).items()): | |
|
139 | # if we have a backup for `f`, we should restore it first and truncate | |||
|
140 | # the restored file | |||
|
141 | bck_entry = store_backup.get(f) | |||
|
142 | if bck_entry is not None: | |||
|
143 | location, file_path, backup_path, cache = bck_entry | |||
|
144 | checkambig = False | |||
|
145 | if checkambigfiles: | |||
|
146 | checkambig = (file_path, location) in checkambigfiles | |||
|
147 | restore_one_backup(opener, file_path, backup_path, checkambig) | |||
|
148 | copy_done.add(bck_entry) | |||
|
149 | # truncate the file to its pre-transaction size | |||
109 | if o or not unlink: |
|
150 | if o or not unlink: | |
110 | checkambig = checkambigfiles and (f, b'') in checkambigfiles |
|
151 | checkambig = checkambigfiles and (f, b'') in checkambigfiles | |
111 | try: |
|
152 | try: | |
@@ -124,45 +165,52 b' def _playback(' | |||||
124 | report(_(b"failed to truncate %s\n") % f) |
|
165 | report(_(b"failed to truncate %s\n") % f) | |
125 | raise |
|
166 | raise | |
126 | else: |
|
167 | else: | |
|
168 | # delete empty file | |||
127 | try: |
|
169 | try: | |
128 | opener.unlink(f) |
|
170 | opener.unlink(f) | |
129 | except FileNotFoundError: |
|
171 | except FileNotFoundError: | |
130 | pass |
|
172 | pass | |
131 |
|
173 | # restore backed up files and clean up temporary files | ||
132 | backupfiles = [] |
|
174 | for entry in backupentries: | |
133 | for l, f, b, c in backupentries: |
|
175 | if entry in copy_done: | |
|
176 | continue | |||
|
177 | l, f, b, c = entry | |||
134 | if l not in vfsmap and c: |
|
178 | if l not in vfsmap and c: | |
135 | report(b"couldn't handle %s: unknown cache location %s\n" % (b, l)) |
|
179 | report(b"couldn't handle %s: unknown cache location %s\n" % (b, l)) | |
136 | vfs = vfsmap[l] |
|
180 | vfs = vfsmap[l] | |
137 | try: |
|
181 | try: | |
|
182 | checkambig = checkambigfiles and (f, l) in checkambigfiles | |||
138 | if f and b: |
|
183 | if f and b: | |
139 | filepath = vfs.join(f) |
|
184 | restore_one_backup(vfs, f, b, checkambig) | |
140 | backuppath = vfs.join(b) |
|
|||
141 | checkambig = checkambigfiles and (f, l) in checkambigfiles |
|
|||
142 | try: |
|
|||
143 | util.copyfile(backuppath, filepath, checkambig=checkambig) |
|
|||
144 | backupfiles.append(b) |
|
|||
145 | except IOError as exc: |
|
|||
146 | e_msg = stringutil.forcebytestr(exc) |
|
|||
147 | report(_(b"failed to recover %s (%s)\n") % (f, e_msg)) |
|
|||
148 | else: |
|
185 | else: | |
149 | target = f or b |
|
186 | target = f or b | |
150 | try: |
|
187 | try: | |
151 | vfs.unlink(target) |
|
188 | vfs.unlink(target) | |
152 | except FileNotFoundError: |
|
189 | except FileNotFoundError: | |
|
190 | # This is fine because | |||
|
191 | # | |||
|
192 | # either we are trying to delete the main file, and it is | |||
|
193 | # already deleted. | |||
|
194 | # | |||
|
195 | # or we are trying to delete a temporary file and it is | |||
|
196 | # already deleted. | |||
|
197 | # | |||
|
198 | # in both case, our target result (delete the file) is | |||
|
199 | # already achieved. | |||
153 | pass |
|
200 | pass | |
154 | except (IOError, OSError, error.Abort): |
|
201 | except (IOError, OSError, error.Abort): | |
155 | if not c: |
|
202 | if not c: | |
156 | raise |
|
203 | raise | |
157 |
|
204 | |||
|
205 | # cleanup transaction state file and the backups file | |||
158 | backuppath = b"%s.backupfiles" % journal |
|
206 | backuppath = b"%s.backupfiles" % journal | |
159 | if opener.exists(backuppath): |
|
207 | if opener.exists(backuppath): | |
160 | opener.unlink(backuppath) |
|
208 | opener.unlink(backuppath) | |
161 | opener.unlink(journal) |
|
209 | opener.unlink(journal) | |
162 | try: |
|
210 | try: | |
163 | for f in backupfiles: |
|
211 | for vfs, f in backupfiles: | |
164 |
if |
|
212 | if vfs.exists(f): | |
165 |
|
|
213 | vfs.unlink(f) | |
166 | except (IOError, OSError, error.Abort): |
|
214 | except (IOError, OSError, error.Abort): | |
167 | # only pure backup file remains, it is sage to ignore any error |
|
215 | # only pure backup file remains, it is sage to ignore any error | |
168 | pass |
|
216 | pass | |
@@ -331,7 +379,7 b' class transaction(util.transactional):' | |||||
331 | self._file.flush() |
|
379 | self._file.flush() | |
332 |
|
380 | |||
333 | @active |
|
381 | @active | |
334 | def addbackup(self, file, hardlink=True, location=b''): |
|
382 | def addbackup(self, file, hardlink=True, location=b'', for_offset=False): | |
335 | """Adds a backup of the file to the transaction |
|
383 | """Adds a backup of the file to the transaction | |
336 |
|
384 | |||
337 | Calling addbackup() creates a hardlink backup of the specified file |
|
385 | Calling addbackup() creates a hardlink backup of the specified file | |
@@ -340,17 +388,25 b' class transaction(util.transactional):' | |||||
340 |
|
388 | |||
341 | * `file`: the file path, relative to .hg/store |
|
389 | * `file`: the file path, relative to .hg/store | |
342 | * `hardlink`: use a hardlink to quickly create the backup |
|
390 | * `hardlink`: use a hardlink to quickly create the backup | |
|
391 | ||||
|
392 | If `for_offset` is set, we expect a offset for this file to have been previously recorded | |||
343 | """ |
|
393 | """ | |
344 | if self._queue: |
|
394 | if self._queue: | |
345 | msg = b'cannot use transaction.addbackup inside "group"' |
|
395 | msg = b'cannot use transaction.addbackup inside "group"' | |
346 | raise error.ProgrammingError(msg) |
|
396 | raise error.ProgrammingError(msg) | |
347 |
|
397 | |||
348 | if ( |
|
398 | if file in self._newfiles or file in self._backupmap: | |
349 | file in self._newfiles |
|
399 | return | |
350 |
|
|
400 | elif file in self._offsetmap and not for_offset: | |
351 | or file in self._backupmap |
|
|||
352 | ): |
|
|||
353 | return |
|
401 | return | |
|
402 | elif for_offset and file not in self._offsetmap: | |||
|
403 | msg = ( | |||
|
404 | 'calling `addbackup` with `for_offmap=True`, ' | |||
|
405 | 'but no offset recorded: [%r] %r' | |||
|
406 | ) | |||
|
407 | msg %= (location, file) | |||
|
408 | raise error.ProgrammingError(msg) | |||
|
409 | ||||
354 | vfs = self._vfsmap[location] |
|
410 | vfs = self._vfsmap[location] | |
355 | dirname, filename = vfs.split(file) |
|
411 | dirname, filename = vfs.split(file) | |
356 | backupfilename = b"%s.backup.%s" % (self._journal, filename) |
|
412 | backupfilename = b"%s.backup.%s" % (self._journal, filename) |
@@ -327,7 +327,9 b' if has_https:' | |||||
327 | self.cert_file = cert_file |
|
327 | self.cert_file = cert_file | |
328 |
|
328 | |||
329 | def connect(self): |
|
329 | def connect(self): | |
330 |
self.sock = socket.create_connection( |
|
330 | self.sock = socket.create_connection( | |
|
331 | (self.host, self.port), self.timeout | |||
|
332 | ) | |||
331 |
|
333 | |||
332 | host = self.host |
|
334 | host = self.host | |
333 | realhostport = self.realhostport # pytype: disable=attribute-error |
|
335 | realhostport = self.realhostport # pytype: disable=attribute-error |
@@ -1,4 +1,4 b'' | |||||
1 |
= Mercurial 6.4 |
|
1 | = Mercurial 6.4 = | |
2 |
|
2 | |||
3 | == New Features == |
|
3 | == New Features == | |
4 |
|
4 | |||
@@ -90,6 +90,31 b' Aside from the following (unordered) com' | |||||
90 | * bundlerepo: apply phase data stored in the bundle instead of assuming `draft` |
|
90 | * bundlerepo: apply phase data stored in the bundle instead of assuming `draft` | |
91 | * config-item: declare undeclared path suboption |
|
91 | * config-item: declare undeclared path suboption | |
92 | * narrow: read pending file when applicable |
|
92 | * narrow: read pending file when applicable | |
|
93 | * rust: fix building on macOS (issue6801) | |||
|
94 | * run-tests: fix a crash when using the coverage options | |||
|
95 | * undo-files: also remove the undo.backupfiles | |||
|
96 | * undo-files: cleanup backup when cleaning undos | |||
|
97 | * undo-files: clean existing files up before writing new one | |||
|
98 | * undo-files: cleanup legacy files when applicable | |||
|
99 | * dirstate-v2: fix an incorrect handling of readdir errors | |||
|
100 | * rust: update zstd dependency | |||
|
101 | * rust: upgrade `rayon` dependency | |||
|
102 | * dirstate: fix the bug in [status] dealing with committed&ignored directories | |||
|
103 | * dirstate: fix a potential traceback when in `copy` and `rename` | |||
|
104 | * histedit: fix diff colors | |||
|
105 | * cext: fix for PyLong refactoring in CPython 3.12 | |||
|
106 | * py3: fix for Python 3.12 emitting SyntaxWarning on invalid escape sequences | |||
|
107 | * statprof: with Python 3.12, lineno is (more) often None | |||
|
108 | * transaction: properly clean up backup file outside of .hg/store/ | |||
|
109 | * transaction: raise on backup restoration error | |||
|
110 | * revlog: improve the robustness of the splitting process | |||
|
111 | * debugdeltachain: stop summing the same chain over and over | |||
|
112 | * url: don't ignore timeout for https connections | |||
|
113 | * py3: fix for Python 3.12 emitting SyntaxWarning on invalid escape sequences | |||
|
114 | * tests: accept a test output change in [tests/test-serve.t] | |||
|
115 | * rust: fix thread cap (for real this time) | |||
|
116 | * dirstate: try refreshing the changelog when parent are unknown | |||
|
117 | * hooks: invalidate the repo after the hooks | |||
93 |
|
118 | |||
94 | == Backwards Compatibility Changes == |
|
119 | == Backwards Compatibility Changes == | |
95 | * rust: upgrade supported Rust toolchain version |
|
120 | * rust: upgrade supported Rust toolchain version |
@@ -47,16 +47,10 b" pub fn status<'dirstate>(" | |||||
47 | options: StatusOptions, |
|
47 | options: StatusOptions, | |
48 | ) -> Result<(DirstateStatus<'dirstate>, Vec<PatternFileWarning>), StatusError> |
|
48 | ) -> Result<(DirstateStatus<'dirstate>, Vec<PatternFileWarning>), StatusError> | |
49 | { |
|
49 | { | |
50 | // Force the global rayon threadpool to not exceed 16 concurrent threads. |
|
50 | // Also cap for a Python caller of this function, but don't complain if | |
51 | // This is a stop-gap measure until we figure out why using more than 16 |
|
51 | // the global threadpool has already been set since this code path is also | |
52 | // threads makes `status` slower for each additional thread. |
|
52 | // being used by `rhg`, which calls this early. | |
53 | // We use `ok()` in case the global threadpool has already been |
|
53 | let _ = crate::utils::cap_default_rayon_threads(); | |
54 | // instantiated in `rhg` or some other caller. |
|
|||
55 | // TODO find the underlying cause and fix it, then remove this. |
|
|||
56 | rayon::ThreadPoolBuilder::new() |
|
|||
57 | .num_threads(16.min(rayon::current_num_threads())) |
|
|||
58 | .build_global() |
|
|||
59 | .ok(); |
|
|||
60 |
|
54 | |||
61 | let (ignore_fn, warnings, patterns_changed): (IgnoreFnType, _, _) = |
|
55 | let (ignore_fn, warnings, patterns_changed): (IgnoreFnType, _, _) = | |
62 | if options.list_ignored || options.list_unknown { |
|
56 | if options.list_ignored || options.list_unknown { |
@@ -498,3 +498,35 b' where' | |||||
498 | Err(e) => Some(Err(e)), |
|
498 | Err(e) => Some(Err(e)), | |
499 | }) |
|
499 | }) | |
500 | } |
|
500 | } | |
|
501 | ||||
|
502 | /// Force the global rayon threadpool to not exceed 16 concurrent threads | |||
|
503 | /// unless the user has specified a value. | |||
|
504 | /// This is a stop-gap measure until we figure out why using more than 16 | |||
|
505 | /// threads makes `status` slower for each additional thread. | |||
|
506 | /// | |||
|
507 | /// TODO find the underlying cause and fix it, then remove this. | |||
|
508 | /// | |||
|
509 | /// # Errors | |||
|
510 | /// | |||
|
511 | /// Returns an error if the global threadpool has already been initialized if | |||
|
512 | /// we try to initialize it. | |||
|
513 | pub fn cap_default_rayon_threads() -> Result<(), rayon::ThreadPoolBuildError> { | |||
|
514 | const THREAD_CAP: usize = 16; | |||
|
515 | ||||
|
516 | if std::env::var("RAYON_NUM_THREADS").is_err() { | |||
|
517 | let available_parallelism = std::thread::available_parallelism() | |||
|
518 | .map(usize::from) | |||
|
519 | .unwrap_or(1); | |||
|
520 | let new_thread_count = THREAD_CAP.min(available_parallelism); | |||
|
521 | let res = rayon::ThreadPoolBuilder::new() | |||
|
522 | .num_threads(new_thread_count) | |||
|
523 | .build_global(); | |||
|
524 | if res.is_ok() { | |||
|
525 | log::trace!( | |||
|
526 | "Capped the rayon threadpool to {new_thread_count} threads", | |||
|
527 | ); | |||
|
528 | } | |||
|
529 | return res; | |||
|
530 | } | |||
|
531 | Ok(()) | |||
|
532 | } |
@@ -140,6 +140,13 b' fn rhg_main(argv: Vec<OsString>) -> ! {' | |||||
140 |
|
140 | |||
141 | env_logger::init(); |
|
141 | env_logger::init(); | |
142 |
|
142 | |||
|
143 | // Make sure nothing in a future version of `rhg` sets the global | |||
|
144 | // threadpool before we can cap default threads. (This is also called | |||
|
145 | // in core because Python uses the same code path, we're adding a | |||
|
146 | // redundant check.) | |||
|
147 | hg::utils::cap_default_rayon_threads() | |||
|
148 | .expect("Rayon threadpool already initialized"); | |||
|
149 | ||||
143 | let early_args = EarlyArgs::parse(&argv); |
|
150 | let early_args = EarlyArgs::parse(&argv); | |
144 |
|
151 | |||
145 | let initial_current_dir = early_args.cwd.map(|cwd| { |
|
152 | let initial_current_dir = early_args.cwd.map(|cwd| { |
@@ -249,7 +249,7 b' commit was created, and status is now cl' | |||||
249 |
|
249 | |||
250 | The status process should return a consistent result and not crash. |
|
250 | The status process should return a consistent result and not crash. | |
251 |
|
251 | |||
252 | #if rust no-rhg dirstate-v2-append |
|
252 | #if no-rhg | |
253 | $ cat $TESTTMP/status-race-lock.out |
|
253 | $ cat $TESTTMP/status-race-lock.out | |
254 | A dir/o |
|
254 | A dir/o | |
255 | R dir/nested/m |
|
255 | R dir/nested/m | |
@@ -258,7 +258,7 b' The status process should return a consi' | |||||
258 | ? q |
|
258 | ? q | |
259 | $ cat $TESTTMP/status-race-lock.log |
|
259 | $ cat $TESTTMP/status-race-lock.log | |
260 | #else |
|
260 | #else | |
261 |
#if |
|
261 | #if pre-some-read dirstate-v2-append | |
262 | $ cat $TESTTMP/status-race-lock.out |
|
262 | $ cat $TESTTMP/status-race-lock.out | |
263 | A dir/o |
|
263 | A dir/o | |
264 | R dir/nested/m |
|
264 | R dir/nested/m | |
@@ -268,12 +268,10 b' The status process should return a consi' | |||||
268 | $ cat $TESTTMP/status-race-lock.log |
|
268 | $ cat $TESTTMP/status-race-lock.log | |
269 | #else |
|
269 | #else | |
270 | $ cat $TESTTMP/status-race-lock.out |
|
270 | $ cat $TESTTMP/status-race-lock.out | |
271 | M dir/o (no-rhg known-bad-output !) |
|
|||
272 |
|
|
271 | ? dir/n | |
273 | ? p |
|
272 | ? p | |
274 | ? q |
|
273 | ? q | |
275 | $ cat $TESTTMP/status-race-lock.log |
|
274 | $ cat $TESTTMP/status-race-lock.log | |
276 | warning: ignoring unknown working parent 02a67a77ee9b! (no-rhg !) |
|
|||
277 | #endif |
|
275 | #endif | |
278 | #endif |
|
276 | #endif | |
279 |
|
277 |
@@ -1423,3 +1423,41 b' HGPLAIN setting in hooks' | |||||
1423 | ### no ######## plain: <unset> |
|
1423 | ### no ######## plain: <unset> | |
1424 | ### auto ###### plain: 1 |
|
1424 | ### auto ###### plain: 1 | |
1425 | Mercurial Distributed SCM (*) (glob) |
|
1425 | Mercurial Distributed SCM (*) (glob) | |
|
1426 | ||||
|
1427 | Test hook that change the underlying repo | |||
|
1428 | ========================================= | |||
|
1429 | ||||
|
1430 | blackbox access the dirstate afterward and can see a changelog / dirstate | |||
|
1431 | desync. | |||
|
1432 | ||||
|
1433 | ||||
|
1434 | $ cd $TESTTMP | |||
|
1435 | $ cat <<EOF >> $HGRCPATH | |||
|
1436 | > [extensions] | |||
|
1437 | > blackbox= | |||
|
1438 | > [hooks] | |||
|
1439 | > post-merge = hg commit -m "auto merge" | |||
|
1440 | > EOF | |||
|
1441 | ||||
|
1442 | $ hg init t | |||
|
1443 | $ cd t | |||
|
1444 | $ touch ".hgignore" | |||
|
1445 | $ hg commit -Am "initial" -d'0 0' | |||
|
1446 | adding .hgignore | |||
|
1447 | ||||
|
1448 | $ echo This is file a1 > a | |||
|
1449 | $ hg commit -Am "commit #1" -d'0 0' | |||
|
1450 | adding a | |||
|
1451 | ||||
|
1452 | $ hg update 0 | |||
|
1453 | 0 files updated, 0 files merged, 1 files removed, 0 files unresolved | |||
|
1454 | $ echo This is file b1 > b | |||
|
1455 | $ hg commit -Am "commit #2" -d'0 0' | |||
|
1456 | adding b | |||
|
1457 | created new head | |||
|
1458 | ||||
|
1459 | $ hg merge 1 | |||
|
1460 | 1 files updated, 0 files merged, 0 files removed, 0 files unresolved | |||
|
1461 | (branch merge, don't forget to commit) | |||
|
1462 | ||||
|
1463 | $ cd .. |
@@ -55,7 +55,7 b' With -v and -p daytime' | |||||
55 | #if no-windows |
|
55 | #if no-windows | |
56 | $ KILLQUIETLY=Y |
|
56 | $ KILLQUIETLY=Y | |
57 | $ hgserve -p daytime |
|
57 | $ hgserve -p daytime | |
58 |
abort: cannot start server at 'localhost:13': |
|
58 | abort: cannot start server at 'localhost:13': $EACCES$ (?) | |
59 | abort: child process failed to start (?) |
|
59 | abort: child process failed to start (?) | |
60 | abort: no port number associated with service 'daytime' (?) |
|
60 | abort: no port number associated with service 'daytime' (?) | |
61 | listening at http://localhost/ (bound to $LOCALIP:13) (?) |
|
61 | listening at http://localhost/ (bound to $LOCALIP:13) (?) |
@@ -1,22 +1,103 b'' | |||||
1 | Test correctness of revlog inline -> non-inline transition |
|
1 | Test correctness of revlog inline -> non-inline transition | |
2 | ---------------------------------------------------------- |
|
2 | ---------------------------------------------------------- | |
3 |
|
3 | |||
4 |
Helper extension to intercept renames |
|
4 | Helper extension to intercept renames and kill process | |
5 |
|
5 | |||
6 | $ cat > $TESTTMP/intercept_rename.py << EOF |
|
6 | $ cat > $TESTTMP/intercept_before_rename.py << EOF | |
7 | > import os |
|
7 | > import os | |
8 |
> import s |
|
8 | > import signal | |
|
9 | > from mercurial import extensions, util | |||
|
10 | > | |||
|
11 | > def extsetup(ui): | |||
|
12 | > def rename(orig, src, dest, *args, **kwargs): | |||
|
13 | > path = util.normpath(dest) | |||
|
14 | > if path.endswith(b'data/file.i'): | |||
|
15 | > os.kill(os.getpid(), signal.SIGKILL) | |||
|
16 | > return orig(src, dest, *args, **kwargs) | |||
|
17 | > extensions.wrapfunction(util, 'rename', rename) | |||
|
18 | > EOF | |||
|
19 | ||||
|
20 | $ cat > $TESTTMP/intercept_after_rename.py << EOF | |||
|
21 | > import os | |||
|
22 | > import signal | |||
9 | > from mercurial import extensions, util |
|
23 | > from mercurial import extensions, util | |
10 | > |
|
24 | > | |
11 | > def extsetup(ui): |
|
25 | > def extsetup(ui): | |
12 | > def close(orig, *args, **kwargs): |
|
26 | > def close(orig, *args, **kwargs): | |
13 | > path = util.normpath(args[0]._atomictempfile__name) |
|
27 | > path = util.normpath(args[0]._atomictempfile__name) | |
|
28 | > r = orig(*args, **kwargs) | |||
14 | > if path.endswith(b'/.hg/store/data/file.i'): |
|
29 | > if path.endswith(b'/.hg/store/data/file.i'): | |
15 | > os._exit(80) |
|
30 | > os.kill(os.getpid(), signal.SIGKILL) | |
16 |
> return |
|
31 | > return r | |
17 | > extensions.wrapfunction(util.atomictempfile, 'close', close) |
|
32 | > extensions.wrapfunction(util.atomictempfile, 'close', close) | |
|
33 | > def extsetup(ui): | |||
|
34 | > def rename(orig, src, dest, *args, **kwargs): | |||
|
35 | > path = util.normpath(dest) | |||
|
36 | > r = orig(src, dest, *args, **kwargs) | |||
|
37 | > if path.endswith(b'data/file.i'): | |||
|
38 | > os.kill(os.getpid(), signal.SIGKILL) | |||
|
39 | > return r | |||
|
40 | > extensions.wrapfunction(util, 'rename', rename) | |||
18 | > EOF |
|
41 | > EOF | |
19 |
|
42 | |||
|
43 | $ cat > $TESTTMP/killme.py << EOF | |||
|
44 | > import os | |||
|
45 | > import signal | |||
|
46 | > | |||
|
47 | > def killme(ui, repo, hooktype, **kwargs): | |||
|
48 | > os.kill(os.getpid(), signal.SIGKILL) | |||
|
49 | > EOF | |||
|
50 | ||||
|
51 | $ cat > $TESTTMP/reader_wait_split.py << EOF | |||
|
52 | > import os | |||
|
53 | > import signal | |||
|
54 | > from mercurial import extensions, revlog, testing | |||
|
55 | > def _wait_post_load(orig, self, *args, **kwargs): | |||
|
56 | > wait = b'data/file' in self.radix | |||
|
57 | > if wait: | |||
|
58 | > testing.wait_file(b"$TESTTMP/writer-revlog-split") | |||
|
59 | > r = orig(self, *args, **kwargs) | |||
|
60 | > if wait: | |||
|
61 | > testing.write_file(b"$TESTTMP/reader-index-read") | |||
|
62 | > testing.wait_file(b"$TESTTMP/writer-revlog-unsplit") | |||
|
63 | > return r | |||
|
64 | > | |||
|
65 | > def extsetup(ui): | |||
|
66 | > extensions.wrapfunction(revlog.revlog, '_loadindex', _wait_post_load) | |||
|
67 | > EOF | |||
|
68 | ||||
|
69 | setup a repository for tests | |||
|
70 | ---------------------------- | |||
|
71 | ||||
|
72 | $ cat >> $HGRCPATH << EOF | |||
|
73 | > [format] | |||
|
74 | > revlog-compression=none | |||
|
75 | > EOF | |||
|
76 | ||||
|
77 | $ hg init troffset-computation | |||
|
78 | $ cd troffset-computation | |||
|
79 | $ printf '%20d' '1' > file | |||
|
80 | $ hg commit -Aqma | |||
|
81 | $ printf '%1024d' '1' > file | |||
|
82 | $ hg commit -Aqmb | |||
|
83 | $ printf '%20d' '1' > file | |||
|
84 | $ hg commit -Aqmc | |||
|
85 | $ dd if=/dev/zero of=file bs=1k count=128 > /dev/null 2>&1 | |||
|
86 | $ hg commit -AqmD --traceback | |||
|
87 | ||||
|
88 | Reference size: | |||
|
89 | $ f -s file | |||
|
90 | file: size=131072 | |||
|
91 | $ f -s .hg/store/data/file* | |||
|
92 | .hg/store/data/file.d: size=132139 | |||
|
93 | .hg/store/data/file.i: size=256 | |||
|
94 | ||||
|
95 | $ cd .. | |||
|
96 | ||||
|
97 | ||||
|
98 | Test a hard crash after the file was split but before the transaction was committed | |||
|
99 | =================================================================================== | |||
|
100 | ||||
20 | Test offset computation to correctly factor in the index entries themselves. |
|
101 | Test offset computation to correctly factor in the index entries themselves. | |
21 | Also test that the new data size has the correct size if the transaction is aborted |
|
102 | Also test that the new data size has the correct size if the transaction is aborted | |
22 | after the index has been replaced. |
|
103 | after the index has been replaced. | |
@@ -28,30 +109,19 b' and will transition to non-inline storag' | |||||
28 | If the transaction adding c, D is rolled back, then we don't undo the revlog split, |
|
109 | If the transaction adding c, D is rolled back, then we don't undo the revlog split, | |
29 | but truncate the index and the data to remove both c and D. |
|
110 | but truncate the index and the data to remove both c and D. | |
30 |
|
111 | |||
31 | $ hg init troffset-computation --config format.revlog-compression=none |
|
|||
32 | $ cd troffset-computation |
|
|||
33 | $ printf '%20d' '1' > file |
|
|||
34 | $ hg commit -Aqma |
|
|||
35 | $ printf '%1024d' '1' > file |
|
|||
36 | $ hg commit -Aqmb |
|
|||
37 | $ printf '%20d' '1' > file |
|
|||
38 | $ hg commit -Aqmc |
|
|||
39 | $ dd if=/dev/zero of=file bs=1k count=128 > /dev/null 2>&1 |
|
|||
40 | $ hg commit -AqmD |
|
|||
41 |
|
112 | |||
42 | $ cd .. |
|
113 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-copy | |
43 |
|
||||
44 | $ hg clone -r 1 troffset-computation troffset-computation-copy --config format.revlog-compression=none -q |
|
|||
45 | $ cd troffset-computation-copy |
|
114 | $ cd troffset-computation-copy | |
46 |
|
115 | |||
47 | Reference size: |
|
116 | Reference size: | |
48 |
|
117 | $ f -s file | ||
|
118 | file: size=1024 | |||
49 | $ f -s .hg/store/data/file* |
|
119 | $ f -s .hg/store/data/file* | |
50 | .hg/store/data/file.i: size=1174 |
|
120 | .hg/store/data/file.i: size=1174 | |
51 |
|
121 | |||
52 | $ cat > .hg/hgrc <<EOF |
|
122 | $ cat > .hg/hgrc <<EOF | |
53 | > [hooks] |
|
123 | > [hooks] | |
54 |
> pretxnchangegroup = python:$TEST |
|
124 | > pretxnchangegroup = python:$TESTTMP/killme.py:killme | |
55 | > EOF |
|
125 | > EOF | |
56 | #if chg |
|
126 | #if chg | |
57 | $ hg pull ../troffset-computation |
|
127 | $ hg pull ../troffset-computation | |
@@ -60,27 +130,38 b' Reference size:' | |||||
60 | #else |
|
130 | #else | |
61 | $ hg pull ../troffset-computation |
|
131 | $ hg pull ../troffset-computation | |
62 | pulling from ../troffset-computation |
|
132 | pulling from ../troffset-computation | |
63 | [80] |
|
133 | Killed | |
|
134 | [137] | |||
64 | #endif |
|
135 | #endif | |
65 | $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | tail -1 |
|
136 | ||
66 | data/file.i 128 |
|
137 | ||
|
138 | The inline revlog still exist, but a split version exist next to it | |||
|
139 | ||||
|
140 | $ f -s .hg/store/data/file* | |||
|
141 | .hg/store/data/file.d: size=132139 | |||
|
142 | .hg/store/data/file.i: size=132395 | |||
|
143 | .hg/store/data/file.i.s: size=256 | |||
|
144 | ||||
67 |
|
145 | |||
68 | The first file.i entry should match the "Reference size" above. |
|
146 | The first file.i entry should match the "Reference size" above. | |
69 | The first file.d entry is the temporary record during the split, |
|
147 | The first file.d entry is the temporary record during the split, | |
70 | the second entry after the split happened. The sum of the second file.d |
|
148 | ||
71 | and the second file.i entry should match the first file.i entry. |
|
149 | A "temporary file" entry exist for the split index. | |
72 |
|
150 | |||
73 | $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file |
|
151 | $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | |
74 | data/file.i 1174 |
|
152 | data/file.i 1174 | |
75 | data/file.d 0 |
|
153 | data/file.d 0 | |
76 | data/file.d 1046 |
|
154 | $ cat .hg/store/journal.backupfiles | tr -s '\000' ' ' | tr -s '\00' ' '| grep data/file | |
77 | data/file.i 128 |
|
155 | data/file.i data/journal.backup.file.i 0 | |
|
156 | data/file.i.s 0 | |||
|
157 | ||||
|
158 | recover is rolling the split back, the fncache is still valid | |||
|
159 | ||||
78 | $ hg recover |
|
160 | $ hg recover | |
79 | rolling back interrupted transaction |
|
161 | rolling back interrupted transaction | |
80 | (verify step skipped, run `hg verify` to check your repository content) |
|
162 | (verify step skipped, run `hg verify` to check your repository content) | |
81 | $ f -s .hg/store/data/file* |
|
163 | $ f -s .hg/store/data/file* | |
82 |
.hg/store/data/file. |
|
164 | .hg/store/data/file.i: size=1174 | |
83 | .hg/store/data/file.i: size=128 |
|
|||
84 | $ hg tip |
|
165 | $ hg tip | |
85 | changeset: 1:cfa8d6e60429 |
|
166 | changeset: 1:cfa8d6e60429 | |
86 | tag: tip |
|
167 | tag: tip | |
@@ -89,47 +170,67 b' and the second file.i entry should match' | |||||
89 | summary: b |
|
170 | summary: b | |
90 |
|
171 | |||
91 | $ hg verify -q |
|
172 | $ hg verify -q | |
92 | warning: revlog 'data/file.d' not in fncache! |
|
|||
93 | 1 warnings encountered! |
|
|||
94 | hint: run "hg debugrebuildfncache" to recover from corrupt fncache |
|
|||
95 | $ hg debugrebuildfncache --only-data |
|
173 | $ hg debugrebuildfncache --only-data | |
96 | adding data/file.d |
|
174 | fncache already up to date | |
97 | 1 items added, 0 removed from fncache |
|
|||
98 | $ hg verify -q |
|
175 | $ hg verify -q | |
99 | $ cd .. |
|
176 | $ cd .. | |
100 |
|
177 | |||
|
178 | Test a hard crash right before the index is move into place | |||
|
179 | =========================================================== | |||
101 |
|
180 | |||
102 | Now retry the procedure but intercept the rename of the index and check that |
|
181 | Now retry the procedure but intercept the rename of the index and check that | |
103 | the journal does not contain the new index size. This demonstrates the edge case |
|
182 | the journal does not contain the new index size. This demonstrates the edge case | |
104 | where the data file is left as garbage. |
|
183 | where the data file is left as garbage. | |
105 |
|
184 | |||
106 |
$ hg clone -r 1 troffset-computation troffset-computation-copy2 |
|
185 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-copy2 | |
107 | $ cd troffset-computation-copy2 |
|
186 | $ cd troffset-computation-copy2 | |
|
187 | ||||
|
188 | Reference size: | |||
|
189 | $ f -s file | |||
|
190 | file: size=1024 | |||
|
191 | $ f -s .hg/store/data/file* | |||
|
192 | .hg/store/data/file.i: size=1174 | |||
|
193 | ||||
108 | $ cat > .hg/hgrc <<EOF |
|
194 | $ cat > .hg/hgrc <<EOF | |
109 | > [extensions] |
|
195 | > [extensions] | |
110 | > intercept_rename = $TESTTMP/intercept_rename.py |
|
196 | > intercept_rename = $TESTTMP/intercept_before_rename.py | |
111 | > [hooks] |
|
|||
112 | > pretxnchangegroup = python:$TESTDIR/helper-killhook.py:killme |
|
|||
113 | > EOF |
|
197 | > EOF | |
114 | #if chg |
|
198 | #if chg | |
115 | $ hg pull ../troffset-computation |
|
199 | $ hg pull ../troffset-computation | |
116 | pulling from ../troffset-computation |
|
200 | pulling from ../troffset-computation | |
|
201 | searching for changes | |||
|
202 | adding changesets | |||
|
203 | adding manifests | |||
|
204 | adding file changes | |||
117 | [255] |
|
205 | [255] | |
118 | #else |
|
206 | #else | |
119 | $ hg pull ../troffset-computation |
|
207 | $ hg pull ../troffset-computation | |
120 | pulling from ../troffset-computation |
|
208 | pulling from ../troffset-computation | |
121 | [80] |
|
209 | searching for changes | |
|
210 | adding changesets | |||
|
211 | adding manifests | |||
|
212 | adding file changes | |||
|
213 | Killed | |||
|
214 | [137] | |||
122 | #endif |
|
215 | #endif | |
|
216 | ||||
|
217 | The inline revlog still exist, but a split version exist next to it | |||
|
218 | ||||
|
219 | $ f -s .hg/store/data/file* | |||
|
220 | .hg/store/data/file.d: size=132139 | |||
|
221 | .hg/store/data/file.i: size=132395 | |||
|
222 | .hg/store/data/file.i.s: size=256 | |||
|
223 | ||||
123 | $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file |
|
224 | $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | |
124 | data/file.i 1174 |
|
225 | data/file.i 1174 | |
125 | data/file.d 0 |
|
226 | data/file.d 0 | |
126 | data/file.d 1046 |
|
227 | ||
|
228 | recover is rolling the split back, the fncache is still valid | |||
127 |
|
229 | |||
128 | $ hg recover |
|
230 | $ hg recover | |
129 | rolling back interrupted transaction |
|
231 | rolling back interrupted transaction | |
130 | (verify step skipped, run `hg verify` to check your repository content) |
|
232 | (verify step skipped, run `hg verify` to check your repository content) | |
131 | $ f -s .hg/store/data/file* |
|
233 | $ f -s .hg/store/data/file* | |
132 | .hg/store/data/file.d: size=1046 |
|
|||
133 | .hg/store/data/file.i: size=1174 |
|
234 | .hg/store/data/file.i: size=1174 | |
134 | $ hg tip |
|
235 | $ hg tip | |
135 | changeset: 1:cfa8d6e60429 |
|
236 | changeset: 1:cfa8d6e60429 | |
@@ -141,10 +242,77 b' where the data file is left as garbage.' | |||||
141 | $ hg verify -q |
|
242 | $ hg verify -q | |
142 | $ cd .. |
|
243 | $ cd .. | |
143 |
|
244 | |||
|
245 | Test a hard crash right after the index is move into place | |||
|
246 | =========================================================== | |||
|
247 | ||||
|
248 | Now retry the procedure but intercept the rename of the index. | |||
|
249 | ||||
|
250 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-crash-after-rename | |||
|
251 | $ cd troffset-computation-crash-after-rename | |||
|
252 | ||||
|
253 | Reference size: | |||
|
254 | $ f -s file | |||
|
255 | file: size=1024 | |||
|
256 | $ f -s .hg/store/data/file* | |||
|
257 | .hg/store/data/file.i: size=1174 | |||
|
258 | ||||
|
259 | $ cat > .hg/hgrc <<EOF | |||
|
260 | > [extensions] | |||
|
261 | > intercept_rename = $TESTTMP/intercept_after_rename.py | |||
|
262 | > EOF | |||
|
263 | #if chg | |||
|
264 | $ hg pull ../troffset-computation | |||
|
265 | pulling from ../troffset-computation | |||
|
266 | searching for changes | |||
|
267 | adding changesets | |||
|
268 | adding manifests | |||
|
269 | adding file changes | |||
|
270 | [255] | |||
|
271 | #else | |||
|
272 | $ hg pull ../troffset-computation | |||
|
273 | pulling from ../troffset-computation | |||
|
274 | searching for changes | |||
|
275 | adding changesets | |||
|
276 | adding manifests | |||
|
277 | adding file changes | |||
|
278 | Killed | |||
|
279 | [137] | |||
|
280 | #endif | |||
|
281 | ||||
|
282 | The inline revlog was over written on disk | |||
|
283 | ||||
|
284 | $ f -s .hg/store/data/file* | |||
|
285 | .hg/store/data/file.d: size=132139 | |||
|
286 | .hg/store/data/file.i: size=256 | |||
|
287 | ||||
|
288 | $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | |||
|
289 | data/file.i 1174 | |||
|
290 | data/file.d 0 | |||
|
291 | ||||
|
292 | recover is rolling the split back, the fncache is still valid | |||
|
293 | ||||
|
294 | $ hg recover | |||
|
295 | rolling back interrupted transaction | |||
|
296 | (verify step skipped, run `hg verify` to check your repository content) | |||
|
297 | $ f -s .hg/store/data/file* | |||
|
298 | .hg/store/data/file.i: size=1174 | |||
|
299 | $ hg tip | |||
|
300 | changeset: 1:cfa8d6e60429 | |||
|
301 | tag: tip | |||
|
302 | user: test | |||
|
303 | date: Thu Jan 01 00:00:00 1970 +0000 | |||
|
304 | summary: b | |||
|
305 | ||||
|
306 | $ hg verify -q | |||
|
307 | $ cd .. | |||
|
308 | ||||
|
309 | Have the transaction rollback itself without any hard crash | |||
|
310 | =========================================================== | |||
|
311 | ||||
144 |
|
312 | |||
145 | Repeat the original test but let hg rollback the transaction. |
|
313 | Repeat the original test but let hg rollback the transaction. | |
146 |
|
314 | |||
147 |
$ hg clone -r 1 troffset-computation troffset-computation-copy-rb |
|
315 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-copy-rb | |
148 | $ cd troffset-computation-copy-rb |
|
316 | $ cd troffset-computation-copy-rb | |
149 | $ cat > .hg/hgrc <<EOF |
|
317 | $ cat > .hg/hgrc <<EOF | |
150 | > [hooks] |
|
318 | > [hooks] | |
@@ -160,9 +328,14 b' Repeat the original test but let hg roll' | |||||
160 | rollback completed |
|
328 | rollback completed | |
161 | abort: pretxnchangegroup hook exited with status 1 |
|
329 | abort: pretxnchangegroup hook exited with status 1 | |
162 | [40] |
|
330 | [40] | |
|
331 | ||||
|
332 | The split was rollback | |||
|
333 | ||||
163 | $ f -s .hg/store/data/file* |
|
334 | $ f -s .hg/store/data/file* | |
164 |
.hg/store/data/file.d: size= |
|
335 | .hg/store/data/file.d: size=0 | |
165 |
.hg/store/data/file.i: size=1 |
|
336 | .hg/store/data/file.i: size=1174 | |
|
337 | ||||
|
338 | ||||
166 | $ hg tip |
|
339 | $ hg tip | |
167 | changeset: 1:cfa8d6e60429 |
|
340 | changeset: 1:cfa8d6e60429 | |
168 | tag: tip |
|
341 | tag: tip | |
@@ -171,8 +344,85 b' Repeat the original test but let hg roll' | |||||
171 | summary: b |
|
344 | summary: b | |
172 |
|
345 | |||
173 | $ hg verify -q |
|
346 | $ hg verify -q | |
174 | warning: revlog 'data/file.d' not in fncache! |
|
|||
175 | 1 warnings encountered! |
|
|||
176 | hint: run "hg debugrebuildfncache" to recover from corrupt fncache |
|
|||
177 | $ cd .. |
|
347 | $ cd .. | |
178 |
|
348 | |||
|
349 | Read race | |||
|
350 | ========= | |||
|
351 | ||||
|
352 | We check that a client that started reading a revlog (its index) after the | |||
|
353 | split and end reading (the data) after the rollback should be fine | |||
|
354 | ||||
|
355 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-race | |||
|
356 | $ cd troffset-computation-race | |||
|
357 | $ cat > .hg/hgrc <<EOF | |||
|
358 | > [hooks] | |||
|
359 | > pretxnchangegroup=$RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/reader-index-read $TESTTMP/writer-revlog-split | |||
|
360 | > pretxnclose = false | |||
|
361 | > EOF | |||
|
362 | ||||
|
363 | start a reader | |||
|
364 | ||||
|
365 | $ hg cat --rev 0 file \ | |||
|
366 | > --config "extensions.wait_read=$TESTTMP/reader_wait_split.py" \ | |||
|
367 | > 2> $TESTTMP/reader.stderr \ | |||
|
368 | > > $TESTTMP/reader.stdout & | |||
|
369 | ||||
|
370 | Do a failed pull in // | |||
|
371 | ||||
|
372 | $ hg pull ../troffset-computation | |||
|
373 | pulling from ../troffset-computation | |||
|
374 | searching for changes | |||
|
375 | adding changesets | |||
|
376 | adding manifests | |||
|
377 | adding file changes | |||
|
378 | transaction abort! | |||
|
379 | rollback completed | |||
|
380 | abort: pretxnclose hook exited with status 1 | |||
|
381 | [40] | |||
|
382 | $ touch $TESTTMP/writer-revlog-unsplit | |||
|
383 | $ wait | |||
|
384 | ||||
|
385 | The reader should be fine | |||
|
386 | $ cat $TESTTMP/reader.stderr | |||
|
387 | $ cat $TESTTMP/reader.stdout | |||
|
388 | 1 (no-eol) | |||
|
389 | $ cd .. | |||
|
390 | ||||
|
391 | pending hooks | |||
|
392 | ============= | |||
|
393 | ||||
|
394 | We checks that hooks properly see the inside of the transaction, while other process don't. | |||
|
395 | ||||
|
396 | $ hg clone --quiet --rev 1 troffset-computation troffset-computation-hooks | |||
|
397 | $ cd troffset-computation-hooks | |||
|
398 | $ cat > .hg/hgrc <<EOF | |||
|
399 | > [hooks] | |||
|
400 | > pretxnclose.01-echo = hg cat -r 'max(all())' file | f --size | |||
|
401 | > pretxnclose.02-echo = $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/hook-done $TESTTMP/hook-tr-ready | |||
|
402 | > pretxnclose.03-abort = false | |||
|
403 | > EOF | |||
|
404 | ||||
|
405 | $ ( | |||
|
406 | > $RUNTESTDIR/testlib/wait-on-file 5 $TESTTMP/hook-tr-ready;\ | |||
|
407 | > hg cat -r 'max(all())' file | f --size;\ | |||
|
408 | > touch $TESTTMP/hook-done | |||
|
409 | > ) >stdout 2>stderr & | |||
|
410 | ||||
|
411 | $ hg pull ../troffset-computation | |||
|
412 | pulling from ../troffset-computation | |||
|
413 | searching for changes | |||
|
414 | adding changesets | |||
|
415 | adding manifests | |||
|
416 | adding file changes | |||
|
417 | size=131072 | |||
|
418 | transaction abort! | |||
|
419 | rollback completed | |||
|
420 | abort: pretxnclose.03-abort hook exited with status 1 | |||
|
421 | [40] | |||
|
422 | ||||
|
423 | $ cat stdout | |||
|
424 | size=1024 | |||
|
425 | $ cat stderr | |||
|
426 | ||||
|
427 | ||||
|
428 | $ cd .. |
1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now