store: make write_file() async

This commit is contained in:
Martin von Zweigbergk 2024-09-03 20:13:46 -07:00 committed by Martin von Zweigbergk
parent ecb2847ef5
commit 4a8d250f2c
10 changed files with 50 additions and 35 deletions

View file

@ -344,8 +344,10 @@ fn fix_file_ids<'a>(
}
});
if new_content != old_content {
let new_file_id =
store.write_file(&tool_input.repo_path, &mut new_content.as_slice())?;
// TODO: send futures back over channel
let new_file_id = store
.write_file(&tool_input.repo_path, &mut new_content.as_slice())
.block_on()?;
updates_tx.send((tool_input, new_file_id)).unwrap();
}
}

View file

@ -453,6 +453,7 @@ pub fn apply_diff_builtin(
files.len(),
"result had a different number of files"
);
// TODO: Write files concurrently
for (path, file) in changed_files.into_iter().zip(files) {
let (selected, _unselected) = file.get_selected_contents();
match selected {
@ -496,7 +497,9 @@ pub fn apply_diff_builtin(
tree_builder.set_or_remove(path, value);
}
scm_record::SelectedContents::Present { contents } => {
let file_id = store.write_file(&path, &mut contents.as_bytes())?;
let file_id = store
.write_file(&path, &mut contents.as_bytes())
.block_on()?;
tree_builder.set_or_remove(
path,
Merge::normal(TreeValue::File {

View file

@ -231,7 +231,8 @@ pub fn run_mergetool_external(
} else {
let new_file_id = tree
.store()
.write_file(repo_path, &mut output_file_contents.as_slice())?;
.write_file(repo_path, &mut output_file_contents.as_slice())
.block_on()?;
Merge::normal(new_file_id)
};
let new_tree_value = match new_file_ids.into_resolved() {

View file

@ -25,6 +25,7 @@ use futures::Stream;
use futures::StreamExt;
use futures::TryStreamExt;
use itertools::Itertools;
use pollster::FutureExt;
use regex::bytes::Regex;
use regex::bytes::RegexBuilder;
@ -513,7 +514,7 @@ pub async fn update_from_content(
};
};
// Either there are no markers or they don't have the expected arity
let file_id = store.write_file(path, &mut &content[..])?;
let file_id = store.write_file(path, &mut &content[..]).await?;
return Ok(Merge::normal(file_id));
};
@ -535,17 +536,18 @@ pub async fn update_from_content(
if zip(contents.iter(), used_file_ids.iter())
.any(|(content, file_id)| file_id.is_none() && !content.is_empty())
{
let file_id = store.write_file(path, &mut &content[..])?;
let file_id = store.write_file(path, &mut &content[..]).await?;
return Ok(Merge::normal(file_id));
}
// Now write the new files contents we found by parsing the file with conflict
// markers.
// TODO: Write these concurrently
let new_file_ids: Vec<Option<FileId>> = zip(contents.iter(), used_file_ids.iter())
.map(|(content, file_id)| -> BackendResult<Option<FileId>> {
match file_id {
Some(_) => {
let file_id = store.write_file(path, &mut content.as_slice())?;
let file_id = store.write_file(path, &mut content.as_slice()).block_on()?;
Ok(Some(file_id))
}
None => {

View file

@ -1170,9 +1170,10 @@ fn has_diff_from_parent(
// TODO: handle copy tracking
let mut tree_diff = from_tree.diff_stream(&to_tree, matcher);
async {
// TODO: Resolve values concurrently
while let Some(entry) = tree_diff.next().await {
let (from_value, to_value) = entry.values?;
let from_value = resolve_file_values(store, &entry.path, from_value)?;
let from_value = resolve_file_values(store, &entry.path, from_value).await?;
if from_value == to_value {
continue;
}
@ -1197,9 +1198,10 @@ fn matches_diff_from_parent(
// TODO: handle copy tracking
let mut tree_diff = from_tree.diff_stream(&to_tree, files_matcher);
async {
// TODO: Resolve values concurrently
while let Some(entry) = tree_diff.next().await {
let (left_value, right_value) = entry.values?;
let left_value = resolve_file_values(store, &entry.path, left_value)?;
let left_value = resolve_file_values(store, &entry.path, left_value).await?;
if left_value == right_value {
continue;
}

View file

@ -606,7 +606,7 @@ impl TreeState {
return Err(TreeStateError::ReadTreeState {
path: tree_state_path,
source: err,
})
});
}
Ok(file) => file,
};
@ -709,7 +709,7 @@ impl TreeState {
self.store.get_root_tree(&self.tree_id)
}
fn write_file_to_store(
async fn write_file_to_store(
&self,
path: &RepoPath,
disk_path: &Path,
@ -718,7 +718,7 @@ impl TreeState {
message: format!("Failed to open file {}", disk_path.display()),
err: err.into(),
})?;
Ok(self.store.write_file(path, &mut file)?)
Ok(self.store.write_file(path, &mut file).await?)
}
fn write_symlink_to_store(
@ -1152,12 +1152,9 @@ impl TreeState {
new_file_state.file_type.clone()
};
let new_tree_values = match new_file_type {
FileType::Normal { executable } => self.write_path_to_store(
repo_path,
&disk_path,
&current_tree_values,
executable,
)?,
FileType::Normal { executable } => self
.write_path_to_store(repo_path, &disk_path, &current_tree_values, executable)
.block_on()?,
FileType::Symlink => {
let id = self.write_symlink_to_store(repo_path, &disk_path)?;
Merge::normal(TreeValue::Symlink(id))
@ -1172,7 +1169,7 @@ impl TreeState {
}
}
fn write_path_to_store(
async fn write_path_to_store(
&self,
repo_path: &RepoPath,
disk_path: &Path,
@ -1184,7 +1181,7 @@ impl TreeState {
if let Some(current_tree_value) = current_tree_values.as_resolved() {
#[cfg(unix)]
let _ = current_tree_value; // use the variable
let id = self.write_file_to_store(repo_path, disk_path)?;
let id = self.write_file_to_store(repo_path, disk_path).await?;
// On Windows, we preserve the executable bit from the current tree.
#[cfg(windows)]
let executable = {

View file

@ -451,9 +451,10 @@ fn merge_trees(merge: &Merge<Tree>) -> BackendResult<Merge<Tree>> {
// any conflicts.
let mut new_tree = backend::Tree::default();
let mut conflicts = vec![];
// TODO: Merge values concurrently
for (basename, path_merge) in all_merged_tree_entries(merge) {
let path = dir.join(basename);
let path_merge = merge_tree_values(store, &path, &path_merge)?;
let path_merge = merge_tree_values(store, &path, &path_merge).block_on()?;
match path_merge.into_resolved() {
Ok(value) => {
new_tree.set_or_remove(basename, value);
@ -487,10 +488,10 @@ fn merge_trees(merge: &Merge<Tree>) -> BackendResult<Merge<Tree>> {
/// Ok(Merge::normal(value)) if the conflict was resolved, and
/// Ok(Merge::absent()) if the path should be removed. Returns the
/// conflict unmodified if it cannot be resolved automatically.
fn merge_tree_values(
async fn merge_tree_values(
store: &Arc<Store>,
path: &RepoPath,
values: &MergedTreeVal,
values: &MergedTreeVal<'_>,
) -> BackendResult<MergedTreeValue> {
if let Some(resolved) = values.resolve_trivial() {
return Ok(Merge::resolved(resolved.cloned()));
@ -504,7 +505,7 @@ fn merge_tree_values(
Ok(merged_tree
.map(|tree| (tree.id() != empty_tree_id).then(|| TreeValue::Tree(tree.id().clone()))))
} else {
let maybe_resolved = try_resolve_file_values(store, path, values)?;
let maybe_resolved = try_resolve_file_values(store, path, values).await?;
Ok(maybe_resolved.unwrap_or_else(|| values.cloned()))
}
}
@ -512,7 +513,7 @@ fn merge_tree_values(
/// Tries to resolve file conflicts by merging the file contents. Treats missing
/// files as empty. If the file conflict cannot be resolved, returns the passed
/// `values` unmodified.
pub fn resolve_file_values(
pub async fn resolve_file_values(
store: &Arc<Store>,
path: &RepoPath,
values: MergedTreeValue,
@ -521,11 +522,11 @@ pub fn resolve_file_values(
return Ok(Merge::resolved(resolved.clone()));
}
let maybe_resolved = try_resolve_file_values(store, path, &values)?;
let maybe_resolved = try_resolve_file_values(store, path, &values).await?;
Ok(maybe_resolved.unwrap_or(values))
}
fn try_resolve_file_values<T: Borrow<TreeValue>>(
async fn try_resolve_file_values<T: Borrow<TreeValue>>(
store: &Arc<Store>,
path: &RepoPath,
values: &Merge<Option<T>>,
@ -537,7 +538,7 @@ fn try_resolve_file_values<T: Borrow<TreeValue>>(
.simplify();
// No fast path for simplified.is_resolved(). If it could be resolved, it would
// have been caught by values.resolve_trivial() above.
if let Some(resolved) = try_resolve_file_conflict(store, path, &simplified)? {
if let Some(resolved) = try_resolve_file_conflict(store, path, &simplified).await? {
Ok(Some(Merge::normal(resolved)))
} else {
// Failed to merge the files, or the paths are not files

View file

@ -242,12 +242,12 @@ impl Store {
self.backend.read_file(path, id).await
}
pub fn write_file(
pub async fn write_file(
&self,
path: &RepoPath,
contents: &mut (dyn Read + Send),
) -> BackendResult<FileId> {
self.backend.write_file(path, contents).block_on()
self.backend.write_file(path, contents).await
}
pub fn read_symlink(&self, path: &RepoPath, id: &SymlinkId) -> BackendResult<String> {

View file

@ -397,7 +397,8 @@ fn merge_tree_value(
Err(conflict) => {
let conflict_borrowed = conflict.map(|value| value.as_ref());
if let Some(tree_value) =
try_resolve_file_conflict(store, &filename, &conflict_borrowed)?
try_resolve_file_conflict(store, &filename, &conflict_borrowed)
.block_on()?
{
Some(tree_value)
} else {
@ -414,10 +415,10 @@ fn merge_tree_value(
///
/// The input `conflict` is supposed to be simplified. It shouldn't contain
/// non-file values that cancel each other.
pub fn try_resolve_file_conflict(
pub async fn try_resolve_file_conflict(
store: &Store,
filename: &RepoPath,
conflict: &MergedTreeVal,
conflict: &MergedTreeVal<'_>,
) -> BackendResult<Option<TreeValue>> {
// If there are any non-file or any missing parts in the conflict, we can't
// merge it. We check early so we don't waste time reading file contents if
@ -456,6 +457,7 @@ pub fn try_resolve_file_conflict(
// cannot
let file_id_conflict = file_id_conflict.simplify();
// TODO: Read the files concurrently
let contents: Merge<Vec<u8>> =
file_id_conflict.try_map(|&file_id| -> BackendResult<Vec<u8>> {
let mut content = vec![];
@ -472,7 +474,9 @@ pub fn try_resolve_file_conflict(
let merge_result = files::merge(&contents);
match merge_result {
MergeResult::Resolved(merged_content) => {
let id = store.write_file(filename, &mut merged_content.as_slice())?;
let id = store
.write_file(filename, &mut merged_content.as_slice())
.await?;
Ok(Some(TreeValue::File { id, executable }))
}
MergeResult::Conflict(_) => Ok(None),

View file

@ -298,7 +298,10 @@ pub fn read_file(store: &Store, path: &RepoPath, id: &FileId) -> Vec<u8> {
}
pub fn write_file(store: &Store, path: &RepoPath, contents: &str) -> FileId {
store.write_file(path, &mut contents.as_bytes()).unwrap()
store
.write_file(path, &mut contents.as_bytes())
.block_on()
.unwrap()
}
pub fn write_normal_file(