Skip to content

Commit

Permalink
copy-tracking: create an explicit TreeDiffEntry struct
Browse files Browse the repository at this point in the history
  • Loading branch information
fowles committed Aug 11, 2024
1 parent ee6b922 commit 8e84c60
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 85 deletions.
5 changes: 1 addition & 4 deletions cli/src/commands/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@

use itertools::Itertools;
use jj_lib::backend::CopyRecords;
use jj_lib::commit::Commit;
use jj_lib::repo::Repo;
use jj_lib::rewrite::merge_commit_trees;
use tracing::instrument;

use crate::cli_util::{
print_unmatched_explicit_paths, CommandHelper, RevisionArg, WorkspaceCommandHelper,
};
use crate::cli_util::{print_unmatched_explicit_paths, CommandHelper, RevisionArg};
use crate::command_error::CommandError;
use crate::diff_util::DiffFormatArgs;
use crate::ui::Ui;
Expand Down
8 changes: 6 additions & 2 deletions cli/src/commands/fix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use itertools::Itertools;
use jj_lib::backend::{BackendError, CommitId, FileId, TreeValue};
use jj_lib::fileset::{self, FilesetExpression};
use jj_lib::matchers::{EverythingMatcher, Matcher};
use jj_lib::merged_tree::MergedTreeBuilder;
use jj_lib::merged_tree::{MergedTreeBuilder, TreeDiffEntry};
use jj_lib::repo::Repo;
use jj_lib::repo_path::{RepoPathBuf, RepoPathUiConverter};
use jj_lib::revset::{RevsetExpression, RevsetIteratorExt};
Expand Down Expand Up @@ -174,7 +174,11 @@ pub(crate) fn cmd_fix(
let parent_tree = commit.parent_tree(tx.repo())?;
let mut diff_stream = parent_tree.diff_stream(&tree, &matcher);
async {
while let Some((repo_path, diff)) = diff_stream.next().await {
while let Some(TreeDiffEntry {
target: repo_path,
value: diff,
}) = diff_stream.next().await
{
let (_before, after) = diff?;
// Deleted files have no file content to fix, and they have no terms in `after`,
// so we don't add any tool inputs for them. Conflicted files produce one tool
Expand Down
19 changes: 15 additions & 4 deletions cli/src/diff_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use jj_lib::diff::{Diff, DiffHunk};
use jj_lib::files::DiffLine;
use jj_lib::matchers::Matcher;
use jj_lib::merge::MergedTreeValue;
use jj_lib::merged_tree::{MergedTree, TreeDiffStream};
use jj_lib::merged_tree::{MergedTree, TreeDiffEntry, TreeDiffStream};
use jj_lib::object_id::ObjectId;
use jj_lib::repo::Repo;
use jj_lib::repo_path::{RepoPath, RepoPathUiConverter};
Expand Down Expand Up @@ -1110,7 +1110,11 @@ pub fn show_diff_summary(
path_converter: &RepoPathUiConverter,
) -> io::Result<()> {
async {
while let Some((repo_path, diff)) = tree_diff.next().await {
while let Some(TreeDiffEntry {
target: repo_path,
value: diff,
}) = tree_diff.next().await
{
let (before, after) = diff.unwrap();
let ui_path = path_converter.format_file_path(&repo_path);
if before.is_present() && after.is_present() {
Expand Down Expand Up @@ -1241,7 +1245,11 @@ pub fn show_types(
path_converter: &RepoPathUiConverter,
) -> io::Result<()> {
async {
while let Some((repo_path, diff)) = tree_diff.next().await {
while let Some(TreeDiffEntry {
target: repo_path,
value: diff,
}) = tree_diff.next().await
{
let (before, after) = diff.unwrap();
writeln!(
formatter.labeled("modified"),
Expand Down Expand Up @@ -1275,7 +1283,10 @@ pub fn show_names(
path_converter: &RepoPathUiConverter,
) -> io::Result<()> {
async {
while let Some((repo_path, _)) = tree_diff.next().await {
while let Some(TreeDiffEntry {
target: repo_path, ..
}) = tree_diff.next().await
{
writeln!(formatter, "{}", path_converter.format_file_path(&repo_path))?;
}
Ok(())
Expand Down
9 changes: 7 additions & 2 deletions cli/src/merge_tools/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use jj_lib::diff::{Diff, DiffHunk};
use jj_lib::files::{self, ContentHunk, MergeResult};
use jj_lib::matchers::Matcher;
use jj_lib::merge::Merge;
use jj_lib::merged_tree::{MergedTree, MergedTreeBuilder};
use jj_lib::merged_tree::{MergedTree, MergedTreeBuilder, TreeDiffEntry};
use jj_lib::object_id::ObjectId;
use jj_lib::repo_path::{RepoPath, RepoPathBuf};
use jj_lib::store::Store;
Expand Down Expand Up @@ -496,7 +496,12 @@ pub fn edit_diff_builtin(
let store = left_tree.store().clone();
let changed_files: Vec<_> = left_tree
.diff_stream(right_tree, matcher)
.map(|(path, diff)| diff.map(|_| path))
.map(
|TreeDiffEntry {
target: path,
value: diff,
}| diff.map(|_| path),
)
.try_collect()
.block_on()?;
let files = make_diff_files(&store, left_tree, right_tree, &changed_files)?;
Expand Down
4 changes: 2 additions & 2 deletions cli/src/merge_tools/diff_working_copies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use jj_lib::fsmonitor::FsmonitorSettings;
use jj_lib::gitignore::GitIgnoreFile;
use jj_lib::local_working_copy::{TreeState, TreeStateError};
use jj_lib::matchers::Matcher;
use jj_lib::merged_tree::MergedTree;
use jj_lib::merged_tree::{MergedTree, TreeDiffEntry};
use jj_lib::repo_path::RepoPathBuf;
use jj_lib::store::Store;
use jj_lib::working_copy::{CheckoutError, SnapshotOptions};
Expand Down Expand Up @@ -132,7 +132,7 @@ pub(crate) fn check_out_trees(
) -> Result<DiffWorkingCopies, DiffCheckoutError> {
let changed_files: Vec<_> = left_tree
.diff_stream(right_tree, matcher)
.map(|(path, _diff)| path)
.map(|TreeDiffEntry { target, .. }| target)
.collect()
.block_on();

Expand Down
27 changes: 16 additions & 11 deletions lib/src/conflicts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::diff::{Diff, DiffHunk};
use crate::files;
use crate::files::{ContentHunk, MergeResult};
use crate::merge::{Merge, MergeBuilder, MergedTreeValue};
use crate::merged_tree::TreeDiffStream;
use crate::merged_tree::{TreeDiffEntry, TreeDiffStream};
use crate::repo_path::{RepoPath, RepoPathBuf};
use crate::store::Store;

Expand Down Expand Up @@ -335,17 +335,22 @@ pub fn materialized_diff_stream<'a>(
),
> + 'a {
tree_diff
.map(|(path, diff)| async {
match diff {
Err(err) => (path, Err(err)),
Ok((before, after)) => {
let before_future = materialize_tree_value(store, &path, before);
let after_future = materialize_tree_value(store, &path, after);
let values = try_join!(before_future, after_future);
(path, values)
.map(
|TreeDiffEntry {
target: path,
value: diff,
}| async {
match diff {
Err(err) => (path, Err(err)),
Ok((before, after)) => {
let before_future = materialize_tree_value(store, &path, before);
let after_future = materialize_tree_value(store, &path, after);
let values = try_join!(before_future, after_future);
(path, values)
}
}
}
})
},
)
.buffered((store.concurrency() / 2).max(1))
}

Expand Down
7 changes: 5 additions & 2 deletions lib/src/default_index/revset_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use crate::conflicts::{materialized_diff_stream, MaterializedTreeValue};
use crate::default_index::{AsCompositeIndex, CompositeIndex, IndexPosition};
use crate::graph::GraphEdge;
use crate::matchers::{Matcher, Visit};
use crate::merged_tree::TreeDiffEntry;
use crate::repo_path::RepoPath;
use crate::revset::{
ResolvedExpression, ResolvedPredicateExpression, Revset, RevsetEvaluationError,
Expand Down Expand Up @@ -1148,8 +1149,10 @@ fn has_diff_from_parent(
let mut tree_diff = from_tree.diff_stream(&to_tree, matcher);
async {
match tree_diff.next().await {
Some((_, Ok(_))) => Ok(true),
Some((_, Err(err))) => Err(err),
Some(TreeDiffEntry { value: Ok(_), .. }) => Ok(true),
Some(TreeDiffEntry {
value: Err(err), ..
}) => Err(err),
None => Ok(false),
}
}
Expand Down
30 changes: 20 additions & 10 deletions lib/src/local_working_copy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use crate::matchers::{
DifferenceMatcher, EverythingMatcher, FilesMatcher, IntersectionMatcher, Matcher, PrefixMatcher,
};
use crate::merge::{Merge, MergeBuilder, MergedTreeValue};
use crate::merged_tree::{MergedTree, MergedTreeBuilder};
use crate::merged_tree::{MergedTree, MergedTreeBuilder, TreeDiffEntry};
use crate::object_id::ObjectId;
use crate::op_store::{OperationId, WorkspaceId};
use crate::repo_path::{RepoPath, RepoPathBuf, RepoPathComponent};
Expand Down Expand Up @@ -1353,15 +1353,21 @@ impl TreeState {
let mut diff_stream = Box::pin(
old_tree
.diff_stream(new_tree, matcher)
.map(|(path, diff)| async {
match diff {
Ok((before, after)) => {
let result = materialize_tree_value(&self.store, &path, after).await;
(path, result.map(|value| (before.is_present(), value)))
.map(
|TreeDiffEntry {
target: path,
value: diff,
}| async {
match diff {
Ok((before, after)) => {
let result =
materialize_tree_value(&self.store, &path, after).await;
(path, result.map(|value| (before.is_present(), value)))
}
Err(err) => (path, Err(err)),
}
Err(err) => (path, Err(err)),
}
})
},
)
.buffered(self.store.concurrency().max(1)),
);
while let Some((path, data)) = diff_stream.next().await {
Expand Down Expand Up @@ -1447,7 +1453,11 @@ impl TreeState {
let mut changed_file_states = Vec::new();
let mut deleted_files = HashSet::new();
let mut diff_stream = old_tree.diff_stream(new_tree, matcher.as_ref());
while let Some((path, diff)) = diff_stream.next().await {
while let Some(TreeDiffEntry {
target: path,
value: diff,
}) = diff_stream.next().await
{
let (_before, after) = diff?;
if after.is_absent() {
deleted_files.insert(path);
Expand Down
88 changes: 61 additions & 27 deletions lib/src/merged_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,16 +321,19 @@ impl MergedTree {
}
}

/// A single entry in a tree diff.
pub struct TreeDiffEntry {
// pub source: RepoPathBuf,
/// The target path.
pub target: RepoPathBuf,
/// The resolved tree values if available.
pub value: BackendResult<(MergedTreeValue, MergedTreeValue)>,
}

/// Type alias for the result from `MergedTree::diff_stream()`. We use a
/// `Stream` instead of an `Iterator` so high-latency backends (e.g. cloud-based
/// ones) can fetch trees asynchronously.
pub type TreeDiffStream<'matcher> = BoxStream<
'matcher,
(
RepoPathBuf,
BackendResult<(MergedTreeValue, MergedTreeValue)>,
),
>;
pub type TreeDiffStream<'matcher> = BoxStream<'matcher, TreeDiffEntry>;

fn all_tree_basenames(trees: &Merge<Tree>) -> impl Iterator<Item = &RepoPathComponent> {
trees
Expand Down Expand Up @@ -694,10 +697,7 @@ impl TreeDiffDirItem {
}

impl Iterator for TreeDiffIterator<'_> {
type Item = (
RepoPathBuf,
BackendResult<(MergedTreeValue, MergedTreeValue)>,
);
type Item = TreeDiffEntry;

fn next(&mut self) -> Option<Self::Item> {
while let Some(top) = self.stack.last_mut() {
Expand All @@ -711,7 +711,10 @@ impl Iterator for TreeDiffIterator<'_> {
},
TreeDiffItem::File(..) => {
if let TreeDiffItem::File(path, before, after) = self.stack.pop().unwrap() {
return Some((path, Ok((before, after))));
return Some(TreeDiffEntry {
target: path,
value: Ok((before, after)),
});
} else {
unreachable!();
}
Expand All @@ -721,13 +724,23 @@ impl Iterator for TreeDiffIterator<'_> {
let tree_before = before.is_tree();
let tree_after = after.is_tree();
let post_subdir = if tree_before || tree_after {
let before_tree = match Self::trees(&self.store, &path, &before) {
Ok(tree) => tree,
Err(err) => return Some((path, Err(err))),
};
let after_tree = match Self::trees(&self.store, &path, &after) {
Ok(tree) => tree,
Err(err) => return Some((path, Err(err))),
let (before_tree, after_tree) = match (
Self::trees(&self.store, &path, &before),
Self::trees(&self.store, &path, &after),
) {
(Ok(before_tree), Ok(after_tree)) => (before_tree, after_tree),
(Err(before_err), _) => {
return Some(TreeDiffEntry {
target: path,
value: Err(before_err),
})
}
(_, Err(after_err)) => {
return Some(TreeDiffEntry {
target: path,
value: Err(after_err),
})
}
};
let subdir =
TreeDiffDirItem::from_trees(&path, &before_tree, &after_tree, self.matcher);
Expand All @@ -738,7 +751,10 @@ impl Iterator for TreeDiffIterator<'_> {
};
if !tree_before && tree_after {
if before.is_present() {
return Some((path, Ok((before, Merge::absent()))));
return Some(TreeDiffEntry {
target: path,
value: Ok((before, Merge::absent())),
});
}
} else if tree_before && !tree_after {
if after.is_present() {
Expand All @@ -748,7 +764,10 @@ impl Iterator for TreeDiffIterator<'_> {
);
}
} else if !tree_before && !tree_after {
return Some((path, Ok((before, after))));
return Some(TreeDiffEntry {
target: path,
value: Ok((before, after)),
});
}
}
None
Expand Down Expand Up @@ -966,10 +985,7 @@ impl<'matcher> TreeDiffStreamImpl<'matcher> {
}

impl Stream for TreeDiffStreamImpl<'_> {
type Item = (
RepoPathBuf,
BackendResult<(MergedTreeValue, MergedTreeValue)>,
);
type Item = TreeDiffEntry;

fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
// Go through all pending tree futures and poll them.
Expand All @@ -981,12 +997,30 @@ impl Stream for TreeDiffStreamImpl<'_> {
Err(_) => {
// File or tree with error
let (key, result) = entry.remove_entry();
Poll::Ready(Some((key.path, result)))
Poll::Ready(Some(match result {
Err(err) => TreeDiffEntry {
target: key.path,
value: Err(err),
},
Ok((before, after)) => TreeDiffEntry {
target: key.path,
value: Ok((before, after)),
},
}))
}
Ok((before, after)) if !before.is_tree() && !after.is_tree() => {
// A diff with no trees involved
let (key, result) = entry.remove_entry();
Poll::Ready(Some((key.path, result)))
Poll::Ready(Some(match result {
Err(err) => TreeDiffEntry {
target: key.path,
value: Err(err),
},
Ok((before, after)) => TreeDiffEntry {
target: key.path,
value: Ok((before, after)),
},
}))
}
_ => {
// The first entry has a tree on at least one side (before or after). We need to
Expand Down
Loading

0 comments on commit 8e84c60

Please sign in to comment.