##// END OF EJS Templates
rhg: fix the bug where sparse config is interpreted as relglob instead of glob...
Arseniy Alekseyev -
r51714:28c0fcff stable
parent child Browse files
Show More
@@ -1,339 +1,339 b''
1 1 use std::{collections::HashSet, path::Path};
2 2
3 3 use format_bytes::{write_bytes, DisplayBytes};
4 4
5 5 use crate::{
6 6 errors::HgError,
7 7 filepatterns::parse_pattern_file_contents,
8 8 matchers::{
9 9 AlwaysMatcher, DifferenceMatcher, IncludeMatcher, Matcher,
10 10 UnionMatcher,
11 11 },
12 12 operations::cat,
13 13 repo::Repo,
14 14 requirements::SPARSE_REQUIREMENT,
15 15 utils::{hg_path::HgPath, SliceExt},
16 16 IgnorePattern, PatternError, PatternFileWarning, PatternSyntax, Revision,
17 17 NULL_REVISION,
18 18 };
19 19
20 20 /// Command which is triggering the config read
21 21 #[derive(Copy, Clone, Debug)]
22 22 pub enum SparseConfigContext {
23 23 Sparse,
24 24 Narrow,
25 25 }
26 26
27 27 impl DisplayBytes for SparseConfigContext {
28 28 fn display_bytes(
29 29 &self,
30 30 output: &mut dyn std::io::Write,
31 31 ) -> std::io::Result<()> {
32 32 match self {
33 33 SparseConfigContext::Sparse => write_bytes!(output, b"sparse"),
34 34 SparseConfigContext::Narrow => write_bytes!(output, b"narrow"),
35 35 }
36 36 }
37 37 }
38 38
39 39 /// Possible warnings when reading sparse configuration
40 40 #[derive(Debug, derive_more::From)]
41 41 pub enum SparseWarning {
42 42 /// Warns about improper paths that start with "/"
43 43 RootWarning {
44 44 context: SparseConfigContext,
45 45 line: Vec<u8>,
46 46 },
47 47 /// Warns about a profile missing from the given changelog revision
48 48 ProfileNotFound { profile: Vec<u8>, rev: Revision },
49 49 #[from]
50 50 Pattern(PatternFileWarning),
51 51 }
52 52
53 53 /// Parsed sparse config
54 54 #[derive(Debug, Default)]
55 55 pub struct SparseConfig {
56 56 // Line-separated
57 57 pub(crate) includes: Vec<u8>,
58 58 // Line-separated
59 59 pub(crate) excludes: Vec<u8>,
60 60 pub(crate) profiles: HashSet<Vec<u8>>,
61 61 pub(crate) warnings: Vec<SparseWarning>,
62 62 }
63 63
64 64 /// All possible errors when reading sparse/narrow config
65 65 #[derive(Debug, derive_more::From)]
66 66 pub enum SparseConfigError {
67 67 IncludesAfterExcludes {
68 68 context: SparseConfigContext,
69 69 },
70 70 EntryOutsideSection {
71 71 context: SparseConfigContext,
72 72 line: Vec<u8>,
73 73 },
74 74 /// Narrow config does not support '%include' directives
75 75 IncludesInNarrow,
76 76 /// An invalid pattern prefix was given to the narrow spec. Includes the
77 77 /// entire pattern for context.
78 78 InvalidNarrowPrefix(Vec<u8>),
79 79 #[from]
80 80 HgError(HgError),
81 81 #[from]
82 82 PatternError(PatternError),
83 83 }
84 84
85 85 /// Parse sparse config file content.
86 86 pub(crate) fn parse_config(
87 87 raw: &[u8],
88 88 context: SparseConfigContext,
89 89 ) -> Result<SparseConfig, SparseConfigError> {
90 90 let mut includes = vec![];
91 91 let mut excludes = vec![];
92 92 let mut profiles = HashSet::new();
93 93 let mut warnings = vec![];
94 94
95 95 #[derive(PartialEq, Eq)]
96 96 enum Current {
97 97 Includes,
98 98 Excludes,
99 99 None,
100 100 }
101 101
102 102 let mut current = Current::None;
103 103 let mut in_section = false;
104 104
105 105 for line in raw.split(|c| *c == b'\n') {
106 106 let line = line.trim();
107 107 if line.is_empty() || line[0] == b'#' {
108 108 // empty or comment line, skip
109 109 continue;
110 110 }
111 111 if line.starts_with(b"%include ") {
112 112 let profile = line[b"%include ".len()..].trim();
113 113 if !profile.is_empty() {
114 114 profiles.insert(profile.into());
115 115 }
116 116 } else if line == b"[include]" {
117 117 if in_section && current == Current::Includes {
118 118 return Err(SparseConfigError::IncludesAfterExcludes {
119 119 context,
120 120 });
121 121 }
122 122 in_section = true;
123 123 current = Current::Includes;
124 124 continue;
125 125 } else if line == b"[exclude]" {
126 126 in_section = true;
127 127 current = Current::Excludes;
128 128 } else {
129 129 if current == Current::None {
130 130 return Err(SparseConfigError::EntryOutsideSection {
131 131 context,
132 132 line: line.into(),
133 133 });
134 134 }
135 135 if line.trim().starts_with(b"/") {
136 136 warnings.push(SparseWarning::RootWarning {
137 137 context,
138 138 line: line.into(),
139 139 });
140 140 continue;
141 141 }
142 142 match current {
143 143 Current::Includes => {
144 144 includes.push(b'\n');
145 145 includes.extend(line.iter());
146 146 }
147 147 Current::Excludes => {
148 148 excludes.push(b'\n');
149 149 excludes.extend(line.iter());
150 150 }
151 151 Current::None => unreachable!(),
152 152 }
153 153 }
154 154 }
155 155
156 156 Ok(SparseConfig {
157 157 includes,
158 158 excludes,
159 159 profiles,
160 160 warnings,
161 161 })
162 162 }
163 163
164 164 fn read_temporary_includes(
165 165 repo: &Repo,
166 166 ) -> Result<Vec<Vec<u8>>, SparseConfigError> {
167 167 let raw = repo.hg_vfs().try_read("tempsparse")?.unwrap_or_default();
168 168 if raw.is_empty() {
169 169 return Ok(vec![]);
170 170 }
171 171 Ok(raw.split(|c| *c == b'\n').map(ToOwned::to_owned).collect())
172 172 }
173 173
174 174 /// Obtain sparse checkout patterns for the given revision
175 175 fn patterns_for_rev(
176 176 repo: &Repo,
177 177 rev: Revision,
178 178 ) -> Result<Option<SparseConfig>, SparseConfigError> {
179 179 if !repo.has_sparse() {
180 180 return Ok(None);
181 181 }
182 182 let raw = repo.hg_vfs().try_read("sparse")?.unwrap_or_default();
183 183
184 184 if raw.is_empty() {
185 185 return Ok(None);
186 186 }
187 187
188 188 let mut config = parse_config(&raw, SparseConfigContext::Sparse)?;
189 189
190 190 if !config.profiles.is_empty() {
191 191 let mut profiles: Vec<Vec<u8>> = config.profiles.into_iter().collect();
192 192 let mut visited = HashSet::new();
193 193
194 194 while let Some(profile) = profiles.pop() {
195 195 if visited.contains(&profile) {
196 196 continue;
197 197 }
198 198 visited.insert(profile.to_owned());
199 199
200 200 let output =
201 201 cat(repo, &rev.to_string(), vec![HgPath::new(&profile)])
202 202 .map_err(|_| {
203 203 HgError::corrupted(
204 204 "dirstate points to non-existent parent node"
205 205 .to_string(),
206 206 )
207 207 })?;
208 208 if output.results.is_empty() {
209 209 config.warnings.push(SparseWarning::ProfileNotFound {
210 210 profile: profile.to_owned(),
211 211 rev,
212 212 })
213 213 }
214 214
215 215 let subconfig = parse_config(
216 216 &output.results[0].1,
217 217 SparseConfigContext::Sparse,
218 218 )?;
219 219 if !subconfig.includes.is_empty() {
220 220 config.includes.push(b'\n');
221 221 config.includes.extend(&subconfig.includes);
222 222 }
223 223 if !subconfig.includes.is_empty() {
224 224 config.includes.push(b'\n');
225 225 config.excludes.extend(&subconfig.excludes);
226 226 }
227 227 config.warnings.extend(subconfig.warnings.into_iter());
228 228 profiles.extend(subconfig.profiles.into_iter());
229 229 }
230 230
231 231 config.profiles = visited;
232 232 }
233 233
234 234 if !config.includes.is_empty() {
235 235 config.includes.extend(b"\n.hg*");
236 236 }
237 237
238 238 Ok(Some(config))
239 239 }
240 240
241 241 /// Obtain a matcher for sparse working directories.
242 242 pub fn matcher(
243 243 repo: &Repo,
244 244 ) -> Result<(Box<dyn Matcher + Sync>, Vec<SparseWarning>), SparseConfigError> {
245 245 let mut warnings = vec![];
246 246 if !repo.requirements().contains(SPARSE_REQUIREMENT) {
247 247 return Ok((Box::new(AlwaysMatcher), warnings));
248 248 }
249 249
250 250 let parents = repo.dirstate_parents()?;
251 251 let mut revs = vec![];
252 252 let p1_rev =
253 253 repo.changelog()?
254 254 .rev_from_node(parents.p1.into())
255 255 .map_err(|_| {
256 256 HgError::corrupted(
257 257 "dirstate points to non-existent parent node".to_string(),
258 258 )
259 259 })?;
260 260 if p1_rev != NULL_REVISION {
261 261 revs.push(p1_rev)
262 262 }
263 263 let p2_rev =
264 264 repo.changelog()?
265 265 .rev_from_node(parents.p2.into())
266 266 .map_err(|_| {
267 267 HgError::corrupted(
268 268 "dirstate points to non-existent parent node".to_string(),
269 269 )
270 270 })?;
271 271 if p2_rev != NULL_REVISION {
272 272 revs.push(p2_rev)
273 273 }
274 274 let mut matchers = vec![];
275 275
276 276 for rev in revs.iter() {
277 277 let config = patterns_for_rev(repo, *rev);
278 278 if let Ok(Some(config)) = config {
279 279 warnings.extend(config.warnings);
280 280 let mut m: Box<dyn Matcher + Sync> = Box::new(AlwaysMatcher);
281 281 if !config.includes.is_empty() {
282 282 let (patterns, subwarnings) = parse_pattern_file_contents(
283 283 &config.includes,
284 284 Path::new(""),
285 Some(b"relglob:".as_ref()),
285 Some(b"glob:".as_ref()),
286 286 false,
287 287 )?;
288 288 warnings.extend(subwarnings.into_iter().map(From::from));
289 289 m = Box::new(IncludeMatcher::new(patterns)?);
290 290 }
291 291 if !config.excludes.is_empty() {
292 292 let (patterns, subwarnings) = parse_pattern_file_contents(
293 293 &config.excludes,
294 294 Path::new(""),
295 Some(b"relglob:".as_ref()),
295 Some(b"glob:".as_ref()),
296 296 false,
297 297 )?;
298 298 warnings.extend(subwarnings.into_iter().map(From::from));
299 299 m = Box::new(DifferenceMatcher::new(
300 300 m,
301 301 Box::new(IncludeMatcher::new(patterns)?),
302 302 ));
303 303 }
304 304 matchers.push(m);
305 305 }
306 306 }
307 307 let result: Box<dyn Matcher + Sync> = match matchers.len() {
308 308 0 => Box::new(AlwaysMatcher),
309 309 1 => matchers.pop().expect("1 is equal to 0"),
310 310 _ => Box::new(UnionMatcher::new(matchers)),
311 311 };
312 312
313 313 let matcher =
314 314 force_include_matcher(result, &read_temporary_includes(repo)?)?;
315 315 Ok((matcher, warnings))
316 316 }
317 317
318 318 /// Returns a matcher that returns true for any of the forced includes before
319 319 /// testing against the actual matcher
320 320 fn force_include_matcher(
321 321 result: Box<dyn Matcher + Sync>,
322 322 temp_includes: &[Vec<u8>],
323 323 ) -> Result<Box<dyn Matcher + Sync>, PatternError> {
324 324 if temp_includes.is_empty() {
325 325 return Ok(result);
326 326 }
327 327 let forced_include_matcher = IncludeMatcher::new(
328 328 temp_includes
329 329 .iter()
330 330 .map(|include| {
331 331 IgnorePattern::new(PatternSyntax::Path, include, Path::new(""))
332 332 })
333 333 .collect(),
334 334 )?;
335 335 Ok(Box::new(UnionMatcher::new(vec![
336 336 Box::new(forced_include_matcher),
337 337 result,
338 338 ])))
339 339 }
@@ -1,440 +1,463 b''
1 1 test sparse
2 2
3 3 $ hg init myrepo
4 4 $ cd myrepo
5 5 $ cat > .hg/hgrc <<EOF
6 6 > [extensions]
7 7 > sparse=
8 8 > strip=
9 9 > EOF
10 10
11 11 $ echo a > show
12 12 $ echo x > hide
13 13 $ hg ci -Aqm 'initial'
14 14
15 15 $ echo b > show
16 16 $ echo y > hide
17 17 $ echo aa > show2
18 18 $ echo xx > hide2
19 19 $ hg ci -Aqm 'two'
20 20
21 21 Verify basic --include
22 22
23 23 $ hg up -q 0
24
25 Test that sparse pattern by default is interpreted as "glob:", and is interpreted relative to the root.
26
27 $ hg debugsparse --reset
28 $ hg debugsparse -X 'foo*bar'
29 $ cat .hg/sparse
30 [exclude]
31 foo*bar
32
33 $ mk() { mkdir -p "$1"; touch "$1"/"$2"; }
34 $ mk 'foo' bar
35 $ mk 'foo-bar' x
36 $ mk 'unanchoredfoo-bar' x
37 $ mk 'foo*bar' x
38 $ mk 'dir/foo-bar' x
39 $ hg status --config rhg.on-unsupported=abort
40 ? dir/foo-bar/x
41 ? foo/bar
42 ? unanchoredfoo-bar/x
43 $ hg clean -a --no-confirm
44 $ rm -r foo*bar
45 $ hg debugsparse --reset
46
24 47 $ hg debugsparse --include 'hide'
25 48 $ ls -A
26 49 .hg
27 50 hide
28 51
29 52 Absolute paths outside the repo should just be rejected
30 53
31 54 #if no-windows
32 55 $ hg debugsparse --include /foo/bar
33 56 abort: paths cannot be absolute
34 57 [255]
35 58 $ hg debugsparse --include '$TESTTMP/myrepo/hide'
36 59
37 60 $ hg debugsparse --include '/root'
38 61 abort: paths cannot be absolute
39 62 [255]
40 63 #else
41 64 TODO: See if this can be made to fail the same way as on Unix
42 65 $ hg debugsparse --include /c/foo/bar
43 66 abort: paths cannot be absolute
44 67 [255]
45 68 $ hg debugsparse --include '$TESTTMP/myrepo/hide'
46 69
47 70 $ hg debugsparse --include '/c/root'
48 71 abort: paths cannot be absolute
49 72 [255]
50 73 #endif
51 74
52 75 Paths should be treated as cwd-relative, not repo-root-relative
53 76 $ mkdir subdir && cd subdir
54 77 $ hg debugsparse --include path
55 78 $ hg debugsparse
56 79 [include]
57 80 $TESTTMP/myrepo/hide
58 81 hide
59 82 subdir/path
60 83
61 84 $ cd ..
62 85 $ echo hello > subdir/file2.ext
63 86 $ cd subdir
64 87 $ hg debugsparse --include '**.ext' # let us test globs
65 88 $ hg debugsparse --include 'path:abspath' # and a path: pattern
66 89 $ cd ..
67 90 $ hg debugsparse
68 91 [include]
69 92 $TESTTMP/myrepo/hide
70 93 hide
71 94 path:abspath
72 95 subdir/**.ext
73 96 subdir/path
74 97
75 98 $ rm -rf subdir
76 99
77 100 Verify commiting while sparse includes other files
78 101
79 102 $ echo z > hide
80 103 $ hg ci -Aqm 'edit hide'
81 104 $ ls -A
82 105 .hg
83 106 hide
84 107 $ hg manifest
85 108 hide
86 109 show
87 110
88 111 Verify --reset brings files back
89 112
90 113 $ hg debugsparse --reset
91 114 $ ls -A
92 115 .hg
93 116 hide
94 117 show
95 118 $ cat hide
96 119 z
97 120 $ cat show
98 121 a
99 122
100 123 Verify 'hg debugsparse' default output
101 124
102 125 $ hg up -q null
103 126 $ hg debugsparse --include 'show*'
104 127
105 128 $ hg debugsparse
106 129 [include]
107 130 show*
108 131
109 132 Verify update only writes included files
110 133
111 134 $ hg up -q 0
112 135 $ ls -A
113 136 .hg
114 137 show
115 138
116 139 $ hg up -q 1
117 140 $ ls -A
118 141 .hg
119 142 show
120 143 show2
121 144
122 145 Verify status only shows included files
123 146
124 147 $ touch hide
125 148 $ touch hide3
126 149 $ echo c > show
127 150 $ hg status
128 151 M show
129 152
130 153 Adding an excluded file should fail
131 154
132 155 $ hg add hide3
133 156 abort: cannot add 'hide3' - it is outside the sparse checkout
134 157 (include file with `hg debugsparse --include <pattern>` or use `hg add -s <file>` to include file directory while adding)
135 158 [255]
136 159
137 160 But adding a truly excluded file shouldn't count
138 161
139 162 $ hg add hide3 -X hide3
140 163
141 164 Verify deleting sparseness while a file has changes fails
142 165
143 166 $ hg debugsparse --delete 'show*'
144 167 pending changes to 'hide'
145 168 abort: cannot change sparseness due to pending changes (delete the files or use --force to bring them back dirty)
146 169 [255]
147 170
148 171 Verify deleting sparseness with --force brings back files
149 172
150 173 $ hg debugsparse -f --delete 'show*'
151 174 pending changes to 'hide'
152 175 $ ls -A
153 176 .hg
154 177 hide
155 178 hide2
156 179 hide3
157 180 show
158 181 show2
159 182 $ hg st
160 183 M hide
161 184 M show
162 185 ? hide3
163 186
164 187 Verify editing sparseness fails if pending changes
165 188
166 189 $ hg debugsparse --include 'show*'
167 190 pending changes to 'hide'
168 191 abort: could not update sparseness due to pending changes
169 192 [255]
170 193
171 194 Verify adding sparseness hides files
172 195
173 196 $ hg debugsparse -f --exclude 'hide*'
174 197 pending changes to 'hide'
175 198 $ ls -A
176 199 .hg
177 200 hide
178 201 hide3
179 202 show
180 203 show2
181 204 $ hg st
182 205 M show
183 206
184 207 $ hg up -qC .
185 208 TODO: add an option to purge to also purge files outside the sparse config?
186 209 $ hg purge --all --config extensions.purge=
187 210 $ ls -A
188 211 .hg
189 212 hide
190 213 hide3
191 214 show
192 215 show2
193 216 For now, manually remove the files
194 217 $ rm hide hide3
195 218
196 219 Verify rebase temporarily includes excluded files
197 220
198 221 $ hg rebase -d 1 -r 2 --config extensions.rebase=
199 222 rebasing 2:b91df4f39e75 tip "edit hide"
200 223 temporarily included 2 file(s) in the sparse checkout for merging
201 224 merging hide
202 225 warning: conflicts while merging hide! (edit, then use 'hg resolve --mark')
203 226 unresolved conflicts (see 'hg resolve', then 'hg rebase --continue')
204 227 [240]
205 228
206 229 $ hg debugsparse
207 230 [exclude]
208 231 hide*
209 232
210 233 Temporarily Included Files (for merge/rebase):
211 234 hide
212 235
213 236 $ cat hide
214 237 <<<<<<< dest: 39278f7c08a9 - test: two
215 238 y
216 239 =======
217 240 z
218 241 >>>>>>> source: b91df4f39e75 - test: edit hide
219 242
220 243 Verify aborting a rebase cleans up temporary files
221 244
222 245 $ hg rebase --abort --config extensions.rebase=
223 246 cleaned up 1 temporarily added file(s) from the sparse checkout
224 247 rebase aborted
225 248 $ rm hide.orig
226 249
227 250 $ ls -A
228 251 .hg
229 252 show
230 253 show2
231 254
232 255 Verify merge fails if merging excluded files
233 256
234 257 $ hg up -q 1
235 258 $ hg merge -r 2
236 259 temporarily included 2 file(s) in the sparse checkout for merging
237 260 merging hide
238 261 warning: conflicts while merging hide! (edit, then use 'hg resolve --mark')
239 262 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
240 263 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
241 264 [1]
242 265 $ hg debugsparse
243 266 [exclude]
244 267 hide*
245 268
246 269 Temporarily Included Files (for merge/rebase):
247 270 hide
248 271
249 272 $ hg up -C .
250 273 cleaned up 1 temporarily added file(s) from the sparse checkout
251 274 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
252 275 $ hg debugsparse
253 276 [exclude]
254 277 hide*
255 278
256 279
257 280 Multiple -I and -X can be passed at once
258 281
259 282 $ hg debugsparse --reset -I '*2' -X 'hide2'
260 283 $ ls -A
261 284 .hg
262 285 hide.orig
263 286 show2
264 287 $ hg debugsparse --reset -X 'hide*'
265 288
266 289 Verify strip -k resets dirstate correctly
267 290
268 291 $ hg status
269 292 $ hg debugsparse
270 293 [exclude]
271 294 hide*
272 295
273 296 $ hg log -r . -T '{rev}\n' --stat
274 297 1
275 298 hide | 2 +-
276 299 hide2 | 1 +
277 300 show | 2 +-
278 301 show2 | 1 +
279 302 4 files changed, 4 insertions(+), 2 deletions(-)
280 303
281 304 $ hg strip -r . -k
282 305 saved backup bundle to $TESTTMP/myrepo/.hg/strip-backup/39278f7c08a9-ce59e002-backup.hg
283 306 $ hg status
284 307 M show
285 308 ? show2
286 309
287 310 Verify rebase succeeds if all changed files are in sparse checkout
288 311
289 312 $ hg commit -Aqm "add show2"
290 313 $ hg rebase -d 1 --config extensions.rebase=
291 314 rebasing 2:bdde55290160 tip "add show2"
292 315 saved backup bundle to $TESTTMP/myrepo/.hg/strip-backup/bdde55290160-216ed9c6-rebase.hg
293 316
294 317 Verify log --sparse only shows commits that affect the sparse checkout
295 318
296 319 $ hg log -T '{rev} '
297 320 2 1 0 (no-eol)
298 321 $ hg log --sparse -T '{rev} '
299 322 2 0 (no-eol)
300 323
301 324 Test status on a file in a subdir
302 325
303 326 $ mkdir -p dir1/dir2
304 327 $ touch dir1/dir2/file
305 328 $ hg debugsparse -I dir1/dir2
306 329 $ hg status
307 330 ? dir1/dir2/file
308 331
309 332 Mix files and subdirectories, both "glob:" and unprefixed
310 333
311 334 $ hg debugsparse --reset
312 335 $ touch dir1/notshown
313 336 $ hg commit -A dir1/notshown -m "notshown"
314 337 $ hg debugsparse --include 'dir1/dir2'
315 338 $ "$PYTHON" $TESTDIR/list-tree.py . | grep -E -v '\.[\/]\.hg'
316 339 ./
317 340 ./dir1/
318 341 ./dir1/dir2/
319 342 ./dir1/dir2/file
320 343 ./hide.orig
321 344 $ hg debugsparse --delete 'dir1/dir2'
322 345 $ hg debugsparse --include 'glob:dir1/dir2'
323 346 $ "$PYTHON" $TESTDIR/list-tree.py . | grep -E -v '\.[\/]\.hg'
324 347 ./
325 348 ./dir1/
326 349 ./dir1/dir2/
327 350 ./dir1/dir2/file
328 351 ./hide.orig
329 352
330 353 Test that add -s adds dirs to sparse profile
331 354
332 355 $ hg debugsparse --reset
333 356 $ hg debugsparse --include empty
334 357 $ hg debugsparse
335 358 [include]
336 359 empty
337 360
338 361
339 362 $ mkdir add
340 363 $ touch add/foo
341 364 $ touch add/bar
342 365 $ hg add add/foo
343 366 abort: cannot add 'add/foo' - it is outside the sparse checkout
344 367 (include file with `hg debugsparse --include <pattern>` or use `hg add -s <file>` to include file directory while adding)
345 368 [255]
346 369 $ hg add -s add/foo
347 370 $ hg st
348 371 A add/foo
349 372 ? add/bar
350 373 $ hg debugsparse
351 374 [include]
352 375 add
353 376 empty
354 377
355 378 $ hg add -s add/*
356 379 add/foo already tracked!
357 380 $ hg st
358 381 A add/bar
359 382 A add/foo
360 383 $ hg debugsparse
361 384 [include]
362 385 add
363 386 empty
364 387
365 388
366 389 $ cd ..
367 390
368 391 Test non-sparse repos work while sparse is loaded
369 392 $ hg init sparserepo
370 393 $ hg init nonsparserepo
371 394 $ cd sparserepo
372 395 $ cat > .hg/hgrc <<EOF
373 396 > [extensions]
374 397 > sparse=
375 398 > EOF
376 399 $ cd ../nonsparserepo
377 400 $ echo x > x && hg add x && hg commit -qAm x
378 401 $ cd ../sparserepo
379 402 $ hg clone ../nonsparserepo ../nonsparserepo2
380 403 updating to branch default
381 404 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
382 405
383 406 Test debugrebuilddirstate
384 407 $ cd ../sparserepo
385 408 $ touch included
386 409 $ touch excluded
387 410 $ hg add included excluded
388 411 $ hg commit -m 'a commit' -q
389 412 $ cp .hg/dirstate ../dirstateboth
390 413 $ hg debugsparse -X excluded
391 414 $ cp ../dirstateboth .hg/dirstate
392 415 $ hg debugrebuilddirstate
393 416 $ hg debugdirstate
394 417 n 0 -1 unset included
395 418
396 419 Test debugdirstate --minimal where file is in the parent manifest but not the
397 420 dirstate
398 421 $ hg debugsparse -X included
399 422 $ hg debugdirstate
400 423 $ cp .hg/dirstate ../dirstateallexcluded
401 424 $ hg debugsparse --reset
402 425 $ hg debugsparse -X excluded
403 426 $ cp ../dirstateallexcluded .hg/dirstate
404 427 $ touch includedadded
405 428 $ hg add includedadded
406 429 $ hg debugdirstate --no-dates
407 430 a 0 -1 unset includedadded
408 431 $ hg debugrebuilddirstate --minimal
409 432 $ hg debugdirstate --no-dates
410 433 n 0 -1 unset included
411 434 a 0 -1 * includedadded (glob)
412 435
413 436 Test debugdirstate --minimal where a file is not in parent manifest
414 437 but in the dirstate. This should take into account excluded files in the
415 438 manifest
416 439 $ cp ../dirstateboth .hg/dirstate
417 440 $ touch includedadded
418 441 $ hg add includedadded
419 442 $ touch excludednomanifest
420 443 $ hg add excludednomanifest
421 444 $ cp .hg/dirstate ../moreexcluded
422 445 $ hg forget excludednomanifest
423 446 $ rm excludednomanifest
424 447 $ hg debugsparse -X excludednomanifest
425 448 $ cp ../moreexcluded .hg/dirstate
426 449 $ hg manifest
427 450 excluded
428 451 included
429 452 We have files in the dirstate that are included and excluded. Some are in the
430 453 manifest and some are not.
431 454 $ hg debugdirstate --no-dates
432 455 n * excluded (glob)
433 456 a * excludednomanifest (glob)
434 457 n * included (glob)
435 458 a * includedadded (glob)
436 459 $ hg debugrebuilddirstate --minimal
437 460 $ hg debugdirstate --no-dates
438 461 n * included (glob)
439 462 a * includedadded (glob)
440 463
General Comments 0
You need to be logged in to leave comments. Login now