|
| 1 | +use crate::Repository; |
| 2 | +pub use gix_status as plumbing; |
| 3 | + |
| 4 | +/// A structure to hold options configuring the status request, which can then be turned into an iterator. |
| 5 | +#[derive(Clone)] |
| 6 | +pub struct Platform<'repo> { |
| 7 | + _repo: &'repo Repository, |
| 8 | +} |
| 9 | + |
| 10 | +/// Status |
| 11 | +impl Repository { |
| 12 | + /// Obtain a platform for configuring and traversing git repository status information. |
| 13 | + /// |
| 14 | + /// By default, this is set to the fastest and most immediate way of obtaining a status, |
| 15 | + /// which is most similar to |
| 16 | + /// `git status --untracked=all --ignored=no --no-renames --ignore-submodules=all` |
| 17 | + /// |
| 18 | + /// ### Deviation |
| 19 | + /// |
| 20 | + /// Whereas Git runs the index-modified check before the directory walk to set entries |
| 21 | + /// as up-to-date to (potentially) safe some disk-access, we run both in parallel which |
| 22 | + /// ultimately is much faster. |
| 23 | + // TODO: if untracked and ignored entries are disabled, don't run a dirwalk at all. |
| 24 | + // TODO: submodule support with reasonable configurability. |
| 25 | + pub fn status(&self) -> Platform<'_> { |
| 26 | + Platform { _repo: self } |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +/// |
| 31 | +pub mod index_worktree { |
| 32 | + use crate::bstr::BStr; |
| 33 | + use crate::{config, Repository}; |
| 34 | + use gix_status::index_as_worktree::traits::{CompareBlobs, SubmoduleStatus}; |
| 35 | + use std::sync::atomic::AtomicBool; |
| 36 | + |
| 37 | + /// The error returned by [Repository::index_worktree_status()]. |
| 38 | + #[derive(Debug, thiserror::Error)] |
| 39 | + #[allow(missing_docs)] |
| 40 | + pub enum Error { |
| 41 | + #[error("A working tree is required to perform a directory walk")] |
| 42 | + MissingWorkDir, |
| 43 | + #[error(transparent)] |
| 44 | + AttributesAndExcludes(#[from] crate::repository::attributes::Error), |
| 45 | + #[error(transparent)] |
| 46 | + Pathspec(#[from] crate::pathspec::init::Error), |
| 47 | + #[error(transparent)] |
| 48 | + Prefix(#[from] gix_path::realpath::Error), |
| 49 | + #[error(transparent)] |
| 50 | + FilesystemOptions(#[from] config::boolean::Error), |
| 51 | + #[error(transparent)] |
| 52 | + IndexAsWorktreeWithRenames(#[from] gix_status::index_as_worktree_with_renames::Error), |
| 53 | + #[error(transparent)] |
| 54 | + StatOptions(#[from] config::stat_options::Error), |
| 55 | + #[error(transparent)] |
| 56 | + ResourceCache(#[from] crate::diff::resource_cache::Error), |
| 57 | + } |
| 58 | + |
| 59 | + /// Options for use with [Repository::index_worktree_status()]. |
| 60 | + #[derive(Debug, Clone, Copy, PartialEq)] |
| 61 | + pub struct Options { |
| 62 | + /// The way all output should be sorted. |
| 63 | + /// |
| 64 | + /// If `None`, and depending on the `rewrites` field, output will be immediate but the output order |
| 65 | + /// isn't determined, and may differ between two runs. `rewrites` also depend on the order of entries that |
| 66 | + /// are presented to it, hence for deterministic results, sorting needs to be enabled. |
| 67 | + /// |
| 68 | + /// If `Some(_)`, all entries are collected beforehand, so they can be sorted before outputting any of them |
| 69 | + /// to the user. |
| 70 | + /// |
| 71 | + /// If immediate output of entries in any order is desired, this should be `None`, |
| 72 | + /// along with `rewrites` being `None` as well. |
| 73 | + pub sorting: Option<gix_status::index_as_worktree_with_renames::Sorting>, |
| 74 | + /// If not `None`, the options to configure the directory walk, determining how its results will look like. |
| 75 | + /// |
| 76 | + /// If `None`, only modification checks are performed. |
| 77 | + /// |
| 78 | + /// Can be instantiated with [Repository::dirwalk_options()]. |
| 79 | + pub dirwalk_options: Option<crate::dirwalk::Options>, |
| 80 | + /// If not `None`, along with configured `dirwalk_options`, rewrite tracking will be performed between the |
| 81 | + /// index and the working tree. |
| 82 | + /// Note that there is no git-configuration specific to index-worktree rename tracking. |
| 83 | + pub rewrites: Option<gix_diff::Rewrites>, |
| 84 | + /// If set, don't use more than this amount of threads for the tracked modification check. |
| 85 | + /// Otherwise, usually use as many threads as there are logical cores. |
| 86 | + /// A value of 0 is interpreted as no-limit |
| 87 | + pub thread_limit: Option<usize>, |
| 88 | + } |
| 89 | + |
| 90 | + impl Repository { |
| 91 | + /// Obtain the status between the index and the worktree, involving modification checks |
| 92 | + /// for all tracked files along with information about untracked (and posisbly ignored) files (if configured). |
| 93 | + /// |
| 94 | + /// * `index` |
| 95 | + /// - The index to use for modification checks, and to know which files are tacked when applying the dirwalk. |
| 96 | + /// * `patterns` |
| 97 | + /// - Optional patterns to use to limit the paths to look at. If empty, all paths are considered. |
| 98 | + /// * `delegate` |
| 99 | + /// - The sink for receiving all status data. |
| 100 | + /// * `compare` and `submodule` |
| 101 | + /// - Implementations for fine-grained control over what happens if a hash must be recalculated, or submodule |
| 102 | + /// information must be retrieved. |
| 103 | + /// * `progress` |
| 104 | + /// - A progress indication for index modification checks. |
| 105 | + /// * `should_interrupt` |
| 106 | + /// - A flag to stop the whole operation. |
| 107 | + /// * `options` |
| 108 | + /// - Additional configuration for all parts of the operation. |
| 109 | + /// |
| 110 | + /// ### Note |
| 111 | + /// |
| 112 | + /// This is a lower-level method, prefer the [`status`](Repository::status()) method for greater ease of use. |
| 113 | + #[allow(clippy::too_many_arguments)] |
| 114 | + pub fn index_worktree_status<'index, T, U, E>( |
| 115 | + &self, |
| 116 | + index: &'index gix_index::State, |
| 117 | + patterns: impl IntoIterator<Item = impl AsRef<BStr>>, |
| 118 | + delegate: &mut impl gix_status::index_as_worktree_with_renames::VisitEntry< |
| 119 | + 'index, |
| 120 | + ContentChange = T, |
| 121 | + SubmoduleStatus = U, |
| 122 | + >, |
| 123 | + compare: impl CompareBlobs<Output = T> + Send + Clone, |
| 124 | + submodule: impl SubmoduleStatus<Output = U, Error = E> + Send + Clone, |
| 125 | + progress: &mut dyn gix_features::progress::Progress, |
| 126 | + should_interrupt: &AtomicBool, |
| 127 | + options: Options, |
| 128 | + ) -> Result<gix_status::index_as_worktree_with_renames::Outcome, Error> |
| 129 | + where |
| 130 | + T: Send + Clone, |
| 131 | + U: Send + Clone, |
| 132 | + E: std::error::Error + Send + Sync + 'static, |
| 133 | + { |
| 134 | + let _span = gix_trace::coarse!("gix::index_worktree_status"); |
| 135 | + let workdir = self.work_dir().ok_or(Error::MissingWorkDir)?; |
| 136 | + let attrs_and_excludes = self.attributes( |
| 137 | + index, |
| 138 | + crate::worktree::stack::state::attributes::Source::WorktreeThenIdMapping, |
| 139 | + crate::worktree::stack::state::ignore::Source::WorktreeThenIdMappingIfNotSkipped, |
| 140 | + None, |
| 141 | + )?; |
| 142 | + let pathspec = crate::Pathspec::new( |
| 143 | + self, |
| 144 | + options |
| 145 | + .dirwalk_options |
| 146 | + .as_ref() |
| 147 | + .map_or(false, |opts| opts.empty_patterns_match_prefix), |
| 148 | + patterns, |
| 149 | + true, /* inherit ignore case */ |
| 150 | + || Ok(attrs_and_excludes.clone()), |
| 151 | + )?; |
| 152 | + |
| 153 | + let cwd = self.current_dir(); |
| 154 | + let git_dir_realpath = |
| 155 | + crate::path::realpath_opts(self.git_dir(), cwd, crate::path::realpath::MAX_SYMLINKS)?; |
| 156 | + let fs_caps = self.filesystem_options()?; |
| 157 | + let accelerate_lookup = fs_caps.ignore_case.then(|| index.prepare_icase_backing()); |
| 158 | + let resource_cache = crate::diff::resource_cache( |
| 159 | + self, |
| 160 | + gix_diff::blob::pipeline::Mode::ToGit, |
| 161 | + attrs_and_excludes.inner, |
| 162 | + gix_diff::blob::pipeline::WorktreeRoots { |
| 163 | + old_root: None, |
| 164 | + new_root: Some(workdir.to_owned()), |
| 165 | + }, |
| 166 | + )?; |
| 167 | + |
| 168 | + let out = gix_status::index_as_worktree_with_renames( |
| 169 | + index, |
| 170 | + workdir, |
| 171 | + delegate, |
| 172 | + compare, |
| 173 | + submodule, |
| 174 | + self.objects.clone().into_arc().expect("arc conversion always works"), |
| 175 | + progress, |
| 176 | + gix_status::index_as_worktree_with_renames::Context { |
| 177 | + pathspec: pathspec.search, |
| 178 | + resource_cache, |
| 179 | + should_interrupt, |
| 180 | + dirwalk: gix_status::index_as_worktree_with_renames::DirwalkContext { |
| 181 | + git_dir_realpath: git_dir_realpath.as_path(), |
| 182 | + current_dir: cwd, |
| 183 | + ignore_case_index_lookup: accelerate_lookup.as_ref(), |
| 184 | + }, |
| 185 | + }, |
| 186 | + gix_status::index_as_worktree_with_renames::Options { |
| 187 | + sorting: options.sorting, |
| 188 | + object_hash: self.object_hash(), |
| 189 | + tracked_file_modifications: gix_status::index_as_worktree::Options { |
| 190 | + fs: fs_caps, |
| 191 | + thread_limit: options.thread_limit, |
| 192 | + stat: self.stat_options()?, |
| 193 | + }, |
| 194 | + dirwalk: options.dirwalk_options.map(Into::into), |
| 195 | + rewrites: options.rewrites, |
| 196 | + }, |
| 197 | + )?; |
| 198 | + Ok(out) |
| 199 | + } |
| 200 | + } |
| 201 | +} |
0 commit comments