D12623: clone: use better names for temp files

2022-05-10 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D12623

AFFECTED FILES
  mercurial/streamclone.py
  mercurial/util.py

CHANGE DETAILS

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -2592,6 +2592,14 @@
 self.close()
 
 
+def tryrmdir(f):
+try:
+removedirs(f)
+except OSError as e:
+if e.errno != errno.ENOENT and e.errno != errno.ENOTEMPTY:
+raise
+
+
 def unlinkpath(f, ignoremissing=False, rmdir=True):
 # type: (bytes, bool, bool) -> None
 """unlink and remove the directory if it is empty"""
diff --git a/mercurial/streamclone.py b/mercurial/streamclone.py
--- a/mercurial/streamclone.py
+++ b/mercurial/streamclone.py
@@ -558,13 +558,20 @@
 @contextlib.contextmanager
 def maketempcopies():
 """return a function to temporary copy file"""
+
 files = []
+dst_dir = pycompat.mkdtemp(prefix=b'hg-clone-')
 try:
 
 def copy(src):
-fd, dst = pycompat.mkstemp()
+fd, dst = pycompat.mkstemp(
+prefix=os.path.basename(src), dir=dst_dir
+)
 os.close(fd)
 files.append(dst)
+# hardlink=True doees nothing here because
+# dst exists, and [copyfiles] doesn't overwrite
+# existing files with hardlinks.
 util.copyfiles(src, dst, hardlink=True)
 return dst
 
@@ -572,6 +579,7 @@
 finally:
 for tmp in files:
 util.tryunlink(tmp)
+util.tryrmdir(dst_dir)
 
 
 def _makemap(repo):



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D12607: censor: make rhg fall back to python when encountering a censored node

2022-05-05 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This is to make it support censor.policy=ignore without having
  to duplicate that logic.
  
  Also, change the censor test in such a way that it uses rhg now,
  because extensions are disabled except when we call [hg censor].

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D12607

AFFECTED FILES
  rust/hg-core/src/errors.rs
  rust/hg-core/src/revlog/revlog.rs
  rust/rhg/src/error.rs
  tests/test-censor.t

CHANGE DETAILS

diff --git a/tests/test-censor.t b/tests/test-censor.t
--- a/tests/test-censor.t
+++ b/tests/test-censor.t
@@ -10,10 +10,6 @@
 
 #endif
 
-  $ cat >> $HGRCPATH < [extensions]
-  > censor=
-  > EOF
   $ cp $HGRCPATH $HGRCPATH.orig
 
 Create repo with unimpeachable content
@@ -81,7 +77,7 @@
 (this also tests file pattern matching: path relative to cwd case)
 
   $ mkdir -p foo/bar/baz
-  $ hg --cwd foo/bar/baz censor -r $C2 -t "remove password" ../../../target
+  $ hg --config extensions.censor= --cwd foo/bar/baz censor -r $C2 -t "remove 
password" ../../../target
   $ hg cat -r $H1 target | head -n 10
   Tainted file is now sanitized
   $ hg cat -r $H2 target | head -n 10
@@ -99,7 +95,7 @@
 
 (this also tests file pattern matching: with 'path:' scheme)
 
-  $ hg --cwd foo/bar/baz censor -r $C1 path:target
+  $ hg --config extensions.censor= --cwd foo/bar/baz censor -r $C1 path:target
   $ hg cat -r $H1 target | head -n 10
   Tainted file is now sanitized
   $ hg cat -r $H2 target | head -n 10
@@ -242,7 +238,7 @@
   $ echo 'advanced head H1' > target
   $ hg ci -m 'advance head H1' target
   $ H1=`hg id --debug -i`
-  $ hg censor -r $C3 target
+  $ hg --config extensions.censor= censor -r $C3 target
   $ hg update -r $H2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg merge -r $C3
@@ -254,14 +250,14 @@
 
   $ hg update -C -r $H2
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg censor -r $H2 target
+  $ hg --config extensions.censor= censor -r $H2 target
   abort: cannot censor file in heads (78a8fc215e79)
   (clean/delete and commit first)
   [255]
   $ echo 'twiddling thumbs' > bystander
   $ hg ci -m 'bystander commit'
   $ H2=`hg id --debug -i`
-  $ hg censor -r "$H2^" target
+  $ hg --config extensions.censor= censor -r "$H2^" target
   abort: cannot censor file in heads (efbe78065929)
   (clean/delete and commit first)
   [255]
@@ -273,7 +269,7 @@
   $ H2=`hg id --debug -i`
   $ hg update -r "$H2^"
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  $ hg censor -r . target
+  $ hg --config extensions.censor= censor -r . target
   abort: cannot censor working directory
   (clean/delete/update first)
   [255]
@@ -286,7 +282,7 @@
   $ hg rm target
   $ hg ci -m 'delete target so it may be censored'
   $ H2=`hg id --debug -i`
-  $ hg censor -r $C4 target
+  $ hg --config extensions.censor= censor -r $C4 target
   $ hg cat -r $C4 target | head -n 10
   $ hg cat -r "$H2^^" target | head -n 10
   Tainted file now super sanitized
@@ -314,7 +310,7 @@
   $ hg revert -r "$H2^" target
   $ hg ci -m 'cleaned 100k passwords'
   $ H2=`hg id --debug -i`
-  $ hg censor -r $C5 target
+  $ hg --config extensions.censor= censor -r $C5 target
   $ hg cat -r $C5 target | head -n 10
   $ hg cat -r $H2 target | head -n 10
   fresh start
@@ -393,7 +389,7 @@
   $ CLEANREV=$H2
   $ hg cat -r $REV target | head -n 10
   Passwords: hunter2hunter2
-  $ hg censor -r $REV target
+  $ hg --config extensions.censor= censor -r $REV target
   $ hg cat -r $REV target | head -n 10
   $ hg cat -r $CLEANREV target | head -n 10
   Re-sanitized; nothing to see here
@@ -503,7 +499,7 @@
 Can import bundle where first revision of a file is censored
 
   $ hg init ../rinit
-  $ hg censor -r 0 target
+  $ hg --config extensions.censor= censor -r 0 target
   $ hg bundle -r 0 --base null ../rinit/initbundle
   1 changesets found
   $ cd ../rinit
@@ -553,7 +549,7 @@
 
   $ hg cat -r $B1 target | wc -l
*50002 (re)
-  $ hg censor -r $B1 target
+  $ hg --config extensions.censor= censor -r $B1 target
   $ hg cat -r $B1 target | wc -l
*0 (re)
 
diff --git a/rust/rhg/src/error.rs b/rust/rhg/src/error.rs
--- a/rust/rhg/src/error.rs
+++ b/rust/rhg/src/error.rs
@@ -73,6 +73,9 @@
 HgError::UnsupportedFeature(message) => {
 CommandError::unsupported(message)
 }
+HgError::CensoredNodeError => {
+CommandError::unsupported("Encountered a censored node")
+}
 HgError::Abort {
 message,
 detailed_exit_code,
diff --git a/rust/hg-core/src/revlog/revlog.rs 
b/rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/revlog/revlog.rs
+++ b/rust/hg-core/src/revlog/revlog.rs
@@ -378,7 +378,7 @@
 }
 }
 
