Mercurial > hg-stable
changeset 51558:0239ebdd0740
branching: merge stable into default
author | Raphaël Gomès <rgomes@octobus.net> |
---|---|
date | Fri, 15 Mar 2024 10:52:51 +0100 |
parents | 79a7616a82b8 (current diff) 4976aacae424 (diff) |
children | 16d93adddce7 |
files | mercurial/branchmap.py mercurial/localrepo.py tests/test-branches.t |
diffstat | 25 files changed, 426 insertions(+), 310 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgsigs Sun Mar 10 03:29:12 2024 +0100 +++ b/.hgsigs Fri Mar 15 10:52:51 2024 +0100 @@ -257,3 +257,4 @@ 136902b3a95db38854ebaf5198a627641065c2ea 0 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
--- a/.hgtags Sun Mar 10 03:29:12 2024 +0100 +++ b/.hgtags Fri Mar 15 10:52:51 2024 +0100 @@ -273,3 +273,4 @@ 136902b3a95db38854ebaf5198a627641065c2ea 6.6.2 3fd1efb3ad124e6686c0fb66e6943cd8aeea5681 6.6.3 d1d48d18db37106b801ef6cb90955536458e7ffc 6.7rc0 +c9ceb4f6025690167bdb245e530de6bac8baae95 6.7
--- a/hgext/chainsaw.py Sun Mar 10 03:29:12 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,231 +0,0 @@ -# chainsaw.py -# -# Copyright 2022 Georges Racinet <georges.racinet@octobus.net> -# -# This software may be used and distributed according to the terms of the -# GNU General Public License version 2 or any later version. -"""chainsaw is a collection of single-minded and dangerous tools. (EXPERIMENTAL) - - "Don't use a chainsaw to cut your food!" - -The chainsaw extension provides commands that are so much geared towards a -specific use case in a specific context or environment that they are totally -inappropriate and **really dangerous** in other contexts. - -The help text of each command explicitly summarizes its context of application -and the wanted end result. - -It is recommended to run these commands with the ``HGPLAIN`` environment -variable (see :hg:`help scripting`). -""" - -import shutil - -from mercurial.i18n import _ -from mercurial import ( - cmdutil, - commands, - error, - localrepo, - registrar, -) -from mercurial.utils import ( - urlutil, -) - -cmdtable = {} -command = registrar.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for -# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should -# be specifying the version(s) of Mercurial they are tested with, or -# leave the attribute unspecified. -testedwith = b'ships-with-hg-core' - - -@command( - b'admin::chainsaw-update', - [ - ( - b'', - b'purge-unknown', - True, - _( - b'Remove unversioned files before update. Disabling this can ' - b'in some cases interfere with the update.' - b'See also :hg:`purge`.' - ), - ), - ( - b'', - b'purge-ignored', - True, - _( - b'Remove ignored files before update. Disable this for ' - b'instance to reuse previous compiler object files. ' - b'See also :hg:`purge`.' - ), - ), - ( - b'', - b'rev', - b'', - _(b'revision to update to'), - ), - ( - b'', - b'source', - b'', - _(b'repository to clone from'), - ), - ( - b'', - b'dest', - b'', - _(b'repository to update to REV (possibly cloning)'), - ), - ( - b'', - b'initial-clone-minimal', - False, - _( - b'Pull only the prescribed revision upon initial cloning. ' - b'This has the side effect of ignoring clone-bundles, ' - b'which if often slower on the client side and stressful ' - b'to the server than applying available clone bundles.' - ), - ), - ], - _( - b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE --dest DEST' - ), - helpbasic=True, - norepo=True, -) -def update(ui, **opts): - """pull and update to a given revision, no matter what, (EXPERIMENTAL) - - Context of application: *some* Continuous Integration (CI) systems, - packaging or deployment tools. - - Wanted end result: local repository at the given REPO_PATH, having the - latest changes to the given revision and with a clean working directory - updated at the given revision. - - chainsaw-update pulls from one source, then updates the working directory - to the given revision, overcoming anything that would stand in the way. - - By default, it will: - - - clone if the local repo does not exist yet, **removing any directory - at the given path** that would not be a Mercurial repository. - The initial clone is full by default, so that clonebundles can be - applied. Use the --initial-clone-minimal flag to avoid this. - - break locks if needed, leading to possible corruption if there - is a concurrent write access. - - perform recovery actions if needed - - revert any local modification. - - purge unknown and ignored files. - - go as far as to reclone if everything else failed (not implemented yet). - - DO NOT use it for anything else than performing a series - of unattended updates, with full exclusive repository access each time - and without any other local work than running build scripts. - In case the local repository is a share (see :hg:`help share`), exclusive - write access to the share source is also mandatory. - - It is recommended to run these commands with the ``HGPLAIN`` environment - variable (see :hg:`scripting`). - - Motivation: in Continuous Integration and Delivery systems (CI/CD), the - occasional remnant or bogus lock are common sources of waste of time (both - working time and calendar time). CI/CD scripts tend to grow with counter- - measures, often done in urgency. Also, whilst it is neat to keep - repositories from one job to the next (especially with large - repositories), an exceptional recloning is better than missing a release - deadline. - """ - rev = opts['rev'] - source = opts['source'] - repo_path = opts['dest'] - if not rev: - raise error.InputError(_(b'specify a target revision with --rev')) - if not source: - raise error.InputError(_(b'specify a pull path with --source')) - if not repo_path: - raise error.InputError(_(b'specify a repo path with --dest')) - repo_path = urlutil.urllocalpath(repo_path) - - try: - repo = localrepo.instance(ui, repo_path, create=False) - repo_created = False - ui.status(_(b'loaded repository at "%s"\n' % repo_path)) - except error.RepoError: - try: - shutil.rmtree(repo_path) - except FileNotFoundError: - ui.status(_(b'no such directory: "%s"\n' % repo_path)) - else: - ui.status( - _( - b'removed non-repository file or directory ' - b'at "%s"' % repo_path - ) - ) - - ui.status(_(b'creating repository at "%s"\n' % repo_path)) - repo = localrepo.instance(ui, repo_path, create=True) - repo_created = True - - if repo.svfs.tryunlink(b'lock'): - ui.status(_(b'had to break store lock\n')) - if repo.vfs.tryunlink(b'wlock'): - ui.status(_(b'had to break working copy lock\n')) - # If another process relock after the breacking above, the next locking - # will have to wait. - with repo.wlock(), repo.lock(): - ui.status(_(b'recovering after interrupted transaction, if any\n')) - repo.recover() - - ui.status(_(b'pulling from %s\n') % source) - if repo_created and not opts.get('initial_clone_minimal'): - pull_revs = [] - else: - pull_revs = [rev] - overrides = {(b'ui', b'quiet'): True} - with repo.ui.configoverride(overrides, b'chainsaw-update'): - pull = cmdutil.findcmd(b'pull', commands.table)[1][0] - ret = pull( - repo.ui, - repo, - source, - rev=pull_revs, - remote_hidden=False, - ) - if ret: - return ret - - purge = cmdutil.findcmd(b'purge', commands.table)[1][0] - ret = purge( - ui, - repo, - dirs=True, - all=opts.get('purge_ignored'), - files=opts.get('purge_unknown'), - confirm=False, - ) - if ret: - return ret - - ui.status(_(b'updating to revision \'%s\'\n') % rev) - update = cmdutil.findcmd(b'update', commands.table)[1][0] - ret = update(ui, repo, rev=rev, clean=True) - if ret: - return ret - - ui.status( - _( - b'chainsaw-update to revision \'%s\' ' - b'for repository at \'%s\' done\n' - ) - % (rev, repo.root) - )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/admin/chainsaw.py Fri Mar 15 10:52:51 2024 +0100 @@ -0,0 +1,226 @@ +# chainsaw.py +# +# Copyright 2022 Georges Racinet <georges.racinet@octobus.net> +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. +"""chainsaw is a collection of single-minded and dangerous tools. (EXPERIMENTAL) + + "Don't use a chainsaw to cut your food!" + +The chainsaw is a collection of commands that are so much geared towards a +specific use case in a specific context or environment that they are totally +inappropriate and **really dangerous** in other contexts. + +The help text of each command explicitly summarizes its context of application +and the wanted end result. + +It is recommended to run these commands with the ``HGPLAIN`` environment +variable (see :hg:`help scripting`). +""" + +import shutil + +from ..i18n import _ +from .. import ( + cmdutil, + commands, + error, + localrepo, + registrar, +) +from ..utils import ( + urlutil, +) + +cmdtable = {} +command = registrar.command(cmdtable) + + +@command( + b'admin::chainsaw-update', + [ + ( + b'', + b'purge-unknown', + True, + _( + b'Remove unversioned files before update. Disabling this can ' + b'in some cases interfere with the update.' + b'See also :hg:`purge`.' + ), + ), + ( + b'', + b'purge-ignored', + True, + _( + b'Remove ignored files before update. Disable this for ' + b'instance to reuse previous compiler object files. ' + b'See also :hg:`purge`.' + ), + ), + ( + b'', + b'rev', + b'', + _(b'revision to update to'), + ), + ( + b'', + b'source', + b'', + _(b'repository to clone from'), + ), + ( + b'', + b'dest', + b'', + _(b'repository to update to REV (possibly cloning)'), + ), + ( + b'', + b'initial-clone-minimal', + False, + _( + b'Pull only the prescribed revision upon initial cloning. ' + b'This has the side effect of ignoring clone-bundles, ' + b'which if often slower on the client side and stressful ' + b'to the server than applying available clone bundles.' + ), + ), + ], + _( + b'hg admin::chainsaw-update [OPTION] --rev REV --source SOURCE --dest DEST' + ), + helpbasic=True, + norepo=True, +) +def update(ui, **opts): + """pull and update to a given revision, no matter what, (EXPERIMENTAL) + + Context of application: *some* Continuous Integration (CI) systems, + packaging or deployment tools. + + Wanted end result: local repository at the given REPO_PATH, having the + latest changes to the given revision and with a clean working directory + updated at the given revision. + + chainsaw-update pulls from one source, then updates the working directory + to the given revision, overcoming anything that would stand in the way. + + By default, it will: + + - clone if the local repo does not exist yet, **removing any directory + at the given path** that would not be a Mercurial repository. + The initial clone is full by default, so that clonebundles can be + applied. Use the --initial-clone-minimal flag to avoid this. + - break locks if needed, leading to possible corruption if there + is a concurrent write access. + - perform recovery actions if needed + - revert any local modification. + - purge unknown and ignored files. + - go as far as to reclone if everything else failed (not implemented yet). + + DO NOT use it for anything else than performing a series + of unattended updates, with full exclusive repository access each time + and without any other local work than running build scripts. + In case the local repository is a share (see :hg:`help share`), exclusive + write access to the share source is also mandatory. + + It is recommended to run these commands with the ``HGPLAIN`` environment + variable (see :hg:`scripting`). + + Motivation: in Continuous Integration and Delivery systems (CI/CD), the + occasional remnant or bogus lock are common sources of waste of time (both + working time and calendar time). CI/CD scripts tend to grow with counter- + measures, often done in urgency. Also, whilst it is neat to keep + repositories from one job to the next (especially with large + repositories), an exceptional recloning is better than missing a release + deadline. + """ + rev = opts['rev'] + source = opts['source'] + repo_path = opts['dest'] + if not rev: + raise error.InputError(_(b'specify a target revision with --rev')) + if not source: + raise error.InputError(_(b'specify a pull path with --source')) + if not repo_path: + raise error.InputError(_(b'specify a repo path with --dest')) + repo_path = urlutil.urllocalpath(repo_path) + + try: + repo = localrepo.instance(ui, repo_path, create=False) + repo_created = False + ui.status(_(b'loaded repository at "%s"\n' % repo_path)) + except error.RepoError: + try: + shutil.rmtree(repo_path) + except FileNotFoundError: + ui.status(_(b'no such directory: "%s"\n' % repo_path)) + else: + ui.status( + _( + b'removed non-repository file or directory ' + b'at "%s"' % repo_path + ) + ) + + ui.status(_(b'creating repository at "%s"\n' % repo_path)) + repo = localrepo.instance(ui, repo_path, create=True) + repo_created = True + + if repo.svfs.tryunlink(b'lock'): + ui.status(_(b'had to break store lock\n')) + if repo.vfs.tryunlink(b'wlock'): + ui.status(_(b'had to break working copy lock\n')) + # If another process relock after the breacking above, the next locking + # will have to wait. + with repo.wlock(), repo.lock(): + ui.status(_(b'recovering after interrupted transaction, if any\n')) + repo.recover() + + ui.status(_(b'pulling from %s\n') % source) + if repo_created and not opts.get('initial_clone_minimal'): + pull_revs = [] + else: + pull_revs = [rev] + overrides = {(b'ui', b'quiet'): True} + with repo.ui.configoverride(overrides, b'chainsaw-update'): + pull = cmdutil.findcmd(b'pull', commands.table)[1][0] + ret = pull( + repo.ui, + repo, + source, + rev=pull_revs, + remote_hidden=False, + ) + if ret: + return ret + + purge = cmdutil.findcmd(b'purge', commands.table)[1][0] + ret = purge( + ui, + repo, + dirs=True, + all=opts.get('purge_ignored'), + files=opts.get('purge_unknown'), + confirm=False, + ) + if ret: + return ret + + ui.status(_(b'updating to revision \'%s\'\n') % rev) + update = cmdutil.findcmd(b'update', commands.table)[1][0] + ret = update(ui, repo, rev=rev, clean=True) + if ret: + return ret + + ui.status( + _( + b'chainsaw-update to revision \'%s\' ' + b'for repository at \'%s\' done\n' + ) + % (rev, repo.root) + )
--- a/mercurial/admin_commands.py Sun Mar 10 03:29:12 2024 +0100 +++ b/mercurial/admin_commands.py Fri Mar 15 10:52:51 2024 +0100 @@ -6,11 +6,12 @@ # GNU General Public License version 2 or any later version. from .i18n import _ -from .admin import verify +from .admin import chainsaw, verify from . import error, registrar, transaction table = {} +table.update(chainsaw.command._table) command = registrar.command(table)
--- a/mercurial/branchmap.py Sun Mar 10 03:29:12 2024 +0100 +++ b/mercurial/branchmap.py Fri Mar 15 10:52:51 2024 +0100 @@ -880,7 +880,7 @@ if self._names: try: - if repo.ui.configbool(b'format', b'mmap-revbranchcache'): + if repo.ui.configbool(b'storage', b'revbranchcache.mmap'): with repo.cachevfs(_rbcrevs) as fp: data = util.buffer(util.mmapread(fp)) else:
--- a/mercurial/configitems.toml Sun Mar 10 03:29:12 2024 +0100 +++ b/mercurial/configitems.toml Fri Mar 15 10:52:51 2024 +0100 @@ -583,6 +583,11 @@ [[items]] section = "devel" +name = "lock-wait-sync-file" +default = "" + +[[items]] +section = "devel" name = "persistent-nodemap" default = false documentation = """When true, revlogs use a special reference version of the \ @@ -1160,6 +1165,19 @@ default = false generic = true + +# The format section is dedicated to control of the repository on disk format +# and constraints. +# +# A format change affects which data is expected to be stored in the repository +# and how. It impacts other client whichever their version are, format change +# often comes with an associated entry in the requirements. +# +# The option are usually in the form `use-xxx-yyy` (with xxx-yy the feature name). +# +# To configure details of how the repository is accessed, without affect the +# repository formats, see the `storage section`. + [[items]] section = "format" name = "bookmarks-in-store" @@ -2102,6 +2120,15 @@ default = true experimental = true + +# The "storage" section house config options that change how the repository +# data are accessed by the current process but does not affects the on disk +# format. They can also adjust how the storage is computed, but without affect +# compatibility wither other clients. +# +# For deeper format change, see the `format` section. + + [[items]] section = "storage" name = "dirstate-v2.slow-path" @@ -2110,6 +2137,11 @@ [[items]] section = "storage" +name = "revbranchcache.mmap" +default = true + +[[items]] +section = "storage" name = "new-repo-backend" default = "revlogv1" experimental = true @@ -2913,8 +2945,3 @@ name = "date-format" default = "" in_core_extension = "blackbox" - -[[items]] -section = "format" -name = "mmap-revbranchcache" -default = false
--- a/mercurial/crecord.py Sun Mar 10 03:29:12 2024 +0100 +++ b/mercurial/crecord.py Fri Mar 15 10:52:51 2024 +0100 @@ -1563,7 +1563,6 @@ def sigwinchhandler(self, n, frame): """handle window resizing""" try: - curses.endwin() self.xscreensize, self.yscreensize = scmutil.termsize(self.ui) self.statuswin.resize(self.numstatuslines, self.xscreensize) self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1 @@ -1694,7 +1693,6 @@ curses.raw() curses.def_prog_mode() - curses.endwin() self.commenttext = self.ui.edit(self.commenttext, self.ui.username()) curses.cbreak() self.stdscr.refresh()
--- a/mercurial/localrepo.py Sun Mar 10 03:29:12 2024 +0100 +++ b/mercurial/localrepo.py Fri Mar 15 10:52:51 2024 +0100 @@ -3072,6 +3072,9 @@ warntimeout = self.ui.configint(b"ui", b"timeout.warn") # internal config: ui.signal-safe-lock signalsafe = self.ui.configbool(b'ui', b'signal-safe-lock') + sync_file = self.ui.config(b'devel', b'lock-wait-sync-file') + if not sync_file: + sync_file = None l = lockmod.trylock( self.ui, @@ -3083,6 +3086,7 @@ acquirefn=acquirefn, desc=desc, signalsafe=signalsafe, + devel_wait_sync_file=sync_file, ) return l
--- a/mercurial/lock.py Sun Mar 10 03:29:12 2024 +0100 +++ b/mercurial/lock.py Fri Mar 15 10:52:51 2024 +0100 @@ -115,6 +115,7 @@ This function is responsible to issue warnings and or debug messages about the held lock while trying to acquires it.""" + devel_wait_file = kwargs.pop("devel_wait_sync_file", None) def printwarning(printer, locker): """issue the usual "waiting on lock" message through any channel""" @@ -150,6 +151,11 @@ l._trylock() break except error.LockHeld as inst: + if devel_wait_file is not None: + # create the file to signal we are waiting + with open(devel_wait_file, 'w'): + pass + if delay == debugidx: printwarning(ui.debug, inst.locker) if delay == warningidx:
--- a/mercurial/obsutil.py Sun Mar 10 03:29:12 2024 +0100 +++ b/mercurial/obsutil.py Fri Mar 15 10:52:51 2024 +0100 @@ -468,10 +468,10 @@ # Check if other meta has changed changeextra = changectx.extra().items() - ctxmeta = list(filter(metanotblacklisted, changeextra)) + ctxmeta = sorted(filter(metanotblacklisted, changeextra)) sourceextra = source.extra().items() - srcmeta = list(filter(metanotblacklisted, sourceextra)) + srcmeta = sorted(filter(metanotblacklisted, sourceextra)) if ctxmeta != srcmeta: effects |= METACHANGED
--- a/mercurial/phases.py Sun Mar 10 03:29:12 2024 +0100 +++ b/mercurial/phases.py Fri Mar 15 10:52:51 2024 +0100 @@ -703,6 +703,7 @@ return set() # search for affected high phase changesets and roots + seen = set(new_revs) push = heapq.heappush pop = heapq.heappop parents = cl.parentrevs @@ -735,9 +736,11 @@ # higher phases delroots.add(current) # schedule a walk down if needed - if p1_phase > targetphase: + if p1_phase > targetphase and p1 not in seen: + seen.add(p1) push(revs, -p1) - if p2_phase > targetphase: + if p2_phase > targetphase and p2 not in seen: + seen.add(p2) push(revs, -p2) if p1_phase < targetphase and p2_phase < targetphase: new_target_roots.add(current)
--- a/relnotes/6.7 Sun Mar 10 03:29:12 2024 +0100 +++ b/relnotes/6.7 Fri Mar 15 10:52:51 2024 +0100 @@ -1,7 +1,4 @@ -= Mercurial 6.7rc0 = - -/!\ These are **tentative** release notes for the upcoming Mercurial 6.7. -Anything can be added or removed before the actual release. += Mercurial 6.7 = As usual, a *lot* of patches don't make it to this list. @@ -20,6 +17,7 @@ * annotate: limit output to range of lines * unbundle: faster computation of changed heads * Large improvements of some of the phases code (23950e39281f) + * rust-filepatterns: export glob_to_re function == New Experimental Features == @@ -41,6 +39,11 @@ * cext: fix potential memory leaks of list items appended with PyList_Append * doc: document that labels must have a dot in them to have an effect * debugformat: fix formatting for compression level + * obsutil: sort metadata before comparing in geteffectflag() + * crecord: drop calls to `curses.endwin()` + * rust-index: don't use mutable borrow for head-diff computation + * rust-index: don't use mutable borrow to computed filtered heads + == Backwards Compatibility Changes ==
--- a/rust/hg-core/src/filepatterns.rs Sun Mar 10 03:29:12 2024 +0100 +++ b/rust/hg-core/src/filepatterns.rs Fri Mar 15 10:52:51 2024 +0100 @@ -73,7 +73,7 @@ } /// Transforms a glob pattern into a regex -fn glob_to_re(pat: &[u8]) -> Vec<u8> { +pub fn glob_to_re(pat: &[u8]) -> Vec<u8> { let mut input = pat; let mut res: Vec<u8> = vec![]; let mut group_depth = 0;
--- a/rust/hg-core/src/matchers.rs Sun Mar 10 03:29:12 2024 +0100 +++ b/rust/hg-core/src/matchers.rs Fri Mar 15 10:52:51 2024 +0100 @@ -737,14 +737,11 @@ } } -/// Returns a function that matches an `HgPath` against the given regex -/// pattern. +/// Return a `RegexBuilder` from a bytes pattern /// -/// This can fail when the pattern is invalid or not supported by the -/// underlying engine (the `regex` crate), for instance anything with -/// back-references. -#[logging_timer::time("trace")] -fn re_matcher(pattern: &[u8]) -> PatternResult<RegexMatcher> { +/// This works around the fact that even if it works on byte haysacks, +/// [`regex::bytes::Regex`] still uses UTF-8 patterns. +pub fn re_bytes_builder(pattern: &[u8]) -> regex::bytes::RegexBuilder { use std::io::Write; // The `regex` crate adds `.*` to the start and end of expressions if there @@ -764,7 +761,18 @@ // # Safety // This is safe because we escaped all non-ASCII bytes. let pattern_string = unsafe { String::from_utf8_unchecked(escaped_bytes) }; - let re = regex::bytes::RegexBuilder::new(&pattern_string) + regex::bytes::RegexBuilder::new(&pattern_string) +} + +/// Returns a function that matches an `HgPath` against the given regex +/// pattern. +/// +/// This can fail when the pattern is invalid or not supported by the +/// underlying engine (the `regex` crate), for instance anything with +/// back-references. +#[logging_timer::time("trace")] +fn re_matcher(pattern: &[u8]) -> PatternResult<RegexMatcher> { + let re = re_bytes_builder(pattern) .unicode(false) // Big repos with big `.hgignore` will hit the default limit and // incur a significant performance hit. One repo's `hg status` hit
--- a/rust/hg-cpython/src/revlog.rs Sun Mar 10 03:29:12 2024 +0100 +++ b/rust/hg-cpython/src/revlog.rs Fri Mar 15 10:52:51 2024 +0100 @@ -854,7 +854,7 @@ ) -> PyResult<PyObject> { let begin = begin.extract::<BaseRevision>(py)?; let end = end.extract::<BaseRevision>(py)?; - let index = &mut *self.index(py).borrow_mut(); + let index = &*self.index(py).borrow(); let begin = Self::check_revision(index, UncheckedRevision(begin - 1), py)?; let end = Self::check_revision(index, UncheckedRevision(end - 1), py)?; @@ -873,7 +873,7 @@ py: Python, filtered_revs: &PyObject, ) -> PyResult<PyObject> { - let index = &mut *self.index(py).borrow_mut(); + let index = &*self.index(py).borrow(); let filtered_revs = rev_pyiter_collect(py, filtered_revs, index)?; if let Some(new_heads) = index
--- a/tests/test-branches.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-branches.t Fri Mar 15 10:52:51 2024 +0100 @@ -2,8 +2,8 @@ #if mmap $ cat <<EOF >> $HGRCPATH - > [format] - > mmap-revbranchcache=true + > [storage] + > revbranchcache.mmap=true > EOF #endif
--- a/tests/test-chainsaw-update.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-chainsaw-update.t Fri Mar 15 10:52:51 2024 +0100 @@ -5,11 +5,6 @@ setup ===== - $ cat >> $HGRCPATH << EOF - > [extensions] - > chainsaw= - > EOF - $ hg init src $ cd src $ echo 1 > root
--- a/tests/test-chg.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-chg.t Fri Mar 15 10:52:51 2024 +0100 @@ -288,7 +288,9 @@ preserved (since setprocname isn't available on py3 and pure version, the 10th-most-recent line is different when using py3): - $ cat log/server.log.1 log/server.log | tail -10 | filterlog +(the "worker process exited" line is matched independently as it order is unstable with the "exiting" line, the worker might exit before the server decide to exit). + + $ cat log/server.log.1 log/server.log | tail -10 | grep -v "worker process exited" | filterlog YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ... (no-setprocname !) YYYY/MM/DD HH:MM:SS (PID)> forked worker process (pid=...) YYYY/MM/DD HH:MM:SS (PID)> setprocname: ... (setprocname !) @@ -298,8 +300,9 @@ YYYY/MM/DD HH:MM:SS (PID)> setenv: ... YYYY/MM/DD HH:MM:SS (PID)> confighash = ... mtimehash = ... YYYY/MM/DD HH:MM:SS (PID)> validate: [] + YYYY/MM/DD HH:MM:SS (PID)> $TESTTMP/extreload/chgsock/server-... is not owned, exiting. + $ cat log/server.log.1 log/server.log | tail -10 | grep "worker process exited" | filterlog YYYY/MM/DD HH:MM:SS (PID)> worker process exited (pid=...) - YYYY/MM/DD HH:MM:SS (PID)> $TESTTMP/extreload/chgsock/server-... is not owned, exiting. global data mutated by schems -----------------------------
--- a/tests/test-completion.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-completion.t Fri Mar 15 10:52:51 2024 +0100 @@ -3,6 +3,7 @@ abort add addremove + admin::chainsaw-update admin::verify annotate archive @@ -66,6 +67,7 @@ abort add addremove + admin::chainsaw-update admin::verify annotate archive @@ -260,6 +262,7 @@ abort: dry-run add: include, exclude, subrepos, dry-run addremove: similarity, subrepos, include, exclude, dry-run + admin::chainsaw-update: purge-unknown, purge-ignored, rev, source, dest, initial-clone-minimal admin::verify: check, option annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, line-range, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template archive: no-decode, prefix, rev, type, subrepos, include, exclude
--- a/tests/test-generaldelta.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-generaldelta.t Fri Mar 15 10:52:51 2024 +0100 @@ -5,9 +5,12 @@ implementation of parentdelta: third manifest revision would be fully inserted due to big distance from its paren revision (zero). +(We disable the revlog compression to avoid the annoying instability in the chunk size that depends on the compressors implementation) + $ cat << EOF >> $HGRCPATH > [format] > sparse-revlog = no + > revlog-compression = none > EOF $ hg init repo --config format.generaldelta=no --config format.usegeneraldelta=no @@ -138,12 +141,12 @@ - Verify non-aggressive merge uses p1 (commit 1) as delta parent $ hg merge -q 0 - $ hg commit -q -m merge + $ hg commit -q -m merge --config storage.revlog.optimize-delta-parent-choice=no $ hg debugdeltachain -m rev p1 p2 chain# chainlen prev delta 0 -1 -1 1 1 -1 base - 1 -1 -1 1 2 0 prev - 2 1 0 1 2 0 p2 + 1 -1 -1 2 1 -1 base + 2 1 0 2 2 1 p1 $ hg strip -q -r . --config extensions.strip= @@ -154,7 +157,7 @@ $ hg debugdeltachain -m rev p1 p2 chain# chainlen prev delta 0 -1 -1 1 1 -1 base - 1 -1 -1 1 2 0 prev + 1 -1 -1 2 1 -1 base 2 1 0 1 2 0 p2 Test that strip bundle use bundle2 @@ -266,13 +269,13 @@ 46 45 -1 3 29 45 p1 58 1334 1671 1.25262 1671 0 0.00000 47 46 -1 3 30 46 p1 58 1380 1729 1.25290 1729 0 0.00000 48 47 -1 3 31 47 p1 58 1426 1787 1.25316 1787 0 0.00000 - 49 5 -1 4 1 -1 base ??? 316 ??? 0.6???? ??? 0 0.00000 (glob) - 50 49 -1 4 2 49 p1 58 362 2?? 0.7???? 2?? 0 0.00000 (glob) - 51 17 -1 4 3 50 prev 3?? 5?? 6?? 1.0???? 6?? 0 0.00000 (glob) - 52 51 -1 4 4 51 p1 58 640 6?? 1.0???? 6?? 0 0.00000 (glob) - 53 52 -1 5 1 -1 base 0 0 0 0.00000 0 0 0.00000 - 54 53 -1 5 2 53 p1 3?? 640 3?? 0.5???? 3?? 0 0.00000 (glob) - $ hg clone --pull source-repo --config experimental.maxdeltachainspan=2800 relax-chain --config format.generaldelta=yes + 49 5 -1 4 1 -1 base 317 316 317 1.00316 317 0 0.00000 + 50 49 -1 4 2 49 p1 58 362 375 1.03591 375 0 0.00000 + 51 17 -1 5 1 -1 base 595 594 595 1.00168 595 0 0.00000 + 52 51 -1 5 2 51 p1 58 640 653 1.02031 653 0 0.00000 + 53 52 -1 6 1 -1 base 0 0 0 0.00000 0 0 0.00000 + 54 53 -1 7 1 -1 base 641 640 641 1.00156 641 0 0.00000 + $ hg clone --pull source-repo --config experimental.maxdeltachainspan=2900 relax-chain --config format.generaldelta=yes requesting all changes adding changesets adding manifests @@ -332,12 +335,12 @@ 46 45 -1 3 29 45 p1 58 1334 1671 1.25262 1671 0 0.00000 47 46 -1 3 30 46 p1 58 1380 1729 1.25290 1729 0 0.00000 48 47 -1 3 31 47 p1 58 1426 1787 1.25316 1787 0 0.00000 - 49 5 -1 4 1 -1 base ??? 316 ??? 0.6???? ??? 0 0.00000 (glob) - 50 49 -1 4 2 49 p1 58 362 2?? 0.7???? 2?? 0 0.00000 (glob) - 51 17 -1 2 13 17 p1 58 594 739 1.24411 278? 20?? 2.7???? (glob) - 52 51 -1 5 1 -1 base 3?? 640 3?? 0.5???? 3?? 0 0.00000 (glob) + 49 5 -1 1 7 5 p1 58 316 389 1.23101 2857 2468 6.34447 + 50 49 -1 4 1 -1 base 363 362 363 1.00276 363 0 0.00000 + 51 17 -1 5 1 -1 base 595 594 595 1.00168 595 0 0.00000 + 52 51 -1 5 2 51 p1 58 640 653 1.02031 653 0 0.00000 53 52 -1 6 1 -1 base 0 0 0 0.00000 0 0 0.00000 - 54 53 -1 7 1 -1 base 3?? 640 3?? 0.5???? 3?? 0 0.00000 (glob) + 54 53 -1 7 1 -1 base 641 640 641 1.00156 641 0 0.00000 $ hg clone --pull source-repo --config experimental.maxdeltachainspan=0 noconst-chain --config format.usegeneraldelta=yes --config storage.revlog.reuse-external-delta-parent=no requesting all changes adding changesets @@ -403,4 +406,4 @@ 51 17 -1 2 13 17 p1 58 594 739 1.24411 2642 1903 2.57510 52 51 -1 2 14 51 p1 58 640 797 1.24531 2700 1903 2.38770 53 52 -1 4 1 -1 base 0 0 0 0.00000 0 0 0.00000 - 54 53 -1 5 1 -1 base 3?? 640 3?? 0.5???? 3?? 0 0.00000 (glob) + 54 53 -1 5 1 -1 base 641 640 641 1.00156 641 0 0.00000
--- a/tests/test-help.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-help.t Fri Mar 15 10:52:51 2024 +0100 @@ -412,6 +412,9 @@ abort abort an unfinished operation (EXPERIMENTAL) add add the specified files on the next commit + admin::chainsaw-update, admin::chainsawupdate + pull and update to a given revision, no matter what, + (EXPERIMENTAL) annotate, blame show changeset information by line for each file clone make a copy of an existing repository @@ -2535,6 +2538,13 @@ add the specified files on the next commit </td></tr> <tr><td> + <a href="/help/admin::chainsaw-update"> + admin::chainsaw-update + </a> + </td><td> + pull and update to a given revision, no matter what, (EXPERIMENTAL) + </td></tr> + <tr><td> <a href="/help/annotate"> annotate </a>
--- a/tests/test-hgweb-json.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-hgweb-json.t Fri Mar 15 10:52:51 2024 +0100 @@ -2038,6 +2038,10 @@ "topic": "add" }, { + "summary": "pull and update to a given revision, no matter what, (EXPERIMENTAL)", + "topic": "admin::chainsaw-update" + }, + { "summary": "show changeset information by line for each file", "topic": "annotate" },
--- a/tests/test-lock-badness.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-lock-badness.t Fri Mar 15 10:52:51 2024 +0100 @@ -49,15 +49,32 @@ One process waiting for another - $ cat > hooks.py << EOF - > import time - > def sleepone(**x): time.sleep(1) - > def sleephalf(**x): time.sleep(0.5) + $ SYNC_FILE_LOCKED="$TESTTMP/sync-file-locked" + $ export SYNC_FILE_LOCKED + $ SYNC_FILE_TRYING_LOCK="$TESTTMP/sync-file-trying-lock" + $ export SYNC_FILE_TRYING_LOCK + $ cat << EOF > locker.sh + > $RUNTESTDIR/testlib/wait-on-file 10 $SYNC_FILE_TRYING_LOCK $SYNC_FILE_LOCKED; + > EOF + $ cat << EOF > waiter.sh + > $RUNTESTDIR/testlib/wait-on-file 10 $SYNC_FILE_LOCKED; > EOF + $ clean_sync() { + > rm -f "$SYNC_FILE_LOCKED" + > rm -f "$SYNC_FILE_TRYING_LOCK" + > } + + + $ clean_sync $ echo b > b/b - $ hg -R b ci -A -m b --config hooks.precommit="python:`pwd`/hooks.py:sleepone" > stdout & - $ hg -R b up -q --config ui.timeout.warn=0 --config hooks.pre-update="python:`pwd`/hooks.py:sleephalf" \ - > > preup-stdout 2>preup-stderr + $ hg -R b ci -A -m b \ + > --config hooks.precommit="sh $TESTTMP/locker.sh" \ + > > stdout & + $ hg -R b up -q \ + > --config ui.timeout.warn=0 \ + > --config hooks.pre-update="sh $TESTTMP/waiter.sh" \ + > --config devel.lock-wait-sync-file="$SYNC_FILE_TRYING_LOCK" \ + > > preup-stdout 2> preup-stderr $ wait $ cat preup-stdout $ cat preup-stderr @@ -68,11 +85,16 @@ On processs waiting on another, warning after a long time. + $ clean_sync $ echo b > b/c - $ hg -R b ci -A -m b --config hooks.precommit="python:`pwd`/hooks.py:sleepone" > stdout & - $ hg -R b up -q --config hooks.pre-update="python:`pwd`/hooks.py:sleephalf" \ - > --config ui.timeout.warn=250 \ - > > preup-stdout 2>preup-stderr + $ hg -R b ci -A -m b \ + > --config hooks.precommit="sh $TESTTMP/locker.sh" \ + > > stdout & + $ hg -R b up -q \ + > --config hooks.pre-update="sh $TESTTMP/waiter.sh" \ + > --config devel.lock-wait-sync-file="$SYNC_FILE_TRYING_LOCK" \ + > --config ui.timeout.warn=250 \ + > > preup-stdout 2> preup-stderr $ wait $ cat preup-stdout $ cat preup-stderr @@ -81,11 +103,16 @@ On processs waiting on another, warning disabled. + $ clean_sync $ echo b > b/d - $ hg -R b ci -A -m b --config hooks.precommit="python:`pwd`/hooks.py:sleepone" > stdout & - $ hg -R b up -q --config hooks.pre-update="python:`pwd`/hooks.py:sleephalf" \ - > --config ui.timeout.warn=-1 \ - > > preup-stdout 2>preup-stderr + $ hg -R b ci -A -m b \ + > --config hooks.precommit="sh $TESTTMP/locker.sh" \ + > > stdout & + $ hg -R b up -q \ + > --config hooks.pre-update="sh $TESTTMP/waiter.sh" \ + > --config devel.lock-wait-sync-file="$SYNC_FILE_TRYING_LOCK" \ + > --config ui.timeout.warn=-1 \ + > > preup-stdout 2>preup-stderr $ wait $ cat preup-stdout $ cat preup-stderr @@ -96,14 +123,19 @@ On processs waiting on another, warning after a long time (debug output on) + $ clean_sync $ echo b > b/e - $ hg -R b ci -A -m b --config hooks.precommit="python:`pwd`/hooks.py:sleepone" > stdout & - $ hg -R b up --config hooks.pre-update="python:`pwd`/hooks.py:sleephalf" \ - > --config ui.timeout.warn=250 --debug\ - > > preup-stdout 2>preup-stderr + $ hg -R b ci -A -m b \ + > --config hooks.precommit="sh $TESTTMP/locker.sh" \ + > > stdout & + $ hg -R b up \ + > --config hooks.pre-update="sh $TESTTMP/waiter.sh" \ + > --config devel.lock-wait-sync-file="$SYNC_FILE_TRYING_LOCK" \ + > --config ui.timeout.warn=250 --debug \ + > > preup-stdout 2>preup-stderr $ wait $ cat preup-stdout - calling hook pre-update: hghook_pre-update.sleephalf + running hook pre-update: sh $TESTTMP/waiter.sh waiting for lock on working directory of b held by process '*' on host '*' (glob) got lock after * seconds (glob) 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -113,14 +145,19 @@ On processs waiting on another, warning disabled, (debug output on) + $ clean_sync $ echo b > b/f - $ hg -R b ci -A -m b --config hooks.precommit="python:`pwd`/hooks.py:sleepone" > stdout & - $ hg -R b up --config hooks.pre-update="python:`pwd`/hooks.py:sleephalf" \ - > --config ui.timeout.warn=-1 --debug\ - > > preup-stdout 2>preup-stderr + $ hg -R b ci -A -m b \ + > --config hooks.precommit="sh $TESTTMP/locker.sh" \ + > > stdout & + $ hg -R b up \ + > --config hooks.pre-update="sh $TESTTMP/waiter.sh" \ + > --config devel.lock-wait-sync-file="$SYNC_FILE_TRYING_LOCK" \ + > --config ui.timeout.warn=-1 --debug\ + > > preup-stdout 2>preup-stderr $ wait $ cat preup-stdout - calling hook pre-update: hghook_pre-update.sleephalf + running hook pre-update: sh $TESTTMP/waiter.sh waiting for lock on working directory of b held by process '*' on host '*' (glob) got lock after * seconds (glob) 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-patchbomb-tls.t Sun Mar 10 03:29:12 2024 +0100 +++ b/tests/test-patchbomb-tls.t Fri Mar 15 10:52:51 2024 +0100 @@ -1,5 +1,15 @@ #require serve ssl + $ wait_log() { + > pattern="$1" + > for s in $TESTDIR/seq.py 10; do + > if grep "$pattern" $TESTTMP/log > /dev/null ; then + > break + > fi + > sleep 1 + > done + > } + Set up SMTP server: $ CERTSDIR="$TESTDIR/sslcerts" @@ -47,6 +57,7 @@ (?i)abort: .*?certificate.verify.failed.* (re) [255] + $ wait_log "ssl error:" $ cat ../log * ssl error: * (glob) $ : > ../log @@ -62,6 +73,7 @@ (?i)abort: .*?certificate.verify.failed.* (re) [255] + $ wait_log "ssl error:" $ cat ../log * ssl error: * (glob) $ : > ../log @@ -83,6 +95,7 @@ (see https://mercurial-scm.org/wiki/SecureConnections for how to configure Mercurial to avoid this error or set hostsecurity.localhost:fingerprints=sha256:20:de:b3:ad:b4:cd:a5:42:f0:74:41:1c:a2:70:1e:da:6e:c0:5c:16:9e:e7:22:0f:f1:b7:e5:6e:e4:92:af:7e to trust this server) [150] + $ wait_log "no hello:" $ cat ../log connection from * (glob) no hello: b'' @@ -143,6 +156,7 @@ (?i)abort: .*?certificate.verify.failed.* (re) [255] + $ wait_log "ssl error:" $ cat ../log * ssl error: * (glob) $ : > ../log