--- a/contrib/perf.py Wed Jan 25 15:34:27 2023 +0100
+++ b/contrib/perf.py Wed Oct 11 02:02:46 2023 +0200
@@ -882,23 +882,143 @@
fm.end()
+def _default_clear_on_disk_tags_cache(repo):
+ from mercurial import tags
+
+ repo.cachevfs.tryunlink(tags._filename(repo))
+
+
+def _default_clear_on_disk_tags_fnodes_cache(repo):
+ from mercurial import tags
+
+ repo.cachevfs.tryunlink(tags._fnodescachefile)
+
+
+def _default_forget_fnodes(repo, revs):
+ """function used by the perf extension to prune some entries from the
+ fnodes cache"""
+ from mercurial import tags
+
+ missing_1 = b'\xff' * 4
+ missing_2 = b'\xff' * 20
+ cache = tags.hgtagsfnodescache(repo.unfiltered())
+ for r in revs:
+ cache._writeentry(r * tags._fnodesrecsize, missing_1, missing_2)
+ cache.write()
+
+
@command(
b'perf::tags|perftags',
formatteropts
+ [
(b'', b'clear-revlogs', False, b'refresh changelog and manifest'),
+ (
+ b'',
+ b'clear-on-disk-cache',
+ False,
+ b'clear on disk tags cache (DESTRUCTIVE)',
+ ),
+ (
+ b'',
+ b'clear-fnode-cache-all',
+ False,
+ b'clear on disk file node cache (DESTRUCTIVE),',
+ ),
+ (
+ b'',
+ b'clear-fnode-cache-rev',
+ [],
+ b'clear on disk file node cache (DESTRUCTIVE),',
+ b'REVS',
+ ),
+ (
+ b'',
+ b'update-last',
+ b'',
+ b'simulate an update over the last N revisions (DESTRUCTIVE),',
+ b'N',
+ ),
],
)
def perftags(ui, repo, **opts):
+ """Benchmark tags retrieval in various situation
+
+ The option marked as (DESTRUCTIVE) will alter the on-disk cache, possibly
+ altering performance after the command was run. However, it does not
+ destroy any stored data.
+ """
+ from mercurial import tags
+
opts = _byteskwargs(opts)
timer, fm = gettimer(ui, opts)
repocleartagscache = repocleartagscachefunc(repo)
clearrevlogs = opts[b'clear_revlogs']
+ clear_disk = opts[b'clear_on_disk_cache']
+ clear_fnode = opts[b'clear_fnode_cache_all']
+
+ clear_fnode_revs = opts[b'clear_fnode_cache_rev']
+ update_last_str = opts[b'update_last']
+ update_last = None
+ if update_last_str:
+ try:
+ update_last = int(update_last_str)
+ except ValueError:
+ msg = b'could not parse value for update-last: "%s"'
+ msg %= update_last_str
+ hint = b'value should be an integer'
+ raise error.Abort(msg, hint=hint)
+
+ clear_disk_fn = getattr(
+ tags,
+ "clear_cache_on_disk",
+ _default_clear_on_disk_tags_cache,
+ )
+ clear_fnodes_fn = getattr(
+ tags,
+ "clear_cache_fnodes",
+ _default_clear_on_disk_tags_fnodes_cache,
+ )
+ clear_fnodes_rev_fn = getattr(
+ tags,
+ "forget_fnodes",
+ _default_forget_fnodes,
+ )
+
+ clear_revs = []
+ if clear_fnode_revs:
+ clear_revs.extends(scmutil.revrange(repo, clear_fnode_revs))
+
+ if update_last:
+ revset = b'last(all(), %d)' % update_last
+ last_revs = repo.unfiltered().revs(revset)
+ clear_revs.extend(last_revs)
+
+ from mercurial import repoview
+
+ rev_filter = {(b'experimental', b'extra-filter-revs'): revset}
+ with repo.ui.configoverride(rev_filter, source=b"perf"):
+ filter_id = repoview.extrafilter(repo.ui)
+
+ filter_name = b'%s%%%s' % (repo.filtername, filter_id)
+ pre_repo = repo.filtered(filter_name)
+ pre_repo.tags() # warm the cache
+ old_tags_path = repo.cachevfs.join(tags._filename(pre_repo))
+ new_tags_path = repo.cachevfs.join(tags._filename(repo))
+
+ clear_revs = sorted(set(clear_revs))
def s():
+ if update_last:
+ util.copyfile(old_tags_path, new_tags_path)
if clearrevlogs:
clearchangelog(repo)
clearfilecache(repo.unfiltered(), 'manifest')
+ if clear_disk:
+ clear_disk_fn(repo)
+ if clear_fnode:
+ clear_fnodes_fn(repo)
+ elif clear_revs:
+ clear_fnodes_rev_fn(repo, clear_revs)
repocleartagscache()
def t():
--- a/hgext/blackbox.py Wed Jan 25 15:34:27 2023 +0100
+++ b/hgext/blackbox.py Wed Oct 11 02:02:46 2023 +0200
@@ -99,6 +99,7 @@
def _log(self, ui, event, msg, opts):
default = ui.configdate(b'devel', b'default-date')
dateformat = ui.config(b'blackbox', b'date-format')
+ debug_to_stderr = ui.configbool(b'blackbox', b'debug.to-stderr')
if dateformat:
date = dateutil.datestr(default, dateformat)
else:
@@ -130,7 +131,10 @@
maxfiles=self._maxfiles,
maxsize=self._maxsize,
) as fp:
- fp.write(fmt % args)
+ msg = fmt % args
+ fp.write(msg)
+ if debug_to_stderr:
+ ui.write_err(msg)
except (IOError, OSError) as err:
# deactivate this to avoid failed logging again
self._trackedevents.clear()
--- a/mercurial/bundle2.py Wed Jan 25 15:34:27 2023 +0100
+++ b/mercurial/bundle2.py Wed Oct 11 02:02:46 2023 +0200
@@ -896,7 +896,7 @@
"""utility to transfer a bundle2 as binary
This is made necessary by the fact the 'getbundle' command over 'ssh'
- have no way to know then the reply end, relying on the bundle to be
+ have no way to know when the reply ends, relying on the bundle to be
interpreted to know its end. This is terrible and we are sorry, but we
needed to move forward to get general delta enabled.
"""
--- a/mercurial/configitems.toml Wed Jan 25 15:34:27 2023 +0100
+++ b/mercurial/configitems.toml Wed Oct 11 02:02:46 2023 +0200
@@ -2796,6 +2796,12 @@
[[items]]
section = "blackbox"
+name = "debug.to-stderr"
+default = false
+in_core_extension = "blackbox"
+
+[[items]]
+section = "blackbox"
name = "dirty"
default = false
in_core_extension = "blackbox"
--- a/mercurial/httppeer.py Wed Jan 25 15:34:27 2023 +0100
+++ b/mercurial/httppeer.py Wed Oct 11 02:02:46 2023 +0200
@@ -662,7 +662,8 @@
return inst
except error.RepoError as httpexception:
try:
- r = statichttprepo.make_peer(ui, b"static-" + path.loc, create)
+ path = path.copy(new_raw_location=b"static-" + path.rawloc)
+ r = statichttprepo.make_peer(ui, path, create)
ui.note(_(b'(falling back to static-http)\n'))
return r
except error.RepoError:
--- a/mercurial/tags.py Wed Jan 25 15:34:27 2023 +0100
+++ b/mercurial/tags.py Wed Oct 11 02:02:46 2023 +0200
@@ -190,10 +190,9 @@
_updatetags(cachetags, alltags)
return alltags
+ has_node = repo.changelog.index.has_node
for head in reversed(heads): # oldest to newest
- assert repo.changelog.index.has_node(
- head
- ), b"tag cache returned bogus head %s" % short(head)
+ assert has_node(head), b"tag cache returned bogus head %s" % short(head)
fnodes = _filterfnodes(tagfnode, reversed(heads))
alltags = _tagsfromfnodes(ui, repo, fnodes)
@@ -910,3 +909,24 @@
)
finally:
lock.release()
+
+
+def clear_cache_on_disk(repo):
+ """function used by the perf extension to "tags" cache"""
+ repo.cachevfs.tryunlink(_filename(repo))
+
+
+def clear_cache_fnodes(repo):
+ """function used by the perf extension to clear "file node cache"""
+ repo.cachevfs.tryunlink(_filename(repo))
+
+
+def forget_fnodes(repo, revs):
+ """function used by the perf extension to prune some entries from the fnodes
+ cache"""
+ missing_1 = b'\xff' * 4
+ missing_2 = b'\xff' * 20
+ cache = hgtagsfnodescache(repo.unfiltered())
+ for r in revs:
+ cache._writeentry(r * _fnodesrecsize, missing_1, missing_2)
+ cache.write()
--- a/mercurial/upgrade_utils/actions.py Wed Jan 25 15:34:27 2023 +0100
+++ b/mercurial/upgrade_utils/actions.py Wed Oct 11 02:02:46 2023 +0200
@@ -289,8 +289,7 @@
postdowngrademessage = _(
b'repository downgraded to not use share safe mode, '
- b'existing shares will not work and needs to'
- b' be reshared.'
+ b'existing shares will not work and need to be reshared.'
)
postupgrademessage = _(
@@ -359,7 +358,7 @@
description = _(b'Stores copies information alongside changesets.')
upgrademessage = _(
- b'Allows to use more efficient algorithm to deal with ' b'copy tracing.'
+ b'Allows to use more efficient algorithm to deal with copy tracing.'
)
touches_filelogs = False
--- a/rust/hg-core/src/revlog/mod.rs Wed Jan 25 15:34:27 2023 +0100
+++ b/rust/hg-core/src/revlog/mod.rs Wed Oct 11 02:02:46 2023 +0200
@@ -236,6 +236,16 @@
data_path: Option<&Path>,
use_nodemap: bool,
) -> Result<Self, HgError> {
+ Self::open_gen(store_vfs, index_path, data_path, use_nodemap, None)
+ }
+
+ fn open_gen(
+ store_vfs: &Vfs,
+ index_path: impl AsRef<Path>,
+ data_path: Option<&Path>,
+ use_nodemap: bool,
+ nodemap_for_test: Option<nodemap::NodeTree>,
+ ) -> Result<Self, HgError> {
let index_path = index_path.as_ref();
let index = {
match store_vfs.mmap_open_opt(&index_path)? {
@@ -273,6 +283,8 @@
)
};
+ let nodemap = nodemap_for_test.or(nodemap);
+
Ok(Revlog {
index,
data_bytes,
@@ -306,23 +318,13 @@
&self,
node: NodePrefix,
) -> Result<Revision, RevlogError> {
- let looked_up = if let Some(nodemap) = &self.nodemap {
+ if let Some(nodemap) = &self.nodemap {
nodemap
.find_bin(&self.index, node)?
.ok_or(RevlogError::InvalidRevision)
} else {
self.rev_from_node_no_persistent_nodemap(node)
- };
-
- if node.is_prefix_of(&NULL_NODE) {
- return match looked_up {
- Ok(_) => Err(RevlogError::AmbiguousPrefix),
- Err(RevlogError::InvalidRevision) => Ok(NULL_REVISION),
- res => res,
- };
- };
-
- looked_up
+ }
}
/// Same as `rev_from_node`, without using a persistent nodemap
@@ -338,17 +340,23 @@
// TODO: consider building a non-persistent nodemap in memory to
// optimize these cases.
let mut found_by_prefix = None;
- for rev in (0..self.len()).rev() {
+ for rev in (-1..self.len() as BaseRevision).rev() {
let rev = Revision(rev as BaseRevision);
- let index_entry = self.index.get_entry(rev).ok_or_else(|| {
- HgError::corrupted(
- "revlog references a revision not in the index",
- )
- })?;
- if node == *index_entry.hash() {
+ let candidate_node = if rev == Revision(-1) {
+ NULL_NODE
+ } else {
+ let index_entry =
+ self.index.get_entry(rev).ok_or_else(|| {
+ HgError::corrupted(
+ "revlog references a revision not in the index",
+ )
+ })?;
+ *index_entry.hash()
+ };
+ if node == candidate_node {
return Ok(rev);
}
- if node.is_prefix_of(index_entry.hash()) {
+ if node.is_prefix_of(&candidate_node) {
if found_by_prefix.is_some() {
return Err(RevlogError::AmbiguousPrefix);
}
@@ -913,7 +921,13 @@
.flatten()
.collect_vec();
std::fs::write(temp.path().join("foo.i"), contents).unwrap();
- let revlog = Revlog::open(&vfs, "foo.i", None, false).unwrap();
+
+ let mut idx = nodemap::tests::TestNtIndex::new();
+ idx.insert_node(Revision(0), node0).unwrap();
+ idx.insert_node(Revision(1), node1).unwrap();
+
+ let revlog =
+ Revlog::open_gen(&vfs, "foo.i", None, true, Some(idx.nt)).unwrap();
// accessing the data shows the corruption
revlog.get_entry(0.into()).unwrap().data().unwrap_err();
--- a/rust/hg-core/src/revlog/nodemap.rs Wed Jan 25 15:34:27 2023 +0100
+++ b/rust/hg-core/src/revlog/nodemap.rs Wed Oct 11 02:02:46 2023 +0200
@@ -693,7 +693,7 @@
}
#[cfg(test)]
-mod tests {
+pub mod tests {
use super::NodeMapError::*;
use super::*;
use crate::revlog::node::{hex_pad_right, Node};
@@ -871,29 +871,36 @@
Ok(())
}
- struct TestNtIndex {
- index: TestIndex,
- nt: NodeTree,
+ pub struct TestNtIndex {
+ pub index: TestIndex,
+ pub nt: NodeTree,
}
impl TestNtIndex {
- fn new() -> Self {
+ pub fn new() -> Self {
TestNtIndex {
index: HashMap::new(),
nt: NodeTree::default(),
}
}
- fn insert(&mut self, rev: i32, hex: &str) -> Result<(), NodeMapError> {
+ pub fn insert_node(
+ &mut self,
+ rev: Revision,
+ node: Node,
+ ) -> Result<(), NodeMapError> {
+ self.index.insert(rev.into(), node);
+ self.nt.insert(&self.index, &node, rev)?;
+ Ok(())
+ }
+
+ pub fn insert(
+ &mut self,
+ rev: Revision,
+ hex: &str,
+ ) -> Result<(), NodeMapError> {
let node = pad_node(hex);
- let rev: UncheckedRevision = rev.into();
- self.index.insert(rev, node);
- self.nt.insert(
- &self.index,
- &node,
- self.index.check_revision(rev).unwrap(),
- )?;
- Ok(())
+ return self.insert_node(rev, node);
}
fn find_hex(
@@ -927,23 +934,23 @@
#[test]
fn test_insert_full_mutable() -> Result<(), NodeMapError> {
let mut idx = TestNtIndex::new();
- idx.insert(0, "1234")?;
+ idx.insert(Revision(0), "1234")?;
assert_eq!(idx.find_hex("1")?, Some(R!(0)));
assert_eq!(idx.find_hex("12")?, Some(R!(0)));
// let's trigger a simple split
- idx.insert(1, "1a34")?;
+ idx.insert(Revision(1), "1a34")?;
assert_eq!(idx.nt.growable.len(), 1);
assert_eq!(idx.find_hex("12")?, Some(R!(0)));
assert_eq!(idx.find_hex("1a")?, Some(R!(1)));
// reinserting is a no_op
- idx.insert(1, "1a34")?;
+ idx.insert(Revision(1), "1a34")?;
assert_eq!(idx.nt.growable.len(), 1);
assert_eq!(idx.find_hex("12")?, Some(R!(0)));
assert_eq!(idx.find_hex("1a")?, Some(R!(1)));
- idx.insert(2, "1a01")?;
+ idx.insert(Revision(2), "1a01")?;
assert_eq!(idx.nt.growable.len(), 2);
assert_eq!(idx.find_hex("1a"), Err(NodeMapError::MultipleResults));
assert_eq!(idx.find_hex("12")?, Some(R!(0)));
@@ -952,7 +959,7 @@
assert_eq!(idx.find_hex("1a12")?, None);
// now let's make it split and create more than one additional block
- idx.insert(3, "1a345")?;
+ idx.insert(Revision(3), "1a345")?;
assert_eq!(idx.nt.growable.len(), 4);
assert_eq!(idx.find_hex("1a340")?, Some(R!(1)));
assert_eq!(idx.find_hex("1a345")?, Some(R!(3)));
@@ -966,7 +973,7 @@
#[test]
fn test_unique_prefix_len_zero_prefix() {
let mut idx = TestNtIndex::new();
- idx.insert(0, "00000abcd").unwrap();
+ idx.insert(Revision(0), "00000abcd").unwrap();
assert_eq!(idx.find_hex("000"), Err(NodeMapError::MultipleResults));
// in the nodetree proper, this will be found at the first nybble
@@ -976,7 +983,7 @@
assert_eq!(idx.unique_prefix_len_hex("00000ab"), Ok(Some(6)));
// same with odd result
- idx.insert(1, "00123").unwrap();
+ idx.insert(Revision(1), "00123").unwrap();
assert_eq!(idx.unique_prefix_len_hex("001"), Ok(Some(3)));
assert_eq!(idx.unique_prefix_len_hex("0012"), Ok(Some(3)));
@@ -1012,10 +1019,10 @@
#[test]
fn test_insert_partly_immutable() -> Result<(), NodeMapError> {
let mut idx = TestNtIndex::new();
- idx.insert(0, "1234")?;
- idx.insert(1, "1235")?;
- idx.insert(2, "131")?;
- idx.insert(3, "cafe")?;
+ idx.insert(Revision(0), "1234")?;
+ idx.insert(Revision(1), "1235")?;
+ idx.insert(Revision(2), "131")?;
+ idx.insert(Revision(3), "cafe")?;
let mut idx = idx.commit();
assert_eq!(idx.find_hex("1234")?, Some(R!(0)));
assert_eq!(idx.find_hex("1235")?, Some(R!(1)));
@@ -1024,7 +1031,7 @@
// we did not add anything since init from readonly
assert_eq!(idx.nt.masked_readonly_blocks(), 0);
- idx.insert(4, "123A")?;
+ idx.insert(Revision(4), "123A")?;
assert_eq!(idx.find_hex("1234")?, Some(R!(0)));
assert_eq!(idx.find_hex("1235")?, Some(R!(1)));
assert_eq!(idx.find_hex("131")?, Some(R!(2)));
@@ -1034,7 +1041,7 @@
assert_eq!(idx.nt.masked_readonly_blocks(), 4);
eprintln!("{:?}", idx.nt);
- idx.insert(5, "c0")?;
+ idx.insert(Revision(5), "c0")?;
assert_eq!(idx.find_hex("cafe")?, Some(R!(3)));
assert_eq!(idx.find_hex("c0")?, Some(R!(5)));
assert_eq!(idx.find_hex("c1")?, None);
@@ -1049,10 +1056,10 @@
#[test]
fn test_invalidate_all() -> Result<(), NodeMapError> {
let mut idx = TestNtIndex::new();
- idx.insert(0, "1234")?;
- idx.insert(1, "1235")?;
- idx.insert(2, "131")?;
- idx.insert(3, "cafe")?;
+ idx.insert(Revision(0), "1234")?;
+ idx.insert(Revision(1), "1235")?;
+ idx.insert(Revision(2), "131")?;
+ idx.insert(Revision(3), "cafe")?;
let mut idx = idx.commit();
idx.nt.invalidate_all();
@@ -1079,9 +1086,9 @@
#[test]
fn test_into_added_bytes() -> Result<(), NodeMapError> {
let mut idx = TestNtIndex::new();
- idx.insert(0, "1234")?;
+ idx.insert(Revision(0), "1234")?;
let mut idx = idx.commit();
- idx.insert(4, "cafe")?;
+ idx.insert(Revision(4), "cafe")?;
let (_, bytes) = idx.nt.into_readonly_and_added_bytes();
// only the root block has been changed
--- a/tests/test-contrib-perf.t Wed Jan 25 15:34:27 2023 +0100
+++ b/tests/test-contrib-perf.t Wed Oct 11 02:02:46 2023 +0200
@@ -194,7 +194,7 @@
benchmark the full generation of a stream clone
perf::stream-locked-section
benchmark the initial, repo-locked, section of a stream-clone
- perf::tags (no help text available)
+ perf::tags Benchmark tags retrieval in various situation
perf::templating
test the rendering time of a given template
perf::unbundle
--- a/tests/test-remotefilelog-gc.t Wed Jan 25 15:34:27 2023 +0100
+++ b/tests/test-remotefilelog-gc.t Wed Oct 11 02:02:46 2023 +0200
@@ -106,11 +106,6 @@
# Test that warning is displayed when the repo path is malformed
$ printf "asdas\0das" >> $CACHEDIR/repos
-#if py311
- $ hg gc
- finished: removed 0 of 4 files (0.00 GB to 0.00 GB)
-#else
$ hg gc
abort: invalid path asdas\x00da: .*(null|NULL).* (re)
[255]
-#endif
--- a/tests/test-rhg.t Wed Jan 25 15:34:27 2023 +0100
+++ b/tests/test-rhg.t Wed Oct 11 02:02:46 2023 +0200
@@ -27,6 +27,8 @@
Reading and setting configuration
$ echo "[ui]" >> $HGRCPATH
$ echo "username = user1" >> $HGRCPATH
+ $ echo "[extensions]" >> $HGRCPATH
+ $ echo "sparse =" >> $HGRCPATH
$ $NO_FALLBACK rhg config ui.username
user1
$ echo "[ui]" >> .hg/hgrc
@@ -309,6 +311,11 @@
.hg/store/00changelog.i
.hg/store/00changelog.n
+Rhg status on a sparse repo with nodemap (this specific combination used to crash in 6.5.2)
+
+ $ hg debugsparse -X excluded-dir
+ $ $NO_FALLBACK rhg status
+
Specifying revisions by changeset ID
$ $NO_FALLBACK rhg files -r c3ae8dec9fad
of
--- a/tests/test-share-safe.t Wed Jan 25 15:34:27 2023 +0100
+++ b/tests/test-share-safe.t Wed Oct 11 02:02:46 2023 +0200
@@ -470,7 +470,7 @@
(it is safe to interrupt this process any time before data migration completes)
upgrading repository requirements
removing temporary repository $TESTTMP/non-share-safe/.hg/upgrade.* (glob)
- repository downgraded to not use share safe mode, existing shares will not work and needs to be reshared.
+ repository downgraded to not use share safe mode, existing shares will not work and need to be reshared.
$ hg debugrequirements
dotencode
--- a/tests/test-static-http.t Wed Jan 25 15:34:27 2023 +0100
+++ b/tests/test-static-http.t Wed Oct 11 02:02:46 2023 +0200
@@ -148,9 +148,17 @@
$ hg paths
default = static-http://localhost:$HGPORT/remotempty
+test autodetecting static-http: scheme (issue6833)
+
+ $ cd ..
+ $ hg init actually-static
+ $ hg clone http://localhost:$HGPORT/actually-static local4
+ no changes found
+ updating to branch default
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
test with non-repo
- $ cd ..
$ mkdir notarepo
$ hg clone static-http://localhost:$HGPORT/notarepo local3
abort: 'http://localhost:$HGPORT/notarepo' does not appear to be an hg repository
@@ -225,6 +233,15 @@
/.hg/store/data/~2ehgsub.i (py37 !)
/.hg/store/data/~2ehgsubstate.i (py37 !)
/.hg/store/requires
+ /actually-static/.hg/bookmarks
+ /actually-static/.hg/bookmarks.current
+ /actually-static/.hg/dirstate
+ /actually-static/.hg/requires
+ /actually-static/.hg/store/00changelog.i
+ /actually-static/.hg/store/00manifest.i
+ /actually-static/.hg/store/requires
+ /actually-static/?cmd=capabilities
+ /actually-static?cmd=capabilities
/notarepo/.hg/00changelog.i
/notarepo/.hg/requires
/remote-with-names/.hg/bookmarks