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

working_copy: do not overwrite ignored file

Since the file should have been removed on Diff::Modified case, we can always
expect that write_file/conflict() creates new file.
This commit is contained in:
Yuya Nishihara 2022-08-01 14:56:08 +09:00 committed by Martin von Zweigbergk
parent b149cb07cc
commit cc860f771c
3 changed files with 10 additions and 32 deletions

View file

@ -176,6 +176,7 @@ fn sparse_patterns_from_proto(proto: &crate::protos::working_copy::TreeState) ->
}
fn create_parent_dirs(disk_path: &Path) -> Result<(), CheckoutError> {
// TODO: Check that we don't follow symlinks while creating parent dirs.
fs::create_dir_all(disk_path.parent().unwrap()).map_err(|err| CheckoutError::IoError {
message: format!(
"Failed to create parent directories for {}",
@ -643,12 +644,9 @@ impl TreeState {
executable: bool,
) -> Result<FileState, CheckoutError> {
create_parent_dirs(disk_path)?;
// TODO: Check that we're not overwriting an un-ignored file here (which might
// be created by a concurrent process).
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.create_new(true) // Don't overwrite un-ignored file. Don't follow symlink.
.open(disk_path)
.map_err(|err| CheckoutError::IoError {
message: format!("Failed to open file {} for writing", disk_path.display()),
@ -710,12 +708,9 @@ impl TreeState {
) -> Result<FileState, CheckoutError> {
create_parent_dirs(disk_path)?;
let conflict = self.store.read_conflict(path, id)?;
// TODO: Check that we're not overwriting an un-ignored file here (which might
// be created by a concurrent process).
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.create_new(true) // Don't overwrite un-ignored file. Don't follow symlink.
.open(disk_path)
.map_err(|err| CheckoutError::IoError {
message: format!("Failed to open file {} for writing", disk_path.display()),

View file

@ -554,9 +554,9 @@ fn test_gitignores(use_git: bool) {
#[test_case(false ; "local backend")]
#[test_case(true ; "git backend")]
fn test_gitignores_checkout_overwrites_ignored(use_git: bool) {
// Tests that a .gitignore'd file gets overwritten if check out a commit where
// the file is tracked.
fn test_gitignores_checkout_never_overwrites_ignored(use_git: bool) {
// Tests that a .gitignore'd file doesn't get overwritten if check out a commit
// where the file is tracked.
let _home_dir = testutils::new_user_home();
let settings = testutils::user_settings();
@ -579,27 +579,15 @@ fn test_gitignores_checkout_overwrites_ignored(use_git: bool) {
let tree = repo.store().get_tree(&RepoPath::root(), &tree_id).unwrap();
// Now check out the tree that adds the file "modified" with contents
// "contents". The exiting contents ("garbage") should be replaced in the
// "contents". The exiting contents ("garbage") shouldn't be replaced in the
// working copy.
let wc = test_workspace.workspace.working_copy_mut();
wc.check_out(repo.op_id().clone(), None, &tree).unwrap();
assert!(wc.check_out(repo.op_id().clone(), None, &tree).is_err());
// Check that the new contents are in the working copy
// Check that the old contents are in the working copy
let path = workspace_root.join("modified");
assert!(path.is_file());
assert_eq!(std::fs::read(&path).unwrap(), b"contents");
// Check that the file is in the tree created by snapshotting the working copy
let mut locked_wc = wc.start_mutation();
let new_tree_id = locked_wc.snapshot(GitIgnoreFile::empty()).unwrap();
locked_wc.discard();
let new_tree = repo
.store()
.get_tree(&RepoPath::root(), &new_tree_id)
.unwrap();
assert!(new_tree
.entry(&RepoPathComponent::from("modified"))
.is_some());
assert_eq!(std::fs::read(&path).unwrap(), b"garbage");
}
#[test_case(false ; "local backend")]

View file

@ -183,11 +183,6 @@ fn test_sparse_commit() {
let sparse_patterns = vec![dir1_path, dir2_path];
locked_wc.set_sparse_patterns(sparse_patterns).unwrap();
locked_wc.finish(repo.op_id().clone());
// Write out a modified version of dir2/file1 again because it was overwritten
// when we added dir2/ to the sparse patterns.
// TODO: We shouldn't overwrite files when updating (there's already a TODO
// about that in `TreeState::write_file()`).
std::fs::write(dir2_file1_path.to_fs_path(&working_copy_path), "modified").unwrap();
// Create a tree from the working copy. Only dir1/file1 and dir2/file1 should be
// updated in the tree.