Mercurial > public > mercurial-scm > hg
annotate rust/hg-core/src/sparse.rs @ 52280:f4aede0f01af
rust-manifest: use `memchr` crate for all byte-finding needs
While writing a very dumb manifest diffing algorithm for a proof-of-concept
I saw that `Manifest::find_by_path` was much slower than I was expecting.
It turns out that the Rust stdlib uses slow (all is relative) code when
searching for byte positions for reasons ranging from portability, SIMD
API stability, nobody doing the work, etc. `memch` is much faster for these
purposes, so let's use it.
I was measuring ~670ms of profile time in `find_by_path`, after this patch
it went down to ~230ms.
author | Rapha?l Gom?s <rgomes@octobus.net> |
---|---|
date | Tue, 12 Nov 2024 23:20:04 +0100 |
parents | ae1ab6d71f4a |
children | f33b87b46135 77b95a4abbb2 |
rev | line source |
---|---|
52039
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
1 use std::{collections::HashSet, fmt::Display, path::Path}; |
49485 | 2 |
52039
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
3 use format_bytes::{format_bytes, write_bytes, DisplayBytes}; |
49485 | 4 |
5 use crate::{ | |
6 errors::HgError, | |
52039
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
7 exit_codes::STATE_ERROR, |
49485 | 8 filepatterns::parse_pattern_file_contents, |
9 matchers::{ | |
10 AlwaysMatcher, DifferenceMatcher, IncludeMatcher, Matcher, | |
11 UnionMatcher, | |
12 }, | |
52039
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
13 narrow::VALID_PREFIXES, |
49485 | 14 operations::cat, |
15 repo::Repo, | |
16 requirements::SPARSE_REQUIREMENT, | |
17 utils::{hg_path::HgPath, SliceExt}, | |
18 IgnorePattern, PatternError, PatternFileWarning, PatternSyntax, Revision, | |
19 NULL_REVISION, | |
20 }; | |
21 | |
22 /// Command which is triggering the config read | |
23 #[derive(Copy, Clone, Debug)] | |
24 pub enum SparseConfigContext { | |
25 Sparse, | |
26 Narrow, | |
27 } | |
28 | |
29 impl DisplayBytes for SparseConfigContext { | |
30 fn display_bytes( | |
31 &self, | |
32 output: &mut dyn std::io::Write, | |
33 ) -> std::io::Result<()> { | |
34 match self { | |
35 SparseConfigContext::Sparse => write_bytes!(output, b"sparse"), | |
36 SparseConfigContext::Narrow => write_bytes!(output, b"narrow"), | |
37 } | |
38 } | |
39 } | |
40 | |
52039
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
41 impl Display for SparseConfigContext { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
43 match self { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
44 SparseConfigContext::Sparse => write!(f, "sparse"), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
45 SparseConfigContext::Narrow => write!(f, "narrow"), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
46 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
47 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
48 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
49 |
49485 | 50 /// Possible warnings when reading sparse configuration |
51 #[derive(Debug, derive_more::From)] | |
52 pub enum SparseWarning { | |
53 /// Warns about improper paths that start with "/" | |
54 RootWarning { | |
55 context: SparseConfigContext, | |
56 line: Vec<u8>, | |
57 }, | |
58 /// Warns about a profile missing from the given changelog revision | |
59 ProfileNotFound { profile: Vec<u8>, rev: Revision }, | |
60 #[from] | |
61 Pattern(PatternFileWarning), | |
62 } | |
63 | |
64 /// Parsed sparse config | |
65 #[derive(Debug, Default)] | |
66 pub struct SparseConfig { | |
67 // Line-separated | |
49489
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
68 pub(crate) includes: Vec<u8>, |
49485 | 69 // Line-separated |
49489
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
70 pub(crate) excludes: Vec<u8>, |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
71 pub(crate) profiles: HashSet<Vec<u8>>, |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
72 pub(crate) warnings: Vec<SparseWarning>, |
49485 | 73 } |
74 | |
49489
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
75 /// All possible errors when reading sparse/narrow config |
49485 | 76 #[derive(Debug, derive_more::From)] |
77 pub enum SparseConfigError { | |
78 IncludesAfterExcludes { | |
79 context: SparseConfigContext, | |
80 }, | |
81 EntryOutsideSection { | |
82 context: SparseConfigContext, | |
83 line: Vec<u8>, | |
84 }, | |
49489
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
85 /// Narrow config does not support '%include' directives |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
86 IncludesInNarrow, |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
87 /// An invalid pattern prefix was given to the narrow spec. Includes the |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
88 /// entire pattern for context. |
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
89 InvalidNarrowPrefix(Vec<u8>), |
49485 | 90 #[from] |
91 HgError(HgError), | |
92 #[from] | |
93 PatternError(PatternError), | |
94 } | |
95 | |
52039
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
96 impl From<SparseConfigError> for HgError { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
97 fn from(value: SparseConfigError) -> Self { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
98 match value { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
99 SparseConfigError::IncludesAfterExcludes { context } => { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
100 HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
101 message: format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
102 "{} config cannot have includes after excludes", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
103 context, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
104 ), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
105 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
106 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
107 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
108 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
109 SparseConfigError::EntryOutsideSection { context, line } => { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
110 HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
111 message: format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
112 "{} config entry outside of section: {}", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
113 context, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
114 String::from_utf8_lossy(&line) |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
115 ), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
116 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
117 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
118 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
119 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
120 SparseConfigError::IncludesInNarrow => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
121 message: "including other spec files using '%include' is not \ |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
122 supported in narrowspec" |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
123 .to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
124 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
125 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
126 }, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
127 SparseConfigError::InvalidNarrowPrefix(vec) => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
128 message: String::from_utf8_lossy(&format_bytes!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
129 b"invalid prefix on narrow pattern: {}", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
130 vec |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
131 )) |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
132 .to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
133 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
134 hint: Some(format!( |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
135 "narrow patterns must begin with one of the following: {}", |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
136 VALID_PREFIXES.join(", ") |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
137 )), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
138 }, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
139 SparseConfigError::HgError(hg_error) => hg_error, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
140 SparseConfigError::PatternError(pattern_error) => HgError::Abort { |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
141 message: pattern_error.to_string(), |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
142 detailed_exit_code: STATE_ERROR, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
143 hint: None, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
144 }, |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
145 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
146 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
147 } |
ae1ab6d71f4a
rust: implement `From<SparseConfigWarning>` for `HgError`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
50854
diff
changeset
|
148 |
49485 | 149 /// Parse sparse config file content. |
49489
7c93e38a0bbd
rhg-status: add support for narrow clones
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49485
diff
changeset
|
150 pub(crate) fn parse_config( |
49485 | 151 raw: &[u8], |
152 context: SparseConfigContext, | |
153 ) -> Result<SparseConfig, SparseConfigError> { | |
154 let mut includes = vec![]; | |
155 let mut excludes = vec![]; | |
156 let mut profiles = HashSet::new(); | |
157 let mut warnings = vec![]; | |
158 | |
159 #[derive(PartialEq, Eq)] | |
160 enum Current { | |
161 Includes, | |
162 Excludes, | |
163 None, | |
49517
52464a20add0
rhg: parallellize computation of [unsure_is_modified]
Arseniy Alekseyev <aalekseyev@janestreet.com>
parents:
49489
diff
changeset
|
164 } |
49485 | 165 |
166 let mut current = Current::None; | |
167 let mut in_section = false; | |
168 | |
169 for line in raw.split(|c| *c == b'\n') { | |
170 let line = line.trim(); | |
171 if line.is_empty() || line[0] == b'#' { | |
172 // empty or comment line, skip | |
173 continue; | |
174 } | |
175 if line.starts_with(b"%include ") { | |
176 let profile = line[b"%include ".len()..].trim(); | |
177 if !profile.is_empty() { | |
178 profiles.insert(profile.into()); | |
179 } | |
180 } else if line == b"[include]" { | |
181 if in_section && current == Current::Includes { | |
182 return Err(SparseConfigError::IncludesAfterExcludes { | |
183 context, | |
184 }); | |
185 } | |
186 in_section = true; | |
187 current = Current::Includes; | |
188 continue; | |
189 } else if line == b"[exclude]" { | |
190 in_section = true; | |
191 current = Current::Excludes; | |
192 } else { | |
193 if current == Current::None { | |
194 return Err(SparseConfigError::EntryOutsideSection { | |
195 context, | |
196 line: line.into(), | |
197 }); | |
198 } | |
199 if line.trim().starts_with(b"/") { | |
200 warnings.push(SparseWarning::RootWarning { | |
201 context, | |
202 line: line.into(), | |
203 }); | |
204 continue; | |
205 } | |
206 match current { | |
207 Current::Includes => { | |
208 includes.push(b'\n'); | |
209 includes.extend(line.iter()); | |
210 } | |
211 Current::Excludes => { | |
212 excludes.push(b'\n'); | |
213 excludes.extend(line.iter()); | |
214 } | |
215 Current::None => unreachable!(), | |
216 } | |
217 } | |
218 } | |
219 | |
220 Ok(SparseConfig { | |
221 includes, | |
222 excludes, | |
223 profiles, | |
224 warnings, | |
225 }) | |
226 } | |
227 | |
228 fn read_temporary_includes( | |
229 repo: &Repo, | |
230 ) -> Result<Vec<Vec<u8>>, SparseConfigError> { | |
49930
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
231 let raw = repo.hg_vfs().try_read("tempsparse")?.unwrap_or_default(); |
49485 | 232 if raw.is_empty() { |
233 return Ok(vec![]); | |
234 } | |
235 Ok(raw.split(|c| *c == b'\n').map(ToOwned::to_owned).collect()) | |
236 } | |
237 | |
238 /// Obtain sparse checkout patterns for the given revision | |
239 fn patterns_for_rev( | |
240 repo: &Repo, | |
241 rev: Revision, | |
242 ) -> Result<Option<SparseConfig>, SparseConfigError> { | |
243 if !repo.has_sparse() { | |
244 return Ok(None); | |
245 } | |
49930
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
246 let raw = repo.hg_vfs().try_read("sparse")?.unwrap_or_default(); |
49485 | 247 |
248 if raw.is_empty() { | |
249 return Ok(None); | |
250 } | |
251 | |
252 let mut config = parse_config(&raw, SparseConfigContext::Sparse)?; | |
253 | |
254 if !config.profiles.is_empty() { | |
255 let mut profiles: Vec<Vec<u8>> = config.profiles.into_iter().collect(); | |
256 let mut visited = HashSet::new(); | |
257 | |
258 while let Some(profile) = profiles.pop() { | |
259 if visited.contains(&profile) { | |
260 continue; | |
261 } | |
262 visited.insert(profile.to_owned()); | |
263 | |
264 let output = | |
265 cat(repo, &rev.to_string(), vec![HgPath::new(&profile)]) | |
266 .map_err(|_| { | |
49930
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
267 HgError::corrupted( |
49485 | 268 "dirstate points to non-existent parent node" |
49930
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
269 .to_string(), |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
270 ) |
49485 | 271 })?; |
272 if output.results.is_empty() { | |
273 config.warnings.push(SparseWarning::ProfileNotFound { | |
274 profile: profile.to_owned(), | |
275 rev, | |
276 }) | |
277 } | |
278 | |
279 let subconfig = parse_config( | |
280 &output.results[0].1, | |
281 SparseConfigContext::Sparse, | |
282 )?; | |
283 if !subconfig.includes.is_empty() { | |
284 config.includes.push(b'\n'); | |
285 config.includes.extend(&subconfig.includes); | |
286 } | |
287 if !subconfig.includes.is_empty() { | |
288 config.includes.push(b'\n'); | |
289 config.excludes.extend(&subconfig.excludes); | |
290 } | |
291 config.warnings.extend(subconfig.warnings.into_iter()); | |
292 profiles.extend(subconfig.profiles.into_iter()); | |
293 } | |
294 | |
295 config.profiles = visited; | |
296 } | |
297 | |
298 if !config.includes.is_empty() { | |
299 config.includes.extend(b"\n.hg*"); | |
300 } | |
301 | |
302 Ok(Some(config)) | |
303 } | |
304 | |
305 /// Obtain a matcher for sparse working directories. | |
306 pub fn matcher( | |
307 repo: &Repo, | |
308 ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> { | |
309 let mut warnings = vec![]; | |
310 if !repo.requirements().contains(SPARSE_REQUIREMENT) { | |
311 return Ok((Box::new(AlwaysMatcher), warnings)); | |
312 } | |
313 | |
314 let parents = repo.dirstate_parents()?; | |
315 let mut revs = vec![]; | |
316 let p1_rev = | |
317 repo.changelog()? | |
318 .rev_from_node(parents.p1.into()) | |
319 .map_err(|_| { | |
49930
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
320 HgError::corrupted( |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
321 "dirstate points to non-existent parent node".to_string(), |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
322 ) |
49485 | 323 })?; |
324 if p1_rev != NULL_REVISION { | |
325 revs.push(p1_rev) | |
326 } | |
327 let p2_rev = | |
328 repo.changelog()? | |
329 .rev_from_node(parents.p2.into()) | |
330 .map_err(|_| { | |
49930
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
331 HgError::corrupted( |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
332 "dirstate points to non-existent parent node".to_string(), |
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
333 ) |
49485 | 334 })?; |
335 if p2_rev != NULL_REVISION { | |
336 revs.push(p2_rev) | |
337 } | |
338 let mut matchers = vec![]; | |
339 | |
340 for rev in revs.iter() { | |
341 let config = patterns_for_rev(repo, *rev); | |
342 if let Ok(Some(config)) = config { | |
343 warnings.extend(config.warnings); | |
344 let mut m: Box<dyn Matcher + Sync> = Box::new(AlwaysMatcher); | |
345 if !config.includes.is_empty() { | |
346 let (patterns, subwarnings) = parse_pattern_file_contents( | |
347 &config.includes, | |
348 Path::new(""), | |
50854
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50818
diff
changeset
|
349 Some(PatternSyntax::Glob), |
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50818
diff
changeset
|
350 false, |
49485 | 351 false, |
352 )?; | |
353 warnings.extend(subwarnings.into_iter().map(From::from)); | |
354 m = Box::new(IncludeMatcher::new(patterns)?); | |
355 } | |
356 if !config.excludes.is_empty() { | |
357 let (patterns, subwarnings) = parse_pattern_file_contents( | |
358 &config.excludes, | |
359 Path::new(""), | |
50854
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50818
diff
changeset
|
360 Some(PatternSyntax::Glob), |
796b5d6693a4
rust: simplify pattern file parsing
Spencer Baugh <sbaugh@janestreet.com>
parents:
50818
diff
changeset
|
361 false, |
49485 | 362 false, |
363 )?; | |
364 warnings.extend(subwarnings.into_iter().map(From::from)); | |
365 m = Box::new(DifferenceMatcher::new( | |
366 m, | |
367 Box::new(IncludeMatcher::new(patterns)?), | |
368 )); | |
369 } | |
370 matchers.push(m); | |
371 } | |
372 } | |
373 let result: Box<dyn Matcher + Sync> = match matchers.len() { | |
374 0 => Box::new(AlwaysMatcher), | |
375 1 => matchers.pop().expect("1 is equal to 0"), | |
376 _ => Box::new(UnionMatcher::new(matchers)), | |
377 }; | |
378 | |
379 let matcher = | |
380 force_include_matcher(result, &read_temporary_includes(repo)?)?; | |
381 Ok((matcher, warnings)) | |
382 } | |
383 | |
384 /// Returns a matcher that returns true for any of the forced includes before | |
385 /// testing against the actual matcher | |
386 fn force_include_matcher( | |
387 result: Box<dyn Matcher + Sync>, | |
388 temp_includes: &[Vec<u8>], | |
389 ) -> Result<Box<dyn Matcher + Sync>, PatternError> { | |
390 if temp_includes.is_empty() { | |
391 return Ok(result); | |
392 } | |
393 let forced_include_matcher = IncludeMatcher::new( | |
394 temp_includes | |
49930
e98fd81bb151
rust-clippy: fix most warnings in `hg-core`
Rapha?l Gom?s <rgomes@octobus.net>
parents:
49517
diff
changeset
|
395 .iter() |
49485 | 396 .map(|include| { |
397 IgnorePattern::new(PatternSyntax::Path, include, Path::new("")) | |
398 }) | |
399 .collect(), | |
400 )?; | |
401 Ok(Box::new(UnionMatcher::new(vec![ | |
402 Box::new(forced_include_matcher), | |
403 result, | |
404 ]))) | |
405 } |