Read about our upcoming Code of Conduct on this issue

This instance will be upgraded to Heptapod 0.28.1 on 2022-01-26 at 16:00 UTC+1 (a few minutes of down time)

Commit 27fa54cf authored by Arseniy Alekseyev's avatar Arseniy Alekseyev
Browse files

rhg: add support for narrow clones and sparse checkouts

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.

Differential Revision:
parent f77e4daaf612
Pipeline #29868 failed with stages
in 54 minutes and 56 seconds
......@@ -248,6 +248,14 @@ impl Repo {
pub fn has_sparse(&self) -> bool {
pub fn has_narrow(&self) -> bool {
fn dirstate_file_contents(&self) -> Result<Vec<u8>, HgError> {
......@@ -88,6 +88,10 @@ const SUPPORTED: &[&str] = &[
// When it starts writing to the repository, it’ll need to either keep the
// persistent nodemap up to date or remove this entry:
// Not all commands support `sparse` and `narrow`. The commands that do
// not should opt out by checking `has_sparse` and `has_narrow`.
// Copied from mercurial/
......@@ -40,10 +40,38 @@ pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
let rev = invocation.subcommand_args.value_of("rev");
let repo = invocation.repo?;
// It seems better if this check is removed: this would correspond to
// automatically enabling the extension if the repo requires it.
// However we need this check to be in sync with vanilla hg so hg tests
// pass.
if repo.has_sparse()
&& invocation.config.get(b"extensions", b"sparse").is_none()
return Err(CommandError::unsupported(
"repo is using sparse, but sparse extension is not enabled",
if let Some(rev) = rev {
if repo.has_narrow() {
return Err(CommandError::unsupported(
"rhg files -r <rev> is not supported in narrow clones",
let files = list_rev_tracked_files(repo, rev).map_err(|e| (e, rev))?;
display_files(invocation.ui, repo, files.iter())
} else {
// The dirstate always reflects the sparse narrowspec, so if
// we only have sparse without narrow all is fine.
// If we have narrow, then [hg files] needs to check if
// the store narrowspec is in sync with the one of the dirstate,
// so we can't support that without explicit code.
if repo.has_narrow() {
return Err(CommandError::unsupported(
"rhg files is not supported in narrow clones",
let distate = Dirstate::new(repo)?;
let files = distate.tracked_files()?;
display_files(invocation.ui, repo, files.into_iter().map(Ok))
......@@ -191,6 +191,13 @@ pub fn run(invocation: &crate::CliInvocation) -> Result<(), CommandError> {
let no_status = args.is_present("no-status");
let repo = invocation.repo?;
if repo.has_sparse() || repo.has_narrow() {
return Err(CommandError::unsupported(
"rhg status is not supported for sparse checkouts or narrow clones yet"
let mut dmap = repo.dirstate_map_mut()?;
let options = StatusOptions {
......@@ -593,7 +593,8 @@ impl OnUnsupported {
const SUPPORTED_EXTENSIONS: &[&[u8]] = &[b"blackbox", b"share"];
const SUPPORTED_EXTENSIONS: &[&[u8]] =
&[b"blackbox", b"share", b"sparse", b"narrow"];
fn check_extensions(config: &Config) -> Result<(), CommandError> {
let enabled = config.get_section_keys(b"extensions");
#require rhg
Rhg works well when sparse working copy is enabled.
$ cd "$TESTTMP"
$ hg init repo-sparse
$ cd repo-sparse
$ cat > .hg/hgrc <<EOF
> [extensions]
> sparse=
$ 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
$ tip=$(hg log -r . --template '{node}')
$ $NO_FALLBACK rhg files -r "$tip"
$ $NO_FALLBACK rhg files
$ $NO_FALLBACK rhg cat -r "$tip" hide
$ cd ..
We support most things when narrow is enabled, too, with a couple of caveats.
$ . "$TESTDIR/"
$ cat >> $HGRCPATH <<EOF
> [extensions]
> narrow=
$ 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
$ "$real_hg" cat -r "$tip" dir1/x
TODO: bad error message
$ $NO_FALLBACK rhg cat -r "$tip" hide
abort: invalid revision identifier: 6d714a4a2998cbfd0620db44da58b749f6565d63
$ "$real_hg" cat -r "$tip" hide
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 <rev> is not supported in narrow clones
$ "$real_hg" files -r "$tip"
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
Adding "orphaned" index files:
$ (cd ..; cp repo-sparse/.hg/store/data/hide.i repo-narrow/.hg/store/data/hide.i)
$ (cd ..; mkdir repo-narrow/.hg/store/data/dir2; cp repo-sparse/.hg/store/data/dir2/z.i 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"
# 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
$ "$real_hg" cat -r "$tip" dir2/z
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment