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