use gix_diff::tree::recorder::Location; pub use gix_diff::*; /// pub mod options { /// pub mod init { /// The error returned when instantiating [diff options](crate::diff::Options). #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { #[cfg(feature = "blob-diff")] #[error(transparent)] RewritesConfiguration(#[from] crate::diff::new_rewrites::Error), } } } /// General diff-related options for configuring rename-tracking and blob diffs. #[derive(Debug, Copy, Clone)] pub struct Options { location: Option, #[cfg(feature = "blob-diff")] rewrites: Option, } impl Default for Options { fn default() -> Self { Options { location: Some(Location::Path), #[cfg(feature = "blob-diff")] rewrites: None, } } } #[cfg(feature = "blob-diff")] impl From for gix_diff::tree_with_rewrites::Options { fn from(opts: Options) -> Self { gix_diff::tree_with_rewrites::Options { location: opts.location, #[cfg(feature = "blob-diff")] rewrites: opts.rewrites, } } } /// Lifecycle impl Options { #[cfg(feature = "blob-diff")] pub(crate) fn from_configuration(config: &crate::config::Cache) -> Result { Ok(Options { location: Some(Location::Path), rewrites: { let (rewrites, is_configured) = config.diff_renames()?; if is_configured { rewrites } else { Some(Default::default()) } }, }) } } /// Setters impl Options { /// Do not keep track of filepaths at all, which will leave all `location` fields empty. pub fn no_locations(&mut self) -> &mut Self { self.location = None; self } /// Keep track of file-names, which makes `location` fields usable with the filename of the changed item. pub fn track_filename(&mut self) -> &mut Self { self.location = Some(Location::FileName); self } /// Keep track of the entire path of a change, relative to the repository. (default). /// /// This makes the `location` field fully usable. pub fn track_path(&mut self) -> &mut Self { self.location = Some(Location::Path); self } /// Provide `None` to disable rewrite tracking entirely, or pass `Some()` to control to /// what extent rename and copy tracking is performed. /// /// Note that by default, the git configuration determines rewrite tracking and git defaults are used /// if nothing is configured, which turns rename tracking with 50% similarity on, while not tracking copies at all. #[cfg(feature = "blob-diff")] pub fn track_rewrites(&mut self, renames: Option) -> &mut Self { self.rewrites = renames; self } } /// Builder impl Options { /// Provide `None` to disable rewrite tracking entirely, or pass `Some()` to control to /// what extent rename and copy tracking is performed. /// /// Note that by default, the git configuration determines rewrite tracking and git defaults are used /// if nothing is configured, which turns rename tracking with 50% similarity on, while not tracking copies at all. #[cfg(feature = "blob-diff")] pub fn with_rewrites(mut self, renames: Option) -> Self { self.rewrites = renames; self } } /// pub mod rename { /// Determine how to do rename tracking. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Tracking { /// Do not track renames at all, the fastest option. Disabled, /// Track renames. Renames, /// Track renames and copies. /// /// This is the most expensive option. RenamesAndCopies, } } /// #[cfg(feature = "blob-diff")] pub(crate) mod utils { use gix_diff::{rewrites::Copies, Rewrites}; use crate::{ config::{cache::util::ApplyLeniency, tree::Diff}, diff::rename::Tracking, Repository, }; /// pub mod new_rewrites { /// The error returned by [`new_rewrites()`](super::new_rewrites()). #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { #[error(transparent)] ConfigDiffRenames(#[from] crate::config::key::GenericError), #[error(transparent)] ConfigDiffRenameLimit(#[from] crate::config::unsigned_integer::Error), } } /// pub mod resource_cache { /// The error returned by [`resource_cache()`](super::resource_cache()). #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { #[error(transparent)] DiffAlgorithm(#[from] crate::config::diff::algorithm::Error), #[error(transparent)] WorktreeFilterOptions(#[from] crate::filter::pipeline::options::Error), #[error(transparent)] DiffDrivers(#[from] crate::config::diff::drivers::Error), #[error(transparent)] DiffPipelineOptions(#[from] crate::config::diff::pipeline_options::Error), #[error(transparent)] CommandContext(#[from] crate::config::command_context::Error), } } /// Create an instance by reading all relevant information from the `config`uration, while being `lenient` or not. /// Returns `Ok((None, false))` if nothing is configured, or `Ok((None, true))` if it's configured and disabled. /// /// Note that missing values will be defaulted similar to what git does. #[allow(clippy::result_large_err)] pub fn new_rewrites( config: &gix_config::File<'static>, lenient: bool, ) -> Result<(Option, bool), new_rewrites::Error> { new_rewrites_inner(config, lenient, &Diff::RENAMES, &Diff::RENAME_LIMIT) } pub(crate) fn new_rewrites_inner( config: &gix_config::File<'static>, lenient: bool, renames: &'static crate::config::tree::diff::Renames, rename_limit: &'static crate::config::tree::keys::UnsignedInteger, ) -> Result<(Option, bool), new_rewrites::Error> { let copies = match config .boolean(renames) .map(|value| renames.try_into_renames(value)) .transpose() .with_leniency(lenient)? { Some(renames) => match renames { Tracking::Disabled => return Ok((None, true)), Tracking::Renames => None, Tracking::RenamesAndCopies => Some(Copies::default()), }, None => return Ok((None, false)), }; let default = Rewrites::default(); Ok(( Rewrites { copies, limit: config .integer(rename_limit) .map(|value| rename_limit.try_into_usize(value)) .transpose() .with_leniency(lenient)? .unwrap_or(default.limit), ..default } .into(), true, )) } /// Return a low-level utility to efficiently prepare a blob-level diff operation between two resources, /// and cache these diffable versions so that matrix-like MxN diffs are efficient. /// /// `repo` is used to obtain the needed configuration values. /// `mode` determines how the diffable files will look like, and also how fast, in average, these conversions are. /// `attr_stack` is for accessing `.gitattributes` for knowing how to apply filters. Know that it's typically adjusted based on the /// `roots` - if there are no worktree roots, `.gitattributes` are also not usually read from worktrees. /// `roots` provide information about where to get diffable data from, so source and destination can either be sourced from /// a worktree, or from the object database, or both. pub fn resource_cache( repo: &Repository, mode: gix_diff::blob::pipeline::Mode, attr_stack: gix_worktree::Stack, roots: gix_diff::blob::pipeline::WorktreeRoots, ) -> Result { let diff_algo = repo.config.diff_algorithm()?; let diff_cache = gix_diff::blob::Platform::new( gix_diff::blob::platform::Options { algorithm: Some(diff_algo), skip_internal_diff_if_external_is_configured: false, }, gix_diff::blob::Pipeline::new( roots, gix_filter::Pipeline::new(repo.command_context()?, crate::filter::Pipeline::options(repo)?), repo.config.diff_drivers()?, repo.config.diff_pipeline_options()?, ), mode, attr_stack, ); Ok(diff_cache) } } #[cfg(feature = "blob-diff")] pub use utils::{new_rewrites, resource_cache};