-
-
Notifications
You must be signed in to change notification settings - Fork 329
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
index()
to diff two indices
It comes with pathspec support to allow for easier integration into the `status()` machinery.
Showing
11 changed files
with
2,067 additions
and
4 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
use crate::index::{Change, ChangeRef}; | ||
use crate::rewrites; | ||
use crate::rewrites::tracker::ChangeKind; | ||
use crate::tree::visit::Relation; | ||
use bstr::BStr; | ||
use gix_object::tree; | ||
use std::borrow::Cow; | ||
|
||
impl ChangeRef<'_, '_> { | ||
/// Copy everything into an owned version of this instance. | ||
pub fn into_owned(self) -> Change { | ||
match self { | ||
ChangeRef::Addition { | ||
location, | ||
index, | ||
entry_mode, | ||
id, | ||
} => ChangeRef::Addition { | ||
location: Cow::Owned(location.into_owned()), | ||
index, | ||
entry_mode, | ||
id: Cow::Owned(id.into_owned()), | ||
}, | ||
ChangeRef::Deletion { | ||
location, | ||
index, | ||
entry_mode, | ||
id, | ||
} => ChangeRef::Deletion { | ||
location: Cow::Owned(location.into_owned()), | ||
index, | ||
entry_mode, | ||
id: Cow::Owned(id.into_owned()), | ||
}, | ||
ChangeRef::Modification { | ||
location, | ||
previous_index, | ||
previous_entry_mode, | ||
previous_id, | ||
index, | ||
entry_mode, | ||
id, | ||
} => ChangeRef::Modification { | ||
location: Cow::Owned(location.into_owned()), | ||
previous_index, | ||
previous_entry_mode, | ||
previous_id: Cow::Owned(previous_id.into_owned()), | ||
index, | ||
entry_mode, | ||
id: Cow::Owned(id.into_owned()), | ||
}, | ||
ChangeRef::Rewrite { | ||
source_location, | ||
source_index, | ||
source_entry_mode, | ||
source_id, | ||
location, | ||
index, | ||
entry_mode, | ||
id, | ||
copy, | ||
} => ChangeRef::Rewrite { | ||
source_location: Cow::Owned(source_location.into_owned()), | ||
source_index, | ||
source_entry_mode, | ||
source_id: Cow::Owned(source_id.into_owned()), | ||
location: Cow::Owned(location.into_owned()), | ||
index, | ||
entry_mode, | ||
id: Cow::Owned(id.into_owned()), | ||
copy, | ||
}, | ||
ChangeRef::Unmerged { | ||
location, | ||
stage, | ||
index, | ||
entry_mode, | ||
id, | ||
} => ChangeRef::Unmerged { | ||
location: Cow::Owned(location.into_owned()), | ||
stage, | ||
index, | ||
entry_mode, | ||
id: Cow::Owned(id.into_owned()), | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl ChangeRef<'_, '_> { | ||
/// Return all shared fields among all variants: `(location, index, entry_mode, id)` | ||
/// | ||
/// In case of rewrites, the fields return to the current change. | ||
pub fn fields(&self) -> (&BStr, usize, gix_index::entry::Mode, &gix_hash::oid) { | ||
match self { | ||
ChangeRef::Addition { | ||
location, | ||
index, | ||
entry_mode, | ||
id, | ||
.. | ||
} | ||
| ChangeRef::Deletion { | ||
location, | ||
index, | ||
entry_mode, | ||
id, | ||
.. | ||
} | ||
| ChangeRef::Modification { | ||
location, | ||
index, | ||
entry_mode, | ||
id, | ||
.. | ||
} | ||
| ChangeRef::Rewrite { | ||
location, | ||
index, | ||
entry_mode, | ||
id, | ||
.. | ||
} | ||
| ChangeRef::Unmerged { | ||
location, | ||
index, | ||
entry_mode, | ||
id, | ||
.. | ||
} => (location.as_ref(), *index, *entry_mode, id), | ||
} | ||
} | ||
} | ||
|
||
impl rewrites::tracker::Change for ChangeRef<'_, '_> { | ||
fn id(&self) -> &gix_hash::oid { | ||
match self { | ||
ChangeRef::Addition { id, .. } | ChangeRef::Deletion { id, .. } | ChangeRef::Modification { id, .. } => { | ||
id.as_ref() | ||
} | ||
ChangeRef::Rewrite { .. } | ChangeRef::Unmerged { .. } => { | ||
unreachable!("BUG") | ||
} | ||
} | ||
} | ||
|
||
fn relation(&self) -> Option<Relation> { | ||
None | ||
} | ||
|
||
fn kind(&self) -> ChangeKind { | ||
match self { | ||
ChangeRef::Addition { .. } => ChangeKind::Addition, | ||
ChangeRef::Deletion { .. } => ChangeKind::Deletion, | ||
ChangeRef::Modification { .. } => ChangeKind::Modification, | ||
ChangeRef::Rewrite { .. } => { | ||
unreachable!("BUG: rewrites can't be determined ahead of time") | ||
} | ||
ChangeRef::Unmerged { .. } => { | ||
unreachable!("BUG: unmerged don't participate in rename tracking") | ||
} | ||
} | ||
} | ||
|
||
fn entry_mode(&self) -> tree::EntryMode { | ||
match self { | ||
ChangeRef::Addition { entry_mode, .. } | ||
| ChangeRef::Deletion { entry_mode, .. } | ||
| ChangeRef::Modification { entry_mode, .. } | ||
| ChangeRef::Rewrite { entry_mode, .. } | ||
| ChangeRef::Unmerged { entry_mode, .. } => { | ||
entry_mode | ||
.to_tree_entry_mode() | ||
// Default is for the impossible case - just don't let it participate in rename tracking. | ||
.unwrap_or(tree::EntryKind::Tree.into()) | ||
} | ||
} | ||
} | ||
|
||
fn id_and_entry_mode(&self) -> (&gix_hash::oid, tree::EntryMode) { | ||
match self { | ||
ChangeRef::Addition { id, entry_mode, .. } | ||
| ChangeRef::Deletion { id, entry_mode, .. } | ||
| ChangeRef::Modification { id, entry_mode, .. } | ||
| ChangeRef::Rewrite { id, entry_mode, .. } | ||
| ChangeRef::Unmerged { id, entry_mode, .. } => { | ||
( | ||
id, | ||
entry_mode | ||
.to_tree_entry_mode() | ||
// Default is for the impossible case - just don't let it participate in rename tracking. | ||
.unwrap_or(tree::EntryKind::Tree.into()), | ||
) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,324 @@ | ||
use super::{Action, ChangeRef, Error, RewriteOptions}; | ||
use crate::rewrites; | ||
use bstr::{BStr, BString, ByteSlice}; | ||
use gix_filter::attributes::glob::pattern::Case; | ||
use std::borrow::Cow; | ||
use std::cell::RefCell; | ||
use std::cmp::Ordering; | ||
|
||
/// Produce an entry-by-entry diff between `lhs` and `rhs`, sending changes to `cb(change) -> Action` for consumption, | ||
/// which would turn `lhs` into `rhs` if applied. | ||
/// Use `pathspec` to reduce the set of entries to look at, and `pathspec_attributes` may be used by pathspecs that perform | ||
/// attribute lookups. | ||
/// | ||
/// If `cb` indicated that the operation should be cancelled, no error is triggered as this isn't supposed to | ||
/// occur through user-interaction - this diff is typically too fast. | ||
/// | ||
/// Note that rewrites will be emitted at the end, so no ordering can be assumed. They will only be tracked if | ||
/// `rewrite_options` is `Some`. Note that the set of entries participating in rename tracking is affected by `pathspec`. | ||
/// | ||
/// Return the outcome of the rewrite tracker if it was enabled. | ||
/// | ||
/// Note that only `rhs` may contain unmerged entries, as `rhs` is expected to be the index read from `.git/index`. | ||
/// Unmerged entries are always provided as changes, one stage at a time, up to three stages for *base*, *ours* and *theirs*. | ||
/// Conceptually, `rhs` is *ours*, and `lhs` is *theirs*. | ||
/// The entries in `lhs` and `rhs` are both expected to be sorted like index entries are typically sorted. | ||
/// | ||
/// Note that sparse indices aren't supported, they must be "unsparsed" before. | ||
pub fn diff<'rhs, 'lhs: 'rhs, E, Find>( | ||
lhs: &'lhs gix_index::State, | ||
rhs: &'rhs gix_index::State, | ||
mut cb: impl FnMut(ChangeRef<'lhs, 'rhs>) -> Result<Action, E>, | ||
rewrite_options: Option<RewriteOptions<'_, Find>>, | ||
pathspec: &mut gix_pathspec::Search, | ||
pathspec_attributes: &mut dyn FnMut(&BStr, Case, bool, &mut gix_attributes::search::Outcome) -> bool, | ||
) -> Result<Option<rewrites::Outcome>, Error> | ||
where | ||
E: Into<Box<dyn std::error::Error + Send + Sync>>, | ||
Find: gix_object::FindObjectOrHeader, | ||
{ | ||
if lhs.is_sparse() || rhs.is_sparse() { | ||
return Err(Error::IsSparse); | ||
} | ||
if lhs | ||
.entries() | ||
.iter() | ||
.any(|e| e.stage() != gix_index::entry::Stage::Unconflicted) | ||
{ | ||
return Err(Error::LhsHasUnmerged); | ||
} | ||
|
||
let lhs_range = lhs | ||
.prefixed_entries_range(pathspec.common_prefix()) | ||
.unwrap_or_else(|| 0..lhs.entries().len()); | ||
let rhs_range = rhs | ||
.prefixed_entries_range(pathspec.common_prefix()) | ||
.unwrap_or_else(|| 0..rhs.entries().len()); | ||
|
||
let pattern_matches = RefCell::new(|relative_path, entry: &gix_index::Entry| { | ||
pathspec | ||
.pattern_matching_relative_path(relative_path, Some(entry.mode.is_submodule()), pathspec_attributes) | ||
.map_or(false, |m| !m.is_excluded()) | ||
}); | ||
|
||
let (mut lhs_iter, mut rhs_iter) = ( | ||
lhs.entries()[lhs_range.clone()] | ||
.iter() | ||
.enumerate() | ||
.map(|(idx, e)| (idx + lhs_range.start, e.path(lhs), e)) | ||
.filter(|(_, path, e)| pattern_matches.borrow_mut()(path, e)), | ||
rhs.entries()[rhs_range.clone()] | ||
.iter() | ||
.enumerate() | ||
.map(|(idx, e)| (idx + rhs_range.start, e.path(rhs), e)) | ||
.filter(|(_, path, e)| pattern_matches.borrow_mut()(path, e)), | ||
); | ||
|
||
let mut conflicting_paths = Vec::<BString>::new(); | ||
let mut cb = move |change: ChangeRef<'lhs, 'rhs>| { | ||
let (location, ..) = change.fields(); | ||
if let ChangeRef::Unmerged { .. } = &change { | ||
if let Err(insert_idx) = conflicting_paths.binary_search_by(|p| p.as_bstr().cmp(location)) { | ||
conflicting_paths.insert(insert_idx, location.to_owned()); | ||
} | ||
cb(change) | ||
} else if conflicting_paths | ||
.binary_search_by(|p| p.as_bstr().cmp(location)) | ||
.is_err() | ||
{ | ||
cb(change) | ||
} else { | ||
Ok(Action::Continue) | ||
} | ||
}; | ||
let mut resource_cache_storage = None; | ||
let mut tracker = rewrite_options.map( | ||
|RewriteOptions { | ||
resource_cache, | ||
rewrites, | ||
find, | ||
}| { | ||
resource_cache_storage = Some((resource_cache, find)); | ||
rewrites::Tracker::<ChangeRef<'lhs, 'rhs>>::new(rewrites) | ||
}, | ||
); | ||
|
||
let (mut lhs_storage, mut rhs_storage) = (lhs_iter.next(), rhs_iter.next()); | ||
loop { | ||
match (lhs_storage, rhs_storage) { | ||
(Some(lhs), Some(rhs)) => { | ||
match emit_unmerged_ignore_intent_to_add(rhs, &mut cb)? { | ||
None => {} | ||
Some(Action::Cancel) => return Ok(None), | ||
Some(Action::Continue) => { | ||
rhs_storage = rhs_iter.next(); | ||
continue; | ||
} | ||
}; | ||
|
||
let (lhs_idx, lhs_path, lhs_entry) = lhs; | ||
let (rhs_idx, rhs_path, rhs_entry) = rhs; | ||
match lhs_path.cmp(rhs_path) { | ||
Ordering::Less => match emit_deletion(lhs, &mut cb, tracker.as_mut())? { | ||
Action::Continue => { | ||
lhs_storage = lhs_iter.next(); | ||
} | ||
Action::Cancel => return Ok(None), | ||
}, | ||
Ordering::Equal => { | ||
if lhs_entry.id != rhs_entry.id || lhs_entry.mode != rhs_entry.mode { | ||
let change = ChangeRef::Modification { | ||
location: Cow::Borrowed(rhs_path), | ||
previous_index: lhs_idx, | ||
previous_entry_mode: lhs_entry.mode, | ||
previous_id: Cow::Borrowed(lhs_entry.id.as_ref()), | ||
index: rhs_idx, | ||
entry_mode: rhs_entry.mode, | ||
id: Cow::Borrowed(rhs_entry.id.as_ref()), | ||
}; | ||
|
||
let change = match tracker.as_mut() { | ||
None => Some(change), | ||
Some(tracker) => tracker.try_push_change(change, rhs_path), | ||
}; | ||
if let Some(change) = change { | ||
match cb(change).map_err(|err| Error::Callback(err.into()))? { | ||
Action::Continue => {} | ||
Action::Cancel => return Ok(None), | ||
} | ||
} | ||
} | ||
lhs_storage = lhs_iter.next(); | ||
rhs_storage = rhs_iter.next(); | ||
} | ||
Ordering::Greater => match emit_addition(rhs, &mut cb, tracker.as_mut())? { | ||
Action::Continue => { | ||
rhs_storage = rhs_iter.next(); | ||
} | ||
Action::Cancel => return Ok(None), | ||
}, | ||
} | ||
} | ||
(Some(lhs), None) => match emit_deletion(lhs, &mut cb, tracker.as_mut())? { | ||
Action::Cancel => return Ok(None), | ||
Action::Continue => { | ||
lhs_storage = lhs_iter.next(); | ||
} | ||
}, | ||
(None, Some(rhs)) => match emit_addition(rhs, &mut cb, tracker.as_mut())? { | ||
Action::Cancel => return Ok(None), | ||
Action::Continue => { | ||
rhs_storage = rhs_iter.next(); | ||
} | ||
}, | ||
(None, None) => break, | ||
} | ||
} | ||
|
||
if let Some((mut tracker, (resource_cache, find))) = tracker.zip(resource_cache_storage) { | ||
let mut cb_err = None; | ||
let out = tracker.emit( | ||
|dst, src| { | ||
let change = if let Some(src) = src { | ||
let (lhs_path, lhs_index, lhs_mode, lhs_id) = src.change.fields(); | ||
let (rhs_path, rhs_index, rhs_mode, rhs_id) = dst.change.fields(); | ||
ChangeRef::Rewrite { | ||
source_location: Cow::Owned(lhs_path.into()), | ||
source_index: lhs_index, | ||
source_entry_mode: lhs_mode, | ||
source_id: Cow::Owned(lhs_id.into()), | ||
location: Cow::Owned(rhs_path.into()), | ||
index: rhs_index, | ||
entry_mode: rhs_mode, | ||
id: Cow::Owned(rhs_id.into()), | ||
copy: match src.kind { | ||
rewrites::tracker::visit::SourceKind::Rename => false, | ||
rewrites::tracker::visit::SourceKind::Copy => true, | ||
}, | ||
} | ||
} else { | ||
dst.change | ||
}; | ||
match cb(change) { | ||
Ok(Action::Continue) => crate::tree::visit::Action::Continue, | ||
Ok(Action::Cancel) => crate::tree::visit::Action::Cancel, | ||
Err(err) => { | ||
cb_err = Some(Error::Callback(err.into())); | ||
crate::tree::visit::Action::Cancel | ||
} | ||
} | ||
}, | ||
resource_cache, | ||
find, | ||
|push| { | ||
for (index, entry) in lhs.entries().iter().enumerate() { | ||
let path = entry.path(rhs); | ||
push( | ||
ChangeRef::Modification { | ||
location: Cow::Borrowed(path), | ||
previous_index: 0, /* does not matter */ | ||
previous_entry_mode: entry.mode, | ||
previous_id: Cow::Owned(entry.id.kind().null()), | ||
index, | ||
entry_mode: entry.mode, | ||
id: Cow::Borrowed(entry.id.as_ref()), | ||
}, | ||
path, | ||
); | ||
} | ||
Ok::<_, std::convert::Infallible>(()) | ||
}, | ||
)?; | ||
|
||
if let Some(err) = cb_err { | ||
Err(err) | ||
} else { | ||
Ok(Some(out)) | ||
} | ||
} else { | ||
Ok(None) | ||
} | ||
} | ||
|
||
fn emit_deletion<'rhs, 'lhs: 'rhs, E>( | ||
(idx, path, entry): (usize, &'lhs BStr, &'lhs gix_index::Entry), | ||
mut cb: impl FnMut(ChangeRef<'lhs, 'rhs>) -> Result<Action, E>, | ||
tracker: Option<&mut rewrites::Tracker<ChangeRef<'lhs, 'rhs>>>, | ||
) -> Result<Action, Error> | ||
where | ||
E: Into<Box<dyn std::error::Error + Send + Sync>>, | ||
{ | ||
let change = ChangeRef::Deletion { | ||
location: Cow::Borrowed(path), | ||
index: idx, | ||
entry_mode: entry.mode, | ||
id: Cow::Borrowed(entry.id.as_ref()), | ||
}; | ||
|
||
let change = match tracker { | ||
None => change, | ||
Some(tracker) => match tracker.try_push_change(change, path) { | ||
Some(change) => change, | ||
None => return Ok(Action::Continue), | ||
}, | ||
}; | ||
|
||
cb(change).map_err(|err| Error::Callback(err.into())) | ||
} | ||
|
||
fn emit_addition<'rhs, 'lhs: 'rhs, E>( | ||
(idx, path, entry): (usize, &'rhs BStr, &'rhs gix_index::Entry), | ||
mut cb: impl FnMut(ChangeRef<'lhs, 'rhs>) -> Result<Action, E>, | ||
tracker: Option<&mut rewrites::Tracker<ChangeRef<'lhs, 'rhs>>>, | ||
) -> Result<Action, Error> | ||
where | ||
E: Into<Box<dyn std::error::Error + Send + Sync>>, | ||
{ | ||
if let Some(action) = emit_unmerged_ignore_intent_to_add((idx, path, entry), &mut cb)? { | ||
return Ok(action); | ||
} | ||
|
||
let change = ChangeRef::Addition { | ||
location: Cow::Borrowed(path), | ||
index: idx, | ||
entry_mode: entry.mode, | ||
id: Cow::Borrowed(entry.id.as_ref()), | ||
}; | ||
|
||
let change = match tracker { | ||
None => change, | ||
Some(tracker) => match tracker.try_push_change(change, path) { | ||
Some(change) => change, | ||
None => return Ok(Action::Continue), | ||
}, | ||
}; | ||
|
||
cb(change).map_err(|err| Error::Callback(err.into())) | ||
} | ||
|
||
fn emit_unmerged_ignore_intent_to_add<'rhs, 'lhs: 'rhs, E>( | ||
(idx, path, entry): (usize, &'rhs BStr, &'rhs gix_index::Entry), | ||
cb: &mut impl FnMut(ChangeRef<'lhs, 'rhs>) -> Result<Action, E>, | ||
) -> Result<Option<Action>, Error> | ||
where | ||
E: Into<Box<dyn std::error::Error + Send + Sync>>, | ||
{ | ||
if entry.flags.contains(gix_index::entry::Flags::INTENT_TO_ADD) { | ||
return Ok(Some(Action::Continue)); | ||
} | ||
let stage = entry.stage(); | ||
if stage == gix_index::entry::Stage::Unconflicted { | ||
return Ok(None); | ||
} | ||
|
||
Ok(Some( | ||
cb(ChangeRef::Unmerged { | ||
location: Cow::Borrowed(path), | ||
stage, | ||
index: idx, | ||
entry_mode: entry.mode, | ||
id: Cow::Borrowed(entry.id.as_ref()), | ||
}) | ||
.map_err(|err| Error::Callback(err.into()))?, | ||
)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
use bstr::BStr; | ||
use std::borrow::Cow; | ||
|
||
/// The error returned by [`index()`](crate::index()). | ||
#[derive(Debug, thiserror::Error)] | ||
#[allow(missing_docs)] | ||
pub enum Error { | ||
#[error("Cannot diff indices that contain sparse entries")] | ||
IsSparse, | ||
#[error("Unmerged entries aren't allowed in the left-hand index, only in the right-hand index")] | ||
LhsHasUnmerged, | ||
#[error("The callback indicated failure")] | ||
Callback(#[source] Box<dyn std::error::Error + Send + Sync>), | ||
#[error("Failure during rename tracking")] | ||
RenameTracking(#[from] crate::rewrites::tracker::emit::Error), | ||
} | ||
|
||
/// What to do after a [ChangeRef] was passed ot the callback of [`index()`](crate::index()). | ||
#[derive(Default, Clone, Copy, PartialOrd, PartialEq, Ord, Eq, Hash)] | ||
pub enum Action { | ||
/// Continue the operation. | ||
#[default] | ||
Continue, | ||
/// Stop the operation immediately. | ||
/// | ||
/// This is useful if one just wants to determine if something changed or not. | ||
Cancel, | ||
} | ||
|
||
/// Options to configure how rewrites are tracked as part of the [`index()`](crate::index()) call. | ||
pub struct RewriteOptions<'a, Find> | ||
where | ||
Find: gix_object::FindObjectOrHeader, | ||
{ | ||
/// The cache to be used when rename-tracking by similarity is enabled, typically the default. | ||
/// Note that it's recommended to call [`clear_resource_cache()`](`crate::blob::Platform::clear_resource_cache()`) | ||
/// between the calls to avoid runaway memory usage, as the cache isn't limited. | ||
pub resource_cache: &'a mut crate::blob::Platform, | ||
/// A way to lookup objects from the object database, for use in similarity checks. | ||
pub find: &'a Find, | ||
/// Configure how rewrites are tracked. | ||
pub rewrites: crate::Rewrites, | ||
} | ||
|
||
/// Identify a change that would have to be applied to `lhs` to obtain `rhs`, as provided in [`index()`](crate::index()). | ||
/// | ||
/// Note that all variants are unconflicted entries, unless it's the [`Self::Unmerged`] one. | ||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub enum ChangeRef<'lhs, 'rhs> { | ||
/// An entry was added to `rhs`. | ||
Addition { | ||
/// The location of the newly added entry in `rhs`. | ||
location: Cow<'rhs, BStr>, | ||
/// The index into the entries array of `rhs` for full access. | ||
index: usize, | ||
/// The mode of the entry in `rhs`. | ||
entry_mode: gix_index::entry::Mode, | ||
/// The object id of the entry in `rhs`. | ||
id: Cow<'rhs, gix_hash::oid>, | ||
}, | ||
/// An entry was removed from `rhs`. | ||
Deletion { | ||
/// The location the entry that doesn't exist in `rhs`. | ||
location: Cow<'lhs, BStr>, | ||
/// The index into the entries array of `lhs` for full access. | ||
index: usize, | ||
/// The mode of the entry in `lhs`. | ||
entry_mode: gix_index::entry::Mode, | ||
/// The object id of the entry in `lhs`. | ||
id: Cow<'rhs, gix_hash::oid>, | ||
}, | ||
/// An entry was modified, i.e. has changed its content or its mode. | ||
Modification { | ||
/// The location of the modified entry both in `lhs` and `rhs`. | ||
location: Cow<'rhs, BStr>, | ||
/// The index into the entries array of `lhs` for full access. | ||
previous_index: usize, | ||
/// The previous mode of the entry, in `lhs`. | ||
previous_entry_mode: gix_index::entry::Mode, | ||
/// The previous object id of the entry, in `lhs`. | ||
previous_id: Cow<'lhs, gix_hash::oid>, | ||
/// The index into the entries array of `rhs` for full access. | ||
index: usize, | ||
/// The mode of the entry in `rhs`. | ||
entry_mode: gix_index::entry::Mode, | ||
/// The object id of the entry in `rhs`. | ||
id: Cow<'rhs, gix_hash::oid>, | ||
}, | ||
/// An entry was renamed or copied from `lhs` to `rhs`. | ||
/// | ||
/// A rename is effectively fusing together the `Deletion` of the source and the `Addition` of the destination. | ||
Rewrite { | ||
/// The location of the source of the rename or copy operation, in `lhs`. | ||
source_location: Cow<'lhs, BStr>, | ||
/// The index of the entry before the rename, into the entries array of `rhs` for full access. | ||
source_index: usize, | ||
/// The mode of the entry before the rewrite, in `lhs`. | ||
source_entry_mode: gix_index::entry::Mode, | ||
/// The object id of the entry before the rewrite. | ||
/// | ||
/// Note that this is the same as `id` if we require the [similarity to be 100%](super::Rewrites::percentage), but may | ||
/// be different otherwise. | ||
source_id: Cow<'lhs, gix_hash::oid>, | ||
|
||
/// The current location of the entry in `rhs`. | ||
location: Cow<'rhs, BStr>, | ||
/// The index of the entry after the rename, into the entries array of `rhs` for full access. | ||
index: usize, | ||
/// The mode of the entry after the rename in `rhs`. | ||
entry_mode: gix_index::entry::Mode, | ||
/// The object id of the entry after the rename in `rhs`. | ||
id: Cow<'rhs, gix_hash::oid>, | ||
|
||
/// If true, this rewrite is created by copy, and `source_id` is pointing to its source. Otherwise, it's a rename, | ||
/// and `source_id` points to a deleted object, as renames are tracked as deletions and additions of the same | ||
/// or similar content. | ||
copy: bool, | ||
}, | ||
/// One of up to three unmerged entries that are provided in order, one for each stage, ordered | ||
/// by `location` and `stage`. | ||
/// | ||
/// Unmerged entries also don't participate in rename tracking, and they are never present in `lhs`. | ||
Unmerged { | ||
/// The current location of the entry in `rhs`. | ||
location: Cow<'rhs, BStr>, | ||
/// The stage of the entry, either *base*, *ours*, or *theirs*. | ||
stage: gix_index::entry::Stage, | ||
/// The index into the entries array of `rhs` for full access. | ||
index: usize, | ||
/// The mode of the entry in `rhs`. | ||
entry_mode: gix_index::entry::Mode, | ||
/// The object id of the entry in `rhs`. | ||
id: Cow<'rhs, gix_hash::oid>, | ||
}, | ||
} | ||
|
||
/// The fully-owned version of [`ChangeRef`]. | ||
pub type Change = ChangeRef<'static, 'static>; | ||
|
||
mod change; | ||
pub(super) mod function; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
+13 KB
(100%)
gix-diff/tests/fixtures/generated-archives/make_diff_for_rewrites_repo.tar
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters