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

working_copy: make reset() take a commit instead of a tree

Our virtual file system at Google (CitC) would like to know the commit
so it can scan backwards and find the closest mainline tree based on
it. Since we always record an operation id (which resolves to a
working-copy commit) when we write the working-copy state, it doesn't
seem like a restriction to require a commit.
This commit is contained in:
Martin von Zweigbergk 2024-01-23 10:13:37 -08:00 committed by Martin von Zweigbergk
parent 9feffa54c8
commit b343289238
6 changed files with 23 additions and 22 deletions

View file

@ -23,7 +23,6 @@ use jj_lib::backend::{Backend, MergedTreeId};
use jj_lib::commit::Commit;
use jj_lib::git_backend::GitBackend;
use jj_lib::local_working_copy::LocalWorkingCopy;
use jj_lib::merged_tree::MergedTree;
use jj_lib::op_store::{OperationId, WorkspaceId};
use jj_lib::repo::ReadonlyRepo;
use jj_lib::repo_path::RepoPathBuf;
@ -237,8 +236,8 @@ impl LockedWorkingCopy for LockedConflictsWorkingCopy {
self.inner.check_out(commit)
}
fn reset(&mut self, new_tree: &MergedTree) -> Result<(), ResetError> {
self.inner.reset(new_tree)
fn reset(&mut self, commit: &Commit) -> Result<(), ResetError> {
self.inner.reset(commit)
}
fn sparse_patterns(&self) -> Result<&[RepoPathBuf], WorkingCopyStateError> {

View file

@ -925,8 +925,7 @@ impl WorkspaceCommandHelper {
// 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.
let new_git_head_tree = new_git_head_commit.tree()?;
locked_ws.locked_wc().reset(&new_git_head_tree)?;
locked_ws.locked_wc().reset(&new_git_head_commit)?;
tx.mut_repo().rebase_descendants(&self.settings)?;
self.user_repo = ReadonlyUserRepo::new(tx.commit("import git head"));
locked_ws.finish(self.user_repo.repo.op_id().clone())?;

View file

@ -55,9 +55,13 @@ pub(crate) fn cmd_untrack(
tree_builder.set_or_remove(path, Merge::absent());
}
let new_tree_id = tree_builder.write_tree(&store)?;
let new_tree = store.get_root_tree(&new_tree_id)?;
// Reset the working copy to the new tree
locked_ws.locked_wc().reset(&new_tree)?;
let new_commit = tx
.mut_repo()
.rewrite_commit(command.settings(), &wc_commit)
.set_tree_id(new_tree_id)
.write()?;
// Reset the working copy to the new commit
locked_ws.locked_wc().reset(&new_commit)?;
// Commit the working copy again so we can inform the user if paths couldn't be
// untracked because they're not ignored.
let wc_tree_id = locked_ws.locked_wc().snapshot(SnapshotOptions {
@ -66,7 +70,7 @@ pub(crate) fn cmd_untrack(
progress: None,
max_new_file_size: command.settings().max_new_file_size()?,
})?;
if wc_tree_id != new_tree_id {
if wc_tree_id != *new_commit.tree_id() {
let wc_tree = store.get_root_tree(&wc_tree_id)?;
let added_back = wc_tree.entries_matching(matcher.as_ref()).collect_vec();
if !added_back.is_empty() {
@ -90,13 +94,9 @@ Make sure they're ignored, then try again.",
} else {
// This means there were some concurrent changes made in the working copy. We
// don't want to mix those in, so reset the working copy again.
locked_ws.locked_wc().reset(&new_tree)?;
locked_ws.locked_wc().reset(&new_commit)?;
}
}
tx.mut_repo()
.rewrite_commit(command.settings(), &wc_commit)
.set_tree_id(new_tree_id)
.write()?;
let num_rebased = tx.mut_repo().rebase_descendants(command.settings())?;
if num_rebased > 0 {
writeln!(ui.stderr(), "Rebased {num_rebased} descendant commits")?;

View file

@ -1739,14 +1739,15 @@ impl LockedWorkingCopy for LockedLocalWorkingCopy {
Ok(stats)
}
fn reset(&mut self, new_tree: &MergedTree) -> Result<(), ResetError> {
fn reset(&mut self, commit: &Commit) -> Result<(), ResetError> {
let new_tree = commit.tree()?;
self.wc
.tree_state_mut()
.map_err(|err| ResetError::Other {
message: "Failed to read the working copy state".to_string(),
err: err.into(),
})?
.reset(new_tree)
.reset(&new_tree)
.block_on()?;
self.tree_state_dirty = true;
Ok(())

View file

@ -26,7 +26,6 @@ use crate::backend::{BackendError, MergedTreeId};
use crate::commit::Commit;
use crate::fsmonitor::FsmonitorKind;
use crate::gitignore::GitIgnoreFile;
use crate::merged_tree::MergedTree;
use crate::op_store::{OperationId, WorkspaceId};
use crate::repo_path::{RepoPath, RepoPathBuf};
use crate::settings::HumanByteSize;
@ -105,8 +104,8 @@ pub trait LockedWorkingCopy {
/// Check out the specified commit in the working copy.
fn check_out(&mut self, commit: &Commit) -> Result<CheckoutStats, CheckoutError>;
/// Update to another tree without touching the files in the working copy.
fn reset(&mut self, new_tree: &MergedTree) -> Result<(), ResetError>;
/// Update to another commit without touching the files in the working copy.
fn reset(&mut self, commit: &Commit) -> Result<(), ResetError>;
/// See `WorkingCopy::sparse_patterns()`
fn sparse_patterns(&self) -> Result<&[RepoPathBuf], WorkingCopyStateError>;

View file

@ -414,10 +414,12 @@ fn test_reset() {
let gitignore_path = RepoPath::from_internal_string(".gitignore");
let tree_without_file = create_tree(repo, &[(gitignore_path, "ignored\n")]);
let commit_without_file = commit_with_tree(repo.store(), tree_without_file.id().clone());
let tree_with_file = create_tree(
repo,
&[(gitignore_path, "ignored\n"), (ignored_path, "code")],
);
let commit_with_file = commit_with_tree(repo.store(), tree_with_file.id().clone());
let ws = &mut test_workspace.workspace;
let commit = commit_with_tree(repo.store(), tree_with_file.id());
@ -432,7 +434,7 @@ fn test_reset() {
// 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_ws = ws.start_working_copy_mutation().unwrap();
locked_ws.locked_wc().reset(&tree_without_file).unwrap();
locked_ws.locked_wc().reset(&commit_without_file).unwrap();
locked_ws.finish(op_id.clone()).unwrap();
assert!(ignored_path.to_fs_path(&workspace_root).is_file());
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
@ -444,7 +446,7 @@ fn test_reset() {
// tracked. The file should become tracked (even though it's ignored).
let ws = &mut test_workspace.workspace;
let mut locked_ws = ws.start_working_copy_mutation().unwrap();
locked_ws.locked_wc().reset(&tree_with_file).unwrap();
locked_ws.locked_wc().reset(&commit_with_file).unwrap();
locked_ws.finish(op_id.clone()).unwrap();
assert!(ignored_path.to_fs_path(&workspace_root).is_file());
let wc: &LocalWorkingCopy = ws.working_copy().as_any().downcast_ref().unwrap();
@ -694,11 +696,12 @@ fn test_gitignores_in_ignored_dir() {
(nested_gitignore_path, "!file\n"),
],
);
let commit2 = commit_with_tree(test_workspace.repo.store(), tree2.id().clone());
let mut locked_ws = test_workspace
.workspace
.start_working_copy_mutation()
.unwrap();
locked_ws.locked_wc().reset(&tree2).unwrap();
locked_ws.locked_wc().reset(&commit2).unwrap();
locked_ws.finish(OperationId::from_hex("abc123")).unwrap();
let new_tree = test_workspace.snapshot().unwrap();