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

working_copy: fix crash when updating and only executable bit changed

This commit is contained in:
Martin von Zweigbergk 2022-04-14 23:03:08 -07:00 committed by Martin von Zweigbergk
parent bd035004b9
commit 53911b076b
3 changed files with 29 additions and 15 deletions

View file

@ -44,6 +44,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Errors are now printed to stderr (they used to be printed to stdout). * Errors are now printed to stderr (they used to be printed to stdout).
* Updating the working copy to a commit where a file's executable bit changed
but the contents was the same used to lead to a crash. That has now been
fixed.
* `jj untrack` now requires at least one path (allowing no arguments was a UX * `jj untrack` now requires at least one path (allowing no arguments was a UX
bug). bug).

View file

@ -592,23 +592,23 @@ impl TreeState {
self.file_states.insert(path.clone(), file_state); self.file_states.insert(path.clone(), file_state);
stats.added_files += 1; stats.added_files += 1;
} }
Diff::Modified(
TreeValue::Normal {
id: old_id,
executable: old_executable,
},
TreeValue::Normal { id, executable },
) if id == old_id => {
// Optimization for when only the executable bit changed
assert_ne!(executable, old_executable);
self.set_executable(&disk_path, executable);
let file_state = self.file_states.get_mut(&path).unwrap();
file_state.mark_executable(executable);
stats.updated_files += 1;
}
Diff::Modified(before, after) => { Diff::Modified(before, after) => {
fs::remove_file(&disk_path).ok(); fs::remove_file(&disk_path).ok();
let file_state = match (before, after) { let file_state = match (before, after) {
(
TreeValue::Normal {
id: old_id,
executable: old_executable,
},
TreeValue::Normal { id, executable },
) if id == old_id => {
// Optimization for when only the executable bit changed
assert_ne!(executable, old_executable);
self.set_executable(&disk_path, executable);
let mut file_state = self.file_states.get(&path).unwrap().clone();
file_state.mark_executable(executable);
file_state
}
(_, TreeValue::Normal { id, executable }) => { (_, TreeValue::Normal { id, executable }) => {
self.write_file(&disk_path, &path, &id, executable) self.write_file(&disk_path, &path, &id, executable)
} }

View file

@ -66,6 +66,8 @@ fn test_checkout_file_transitions(use_git: bool) {
Missing, Missing,
Normal, Normal,
Executable, Executable,
// Executable, but same content as Normal, to test transition where only the bit changed
ExecutableNormalContent,
Conflict, Conflict,
#[cfg_attr(windows, allow(dead_code))] #[cfg_attr(windows, allow(dead_code))]
Symlink, Symlink,
@ -99,6 +101,13 @@ fn test_checkout_file_transitions(use_git: bool) {
executable: true, executable: true,
} }
} }
Kind::ExecutableNormalContent => {
let id = testutils::write_file(store, path, "normal file contents");
TreeValue::Normal {
id,
executable: true,
}
}
Kind::Conflict => { Kind::Conflict => {
let base_file_id = testutils::write_file(store, path, "base file contents"); let base_file_id = testutils::write_file(store, path, "base file contents");
let left_file_id = testutils::write_file(store, path, "left file contents"); let left_file_id = testutils::write_file(store, path, "left file contents");
@ -162,6 +171,7 @@ fn test_checkout_file_transitions(use_git: bool) {
Kind::Missing, Kind::Missing,
Kind::Normal, Kind::Normal,
Kind::Executable, Kind::Executable,
Kind::ExecutableNormalContent,
Kind::Conflict, Kind::Conflict,
Kind::Tree, Kind::Tree,
]; ];
@ -217,7 +227,7 @@ fn test_checkout_file_transitions(use_git: bool) {
path path
); );
} }
Kind::Executable => { Kind::Executable | Kind::ExecutableNormalContent => {
assert!(maybe_metadata.is_ok(), "{:?} should exist", path); assert!(maybe_metadata.is_ok(), "{:?} should exist", path);
let metadata = maybe_metadata.unwrap(); let metadata = maybe_metadata.unwrap();
assert!(metadata.is_file(), "{:?} should be a file", path); assert!(metadata.is_file(), "{:?} should be a file", path);