ok/jj
1
0
Fork 0
forked from mirrors/jj

working copy: return Box<dyn WorkingCopy> from finish()

This commit is contained in:
Martin von Zweigbergk 2023-10-13 22:46:28 -07:00 committed by Martin von Zweigbergk
parent 6a13fa8264
commit 580586d008
9 changed files with 61 additions and 37 deletions

View file

@ -41,7 +41,7 @@ use jj_lib::git_backend::GitBackend;
use jj_lib::gitignore::GitIgnoreFile;
use jj_lib::hex_util::to_reverse_hex;
use jj_lib::id_prefix::IdPrefixContext;
use jj_lib::local_working_copy::{LocalWorkingCopy, LockedLocalWorkingCopy};
use jj_lib::local_working_copy::LockedLocalWorkingCopy;
use jj_lib::matchers::{EverythingMatcher, Matcher, PrefixMatcher, Visit};
use jj_lib::merged_tree::{MergedTree, MergedTreeBuilder};
use jj_lib::op_heads_store::{self, OpHeadResolutionError, OpHeadsStore};
@ -853,7 +853,7 @@ impl WorkspaceCommandHelper {
&self.user_repo.repo
}
pub fn working_copy(&self) -> &LocalWorkingCopy {
pub fn working_copy(&self) -> &dyn WorkingCopy {
self.workspace.working_copy()
}

View file

@ -12,14 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::any::Any;
use std::fmt::Debug;
use std::io::Write as _;
use clap::Subcommand;
use jj_lib::backend::ObjectId;
use jj_lib::default_index_store::{DefaultIndexStore, ReadonlyIndexWrapper};
use jj_lib::local_working_copy::{LocalWorkingCopy, LockedLocalWorkingCopy};
use jj_lib::revset;
use jj_lib::working_copy::WorkingCopy;
use jj_lib::working_copy::{LockedWorkingCopy, WorkingCopy};
use crate::cli_util::{resolve_op_for_load, user_error, CommandError, CommandHelper};
use crate::template_parser;
@ -103,7 +105,7 @@ pub fn cmd_debug(
DebugCommands::Revset(args) => cmd_debug_revset(ui, command, args)?,
DebugCommands::WorkingCopy(_wc_matches) => {
let workspace_command = command.workspace_helper(ui)?;
let wc = workspace_command.working_copy();
let wc = check_local_disk_wc(workspace_command.working_copy().as_any())?;
writeln!(ui.stdout(), "Current operation: {:?}", wc.operation_id())?;
writeln!(ui.stdout(), "Current tree: {:?}", wc.tree_id()?)?;
for (file, state) in wc.file_states()? {
@ -250,16 +252,25 @@ fn cmd_debug_watchman(
let repo = workspace_command.repo().clone();
match subcommand {
DebugWatchmanSubcommand::QueryClock => {
let (clock, _changed_files) = workspace_command.working_copy().query_watchman()?;
let wc = check_local_disk_wc(workspace_command.working_copy().as_any())?;
let (clock, _changed_files) = wc.query_watchman()?;
writeln!(ui.stdout(), "Clock: {clock:?}")?;
}
DebugWatchmanSubcommand::QueryChangedFiles => {
let (_clock, changed_files) = workspace_command.working_copy().query_watchman()?;
let wc = check_local_disk_wc(workspace_command.working_copy().as_any())?;
let (_clock, changed_files) = wc.query_watchman()?;
writeln!(ui.stdout(), "Changed files: {changed_files:?}")?;
}
DebugWatchmanSubcommand::ResetClock => {
let (mut locked_ws, _commit) = workspace_command.start_working_copy_mutation()?;
locked_ws.locked_wc().reset_watchman()?;
let Some(locked_local_wc): Option<&mut LockedLocalWorkingCopy> =
locked_ws.locked_wc().as_any_mut().downcast_mut()
else {
return Err(user_error(
"This command requires a standard local-disk working copy",
));
};
locked_local_wc.reset_watchman()?;
locked_ws.finish(repo.op_id().clone())?;
writeln!(ui.stderr(), "Reset Watchman clock")?;
}
@ -277,3 +288,8 @@ fn cmd_debug_watchman(
"Cannot query Watchman because jj was not compiled with the `watchman` feature",
))
}
fn check_local_disk_wc(x: &dyn Any) -> Result<&LocalWorkingCopy, CommandError> {
x.downcast_ref()
.ok_or_else(|| user_error("This command requires a standard local-disk working copy"))
}

View file

@ -47,7 +47,7 @@ use jj_lib::revset_graph::{
};
use jj_lib::rewrite::{back_out_commit, merge_commit_trees, rebase_commit, DescendantRebaser};
use jj_lib::settings::UserSettings;
use jj_lib::working_copy::{LockedWorkingCopy, SnapshotOptions, WorkingCopy};
use jj_lib::working_copy::{LockedWorkingCopy, SnapshotOptions};
use jj_lib::workspace::Workspace;
use jj_lib::{conflicts, file_util, revset};
use maplit::{hashmap, hashset};

View file

@ -1490,6 +1490,10 @@ impl LockedWorkingCopy for LockedLocalWorkingCopy {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn old_operation_id(&self) -> &OperationId {
&self.old_operation_id
}
@ -1563,7 +1567,7 @@ impl LockedWorkingCopy for LockedLocalWorkingCopy {
fn finish(
mut self,
operation_id: OperationId,
) -> Result<LocalWorkingCopy, WorkingCopyStateError> {
) -> Result<Box<dyn WorkingCopy>, WorkingCopyStateError> {
assert!(self.tree_state_dirty || &self.old_tree_id == self.wc.tree_id()?);
if self.tree_state_dirty {
self.wc
@ -1579,7 +1583,7 @@ impl LockedWorkingCopy for LockedLocalWorkingCopy {
self.wc.save();
}
// TODO: Clear the "pending_checkout" file here.
Ok(self.wc)
Ok(Box::new(self.wc))
}
}

View file

@ -25,7 +25,7 @@ use thiserror::Error;
use crate::backend::{BackendError, MergedTreeId};
use crate::fsmonitor::FsmonitorKind;
use crate::gitignore::GitIgnoreFile;
use crate::local_working_copy::{LocalWorkingCopy, LockedLocalWorkingCopy};
use crate::local_working_copy::LockedLocalWorkingCopy;
use crate::merged_tree::MergedTree;
use crate::op_store::{OperationId, WorkspaceId};
use crate::repo_path::RepoPath;
@ -69,6 +69,9 @@ pub trait LockedWorkingCopy {
/// Should return `self`. For down-casting purposes.
fn as_any(&self) -> &dyn Any;
/// Should return `self`. For down-casting purposes.
fn as_any_mut(&mut self) -> &mut dyn Any;
/// The operation at the time the lock was taken
fn old_operation_id(&self) -> &OperationId;
@ -101,8 +104,10 @@ pub trait LockedWorkingCopy {
/// Finish the modifications to the working copy by writing the updated
/// states to disk. Returns the new (unlocked) working copy.
// TODO: return a `Box<dyn WorkingCopy>` instead
fn finish(self, operation_id: OperationId) -> Result<LocalWorkingCopy, WorkingCopyStateError>;
fn finish(
self,
operation_id: OperationId,
) -> Result<Box<dyn WorkingCopy>, WorkingCopyStateError>;
}
/// An error while snapshotting the working copy.

View file

@ -78,7 +78,7 @@ pub struct Workspace {
// working copy files live.
workspace_root: PathBuf,
repo_loader: RepoLoader,
working_copy: LocalWorkingCopy,
working_copy: Box<dyn WorkingCopy>,
}
fn create_jj_dir(workspace_root: &Path) -> Result<PathBuf, WorkspaceInitError> {
@ -98,7 +98,7 @@ fn init_working_copy(
workspace_root: &Path,
jj_dir: &Path,
workspace_id: WorkspaceId,
) -> Result<(LocalWorkingCopy, Arc<ReadonlyRepo>), WorkspaceInitError> {
) -> Result<(Box<dyn WorkingCopy>, Arc<ReadonlyRepo>), WorkspaceInitError> {
let working_copy_state_path = jj_dir.join("working_copy");
std::fs::create_dir(&working_copy_state_path).context(&working_copy_state_path)?;
@ -120,13 +120,13 @@ fn init_working_copy(
repo.op_id().clone(),
workspace_id,
)?;
Ok((working_copy, repo))
Ok((Box::new(working_copy), repo))
}
impl Workspace {
fn new(
workspace_root: &Path,
working_copy: LocalWorkingCopy,
working_copy: Box<dyn WorkingCopy>,
repo_loader: RepoLoader,
) -> Result<Workspace, PathError> {
let workspace_root = workspace_root.canonicalize().context(workspace_root)?;
@ -295,8 +295,8 @@ impl Workspace {
&self.repo_loader
}
pub fn working_copy(&self) -> &LocalWorkingCopy {
&self.working_copy
pub fn working_copy(&self) -> &dyn WorkingCopy {
self.working_copy.as_ref()
}
pub fn start_working_copy_mutation(
@ -415,7 +415,7 @@ impl WorkspaceLoader {
self.workspace_root.clone(),
self.working_copy_state_path.clone(),
);
let workspace = Workspace::new(&self.workspace_root, working_copy, repo_loader)?;
let workspace = Workspace::new(&self.workspace_root, Box::new(working_copy), repo_loader)?;
Ok(workspace)
}
}

View file

@ -34,9 +34,7 @@ use jj_lib::op_store::{OperationId, WorkspaceId};
use jj_lib::repo::{ReadonlyRepo, Repo};
use jj_lib::repo_path::{RepoPath, RepoPathComponent, RepoPathJoin};
use jj_lib::settings::UserSettings;
use jj_lib::working_copy::{
CheckoutStats, LockedWorkingCopy, SnapshotError, SnapshotOptions, WorkingCopy,
};
use jj_lib::working_copy::{CheckoutStats, LockedWorkingCopy, SnapshotError, SnapshotOptions};
use jj_lib::workspace::LockedWorkspace;
use test_case::test_case;
use testutils::{create_tree, write_random_commit, TestRepoBackend, TestWorkspace};
@ -417,7 +415,7 @@ 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());
let wc = ws.working_copy();
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
assert!(wc.file_states().unwrap().contains_key(&ignored_path));
// After we reset to the commit without the file, it should still exist on disk,
@ -427,7 +425,7 @@ fn test_reset() {
locked_ws.locked_wc().reset(&tree_without_file).unwrap();
locked_ws.finish(op_id.clone()).unwrap();
assert!(ignored_path.to_fs_path(&workspace_root).is_file());
let wc = ws.working_copy();
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
assert!(!wc.file_states().unwrap().contains_key(&ignored_path));
let new_tree = test_workspace.snapshot().unwrap();
assert_eq!(new_tree.id(), tree_without_file.id());
@ -439,7 +437,7 @@ fn test_reset() {
locked_ws.locked_wc().reset(&tree_with_file).unwrap();
locked_ws.finish(op_id.clone()).unwrap();
assert!(ignored_path.to_fs_path(&workspace_root).is_file());
let wc = ws.working_copy();
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
assert!(wc.file_states().unwrap().contains_key(&ignored_path));
let new_tree = test_workspace.snapshot().unwrap();
assert_eq!(new_tree.id(), tree_with_file.id());
@ -464,11 +462,12 @@ fn test_checkout_discard() {
let ws = &mut test_workspace.workspace;
ws.check_out(repo.op_id().clone(), None, &tree1).unwrap();
let state_path = ws.working_copy().state_path().to_path_buf();
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
let state_path = wc.state_path().to_path_buf();
// Test the setup: the file should exist on disk and in the tree state.
assert!(file1_path.to_fs_path(&workspace_root).is_file());
let wc = ws.working_copy();
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
assert!(wc.file_states().unwrap().contains_key(&file1_path));
// Start a checkout
@ -484,7 +483,7 @@ fn test_checkout_discard() {
drop(locked_ws);
// The change should remain in the working copy, but not in memory and not saved
let wc = ws.working_copy();
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
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());
@ -538,6 +537,7 @@ fn test_snapshot_special_file() {
let mut test_workspace = TestWorkspace::init(&settings);
let workspace_root = test_workspace.workspace.workspace_root().clone();
let store = test_workspace.repo.store();
let ws = &mut test_workspace.workspace;
let file1_path = RepoPath::from_internal_string("file1");
let file1_disk_path = file1_path.to_fs_path(&workspace_root);
@ -552,10 +552,7 @@ fn test_snapshot_special_file() {
assert!(!socket_disk_path.is_file());
// Snapshot the working copy with the socket file
let mut locked_ws = test_workspace
.workspace
.start_working_copy_mutation()
.unwrap();
let mut locked_ws = ws.start_working_copy_mutation().unwrap();
let tree_id = locked_ws
.locked_wc()
.snapshot(SnapshotOptions::empty_for_test())
@ -567,7 +564,7 @@ fn test_snapshot_special_file() {
tree.entries().map(|(path, _value)| path).collect_vec(),
vec![file1_path.clone(), file2_path.clone()]
);
let wc = test_workspace.workspace.working_copy();
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
assert_eq!(
wc.file_states().unwrap().keys().cloned().collect_vec(),
vec![file1_path, file2_path.clone()]
@ -582,7 +579,8 @@ fn test_snapshot_special_file() {
tree.entries().map(|(path, _value)| path).collect_vec(),
vec![file2_path.clone()]
);
let wc = test_workspace.workspace.working_copy();
let ws = &mut test_workspace.workspace;
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
assert_eq!(
wc.file_states().unwrap().keys().cloned().collect_vec(),
vec![file2_path]

View file

@ -18,7 +18,7 @@ use std::thread;
use assert_matches::assert_matches;
use jj_lib::repo::Repo;
use jj_lib::repo_path::RepoPath;
use jj_lib::working_copy::{CheckoutError, LockedWorkingCopy, SnapshotOptions, WorkingCopy};
use jj_lib::working_copy::{CheckoutError, LockedWorkingCopy, SnapshotOptions};
use jj_lib::workspace::Workspace;
use testutils::{create_tree, write_working_copy_file, TestRepo, TestWorkspace};

View file

@ -86,7 +86,7 @@ fn test_sparse_checkout() {
// Write the new state to disk
locked_ws.finish(repo.op_id().clone()).unwrap();
let wc = ws.working_copy();
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
assert_eq!(
wc.file_states().unwrap().keys().collect_vec(),
vec![&dir1_file1_path, &dir1_file2_path, &dir1_subdir1_file1_path]
@ -130,6 +130,7 @@ fn test_sparse_checkout() {
.exists());
assert!(dir2_file1_path.to_fs_path(&working_copy_path).exists());
let wc = locked_wc.finish(repo.op_id().clone()).unwrap();
let wc: &LocalWorkingCopy = wc.as_any().downcast_ref().unwrap();
assert_eq!(
wc.file_states().unwrap().keys().collect_vec(),
vec![&dir1_subdir1_file1_path, &dir2_file1_path, &root_file1_path]