gix/repository/diff.rs
1use gix_object::TreeRefIter;
2
3use crate::{
4 repository::{diff_resource_cache, diff_tree_to_tree},
5 Repository, Tree,
6};
7
8/// Diff-utilities
9impl Repository {
10 /// Create a resource cache for diffable objects, and configured with everything it needs to know to perform diffs
11 /// faithfully just like `git` would.
12 /// `mode` controls what version of a resource should be diffed.
13 /// `worktree_roots` determine if files can be read from the worktree, where each side of the diff operation can
14 /// be represented by its own worktree root. `.gitattributes` are automatically read from the worktree if at least
15 /// one worktree is present.
16 ///
17 /// Note that attributes will always be obtained from the current `HEAD` index even if the resources being diffed
18 /// might live in another tree. Further, if one of the `worktree_roots` are set, attributes will also be read from
19 /// the worktree. Otherwise, it will be skipped and attributes are read from the index tree instead.
20 pub fn diff_resource_cache(
21 &self,
22 mode: gix_diff::blob::pipeline::Mode,
23 worktree_roots: gix_diff::blob::pipeline::WorktreeRoots,
24 ) -> Result<gix_diff::blob::Platform, diff_resource_cache::Error> {
25 let index = self.index_or_load_from_head_or_empty()?;
26 Ok(crate::diff::resource_cache(
27 self,
28 mode,
29 self.attributes_only(
30 &index,
31 if worktree_roots.is_unset() {
32 gix_worktree::stack::state::attributes::Source::IdMapping
33 } else {
34 gix_worktree::stack::state::attributes::Source::WorktreeThenIdMapping
35 },
36 )?
37 .inner,
38 worktree_roots,
39 )?)
40 }
41
42 /// Produce the changes that would need to be applied to `old_tree` to create `new_tree`.
43 /// If `options` are unset, they will be filled in according to the git configuration of this repository, and with
44 /// [full paths being tracked](crate::diff::Options::track_path()) as well, which typically means that
45 /// rewrite tracking might be disabled if done so explicitly by the user.
46 /// If `options` are set, the user can take full control over the settings.
47 ///
48 /// Note that this method exists to evoke similarity to `git2`, and makes it easier to fully control diff settings.
49 /// A more fluent version [may be used as well](Tree::changes()).
50 pub fn diff_tree_to_tree<'a, 'old_repo: 'a, 'new_repo: 'a>(
51 &self,
52 old_tree: impl Into<Option<&'a Tree<'old_repo>>>,
53 new_tree: impl Into<Option<&'a Tree<'new_repo>>>,
54 options: impl Into<Option<crate::diff::Options>>,
55 ) -> Result<Vec<crate::object::tree::diff::ChangeDetached>, diff_tree_to_tree::Error> {
56 let mut cache = self.diff_resource_cache(gix_diff::blob::pipeline::Mode::ToGit, Default::default())?;
57 let opts = options
58 .into()
59 .map_or_else(|| crate::diff::Options::from_configuration(&self.config), Ok)?
60 .into();
61
62 let empty_tree = self.empty_tree();
63 let old_tree = old_tree.into().unwrap_or(&empty_tree);
64 let new_tree = new_tree.into().unwrap_or(&empty_tree);
65 let mut out = Vec::new();
66 gix_diff::tree_with_rewrites(
67 TreeRefIter::from_bytes(&old_tree.data),
68 TreeRefIter::from_bytes(&new_tree.data),
69 &mut cache,
70 &mut Default::default(),
71 &self.objects,
72 |change| -> Result<_, std::convert::Infallible> {
73 out.push(change.into_owned());
74 Ok(gix_diff::tree_with_rewrites::Action::Continue)
75 },
76 opts,
77 )?;
78 Ok(out)
79 }
80
81 /// Return a resource cache suitable for diffing blobs from trees directly, where no worktree checkout exists.
82 ///
83 /// For more control, see [`diff_resource_cache()`](Self::diff_resource_cache).
84 pub fn diff_resource_cache_for_tree_diff(&self) -> Result<gix_diff::blob::Platform, diff_resource_cache::Error> {
85 self.diff_resource_cache(
86 gix_diff::blob::pipeline::Mode::ToGit,
87 gix_diff::blob::pipeline::WorktreeRoots::default(),
88 )
89 }
90}