]> git.proxmox.com Git - rustc.git/blob - vendor/gix-worktree/src/cache/state/mod.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / vendor / gix-worktree / src / cache / state / mod.rs
1 use std::path::PathBuf;
2
3 use bstr::{BString, ByteSlice};
4 use gix_glob::pattern::Case;
5
6 use crate::{cache::State, PathIdMapping};
7
8 type AttributeMatchGroup = gix_attributes::Search;
9 type IgnoreMatchGroup = gix_ignore::Search;
10
11 /// State related to attributes associated with files in the repository.
12 #[derive(Default, Clone)]
13 pub struct Attributes {
14 /// Attribute patterns which aren't tied to the repository root, hence are global, they contribute first.
15 globals: AttributeMatchGroup,
16 /// Attribute patterns that match the currently set directory (in the stack).
17 ///
18 /// Note that the root-level file is always loaded, if present, followed by, the `$GIT_DIR/info/attributes`, if present, based
19 /// on the location of the `info_attributes` file.
20 stack: AttributeMatchGroup,
21 /// The first time we push the root, we have to load additional information from this file if it exists along with the root attributes
22 /// file if possible, and keep them there throughout.
23 info_attributes: Option<PathBuf>,
24 /// A lookup table to accelerate searches.
25 collection: gix_attributes::search::MetadataCollection,
26 /// Where to read `.gitattributes` data from.
27 source: attributes::Source,
28 }
29
30 /// State related to the exclusion of files, supporting static overrides and globals, along with a stack of dynamically read
31 /// ignore files from disk or from the index each time the directory changes.
32 #[derive(Default, Clone)]
33 #[allow(unused)]
34 pub struct Ignore {
35 /// Ignore patterns passed as overrides to everything else, typically passed on the command-line and the first patterns to
36 /// be consulted.
37 overrides: IgnoreMatchGroup,
38 /// Ignore patterns that match the currently set director (in the stack), which is pushed and popped as needed.
39 stack: IgnoreMatchGroup,
40 /// Ignore patterns which aren't tied to the repository root, hence are global. They are consulted last.
41 globals: IgnoreMatchGroup,
42 /// A matching stack of pattern indices which is empty if we have just been initialized to indicate that the
43 /// currently set directory had a pattern matched. Note that this one could be negated.
44 /// (index into match groups, index into list of pattern lists, index into pattern list)
45 matched_directory_patterns_stack: Vec<Option<(usize, usize, usize)>>,
46 /// The name of the file to look for in directories.
47 pub(crate) exclude_file_name_for_directories: BString,
48 /// Where to read ignore files from
49 source: ignore::Source,
50 }
51
52 ///
53 pub mod attributes;
54 ///
55 pub mod ignore;
56
57 /// Initialization
58 impl State {
59 /// Configure a state to be suitable for checking out files, which only needs access to attribute files read from the index.
60 pub fn for_checkout(unlink_on_collision: bool, attributes: Attributes) -> Self {
61 State::CreateDirectoryAndAttributesStack {
62 unlink_on_collision,
63 attributes,
64 }
65 }
66
67 /// Configure a state for adding files, with support for ignore files and attribute files.
68 pub fn for_add(attributes: Attributes, ignore: Ignore) -> Self {
69 State::AttributesAndIgnoreStack { attributes, ignore }
70 }
71
72 /// Configure a state for status retrieval, which needs access to ignore files only.
73 pub fn for_status(ignore: Ignore) -> Self {
74 State::IgnoreStack(ignore)
75 }
76 }
77
78 /// Utilities
79 impl State {
80 /// Returns a vec of tuples of relative index paths along with the best usable blob OID for
81 /// either *ignore* or *attribute* files or both. This allows files to be accessed directly from
82 /// the object database without the need for a worktree checkout.
83 ///
84 /// Note that this method…
85 /// - ignores entries which aren't blobs.
86 /// - ignores ignore entries which are not skip-worktree.
87 /// - within merges, picks 'our' stage both for *ignore* and *attribute* files.
88 ///
89 /// * `index` is where we look for suitable files by path in order to obtain their blob hash.
90 /// * `paths` is the indices storage backend for paths.
91 /// * `case` determines if the search for files should be case-sensitive or not.
92 pub fn id_mappings_from_index(
93 &self,
94 index: &gix_index::State,
95 paths: &gix_index::PathStorageRef,
96 ignore_source: ignore::Source,
97 case: Case,
98 ) -> Vec<PathIdMapping> {
99 let a1_backing;
100 let a2_backing;
101 let names = match self {
102 State::IgnoreStack(v) => {
103 a1_backing = [(v.exclude_file_name_for_directories.as_bytes().as_bstr(), true)];
104 a1_backing.as_ref()
105 }
106 State::AttributesAndIgnoreStack { ignore, .. } => {
107 a2_backing = [
108 (ignore.exclude_file_name_for_directories.as_bytes().as_bstr(), true),
109 (".gitattributes".into(), false),
110 ];
111 a2_backing.as_ref()
112 }
113 State::CreateDirectoryAndAttributesStack { .. } => {
114 a1_backing = [(".gitattributes".into(), true)];
115 a1_backing.as_ref()
116 }
117 };
118
119 index
120 .entries()
121 .iter()
122 .filter_map(move |entry| {
123 let path = entry.path_in(paths);
124
125 // Stage 0 means there is no merge going on, stage 2 means it's 'our' side of the merge, but then
126 // there won't be a stage 0.
127 if entry.mode == gix_index::entry::Mode::FILE && (entry.stage() == 0 || entry.stage() == 2) {
128 let basename = path
129 .rfind_byte(b'/')
130 .map(|pos| path[pos + 1..].as_bstr())
131 .unwrap_or(path);
132 let is_ignore = names.iter().find_map(|t| {
133 match case {
134 Case::Sensitive => basename == t.0,
135 Case::Fold => basename.eq_ignore_ascii_case(t.0),
136 }
137 .then_some(t.1)
138 })?;
139 if is_ignore {
140 match ignore_source {
141 ignore::Source::IdMapping => {}
142 ignore::Source::WorktreeThenIdMappingIfNotSkipped => {
143 // See https://github.com/git/git/blob/master/dir.c#L912:L912
144 if !entry.flags.contains(gix_index::entry::Flags::SKIP_WORKTREE) {
145 return None;
146 }
147 }
148 };
149 }
150 Some((path.to_owned(), entry.id))
151 } else {
152 None
153 }
154 })
155 .collect()
156 }
157
158 pub(crate) fn ignore_or_panic(&self) -> &Ignore {
159 match self {
160 State::IgnoreStack(v) => v,
161 State::AttributesAndIgnoreStack { ignore, .. } => ignore,
162 State::CreateDirectoryAndAttributesStack { .. } => {
163 unreachable!("BUG: must not try to check excludes without it being setup")
164 }
165 }
166 }
167
168 pub(crate) fn attributes_or_panic(&self) -> &Attributes {
169 match self {
170 State::AttributesAndIgnoreStack { attributes, .. }
171 | State::CreateDirectoryAndAttributesStack { attributes, .. } => attributes,
172 State::IgnoreStack(_) => {
173 unreachable!("BUG: must not try to check excludes without it being setup")
174 }
175 }
176 }
177 }