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
use crate::{Repository, Result, config::tree};
/// 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>(
&'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>,
) -> Result<Outcome> {
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 })
}
}