just playing with tangled
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cli: use `MergedTree` for finding conflicts

`MergedTree` is now ready to be used when checking if a commit has
conflicts, and when listing conflicts. We don't yet a way for the user
to say they want to use tree-level conflicts even for these
cases. However, since the backend can decide, we should be able to
have our backend return tree-level conflicts. All writes will still
use path-level conflicts, so the experimentation we can do at Google
is limited.

Beacause `MergedTree` doesn't yet have a way of walking conflicts
while restricting it by a matcher, this will make `jj resolve` a
little slower. I suspect no one will notice.

authored by

Martin von Zweigbergk and committed by
Martin von Zweigbergk
1c3fe9a6 006c7646

+42 -21
+17 -1
lib/src/commit.rs
··· 20 20 use std::sync::Arc; 21 21 22 22 use crate::backend; 23 - use crate::backend::{ChangeId, CommitId, Signature, TreeId}; 23 + use crate::backend::{BackendError, ChangeId, CommitId, Signature, TreeId}; 24 + use crate::merged_tree::MergedTree; 24 25 use crate::repo_path::RepoPath; 25 26 use crate::store::Store; 26 27 use crate::tree::Tree; ··· 101 102 .collect() 102 103 } 103 104 105 + // TODO(#1624): Delete when all callers use `merged_tree()` 104 106 pub fn tree(&self) -> Tree { 105 107 self.store 106 108 .get_tree(&RepoPath::root(), self.data.root_tree.as_legacy_tree_id()) 107 109 .unwrap() 110 + } 111 + 112 + pub fn merged_tree(&self) -> Result<MergedTree, BackendError> { 113 + if self.data.uses_tree_conflict_format { 114 + let tree_conflict = self 115 + .data 116 + .root_tree 117 + .try_map(|tree_id| self.store.get_tree(&RepoPath::root(), tree_id))?; 118 + Ok(MergedTree::new(tree_conflict)) 119 + } else { 120 + let tree_id = self.data.root_tree.as_legacy_tree_id(); 121 + let tree = self.store.get_tree(&RepoPath::root(), tree_id)?; 122 + Ok(MergedTree::legacy(tree)) 123 + } 108 124 } 109 125 110 126 pub fn tree_id(&self) -> &TreeId {
+1 -1
lib/src/default_revset_engine.rs
··· 870 870 } 871 871 RevsetFilterPredicate::HasConflict => pure_predicate_fn(move |entry| { 872 872 let commit = store.get_commit(&entry.commit_id()).unwrap(); 873 - commit.tree().has_conflict() 873 + commit.merged_tree().unwrap().has_conflict() 874 874 }), 875 875 } 876 876 }
+8
lib/src/merged_tree.rs
··· 194 194 pub fn conflicts(&self) -> impl Iterator<Item = (RepoPath, Conflict<Option<TreeValue>>)> { 195 195 ConflictIterator::new(self.clone()) 196 196 } 197 + 198 + /// Whether this tree has conflicts. 199 + pub fn has_conflict(&self) -> bool { 200 + match self { 201 + MergedTree::Legacy(tree) => tree.has_conflict(), 202 + MergedTree::Merge(conflict) => !conflict.is_resolved(), 203 + } 204 + } 197 205 } 198 206 199 207 fn all_tree_conflict_names(conflict: &Conflict<Tree>) -> impl Iterator<Item = &RepoPathComponent> {
+1 -1
src/commands/git.rs
··· 845 845 { 846 846 reasons.push("it has no author and/or committer set"); 847 847 } 848 - if commit.tree().has_conflict() { 848 + if commit.merged_tree()?.has_conflict() { 849 849 reasons.push("it has conflicts"); 850 850 } 851 851 if !reasons.is_empty() {
+12 -15
src/commands/mod.rs
··· 1524 1524 )?; 1525 1525 } 1526 1526 1527 - let conflicts = tree.conflicts(); 1527 + let conflicts = wc_commit.merged_tree()?.conflicts().collect_vec(); 1528 1528 if !conflicts.is_empty() { 1529 1529 writeln!( 1530 1530 formatter.labeled("conflict"), 1531 1531 "There are unresolved conflicts at these paths:" 1532 1532 )?; 1533 - print_conflicted_paths(&conflicts, &tree, formatter, &workspace_command)? 1533 + print_conflicted_paths(&conflicts, formatter, &workspace_command)? 1534 1534 } 1535 1535 } 1536 1536 ··· 2667 2667 let mut workspace_command = command.workspace_helper(ui)?; 2668 2668 let matcher = workspace_command.matcher_from_values(&args.paths)?; 2669 2669 let commit = workspace_command.resolve_single_rev(&args.revision)?; 2670 - let tree = commit.tree(); 2671 - let conflicts = tree.conflicts_matching(matcher.as_ref()); 2670 + let tree = commit.merged_tree()?; 2671 + let conflicts = tree 2672 + .conflicts() 2673 + .filter(|path| matcher.matches(&path.0)) 2674 + .collect_vec(); 2672 2675 if conflicts.is_empty() { 2673 2676 return Err(CommandError::CliError(format!( 2674 2677 "No conflicts found {}", ··· 2682 2685 if args.list { 2683 2686 return print_conflicted_paths( 2684 2687 &conflicts, 2685 - &tree, 2686 2688 ui.stdout_formatter().as_mut(), 2687 2689 &workspace_command, 2688 2690 ); ··· 2703 2705 tx.finish(ui)?; 2704 2706 2705 2707 if !args.quiet { 2706 - let new_tree = new_commit.tree(); 2707 - let new_conflicts = new_tree.conflicts_matching(&EverythingMatcher); 2708 + let new_tree = new_commit.merged_tree()?; 2709 + let new_conflicts = new_tree.conflicts().collect_vec(); 2708 2710 if !new_conflicts.is_empty() { 2709 2711 ui.write("After this operation, some files at this revision still have conflicts:\n")?; 2710 2712 print_conflicted_paths( 2711 2713 &new_conflicts, 2712 - &tree, 2713 2714 ui.stdout_formatter().as_mut(), 2714 2715 &workspace_command, 2715 2716 )?; ··· 2719 2720 } 2720 2721 2721 2722 fn print_conflicted_paths( 2722 - conflicts: &[(RepoPath, jj_lib::backend::ConflictId)], 2723 - tree: &Tree, 2723 + conflicts: &[(RepoPath, Conflict<Option<TreeValue>>)], 2724 2724 formatter: &mut dyn Formatter, 2725 2725 workspace_command: &WorkspaceCommandHelper, 2726 2726 ) -> Result<(), CommandError> { 2727 2727 let formatted_paths = conflicts 2728 2728 .iter() 2729 - .map(|(path, _id)| workspace_command.format_file_path(path)) 2729 + .map(|(path, _conflict)| workspace_command.format_file_path(path)) 2730 2730 .collect_vec(); 2731 2731 let max_path_len = formatted_paths.iter().map(|p| p.len()).max().unwrap_or(0); 2732 2732 let formatted_paths = formatted_paths 2733 2733 .into_iter() 2734 2734 .map(|p| format!("{:width$}", p, width = max_path_len.min(32) + 3)); 2735 2735 2736 - for ((repo_path, conflict_id), formatted_path) in 2737 - std::iter::zip(conflicts.iter(), formatted_paths) 2738 - { 2739 - let conflict = tree.store().read_conflict(repo_path, conflict_id)?; 2736 + for ((_, conflict), formatted_path) in std::iter::zip(conflicts.iter(), formatted_paths) { 2740 2737 let sides = conflict.adds().len(); 2741 2738 let n_adds = conflict.adds().iter().flatten().count(); 2742 2739 let deletions = sides - n_adds;
+3 -3
src/commit_templater.rs
··· 299 299 let maybe_entries = repo.resolve_change_id(commit.change_id()); 300 300 maybe_entries.map_or(true, |entries| !entries.contains(commit.id())) 301 301 })), 302 - "conflict" => { 303 - language.wrap_boolean(wrap_fn(property, |commit| commit.tree().has_conflict())) 304 - } 302 + "conflict" => language.wrap_boolean(wrap_fn(property, |commit| { 303 + commit.merged_tree().unwrap().has_conflict() 304 + })), 305 305 "empty" => language.wrap_boolean(wrap_fn(property, |commit| { 306 306 let parent_tree = rewrite::merge_commit_trees(repo, &commit.parents()).unwrap(); 307 307 commit.tree_id() == parent_tree.id()