Skip to main content

grit_lib/diff/
handle.rs

1//! Repository-scoped tree and index diffs via [`Diff`].
2use crate::diff::targets::collect_files_for_target;
3use crate::diff::{
4    diff_index_to_tree, diff_index_to_worktree, diff_trees, diff_trees_with_renames_and_policy,
5    DiffEntry, DiffOptions,
6};
7use crate::diff::{DiffTarget, FileDiff, UncommittedDiff};
8use crate::error::Result;
9use crate::index::Index;
10use crate::objects::ObjectId;
11use crate::repo::Repository;
12
13/// Compare trees, the index, and the working tree for one repository.
14#[derive(Debug, Copy, Clone)]
15pub struct Diff<'repo> {
16    repo: &'repo Repository,
17}
18
19impl<'repo> Diff<'repo> {
20    /// Bind diff helpers to `repo`.
21    #[inline]
22    #[must_use]
23    pub fn new(repo: &'repo Repository) -> Self {
24        Self { repo }
25    }
26
27    /// Underlying repository.
28    #[inline]
29    #[must_use]
30    pub fn repository(&self) -> &'repo Repository {
31        self.repo
32    }
33
34    /// Diff two tree oids (either side may be `None` for an empty tree).
35    ///
36    /// Reads tree metadata without loading blobs and skips identical subtree ids.
37    /// The walk retains active directory frames alongside the ODB's bounded tree
38    /// cache; the returned vector owns metadata for every changed path.
39    ///
40    /// # Errors
41    ///
42    /// Propagates ODB read and tree walk errors.
43    ///
44    /// # Examples
45    ///
46    /// See the library guide on [diffing](https://grit-scm.com/docs/library/diff/).
47    #[inline]
48    pub fn tree_to_tree(
49        &self,
50        old_tree: Option<&ObjectId>,
51        new_tree: Option<&ObjectId>,
52    ) -> Result<Vec<DiffEntry>> {
53        diff_trees(self.repo.odb(), old_tree, new_tree, "")
54    }
55
56    /// Like [`Self::tree_to_tree`], then pair renames using `options.renames` (Git `-M`).
57    ///
58    /// # Errors
59    ///
60    /// Propagates ODB read, tree walk, and rename detection errors.
61    pub fn tree_to_tree_renames(
62        &self,
63        old_tree: Option<&ObjectId>,
64        new_tree: Option<&ObjectId>,
65        options: &DiffOptions,
66    ) -> Result<Vec<DiffEntry>> {
67        let config = self.repo.config()?;
68        diff_trees_with_renames_and_policy(
69            self.repo.odb(),
70            old_tree,
71            new_tree,
72            "",
73            options.rename_threshold_u32(),
74            config.as_ref(),
75            &self.repo.thread_policy(),
76        )
77    }
78
79    /// Diff the index against a tree (typically `HEAD`'s tree).
80    ///
81    /// # Errors
82    ///
83    /// Propagates ODB and index diff errors.
84    #[inline]
85    pub fn index_to_tree(
86        &self,
87        index: &Index,
88        tree_oid: Option<&ObjectId>,
89        ignore_submodules: bool,
90    ) -> Result<Vec<DiffEntry>> {
91        diff_index_to_tree(self.repo.odb(), index, tree_oid, ignore_submodules)
92    }
93
94    /// Diff the working tree against the index.
95    ///
96    /// # Errors
97    ///
98    /// Returns an error when the repository has no work tree or diff I/O fails.
99    #[inline]
100    pub fn worktree_to_index(
101        &self,
102        index: &mut Index,
103        ignore_submodule_untracked: bool,
104        simplify_gitlinks: bool,
105    ) -> Result<Vec<DiffEntry>> {
106        let work_tree = self
107            .repo
108            .work_tree()
109            .ok_or(crate::error::Error::RequiresWorkTree)?;
110        diff_index_to_worktree(
111            self.repo.odb(),
112            index,
113            work_tree,
114            ignore_submodule_untracked,
115            simplify_gitlinks,
116        )
117    }
118
119    /// Staged, unstaged, and working-tree diffs over the repository's on-disk index.
120    ///
121    /// # Errors
122    ///
123    /// Returns index read errors.
124    pub fn uncommitted(&self) -> Result<UncommittedDiff<'repo>> {
125        let index = self.repo.index_handle().load_preserving_sparse()?;
126        Ok(UncommittedDiff::new(self.repo, index))
127    }
128
129    /// Structured per-file diffs for `target`, honoring `pathspecs` and line-diff `opts`.
130    ///
131    /// # Errors
132    ///
133    /// Propagates index, ODB, and worktree diff failures.
134    pub fn files(
135        &self,
136        target: &DiffTarget,
137        pathspecs: &[String],
138        opts: &DiffOptions,
139    ) -> Result<Vec<FileDiff>> {
140        collect_files_for_target(self.repo, target, pathspecs, opts.clone())
141    }
142}