mirror of
https://github.com/martinvonz/jj.git
synced 2024-11-13 14:15:02 +03:00
working_copy: allow updating sparse patterns (#52)
With this patch, we add support for setting the sparse patterns, and we respect it when updating the working copy.
This commit is contained in:
parent
ceb6c152a1
commit
0d881de56c
@ -37,7 +37,7 @@ use crate::backend::{
|
||||
use crate::conflicts::{materialize_conflict, update_conflict_from_content};
|
||||
use crate::gitignore::GitIgnoreFile;
|
||||
use crate::lock::FileLock;
|
||||
use crate::matchers::{EverythingMatcher, Matcher};
|
||||
use crate::matchers::{DifferenceMatcher, Matcher, PrefixMatcher};
|
||||
use crate::op_store::{OperationId, WorkspaceId};
|
||||
use crate::repo_path::{RepoPath, RepoPathComponent, RepoPathJoin};
|
||||
use crate::store::Store;
|
||||
@ -226,6 +226,10 @@ impl TreeState {
|
||||
&self.sparse_patterns
|
||||
}
|
||||
|
||||
fn sparse_matcher(&self) -> Box<dyn Matcher> {
|
||||
Box::new(PrefixMatcher::new(&self.sparse_patterns))
|
||||
}
|
||||
|
||||
pub fn init(store: Arc<Store>, working_copy_path: PathBuf, state_path: PathBuf) -> TreeState {
|
||||
let mut wc = TreeState::empty(store, working_copy_path, state_path);
|
||||
wc.save();
|
||||
@ -579,13 +583,39 @@ impl TreeState {
|
||||
BackendError::NotFound => CheckoutError::SourceNotFound,
|
||||
other => CheckoutError::InternalBackendError(other),
|
||||
})?;
|
||||
let stats = self.update(&old_tree, new_tree, &EverythingMatcher)?;
|
||||
let stats = self.update(&old_tree, new_tree, self.sparse_matcher().as_ref())?;
|
||||
self.tree_id = new_tree.id().clone();
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
pub fn set_sparse_patterns(&mut self, sparse_patterns: Vec<RepoPath>) {
|
||||
pub fn set_sparse_patterns(
|
||||
&mut self,
|
||||
sparse_patterns: Vec<RepoPath>,
|
||||
) -> Result<CheckoutStats, CheckoutError> {
|
||||
let tree = self
|
||||
.store
|
||||
.get_tree(&RepoPath::root(), &self.tree_id)
|
||||
.map_err(|err| match err {
|
||||
BackendError::NotFound => CheckoutError::SourceNotFound,
|
||||
other => CheckoutError::InternalBackendError(other),
|
||||
})?;
|
||||
let old_matcher = PrefixMatcher::new(&self.sparse_patterns);
|
||||
let new_matcher = PrefixMatcher::new(&sparse_patterns);
|
||||
let added_matcher = DifferenceMatcher::new(&new_matcher, &old_matcher);
|
||||
let removed_matcher = DifferenceMatcher::new(&old_matcher, &new_matcher);
|
||||
let empty_tree = Tree::null(self.store.clone(), RepoPath::root());
|
||||
let added_stats = self.update(&empty_tree, &tree, &added_matcher)?;
|
||||
let removed_stats = self.update(&tree, &empty_tree, &removed_matcher)?;
|
||||
self.sparse_patterns = sparse_patterns;
|
||||
assert_eq!(added_stats.updated_files, 0);
|
||||
assert_eq!(added_stats.removed_files, 0);
|
||||
assert_eq!(removed_stats.updated_files, 0);
|
||||
assert_eq!(removed_stats.added_files, 0);
|
||||
Ok(CheckoutStats {
|
||||
updated_files: 0,
|
||||
added_files: added_stats.added_files,
|
||||
removed_files: removed_stats.removed_files,
|
||||
})
|
||||
}
|
||||
|
||||
fn update(
|
||||
@ -684,7 +714,7 @@ impl TreeState {
|
||||
other => ResetError::InternalBackendError(other),
|
||||
})?;
|
||||
|
||||
for (path, diff) in old_tree.diff(new_tree, &EverythingMatcher) {
|
||||
for (path, diff) in old_tree.diff(new_tree, self.sparse_matcher().as_ref()) {
|
||||
match diff {
|
||||
Diff::Removed(_before) => {
|
||||
self.file_states.remove(&path);
|
||||
@ -766,6 +796,10 @@ impl WorkingCopy {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn working_copy_path(&self) -> &Path {
|
||||
&self.working_copy_path
|
||||
}
|
||||
|
||||
pub fn state_path(&self) -> &Path {
|
||||
&self.state_path
|
||||
}
|
||||
@ -925,8 +959,8 @@ impl LockedWorkingCopy<'_> {
|
||||
}
|
||||
|
||||
pub fn check_out(&mut self, new_tree: &Tree) -> Result<CheckoutStats, CheckoutError> {
|
||||
// TODO: Write a "pending_checkout" file with the old and new TreeIds so we can
|
||||
// continue an interrupted checkout if we find such a file.
|
||||
// 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().as_mut().unwrap().check_out(new_tree)?;
|
||||
Ok(stats)
|
||||
}
|
||||
@ -939,7 +973,12 @@ impl LockedWorkingCopy<'_> {
|
||||
self.wc.sparse_patterns()
|
||||
}
|
||||
|
||||
pub fn set_sparse_patterns(&mut self, new_sparse_patterns: Vec<RepoPath>) {
|
||||
pub fn set_sparse_patterns(
|
||||
&mut self,
|
||||
new_sparse_patterns: Vec<RepoPath>,
|
||||
) -> Result<CheckoutStats, CheckoutError> {
|
||||
// TODO: Write a "pending_checkout" file with new sparse patterns so we can
|
||||
// continue an interrupted update if we find such a file.
|
||||
self.wc
|
||||
.tree_state()
|
||||
.as_mut()
|
||||
|
124
lib/tests/test_working_copy_sparse.rs
Normal file
124
lib/tests/test_working_copy_sparse.rs
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use itertools::Itertools;
|
||||
use jujutsu_lib::repo_path::RepoPath;
|
||||
use jujutsu_lib::testutils;
|
||||
use jujutsu_lib::working_copy::{CheckoutStats, WorkingCopy};
|
||||
|
||||
#[test]
|
||||
fn test_sparse_checkout() {
|
||||
let settings = testutils::user_settings();
|
||||
let mut test_workspace = testutils::init_workspace(&settings, false);
|
||||
let repo = &test_workspace.repo;
|
||||
let working_copy_path = test_workspace.workspace.workspace_root().clone();
|
||||
|
||||
let root_file1_path = RepoPath::from_internal_string("file1");
|
||||
let root_file2_path = RepoPath::from_internal_string("file2");
|
||||
let dir1_path = RepoPath::from_internal_string("dir1");
|
||||
let dir1_file1_path = RepoPath::from_internal_string("dir1/file1");
|
||||
let dir1_file2_path = RepoPath::from_internal_string("dir1/file2");
|
||||
let dir1_subdir1_path = RepoPath::from_internal_string("dir1/subdir1");
|
||||
let dir1_subdir1_file1_path = RepoPath::from_internal_string("dir1/subdir1/file1");
|
||||
let dir2_path = RepoPath::from_internal_string("dir2");
|
||||
let dir2_file1_path = RepoPath::from_internal_string("dir2/file1");
|
||||
|
||||
let tree = testutils::create_tree(
|
||||
repo,
|
||||
&[
|
||||
(&root_file1_path, "contents"),
|
||||
(&root_file2_path, "contents"),
|
||||
(&dir1_file1_path, "contents"),
|
||||
(&dir1_file2_path, "contents"),
|
||||
(&dir1_subdir1_file1_path, "contents"),
|
||||
(&dir2_file1_path, "contents"),
|
||||
],
|
||||
);
|
||||
|
||||
let wc = test_workspace.workspace.working_copy_mut();
|
||||
wc.check_out(repo.op_id().clone(), None, &tree).unwrap();
|
||||
|
||||
// Set sparse patterns to only dir1/
|
||||
let mut locked_wc = wc.start_mutation();
|
||||
let sparse_patterns = vec![dir1_path];
|
||||
let stats = locked_wc
|
||||
.set_sparse_patterns(sparse_patterns.clone())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
stats,
|
||||
CheckoutStats {
|
||||
updated_files: 0,
|
||||
added_files: 0,
|
||||
removed_files: 3
|
||||
}
|
||||
);
|
||||
assert_eq!(locked_wc.sparse_patterns(), 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());
|
||||
assert!(dir1_file2_path.to_fs_path(&working_copy_path).exists());
|
||||
assert!(dir1_subdir1_file1_path
|
||||
.to_fs_path(&working_copy_path)
|
||||
.exists());
|
||||
assert!(!dir2_file1_path.to_fs_path(&working_copy_path).exists());
|
||||
|
||||
// Write the new state to disk
|
||||
locked_wc.finish(repo.op_id().clone());
|
||||
assert_eq!(
|
||||
wc.file_states().keys().collect_vec(),
|
||||
vec![&dir1_file1_path, &dir1_file2_path, &dir1_subdir1_file1_path]
|
||||
);
|
||||
assert_eq!(wc.sparse_patterns(), sparse_patterns);
|
||||
|
||||
// Reload the state to check that it was persisted
|
||||
let mut wc = WorkingCopy::load(
|
||||
repo.store().clone(),
|
||||
wc.working_copy_path().to_path_buf(),
|
||||
wc.state_path().to_path_buf(),
|
||||
);
|
||||
assert_eq!(
|
||||
wc.file_states().keys().collect_vec(),
|
||||
vec![&dir1_file1_path, &dir1_file2_path, &dir1_subdir1_file1_path]
|
||||
);
|
||||
assert_eq!(wc.sparse_patterns(), sparse_patterns);
|
||||
|
||||
// Set sparse patterns to file2, dir1/subdir1/ and dir2/
|
||||
let mut locked_wc = wc.start_mutation();
|
||||
let sparse_patterns = vec![root_file1_path.clone(), dir1_subdir1_path, dir2_path];
|
||||
let stats = locked_wc
|
||||
.set_sparse_patterns(sparse_patterns.clone())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
stats,
|
||||
CheckoutStats {
|
||||
updated_files: 0,
|
||||
added_files: 2,
|
||||
removed_files: 2
|
||||
}
|
||||
);
|
||||
assert_eq!(locked_wc.sparse_patterns(), 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());
|
||||
assert!(!dir1_file2_path.to_fs_path(&working_copy_path).exists());
|
||||
assert!(dir1_subdir1_file1_path
|
||||
.to_fs_path(&working_copy_path)
|
||||
.exists());
|
||||
assert!(dir2_file1_path.to_fs_path(&working_copy_path).exists());
|
||||
locked_wc.finish(repo.op_id().clone());
|
||||
assert_eq!(
|
||||
wc.file_states().keys().collect_vec(),
|
||||
vec![&dir1_subdir1_file1_path, &dir2_file1_path, &root_file1_path]
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user