working_copy: return Result from WorkingCopy::tree_state/WorkingCopy::tree_state_mut

This commit is contained in:
Waleed Khan 2023-07-14 23:33:59 +03:00
parent 60f1d7e307
commit 6d7998f8c5
7 changed files with 124 additions and 104 deletions

View File

@ -15,6 +15,7 @@
#![allow(missing_docs)]
use std::collections::{BTreeMap, HashSet};
use std::error::Error;
use std::ffi::OsString;
use std::fs;
use std::fs::{DirEntry, File, Metadata, OpenOptions};
@ -299,6 +300,8 @@ pub enum SnapshotError {
InvalidUtf8SymlinkTarget { path: PathBuf, target: PathBuf },
#[error("Internal backend error: {0}")]
InternalBackendError(#[from] BackendError),
#[error(transparent)]
TreeStateError(#[from] TreeStateError),
}
#[derive(Debug, Error)]
@ -374,6 +377,8 @@ pub enum ResetError {
},
#[error("Internal error: {0}")]
InternalBackendError(#[from] BackendError),
#[error(transparent)]
TreeStateError(#[from] TreeStateError),
}
#[derive(Debug, Error)]
@ -398,6 +403,8 @@ pub enum TreeStateError {
path: PathBuf,
source: tempfile::PersistError,
},
#[error("Filesystem monitor error: {0}")]
Fsmonitor(Box<dyn Error + Send + Sync>),
}
impl TreeState {
@ -580,10 +587,16 @@ impl TreeState {
#[tokio::main]
pub async fn query_watchman(
&self,
) -> Result<(watchman::Clock, Option<Vec<PathBuf>>), watchman::Error> {
let fsmonitor = watchman::Fsmonitor::init(&self.working_copy_path).await?;
) -> Result<(watchman::Clock, Option<Vec<PathBuf>>), TreeStateError> {
let fsmonitor = watchman::Fsmonitor::init(&self.working_copy_path)
.await
.map_err(|err| TreeStateError::Fsmonitor(Box::new(err)))?;
let previous_clock = self.watchman_clock.clone().map(watchman::Clock::from);
fsmonitor.query_changed_files(previous_clock).await
let changed_files = fsmonitor
.query_changed_files(previous_clock)
.await
.map_err(|err| TreeStateError::Fsmonitor(Box::new(err)))?;
Ok(changed_files)
}
/// Look for changes to the working copy. If there are any changes, create
@ -1306,33 +1319,31 @@ impl WorkingCopy {
&self.checkout_state().workspace_id
}
fn tree_state(&self) -> &TreeState {
self.tree_state
.get_or_try_init(|| {
TreeState::load(
self.store.clone(),
self.working_copy_path.clone(),
self.state_path.clone(),
)
})
.unwrap() // FIXME: propagate error
fn tree_state(&self) -> Result<&TreeState, TreeStateError> {
self.tree_state.get_or_try_init(|| {
TreeState::load(
self.store.clone(),
self.working_copy_path.clone(),
self.state_path.clone(),
)
})
}
fn tree_state_mut(&mut self) -> &mut TreeState {
self.tree_state(); // ensure loaded
self.tree_state.get_mut().unwrap()
fn tree_state_mut(&mut self) -> Result<&mut TreeState, TreeStateError> {
self.tree_state()?; // ensure loaded
Ok(self.tree_state.get_mut().unwrap())
}
pub fn current_tree_id(&self) -> &TreeId {
self.tree_state().current_tree_id()
pub fn current_tree_id(&self) -> Result<&TreeId, TreeStateError> {
Ok(self.tree_state()?.current_tree_id())
}
pub fn file_states(&self) -> &BTreeMap<RepoPath, FileState> {
self.tree_state().file_states()
pub fn file_states(&self) -> Result<&BTreeMap<RepoPath, FileState>, TreeStateError> {
Ok(self.tree_state()?.file_states())
}
pub fn sparse_patterns(&self) -> &[RepoPath] {
self.tree_state().sparse_patterns()
pub fn sparse_patterns(&self) -> Result<&[RepoPath], TreeStateError> {
Ok(self.tree_state()?.sparse_patterns())
}
fn save(&mut self) {
@ -1343,7 +1354,7 @@ impl WorkingCopy {
});
}
pub fn start_mutation(&mut self) -> LockedWorkingCopy {
pub fn start_mutation(&mut self) -> Result<LockedWorkingCopy, TreeStateError> {
let lock_path = self.state_path.join("working_copy.lock");
let lock = FileLock::lock(lock_path);
@ -1353,16 +1364,16 @@ impl WorkingCopy {
// has changed.
self.tree_state.take();
let old_operation_id = self.operation_id().clone();
let old_tree_id = self.current_tree_id().clone();
let old_tree_id = self.current_tree_id()?.clone();
LockedWorkingCopy {
Ok(LockedWorkingCopy {
wc: self,
lock,
old_operation_id,
old_tree_id,
tree_state_dirty: false,
closed: false,
}
})
}
pub fn check_out(
@ -1371,7 +1382,7 @@ impl WorkingCopy {
old_tree_id: Option<&TreeId>,
new_tree: &Tree,
) -> Result<CheckoutStats, CheckoutError> {
let mut locked_wc = self.start_mutation();
let mut locked_wc = self.start_mutation()?;
// Check if the current working-copy commit has changed on disk compared to what
// the caller expected. It's safe to check out another commit
// regardless, but it's probably not what the caller wanted, so we let
@ -1390,8 +1401,8 @@ impl WorkingCopy {
#[cfg(feature = "watchman")]
pub fn query_watchman(
&self,
) -> Result<(watchman::Clock, Option<Vec<PathBuf>>), watchman::Error> {
self.tree_state().query_watchman()
) -> Result<(watchman::Clock, Option<Vec<PathBuf>>), TreeStateError> {
self.tree_state()?.query_watchman()
}
}
@ -1419,7 +1430,7 @@ impl LockedWorkingCopy<'_> {
}
pub fn reset_watchman(&mut self) -> Result<(), SnapshotError> {
self.wc.tree_state_mut().reset_watchman();
self.wc.tree_state_mut()?.reset_watchman();
self.tree_state_dirty = true;
Ok(())
}
@ -1428,7 +1439,7 @@ impl LockedWorkingCopy<'_> {
// because the TreeState may be long-lived if the library is used in a
// long-lived process.
pub fn snapshot(&mut self, options: SnapshotOptions) -> Result<TreeId, SnapshotError> {
let tree_state = self.wc.tree_state_mut();
let tree_state = self.wc.tree_state_mut()?;
self.tree_state_dirty |= tree_state.snapshot(options)?;
Ok(tree_state.current_tree_id().clone())
}
@ -1436,18 +1447,18 @@ impl LockedWorkingCopy<'_> {
pub fn check_out(&mut self, new_tree: &Tree) -> Result<CheckoutStats, CheckoutError> {
// TODO: Write a "pending_checkout" file with the new TreeId so we can
// continue an interrupted update if we find such a file.
let stats = self.wc.tree_state_mut().check_out(new_tree)?;
let stats = self.wc.tree_state_mut()?.check_out(new_tree)?;
self.tree_state_dirty = true;
Ok(stats)
}
pub fn reset(&mut self, new_tree: &Tree) -> Result<(), ResetError> {
self.wc.tree_state_mut().reset(new_tree)?;
self.wc.tree_state_mut()?.reset(new_tree)?;
self.tree_state_dirty = true;
Ok(())
}
pub fn sparse_patterns(&self) -> &[RepoPath] {
pub fn sparse_patterns(&self) -> Result<&[RepoPath], TreeStateError> {
self.wc.sparse_patterns()
}
@ -1459,16 +1470,16 @@ impl LockedWorkingCopy<'_> {
// continue an interrupted update if we find such a file.
let stats = self
.wc
.tree_state_mut()
.tree_state_mut()?
.set_sparse_patterns(new_sparse_patterns)?;
self.tree_state_dirty = true;
Ok(stats)
}
pub fn finish(mut self, operation_id: OperationId) -> Result<(), TreeStateError> {
assert!(self.tree_state_dirty || &self.old_tree_id == self.wc.current_tree_id());
assert!(self.tree_state_dirty || &self.old_tree_id == self.wc.current_tree_id()?);
if self.tree_state_dirty {
self.wc.tree_state_mut().save()?;
self.wc.tree_state_mut()?.save()?;
}
if self.old_operation_id != operation_id {
self.wc.checkout_state_mut().operation_id = operation_id;

View File

@ -44,8 +44,8 @@ fn test_root(use_git: bool) {
let repo = &test_workspace.repo;
let wc = test_workspace.workspace.working_copy_mut();
assert_eq!(wc.sparse_patterns(), vec![RepoPath::root()]);
let mut locked_wc = wc.start_mutation();
assert_eq!(wc.sparse_patterns().unwrap(), vec![RepoPath::root()]);
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -205,7 +205,7 @@ fn test_checkout_file_transitions(use_git: bool) {
.unwrap();
// Check that the working copy is clean.
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -340,17 +340,17 @@ fn test_reset() {
// Test the setup: the file should exist on disk and in the tree state.
assert!(ignored_path.to_fs_path(&workspace_root).is_file());
assert!(wc.file_states().contains_key(&ignored_path));
assert!(wc.file_states().unwrap().contains_key(&ignored_path));
// After we reset to the commit without the file, it should still exist on disk,
// but it should not be in the tree state, and it should not get added when we
// commit the working copy (because it's ignored).
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
locked_wc.reset(&tree_without_file).unwrap();
locked_wc.finish(repo.op_id().clone()).unwrap();
assert!(ignored_path.to_fs_path(&workspace_root).is_file());
assert!(!wc.file_states().contains_key(&ignored_path));
let mut locked_wc = wc.start_mutation();
assert!(!wc.file_states().unwrap().contains_key(&ignored_path));
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -360,12 +360,12 @@ fn test_reset() {
// After we reset to the commit without the file, it should still exist on disk,
// but it should not be in the tree state, and it should not get added when we
// commit the working copy (because it's ignored).
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
locked_wc.reset(&tree_without_file).unwrap();
locked_wc.finish(repo.op_id().clone()).unwrap();
assert!(ignored_path.to_fs_path(&workspace_root).is_file());
assert!(!wc.file_states().contains_key(&ignored_path));
let mut locked_wc = wc.start_mutation();
assert!(!wc.file_states().unwrap().contains_key(&ignored_path));
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -374,12 +374,12 @@ fn test_reset() {
// Now test the opposite direction: resetting to a commit where the file is
// tracked. The file should become tracked (even though it's ignored).
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
locked_wc.reset(&tree_with_file).unwrap();
locked_wc.finish(repo.op_id().clone()).unwrap();
assert!(ignored_path.to_fs_path(&workspace_root).is_file());
assert!(wc.file_states().contains_key(&ignored_path));
let mut locked_wc = wc.start_mutation();
assert!(wc.file_states().unwrap().contains_key(&ignored_path));
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -410,27 +410,27 @@ fn test_checkout_discard() {
// Test the setup: the file should exist on disk and in the tree state.
assert!(file1_path.to_fs_path(&workspace_root).is_file());
assert!(wc.file_states().contains_key(&file1_path));
assert!(wc.file_states().unwrap().contains_key(&file1_path));
// Start a checkout
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
locked_wc.check_out(&tree2).unwrap();
// The change should be reflected in the working copy but not saved
assert!(!file1_path.to_fs_path(&workspace_root).is_file());
assert!(file2_path.to_fs_path(&workspace_root).is_file());
let reloaded_wc = WorkingCopy::load(store.clone(), workspace_root.clone(), state_path.clone());
assert!(reloaded_wc.file_states().contains_key(&file1_path));
assert!(!reloaded_wc.file_states().contains_key(&file2_path));
assert!(reloaded_wc.file_states().unwrap().contains_key(&file1_path));
assert!(!reloaded_wc.file_states().unwrap().contains_key(&file2_path));
locked_wc.discard();
// The change should remain in the working copy, but not in memory and not saved
assert!(wc.file_states().contains_key(&file1_path));
assert!(!wc.file_states().contains_key(&file2_path));
assert!(wc.file_states().unwrap().contains_key(&file1_path));
assert!(!wc.file_states().unwrap().contains_key(&file2_path));
assert!(!file1_path.to_fs_path(&workspace_root).is_file());
assert!(file2_path.to_fs_path(&workspace_root).is_file());
let reloaded_wc = WorkingCopy::load(store.clone(), workspace_root, state_path);
assert!(reloaded_wc.file_states().contains_key(&file1_path));
assert!(!reloaded_wc.file_states().contains_key(&file2_path));
assert!(reloaded_wc.file_states().unwrap().contains_key(&file1_path));
assert!(!reloaded_wc.file_states().unwrap().contains_key(&file2_path));
}
#[test_case(false ; "local backend")]
@ -457,7 +457,7 @@ fn test_snapshot_racy_timestamps(use_git: bool) {
.unwrap();
file.write_all(format!("contents {i}").as_bytes()).unwrap();
}
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -491,7 +491,7 @@ fn test_snapshot_special_file() {
// Snapshot the working copy with the socket file
let wc = test_workspace.workspace.working_copy_mut();
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -503,14 +503,14 @@ fn test_snapshot_special_file() {
vec![file1_path.clone(), file2_path.clone()]
);
assert_eq!(
wc.file_states().keys().cloned().collect_vec(),
wc.file_states().unwrap().keys().cloned().collect_vec(),
vec![file1_path, file2_path.clone()]
);
// Replace a regular file by a socket and snapshot the working copy again
std::fs::remove_file(&file1_disk_path).unwrap();
UnixListener::bind(&file1_disk_path).unwrap();
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -522,7 +522,7 @@ fn test_snapshot_special_file() {
vec![file2_path.clone()]
);
assert_eq!(
wc.file_states().keys().cloned().collect_vec(),
wc.file_states().unwrap().keys().cloned().collect_vec(),
vec![file2_path]
);
}
@ -552,7 +552,7 @@ fn test_gitignores(use_git: bool) {
testutils::write_working_copy_file(&workspace_root, &subdir_modified_path, "1");
let wc = test_workspace.workspace.working_copy_mut();
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id1 = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -584,7 +584,7 @@ fn test_gitignores(use_git: bool) {
testutils::write_working_copy_file(&workspace_root, &subdir_modified_path, "2");
testutils::write_working_copy_file(&workspace_root, &subdir_ignored_path, "2");
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id2 = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -675,7 +675,7 @@ fn test_gitignores_ignored_directory_already_tracked(use_git: bool) {
// Check that the file is still in the tree created by snapshotting the working
// copy (that it didn't get removed because the directory is ignored)
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -707,7 +707,11 @@ fn test_dotgit_ignored(use_git: bool) {
&RepoPath::from_internal_string(".git/file"),
"contents",
);
let mut locked_wc = test_workspace.workspace.working_copy_mut().start_mutation();
let mut locked_wc = test_workspace
.workspace
.working_copy_mut()
.start_mutation()
.unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -721,7 +725,11 @@ fn test_dotgit_ignored(use_git: bool) {
&RepoPath::from_internal_string(".git"),
"contents",
);
let mut locked_wc = test_workspace.workspace.working_copy_mut().start_mutation();
let mut locked_wc = test_workspace
.workspace
.working_copy_mut()
.start_mutation()
.unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -777,7 +785,7 @@ fn test_gitsubmodule() {
// Check that the files present in the submodule are not tracked
// when we snapshot
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -831,7 +839,7 @@ fn test_fsmonitor() {
let workspace_root = test_workspace.workspace.workspace_root().clone();
let wc = test_workspace.workspace.working_copy_mut();
assert_eq!(wc.sparse_patterns(), vec![RepoPath::root()]);
assert_eq!(wc.sparse_patterns().unwrap(), vec![RepoPath::root()]);
let foo_path = RepoPath::from_internal_string("foo");
let bar_path = RepoPath::from_internal_string("bar");
@ -861,14 +869,14 @@ fn test_fsmonitor() {
};
{
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let tree_id = snapshot(&mut locked_wc, &[]);
assert_eq!(tree_id, *repo.store().empty_tree_id());
locked_wc.discard();
}
{
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let tree_id = snapshot(&mut locked_wc, &[&foo_path]);
insta::assert_snapshot!(testutils::dump_tree(repo.store(), &tree_id), @r###"
tree 205f6b799e7d5c2524468ca006a0131aa57ecce7
@ -878,7 +886,7 @@ fn test_fsmonitor() {
}
{
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let tree_id = snapshot(
&mut locked_wc,
&[&foo_path, &bar_path, &nested_path, &ignored_path],
@ -895,7 +903,7 @@ fn test_fsmonitor() {
{
testutils::write_working_copy_file(&workspace_root, &foo_path, "updated foo\n");
testutils::write_working_copy_file(&workspace_root, &bar_path, "updated bar\n");
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let tree_id = snapshot(&mut locked_wc, &[&foo_path]);
insta::assert_snapshot!(testutils::dump_tree(repo.store(), &tree_id), @r###"
tree 2f57ab8f48ae62e3137079f2add9878dfa1d1bcc
@ -908,7 +916,7 @@ fn test_fsmonitor() {
{
std::fs::remove_file(foo_path.to_fs_path(&workspace_root)).unwrap();
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let tree_id = snapshot(&mut locked_wc, &[&foo_path]);
insta::assert_snapshot!(testutils::dump_tree(repo.store(), &tree_id), @r###"
tree 34b83765131477e1a7d72160079daec12c6144e3

View File

@ -72,7 +72,10 @@ fn test_concurrent_checkout(use_git: bool) {
// Check that the tree2 is still checked out on disk.
let workspace3 =
Workspace::load(&settings, &workspace1_root, &StoreFactories::default()).unwrap();
assert_eq!(workspace3.working_copy().current_tree_id(), &tree_id2);
assert_eq!(
workspace3.working_copy().current_tree_id().unwrap(),
&tree_id2
);
}
#[test_case(false ; "local backend")]
@ -133,7 +136,7 @@ fn test_checkout_parallel(use_git: bool) {
// different tree than the one we just checked out, but since
// write_tree() should take the same lock as check_out(), write_tree()
// should never produce a different tree.
let mut locked_wc = workspace.working_copy_mut().start_mutation();
let mut locked_wc = workspace.working_copy_mut().start_mutation().unwrap();
let new_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();

View File

@ -52,7 +52,7 @@ fn test_sparse_checkout() {
wc.check_out(repo.op_id().clone(), None, &tree).unwrap();
// Set sparse patterns to only dir1/
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let sparse_patterns = vec![dir1_path];
let stats = locked_wc
.set_sparse_patterns(sparse_patterns.clone())
@ -65,7 +65,7 @@ fn test_sparse_checkout() {
removed_files: 3
}
);
assert_eq!(locked_wc.sparse_patterns(), sparse_patterns);
assert_eq!(locked_wc.sparse_patterns().unwrap(), sparse_patterns);
assert!(!root_file1_path.to_fs_path(&working_copy_path).exists());
assert!(!root_file2_path.to_fs_path(&working_copy_path).exists());
assert!(dir1_file1_path.to_fs_path(&working_copy_path).exists());
@ -78,10 +78,10 @@ fn test_sparse_checkout() {
// Write the new state to disk
locked_wc.finish(repo.op_id().clone()).unwrap();
assert_eq!(
wc.file_states().keys().collect_vec(),
wc.file_states().unwrap().keys().collect_vec(),
vec![&dir1_file1_path, &dir1_file2_path, &dir1_subdir1_file1_path]
);
assert_eq!(wc.sparse_patterns(), sparse_patterns);
assert_eq!(wc.sparse_patterns().unwrap(), sparse_patterns);
// Reload the state to check that it was persisted
let mut wc = WorkingCopy::load(
@ -90,13 +90,13 @@ fn test_sparse_checkout() {
wc.state_path().to_path_buf(),
);
assert_eq!(
wc.file_states().keys().collect_vec(),
wc.file_states().unwrap().keys().collect_vec(),
vec![&dir1_file1_path, &dir1_file2_path, &dir1_subdir1_file1_path]
);
assert_eq!(wc.sparse_patterns(), sparse_patterns);
assert_eq!(wc.sparse_patterns().unwrap(), sparse_patterns);
// Set sparse patterns to file2, dir1/subdir1/ and dir2/
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let sparse_patterns = vec![root_file1_path.clone(), dir1_subdir1_path, dir2_path];
let stats = locked_wc
.set_sparse_patterns(sparse_patterns.clone())
@ -109,7 +109,7 @@ fn test_sparse_checkout() {
removed_files: 2
}
);
assert_eq!(locked_wc.sparse_patterns(), sparse_patterns);
assert_eq!(locked_wc.sparse_patterns().unwrap(), sparse_patterns);
assert!(root_file1_path.to_fs_path(&working_copy_path).exists());
assert!(!root_file2_path.to_fs_path(&working_copy_path).exists());
assert!(!dir1_file1_path.to_fs_path(&working_copy_path).exists());
@ -120,7 +120,7 @@ fn test_sparse_checkout() {
assert!(dir2_file1_path.to_fs_path(&working_copy_path).exists());
locked_wc.finish(repo.op_id().clone()).unwrap();
assert_eq!(
wc.file_states().keys().collect_vec(),
wc.file_states().unwrap().keys().collect_vec(),
vec![&dir1_subdir1_file1_path, &dir2_file1_path, &root_file1_path]
);
}
@ -152,7 +152,7 @@ fn test_sparse_commit() {
wc.check_out(repo.op_id().clone(), None, &tree).unwrap();
// Set sparse patterns to only dir1/
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let sparse_patterns = vec![dir1_path.clone()];
locked_wc.set_sparse_patterns(sparse_patterns).unwrap();
locked_wc.finish(repo.op_id().clone()).unwrap();
@ -166,7 +166,7 @@ fn test_sparse_commit() {
// Create a tree from the working copy. Only dir1/file1 should be updated in the
// tree.
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let modified_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -180,14 +180,14 @@ fn test_sparse_commit() {
assert_eq!(diff[0].0, dir1_file1_path);
// Set sparse patterns to also include dir2/
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let sparse_patterns = vec![dir1_path, dir2_path];
locked_wc.set_sparse_patterns(sparse_patterns).unwrap();
locked_wc.finish(repo.op_id().clone()).unwrap();
// Create a tree from the working copy. Only dir1/file1 and dir2/file1 should be
// updated in the tree.
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let modified_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();
@ -217,7 +217,7 @@ fn test_sparse_commit_gitignore() {
let wc = test_workspace.workspace.working_copy_mut();
// Set sparse patterns to only dir1/
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let sparse_patterns = vec![dir1_path.clone()];
locked_wc.set_sparse_patterns(sparse_patterns).unwrap();
locked_wc.finish(repo.op_id().clone()).unwrap();
@ -230,7 +230,7 @@ fn test_sparse_commit_gitignore() {
// Create a tree from the working copy. Only dir1/file2 should be updated in the
// tree because dir1/file1 is ignored.
let mut locked_wc = wc.start_mutation();
let mut locked_wc = wc.start_mutation().unwrap();
let modified_tree_id = locked_wc
.snapshot(SnapshotOptions::empty_for_test())
.unwrap();

View File

@ -749,7 +749,7 @@ impl WorkspaceCommandHelper {
tx.mut_repo()
.check_out(workspace_id, &self.settings, &new_git_head_commit)?;
let mut locked_working_copy =
self.workspace.working_copy_mut().start_mutation();
self.workspace.working_copy_mut().start_mutation()?;
// The working copy was presumably updated by the git command that updated
// HEAD, so we just need to reset our working copy
// state to it without updating working copy files.
@ -825,7 +825,7 @@ impl WorkspaceCommandHelper {
return Err(user_error("Nothing checked out in this workspace"));
};
let locked_working_copy = self.workspace.working_copy_mut().start_mutation();
let locked_working_copy = self.workspace.working_copy_mut().start_mutation()?;
Ok((locked_working_copy, wc_commit))
}
@ -1104,7 +1104,7 @@ impl WorkspaceCommandHelper {
let base_ignores = self.base_ignores();
// Compare working-copy tree and operation with repo's, and reload as needed.
let mut locked_wc = self.workspace.working_copy_mut().start_mutation();
let mut locked_wc = self.workspace.working_copy_mut().start_mutation()?;
let old_op_id = locked_wc.old_operation_id().clone();
let (repo, wc_commit) = match check_stale_working_copy(&locked_wc, &wc_commit, &repo) {
Ok(None) => (repo, wc_commit),
@ -1816,7 +1816,7 @@ pub fn update_working_copy(
Some(stats)
} else {
// Record new operation id which represents the latest working-copy state
let locked_wc = wc.start_mutation();
let locked_wc = wc.start_mutation()?;
locked_wc.finish(repo.op_id().clone())?;
None
};

View File

@ -103,8 +103,8 @@ pub fn cmd_debug(
let workspace_command = command.workspace_helper(ui)?;
let wc = workspace_command.working_copy();
writeln!(ui, "Current operation: {:?}", wc.operation_id())?;
writeln!(ui, "Current tree: {:?}", wc.current_tree_id())?;
for (file, state) in wc.file_states() {
writeln!(ui, "Current tree: {:?}", wc.current_tree_id()?)?;
for (file, state) in wc.file_states()? {
writeln!(
ui,
"{:?} {:13?} {:10?} {:?}",
@ -238,13 +238,11 @@ fn cmd_debug_watchman(
let repo = workspace_command.repo().clone();
match subcommand {
DebugWatchmanSubcommand::QueryClock => {
let (clock, _changed_files) =
workspace_command.working_copy().query_watchman().unwrap();
let (clock, _changed_files) = workspace_command.working_copy().query_watchman()?;
ui.write(&format!("Clock: {clock:?}"))?;
}
DebugWatchmanSubcommand::QueryChangedFiles => {
let (_clock, changed_files) =
workspace_command.working_copy().query_watchman().unwrap();
let (_clock, changed_files) = workspace_command.working_copy().query_watchman()?;
ui.write(&format!("Changed files: {changed_files:?}"))?;
}
DebugWatchmanSubcommand::ResetClock => {

View File

@ -3559,7 +3559,7 @@ fn cmd_sparse_list(
_args: &SparseListArgs,
) -> Result<(), CommandError> {
let workspace_command = command.workspace_helper(ui)?;
for path in workspace_command.working_copy().sparse_patterns() {
for path in workspace_command.working_copy().sparse_patterns()? {
let ui_path = workspace_command.format_file_path(path);
writeln!(ui, "{ui_path}")?;
}
@ -3596,7 +3596,7 @@ fn cmd_sparse_set(
new_patterns.insert(RepoPath::root());
} else {
if !args.clear {
new_patterns.extend(locked_wc.sparse_patterns().iter().cloned());
new_patterns.extend(locked_wc.sparse_patterns()?.iter().cloned());
for path in paths_to_remove {
new_patterns.remove(&path);
}