1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::{config::tree, Repository};
/// The error returned by [Repository::tree_index_status()].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
IndexFromMTree(#[from] crate::repository::index_from_tree::Error),
#[error(transparent)]
RewritesConfiguration(#[from] crate::diff::new_rewrites::Error),
#[error("Could not create diff-cache for similarity checks")]
DiffResourceCache(#[from] crate::repository::diff_resource_cache::Error),
#[error(transparent)]
TreeIndexDiff(#[from] gix_diff::index::Error),
}
/// Specify how to perform rewrite tracking [Repository::tree_index_status()].
#[derive(Default, Debug, Copy, Clone)]
pub enum TrackRenames {
/// Check `status.renames` and then `diff.renames` if the former isn't set. Otherwise, default to performing rewrites if nothing
/// is set.
#[default]
AsConfigured,
/// Track renames according ot the given configuration.
Given(gix_diff::Rewrites),
/// Do not track renames.
Disabled,
}
/// The outcome of [Repository::tree_index_status()].
#[derive(Clone)]
pub struct Outcome {
/// Additional information produced by the rename tracker.
///
/// It may be `None` if rename tracking was disabled.
pub rewrite: Option<gix_diff::rewrites::Outcome>,
/// The index produced from the input `tree` for the purpose of diffing.
///
/// At some point this might go away once it's possible to diff an index from a tree directly.
pub tree_index: gix_index::State,
}
impl Repository {
/// Produce the `git status` portion that shows the difference between `tree_id` (usually `HEAD^{tree}`) and the `worktree_index`
/// (typically the current `.git/index`), and pass all changes to `cb(change, tree_index, worktree_index)` with
/// full access to both indices that contributed to the change.
///
/// *(It's notable that internally, the `tree_id` is converted into an index before diffing these)*.
/// Set `pathspec` to `Some(_)` to further reduce the set of files to check.
///
/// ### Notes
///
/// * This is a low-level method - prefer the [`Repository::status()`] platform instead for access to various iterators
/// over the same information.
pub fn tree_index_status<'repo, E>(
&'repo self,
tree_id: &gix_hash::oid,
worktree_index: &gix_index::State,
pathspec: Option<&mut crate::Pathspec<'repo>>,
renames: TrackRenames,
mut cb: impl FnMut(
gix_diff::index::ChangeRef<'_, '_>,
&gix_index::State,
&gix_index::State,
) -> Result<gix_diff::index::Action, E>,
) -> Result<Outcome, Error>
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
let _span = gix_trace::coarse!("gix::tree_index_status");
let tree_index: gix_index::State = self.index_from_tree(tree_id)?.into();
let rewrites = match renames {
TrackRenames::AsConfigured => {
let (mut rewrites, mut is_configured) = crate::diff::utils::new_rewrites_inner(
&self.config.resolved,
self.config.lenient_config,
&tree::Status::RENAMES,
&tree::Status::RENAME_LIMIT,
)?;
if !is_configured {
(rewrites, is_configured) =
crate::diff::utils::new_rewrites(&self.config.resolved, self.config.lenient_config)?;
}
if !is_configured {
rewrites = Some(Default::default());
}
rewrites
}
TrackRenames::Given(rewrites) => Some(rewrites),
TrackRenames::Disabled => None,
};
let mut resource_cache = None;
if rewrites.is_some() {
resource_cache = Some(self.diff_resource_cache_for_tree_diff()?);
}
let mut pathspec_storage = None;
if pathspec.is_none() {
pathspec_storage = self
.pathspec(
true,
None::<&str>,
false,
&gix_index::State::new(self.object_hash()),
gix_worktree::stack::state::attributes::Source::IdMapping,
)
.expect("Impossible for this to fail without patterns")
.into();
}
let pathspec =
pathspec.unwrap_or_else(|| pathspec_storage.as_mut().expect("set if pathspec isn't set by user"));
let rewrite = gix_diff::index(
&tree_index,
worktree_index,
|change| cb(change, &tree_index, worktree_index),
rewrites
.zip(resource_cache.as_mut())
.map(|(rewrites, resource_cache)| gix_diff::index::RewriteOptions {
resource_cache,
find: self,
rewrites,
}),
&mut pathspec.search,
&mut |relative_path, case, is_dir, out| {
let stack = pathspec.stack.as_mut().expect("initialized in advance");
stack
.set_case(case)
.at_entry(
relative_path,
Some(crate::pathspec::is_dir_to_mode(is_dir)),
&pathspec.repo.objects,
)
.is_ok_and(|platform| platform.matching_attributes(out))
},
)?;
Ok(Outcome { rewrite, tree_index })
}
}