-pub fn is_cencored() -> bool {
+pub 

D12604: censor: fix [hg update] away from a revision with censored files

2022-05-04 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D12604

AFFECTED FILES
  mercurial/filelog.py
  tests/test-censor2.t

CHANGE DETAILS

diff --git a/tests/test-censor2.t b/tests/test-censor2.t
--- a/tests/test-censor2.t
+++ b/tests/test-censor2.t
@@ -19,5 +19,4 @@
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ cat target
   $ hg update tip
-  abort: file censored data/target:b1c12cf98dc8 (known-bad-output !)
-  [255]
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
diff --git a/mercurial/filelog.py b/mercurial/filelog.py
--- a/mercurial/filelog.py
+++ b/mercurial/filelog.py
@@ -202,10 +202,10 @@
 
 # for revisions with renames, we have to go the slow way
 node = self.node(rev)
+if self.iscensored(rev):
+return 0
 if self.renamed(node):
 return len(self.read(node))
-if self.iscensored(rev):
-return 0
 
 # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
 # XXX See also basefilectx.cmp.



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D12584: censor: demonstrate a bug

2022-04-27 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D12584

AFFECTED FILES
  tests/test-censor2.t

CHANGE DETAILS

diff --git a/tests/test-censor2.t b/tests/test-censor2.t
new file mode 100644
--- /dev/null
+++ b/tests/test-censor2.t
@@ -0,0 +1,23 @@
+  $ cat >> $HGRCPATH < [censor]
+  > policy=ignore
+  > EOF
+
+  $ mkdir r
+  $ cd r
+  $ hg init
+  $ echo secret > target
+  $ hg commit -Am "secret"
+  adding target
+  $ touch bystander
+  $ hg commit -Am "innocent"
+  adding bystander
+  $ echo erased-secret > target
+  $ hg commit -m "erased secret"
+  $ hg censor target --config extensions.censor= -r .^^
+  $ hg update .^
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ cat target
+  $ hg update tip
+  abort: file censored data/target:b1c12cf98dc8 (known-bad-output !)
+  [255]



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D12549: branchmap: add a test that shows bad interaction with strip

2022-04-13 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D12549

AFFECTED FILES
  tests/test-strip-branch-cache.t

CHANGE DETAILS

diff --git a/tests/test-strip-branch-cache.t b/tests/test-strip-branch-cache.t
new file mode 100644
--- /dev/null
+++ b/tests/test-strip-branch-cache.t
@@ -0,0 +1,59 @@
+Define helpers.
+
+  $ hg_log () { hg log -G -T "{rev}:{node|short}"; }
+  $ commit () { echo "foo - ${2:-$1}" > $1; hg commit -Aqm "Edited $1"; }
+  $ strip() { hg --config extensions.strip= strip -q -r "$1" ; }
+
+Setup hg repo.
+
+  $ hg init repo
+  $ cd repo
+  $ touch x; hg add x; hg commit -m "initial"
+  $ hg clone -q . ../clone
+  $ commit a
+
+  $ cd ../clone
+  $ hg up -q root 
+  abort: unknown revision 'root'
+  [10]
+
+  $ commit b
+
+  $ hg pull -q ../repo
+
+  $ cat .hg/cache/branch2-visible
+  222ae9789a75703f9836e44de7db179cbfd420ee 2
+  a3498d6e39376d2456425dd8c692367bdbf00fa2 o default
+  222ae9789a75703f9836e44de7db179cbfd420ee o default
+
+  $ hg_log
+  o  2:222ae9789a75
+  |
+  | @  1:a3498d6e3937
+  |/
+  o  0:7ab0a3bd758a
+  
+
+  $ strip '1:'
+
+The branchmap cache is not adjusted on strip.
+Now mentions a changelog entry that has been stripped.
+
+  $ cat .hg/cache/branch2-visible
+  222ae9789a75703f9836e44de7db179cbfd420ee 2
+  a3498d6e39376d2456425dd8c692367bdbf00fa2 o default
+  222ae9789a75703f9836e44de7db179cbfd420ee o default
+
+  $ commit c
+
+Not adjusted on commit, either.
+
+  $ cat .hg/cache/branch2-visible
+  222ae9789a75703f9836e44de7db179cbfd420ee 2
+  a3498d6e39376d2456425dd8c692367bdbf00fa2 o default
+  222ae9789a75703f9836e44de7db179cbfd420ee o default
+
+On pull we end up with the same tip, and so wrongly reuse the invalid cache 
and crash.
+
+  $ hg pull ../repo 2>&1 | grep 'ValueError:'
+  ValueError: node a3498d6e39376d2456425dd8c692367bdbf00fa2 does not exist



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D12543: rhg: refactor to pass argv down, instead of caling args_os()

2022-04-12 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This refactoring makes it easy to patch some command-line preprocessing into 
rhg.
  We use this to support using rhg as a shebang interpreter, for example.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D12543

AFFECTED FILES
  rust/rhg/src/blackbox.rs
  rust/rhg/src/main.rs

CHANGE DETAILS

diff --git a/rust/rhg/src/main.rs b/rust/rhg/src/main.rs
--- a/rust/rhg/src/main.rs
+++ b/rust/rhg/src/main.rs
@@ -25,6 +25,7 @@
 }
 
 fn main_with_result(
+argv: Vec,
 process_start_time: ::ProcessStartTime,
 ui: ::Ui,
 repo: Result<, >,
@@ -78,7 +79,7 @@
 .version("0.0.1");
 let app = add_subcommand_args(app);
 
-let matches = app.clone().get_matches_safe()?;
+let matches = app.clone().get_matches_from_safe(argv.iter())?;
 
 let (subcommand_name, subcommand_matches) = matches.subcommand();
 
@@ -123,9 +124,9 @@
 if config.is_extension_enabled(b"blackbox") {
 let blackbox =
 blackbox::Blackbox::new(, process_start_time)?;
-blackbox.log_command_start();
+blackbox.log_command_start(argv.iter());
 let result = run();
-blackbox.log_command_end(exit_code(
+blackbox.log_command_end(argv.iter(), exit_code(
 ,
 // TODO: show a warning or combine with original error if
 // `get_bool` returns an error
@@ -139,7 +140,7 @@
 }
 }
 
-fn main() {
+fn rhg_main(argv: Vec) -> ! {
 // Run this first, before we find out if the blackbox extension is even
 // enabled, in order to include everything in-between in the duration
 // measurements. Reading config files can be slow if they’re on NFS.
@@ -147,7 +148,7 @@
 
 env_logger::init();
 
-let early_args = EarlyArgs::parse(std::env::args_os());
+let early_args = EarlyArgs::parse();
 
 let initial_current_dir = early_args.cwd.map(|cwd| {
 let cwd = get_path_from_bytes();
@@ -158,6 +159,7 @@
 })
 .unwrap_or_else(|error| {
 exit(
+,
 ,
 ::new_infallible(::empty()),
 OnUnsupported::Abort,
@@ -179,6 +181,7 @@
 let on_unsupported = OnUnsupported::Abort;
 
 exit(
+,
 _current_dir,
 ::new_infallible(::empty()),
 on_unsupported,
@@ -191,6 +194,7 @@
 .load_cli_args(early_args.config, early_args.color)
 .unwrap_or_else(|error| {
 exit(
+,
 _current_dir,
 ::new_infallible(_repo_config),
 OnUnsupported::from_config(_repo_config),
@@ -209,6 +213,7 @@
 }
 if SCHEME_RE.is_match(_path_bytes) {
 exit(
+,
 _current_dir,
 ::new_infallible(_repo_config),
 OnUnsupported::from_config(_repo_config),
@@ -299,6 +304,7 @@
 Err(NoRepoInCwdError { cwd: at })
 }
 Err(error) => exit(
+,
 _current_dir,
 ::new_infallible(_repo_config),
 OnUnsupported::from_config(_repo_config),
@@ -318,6 +324,7 @@
 };
 let ui = Ui::new().unwrap_or_else(|error| {
 exit(
+,
 _current_dir,
 ::new_infallible(),
 OnUnsupported::from_config(),
@@ -330,12 +337,14 @@
 let on_unsupported = OnUnsupported::from_config(config);
 
 let result = main_with_result(
+argv.iter().map(|s| s.to_owned()).collect(),
 _start_time,
 ,
 repo_result.as_ref(),
 config,
 );
 exit(
+,
 _current_dir,
 ,
 on_unsupported,
@@ -348,6 +357,10 @@
 )
 }
 
+fn main() -> ! {
+rhg_main(std::env::args_os().collect())
+}
+
 fn exit_code(
 result: <(), CommandError>,
 use_detailed_exit_code: bool,
@@ -374,7 +387,8 @@
 }
 }
 
-fn exit(
+fn exit<'b>(
+original_args: &'b [OsString],
 initial_current_dir: ,
 ui: ,
 mut on_unsupported: OnUnsupported,
@@ -386,7 +400,7 @@
 Err(CommandError::UnsupportedFeature { message }),
 ) = (_unsupported, )
 {
-let mut args = std::env::args_os();
+let mut args = original_args.iter();
 let executable = match executable {
 None => {
 exit_no_fallback(
@@ -546,7 +560,7 @@
 }
 
 impl EarlyArgs {
-fn parse(args: impl IntoIterator) -> Self {
+fn parse<'a>(args: impl IntoIterator) -> Self {
 let mut args = args.into_iter().map(get_bytes_from_os_str);
 let mut config = Vec::new();
 let mut color = None;
diff --git a/rust/rhg/src/blackbox.rs b/rust/rhg/src/blackbox.rs
--- a/rust/rhg/src/blackbox.rs
+++ b/rust/rhg/src/blackbox.rs
@@ -5,6 +5,7 @@
 use 

D12542: narrow: support debugupgraderepo

2022-04-12 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: durin42.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D12542

AFFECTED FILES
  mercurial/upgrade_utils/actions.py
  tests/test-narrow.t

CHANGE DETAILS

diff --git a/tests/test-narrow.t b/tests/test-narrow.t
--- a/tests/test-narrow.t
+++ b/tests/test-narrow.t
@@ -71,6 +71,17 @@
   updating to branch default
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
 
+The "narrow" repo requirement is ignored by [debugupgraderepo]
+
+#if tree
+  $ (cd should-work; hg debugupgraderepo)
+  abort: cannot upgrade repository; unsupported source requirement: 
treemanifest
+  [255]
+#else
+  $ (cd should-work; hg debugupgraderepo | grep 'no format upgrades found in 
existing repository')
+  (no format upgrades found in existing repository)
+#endif
+
 Test repo with local changes
   $ hg clone --narrow ssh://user@dummy/master narrow-local-changes --include 
d0 --include d3 --include d6
   requesting all changes
diff --git a/mercurial/upgrade_utils/actions.py 
b/mercurial/upgrade_utils/actions.py
--- a/mercurial/upgrade_utils/actions.py
+++ b/mercurial/upgrade_utils/actions.py
@@ -33,10 +33,16 @@
 requirements.CHANGELOGV2_REQUIREMENT,
 }
 
-
 def preservedrequirements(repo):
 preserved = {
 requirements.SHARED_REQUIREMENT,
+# Blindly skipping the narrow requirement may not be correct in 
general,
+# for example if there are some upgrades that would replay the commits,
+# instead of doing a per-revlog rewrite.
+#
+# This is not a big deal for us, because
+# for rewrites like that we'll want to re-clone the repo, anyway.
+requirements.NARROW_REQUIREMENT,
 }
 return preserved & repo.requirements
 
@@ -1004,7 +1010,7 @@
 def supporteddestrequirements(repo):
 """Obtain requirements that upgrade supports in the destination.
 
-If the result of the upgrade would create requirements not in this set,
+If the result of the upgrade would have requirements not in this set,
 the upgrade is disallowed.
 
 Extensions should monkeypatch this to add their custom requirements.
@@ -1024,6 +1030,7 @@
 requirements.SHARESAFE_REQUIREMENT,
 requirements.SPARSEREVLOG_REQUIREMENT,
 requirements.STORE_REQUIREMENT,
+requirements.NARROW_REQUIREMENT,
 }
 for name in compression.compengines:
 engine = compression.compengines[name]



To: aalekseyev, durin42, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D12213: rhg: simplify the handling of share-safe config mismatch

2022-02-21 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D12213

AFFECTED FILES
  rust/hg-core/src/repo.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/repo.rs b/rust/hg-core/src/repo.rs
--- a/rust/hg-core/src/repo.rs
+++ b/rust/hg-core/src/repo.rs
@@ -6,7 +6,6 @@
 use crate::dirstate_tree::owning::OwningDirstateMap;
 use crate::errors::HgResultExt;
 use crate::errors::{HgError, IoResultExt};
-use crate::exit_codes;
 use crate::lock::{try_with_lock_no_wait, LockError};
 use crate::manifest::{Manifest, Manifestlog};
 use crate::revlog::filelog::Filelog;
@@ -160,31 +159,8 @@
 requirements::load(Vfs { base: _path })?
 .contains(requirements::SHARESAFE_REQUIREMENT);
 
-if share_safe && !source_is_share_safe {
-return Err(match config
-.get(b"share", b"safe-mismatch.source-not-safe")
-{
-Some(b"abort") | None => HgError::abort(
-"abort: share source does not support share-safe 
requirement\n\
-(see `hg help config.format.use-share-safe` for more 
information)",
-exit_codes::ABORT,
-),
-_ => HgError::unsupported("share-safe downgrade"),
-}
-.into());
-} else if source_is_share_safe && !share_safe {
-return Err(
-match config.get(b"share", b"safe-mismatch.source-safe") {
-Some(b"abort") | None => HgError::abort(
-"abort: version mismatch: source uses share-safe \
-functionality while the current share does not\n\
-(see `hg help config.format.use-share-safe` for 
more information)",
-exit_codes::ABORT,
-),
-_ => HgError::unsupported("share-safe upgrade"),
-}
-.into(),
-);
+if share_safe != source_is_share_safe {
+return Err(HgError::unsupported("share-safe mismatch").into());
 }
 
 if share_safe {



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D12202: status: fix hg status race against file deletion

2022-02-17 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D12202

AFFECTED FILES
  rust/hg-core/src/dirstate_tree/status.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate_tree/status.rs 
b/rust/hg-core/src/dirstate_tree/status.rs
--- a/rust/hg-core/src/dirstate_tree/status.rs
+++ b/rust/hg-core/src/dirstate_tree/status.rs
@@ -713,7 +713,17 @@
 let mut results = Vec::new();
 for entry in path.read_dir()? {
 let entry = entry?;
-let metadata = entry.metadata()?;
+let metadata = match entry.metadata() {
+Ok(v) => v,
+Err(e) => {
+// race with file deletion?
+if e.kind() == std::io::ErrorKind::NotFound {
+continue;
+} else {
+return Err(e);
+}
+}
+};
 let name = get_bytes_from_os_string(entry.file_name());
 // FIXME don't do this when cached
 if name == b".hg" {



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D12009: revlog: fix a bug where transaction can be aborted partially

2022-01-20 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  Fix a repo corruption bug caused by a partial transaction rollback.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D12009

AFFECTED FILES
  mercurial/revlog.py
  tests/test-transaction-rollback-on-revlog-split.t

CHANGE DETAILS

diff --git a/tests/test-transaction-rollback-on-revlog-split.t 
b/tests/test-transaction-rollback-on-revlog-split.t
--- a/tests/test-transaction-rollback-on-revlog-split.t
+++ b/tests/test-transaction-rollback-on-revlog-split.t
@@ -63,7 +63,7 @@
   [80]
 #endif
   $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | tail -1
-  data/file.i 192
+  data/file.i 128
 
 The first file.i entry should match the "Reference size" above.
 The first file.d entry is the temporary record during the split,
@@ -73,14 +73,14 @@
   $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file
   data/file.i 1174
   data/file.d 0
-  data/file.d 1067
-  data/file.i 192
+  data/file.d 1046
+  data/file.i 128
   $ hg recover
   rolling back interrupted transaction
   (verify step skipped, run `hg verify` to check your repository content)
   $ f -s .hg/store/data/file*
-  .hg/store/data/file.d: size=1067
-  .hg/store/data/file.i: size=192
+  .hg/store/data/file.d: size=1046
+  .hg/store/data/file.i: size=128
   $ hg tip
   changeset:   1:cfa8d6e60429
   tag: tip
@@ -90,23 +90,12 @@
   
   $ hg verify -q
warning: revlog 'data/file.d' not in fncache!
-   file@?: rev 2 points to nonexistent changeset 2
-   (expected )
-   file@?: fa1120531cc1 not in manifests
-  2 warnings encountered!
+  1 warnings encountered!
   hint: run "hg debugrebuildfncache" to recover from corrupt fncache
-  2 integrity errors encountered!
-  [1]
   $ hg debugrebuildfncache --only-data
   adding data/file.d
   1 items added, 0 removed from fncache
   $ hg verify -q
-   file@?: rev 2 points to nonexistent changeset 2
-   (expected )
-   file@?: fa1120531cc1 not in manifests
-  1 warnings encountered!
-  2 integrity errors encountered!
-  [1]
   $ cd ..
 
 
@@ -134,13 +123,13 @@
   $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file
   data/file.i 1174
   data/file.d 0
-  data/file.d 1067
+  data/file.d 1046
 
   $ hg recover
   rolling back interrupted transaction
   (verify step skipped, run `hg verify` to check your repository content)
   $ f -s .hg/store/data/file*
-  .hg/store/data/file.d: size=1067
+  .hg/store/data/file.d: size=1046
   .hg/store/data/file.i: size=1174
   $ hg tip
   changeset:   1:cfa8d6e60429
@@ -172,8 +161,8 @@
   abort: pretxnchangegroup hook exited with status 1
   [40]
   $ f -s .hg/store/data/file*
-  .hg/store/data/file.d: size=1067
-  .hg/store/data/file.i: size=192
+  .hg/store/data/file.d: size=1046
+  .hg/store/data/file.i: size=128
   $ hg tip
   changeset:   1:cfa8d6e60429
   tag: tip
@@ -183,12 +172,7 @@
   
   $ hg verify -q
warning: revlog 'data/file.d' not in fncache!
-   file@?: rev 2 points to nonexistent changeset 2
-   (expected )
-   file@?: fa1120531cc1 not in manifests
-  2 warnings encountered!
+  1 warnings encountered!
   hint: run "hg debugrebuildfncache" to recover from corrupt fncache
-  2 integrity errors encountered!
-  [1]
   $ cd ..
 
diff --git a/mercurial/revlog.py b/mercurial/revlog.py
--- a/mercurial/revlog.py
+++ b/mercurial/revlog.py
@@ -2006,7 +2006,7 @@
 raise error.RevlogError(
 _(b"%s not found in the transaction") % self._indexfile
 )
-trindex = 0
+trindex = None
 tr.add(self._datafile, 0)
 
 existing_handles = False
@@ -2029,10 +2029,17 @@
 with self._indexfp() as read_ifh:
 for r in self:
 new_dfh.write(self._getsegmentforrevs(r, r, 
df=read_ifh)[1])
-if troffset <= self.start(r) + r * self.index.entry_size:
+if (
+trindex is None
+and troffset
+<= self.start(r) + r * self.index.entry_size
+):
 trindex = r
 new_dfh.flush()
 
+if trindex is None:
+trindex = 0
+
 with self.__index_new_fp() as fp:
 self._format_flags &= ~FLAG_INLINE_DATA
 self._inline = False



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D12008: revlog: demonstrate a bug where transaction can be aborted partially

2022-01-20 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D12008

AFFECTED FILES
  tests/test-transaction-rollback-on-revlog-split.t

CHANGE DETAILS

diff --git a/tests/test-transaction-rollback-on-revlog-split.t 
b/tests/test-transaction-rollback-on-revlog-split.t
--- a/tests/test-transaction-rollback-on-revlog-split.t
+++ b/tests/test-transaction-rollback-on-revlog-split.t
@@ -17,22 +17,28 @@
   > extensions.wrapfunction(util.atomictempfile, 'close', close)
   > EOF
 
-Test offset computation to correctly factor in the index entries themselve.
+Test offset computation to correctly factor in the index entries themselves.
 Also test that the new data size has the correct size if the transaction is 
aborted
 after the index has been replaced.
 
-Test repo has one small, one moderate and one big change. The clone has
-the small and moderate change and will transition to non-inline storage when
-adding the big change.
+Test repo has commits a, b, c, D, where D is large (grows the revlog enough 
that it
+transitions to non-inline storage). The clone initially has changes a, b
+and will transition to non-inline storage when adding c, D.
+
+If the transaction adding c, D is rolled back, then we don't undo the revlog 
split,
+but truncate the index and the data to remove both c and D.
 
   $ hg init troffset-computation --config format.revlog-compression=none
   $ cd troffset-computation
   $ printf '%20d' '1' > file
-  $ hg commit -Aqm_
+  $ hg commit -Aqma
   $ printf '%1024d' '1' > file
-  $ hg commit -Aqm_
+  $ hg commit -Aqmb
+  $ printf '%20d' '1' > file
+  $ hg commit -Aqmc
   $ dd if=/dev/zero of=file bs=1k count=128 > /dev/null 2>&1
-  $ hg commit -Aqm_
+  $ hg commit -AqmD
+
   $ cd ..
 
   $ hg clone -r 1 troffset-computation troffset-computation-copy --config 
format.revlog-compression=none -q
@@ -57,9 +63,9 @@
   [80]
 #endif
   $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file | tail -1
-  data/file.i 128
+  data/file.i 192
 
-The first file.i entry should match the size above.
+The first file.i entry should match the "Reference size" above.
 The first file.d entry is the temporary record during the split,
 the second entry after the split happened. The sum of the second file.d
 and the second file.i entry should match the first file.i entry.
@@ -67,29 +73,40 @@
   $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file
   data/file.i 1174
   data/file.d 0
-  data/file.d 1046
-  data/file.i 128
+  data/file.d 1067
+  data/file.i 192
   $ hg recover
   rolling back interrupted transaction
   (verify step skipped, run `hg verify` to check your repository content)
   $ f -s .hg/store/data/file*
-  .hg/store/data/file.d: size=1046
-  .hg/store/data/file.i: size=128
+  .hg/store/data/file.d: size=1067
+  .hg/store/data/file.i: size=192
   $ hg tip
-  changeset:   1:3ce491143aec
+  changeset:   1:cfa8d6e60429
   tag: tip
   user:test
   date:Thu Jan 01 00:00:00 1970 +
-  summary: _
+  summary: b
   
   $ hg verify -q
warning: revlog 'data/file.d' not in fncache!
-  1 warnings encountered!
+   file@?: rev 2 points to nonexistent changeset 2
+   (expected )
+   file@?: fa1120531cc1 not in manifests
+  2 warnings encountered!
   hint: run "hg debugrebuildfncache" to recover from corrupt fncache
+  2 integrity errors encountered!
+  [1]
   $ hg debugrebuildfncache --only-data
   adding data/file.d
   1 items added, 0 removed from fncache
   $ hg verify -q
+   file@?: rev 2 points to nonexistent changeset 2
+   (expected )
+   file@?: fa1120531cc1 not in manifests
+  1 warnings encountered!
+  2 integrity errors encountered!
+  [1]
   $ cd ..
 
 
@@ -117,20 +134,20 @@
   $ cat .hg/store/journal | tr -s '\000' ' ' | grep data/file
   data/file.i 1174
   data/file.d 0
-  data/file.d 1046
+  data/file.d 1067
 
   $ hg recover
   rolling back interrupted transaction
   (verify step skipped, run `hg verify` to check your repository content)
   $ f -s .hg/store/data/file*
-  .hg/store/data/file.d: size=1046
+  .hg/store/data/file.d: size=1067
   .hg/store/data/file.i: size=1174
   $ hg tip
-  changeset:   1:3ce491143aec
+  changeset:   1:cfa8d6e60429
   tag: tip
   user:test
   date:Thu Jan 01 00:00:00 1970 +
-  summary: _
+  summary: b
   
   $ hg verify -q
   $ cd ..
@@ -155,18 +172,23 @@
   abort: pretxnchangegroup hook exited with status 1
   [40]
   $ f -s .hg/store/data/file*
-  .hg/store/data/file.d: size=1046
-  .hg/store/data/file.i: size=128
+  .hg/store/data/file.d: size=1067
+  .hg/store/data/file.i: size=192
   $ hg tip
-  changeset:   1:3ce491143aec
+  changeset:   1:cfa8d6e60429
   tag: tip
   user:test
   date:Thu Jan 01 00:00:00 1970 +
-  summary: _
+  summary: b
   
   $ hg verify -q
warning: revlog 'data/file.d' 

D11882: rhg: fix a crash on non-generaldelta revlogs

2021-12-07 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D11882

AFFECTED FILES
  rust/hg-core/src/revlog/index.rs
  rust/hg-core/src/revlog/revlog.rs
  tests/test-rhg-no-generaldelta.t

CHANGE DETAILS

diff --git a/tests/test-rhg-no-generaldelta.t b/tests/test-rhg-no-generaldelta.t
--- a/tests/test-rhg-no-generaldelta.t
+++ b/tests/test-rhg-no-generaldelta.t
@@ -22,9 +22,7 @@
 1   120prev 14143 93   
0.6503593 00.0
 2   131prev 12141105   
0.74468   105 00.0
 
-rhg breaks on non-generaldelta revlogs:
+rhg works on non-generaldelta revlogs:
 
   $ $NO_FALLBACK hg cat f -r . | f --sha256 --size
-  abort: corrupted revlog (rhg !)
-  size=0, 
sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 (rhg !)
-  size=141, 
sha256=1a7fe778e33d64d5e14a9a126b77038b328356e67bacf308797bc0e39bf204f3 (no-rhg 
!)
+  size=141, 
sha256=1a7fe778e33d64d5e14a9a126b77038b328356e67bacf308797bc0e39bf204f3
diff --git a/rust/hg-core/src/revlog/revlog.rs 
b/rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/revlog/revlog.rs
+++ b/rust/hg-core/src/revlog/revlog.rs
@@ -191,11 +191,20 @@
 // Todo return -> Cow
 let mut entry = self.get_entry(rev)?;
 let mut delta_chain = vec![];
-while let Some(base_rev) = entry.base_rev {
-delta_chain.push(entry);
-entry = self
-.get_entry(base_rev)
-.map_err(|_| RevlogError::corrupted())?;
+
+if self.index.uses_generaldelta() {
+while let Some(base_rev) = entry.base_rev_or_base_of_delta_chain {
+delta_chain.push(entry);
+entry = self.get_entry_internal(base_rev)?;
+}
+} else {
+if let Some(base_rev) = entry.base_rev_or_base_of_delta_chain {
+delta_chain.push(entry);
+entry = self.get_entry_internal(base_rev)?;
+for rev in (base_rev + 1..rev).rev() {
+delta_chain.push(self.get_entry_internal(rev)?);
+}
+}
 }
 
 // TODO do not look twice in the index
@@ -291,14 +300,26 @@
 bytes: data,
 compressed_len: index_entry.compressed_len(),
 uncompressed_len: index_entry.uncompressed_len(),
-base_rev: if index_entry.base_revision() == rev {
+base_rev_or_base_of_delta_chain: if index_entry
+.base_revision_or_base_of_delta_chain()
+== rev
+{
 None
 } else {
-Some(index_entry.base_revision())
+Some(index_entry.base_revision_or_base_of_delta_chain())
 },
 };
 Ok(entry)
 }
+
+// when resolving internal references within revlog, any errors
+// should be reported as corruption, instead of e.g. "invalid revision"
+fn get_entry_internal(
+,
+rev: Revision,
+) -> Result {
+return self.get_entry(rev).map_err(|_| RevlogError::corrupted());
+}
 }
 
 /// The revlog entry's bytes and the necessary informations to extract
@@ -309,7 +330,7 @@
 bytes: &'a [u8],
 compressed_len: usize,
 uncompressed_len: usize,
-base_rev: Option,
+base_rev_or_base_of_delta_chain: Option,
 }
 
 impl<'a> RevlogEntry<'a> {
@@ -375,7 +396,7 @@
 /// Tell if the entry is a snapshot or a delta
 /// (influences on decompression).
 fn is_delta() -> bool {
-self.base_rev.is_some()
+self.base_rev_or_base_of_delta_chain.is_some()
 }
 }
 
diff --git a/rust/hg-core/src/revlog/index.rs b/rust/hg-core/src/revlog/index.rs
--- a/rust/hg-core/src/revlog/index.rs
+++ b/rust/hg-core/src/revlog/index.rs
@@ -85,6 +85,7 @@
 /// Offsets of starts of index blocks.
 /// Only needed when the index is interleaved with data.
 offsets: Option>,
+uses_generaldelta: bool,
 }
 
 impl Index {
@@ -101,6 +102,11 @@
 return Err(HgError::corrupted("unsupported revlog version"));
 }
 
+// This is only correct because we know version is REVLOGV1.
+// In v2 we always use generaldelta, while in v0 we never use
+// generaldelta. Similar for [is_inline] (it's only used in v1).
+let uses_generaldelta = header.format_flags().uses_generaldelta();
+
 if header.format_flags().is_inline() {
 let mut offset: usize = 0;
 let mut offsets = Vec::new();
@@ -120,6 +126,7 @@
 Ok(Self {
 bytes,
 offsets: Some(offsets),
+uses_generaldelta,
 })
 } else {
 Err(HgError::corrupted("unexpected 

D11881: rhg: centralize index header parsing

2021-12-07 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  Centralize index header parsing, parse the generaldelta flag,
  and leave breadcrumbs to relate the code to python.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D11881

AFFECTED FILES
  rust/hg-core/src/revlog/index.rs
  rust/hg-core/src/revlog/revlog.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/revlog/revlog.rs 
b/rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/revlog/revlog.rs
+++ b/rust/hg-core/src/revlog/revlog.rs
@@ -3,7 +3,6 @@
 use std::ops::Deref;
 use std::path::Path;
 
-use byteorder::{BigEndian, ByteOrder};
 use flate2::read::ZlibDecoder;
 use micro_timer::timed;
 use sha1::{Digest, Sha1};
@@ -74,13 +73,6 @@
 match repo.store_vfs().mmap_open_opt(_path)? {
 None => Index::new(Box::new(vec![])),
 Some(index_mmap) => {
-let version = get_version(_mmap)?;
-if version != 1 {
-// A proper new version should have had a repo/store
-// requirement.
-return Err(HgError::corrupted("corrupted revlog"));
-}
-
 let index = Index::new(Box::new(index_mmap))?;
 Ok(index)
 }
@@ -387,19 +379,6 @@
 }
 }
 
-/// Format version of the revlog.
-pub fn get_version(index_bytes: &[u8]) -> Result {
-if index_bytes.len() == 0 {
-return Ok(1);
-};
-if index_bytes.len() < 4 {
-return Err(HgError::corrupted(
-"corrupted revlog: can't read the index format header",
-));
-};
-Ok(BigEndian::read_u16(_bytes[2..=3]))
-}
-
 /// Calculate the hash of a revision given its data and its parents.
 fn hash(
 data: &[u8],
diff --git a/rust/hg-core/src/revlog/index.rs b/rust/hg-core/src/revlog/index.rs
--- a/rust/hg-core/src/revlog/index.rs
+++ b/rust/hg-core/src/revlog/index.rs
@@ -9,6 +9,76 @@
 
 pub const INDEX_ENTRY_SIZE: usize = 64;
 
+pub struct IndexHeader {
+header_bytes: [u8; 4],
+}
+
+#[derive(Copy, Clone)]
+pub struct IndexHeaderFlags {
+flags: u16,
+}
+
+// Corresponds to the high bits of `_format_flags` in python
+impl IndexHeaderFlags {
+// Corresponds to FLAG_INLINE_DATA in python
+pub fn is_inline(self) -> bool {
+return self.flags & 1 != 0;
+}
+// Corresponds to FLAG_GENERALDELTA in python
+pub fn uses_generaldelta(self) -> bool {
+return self.flags & 2 != 0;
+}
+}
+
+// Corresponds to the INDEX_HEADER structure,
+// which is parsed as a `header` variable in `_loadindex` in `revlog.py`
+impl IndexHeader {
+fn format_flags() -> IndexHeaderFlags {
+// No "unknown flags" check here, unlike in python. Maybe there should
+// be.
+return IndexHeaderFlags {
+flags: BigEndian::read_u16(_bytes[0..2]),
+};
+}
+
+// The only revlog version currently supported by rhg.
+const REVLOGV1: u16 = 1;
+
+// Corresponds to `_format_version` in Python.
+// The only curently supported version is
+fn format_version() -> u16 {
+return BigEndian::read_u16(_bytes[2..4]);
+}
+
+const EMPTY_INDEX_HEADER: IndexHeader = IndexHeader {
+// We treat an empty file as a valid index with no entries.
+// Here we make an arbitrary choice of what we assume the format of the
+// index to be (V1, using generaldelta).
+// This doesn't matter too much, since we're only doing read-only
+// access. but the value corresponds to the `new_header` variable in
+// `revlog.py`, `_loadindex`
+header_bytes: [0, 3, 0, 1],
+};
+
+fn parse(index_bytes: &[u8]) -> Result {
+if index_bytes.len() == 0 {
+return Ok(IndexHeader::EMPTY_INDEX_HEADER);
+}
+if index_bytes.len() < 4 {
+return Err(HgError::corrupted(
+"corrupted revlog: can't read the index format header",
+));
+}
+return Ok(IndexHeader {
+header_bytes: {
+let bytes: [u8; 4] =
+index_bytes[0..4].try_into().expect("impossible");
+bytes
+},
+});
+}
+}
+
 /// A Revlog index
 pub struct Index {
 bytes: Box + Send>,
@@ -23,7 +93,15 @@
 pub fn new(
 bytes: Box + Send>,
 ) -> Result {
-if is_inline() {
+let header = IndexHeader::parse(bytes.as_ref())?;
+
+if header.format_version() != IndexHeader::REVLOGV1 {
+// A proper new version should have had a repo/store
+// requirement.
+return Err(HgError::corrupted("unsupported revlog version"));
+}
+
+if header.format_flags().is_inline() {
 let mut offset: usize = 0;
 let mut offsets = Vec::new();
 
@@ 

D11880: rhg: demonstrate that rhg breaks on non-generaldelta revlogs

2021-12-07 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D11880

AFFECTED FILES
  tests/test-rhg-no-generaldelta.t

CHANGE DETAILS

diff --git a/tests/test-rhg-no-generaldelta.t b/tests/test-rhg-no-generaldelta.t
new file mode 100644
--- /dev/null
+++ b/tests/test-rhg-no-generaldelta.t
@@ -0,0 +1,30 @@
+
+  $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
+
+  $ cat << EOF >> $HGRCPATH
+  > [format]
+  > sparse-revlog = no
+  > EOF
+
+  $ hg init repo --config format.generaldelta=no --config 
format.usegeneraldelta=no
+  $ cd repo
+  $ seq 50 > f
+  $ hg commit -q -Am initial
+  $ echo x >> f
+  $ hg commit -q -Am x
+  $ hg update .^
+  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ (seq 50; echo x) | (read; cat) > f
+  $ hg commit -q -Am y
+  $ hg debugdeltachain f
+  rev  chain# chainlen prev   delta   sizerawsize  chainsize   
  ratio   lindist extradist extraratio
+0   11   -1base 79141 79   
0.5602879 00.0
+1   120prev 14143 93   
0.6503593 00.0
+2   131prev 12141105   
0.74468   105 00.0
+
+rhg breaks on non-generaldelta revlogs:
+
+  $ $NO_FALLBACK hg cat f -r . | f --sha256 --size
+  abort: corrupted revlog (rhg !)
+  size=0, 
sha256=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 (rhg !)
+  size=141, 
sha256=1a7fe778e33d64d5e14a9a126b77038b328356e67bacf308797bc0e39bf204f3 (no-rhg 
!)



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11818: rhg: refactor to use IgnoreFnType alias more widely

2021-11-29 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11818

AFFECTED FILES
  rust/hg-core/src/matchers.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/matchers.rs b/rust/hg-core/src/matchers.rs
--- a/rust/hg-core/src/matchers.rs
+++ b/rust/hg-core/src/matchers.rs
@@ -22,6 +22,7 @@
 PatternSyntax,
 };
 
+use crate::dirstate::status::IgnoreFnType;
 use crate::filepatterns::normalize_path_bytes;
 use std::borrow::ToOwned;
 use std::collections::HashSet;
@@ -246,7 +247,7 @@
 /// ```
 pub struct IncludeMatcher<'a> {
 patterns: Vec,
-match_fn: Box Fn(&'r HgPath) -> bool + 'a + Sync>,
+match_fn: IgnoreFnType<'a>,
 /// Whether all the patterns match a prefix (i.e. recursively)
 prefix: bool,
 roots: HashSet,
@@ -341,9 +342,9 @@
 
 /// Returns the regex pattern and a function that matches an `HgPath` against
 /// said regex formed by the given ignore patterns.
-fn build_regex_match(
-ignore_patterns: &[IgnorePattern],
-) -> PatternResult<(Vec, Box bool + Sync>)> {
+fn build_regex_match<'a, 'b>(
+ignore_patterns: &'a [IgnorePattern],
+) -> PatternResult<(Vec, IgnoreFnType<'b>)> {
 let mut regexps = vec![];
 let mut exact_set = HashSet::new();
 
@@ -365,10 +366,10 @@
 let func = move |filename: | {
 exact_set.contains(filename) || matcher(filename)
 };
-Box::new(func) as Box bool + Sync>
+Box::new(func) as IgnoreFnType
 } else {
 let func = move |filename: | exact_set.contains(filename);
-Box::new(func) as Box bool + Sync>
+Box::new(func) as IgnoreFnType
 };
 
 Ok((full_regex, func))
@@ -479,8 +480,8 @@
 /// should be matched.
 fn build_match<'a, 'b>(
 ignore_patterns: Vec,
-) -> PatternResult<(Vec, Box bool + 'b + Sync>)> {
-let mut match_funcs: Vec bool + Sync>> = vec![];
+) -> PatternResult<(Vec, IgnoreFnType<'b>)> {
+let mut match_funcs: Vec> = vec![];
 // For debugging and printing
 let mut patterns = vec![];
 
@@ -567,10 +568,7 @@
 mut all_pattern_files: Vec,
 root_dir: ,
 inspect_pattern_bytes:  impl FnMut(&[u8]),
-) -> PatternResult<(
-Box Fn(&'r HgPath) -> bool + Sync + 'a>,
-Vec,
-)> {
+) -> PatternResult<(IgnoreFnType<'a>, Vec)> {
 let mut all_patterns = vec![];
 let mut all_warnings = vec![];
 



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11817: sparse: lock the store when updating requirements config

2021-11-29 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D11817

AFFECTED FILES
  mercurial/sparse.py
  tests/test-sparse-with-safe-share.t

CHANGE DETAILS

diff --git a/tests/test-sparse-with-safe-share.t 
b/tests/test-sparse-with-safe-share.t
--- a/tests/test-sparse-with-safe-share.t
+++ b/tests/test-sparse-with-safe-share.t
@@ -16,10 +16,8 @@
   $ echo x > hide
   $ hg ci -Aqm 'initial'
 
-Verify basic --include
+Regression test: checks that this command correctly locks the store
+before updating the store [requirements] config.
 
   $ hg up -q 0
   $ hg debugsparse --include 'hide'
-  devel-warn: write with no lock: "requires" at: *mercurial/scmutil.py:1558 
(writerequires) (glob)
-
-TODO: bug in sparse when used together with safe-share^
diff --git a/mercurial/sparse.py b/mercurial/sparse.py
--- a/mercurial/sparse.py
+++ b/mercurial/sparse.py
@@ -718,7 +718,7 @@
 
 The new config is written out and a working directory refresh is performed.
 """
-with repo.wlock(), repo.dirstate.parentchange():
+with repo.wlock(), repo.lock(), repo.dirstate.parentchange():
 raw = repo.vfs.tryread(b'sparse')
 oldinclude, oldexclude, oldprofiles = parseconfig(
 repo.ui, raw, b'sparse'



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11766: sparse: demonstrate a bug when used with safe-share

2021-11-16 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11766

AFFECTED FILES
  tests/test-sparse-with-safe-share.t

CHANGE DETAILS

diff --git a/tests/test-sparse-with-safe-share.t 
b/tests/test-sparse-with-safe-share.t
new file mode 100644
--- /dev/null
+++ b/tests/test-sparse-with-safe-share.t
@@ -0,0 +1,25 @@
+Same with share-safe
+
+  $ echo "[format]" >> $HGRCPATH
+  $ echo "use-share-safe = True" >> $HGRCPATH
+
+  $ cd $TESTTMP
+
+  $ hg init myrepo
+  $ cd myrepo
+  $ cat > .hg/hgrc < [extensions]
+  > sparse=
+  > EOF
+
+  $ echo a > show
+  $ echo x > hide
+  $ hg ci -Aqm 'initial'
+
+Verify basic --include
+
+  $ hg up -q 0
+  $ hg debugsparse --include 'hide'
+  devel-warn: write with no lock: "requires" at: *mercurial/scmutil.py:1558 
(writerequires) (glob)
+
+TODO: bug in sparse when used together with safe-share^



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11764: rhg: add support for narrow clones and sparse checkouts

2021-11-16 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This adds a minimal support that can be implemented without parsing the 
narrowspec.
  We can parse the narrowspec and add support for more operations later.
  
  The reason we need so few code changes is as follows:
  
  Most operations need no special treatment of sparse because
  some of them only read dirstate (`rhg files` without `-r`),
  which bakes in the filtering,
  some of them only read store (`rhg files -r`, `rhg cat`),
  and some of them read no data at all (`rhg root`, `rhg debugrequirements`).
  
  `status` is the command that might care about sparse, so we just disable
  rhg on it.
  
  For narrow clones, `rhg files` clearly needs the narrowspec to work
  correctly, so we fall back.
  `rhg cat` seems to work consistently with `hg cat` if the file exists.
  If the file is hidden by narrow spec, the error message is different and 
confusing, so
  that's something that we should improve in follow-up patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11764

AFFECTED FILES
  rust/hg-core/src/repo.rs
  rust/hg-core/src/requirements.rs
  rust/rhg/src/commands/files.rs
  rust/rhg/src/commands/status.rs
  rust/rhg/src/main.rs
  tests/test-rhg-sparse-narrow.t

CHANGE DETAILS

diff --git a/tests/test-rhg-sparse-narrow.t b/tests/test-rhg-sparse-narrow.t
new file mode 100644
--- /dev/null
+++ b/tests/test-rhg-sparse-narrow.t
@@ -0,0 +1,120 @@
+#require rhg
+
+  $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
+
+Rhg works well when sparse working copy is enabled.
+
+  $ cd "$TESTTMP"
+  $ hg init repo-sparse
+  $ cd repo-sparse
+  $ cat > .hg/hgrc < [extensions]
+  > sparse=
+  > EOF
+
+  $ echo a > show
+  $ echo x > hide
+  $ mkdir dir1 dir2
+  $ echo x > dir1/x
+  $ echo y > dir1/y
+  $ echo z > dir2/z
+
+  $ hg ci -Aqm 'initial'
+  $ hg debugsparse --include 'show'
+  $ ls -A
+  .hg
+  show
+
+  $ tip=$(hg log -r . --template '{node}')
+  $ $NO_FALLBACK rhg files -r "$tip"
+  dir1/x
+  dir1/y
+  dir2/z
+  hide
+  show
+  $ $NO_FALLBACK rhg files
+  show
+
+  $ $NO_FALLBACK rhg cat -r "$tip" hide
+  x
+
+  $ cd ..
+
+We support most things when narrow is enabled, too, with a couple of caveats.
+
+  $ . "$TESTDIR/narrow-library.sh"
+  $ real_hg=$RHG_FALLBACK_EXECUTABLE
+
+  $ cat >> $HGRCPATH < [extensions]
+  > narrow=
+  > EOF
+
+  $ hg clone --narrow  ./repo-sparse repo-narrow --include dir1
+  requesting all changes
+  adding changesets
+  adding manifests
+  adding file changes
+  added 1 changesets with 2 changes to 2 files
+  new changesets 6d714a4a2998
+  updating to branch default
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+  $ cd repo-narrow
+
+  $ $NO_FALLBACK rhg cat -r "$tip" dir1/x
+  x
+  $ "$real_hg" cat -r "$tip" dir1/x
+  x
+
+TODO: bad error message
+
+  $ $NO_FALLBACK rhg cat -r "$tip" hide
+  abort: invalid revision identifier: 6d714a4a2998cbfd0620db44da58b749f6565d63
+  [255]
+  $ "$real_hg" cat -r "$tip" hide
+  [1]
+
+A naive implementation of [rhg files] leaks the paths that are supposed to be
+hidden by narrow, so we just fall back to hg.
+
+  $ $NO_FALLBACK rhg files -r "$tip"
+  unsupported feature: rhg files -r  is not support in narrow clones
+  [252]
+  $ "$real_hg" files -r "$tip"
+  dir1/x
+  dir1/y
+
+Hg status needs to do some filtering based on narrow spec, so we don't
+support it in rhg for narrow clones yet.
+
+  $ mkdir dir2
+  $ touch dir2/q
+  $ "$real_hg" status
+  $ $NO_FALLBACK rhg --config rhg.status=true status
+  unsupported feature: rhg status is not supported for sparse checkouts or 
narrow clones yet
+  [252]
+
+Adding "orphaned" index files:
+
+  $ (cd ..; cp {repo-sparse,repo-narrow}/.hg/store/data/hide.i)
+  $ (cd ..; mkdir repo-narrow/.hg/store/data/dir2; cp 
{repo-sparse,repo-narrow}/.hg/store/data/dir2/z.i)
+  $ "$real_hg" verify
+  checking changesets
+  checking manifests
+  crosschecking files in changesets and manifests
+  checking files
+  checked 1 changesets with 2 changes to 2 files
+
+  $ "$real_hg" files -r "$tip"
+  dir1/x
+  dir1/y
+
+# TODO: even though [hg files] hides the orphaned dir2/z, [hg cat] still shows 
it.
+# rhg has the same issue, but at least it's not specific to rhg.
+# This is despite [hg verify] succeeding above.
+
+  $ $NO_FALLBACK rhg cat -r "$tip" dir2/z
+  z
+  $ "$real_hg" cat -r "$tip" dir2/z
+  z
diff --git a/rust/rhg/src/main.rs b/rust/rhg/src/main.rs
--- a/rust/rhg/src/main.rs
+++ b/rust/rhg/src/main.rs
@@ -588,7 +588,8 @@
 }
 }
 
-const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
+const SUPPORTED_EXTENSIONS: &[&[u8]] =
+&[b"blackbox", b"share", b"sparse", b"narrow"];
 
 fn check_extensions(config: ) -> Result<(), CommandError> {
 let enabled = config.get_section_keys(b"extensions");
diff --git a/rust/rhg/src/commands/status.rs b/rust/rhg/src/commands/status.rs
--- 

D11752: rhg: allow rhg in sparse repos when the operations only need the store

2021-11-11 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11752

AFFECTED FILES
  rust/hg-core/src/operations/list_tracked_files.rs
  rust/hg-core/src/repo.rs
  rust/hg-core/src/requirements.rs
  rust/rhg/src/blackbox.rs
  rust/rhg/src/commands/status.rs
  rust/rhg/src/main.rs
  tests/test-rhg-sparse.t

CHANGE DETAILS

diff --git a/tests/test-rhg-sparse.t b/tests/test-rhg-sparse.t
new file mode 100644
--- /dev/null
+++ b/tests/test-rhg-sparse.t
@@ -0,0 +1,33 @@
+#require rhg
+
+  $ NO_FALLBACK="env RHG_ON_UNSUPPORTED=abort"
+
+Even though sparse working copy is not supported, the commands
+that operate on the store still work even if the working copy is sparse.
+
+  $ cd "$TESTTMP"
+  $ hg init repo-sparse
+  $ cd repo-sparse
+  $ cat > .hg/hgrc < [extensions]
+  > sparse=
+  > EOF
+
+  $ echo a > show
+  $ echo x > hide
+  $ hg ci -Aqm 'initial'
+  $ hg debugsparse --include 'show'
+  $ ls -A
+  .hg
+  show
+
+  $ tip=$(hg log -r . --template '{node}')
+  $ $NO_FALLBACK rhg files -r "$tip"
+  hide
+  show
+  $ $NO_FALLBACK rhg files
+  unsupported feature: repository uses a sparse working copy, this is not 
supported by rhg
+  [252]
+
+  $ $NO_FALLBACK rhg cat -r "$tip" hide
+  x
diff --git a/rust/rhg/src/main.rs b/rust/rhg/src/main.rs
--- a/rust/rhg/src/main.rs
+++ b/rust/rhg/src/main.rs
@@ -104,7 +104,7 @@
 
 if let Ok(repo) = repo {
 // We don't support subrepos, fallback if the subrepos file is present
-if repo.working_directory_vfs().join(".hgsub").exists() {
+if repo.is_a_subrepo() {
 let msg = "subrepos (.hgsub is present)";
 return Err(CommandError::unsupported(msg));
 }
@@ -588,7 +588,8 @@
 }
 }
 
-const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
+const SUPPORTED_EXTENSIONS: &[&[u8]] =
+&[b"blackbox", b"share", b"sparse", b"narrow"];
 
 fn check_extensions(config: ) -> Result<(), CommandError> {
 let enabled = config.get_section_keys(b"extensions");
diff --git a/rust/rhg/src/commands/status.rs b/rust/rhg/src/commands/status.rs
--- a/rust/rhg/src/commands/status.rs
+++ b/rust/rhg/src/commands/status.rs
@@ -190,7 +190,7 @@
 list_ignored: display_states.ignored,
 collect_traversed_dirs: false,
 };
-let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO 
hardcoded
+let ignore_file = repo.working_directory_vfs()?.join(".hgignore"); // TODO 
hardcoded
 let (mut ds_status, pattern_warnings) = dmap.status(
 ,
 repo.working_directory_path().to_owned(),
@@ -311,6 +311,6 @@
 let contents_in_p1 = filelog_entry.data()?;
 
 let fs_path = hg_path_to_os_string(hg_path).expect("HgPath conversion");
-let fs_contents = repo.working_directory_vfs().read(fs_path)?;
+let fs_contents = repo.working_directory_vfs()?.read(fs_path)?;
 return Ok(contents_in_p1 != &*fs_contents);
 }
diff --git a/rust/rhg/src/blackbox.rs b/rust/rhg/src/blackbox.rs
--- a/rust/rhg/src/blackbox.rs
+++ b/rust/rhg/src/blackbox.rs
@@ -133,11 +133,13 @@
 pid,
 message
 );
-let result =
-hg::logging::LogFile::new(self.repo.hg_vfs(), "blackbox.log")
-.max_size(Some(self.max_size))
-.max_files(self.max_files)
-.write();
+let result = hg::logging::LogFile::new(
+self.repo.hg_vfs_not_using_dirstate(),
+"blackbox.log",
+)
+.max_size(Some(self.max_size))
+.max_files(self.max_files)
+.write();
 match result {
 Ok(()) => {}
 Err(_io_error) => {
diff --git a/rust/hg-core/src/requirements.rs b/rust/hg-core/src/requirements.rs
--- a/rust/hg-core/src/requirements.rs
+++ b/rust/hg-core/src/requirements.rs
@@ -88,6 +88,9 @@
 // When it starts writing to the repository, it’ll need to either keep the
 // persistent nodemap up to date or remove this entry:
 NODEMAP_REQUIREMENT,
+// We don't understand sparse working copies, but we understand
+// how to use the store because it's not affected by sparse.
+SPARSE_REQUIREMENT,
 ];
 
 // Copied from mercurial/requirements.py:
diff --git a/rust/hg-core/src/repo.rs b/rust/hg-core/src/repo.rs
--- a/rust/hg-core/src/repo.rs
+++ b/rust/hg-core/src/repo.rs
@@ -227,7 +227,31 @@
 
 /// For accessing repository files (in `.hg`), except for the store
 /// (`.hg/store`).
-pub fn hg_vfs() -> Vfs<'_> {
+fn hg_vfs() -> Vfs<'_> {
+Vfs { base: _hg }
+}
+
+fn has_sparse() -> bool {
+self.requirements.contains(requirements::SPARSE_REQUIREMENT)
+}
+
+fn check_not_sparse() -> Result<(), HgError> {
+if self.has_sparse() {
+Err(HgError::unsupported(format!(
+"repository uses a sparse working copy, this is not supported 

D11751: rhg: only complain about poorly configured fallback when falling back

2021-11-11 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11751

AFFECTED FILES
  rust/rhg/src/main.rs
  tests/test-rhg.t

CHANGE DETAILS

diff --git a/tests/test-rhg.t b/tests/test-rhg.t
--- a/tests/test-rhg.t
+++ b/tests/test-rhg.t
@@ -42,6 +42,10 @@
   abort: No space left on device (os error 28)
   [255]
 
+  $ rhg --config rhg.on-unsupported=fallback --config 
rhg.fallback-executable=! root > /dev/full
+  abort: No space left on device (os error 28)
+  [255]
+
 Deleted repository
   $ rm -rf `pwd`
   $ $NO_FALLBACK rhg root
diff --git a/rust/rhg/src/main.rs b/rust/rhg/src/main.rs
--- a/rust/rhg/src/main.rs
+++ b/rust/rhg/src/main.rs
@@ -179,7 +179,7 @@
 exit(
 _current_dir,
 ,
-OnUnsupported::from_config(, _repo_config),
+OnUnsupported::from_config(_repo_config),
 Err(error.into()),
 non_repo_config
 .get_bool(b"ui", b"detailed-exit-code")
@@ -197,7 +197,7 @@
 exit(
 _current_dir,
 ,
-OnUnsupported::from_config(, _repo_config),
+OnUnsupported::from_config(_repo_config),
 Err(CommandError::UnsupportedFeature {
 message: format_bytes!(
 b"URL-like --repository {}",
@@ -287,7 +287,7 @@
 Err(error) => exit(
 _current_dir,
 ,
-OnUnsupported::from_config(, _repo_config),
+OnUnsupported::from_config(_repo_config),
 Err(error.into()),
 // TODO: show a warning or combine with original error if
 // `get_bool` returns an error
@@ -302,7 +302,7 @@
 } else {
 _repo_config
 };
-let on_unsupported = OnUnsupported::from_config(, config);
+let on_unsupported = OnUnsupported::from_config(config);
 
 let result = main_with_result(
 _start_time,
@@ -362,6 +362,20 @@
 ) = (_unsupported, )
 {
 let mut args = std::env::args_os();
+let executable = match executable {
+None => {
+exit_no_fallback(
+ui,
+OnUnsupported::Abort,
+Err(CommandError::abort(
+"abort: 'rhg.on-unsupported=fallback' without \
+'rhg.fallback-executable' set.",
+)),
+false,
+);
+}
+Some(executable) => executable,
+};
 let executable_path = get_path_from_bytes();
 let this_executable = args.next().expect("exepcted argv[0] to exist");
 if executable_path == ::from(this_executable) {
@@ -374,7 +388,8 @@
 ));
 on_unsupported = OnUnsupported::Abort
 } else {
-// `args` is now `argv[1..]` since we’ve already consumed `argv[0]`
+// `args` is now `argv[1..]` since we’ve already consumed
+// `argv[0]`
 let mut command = Command::new(executable_path);
 command.args(args);
 if let Some(initial) = initial_current_dir {
@@ -549,13 +564,13 @@
 /// Silently exit with code 252.
 AbortSilent,
 /// Try running a Python implementation
-Fallback { executable: Vec },
+Fallback { executable: Option> },
 }
 
 impl OnUnsupported {
 const DEFAULT: Self = OnUnsupported::Abort;
 
-fn from_config(ui: , config: ) -> Self {
+fn from_config(config: ) -> Self {
 match config
 .get(b"rhg", b"on-unsupported")
 .map(|value| value.to_ascii_lowercase())
@@ -566,18 +581,7 @@
 Some(b"fallback") => OnUnsupported::Fallback {
 executable: config
 .get(b"rhg", b"fallback-executable")
-.unwrap_or_else(|| {
-exit_no_fallback(
-ui,
-Self::Abort,
-Err(CommandError::abort(
-"abort: 'rhg.on-unsupported=fallback' without \
-'rhg.fallback-executable' set."
-)),
-false,
-)
-})
-.to_owned(),
+.map(|x| x.to_owned()),
 },
 None => Self::DEFAULT,
 Some(_) => {



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11731: rhg: lazily get filesystem metadata

2021-11-02 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  rhg: use openat library for more efficient filesystem access

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11731

AFFECTED FILES
  rust/Cargo.lock
  rust/hg-core/Cargo.toml
  rust/hg-core/src/dirstate/entry.rs
  rust/hg-core/src/dirstate_tree/status.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/dirstate_tree/status.rs 
b/rust/hg-core/src/dirstate_tree/status.rs
--- a/rust/hg-core/src/dirstate_tree/status.rs
+++ b/rust/hg-core/src/dirstate_tree/status.rs
@@ -8,13 +8,10 @@
 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
 use crate::matchers::get_ignore_function;
 use crate::matchers::Matcher;
-use crate::utils::files::get_bytes_from_os_string;
-use crate::utils::files::get_path_from_bytes;
 use crate::utils::hg_path::HgPath;
 use crate::BadMatch;
 use crate::DirstateStatus;
 use crate::EntryState;
-use crate::HgPathBuf;
 use crate::PatternFileWarning;
 use crate::StatusError;
 use crate::StatusOptions;
@@ -27,6 +24,11 @@
 use std::path::PathBuf;
 use std::sync::Mutex;
 use std::time::SystemTime;
+use openat::{Dir};
+use std::ffi::CStr;
+use std::os::unix::ffi::OsStrExt;
+use std::ffi::CString;
+use std::convert::TryInto;
 
 /// Returns the status of the working directory compared to its parent
 /// changeset.
@@ -80,6 +82,7 @@
 // If the path we have for the repository root is a symlink, do follow it.
 // (As opposed to symlinks within the working directory which are not
 // followed, using `std::fs::symlink_metadata`.)
+let root_dir = openat::Dir::open(_dir)?;
 common.traverse_fs_directory_and_dirstate(
 has_ignored_ancestor,
 dmap.root.as_ref(),
@@ -141,14 +144,94 @@
 filesystem_time_at_status_start: Option,
 }
 
+enum MetadataOrType<'a> {
+Metadata(&'a openat::Metadata),
+FileType(openat::SimpleType)
+}
+
+struct FilesystemEntry<'a> {
+dir : &'a Dir,
+name : &'a CStr,
+metadata_or_type : MetadataOrType<'a>
+}
+
+enum TORREF <'a, T> {
+   Owned(T),
+   Borrowed(&'a T)
+}
+
+impl <'a, T> TORREF <'a, T> {
+fn borrow() ->  {
+match self {
+TORREF::Owned(t) => ,
+TORREF::Borrowed(r) => r
+}
+}
+}
+
+impl <'a> FilesystemEntry<'a> {
+
+fn of_read_dir(dir : &'a Dir, entry : &'a openat::Entry) -> Self {
+FilesystemEntry {
+   dir, name : <& openat::Entry as 
openat::AsPath>::to_path(entry).unwrap(), metadata_or_type : 
MetadataOrType::FileType(entry.simple_type().unwrap()) // TODO: not unwrap
+}
+}
+
+fn of_metadata(dir : &'a Dir, name : &'a CStr, metadata : &'a 
openat::Metadata) -> Self {
+FilesystemEntry {
+   dir, name, metadata_or_type : MetadataOrType::Metadata(metadata)
+}
+}
+
+fn file_type() -> openat::SimpleType {
+   match self.metadata_or_type {
+   MetadataOrType::Metadata(metadata) => metadata.simple_type (),
+   MetadataOrType::FileType(file_type) => file_type
+   }
+}
+
+// Returns None if the entry is not a directory
+fn as_directory() -> io::Result> {
+match self.file_type() {
+openat::SimpleType::Dir => {
+let dir = self.dir.sub_dir(self.name)?;
+let metadata = dir.self_metadata()?;
+Ok(Some((dir, metadata)))
+},
+_ => Ok(None)
+}
+}
+
+fn metadata() -> io::Result> {
+match self.metadata_or_type {
+MetadataOrType::Metadata(m) => Ok(TORREF::Borrowed(m)),
+MetadataOrType::FileType(_) => {
+let metadata = self.dir.metadata(self.name)?;
+Ok(TORREF::Owned(metadata)) }
+}
+}
+}
+
+fn stat_mtime(metadata : ::Metadata) -> SystemTime {
+let stat = metadata.stat();
+let nsec : i64 = stat.st_mtime_nsec as libc::c_long;
+let nsec : u32 = nsec.try_into().unwrap();
+let sec : i64 = stat.st_mtime as libc::time_t;
+let sec : u64 = sec.try_into().unwrap();
+let dur = std::time::Duration::new(sec, nsec);
+SystemTime::UNIX_EPOCH.checked_add(dur).unwrap()
+}
+
+
+
 impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> {
 fn read_dir(
 ,
 hg_path: ,
-fs_path: ,
+fs_dir: ,
 is_at_repo_root: bool,
-) -> Result, ()> {
-DirEntry::read_dir(fs_path, is_at_repo_root)
+) -> Result, ()> {
+read_dir(fs_dir, is_at_repo_root)
 .map_err(|error| self.io_error(error, hg_path))
 }
 
@@ -182,7 +265,7 @@
 /// need to call `read_dir`.
 fn can_skip_fs_readdir(
 ,
-directory_metadata: Option<::fs::Metadata>,
+directory_metadata: Option<::Metadata>,
 cached_directory_mtime: Option,
 ) -> bool {
 if !self.options.list_unknown && 

D11724: merge: with stable

2021-10-27 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11724

AFFECTED FILES
  .hgsigs
  .hgtags
  relnotes/5.9

CHANGE DETAILS

diff --git a/relnotes/5.9 b/relnotes/5.9
--- a/relnotes/5.9
+++ b/relnotes/5.9
@@ -26,6 +26,10 @@
 
 == Bug Fixes ==
 
+ * Fixed committing empty files with `narrow`
+ * Allow overriding `pip`'s pep517 compliance to build C or Rust extensions
+ * Fixed regression on outgoing email when not specifying revisions
+ * Fixed a regression causing bookmarks to disappear when using Rust 
persistent nodemap
  * Fixed a regression (in 5.9.1) introduced in 5.9 when cloning repos with
deep filenames
  * Fixed detection of directories becoming symlinks, but only when using the
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -230,3 +230,4 @@
 53221078e0de65d1a821ce5311dec45a7a978301 5.9
 86a60679cf619e14cee9442f865fcf31b142cb9f 5.9.1
 750920b18aaaddd654756be40dec59d90f2643be 5.9.2
+6ee0244fc1cf889ae543d2ce0ec45201ae0be6e1 5.9.3
diff --git a/.hgsigs b/.hgsigs
--- a/.hgsigs
+++ b/.hgsigs
@@ -217,3 +217,4 @@
 53221078e0de65d1a821ce5311dec45a7a978301 0 
iQJJBAABCgAzFiEEgY2HzRrBgMOUyG5jOjPeRg2ew58FAmEeqLUVHDc4OTVwdWxraXRAZ21haWwuY29tAAoJEDoz3kYNnsOfMb4P/R4oPBjSKrlGbuxYClNdP0lV4C1NUU1SPa+Il4QwGQteKD+RDfvp8z8+c45rVIEGiUNzaSJP/ZEyhBVW657rYzIhBnZgqnpwBzOViqe4Q3lHiq6wPKjEDIRJafcqMb6MaViPS6iRn6hhMlAcPcoabwhXrUgv8QyxVSTFlJm0RGbUVekQLIWKEAnwcWLHKt0d2DrB0/706xXtKxdJ8N/2WCVOOkr7UvpdLXo3quOz1S930/o1iF/csggsi9q4oZYj2XBdBGHayoqkhKAQMyBfXH19RqW3SWZafY8whrZDCz+9AAmJJk8hjQl6xrT/ZVweRfqvRoMJBgjQdFTi58wjC8995ZXKEC7jsJCEblyRJkc23opuAArPEkJXLDR+oK1vOfikaRjmQoMPAMDjbxTUyVOuHcX+PxMtq9NAO0MKcnSr+D2Xc28TGY9PkBhRkEnN3nlZH5z7DvF8GfOnUt5SGhFiQHhXnL6jDBCQVDKAoCJn0WKDG9+29I6st2eGEwKaIjZQ9NCtaLASiauopMOyWWbHeM58bCl80TBXuj+3W+mo+zDSLoGwWJc5oFdFpmnGGTQtkxPDiV4ksIgJAMb/KHkGY+RxnEsWgX1VcR2c1sYD4nzOjrt4RuvX1i+cfzRjLOchPiru7BbrBQRTXGhrvNzsS9laTCxCH2oDazIudia4
 86a60679cf619e14cee9442f865fcf31b142cb9f 0 
iQJJBAABCgAzFiEEgY2HzRrBgMOUyG5jOjPeRg2ew58FAmEtHx4VHDc4OTVwdWxraXRAZ21haWwuY29tAAoJEDoz3kYNnsOfALUP/331tj8MaD6Ld0Jq+yLK7dRlLa0iZ6Kbq2Nq2bYFrv1V99RMG/0xipxWnHfn+B0qdane15tgYIugiVl5pQCGRBeva5CJEg5hfiN53tDDXc2duwaj+kYAREPZJm3lEtv4Tp87E8XZxnJ5qDnNeLCmtpFEEs2bgOHHY/fwHUf/hu0jHJHvkxXh8zPHBf2le6UOMR65PS89bv0jKKmtYPVuYhs/sPRFp78FbYZPiJ0x5NxQsrkYd3ViaQaT2Hb47fpTEg/t1yD3nkZyxHzrGhkFwrLJDMTafuPaXtzVN0BPT9iztgONm+5cF4g6+4AvFWvi5ki87UmrYMCHoiBxKycKR6O+rxh5aay/69I5iIJlcrxyZ/YkzaTUbw4rAZdaTfODwaYOBeMPJp/MviNB5kEGeCV3yLpbftIzsO9BPJ4VtSadVA4HPN/OvAGcYvGO58rN22ojHnqyrnmmuhc4K2/i94+dkMbTyKHrROMXwkJFgH4i3nukyo5fYw5c5ggYAvtEsHLpihv9hXPafTQvmz17f+7/fNi6qJsjEhH8MPjfFpydkjptIyszZ9tx6HyE+2699vJGVHRVepw6RFVOuneXsyKzNeSaw/LmO7B+PfBxpBTvWLblD6DH09pzisTacoMrhvugvfGZsYEFxGt34NvN3Hqj0+ongzFM53UvzMy2fLm5
 750920b18aaaddd654756be40dec59d90f2643be 0 
iQJJBAABCgAzFiEEgY2HzRrBgMOUyG5jOjPeRg2ew58FAmFcc4wVHDc4OTVwdWxraXRAZ21haWwuY29tAAoJEDoz3kYNnsOfatIP+wXnpFitqScNjqnBK6+DaTj+rmBlKoZGB1IQJW5ziDN59gJmT/axemrc3O8BJ/OFO+gDFTX6mk1/L+1Ul4BAF8Yo8XrPd/V7+M02ZUgKTbHmOqTosa9sLeSEojdQQRfSPTHgtA3CLm6VB91fCCfpS9yfCWO3+T8owNelHl8beSqcSlmAzPjqeF1EmalBO4YjSeOCfSdNpVvUGYG8OL/LwYWJqbea7LpN/Sq0piNMqYbc9GYeB9tnf0338WlGEaLTTDk8V3iES+EZxTNeN8NnpGvU0RN50CUfFVyadtbdXUzRDjF4mpdEnsQBkje3hGotyrzDZs1IjKGCANiNBb6dyn/wgv4APOLFw/BLat1Y7z2ZJ6sqUkBbfOs6H2KfufwFZl1sggG1NNXYrwjdS8dHuwi7FRzWMgcYi8Rle8qX8xK/3+We1rwbHfYxhmlEvC8VEC9PZl/K13aIuKmCQ36Es8C/qAtnNfSKZNkYoi/ueAvGFvJo2win1/wIa/6GvBfCxS3ExR1dH+tAUHj2HgMuQXMI6p9OuEloI/mJbdLmU9vnn06EcIyiIPd3dn4H2k0h2WNzyIoVE6YjD5T86jumrUxIj6hp+C9XYYkoj4KR17Pk7U4i3GixDpupLc/KoxiQRGSQTogPjD5O5RCg41tFaGav/TcyW/pb9gTI+v3ALjbZ
+6ee0244fc1cf889ae543d2ce0ec45201ae0be6e1 0 
iQJJBAABCgAzFiEEgY2HzRrBgMOUyG5jOjPeRg2ew58FAmF4AWgVHDc4OTVwdWxraXRAZ21haWwuY29tAAoJEDoz3kYNnsOfxu8P/R8FftAoLkFGHnrzXA9Wa+ch+wunUNixCSimuXjG5sUtDSDlNT+xGj0deTVRVDylFd5HShR6a8NV+2P9edgJYDOKE70j4DJxHdeDyZ3l09YEBymrluE4FygXwpG0B3Ew9pUD85yFxa6UfIFWvNTGYi7XCHBl85buCkMACafN97802jXuE3JV53FvW6Fp917hM0saG48Cnp33WZxdUrZdxXU0Q8bZ9OBYCuGq8Wt2ZIqfEM6YXmvOzlkZf6oJb65rYOw2KgfLs/5nEGiDUNK2akuEhAZLi7uL0dt4WzYAbLyRhIpMpFPitk9P+Ges7iYINwSyZKZcsNPm0NiJupSjKqIYuuLte9HR59RkDFGgM9hbFnskElgHXMqLxi+RqjDVrj2efbuyWzDCn6eVZyn7vmxy9/oLM9vnVsvvdziN2uNUPL4CVmnOZciCdkEZQtWynyyEGzNyq7kPH593ct3tYMxpzs3wa3o+sSdph3lf7caXskij0d0woRZneuZFwp26Ha9tKMMRmXzgFvipzL+o2ANWV6X2udO0pXmKhzYJSBcUPlmVz8hyJaV2D3nmXeFHKVrPa/CqnSGNPWNQC39im1NyPKbfJAA9DZmw7FKg/b23tJq8w9WkBAghEUhC4e54Eb068awt/RDaD6oBYfpdCnQ1pbC/6PHnRSOm8PubGoOZ



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11723: rhg: make it possible to opt out of [rhg cat]

2021-10-27 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D11723

AFFECTED FILES
  rust/rhg/src/commands/cat.rs

CHANGE DETAILS

diff --git a/rust/rhg/src/commands/cat.rs b/rust/rhg/src/commands/cat.rs
--- a/rust/rhg/src/commands/cat.rs
+++ b/rust/rhg/src/commands/cat.rs
@@ -33,6 +33,15 @@
 
 #[timed]
 pub fn run(invocation: ::CliInvocation) -> Result<(), CommandError> {
+let cat_enabled_default = true;
+let cat_enabled = invocation.config.get_option(b"rhg", b"cat")?;
+if !cat_enabled.unwrap_or(cat_enabled_default) {
+return Err(CommandError::unsupported(
+"cat is disabled in rhg (enable it with 'rhg.cat = true' \
+or enable fallback with 'rhg.on-unsupported = fallback')",
+));
+}
+
 let rev = invocation.subcommand_args.value_of("rev");
 let file_args = match invocation.subcommand_args.values_of("files") {
 Some(files) => files.collect(),



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11722: rhg: implement the rhg-debugignore subcommand

2021-10-27 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This can be used to inspect the generated pattern, but also to benchmark
  the time it takes to parse hgignore.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D11722

AFFECTED FILES
  rust/hg-core/src/matchers.rs
  rust/rhg/src/commands/debugignorerhg.rs
  rust/rhg/src/main.rs

CHANGE DETAILS

diff --git a/rust/rhg/src/main.rs b/rust/rhg/src/main.rs
--- a/rust/rhg/src/main.rs
+++ b/rust/rhg/src/main.rs
@@ -434,6 +434,7 @@
 cat
 debugdata
 debugrequirements
+debugignorerhg
 files
 root
 config
diff --git a/rust/rhg/src/commands/debugignorerhg.rs 
b/rust/rhg/src/commands/debugignorerhg.rs
new file mode 100644
--- /dev/null
+++ b/rust/rhg/src/commands/debugignorerhg.rs
@@ -0,0 +1,39 @@
+
+use crate::error::CommandError;
+use clap::{SubCommand};
+use hg;
+use hg::matchers::get_ignore_matcher;
+use hg::StatusError;
+use log::{warn};
+
+pub const HELP_TEXT:  = "
+Show effective hgignore patterns used by rhg.
+
+This is a pure Rust version of `hg debugignore`.
+
+Some options might be missing, check the list below.
+";
+
+pub fn args() -> clap::App<'static, 'static> {
+SubCommand::with_name("debugignorerhg").about(HELP_TEXT)
+}
+
+pub fn run(invocation: ::CliInvocation) -> Result<(), CommandError> {
+let repo = invocation.repo?;
+
+let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO 
hardcoded
+
+let (ignore_matcher, warnings) = get_ignore_matcher(
+vec![ignore_file],
+_directory_path().to_owned(),
+ |_pattern_bytes| (),
+).map_err(|e| StatusError::from(e))?;
+
+if !warnings.is_empty() {
+warn!("Pattern warnings: {:?}", );
+}
+
+let patterns = ignore_matcher.debug_get_patterns();
+println!("{}", String::from_utf8_lossy(patterns));
+Ok (())
+}
diff --git a/rust/hg-core/src/matchers.rs b/rust/hg-core/src/matchers.rs
--- a/rust/hg-core/src/matchers.rs
+++ b/rust/hg-core/src/matchers.rs
@@ -560,14 +560,11 @@
 /// Parses all "ignore" files with their recursive includes and returns a
 /// function that checks whether a given file (in the general sense) should be
 /// ignored.
-pub fn get_ignore_function<'a>(
+pub fn get_ignore_matcher<'a>(
 mut all_pattern_files: Vec,
 root_dir: ,
 inspect_pattern_bytes:  impl FnMut(&[u8]),
-) -> PatternResult<(
-Box Fn(&'r HgPath) -> bool + Sync + 'a>,
-Vec,
-)> {
+) -> PatternResult<(IncludeMatcher<'a>, Vec)> {
 let mut all_patterns = vec![];
 let mut all_warnings = vec![];
 
@@ -590,10 +587,28 @@
 all_warnings.extend(warnings);
 }
 let matcher = IncludeMatcher::new(all_patterns)?;
-Ok((
-Box::new(move |path: | matcher.matches(path)),
-all_warnings,
-))
+Ok((matcher, all_warnings))
+}
+
+/// Parses all "ignore" files with their recursive includes and returns a
+/// function that checks whether a given file (in the general sense) should be
+/// ignored.
+pub fn get_ignore_function<'a>(
+all_pattern_files: Vec,
+root_dir: ,
+inspect_pattern_bytes:  impl FnMut(&[u8]),
+) -> PatternResult<(
+Box Fn(&'r HgPath) -> bool + Sync + 'a>,
+Vec,
+)> {
+let res =
+get_ignore_matcher(all_pattern_files, root_dir, inspect_pattern_bytes);
+res.map(|(matcher, all_warnings)| {
+let res: Box Fn(&'r HgPath) -> bool + Sync + 'a> =
+Box::new(move |path: | matcher.matches(path));
+
+(res, all_warnings)
+})
 }
 
 impl<'a> IncludeMatcher<'a> {
@@ -628,6 +643,10 @@
 .chain(self.parents.iter());
 DirsChildrenMultiset::new(thing, Some())
 }
+
+pub fn debug_get_patterns() -> &[u8] {
+self.patterns.as_ref()
+}
 }
 
 impl<'a> Display for IncludeMatcher<'a> {



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11721: rhg: more efficient `HgPath::join`

2021-10-26 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This commit makes `HgPath::join` slightly more efficient
  by avoiding one copy.
  
  It also avoids a particularly inefficient (quadratic) use of
  `HgPath::join` by using a new mutating function `HgPathBuf::push` instead.
  
  The name for `HgPath::push` is chosen by analogy to `PathBuf::push`.

REPOSITORY
  rHG Mercurial

BRANCH
  stable

REVISION DETAIL
  https://phab.mercurial-scm.org/D11721

AFFECTED FILES
  rust/hg-core/src/filepatterns.rs
  rust/hg-core/src/matchers.rs
  rust/hg-core/src/utils/hg_path.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/utils/hg_path.rs 
b/rust/hg-core/src/utils/hg_path.rs
--- a/rust/hg-core/src/utils/hg_path.rs
+++ b/rust/hg-core/src/utils/hg_path.rs
@@ -220,13 +220,11 @@
 ),
 }
 }
-pub fn join>(, other: ) -> HgPathBuf {
-let mut inner = self.inner.to_owned();
-if !inner.is_empty() && inner.last() != Some('/') {
-inner.push(b'/');
-}
-inner.extend(other.as_ref().bytes());
-HgPathBuf::from_bytes()
+
+pub fn join(, path: ) -> HgPathBuf {
+let mut buf = self.to_owned();
+buf.push(path);
+buf
 }
 
 pub fn components() -> impl Iterator {
@@ -405,7 +403,15 @@
 pub fn new() -> Self {
 Default::default()
 }
-pub fn push( self, byte: u8) {
+
+pub fn push>( self, other: ) -> () {
+if !self.inner.is_empty() && self.inner.last() != Some('/') {
+self.inner.push(b'/');
+}
+self.inner.extend(other.as_ref().bytes())
+}
+
+pub fn push_byte( self, byte: u8) {
 self.inner.push(byte);
 }
 pub fn from_bytes(s: &[u8]) -> HgPathBuf {
diff --git a/rust/hg-core/src/matchers.rs b/rust/hg-core/src/matchers.rs
--- a/rust/hg-core/src/matchers.rs
+++ b/rust/hg-core/src/matchers.rs
@@ -402,8 +402,8 @@
 }
 root.push(HgPathBuf::from_bytes(p));
 }
-let buf =
-root.iter().fold(HgPathBuf::new(), |acc, r| acc.join(r));
+let mut buf = HgPathBuf::new();
+root.iter().fold((), |(), r| buf.push(r));
 roots.push(buf);
 }
 PatternSyntax::Path | PatternSyntax::RelPath => {
diff --git a/rust/hg-core/src/filepatterns.rs b/rust/hg-core/src/filepatterns.rs
--- a/rust/hg-core/src/filepatterns.rs
+++ b/rust/hg-core/src/filepatterns.rs
@@ -536,7 +536,7 @@
 Ok(Self {
 prefix: path_to_hg_path_buf(prefix).and_then(|mut p| {
 if !p.is_empty() {
-p.push(b'/');
+p.push_byte(b'/');
 }
 Ok(p)
 })?,



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11716: tests: better determinism in test-chg.t

2021-10-21 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  chg tests fail pretty often with "Sample count: *" line disappearing.
  It disappears because the sample count is zero, in which case a custom 
message is printed.
  This commit makes the test succeed in that case.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11716

AFFECTED FILES
  tests/test-chg.t

CHANGE DETAILS

diff --git a/tests/test-chg.t b/tests/test-chg.t
--- a/tests/test-chg.t
+++ b/tests/test-chg.t
@@ -475,7 +475,7 @@
   $ hg init $TESTTMP/profiling
   $ cd $TESTTMP/profiling
   $ filteredchg() {
-  >   CHGDEBUG=1 chg "$@" 2>&1 | egrep 'Sample count|start cmdserver' || true
+  >   CHGDEBUG=1 chg "$@" 2>&1 | sed -rn 's_^No samples recorded.*$_Sample 
count: 0_; /Sample count/p; /start cmdserver/p'
   > }
   $ newchg() {
   >   chg --kill-chg-daemon



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11689: rhg: simplify split_metadata

2021-10-19 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  It turns out that it's possible to implement `FilelogEntry.into_data`
  on top of `split`, as proposed by @spectral.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11689

AFFECTED FILES
  rust/hg-core/src/revlog/filelog.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/revlog/filelog.rs 
b/rust/hg-core/src/revlog/filelog.rs
--- a/rust/hg-core/src/revlog/filelog.rs
+++ b/rust/hg-core/src/revlog/filelog.rs
@@ -54,28 +54,18 @@
 
 impl FilelogEntry {
 /// Split into metadata and data
-/// Returns None if there is no metadata, so the entire entry is data.
-fn split_metadata() -> Result, HgError> {
+pub fn split() -> Result<(Option<&[u8]>, &[u8]), HgError> {
 const DELIMITER: &[u8; 2] = &[b'\x01', b'\n'];
 
 if let Some(rest) = self.0.drop_prefix(DELIMITER) {
 if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) {
-Ok(Some((metadata, data)))
+Ok((Some(metadata), data))
 } else {
 Err(HgError::corrupted(
 "Missing metadata end delimiter in filelog entry",
 ))
 }
 } else {
-Ok(None)
-}
-}
-
-/// Split into metadata and data
-pub fn split() -> Result<(Option<&[u8]>, &[u8]), HgError> {
-if let Some((metadata, data)) = self.split_metadata()? {
-Ok((Some(metadata), data))
-} else {
 Ok((None, ))
 }
 }
@@ -89,7 +79,7 @@
 /// Consume the entry, and convert it into data, discarding any metadata,
 /// if present.
 pub fn into_data(self) -> Result, HgError> {
-if let Some((_metadata, data)) = self.split_metadata()? {
+if let (Some(_metadata), data) = self.split()? {
 Ok(data.to_owned())
 } else {
 Ok(self.0)



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, spectral, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11679: rhg: simplify the type of FilelogEntry

2021-10-15 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  rhg: add FilelogEntry.into_data
  
  rhg: internally, return a structured representatioon from hg cat

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11679

AFFECTED FILES
  rust/hg-core/src/operations/cat.rs
  rust/hg-core/src/revlog/filelog.rs
  rust/rhg/src/commands/cat.rs

CHANGE DETAILS

diff --git a/rust/rhg/src/commands/cat.rs b/rust/rhg/src/commands/cat.rs
--- a/rust/rhg/src/commands/cat.rs
+++ b/rust/rhg/src/commands/cat.rs
@@ -66,6 +66,7 @@
 .map_err(|e| CommandError::abort(e.to_string()))?;
 files.push(hg_file);
 }
+let files = files.iter().map(|file| file.as_ref()).collect();
 // TODO probably move this to a util function like `repo.default_rev` or
 // something when it's used somewhere else
 let rev = match rev {
@@ -74,7 +75,9 @@
 };
 
 let output = cat(, , files).map_err(|e| (e, rev.as_str()))?;
-invocation.ui.write_stdout()?;
+for (_file, contents) in output.results {
+invocation.ui.write_stdout()?;
+}
 if !output.missing.is_empty() {
 let short = format!("{:x}", output.node.short()).into_bytes();
 for path in  {
diff --git a/rust/hg-core/src/revlog/filelog.rs 
b/rust/hg-core/src/revlog/filelog.rs
--- a/rust/hg-core/src/revlog/filelog.rs
+++ b/rust/hg-core/src/revlog/filelog.rs
@@ -7,7 +7,6 @@
 use crate::utils::files::get_path_from_bytes;
 use crate::utils::hg_path::HgPath;
 use crate::utils::SliceExt;
-use std::borrow::Cow;
 use std::path::PathBuf;
 
 /// A specialized `Revlog` to work with file data logs.
@@ -40,7 +39,7 @@
 ,
 file_rev: Revision,
 ) -> Result {
-let data = self.revlog.get_rev_data(file_rev)?;
+let data: Vec = self.revlog.get_rev_data(file_rev)?;
 Ok(FilelogEntry(data.into()))
 }
 }
@@ -51,22 +50,32 @@
 get_path_from_bytes(_bytes).into()
 }
 
-pub struct FilelogEntry<'filelog>(Cow<'filelog, [u8]>);
+pub struct FilelogEntry(Vec);
 
-impl<'filelog> FilelogEntry<'filelog> {
+impl FilelogEntry {
 /// Split into metadata and data
-pub fn split() -> Result<(Option<&[u8]>, &[u8]), HgError> {
+/// Returns None if there is no metadata, so the entire entry is data.
+fn split_metadata() -> Result, HgError> {
 const DELIMITER: &[u8; 2] = &[b'\x01', b'\n'];
 
 if let Some(rest) = self.0.drop_prefix(DELIMITER) {
 if let Some((metadata, data)) = rest.split_2_by_slice(DELIMITER) {
-Ok((Some(metadata), data))
+Ok(Some((metadata, data)))
 } else {
 Err(HgError::corrupted(
 "Missing metadata end delimiter in filelog entry",
 ))
 }
 } else {
+Ok(None)
+}
+}
+
+/// Split into metadata and data
+pub fn split() -> Result<(Option<&[u8]>, &[u8]), HgError> {
+if let Some((metadata, data)) = self.split_metadata()? {
+Ok((Some(metadata), data))
+} else {
 Ok((None, ))
 }
 }
@@ -76,4 +85,12 @@
 let (_metadata, data) = self.split()?;
 Ok(data)
 }
+
+pub fn into_data(self) -> Result, HgError> {
+if let Some((_metadata, data)) = self.split_metadata()? {
+Ok(data.to_owned())
+} else {
+Ok(self.0)
+}
+}
 }
diff --git a/rust/hg-core/src/operations/cat.rs 
b/rust/hg-core/src/operations/cat.rs
--- a/rust/hg-core/src/operations/cat.rs
+++ b/rust/hg-core/src/operations/cat.rs
@@ -10,20 +10,19 @@
 use crate::revlog::Node;
 
 use crate::utils::hg_path::HgPath;
-use crate::utils::hg_path::HgPathBuf;
 
 use itertools::put_back;
 use itertools::PutBack;
 use std::cmp::Ordering;
 
-pub struct CatOutput {
+pub struct CatOutput<'a> {
 /// Whether any file in the manifest matched the paths given as CLI
 /// arguments
 pub found_any: bool,
 /// The contents of matching files, in manifest order
-pub concatenated: Vec,
+pub results: Vec<(&'a HgPath, Vec)>,
 /// Which of the CLI arguments did not match any manifest file
-pub missing: Vec,
+pub missing: Vec<&'a HgPath>,
 /// The node ID that the given revset was resolved to
 pub node: Node,
 }
@@ -32,7 +31,7 @@
 fn find_item<'a, 'b, 'c, D, I: Iterator>(
 i:  PutBack,
 needle: &'b HgPath,
-) -> Option {
+) -> Option {
 loop {
 match i.next() {
 None => return None,
@@ -42,7 +41,7 @@
 return None;
 }
 Ordering::Greater => continue,
-Ordering::Equal => return Some(val),
+Ordering::Equal => return Some(val.1),
 },
 }
 }
@@ -57,7 +56,7 @@
 >(
 manifest: I,
 files: J,
-) -> (Vec<(&'a HgPath, D)>, Vec<&'b HgPath>) {
+) -> (Vec<(&'b HgPath, D)>, Vec<&'b 

D11664: rhg: fix `hg cat` interaction with null revision

2021-10-14 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11664

AFFECTED FILES
  rust/hg-core/src/revlog/revlog.rs
  tests/test-rhg.t

CHANGE DETAILS

diff --git a/tests/test-rhg.t b/tests/test-rhg.t
--- a/tests/test-rhg.t
+++ b/tests/test-rhg.t
@@ -129,8 +129,8 @@
   $ $NO_FALLBACK rhg cat -r d file-2
   2
   $ $NO_FALLBACK rhg cat -r  file-2
-  abort: invalid revision identifier: 
-  [255]
+  file-2: no such file in rev 
+  [1]
 
 Cat files
   $ cd $TESTTMP
diff --git a/rust/hg-core/src/revlog/revlog.rs 
b/rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/revlog/revlog.rs
+++ b/rust/hg-core/src/revlog/revlog.rs
@@ -76,7 +76,8 @@
 Some(index_mmap) => {
 let version = get_version(_mmap)?;
 if version != 1 {
-// A proper new version should have had a repo/store 
requirement.
+// A proper new version should have had a repo/store
+// requirement.
 return Err(HgError::corrupted("corrupted revlog"));
 }
 
@@ -128,6 +129,9 @@
 /// Returns the node ID for the given revision number, if it exists in this
 /// revlog
 pub fn node_from_rev(, rev: Revision) -> Option<> {
+if rev == NULL_REVISION {
+return Some(_NODE);
+}
 Some(self.index.get_entry(rev)?.hash())
 }
 
@@ -424,6 +428,6 @@
 .with_version(1)
 .build();
 
-assert_eq!(get_version().map_err(|_err|()), Ok(1))
+assert_eq!(get_version().map_err(|_err| ()), Ok(1))
 }
 }



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11659: rhg: do not try to open a nodemap for an inline index

2021-10-14 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This saves an [open] system call per file, which is a small saving, but
  it showed up in the profile at large file counts (it accounted for 30ms
  out of 400ms needed for catting 1 files, on a ZFS filesystem on Linux,
  so ~3us per syscall).

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11659

AFFECTED FILES
  rust/hg-core/src/revlog/index.rs
  rust/hg-core/src/revlog/revlog.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/revlog/revlog.rs 
b/rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/revlog/revlog.rs
+++ b/rust/hg-core/src/revlog/revlog.rs
@@ -99,14 +99,18 @@
 Some(Box::new(data_mmap))
 };
 
-let nodemap = NodeMapDocket::read_from_file(repo, index_path)?.map(
-|(docket, data)| {
-nodemap::NodeTree::load_bytes(
-Box::new(data),
-docket.data_length,
-)
-},
-);
+let nodemap = if index.is_inline() {
+None
+} else {
+NodeMapDocket::read_from_file(repo, index_path)?.map(
+|(docket, data)| {
+nodemap::NodeTree::load_bytes(
+Box::new(data),
+docket.data_length,
+)
+},
+)
+};
 
 Ok(Revlog {
 index,
diff --git a/rust/hg-core/src/revlog/index.rs b/rust/hg-core/src/revlog/index.rs
--- a/rust/hg-core/src/revlog/index.rs
+++ b/rust/hg-core/src/revlog/index.rs
@@ -57,7 +57,7 @@
 
 /// Value of the inline flag.
 pub fn is_inline() -> bool {
-is_inline()
+self.offsets.is_some()
 }
 
 /// Return a slice of bytes if `revlog` is inline. Panic if not.



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11656: rhg: fix the test

2021-10-13 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11656

AFFECTED FILES
  rust/hg-core/src/revlog/revlog.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/revlog/revlog.rs 
b/rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/revlog/revlog.rs
+++ b/rust/hg-core/src/revlog/revlog.rs
@@ -424,6 +424,6 @@
 .with_version(1)
 .build();
 
-assert_eq!(get_version(), 1)
+assert_eq!(get_version().map_err(|_err|()), Ok(1))
 }
 }



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11651: rhg: do not fail when the repo is empty

2021-10-12 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11651

AFFECTED FILES
  rust/hg-core/src/revlog/revlog.rs
  rust/hg-core/src/vfs.rs
  tests/test-empty-manifest-index.t

CHANGE DETAILS

diff --git a/tests/test-empty-manifest-index.t 
b/tests/test-empty-manifest-index.t
--- a/tests/test-empty-manifest-index.t
+++ b/tests/test-empty-manifest-index.t
@@ -1,23 +1,27 @@
-Create a repo such that the changelog entry refers to a null manifest node:
+Test null revisions (node , aka rev -1)
+in various circumstances.
+
+Make an empty repo:
 
   $ hg init a
   $ cd a
-  $ hg log
-  $ touch x
-  $ hg add x
-  $ hg commit -m "init"
-  $ hg rm x
-  $ hg commit -q --amend
 
-  $ wc -c < .hg/store/00manifest.i
-  0
-
-Make sure that the manifest can be read (and is empty):
-
-  $ hg --config rhg.on-unsupported=abort files -r .
+  $ hg files -r 
+  [1]
+  $ hg files -r .
   [1]
 
-Test a null changelog rev, too:
+Add an empty commit (this makes the changelog refer to a null manifest node):
+
+
+  $ hg commit -m "init" --config ui.allowemptycommit=true
 
-  $ hg --config rhg.on-unsupported=abort files -r 

+  $ hg files -r .
   [1]
+
+Strip that empty commit (this makes the changelog file empty, as opposed to 
missing):
+
+  $ hg --config 'extensions.strip=' strip . > /dev/null
+
+  $ hg files -r .
+  [1]
diff --git a/rust/hg-core/src/vfs.rs b/rust/hg-core/src/vfs.rs
--- a/rust/hg-core/src/vfs.rs
+++ b/rust/hg-core/src/vfs.rs
@@ -9,6 +9,8 @@
 pub(crate) base: &'a Path,
 }
 
+struct FileNotFound(std::io::Error, PathBuf);
+
 impl Vfs<'_> {
 pub fn join(, relative_path: impl AsRef) -> PathBuf {
 self.base.join(relative_path)
@@ -22,16 +24,41 @@
 std::fs::read().when_reading_file()
 }
 
+fn mmap_open_gen(
+,
+relative_path: impl AsRef,
+) -> Result, HgError> {
+let path = self.join(relative_path);
+let file = match std::fs::File::open() {
+Err(err) => {
+if let ErrorKind::NotFound = err.kind() {
+return Ok(Err(FileNotFound(err, path)));
+};
+return (Err(err)).when_reading_file();
+}
+Ok(file) => file,
+};
+// TODO: what are the safety requirements here?
+let mmap = unsafe { MmapOptions::new().map() }
+.when_reading_file()?;
+Ok(Ok(mmap))
+}
+
+pub fn mmap_open_opt(
+,
+relative_path: impl AsRef,
+) -> Result, HgError> {
+self.mmap_open_gen(relative_path).map(|res| res.ok())
+}
+
 pub fn mmap_open(
 ,
 relative_path: impl AsRef,
 ) -> Result {
-let path = self.base.join(relative_path);
-let file = std::fs::File::open().when_reading_file()?;
-// TODO: what are the safety requirements here?
-let mmap = unsafe { MmapOptions::new().map() }
-.when_reading_file()?;
-Ok(mmap)
+match self.mmap_open_gen(relative_path)? {
+Err(FileNotFound(err, path)) => Err(err).when_reading_file(),
+Ok(res) => Ok(res),
+}
 }
 
 pub fn rename(
diff --git a/rust/hg-core/src/revlog/revlog.rs 
b/rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/revlog/revlog.rs
+++ b/rust/hg-core/src/revlog/revlog.rs
@@ -70,15 +70,21 @@
 data_path: Option<>,
 ) -> Result {
 let index_path = index_path.as_ref();
-let index_mmap = repo.store_vfs().mmap_open(_path)?;
+let index = {
+match repo.store_vfs().mmap_open_opt(_path)? {
+None => Index::new(Box::new(vec![])),
+Some(index_mmap) => {
+let version = get_version(_mmap)?;
+if version != 1 {
+// A proper new version should have had a repo/store 
requirement.
+return Err(HgError::corrupted("corrupted revlog"));
+}
 
-let version = get_version(_mmap)?;
-if version != 1 {
-// A proper new version should have had a repo/store requirement.
-return Err(HgError::corrupted("corrupted revlog"));
-}
-
-let index = Index::new(Box::new(index_mmap))?;
+let index = Index::new(Box::new(index_mmap))?;
+Ok(index)
+}
+}
+}?;
 
 let default_data_path = index_path.with_extension("d");
 



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11650: rhg: handle null changelog and manifest revisions

2021-10-12 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11650

AFFECTED FILES
  rust/hg-core/src/revlog/changelog.rs
  rust/hg-core/src/revlog/index.rs
  rust/hg-core/src/revlog/revlog.rs
  tests/test-empty-manifest-index.t

CHANGE DETAILS

diff --git a/tests/test-empty-manifest-index.t 
b/tests/test-empty-manifest-index.t
new file mode 100644
--- /dev/null
+++ b/tests/test-empty-manifest-index.t
@@ -0,0 +1,23 @@
+Create a repo such that the changelog entry refers to a null manifest node:
+
+  $ hg init a
+  $ cd a
+  $ hg log
+  $ touch x
+  $ hg add x
+  $ hg commit -m "init"
+  $ hg rm x
+  $ hg commit -q --amend
+
+  $ wc -c < .hg/store/00manifest.i
+  0
+
+Make sure that the manifest can be read (and is empty):
+
+  $ hg --config rhg.on-unsupported=abort files -r .
+  [1]
+
+Test a null changelog rev, too:
+
+  $ hg --config rhg.on-unsupported=abort files -r 

+  [1]
diff --git a/rust/hg-core/src/revlog/revlog.rs 
b/rust/hg-core/src/revlog/revlog.rs
--- a/rust/hg-core/src/revlog/revlog.rs
+++ b/rust/hg-core/src/revlog/revlog.rs
@@ -72,7 +72,7 @@
 let index_path = index_path.as_ref();
 let index_mmap = repo.store_vfs().mmap_open(_path)?;
 
-let version = get_version(_mmap);
+let version = get_version(_mmap)?;
 if version != 1 {
 // A proper new version should have had a repo/store requirement.
 return Err(HgError::corrupted("corrupted revlog"));
@@ -179,6 +179,9 @@
 /// snapshot to rebuild the final data.
 #[timed]
 pub fn get_rev_data(, rev: Revision) -> Result, RevlogError> {
+if rev == NULL_REVISION {
+return Ok(vec![]);
+};
 // Todo return -> Cow
 let mut entry = self.get_entry(rev)?;
 let mut delta_chain = vec![];
@@ -371,8 +374,16 @@
 }
 
 /// Format version of the revlog.
-pub fn get_version(index_bytes: &[u8]) -> u16 {
-BigEndian::read_u16(_bytes[2..=3])
+pub fn get_version(index_bytes: &[u8]) -> Result {
+if index_bytes.len() == 0 {
+return Ok(1);
+};
+if index_bytes.len() < 4 {
+return Err(HgError::corrupted(
+"corrupted revlog: can't read the index format header",
+));
+};
+Ok(BigEndian::read_u16(_bytes[2..=3]))
 }
 
 /// Calculate the hash of a revision given its data and its parents.
diff --git a/rust/hg-core/src/revlog/index.rs b/rust/hg-core/src/revlog/index.rs
--- a/rust/hg-core/src/revlog/index.rs
+++ b/rust/hg-core/src/revlog/index.rs
@@ -208,6 +208,9 @@
 
 /// Value of the inline flag.
 pub fn is_inline(index_bytes: &[u8]) -> bool {
+if index_bytes.len() < 4 {
+return true;
+}
 match _bytes[0..=1] {
 [0, 0] | [0, 2] => false,
 _ => true,
diff --git a/rust/hg-core/src/revlog/changelog.rs 
b/rust/hg-core/src/revlog/changelog.rs
--- a/rust/hg-core/src/revlog/changelog.rs
+++ b/rust/hg-core/src/revlog/changelog.rs
@@ -1,5 +1,6 @@
 use crate::errors::HgError;
 use crate::repo::Repo;
+use crate::revlog::node::NULL_NODE;
 use crate::revlog::revlog::{Revlog, RevlogError};
 use crate::revlog::Revision;
 use crate::revlog::{Node, NodePrefix};
@@ -58,10 +59,9 @@
 /// Return the node id of the `manifest` referenced by this `changelog`
 /// entry.
 pub fn manifest_node() -> Result {
-Node::from_hex_for_repo(
-self.lines()
-.next()
-.ok_or_else(|| HgError::corrupted("empty changelog entry"))?,
-)
+match self.lines().next() {
+None => Ok(NULL_NODE),
+Some(x) => Node::from_hex_for_repo(x),
+}
 }
 }



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11617: rhg: in rhg cat cli, fix the long name of the --rev flag\n\nAlso tweak the help for the anonymous argument.

2021-10-05 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11617

AFFECTED FILES
  rust/rhg/src/commands/cat.rs
  tests/test-rhg.t

CHANGE DETAILS

diff --git a/tests/test-rhg.t b/tests/test-rhg.t
--- a/tests/test-rhg.t
+++ b/tests/test-rhg.t
@@ -121,6 +121,8 @@
   file-3
   $ $NO_FALLBACK rhg cat -r cf8b83 file-2
   2
+  $ $NO_FALLBACK rhg cat --rev cf8b83 file-2
+  2
   $ $NO_FALLBACK rhg cat -r c file-2
   abort: ambiguous revision identifier: c
   [255]
diff --git a/rust/rhg/src/commands/cat.rs b/rust/rhg/src/commands/cat.rs
--- a/rust/rhg/src/commands/cat.rs
+++ b/rust/rhg/src/commands/cat.rs
@@ -16,7 +16,7 @@
 Arg::with_name("rev")
 .help("search the repository as it is in REV")
 .short("-r")
-.long("--revision")
+.long("--rev")
 .value_name("REV")
 .takes_value(true),
 )
@@ -26,7 +26,7 @@
 .multiple(true)
 .empty_values(false)
 .value_name("FILE")
-.help("Activity to start: activity@category"),
+.help("Files to output"),
 )
 .about(HELP_TEXT)
 }



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11615: rhg: faster hg cat when many files are requested

2021-10-05 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11615

AFFECTED FILES
  rust/hg-core/src/operations/cat.rs
  rust/rhg/src/commands/cat.rs

CHANGE DETAILS

diff --git a/rust/rhg/src/commands/cat.rs b/rust/rhg/src/commands/cat.rs
--- a/rust/rhg/src/commands/cat.rs
+++ b/rust/rhg/src/commands/cat.rs
@@ -59,7 +59,7 @@
 
 match rev {
 Some(rev) => {
-let output = cat(, rev, ).map_err(|e| (e, rev))?;
+let output = cat(, rev, files).map_err(|e| (e, rev))?;
 invocation.ui.write_stdout()?;
 if !output.missing.is_empty() {
 let short = format!("{:x}", output.node.short()).into_bytes();
diff --git a/rust/hg-core/src/operations/cat.rs 
b/rust/hg-core/src/operations/cat.rs
--- a/rust/hg-core/src/operations/cat.rs
+++ b/rust/hg-core/src/operations/cat.rs
@@ -11,6 +11,9 @@
 
 use crate::utils::hg_path::HgPathBuf;
 
+use itertools::EitherOrBoth::{Both, Left, Right};
+use itertools::Itertools;
+
 pub struct CatOutput {
 /// Whether any file in the manifest matched the paths given as CLI
 /// arguments
@@ -31,7 +34,7 @@
 pub fn cat<'a>(
 repo: ,
 revset: ,
-files: &'a [HgPathBuf],
+mut files: Vec,
 ) -> Result {
 let rev = crate::revset::resolve_single(revset, repo)?;
 let manifest = repo.manifest_for_rev(rev)?;
@@ -40,13 +43,21 @@
 .node_from_rev(rev)
 .expect("should succeed when repo.manifest did");
 let mut bytes = vec![];
-let mut matched = vec![false; files.len()];
 let mut found_any = false;
+files.sort_unstable();
+
+let mut missing = vec![];
 
-for (manifest_file, node_bytes) in manifest.files_with_nodes() {
-for (cat_file, is_matched) in files.iter().zip( matched) {
-if cat_file.as_bytes() == manifest_file.as_bytes() {
-*is_matched = true;
+for entry in manifest
+.files_with_nodes()
+.merge_join_by(files.iter(), |(manifest_file, _), file| {
+manifest_file.cmp(_ref())
+})
+{
+match entry {
+Left(_) => (),
+Right(path) => missing.push(path),
+Both((manifest_file, node_bytes), _) => {
 found_any = true;
 let file_log = repo.filelog(manifest_file)?;
 let file_node = Node::from_hex_for_repo(node_bytes)?;
@@ -56,11 +67,12 @@
 }
 }
 
+// make the order of the [missing] files
+// match the order they were specified on the command line
 let missing: Vec<_> = files
 .iter()
-.zip()
-.filter(|pair| !*pair.1)
-.map(|pair| pair.0.clone())
+.filter(|file| missing.contains(file))
+.map(|file| file.clone())
 .collect();
 Ok(CatOutput {
 found_any,



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D11616: rhg: stop manifest traversal when no more files are needed

2021-10-05 Thread aalekseyev (Arseniy Alekseyev)
aalekseyev created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REPOSITORY
  rHG Mercurial

BRANCH
  default

REVISION DETAIL
  https://phab.mercurial-scm.org/D11616

AFFECTED FILES
  rust/hg-core/src/operations/cat.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/operations/cat.rs 
b/rust/hg-core/src/operations/cat.rs
--- a/rust/hg-core/src/operations/cat.rs
+++ b/rust/hg-core/src/operations/cat.rs
@@ -9,10 +9,12 @@
 use crate::revlog::revlog::RevlogError;
 use crate::revlog::Node;
 
+use crate::utils::hg_path::HgPath;
 use crate::utils::hg_path::HgPathBuf;
 
-use itertools::EitherOrBoth::{Both, Left, Right};
-use itertools::Itertools;
+use itertools::put_back;
+use itertools::PutBack;
+use std::cmp::Ordering;
 
 pub struct CatOutput {
 /// Whether any file in the manifest matched the paths given as CLI
@@ -26,6 +28,50 @@
 pub node: Node,
 }
 
+// Find an item in an iterator over a sorted collection.
+fn find_item<'a, 'b, 'c, D, I: Iterator>(
+i:  PutBack,
+needle: &'b HgPath,
+) -> Option {
+loop {
+match i.next() {
+None => return None,
+Some(val) => match needle.as_bytes().cmp(val.0.as_bytes()) {
+Ordering::Less => {
+i.put_back(val);
+return None;
+}
+Ordering::Greater => continue,
+Ordering::Equal => return Some(val),
+},
+}
+}
+}
+
+fn find_files_in_manifest<
+'a,
+'b,
+'c,
+D,
+I: Iterator,
+J: Iterator,
+>(
+i: I,
+j: J,
+) -> (Vec<(&'a HgPath, D)>, Vec<&'b HgPath>) {
+let mut manifest_iterator = put_back(i);
+let mut res = vec![];
+let mut missing = vec![];
+
+for file in j {
+match find_item( manifest_iterator, file) {
+None => missing.push(file),
+Some(item) => res.push(item),
+}
+}
+return (res, missing);
+}
+
 /// Output the given revision of files
 ///
 /// * `root`: Repository root
@@ -42,36 +88,28 @@
 .changelog()?
 .node_from_rev(rev)
 .expect("should succeed when repo.manifest did");
-let mut bytes = vec![];
+let mut bytes: Vec = vec![];
 let mut found_any = false;
+
 files.sort_unstable();
 
-let mut missing = vec![];
+let (found, missing) = find_files_in_manifest(
+manifest.files_with_nodes(),
+files.iter().map(|f| f.as_ref()),
+);
 
-for entry in manifest
-.files_with_nodes()
-.merge_join_by(files.iter(), |(manifest_file, _), file| {
-manifest_file.cmp(_ref())
-})
-{
-match entry {
-Left(_) => (),
-Right(path) => missing.push(path),
-Both((manifest_file, node_bytes), _) => {
-found_any = true;
-let file_log = repo.filelog(manifest_file)?;
-let file_node = Node::from_hex_for_repo(node_bytes)?;
-let entry = file_log.data_for_node(file_node)?;
-bytes.extend(entry.data()?)
-}
-}
+for (manifest_file, node_bytes) in found {
+found_any = true;
+let file_log = repo.filelog(manifest_file)?;
+let file_node = Node::from_hex_for_repo(node_bytes)?;
+bytes.extend(file_log.data_for_node(file_node)?.data()?);
 }
 
 // make the order of the [missing] files
 // match the order they were specified on the command line
 let missing: Vec<_> = files
 .iter()
-.filter(|file| missing.contains(file))
+.filter(|file| missing.contains(_ref()))
 .map(|file| file.clone())
 .collect();
 Ok(CatOutput {



To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel