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

login
register
mail settings
Submitter phabricator
Date Nov. 11, 2021, 7:24 p.m.
Message ID <differential-rev-PHID-DREV-a5jru7efewfj6hzp5q7l-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/50077/
State Superseded
Headers show

Comments

phabricator - Nov. 11, 2021, 7:24 p.m.
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




To: aalekseyev, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

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 <<EOF
+  > [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: &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(
         &AlwaysMatcher,
         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(&line);
+        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(&line);
         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(&self) -> Vfs<'_> {
+    fn hg_vfs(&self) -> Vfs<'_> {
+        Vfs { base: &self.dot_hg }
+    }
+
+    fn has_sparse(&self) -> bool {
+        self.requirements.contains(requirements::SPARSE_REQUIREMENT)
+    }
+
+    fn check_not_sparse(&self) -> Result<(), HgError> {
+        if self.has_sparse() {
+            Err(HgError::unsupported(format!(
+            "repository uses a sparse working copy, this is not supported by rhg",
+        )))
+        } else {
+            Ok(())
+        }
+    }
+
+    /// For accessing the dirstate.
+    pub fn hg_vfs_for_dirstate(&self) -> Result<Vfs<'_>, HgError> {
+        let () = self.check_not_sparse()?;
+        Ok(Vfs { base: &self.dot_hg })
+    }
+
+    pub fn hg_vfs_not_using_dirstate(&self) -> Vfs<'_> {
         Vfs { base: &self.dot_hg }
     }
 
@@ -237,10 +261,19 @@ 
     }
 
     /// For accessing the working copy
-    pub fn working_directory_vfs(&self) -> Vfs<'_> {
-        Vfs {
+    pub fn working_directory_vfs(&self) -> Result<Vfs<'_>, HgError> {
+        let () = self.check_not_sparse()?;
+        Ok(Vfs {
             base: &self.working_directory,
-        }
+        })
+    }
+
+    pub fn is_a_subrepo(&self) -> bool {
+        (Vfs {
+            base: &self.working_directory,
+        })
+        .join(".hgsub")
+        .exists()
     }
 
     pub fn has_dirstate_v2(&self) -> bool {
diff --git a/rust/hg-core/src/operations/list_tracked_files.rs b/rust/hg-core/src/operations/list_tracked_files.rs
--- a/rust/hg-core/src/operations/list_tracked_files.rs
+++ b/rust/hg-core/src/operations/list_tracked_files.rs
@@ -25,11 +25,12 @@ 
 
 impl Dirstate {
     pub fn new(repo: &Repo) -> Result<Self, HgError> {
-        let mut content = repo.hg_vfs().read("dirstate")?;
+        let hg_vfs = repo.hg_vfs_for_dirstate()?;
+        let mut content = hg_vfs.read("dirstate")?;
         let v2_metadata = if repo.has_dirstate_v2() {
             let docket = read_docket(&content)?;
             let meta = docket.tree_metadata().to_vec();
-            content = repo.hg_vfs().read(docket.data_filename())?;
+            content = hg_vfs.read(docket.data_filename())?;
             Some(meta)
         } else {
             None