Skip to content

Commit

Permalink
Merge pull request #1630 from GitoxideLabs/diff-fix
Browse files Browse the repository at this point in the history
diff fix
  • Loading branch information
Byron authored Oct 15, 2024
2 parents f186c23 + 53fa8ab commit 155b5e1
Show file tree
Hide file tree
Showing 4 changed files with 606 additions and 15 deletions.
41 changes: 36 additions & 5 deletions gix-diff/src/rewrites/tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,10 @@ impl<T: Change> Tracker<T> {
.relation()
.filter(|_| matches!(change_kind, ChangeKind::Addition | ChangeKind::Deletion));
let entry_kind = change.entry_mode().kind();
if let (None | Some(Relation::ChildOfParent(_)), EntryKind::Commit | EntryKind::Tree) = (relation, entry_kind) {
if entry_kind == EntryKind::Commit {
return Some(change);
}
if let (None, EntryKind::Tree) = (relation, entry_kind) {
return Some(change);
};

Expand Down Expand Up @@ -221,27 +224,46 @@ impl<T: Change> Tracker<T> {
PushSourceTreeFn: FnMut(&mut dyn FnMut(T, &BStr)) -> Result<(), E>,
E: std::error::Error + Send + Sync + 'static,
{
fn is_parent(change: &impl Change) -> bool {
matches!(change.relation(), Some(Relation::Parent(_)))
}
diff_cache.options.skip_internal_diff_if_external_is_configured = false;

// The point of this is to optimize for identity-based lookups, which should be easy to find
// by partitioning.
fn by_id_and_location<T: Change>(a: &Item<T>, b: &Item<T>) -> std::cmp::Ordering {
a.change
.id()
.cmp(b.change.id())
.then_with(|| a.path.start.cmp(&b.path.start).then(a.path.end.cmp(&b.path.end)))
}
self.items.sort_by(by_id_and_location);

let mut out = Outcome {
options: self.rewrites,
..Default::default()
};
self.items.sort_by(by_id_and_location);

// Rewrites by directory can be pruned out quickly, quickly pruning candidates
// for the following per-item steps.
self.match_pairs_of_kind(
visit::SourceKind::Rename,
&mut cb,
None, /* by identity for parents */
&mut out,
diff_cache,
objects,
Some(is_parent),
)?;

self.match_pairs_of_kind(
visit::SourceKind::Rename,
&mut cb,
self.rewrites.percentage,
&mut out,
diff_cache,
objects,
None,
)?;

if let Some(copies) = self.rewrites.copies {
Expand All @@ -252,6 +274,7 @@ impl<T: Change> Tracker<T> {
&mut out,
diff_cache,
objects,
None,
)?;

match copies.source {
Expand All @@ -275,6 +298,7 @@ impl<T: Change> Tracker<T> {
&mut out,
diff_cache,
objects,
None,
)?;
}
}
Expand All @@ -299,6 +323,7 @@ impl<T: Change> Tracker<T> {
}

impl<T: Change> Tracker<T> {
#[allow(clippy::too_many_arguments)]
fn match_pairs_of_kind(
&mut self,
kind: visit::SourceKind,
Expand All @@ -307,10 +332,11 @@ impl<T: Change> Tracker<T> {
out: &mut Outcome,
diff_cache: &mut crate::blob::Platform,
objects: &impl gix_object::FindObjectOrHeader,
filter: Option<fn(&T) -> bool>,
) -> Result<(), emit::Error> {
// we try to cheaply reduce the set of possibilities first, before possibly looking more exhaustively.
let needs_second_pass = !needs_exact_match(percentage);
if self.match_pairs(cb, None /* by identity */, kind, out, diff_cache, objects)? == Action::Cancel {
if self.match_pairs(cb, None /* by identity */, kind, out, diff_cache, objects, filter)? == Action::Cancel {
return Ok(());
}
if needs_second_pass {
Expand All @@ -335,12 +361,13 @@ impl<T: Change> Tracker<T> {
}
};
if !is_limited {
self.match_pairs(cb, percentage, kind, out, diff_cache, objects)?;
self.match_pairs(cb, percentage, kind, out, diff_cache, objects, None)?;
}
}
Ok(())
}

#[allow(clippy::too_many_arguments)]
fn match_pairs(
&mut self,
cb: &mut impl FnMut(visit::Destination<'_, T>, Option<visit::Source<'_, T>>) -> Action,
Expand All @@ -349,10 +376,14 @@ impl<T: Change> Tracker<T> {
stats: &mut Outcome,
diff_cache: &mut crate::blob::Platform,
objects: &impl gix_object::FindObjectOrHeader,
filter: Option<fn(&T) -> bool>,
) -> Result<Action, emit::Error> {
let mut dest_ofs = 0;
while let Some((mut dest_idx, dest)) = self.items[dest_ofs..].iter().enumerate().find_map(|(idx, item)| {
(!item.emitted && matches!(item.change.kind(), ChangeKind::Addition)).then_some((idx, item))
(!item.emitted
&& matches!(item.change.kind(), ChangeKind::Addition)
&& filter.map_or(true, |f| f(&item.change)))
.then_some((idx, item))
}) {
dest_idx += dest_ofs;
dest_ofs = dest_idx + 1;
Expand Down
17 changes: 9 additions & 8 deletions gix-diff/tests/diff/rewrites/tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,9 +650,9 @@ fn simple_directory_rename_by_id() -> crate::Result {
},
"d/subdir".into(),
)
.is_some(),
"trees that are children are simply ignored. It's easier to compare views of worktrees (`gix-dirwalk`) \
and trees/indices that way."
.is_none(),
"trees that are children are kept and matched. That way, they can quickly be pruned which is done first.\
Those who don't need them can prune them in a later step."
);
assert!(track
.try_push_change(
Expand All @@ -664,7 +664,7 @@ fn simple_directory_rename_by_id() -> crate::Result {
},
"d-renamed/subdir".into(),
)
.is_some());
.is_none());
let _odb = util::add_retained_blobs(
&mut track,
[
Expand Down Expand Up @@ -692,22 +692,23 @@ fn simple_directory_rename_by_id() -> crate::Result {
assert_eq!(dst.change.relation, Some(Relation::Parent(1)));
assert_eq!(dst.change.mode.kind(), EntryKind::Tree);
}
1..=4 => {
1..=5 => {
let src = src.unwrap();
let (expected_src, expected_dst) = &[
("d/a", "d-renamed/a"),
("d/c", "d-renamed/c"),
("d/b", "d-renamed/b"),
("d/subdir", "d-renamed/subdir"),
("d/subdir/d", "d-renamed/subdir/d"),
][calls - 1];
assert_eq!(src.location, expected_src);
assert_eq!(dst.location, expected_dst);
}
5 => {
6 => {
assert_eq!(src, None);
assert_eq!(dst.location, "a");
}
6 => {
7 => {
assert_eq!(src, None);
assert_eq!(dst.location, "b");
}
Expand All @@ -723,7 +724,7 @@ fn simple_directory_rename_by_id() -> crate::Result {
..Default::default()
}
);
assert_eq!(calls, 7, "Should not have too few calls");
assert_eq!(calls, 8, "Should not have too few calls");
Ok(())
}

Expand Down
6 changes: 6 additions & 0 deletions gix/tests/fixtures/make_diff_repos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ git init jj-trackcopy-1
rm -f "$index"
git update-index --index-info < "$ROOT/assets/jj-trackcopy-1/47bd6f4aa4a7eeef8b01ce168c6c771bdfffcbd3.tree"
git commit --allow-empty -F "$ROOT/assets/jj-trackcopy-1/47bd6f4aa4a7eeef8b01ce168c6c771bdfffcbd3.msg"

git checkout -f HEAD
git mv cli c
git commit -m "renamed cli to c"

rm -Rf c/
)
Loading

0 comments on commit 155b5e1

Please sign in to comment.