##// END OF EJS Templates
branching: merge stable into default
Raphaël Gomès -
r51256:64cdd80d merge default
parent child Browse files
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 trindex is None:
2088 if side_write:
2067 trindex = 0
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 # the index fails, we should remove the datafile. It is more
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 opener.exists(f):
212 if vfs.exists(f):
165 opener.unlink(f)
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 or file in self._offsetmap
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((self.host, self.port))
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.4rc0 =
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 rhg pre-some-read dirstate-v2-append
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 ? dir/n
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': Permission denied (?)
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 sys
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 orig(*args, **kwargs)
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:$TESTDIR/helper-killhook.py:killme
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.d: size=1046
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 --config format.revlog-compression=none -q
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 --config format.revlog-compression=none -q
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=1046
335 .hg/store/data/file.d: size=0
165 .hg/store/data/file.i: size=128
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