##// END OF EJS Templates
rust: implement `From<SparseConfigWarning>` for `HgError`...
Raphaël Gomès -
r52936:ae1ab6d7 default
parent child Browse files
Show More
@@ -1,115 +1,115
1 use std::path::Path;
1 use std::path::Path;
2
2
3 use crate::{
3 use crate::{
4 errors::HgError,
4 errors::HgError,
5 exit_codes,
5 exit_codes,
6 filepatterns::parse_pattern_file_contents,
6 filepatterns::parse_pattern_file_contents,
7 matchers::{
7 matchers::{
8 AlwaysMatcher, DifferenceMatcher, IncludeMatcher, Matcher,
8 AlwaysMatcher, DifferenceMatcher, IncludeMatcher, Matcher,
9 NeverMatcher,
9 NeverMatcher,
10 },
10 },
11 repo::Repo,
11 repo::Repo,
12 requirements::NARROW_REQUIREMENT,
12 requirements::NARROW_REQUIREMENT,
13 sparse::{self, SparseConfigError, SparseWarning},
13 sparse::{self, SparseConfigError, SparseWarning},
14 };
14 };
15
15
16 /// The file in .hg/store/ that indicates which paths exit in the store
16 /// The file in .hg/store/ that indicates which paths exit in the store
17 const FILENAME: &str = "narrowspec";
17 const FILENAME: &str = "narrowspec";
18 /// The file in .hg/ that indicates which paths exit in the dirstate
18 /// The file in .hg/ that indicates which paths exit in the dirstate
19 const DIRSTATE_FILENAME: &str = "narrowspec.dirstate";
19 const DIRSTATE_FILENAME: &str = "narrowspec.dirstate";
20
20
21 /// Pattern prefixes that are allowed in narrow patterns. This list MUST
21 /// Pattern prefixes that are allowed in narrow patterns. This list MUST
22 /// only contain patterns that are fast and safe to evaluate. Keep in mind
22 /// only contain patterns that are fast and safe to evaluate. Keep in mind
23 /// that patterns are supplied by clients and executed on remote servers
23 /// that patterns are supplied by clients and executed on remote servers
24 /// as part of wire protocol commands. That means that changes to this
24 /// as part of wire protocol commands. That means that changes to this
25 /// data structure influence the wire protocol and should not be taken
25 /// data structure influence the wire protocol and should not be taken
26 /// lightly - especially removals.
26 /// lightly - especially removals.
27 const VALID_PREFIXES: [&str; 2] = ["path:", "rootfilesin:"];
27 pub const VALID_PREFIXES: [&str; 2] = ["path:", "rootfilesin:"];
28
28
29 /// Return the matcher for the current narrow spec, and all configuration
29 /// Return the matcher for the current narrow spec, and all configuration
30 /// warnings to display.
30 /// warnings to display.
31 pub fn matcher(
31 pub fn matcher(
32 repo: &Repo,
32 repo: &Repo,
33 ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> {
33 ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> {
34 let mut warnings = vec![];
34 let mut warnings = vec![];
35 if !repo.requirements().contains(NARROW_REQUIREMENT) {
35 if !repo.requirements().contains(NARROW_REQUIREMENT) {
36 return Ok((Box::new(AlwaysMatcher), warnings));
36 return Ok((Box::new(AlwaysMatcher), warnings));
37 }
37 }
38 // Treat "narrowspec does not exist" the same as "narrowspec file exists
38 // Treat "narrowspec does not exist" the same as "narrowspec file exists
39 // and is empty".
39 // and is empty".
40 let store_spec = repo.store_vfs().try_read(FILENAME)?.unwrap_or_default();
40 let store_spec = repo.store_vfs().try_read(FILENAME)?.unwrap_or_default();
41 let working_copy_spec = repo
41 let working_copy_spec = repo
42 .hg_vfs()
42 .hg_vfs()
43 .try_read(DIRSTATE_FILENAME)?
43 .try_read(DIRSTATE_FILENAME)?
44 .unwrap_or_default();
44 .unwrap_or_default();
45 if store_spec != working_copy_spec {
45 if store_spec != working_copy_spec {
46 return Err(HgError::abort(
46 return Err(HgError::abort(
47 "abort: working copy's narrowspec is stale",
47 "abort: working copy's narrowspec is stale",
48 exit_codes::STATE_ERROR,
48 exit_codes::STATE_ERROR,
49 Some("run 'hg tracked --update-working-copy'".into()),
49 Some("run 'hg tracked --update-working-copy'".into()),
50 )
50 )
51 .into());
51 .into());
52 }
52 }
53
53
54 let config = sparse::parse_config(
54 let config = sparse::parse_config(
55 &store_spec,
55 &store_spec,
56 sparse::SparseConfigContext::Narrow,
56 sparse::SparseConfigContext::Narrow,
57 )?;
57 )?;
58
58
59 warnings.extend(config.warnings);
59 warnings.extend(config.warnings);
60
60
61 if !config.profiles.is_empty() {
61 if !config.profiles.is_empty() {
62 // TODO (from Python impl) maybe do something with profiles?
62 // TODO (from Python impl) maybe do something with profiles?
63 return Err(SparseConfigError::IncludesInNarrow);
63 return Err(SparseConfigError::IncludesInNarrow);
64 }
64 }
65 validate_patterns(&config.includes)?;
65 validate_patterns(&config.includes)?;
66 validate_patterns(&config.excludes)?;
66 validate_patterns(&config.excludes)?;
67
67
68 if config.includes.is_empty() {
68 if config.includes.is_empty() {
69 return Ok((Box::new(NeverMatcher), warnings));
69 return Ok((Box::new(NeverMatcher), warnings));
70 }
70 }
71
71
72 let (patterns, subwarnings) = parse_pattern_file_contents(
72 let (patterns, subwarnings) = parse_pattern_file_contents(
73 &config.includes,
73 &config.includes,
74 Path::new(""),
74 Path::new(""),
75 None,
75 None,
76 false,
76 false,
77 true,
77 true,
78 )?;
78 )?;
79 warnings.extend(subwarnings.into_iter().map(From::from));
79 warnings.extend(subwarnings.into_iter().map(From::from));
80
80
81 let mut m: Box<dyn Matcher + Sync> =
81 let mut m: Box<dyn Matcher + Sync> =
82 Box::new(IncludeMatcher::new(patterns)?);
82 Box::new(IncludeMatcher::new(patterns)?);
83
83
84 let (patterns, subwarnings) = parse_pattern_file_contents(
84 let (patterns, subwarnings) = parse_pattern_file_contents(
85 &config.excludes,
85 &config.excludes,
86 Path::new(""),
86 Path::new(""),
87 None,
87 None,
88 false,
88 false,
89 true,
89 true,
90 )?;
90 )?;
91 if !patterns.is_empty() {
91 if !patterns.is_empty() {
92 warnings.extend(subwarnings.into_iter().map(From::from));
92 warnings.extend(subwarnings.into_iter().map(From::from));
93 let exclude_matcher = Box::new(IncludeMatcher::new(patterns)?);
93 let exclude_matcher = Box::new(IncludeMatcher::new(patterns)?);
94 m = Box::new(DifferenceMatcher::new(m, exclude_matcher));
94 m = Box::new(DifferenceMatcher::new(m, exclude_matcher));
95 }
95 }
96
96
97 Ok((m, warnings))
97 Ok((m, warnings))
98 }
98 }
99
99
100 fn validate_patterns(patterns: &[u8]) -> Result<(), SparseConfigError> {
100 fn validate_patterns(patterns: &[u8]) -> Result<(), SparseConfigError> {
101 for pattern in patterns.split(|c| *c == b'\n') {
101 for pattern in patterns.split(|c| *c == b'\n') {
102 if pattern.is_empty() {
102 if pattern.is_empty() {
103 continue;
103 continue;
104 }
104 }
105 for prefix in VALID_PREFIXES.iter() {
105 for prefix in VALID_PREFIXES.iter() {
106 if pattern.starts_with(prefix.as_bytes()) {
106 if pattern.starts_with(prefix.as_bytes()) {
107 return Ok(());
107 return Ok(());
108 }
108 }
109 }
109 }
110 return Err(SparseConfigError::InvalidNarrowPrefix(
110 return Err(SparseConfigError::InvalidNarrowPrefix(
111 pattern.to_owned(),
111 pattern.to_owned(),
112 ));
112 ));
113 }
113 }
114 Ok(())
114 Ok(())
115 }
115 }
@@ -1,341 +1,405
1 use std::{collections::HashSet, path::Path};
1 use std::{collections::HashSet, fmt::Display, path::Path};
2
2
3 use format_bytes::{write_bytes, DisplayBytes};
3 use format_bytes::{format_bytes, write_bytes, DisplayBytes};
4
4
5 use crate::{
5 use crate::{
6 errors::HgError,
6 errors::HgError,
7 exit_codes::STATE_ERROR,
7 filepatterns::parse_pattern_file_contents,
8 filepatterns::parse_pattern_file_contents,
8 matchers::{
9 matchers::{
9 AlwaysMatcher, DifferenceMatcher, IncludeMatcher, Matcher,
10 AlwaysMatcher, DifferenceMatcher, IncludeMatcher, Matcher,
10 UnionMatcher,
11 UnionMatcher,
11 },
12 },
13 narrow::VALID_PREFIXES,
12 operations::cat,
14 operations::cat,
13 repo::Repo,
15 repo::Repo,
14 requirements::SPARSE_REQUIREMENT,
16 requirements::SPARSE_REQUIREMENT,
15 utils::{hg_path::HgPath, SliceExt},
17 utils::{hg_path::HgPath, SliceExt},
16 IgnorePattern, PatternError, PatternFileWarning, PatternSyntax, Revision,
18 IgnorePattern, PatternError, PatternFileWarning, PatternSyntax, Revision,
17 NULL_REVISION,
19 NULL_REVISION,
18 };
20 };
19
21
20 /// Command which is triggering the config read
22 /// Command which is triggering the config read
21 #[derive(Copy, Clone, Debug)]
23 #[derive(Copy, Clone, Debug)]
22 pub enum SparseConfigContext {
24 pub enum SparseConfigContext {
23 Sparse,
25 Sparse,
24 Narrow,
26 Narrow,
25 }
27 }
26
28
27 impl DisplayBytes for SparseConfigContext {
29 impl DisplayBytes for SparseConfigContext {
28 fn display_bytes(
30 fn display_bytes(
29 &self,
31 &self,
30 output: &mut dyn std::io::Write,
32 output: &mut dyn std::io::Write,
31 ) -> std::io::Result<()> {
33 ) -> std::io::Result<()> {
32 match self {
34 match self {
33 SparseConfigContext::Sparse => write_bytes!(output, b"sparse"),
35 SparseConfigContext::Sparse => write_bytes!(output, b"sparse"),
34 SparseConfigContext::Narrow => write_bytes!(output, b"narrow"),
36 SparseConfigContext::Narrow => write_bytes!(output, b"narrow"),
35 }
37 }
36 }
38 }
37 }
39 }
38
40
41 impl Display for SparseConfigContext {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 SparseConfigContext::Sparse => write!(f, "sparse"),
45 SparseConfigContext::Narrow => write!(f, "narrow"),
46 }
47 }
48 }
49
39 /// Possible warnings when reading sparse configuration
50 /// Possible warnings when reading sparse configuration
40 #[derive(Debug, derive_more::From)]
51 #[derive(Debug, derive_more::From)]
41 pub enum SparseWarning {
52 pub enum SparseWarning {
42 /// Warns about improper paths that start with "/"
53 /// Warns about improper paths that start with "/"
43 RootWarning {
54 RootWarning {
44 context: SparseConfigContext,
55 context: SparseConfigContext,
45 line: Vec<u8>,
56 line: Vec<u8>,
46 },
57 },
47 /// Warns about a profile missing from the given changelog revision
58 /// Warns about a profile missing from the given changelog revision
48 ProfileNotFound { profile: Vec<u8>, rev: Revision },
59 ProfileNotFound { profile: Vec<u8>, rev: Revision },
49 #[from]
60 #[from]
50 Pattern(PatternFileWarning),
61 Pattern(PatternFileWarning),
51 }
62 }
52
63
53 /// Parsed sparse config
64 /// Parsed sparse config
54 #[derive(Debug, Default)]
65 #[derive(Debug, Default)]
55 pub struct SparseConfig {
66 pub struct SparseConfig {
56 // Line-separated
67 // Line-separated
57 pub(crate) includes: Vec<u8>,
68 pub(crate) includes: Vec<u8>,
58 // Line-separated
69 // Line-separated
59 pub(crate) excludes: Vec<u8>,
70 pub(crate) excludes: Vec<u8>,
60 pub(crate) profiles: HashSet<Vec<u8>>,
71 pub(crate) profiles: HashSet<Vec<u8>>,
61 pub(crate) warnings: Vec<SparseWarning>,
72 pub(crate) warnings: Vec<SparseWarning>,
62 }
73 }
63
74
64 /// All possible errors when reading sparse/narrow config
75 /// All possible errors when reading sparse/narrow config
65 #[derive(Debug, derive_more::From)]
76 #[derive(Debug, derive_more::From)]
66 pub enum SparseConfigError {
77 pub enum SparseConfigError {
67 IncludesAfterExcludes {
78 IncludesAfterExcludes {
68 context: SparseConfigContext,
79 context: SparseConfigContext,
69 },
80 },
70 EntryOutsideSection {
81 EntryOutsideSection {
71 context: SparseConfigContext,
82 context: SparseConfigContext,
72 line: Vec<u8>,
83 line: Vec<u8>,
73 },
84 },
74 /// Narrow config does not support '%include' directives
85 /// Narrow config does not support '%include' directives
75 IncludesInNarrow,
86 IncludesInNarrow,
76 /// An invalid pattern prefix was given to the narrow spec. Includes the
87 /// An invalid pattern prefix was given to the narrow spec. Includes the
77 /// entire pattern for context.
88 /// entire pattern for context.
78 InvalidNarrowPrefix(Vec<u8>),
89 InvalidNarrowPrefix(Vec<u8>),
79 #[from]
90 #[from]
80 HgError(HgError),
91 HgError(HgError),
81 #[from]
92 #[from]
82 PatternError(PatternError),
93 PatternError(PatternError),
83 }
94 }
84
95
96 impl From<SparseConfigError> for HgError {
97 fn from(value: SparseConfigError) -> Self {
98 match value {
99 SparseConfigError::IncludesAfterExcludes { context } => {
100 HgError::Abort {
101 message: format!(
102 "{} config cannot have includes after excludes",
103 context,
104 ),
105 detailed_exit_code: STATE_ERROR,
106 hint: None,
107 }
108 }
109 SparseConfigError::EntryOutsideSection { context, line } => {
110 HgError::Abort {
111 message: format!(
112 "{} config entry outside of section: {}",
113 context,
114 String::from_utf8_lossy(&line)
115 ),
116 detailed_exit_code: STATE_ERROR,
117 hint: None,
118 }
119 }
120 SparseConfigError::IncludesInNarrow => HgError::Abort {
121 message: "including other spec files using '%include' is not \
122 supported in narrowspec"
123 .to_string(),
124 detailed_exit_code: STATE_ERROR,
125 hint: None,
126 },
127 SparseConfigError::InvalidNarrowPrefix(vec) => HgError::Abort {
128 message: String::from_utf8_lossy(&format_bytes!(
129 b"invalid prefix on narrow pattern: {}",
130 vec
131 ))
132 .to_string(),
133 detailed_exit_code: STATE_ERROR,
134 hint: Some(format!(
135 "narrow patterns must begin with one of the following: {}",
136 VALID_PREFIXES.join(", ")
137 )),
138 },
139 SparseConfigError::HgError(hg_error) => hg_error,
140 SparseConfigError::PatternError(pattern_error) => HgError::Abort {
141 message: pattern_error.to_string(),
142 detailed_exit_code: STATE_ERROR,
143 hint: None,
144 },
145 }
146 }
147 }
148
85 /// Parse sparse config file content.
149 /// Parse sparse config file content.
86 pub(crate) fn parse_config(
150 pub(crate) fn parse_config(
87 raw: &[u8],
151 raw: &[u8],
88 context: SparseConfigContext,
152 context: SparseConfigContext,
89 ) -> Result<SparseConfig, SparseConfigError> {
153 ) -> Result<SparseConfig, SparseConfigError> {
90 let mut includes = vec![];
154 let mut includes = vec![];
91 let mut excludes = vec![];
155 let mut excludes = vec![];
92 let mut profiles = HashSet::new();
156 let mut profiles = HashSet::new();
93 let mut warnings = vec![];
157 let mut warnings = vec![];
94
158
95 #[derive(PartialEq, Eq)]
159 #[derive(PartialEq, Eq)]
96 enum Current {
160 enum Current {
97 Includes,
161 Includes,
98 Excludes,
162 Excludes,
99 None,
163 None,
100 }
164 }
101
165
102 let mut current = Current::None;
166 let mut current = Current::None;
103 let mut in_section = false;
167 let mut in_section = false;
104
168
105 for line in raw.split(|c| *c == b'\n') {
169 for line in raw.split(|c| *c == b'\n') {
106 let line = line.trim();
170 let line = line.trim();
107 if line.is_empty() || line[0] == b'#' {
171 if line.is_empty() || line[0] == b'#' {
108 // empty or comment line, skip
172 // empty or comment line, skip
109 continue;
173 continue;
110 }
174 }
111 if line.starts_with(b"%include ") {
175 if line.starts_with(b"%include ") {
112 let profile = line[b"%include ".len()..].trim();
176 let profile = line[b"%include ".len()..].trim();
113 if !profile.is_empty() {
177 if !profile.is_empty() {
114 profiles.insert(profile.into());
178 profiles.insert(profile.into());
115 }
179 }
116 } else if line == b"[include]" {
180 } else if line == b"[include]" {
117 if in_section && current == Current::Includes {
181 if in_section && current == Current::Includes {
118 return Err(SparseConfigError::IncludesAfterExcludes {
182 return Err(SparseConfigError::IncludesAfterExcludes {
119 context,
183 context,
120 });
184 });
121 }
185 }
122 in_section = true;
186 in_section = true;
123 current = Current::Includes;
187 current = Current::Includes;
124 continue;
188 continue;
125 } else if line == b"[exclude]" {
189 } else if line == b"[exclude]" {
126 in_section = true;
190 in_section = true;
127 current = Current::Excludes;
191 current = Current::Excludes;
128 } else {
192 } else {
129 if current == Current::None {
193 if current == Current::None {
130 return Err(SparseConfigError::EntryOutsideSection {
194 return Err(SparseConfigError::EntryOutsideSection {
131 context,
195 context,
132 line: line.into(),
196 line: line.into(),
133 });
197 });
134 }
198 }
135 if line.trim().starts_with(b"/") {
199 if line.trim().starts_with(b"/") {
136 warnings.push(SparseWarning::RootWarning {
200 warnings.push(SparseWarning::RootWarning {
137 context,
201 context,
138 line: line.into(),
202 line: line.into(),
139 });
203 });
140 continue;
204 continue;
141 }
205 }
142 match current {
206 match current {
143 Current::Includes => {
207 Current::Includes => {
144 includes.push(b'\n');
208 includes.push(b'\n');
145 includes.extend(line.iter());
209 includes.extend(line.iter());
146 }
210 }
147 Current::Excludes => {
211 Current::Excludes => {
148 excludes.push(b'\n');
212 excludes.push(b'\n');
149 excludes.extend(line.iter());
213 excludes.extend(line.iter());
150 }
214 }
151 Current::None => unreachable!(),
215 Current::None => unreachable!(),
152 }
216 }
153 }
217 }
154 }
218 }
155
219
156 Ok(SparseConfig {
220 Ok(SparseConfig {
157 includes,
221 includes,
158 excludes,
222 excludes,
159 profiles,
223 profiles,
160 warnings,
224 warnings,
161 })
225 })
162 }
226 }
163
227
164 fn read_temporary_includes(
228 fn read_temporary_includes(
165 repo: &Repo,
229 repo: &Repo,
166 ) -> Result<Vec<Vec<u8>>, SparseConfigError> {
230 ) -> Result<Vec<Vec<u8>>, SparseConfigError> {
167 let raw = repo.hg_vfs().try_read("tempsparse")?.unwrap_or_default();
231 let raw = repo.hg_vfs().try_read("tempsparse")?.unwrap_or_default();
168 if raw.is_empty() {
232 if raw.is_empty() {
169 return Ok(vec![]);
233 return Ok(vec![]);
170 }
234 }
171 Ok(raw.split(|c| *c == b'\n').map(ToOwned::to_owned).collect())
235 Ok(raw.split(|c| *c == b'\n').map(ToOwned::to_owned).collect())
172 }
236 }
173
237
174 /// Obtain sparse checkout patterns for the given revision
238 /// Obtain sparse checkout patterns for the given revision
175 fn patterns_for_rev(
239 fn patterns_for_rev(
176 repo: &Repo,
240 repo: &Repo,
177 rev: Revision,
241 rev: Revision,
178 ) -> Result<Option<SparseConfig>, SparseConfigError> {
242 ) -> Result<Option<SparseConfig>, SparseConfigError> {
179 if !repo.has_sparse() {
243 if !repo.has_sparse() {
180 return Ok(None);
244 return Ok(None);
181 }
245 }
182 let raw = repo.hg_vfs().try_read("sparse")?.unwrap_or_default();
246 let raw = repo.hg_vfs().try_read("sparse")?.unwrap_or_default();
183
247
184 if raw.is_empty() {
248 if raw.is_empty() {
185 return Ok(None);
249 return Ok(None);
186 }
250 }
187
251
188 let mut config = parse_config(&raw, SparseConfigContext::Sparse)?;
252 let mut config = parse_config(&raw, SparseConfigContext::Sparse)?;
189
253
190 if !config.profiles.is_empty() {
254 if !config.profiles.is_empty() {
191 let mut profiles: Vec<Vec<u8>> = config.profiles.into_iter().collect();
255 let mut profiles: Vec<Vec<u8>> = config.profiles.into_iter().collect();
192 let mut visited = HashSet::new();
256 let mut visited = HashSet::new();
193
257
194 while let Some(profile) = profiles.pop() {
258 while let Some(profile) = profiles.pop() {
195 if visited.contains(&profile) {
259 if visited.contains(&profile) {
196 continue;
260 continue;
197 }
261 }
198 visited.insert(profile.to_owned());
262 visited.insert(profile.to_owned());
199
263
200 let output =
264 let output =
201 cat(repo, &rev.to_string(), vec![HgPath::new(&profile)])
265 cat(repo, &rev.to_string(), vec![HgPath::new(&profile)])
202 .map_err(|_| {
266 .map_err(|_| {
203 HgError::corrupted(
267 HgError::corrupted(
204 "dirstate points to non-existent parent node"
268 "dirstate points to non-existent parent node"
205 .to_string(),
269 .to_string(),
206 )
270 )
207 })?;
271 })?;
208 if output.results.is_empty() {
272 if output.results.is_empty() {
209 config.warnings.push(SparseWarning::ProfileNotFound {
273 config.warnings.push(SparseWarning::ProfileNotFound {
210 profile: profile.to_owned(),
274 profile: profile.to_owned(),
211 rev,
275 rev,
212 })
276 })
213 }
277 }
214
278
215 let subconfig = parse_config(
279 let subconfig = parse_config(
216 &output.results[0].1,
280 &output.results[0].1,
217 SparseConfigContext::Sparse,
281 SparseConfigContext::Sparse,
218 )?;
282 )?;
219 if !subconfig.includes.is_empty() {
283 if !subconfig.includes.is_empty() {
220 config.includes.push(b'\n');
284 config.includes.push(b'\n');
221 config.includes.extend(&subconfig.includes);
285 config.includes.extend(&subconfig.includes);
222 }
286 }
223 if !subconfig.includes.is_empty() {
287 if !subconfig.includes.is_empty() {
224 config.includes.push(b'\n');
288 config.includes.push(b'\n');
225 config.excludes.extend(&subconfig.excludes);
289 config.excludes.extend(&subconfig.excludes);
226 }
290 }
227 config.warnings.extend(subconfig.warnings.into_iter());
291 config.warnings.extend(subconfig.warnings.into_iter());
228 profiles.extend(subconfig.profiles.into_iter());
292 profiles.extend(subconfig.profiles.into_iter());
229 }
293 }
230
294
231 config.profiles = visited;
295 config.profiles = visited;
232 }
296 }
233
297
234 if !config.includes.is_empty() {
298 if !config.includes.is_empty() {
235 config.includes.extend(b"\n.hg*");
299 config.includes.extend(b"\n.hg*");
236 }
300 }
237
301
238 Ok(Some(config))
302 Ok(Some(config))
239 }
303 }
240
304
241 /// Obtain a matcher for sparse working directories.
305 /// Obtain a matcher for sparse working directories.
242 pub fn matcher(
306 pub fn matcher(
243 repo: &Repo,
307 repo: &Repo,
244 ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> {
308 ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> {
245 let mut warnings = vec![];
309 let mut warnings = vec![];
246 if !repo.requirements().contains(SPARSE_REQUIREMENT) {
310 if !repo.requirements().contains(SPARSE_REQUIREMENT) {
247 return Ok((Box::new(AlwaysMatcher), warnings));
311 return Ok((Box::new(AlwaysMatcher), warnings));
248 }
312 }
249
313
250 let parents = repo.dirstate_parents()?;
314 let parents = repo.dirstate_parents()?;
251 let mut revs = vec![];
315 let mut revs = vec![];
252 let p1_rev =
316 let p1_rev =
253 repo.changelog()?
317 repo.changelog()?
254 .rev_from_node(parents.p1.into())
318 .rev_from_node(parents.p1.into())
255 .map_err(|_| {
319 .map_err(|_| {
256 HgError::corrupted(
320 HgError::corrupted(
257 "dirstate points to non-existent parent node".to_string(),
321 "dirstate points to non-existent parent node".to_string(),
258 )
322 )
259 })?;
323 })?;
260 if p1_rev != NULL_REVISION {
324 if p1_rev != NULL_REVISION {
261 revs.push(p1_rev)
325 revs.push(p1_rev)
262 }
326 }
263 let p2_rev =
327 let p2_rev =
264 repo.changelog()?
328 repo.changelog()?
265 .rev_from_node(parents.p2.into())
329 .rev_from_node(parents.p2.into())
266 .map_err(|_| {
330 .map_err(|_| {
267 HgError::corrupted(
331 HgError::corrupted(
268 "dirstate points to non-existent parent node".to_string(),
332 "dirstate points to non-existent parent node".to_string(),
269 )
333 )
270 })?;
334 })?;
271 if p2_rev != NULL_REVISION {
335 if p2_rev != NULL_REVISION {
272 revs.push(p2_rev)
336 revs.push(p2_rev)
273 }
337 }
274 let mut matchers = vec![];
338 let mut matchers = vec![];
275
339
276 for rev in revs.iter() {
340 for rev in revs.iter() {
277 let config = patterns_for_rev(repo, *rev);
341 let config = patterns_for_rev(repo, *rev);
278 if let Ok(Some(config)) = config {
342 if let Ok(Some(config)) = config {
279 warnings.extend(config.warnings);
343 warnings.extend(config.warnings);
280 let mut m: Box<dyn Matcher + Sync> = Box::new(AlwaysMatcher);
344 let mut m: Box<dyn Matcher + Sync> = Box::new(AlwaysMatcher);
281 if !config.includes.is_empty() {
345 if !config.includes.is_empty() {
282 let (patterns, subwarnings) = parse_pattern_file_contents(
346 let (patterns, subwarnings) = parse_pattern_file_contents(
283 &config.includes,
347 &config.includes,
284 Path::new(""),
348 Path::new(""),
285 Some(PatternSyntax::Glob),
349 Some(PatternSyntax::Glob),
286 false,
350 false,
287 false,
351 false,
288 )?;
352 )?;
289 warnings.extend(subwarnings.into_iter().map(From::from));
353 warnings.extend(subwarnings.into_iter().map(From::from));
290 m = Box::new(IncludeMatcher::new(patterns)?);
354 m = Box::new(IncludeMatcher::new(patterns)?);
291 }
355 }
292 if !config.excludes.is_empty() {
356 if !config.excludes.is_empty() {
293 let (patterns, subwarnings) = parse_pattern_file_contents(
357 let (patterns, subwarnings) = parse_pattern_file_contents(
294 &config.excludes,
358 &config.excludes,
295 Path::new(""),
359 Path::new(""),
296 Some(PatternSyntax::Glob),
360 Some(PatternSyntax::Glob),
297 false,
361 false,
298 false,
362 false,
299 )?;
363 )?;
300 warnings.extend(subwarnings.into_iter().map(From::from));
364 warnings.extend(subwarnings.into_iter().map(From::from));
301 m = Box::new(DifferenceMatcher::new(
365 m = Box::new(DifferenceMatcher::new(
302 m,
366 m,
303 Box::new(IncludeMatcher::new(patterns)?),
367 Box::new(IncludeMatcher::new(patterns)?),
304 ));
368 ));
305 }
369 }
306 matchers.push(m);
370 matchers.push(m);
307 }
371 }
308 }
372 }
309 let result: Box<dyn Matcher + Sync> = match matchers.len() {
373 let result: Box<dyn Matcher + Sync> = match matchers.len() {
310 0 => Box::new(AlwaysMatcher),
374 0 => Box::new(AlwaysMatcher),
311 1 => matchers.pop().expect("1 is equal to 0"),
375 1 => matchers.pop().expect("1 is equal to 0"),
312 _ => Box::new(UnionMatcher::new(matchers)),
376 _ => Box::new(UnionMatcher::new(matchers)),
313 };
377 };
314
378
315 let matcher =
379 let matcher =
316 force_include_matcher(result, &read_temporary_includes(repo)?)?;
380 force_include_matcher(result, &read_temporary_includes(repo)?)?;
317 Ok((matcher, warnings))
381 Ok((matcher, warnings))
318 }
382 }
319
383
320 /// Returns a matcher that returns true for any of the forced includes before
384 /// Returns a matcher that returns true for any of the forced includes before
321 /// testing against the actual matcher
385 /// testing against the actual matcher
322 fn force_include_matcher(
386 fn force_include_matcher(
323 result: Box<dyn Matcher + Sync>,
387 result: Box<dyn Matcher + Sync>,
324 temp_includes: &[Vec<u8>],
388 temp_includes: &[Vec<u8>],
325 ) -> Result<Box<dyn Matcher + Sync>, PatternError> {
389 ) -> Result<Box<dyn Matcher + Sync>, PatternError> {
326 if temp_includes.is_empty() {
390 if temp_includes.is_empty() {
327 return Ok(result);
391 return Ok(result);
328 }
392 }
329 let forced_include_matcher = IncludeMatcher::new(
393 let forced_include_matcher = IncludeMatcher::new(
330 temp_includes
394 temp_includes
331 .iter()
395 .iter()
332 .map(|include| {
396 .map(|include| {
333 IgnorePattern::new(PatternSyntax::Path, include, Path::new(""))
397 IgnorePattern::new(PatternSyntax::Path, include, Path::new(""))
334 })
398 })
335 .collect(),
399 .collect(),
336 )?;
400 )?;
337 Ok(Box::new(UnionMatcher::new(vec![
401 Ok(Box::new(UnionMatcher::new(vec![
338 Box::new(forced_include_matcher),
402 Box::new(forced_include_matcher),
339 result,
403 result,
340 ])))
404 ])))
341 }
405 }
General Comments 0
You need to be logged in to leave comments. Login now