D12623: clone: use better names for temp files
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
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
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
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
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()
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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]
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
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`
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
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
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
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
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
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
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
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
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.
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
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
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