working_copy: propagate backend errors on checkout

This commit is contained in:
Martin von Zweigbergk 2022-05-23 21:58:24 -07:00 committed by Martin von Zweigbergk
parent 899325e94e
commit d451242746

View file

@ -557,7 +557,7 @@ impl TreeState {
path: &RepoPath, path: &RepoPath,
id: &FileId, id: &FileId,
executable: bool, executable: bool,
) -> FileState { ) -> Result<FileState, CheckoutError> {
create_parent_dirs(disk_path); create_parent_dirs(disk_path);
// TODO: Check that we're not overwriting an un-ignored file here (which might // TODO: Check that we're not overwriting an un-ignored file here (which might
// be created by a concurrent process). // be created by a concurrent process).
@ -567,7 +567,7 @@ impl TreeState {
.truncate(true) .truncate(true)
.open(disk_path) .open(disk_path)
.unwrap_or_else(|err| panic!("failed to open {:?} for write: {:?}", &disk_path, err)); .unwrap_or_else(|err| panic!("failed to open {:?} for write: {:?}", &disk_path, err));
let mut contents = self.store.read_file(path, id).unwrap(); let mut contents = self.store.read_file(path, id)?;
std::io::copy(&mut contents, &mut file).unwrap(); std::io::copy(&mut contents, &mut file).unwrap();
self.set_executable(disk_path, executable); self.set_executable(disk_path, executable);
// Read the file state while we still have the file open. That way, know that // Read the file state while we still have the file open. That way, know that
@ -579,11 +579,16 @@ impl TreeState {
// for Windows, since the executable bit is not reflected in the file system // for Windows, since the executable bit is not reflected in the file system
// there. // there.
file_state.mark_executable(executable); file_state.mark_executable(executable);
file_state Ok(file_state)
} }
#[cfg_attr(windows, allow(unused_variables))] #[cfg_attr(windows, allow(unused_variables))]
fn write_symlink(&self, disk_path: &Path, path: &RepoPath, id: &SymlinkId) -> FileState { fn write_symlink(
&self,
disk_path: &Path,
path: &RepoPath,
id: &SymlinkId,
) -> Result<FileState, CheckoutError> {
create_parent_dirs(disk_path); create_parent_dirs(disk_path);
#[cfg(windows)] #[cfg(windows)]
{ {
@ -591,16 +596,21 @@ impl TreeState {
} }
#[cfg(unix)] #[cfg(unix)]
{ {
let target = self.store.read_symlink(path, id).unwrap(); let target = self.store.read_symlink(path, id)?;
let target = PathBuf::from(&target); let target = PathBuf::from(&target);
symlink(target, disk_path).unwrap(); symlink(target, disk_path).unwrap();
} }
file_state(disk_path).unwrap() Ok(file_state(disk_path).unwrap())
} }
fn write_conflict(&self, disk_path: &Path, path: &RepoPath, id: &ConflictId) -> FileState { fn write_conflict(
&self,
disk_path: &Path,
path: &RepoPath,
id: &ConflictId,
) -> Result<FileState, CheckoutError> {
create_parent_dirs(disk_path); create_parent_dirs(disk_path);
let conflict = self.store.read_conflict(path, id).unwrap(); let conflict = self.store.read_conflict(path, id)?;
// TODO: Check that we're not overwriting an un-ignored file here (which might // TODO: Check that we're not overwriting an un-ignored file here (which might
// be created by a concurrent process). // be created by a concurrent process).
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
@ -614,7 +624,7 @@ impl TreeState {
// Windows like we do with the executable bit for regular files. // Windows like we do with the executable bit for regular files.
let mut result = file_state(disk_path).unwrap(); let mut result = file_state(disk_path).unwrap();
result.file_type = FileType::Conflict { id: id.clone() }; result.file_type = FileType::Conflict { id: id.clone() };
result Ok(result)
} }
#[cfg_attr(windows, allow(unused_variables))] #[cfg_attr(windows, allow(unused_variables))]
@ -705,10 +715,10 @@ impl TreeState {
Diff::Added(after) => { Diff::Added(after) => {
let file_state = match after { let file_state = match after {
TreeValue::Normal { id, executable } => { TreeValue::Normal { id, executable } => {
self.write_file(&disk_path, &path, &id, executable) self.write_file(&disk_path, &path, &id, executable)?
} }
TreeValue::Symlink(id) => self.write_symlink(&disk_path, &path, &id), TreeValue::Symlink(id) => self.write_symlink(&disk_path, &path, &id)?,
TreeValue::Conflict(id) => self.write_conflict(&disk_path, &path, &id), TreeValue::Conflict(id) => self.write_conflict(&disk_path, &path, &id)?,
TreeValue::GitSubmodule(_id) => { TreeValue::GitSubmodule(_id) => {
println!("ignoring git submodule at {:?}", path); println!("ignoring git submodule at {:?}", path);
continue; continue;
@ -738,10 +748,14 @@ impl TreeState {
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, executable }) => { (_, TreeValue::Normal { id, executable }) => {
self.write_file(&disk_path, &path, &id, executable) self.write_file(&disk_path, &path, &id, executable)?
}
(_, TreeValue::Symlink(id)) => {
self.write_symlink(&disk_path, &path, &id)?
}
(_, TreeValue::Conflict(id)) => {
self.write_conflict(&disk_path, &path, &id)?
} }
(_, TreeValue::Symlink(id)) => self.write_symlink(&disk_path, &path, &id),
(_, TreeValue::Conflict(id)) => self.write_conflict(&disk_path, &path, &id),
(_, TreeValue::GitSubmodule(_id)) => { (_, TreeValue::GitSubmodule(_id)) => {
println!("ignoring git submodule at {:?}", path); println!("ignoring git submodule at {:?}", path);
self.file_states.remove(&path); self.file_states.remove(&path);