merged_tree: propagate errors from conflict iterator
Some checks are pending
binaries / Build binary artifacts (push) Waiting to run
nix / flake check (push) Waiting to run
build / build (, macos-13) (push) Waiting to run
build / build (, macos-14) (push) Waiting to run
build / build (, ubuntu-latest) (push) Waiting to run
build / build (, windows-latest) (push) Waiting to run
build / build (--all-features, ubuntu-latest) (push) Waiting to run
build / Build jj-lib without Git support (push) Waiting to run
build / Check protos (push) Waiting to run
build / Check formatting (push) Waiting to run
build / Check that MkDocs can build the docs (push) Waiting to run
build / Check that MkDocs can build the docs with latest Python and uv (push) Waiting to run
build / cargo-deny (advisories) (push) Waiting to run
build / cargo-deny (bans licenses sources) (push) Waiting to run
build / Clippy check (push) Waiting to run
Codespell / Codespell (push) Waiting to run
website / prerelease-docs-build-deploy (ubuntu-latest) (push) Waiting to run
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run

This commit is contained in:
Martin von Zweigbergk 2024-11-22 22:40:32 -08:00 committed by Martin von Zweigbergk
parent 9ec216a2bb
commit 10c90a5099
3 changed files with 33 additions and 16 deletions

View file

@ -51,6 +51,7 @@ use clap_complete::ArgValueCandidates;
use indexmap::IndexMap; use indexmap::IndexMap;
use indexmap::IndexSet; use indexmap::IndexSet;
use itertools::Itertools; use itertools::Itertools;
use jj_lib::backend::BackendResult;
use jj_lib::backend::ChangeId; use jj_lib::backend::ChangeId;
use jj_lib::backend::CommitId; use jj_lib::backend::CommitId;
use jj_lib::backend::MergedTreeId; use jj_lib::backend::MergedTreeId;
@ -2467,7 +2468,7 @@ fn update_stale_working_copy(
#[instrument(skip_all)] #[instrument(skip_all)]
pub fn print_conflicted_paths( pub fn print_conflicted_paths(
conflicts: Vec<(RepoPathBuf, MergedTreeValue)>, conflicts: Vec<(RepoPathBuf, BackendResult<MergedTreeValue>)>,
formatter: &mut dyn Formatter, formatter: &mut dyn Formatter,
workspace_command: &WorkspaceCommandHelper, workspace_command: &WorkspaceCommandHelper,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
@ -2481,7 +2482,9 @@ pub fn print_conflicted_paths(
.map(|p| format!("{:width$}", p, width = max_path_len.min(32) + 3)); .map(|p| format!("{:width$}", p, width = max_path_len.min(32) + 3));
for ((_, conflict), formatted_path) in std::iter::zip(conflicts, formatted_paths) { for ((_, conflict), formatted_path) in std::iter::zip(conflicts, formatted_paths) {
let conflict = conflict.simplify(); // TODO: Display the error for the path instead of failing the whole command if
// `conflict` is an error?
let conflict = conflict?.simplify();
let sides = conflict.num_sides(); let sides = conflict.num_sides();
let n_adds = conflict.adds().flatten().count(); let n_adds = conflict.adds().flatten().count();
let deletions = sides - n_adds; let deletions = sides - n_adds;

View file

@ -182,7 +182,7 @@ impl MergedTree {
/// all sides are trees, so tree/file conflicts will be reported as a single /// all sides are trees, so tree/file conflicts will be reported as a single
/// conflict, not one for each path in the tree. /// conflict, not one for each path in the tree.
// TODO: Restrict this by a matcher (or add a separate method for that). // TODO: Restrict this by a matcher (or add a separate method for that).
pub fn conflicts(&self) -> impl Iterator<Item = (RepoPathBuf, MergedTreeValue)> { pub fn conflicts(&self) -> impl Iterator<Item = (RepoPathBuf, BackendResult<MergedTreeValue>)> {
ConflictIterator::new(self) ConflictIterator::new(self)
} }
@ -651,20 +651,25 @@ impl ConflictIterator {
} }
impl Iterator for ConflictIterator { impl Iterator for ConflictIterator {
type Item = (RepoPathBuf, MergedTreeValue); type Item = (RepoPathBuf, BackendResult<MergedTreeValue>);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
while let Some(top) = self.stack.last_mut() { while let Some(top) = self.stack.last_mut() {
if let Some((path, tree_values)) = top.entries.pop() { if let Some((path, tree_values)) = top.entries.pop() {
// TODO: propagate errors match tree_values.to_tree_merge(&self.store, &path) {
if let Some(trees) = tree_values.to_tree_merge(&self.store, &path).unwrap() { Ok(Some(trees)) => {
// If all sides are trees or missing, descend into the merged tree // If all sides are trees or missing, descend into the merged tree
self.stack.push(ConflictsDirItem::from(&trees)); self.stack.push(ConflictsDirItem::from(&trees));
} else { }
// Otherwise this is a conflict between files, trees, etc. If they could Ok(None) => {
// be automatically resolved, they should have been when the top-level // Otherwise this is a conflict between files, trees, etc. If they could
// tree conflict was written, so we assume that they can't be. // be automatically resolved, they should have been when the top-level
return Some((path, tree_values)); // tree conflict was written, so we assume that they can't be.
return Some((path, Ok(tree_values)));
}
Err(err) => {
return Some((path, Err(err)));
}
} }
} else { } else {
self.stack.pop(); self.stack.pop();

View file

@ -638,7 +638,10 @@ fn test_conflict_iterator() {
vec![base1.clone()], vec![base1.clone()],
vec![side1.clone(), side2.clone()], vec![side1.clone(), side2.clone()],
)); ));
let conflicts = tree.conflicts().collect_vec(); let conflicts = tree
.conflicts()
.map(|(path, conflict)| (path, conflict.unwrap()))
.collect_vec();
let conflict_at = |path: &RepoPath| { let conflict_at = |path: &RepoPath| {
Merge::from_removes_adds( Merge::from_removes_adds(
vec![base1.path_value(path).unwrap()], vec![base1.path_value(path).unwrap()],
@ -672,7 +675,10 @@ fn test_conflict_iterator() {
// After we resolve conflicts, there are only non-trivial conflicts left // After we resolve conflicts, there are only non-trivial conflicts left
let tree = tree.resolve().unwrap(); let tree = tree.resolve().unwrap();
let conflicts = tree.conflicts().collect_vec(); let conflicts = tree
.conflicts()
.map(|(path, conflict)| (path, conflict.unwrap()))
.collect_vec();
assert_eq!( assert_eq!(
conflicts, conflicts,
vec![ vec![
@ -725,7 +731,10 @@ fn test_conflict_iterator_higher_arity() {
vec![base1.clone(), base2.clone()], vec![base1.clone(), base2.clone()],
vec![side1.clone(), side2.clone(), side3.clone()], vec![side1.clone(), side2.clone(), side3.clone()],
)); ));
let conflicts = tree.conflicts().collect_vec(); let conflicts = tree
.conflicts()
.map(|(path, conflict)| (path, conflict.unwrap()))
.collect_vec();
let conflict_at = |path: &RepoPath| { let conflict_at = |path: &RepoPath| {
Merge::from_removes_adds( Merge::from_removes_adds(
vec![ vec![