Show More
@@ -179,6 +179,9 b' bundle_spec_param_processing = {' | |||||
179 | b"obsolescence": param_bool, |
|
179 | b"obsolescence": param_bool, | |
180 | b"obsolescence-mandatory": param_bool, |
|
180 | b"obsolescence-mandatory": param_bool, | |
181 | b"phases": param_bool, |
|
181 | b"phases": param_bool, | |
|
182 | b"changegroup": param_bool, | |||
|
183 | b"tagsfnodescache": param_bool, | |||
|
184 | b"revbranchcache": param_bool, | |||
182 | } |
|
185 | } | |
183 |
|
186 | |||
184 |
|
187 |
@@ -158,9 +158,8 b' def getbundlespec(ui, fh):' | |||||
158 | params[b'obsolescence-mandatory'] = b'no' |
|
158 | params[b'obsolescence-mandatory'] = b'no' | |
159 |
|
159 | |||
160 | if not version: |
|
160 | if not version: | |
161 | raise error.Abort( |
|
161 | params[b'changegroup'] = b'no' | |
162 | _(b'could not identify changegroup version in bundle') |
|
162 | version = b'v2' | |
163 | ) |
|
|||
164 | spec = b'%s-%s' % (comp, version) |
|
163 | spec = b'%s-%s' % (comp, version) | |
165 | if params: |
|
164 | if params: | |
166 | spec += b';' |
|
165 | spec += b';' |
@@ -638,7 +638,10 b' class patternmatcher(basematcher):' | |||||
638 | super(patternmatcher, self).__init__(badfn) |
|
638 | super(patternmatcher, self).__init__(badfn) | |
639 | kindpats.sort() |
|
639 | kindpats.sort() | |
640 |
|
640 | |||
|
641 | roots, dirs, parents = _rootsdirsandparents(kindpats) | |||
641 | self._files = _explicitfiles(kindpats) |
|
642 | self._files = _explicitfiles(kindpats) | |
|
643 | self._dirs_explicit = set(dirs) | |||
|
644 | self._dirs = parents | |||
642 | self._prefix = _prefix(kindpats) |
|
645 | self._prefix = _prefix(kindpats) | |
643 | self._pats, self._matchfn = _buildmatch(kindpats, b'$', root) |
|
646 | self._pats, self._matchfn = _buildmatch(kindpats, b'$', root) | |
644 |
|
647 | |||
@@ -647,14 +650,14 b' class patternmatcher(basematcher):' | |||||
647 | return True |
|
650 | return True | |
648 | return self._matchfn(fn) |
|
651 | return self._matchfn(fn) | |
649 |
|
652 | |||
650 | @propertycache |
|
|||
651 | def _dirs(self): |
|
|||
652 | return set(pathutil.dirs(self._fileset)) |
|
|||
653 |
|
||||
654 | def visitdir(self, dir): |
|
653 | def visitdir(self, dir): | |
655 | if self._prefix and dir in self._fileset: |
|
654 | if self._prefix and dir in self._fileset: | |
656 | return b'all' |
|
655 | return b'all' | |
657 | return dir in self._dirs or path_or_parents_in_set(dir, self._fileset) |
|
656 | return ( | |
|
657 | dir in self._dirs | |||
|
658 | or path_or_parents_in_set(dir, self._fileset) | |||
|
659 | or path_or_parents_in_set(dir, self._dirs_explicit) | |||
|
660 | ) | |||
658 |
|
661 | |||
659 | def visitchildrenset(self, dir): |
|
662 | def visitchildrenset(self, dir): | |
660 | ret = self.visitdir(dir) |
|
663 | ret = self.visitdir(dir) | |
@@ -1461,7 +1464,7 b' def _buildregexmatch(kindpats, globsuffi' | |||||
1461 | allgroups = [] |
|
1464 | allgroups = [] | |
1462 | regexps = [] |
|
1465 | regexps = [] | |
1463 | exact = set() |
|
1466 | exact = set() | |
1464 |
for |
|
1467 | for kind, pattern, _source in kindpats: | |
1465 | if kind == b'filepath': |
|
1468 | if kind == b'filepath': | |
1466 | exact.add(pattern) |
|
1469 | exact.add(pattern) | |
1467 | continue |
|
1470 | continue |
@@ -527,24 +527,34 b' class sshserver:' | |||||
527 | def __init__(self, ui, repo, logfh=None, accesshidden=False): |
|
527 | def __init__(self, ui, repo, logfh=None, accesshidden=False): | |
528 | self._ui = ui |
|
528 | self._ui = ui | |
529 | self._repo = repo |
|
529 | self._repo = repo | |
530 | self._fin, self._fout = ui.protectfinout() |
|
|||
531 | self._accesshidden = accesshidden |
|
530 | self._accesshidden = accesshidden | |
532 |
|
531 | self._logfh = logfh | ||
533 | # Log write I/O to stdout and stderr if configured. |
|
|||
534 | if logfh: |
|
|||
535 | self._fout = util.makeloggingfileobject( |
|
|||
536 | logfh, self._fout, b'o', logdata=True |
|
|||
537 | ) |
|
|||
538 | ui.ferr = util.makeloggingfileobject( |
|
|||
539 | logfh, ui.ferr, b'e', logdata=True |
|
|||
540 | ) |
|
|||
541 |
|
532 | |||
542 | def serve_forever(self): |
|
533 | def serve_forever(self): | |
543 | self.serveuntil(threading.Event()) |
|
534 | self.serveuntil(threading.Event()) | |
544 | self._ui.restorefinout(self._fin, self._fout) |
|
|||
545 |
|
535 | |||
546 | def serveuntil(self, ev): |
|
536 | def serveuntil(self, ev): | |
547 | """Serve until a threading.Event is set.""" |
|
537 | """Serve until a threading.Event is set.""" | |
548 | _runsshserver( |
|
538 | with self._ui.protectedfinout() as (fin, fout): | |
549 | self._ui, self._repo, self._fin, self._fout, ev, self._accesshidden |
|
539 | if self._logfh: | |
550 | ) |
|
540 | # Log write I/O to stdout and stderr if configured. | |
|
541 | fout = util.makeloggingfileobject( | |||
|
542 | self._logfh, | |||
|
543 | fout, | |||
|
544 | b'o', | |||
|
545 | logdata=True, | |||
|
546 | ) | |||
|
547 | self._ui.ferr = util.makeloggingfileobject( | |||
|
548 | self._logfh, | |||
|
549 | self._ui.ferr, | |||
|
550 | b'e', | |||
|
551 | logdata=True, | |||
|
552 | ) | |||
|
553 | _runsshserver( | |||
|
554 | self._ui, | |||
|
555 | self._repo, | |||
|
556 | fin, | |||
|
557 | fout, | |||
|
558 | ev, | |||
|
559 | self._accesshidden, | |||
|
560 | ) |
@@ -158,14 +158,13 b" pub struct DirsChildrenMultiset<'a> {" | |||||
158 | } |
|
158 | } | |
159 |
|
159 | |||
160 | impl<'a> DirsChildrenMultiset<'a> { |
|
160 | impl<'a> DirsChildrenMultiset<'a> { | |
161 | pub fn new( |
|
161 | pub fn new<I: Iterator<Item = &'a HgPathBuf>>( | |
162 | paths: impl Iterator<Item = &'a HgPathBuf>, |
|
162 | paths: impl Iterator<Item = &'a HgPathBuf>, | |
163 |
only_include: Option< |
|
163 | only_include: Option<I>, | |
164 | ) -> Self { |
|
164 | ) -> Self { | |
165 | let mut new = Self { |
|
165 | let mut new = Self { | |
166 | inner: HashMap::default(), |
|
166 | inner: HashMap::default(), | |
167 | only_include: only_include |
|
167 | only_include: only_include.map(|s| s.map(AsRef::as_ref).collect()), | |
168 | .map(|s| s.iter().map(AsRef::as_ref).collect()), |
|
|||
169 | }; |
|
168 | }; | |
170 |
|
169 | |||
171 | for path in paths { |
|
170 | for path in paths { |
@@ -57,7 +57,7 b' pub enum PatternSyntax {' | |||||
57 | RelRegexp, |
|
57 | RelRegexp, | |
58 | /// A path relative to repository root, which is matched non-recursively |
|
58 | /// A path relative to repository root, which is matched non-recursively | |
59 | /// (will not match subdirectories) |
|
59 | /// (will not match subdirectories) | |
60 | RootFiles, |
|
60 | RootFilesIn, | |
61 | /// A file of patterns to read and include |
|
61 | /// A file of patterns to read and include | |
62 | Include, |
|
62 | Include, | |
63 | /// A file of patterns to match against files under the same directory |
|
63 | /// A file of patterns to match against files under the same directory | |
@@ -158,7 +158,7 b' pub fn parse_pattern_syntax(' | |||||
158 | b"path:" => Ok(PatternSyntax::Path), |
|
158 | b"path:" => Ok(PatternSyntax::Path), | |
159 | b"filepath:" => Ok(PatternSyntax::FilePath), |
|
159 | b"filepath:" => Ok(PatternSyntax::FilePath), | |
160 | b"relpath:" => Ok(PatternSyntax::RelPath), |
|
160 | b"relpath:" => Ok(PatternSyntax::RelPath), | |
161 | b"rootfilesin:" => Ok(PatternSyntax::RootFiles), |
|
161 | b"rootfilesin:" => Ok(PatternSyntax::RootFilesIn), | |
162 | b"relglob:" => Ok(PatternSyntax::RelGlob), |
|
162 | b"relglob:" => Ok(PatternSyntax::RelGlob), | |
163 | b"relre:" => Ok(PatternSyntax::RelRegexp), |
|
163 | b"relre:" => Ok(PatternSyntax::RelRegexp), | |
164 | b"glob:" => Ok(PatternSyntax::Glob), |
|
164 | b"glob:" => Ok(PatternSyntax::Glob), | |
@@ -227,7 +227,7 b' fn _build_single_regex(entry: &IgnorePat' | |||||
227 | } |
|
227 | } | |
228 | [escape_pattern(pattern).as_slice(), b"(?:/|$)"].concat() |
|
228 | [escape_pattern(pattern).as_slice(), b"(?:/|$)"].concat() | |
229 | } |
|
229 | } | |
230 | PatternSyntax::RootFiles => { |
|
230 | PatternSyntax::RootFilesIn => { | |
231 | let mut res = if pattern == b"." { |
|
231 | let mut res = if pattern == b"." { | |
232 | vec![] |
|
232 | vec![] | |
233 | } else { |
|
233 | } else { | |
@@ -316,7 +316,7 b' pub fn build_single_regex(' | |||||
316 | | PatternSyntax::Path |
|
316 | | PatternSyntax::Path | |
317 | | PatternSyntax::RelGlob |
|
317 | | PatternSyntax::RelGlob | |
318 | | PatternSyntax::RelPath |
|
318 | | PatternSyntax::RelPath | |
319 | | PatternSyntax::RootFiles => normalize_path_bytes(pattern), |
|
319 | | PatternSyntax::RootFilesIn => normalize_path_bytes(pattern), | |
320 | PatternSyntax::Include | PatternSyntax::SubInclude => { |
|
320 | PatternSyntax::Include | PatternSyntax::SubInclude => { | |
321 | return Err(PatternError::NonRegexPattern(entry.clone())) |
|
321 | return Err(PatternError::NonRegexPattern(entry.clone())) | |
322 | } |
|
322 | } | |
@@ -342,7 +342,7 b' lazy_static! {' | |||||
342 | m.insert(b"path:".as_ref(), PatternSyntax::Path); |
|
342 | m.insert(b"path:".as_ref(), PatternSyntax::Path); | |
343 | m.insert(b"filepath:".as_ref(), PatternSyntax::FilePath); |
|
343 | m.insert(b"filepath:".as_ref(), PatternSyntax::FilePath); | |
344 | m.insert(b"relpath:".as_ref(), PatternSyntax::RelPath); |
|
344 | m.insert(b"relpath:".as_ref(), PatternSyntax::RelPath); | |
345 | m.insert(b"rootfilesin:".as_ref(), PatternSyntax::RootFiles); |
|
345 | m.insert(b"rootfilesin:".as_ref(), PatternSyntax::RootFilesIn); | |
346 | m.insert(b"relglob:".as_ref(), PatternSyntax::RelGlob); |
|
346 | m.insert(b"relglob:".as_ref(), PatternSyntax::RelGlob); | |
347 | m.insert(b"relre:".as_ref(), PatternSyntax::RelRegexp); |
|
347 | m.insert(b"relre:".as_ref(), PatternSyntax::RelRegexp); | |
348 | m.insert(b"glob:".as_ref(), PatternSyntax::Glob); |
|
348 | m.insert(b"glob:".as_ref(), PatternSyntax::Glob); | |
@@ -385,7 +385,7 b' pub fn parse_one_pattern(' | |||||
385 | | PatternSyntax::Glob |
|
385 | | PatternSyntax::Glob | |
386 | | PatternSyntax::RelGlob |
|
386 | | PatternSyntax::RelGlob | |
387 | | PatternSyntax::RelPath |
|
387 | | PatternSyntax::RelPath | |
388 | | PatternSyntax::RootFiles |
|
388 | | PatternSyntax::RootFilesIn | |
389 | if normalize => |
|
389 | if normalize => | |
390 | { |
|
390 | { | |
391 | normalize_path_bytes(pattern_bytes) |
|
391 | normalize_path_bytes(pattern_bytes) |
@@ -17,7 +17,7 b' use crate::{' | |||||
17 | PatternFileWarning, PatternResult, |
|
17 | PatternFileWarning, PatternResult, | |
18 | }, |
|
18 | }, | |
19 | utils::{ |
|
19 | utils::{ | |
20 | files::find_dirs, |
|
20 | files::{dir_ancestors, find_dirs}, | |
21 | hg_path::{HgPath, HgPathBuf, HgPathError}, |
|
21 | hg_path::{HgPath, HgPathBuf, HgPathError}, | |
22 | Escaped, |
|
22 | Escaped, | |
23 | }, |
|
23 | }, | |
@@ -35,12 +35,14 b' use std::{borrow::ToOwned, collections::' | |||||
35 | pub enum VisitChildrenSet { |
|
35 | pub enum VisitChildrenSet { | |
36 | /// Don't visit anything |
|
36 | /// Don't visit anything | |
37 | Empty, |
|
37 | Empty, | |
38 |
/// |
|
38 | /// Visit this directory and probably its children | |
39 | This, |
|
39 | This, | |
40 | /// Visit this directory and these subdirectories |
|
40 | /// Only visit the children (both files and directories) if they | |
|
41 | /// are mentioned in this set. (empty set corresponds to [Empty]) | |||
41 | /// TODO Should we implement a `NonEmptyHashSet`? |
|
42 | /// TODO Should we implement a `NonEmptyHashSet`? | |
42 | Set(HashSet<HgPathBuf>), |
|
43 | Set(HashSet<HgPathBuf>), | |
43 | /// Visit this directory and all subdirectories |
|
44 | /// Visit this directory and all subdirectories | |
|
45 | /// (you can stop asking about the children set) | |||
44 | Recursive, |
|
46 | Recursive, | |
45 | } |
|
47 | } | |
46 |
|
48 | |||
@@ -297,6 +299,7 b" pub struct PatternMatcher<'a> {" | |||||
297 | /// Whether all the patterns match a prefix (i.e. recursively) |
|
299 | /// Whether all the patterns match a prefix (i.e. recursively) | |
298 | prefix: bool, |
|
300 | prefix: bool, | |
299 | files: HashSet<HgPathBuf>, |
|
301 | files: HashSet<HgPathBuf>, | |
|
302 | dirs_explicit: HashSet<HgPathBuf>, | |||
300 | dirs: DirsMultiset, |
|
303 | dirs: DirsMultiset, | |
301 | } |
|
304 | } | |
302 |
|
305 | |||
@@ -313,8 +316,13 b' impl core::fmt::Debug for PatternMatcher' | |||||
313 |
|
316 | |||
314 | impl<'a> PatternMatcher<'a> { |
|
317 | impl<'a> PatternMatcher<'a> { | |
315 | pub fn new(ignore_patterns: Vec<IgnorePattern>) -> PatternResult<Self> { |
|
318 | pub fn new(ignore_patterns: Vec<IgnorePattern>) -> PatternResult<Self> { | |
316 | let (files, _) = roots_and_dirs(&ignore_patterns); |
|
319 | let RootsDirsAndParents { | |
317 | let dirs = DirsMultiset::from_manifest(&files)?; |
|
320 | roots, | |
|
321 | dirs: dirs_explicit, | |||
|
322 | parents, | |||
|
323 | } = roots_dirs_and_parents(&ignore_patterns)?; | |||
|
324 | let files = roots; | |||
|
325 | let dirs = parents; | |||
318 | let files: HashSet<HgPathBuf> = HashSet::from_iter(files); |
|
326 | let files: HashSet<HgPathBuf> = HashSet::from_iter(files); | |
319 |
|
327 | |||
320 | let prefix = ignore_patterns.iter().all(|k| { |
|
328 | let prefix = ignore_patterns.iter().all(|k| { | |
@@ -328,6 +336,7 b" impl<'a> PatternMatcher<'a> {" | |||||
328 | prefix, |
|
336 | prefix, | |
329 | files, |
|
337 | files, | |
330 | dirs, |
|
338 | dirs, | |
|
339 | dirs_explicit, | |||
331 | }) |
|
340 | }) | |
332 | } |
|
341 | } | |
333 | } |
|
342 | } | |
@@ -352,9 +361,13 b" impl<'a> Matcher for PatternMatcher<'a> " | |||||
352 | if self.prefix && self.files.contains(directory) { |
|
361 | if self.prefix && self.files.contains(directory) { | |
353 | return VisitChildrenSet::Recursive; |
|
362 | return VisitChildrenSet::Recursive; | |
354 | } |
|
363 | } | |
355 | let path_or_parents_in_set = find_dirs(directory) |
|
364 | if self.dirs.contains(directory) { | |
356 | .any(|parent_dir| self.files.contains(parent_dir)); |
|
365 | return VisitChildrenSet::This; | |
357 | if self.dirs.contains(directory) || path_or_parents_in_set { |
|
366 | } | |
|
367 | if dir_ancestors(directory).any(|parent_dir| { | |||
|
368 | self.files.contains(parent_dir) | |||
|
369 | || self.dirs_explicit.contains(parent_dir) | |||
|
370 | }) { | |||
358 | VisitChildrenSet::This |
|
371 | VisitChildrenSet::This | |
359 | } else { |
|
372 | } else { | |
360 | VisitChildrenSet::Empty |
|
373 | VisitChildrenSet::Empty | |
@@ -390,7 +403,7 b" impl<'a> Matcher for PatternMatcher<'a> " | |||||
390 | /// assert_eq!(matcher.matches(HgPath::new(b"but not this")), false); |
|
403 | /// assert_eq!(matcher.matches(HgPath::new(b"but not this")), false); | |
391 | /// /// |
|
404 | /// /// | |
392 | /// let ignore_patterns = |
|
405 | /// let ignore_patterns = | |
393 | /// vec![IgnorePattern::new(PatternSyntax::RootFiles, b"dir/subdir", Path::new(""))]; |
|
406 | /// vec![IgnorePattern::new(PatternSyntax::RootFilesIn, b"dir/subdir", Path::new(""))]; | |
394 | /// let matcher = IncludeMatcher::new(ignore_patterns).unwrap(); |
|
407 | /// let matcher = IncludeMatcher::new(ignore_patterns).unwrap(); | |
395 | /// /// |
|
408 | /// /// | |
396 | /// assert!(!matcher.matches(HgPath::new(b"file"))); |
|
409 | /// assert!(!matcher.matches(HgPath::new(b"file"))); | |
@@ -405,7 +418,7 b" pub struct IncludeMatcher<'a> {" | |||||
405 | prefix: bool, |
|
418 | prefix: bool, | |
406 | roots: HashSet<HgPathBuf>, |
|
419 | roots: HashSet<HgPathBuf>, | |
407 | dirs: HashSet<HgPathBuf>, |
|
420 | dirs: HashSet<HgPathBuf>, | |
408 | parents: HashSet<HgPathBuf>, |
|
421 | parents: DirsMultiset, | |
409 | } |
|
422 | } | |
410 |
|
423 | |||
411 | impl core::fmt::Debug for IncludeMatcher<'_> { |
|
424 | impl core::fmt::Debug for IncludeMatcher<'_> { | |
@@ -861,7 +874,7 b' fn roots_and_dirs(' | |||||
861 | }); |
|
874 | }); | |
862 | roots.push(pat.to_owned()); |
|
875 | roots.push(pat.to_owned()); | |
863 | } |
|
876 | } | |
864 | PatternSyntax::RootFiles => { |
|
877 | PatternSyntax::RootFilesIn => { | |
865 | let pat = if pattern == b"." { |
|
878 | let pat = if pattern == b"." { | |
866 | &[] as &[u8] |
|
879 | &[] as &[u8] | |
867 | } else { |
|
880 | } else { | |
@@ -885,7 +898,7 b' struct RootsDirsAndParents {' | |||||
885 | /// Directories to match non-recursively |
|
898 | /// Directories to match non-recursively | |
886 | pub dirs: HashSet<HgPathBuf>, |
|
899 | pub dirs: HashSet<HgPathBuf>, | |
887 | /// Implicitly required directories to go to items in either roots or dirs |
|
900 | /// Implicitly required directories to go to items in either roots or dirs | |
888 |
pub parents: |
|
901 | pub parents: DirsMultiset, | |
889 | } |
|
902 | } | |
890 |
|
903 | |||
891 | /// Extract roots, dirs and parents from patterns. |
|
904 | /// Extract roots, dirs and parents from patterns. | |
@@ -894,18 +907,11 b' fn roots_dirs_and_parents(' | |||||
894 | ) -> PatternResult<RootsDirsAndParents> { |
|
907 | ) -> PatternResult<RootsDirsAndParents> { | |
895 | let (roots, dirs) = roots_and_dirs(ignore_patterns); |
|
908 | let (roots, dirs) = roots_and_dirs(ignore_patterns); | |
896 |
|
909 | |||
897 |
let mut parents = |
|
910 | let mut parents = DirsMultiset::from_manifest(&dirs)?; | |
898 |
|
911 | |||
899 | parents.extend( |
|
912 | for path in &roots { | |
900 | DirsMultiset::from_manifest(&dirs)? |
|
913 | parents.add_path(path)? | |
901 | .iter() |
|
914 | } | |
902 | .map(ToOwned::to_owned), |
|
|||
903 | ); |
|
|||
904 | parents.extend( |
|
|||
905 | DirsMultiset::from_manifest(&roots)? |
|
|||
906 | .iter() |
|
|||
907 | .map(ToOwned::to_owned), |
|
|||
908 | ); |
|
|||
909 |
|
915 | |||
910 | Ok(RootsDirsAndParents { |
|
916 | Ok(RootsDirsAndParents { | |
911 | roots: HashSet::from_iter(roots), |
|
917 | roots: HashSet::from_iter(roots), | |
@@ -958,7 +964,7 b" fn build_match<'a>(" | |||||
958 | // with a regex. |
|
964 | // with a regex. | |
959 | if ignore_patterns |
|
965 | if ignore_patterns | |
960 | .iter() |
|
966 | .iter() | |
961 | .all(|k| k.syntax == PatternSyntax::RootFiles) |
|
967 | .all(|k| k.syntax == PatternSyntax::RootFilesIn) | |
962 | { |
|
968 | { | |
963 | let dirs: HashSet<_> = ignore_patterns |
|
969 | let dirs: HashSet<_> = ignore_patterns | |
964 | .iter() |
|
970 | .iter() | |
@@ -1077,7 +1083,7 b" impl<'a> IncludeMatcher<'a> {" | |||||
1077 | .iter() |
|
1083 | .iter() | |
1078 | .chain(self.roots.iter()) |
|
1084 | .chain(self.roots.iter()) | |
1079 | .chain(self.parents.iter()); |
|
1085 | .chain(self.parents.iter()); | |
1080 |
DirsChildrenMultiset::new(thing, Some( |
|
1086 | DirsChildrenMultiset::new(thing, Some(self.parents.iter())) | |
1081 | } |
|
1087 | } | |
1082 |
|
1088 | |||
1083 | pub fn debug_get_patterns(&self) -> &[u8] { |
|
1089 | pub fn debug_get_patterns(&self) -> &[u8] { | |
@@ -1105,6 +1111,9 b" impl<'a> Display for IncludeMatcher<'a> " | |||||
1105 | mod tests { |
|
1111 | mod tests { | |
1106 | use super::*; |
|
1112 | use super::*; | |
1107 | use pretty_assertions::assert_eq; |
|
1113 | use pretty_assertions::assert_eq; | |
|
1114 | use std::collections::BTreeMap; | |||
|
1115 | use std::collections::BTreeSet; | |||
|
1116 | use std::fmt::Debug; | |||
1108 | use std::path::Path; |
|
1117 | use std::path::Path; | |
1109 |
|
1118 | |||
1110 | #[test] |
|
1119 | #[test] | |
@@ -1141,9 +1150,12 b' mod tests {' | |||||
1141 |
|
1150 | |||
1142 | let dirs = HashSet::new(); |
|
1151 | let dirs = HashSet::new(); | |
1143 |
|
1152 | |||
1144 |
let |
|
1153 | let parents = DirsMultiset::from_manifest(&[ | |
1145 | parents.insert(HgPathBuf::new()); |
|
1154 | HgPathBuf::from_bytes(b"x"), | |
1146 |
|
|
1155 | HgPathBuf::from_bytes(b"g/x"), | |
|
1156 | HgPathBuf::from_bytes(b"g/y"), | |||
|
1157 | ]) | |||
|
1158 | .unwrap(); | |||
1147 |
|
1159 | |||
1148 | assert_eq!( |
|
1160 | assert_eq!( | |
1149 | roots_dirs_and_parents(&pats).unwrap(), |
|
1161 | roots_dirs_and_parents(&pats).unwrap(), | |
@@ -1316,61 +1328,60 b' mod tests {' | |||||
1316 |
|
1328 | |||
1317 | // VisitdirRootfilesin |
|
1329 | // VisitdirRootfilesin | |
1318 | let m = PatternMatcher::new(vec![IgnorePattern::new( |
|
1330 | let m = PatternMatcher::new(vec![IgnorePattern::new( | |
1319 | PatternSyntax::RootFiles, |
|
1331 | PatternSyntax::RootFilesIn, | |
1320 | b"dir/subdir", |
|
1332 | b"dir/subdir", | |
1321 | Path::new(""), |
|
1333 | Path::new(""), | |
1322 | )]) |
|
1334 | )]) | |
1323 | .unwrap(); |
|
1335 | .unwrap(); | |
1324 | assert_eq!( |
|
1336 | assert_eq!( | |
1325 | m.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1337 | m.visit_children_set(HgPath::new(b"dir/subdir/x")), | |
1326 |
VisitChildrenSet:: |
|
1338 | VisitChildrenSet::This | |
1327 | ); |
|
1339 | ); | |
1328 | assert_eq!( |
|
1340 | assert_eq!( | |
1329 | m.visit_children_set(HgPath::new(b"folder")), |
|
1341 | m.visit_children_set(HgPath::new(b"folder")), | |
1330 | VisitChildrenSet::Empty |
|
1342 | VisitChildrenSet::Empty | |
1331 | ); |
|
1343 | ); | |
1332 | // FIXME: These should probably be This. |
|
|||
1333 | assert_eq!( |
|
1344 | assert_eq!( | |
1334 | m.visit_children_set(HgPath::new(b"")), |
|
1345 | m.visit_children_set(HgPath::new(b"")), | |
1335 |
VisitChildrenSet:: |
|
1346 | VisitChildrenSet::This | |
1336 | ); |
|
1347 | ); | |
1337 | assert_eq!( |
|
1348 | assert_eq!( | |
1338 | m.visit_children_set(HgPath::new(b"dir")), |
|
1349 | m.visit_children_set(HgPath::new(b"dir")), | |
1339 |
VisitChildrenSet:: |
|
1350 | VisitChildrenSet::This | |
1340 | ); |
|
1351 | ); | |
1341 | assert_eq!( |
|
1352 | assert_eq!( | |
1342 | m.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1353 | m.visit_children_set(HgPath::new(b"dir/subdir")), | |
1343 |
VisitChildrenSet:: |
|
1354 | VisitChildrenSet::This | |
1344 | ); |
|
1355 | ); | |
1345 |
|
1356 | |||
1346 | // VisitchildrensetRootfilesin |
|
1357 | // VisitchildrensetRootfilesin | |
1347 | let m = PatternMatcher::new(vec![IgnorePattern::new( |
|
1358 | let m = PatternMatcher::new(vec![IgnorePattern::new( | |
1348 | PatternSyntax::RootFiles, |
|
1359 | PatternSyntax::RootFilesIn, | |
1349 | b"dir/subdir", |
|
1360 | b"dir/subdir", | |
1350 | Path::new(""), |
|
1361 | Path::new(""), | |
1351 | )]) |
|
1362 | )]) | |
1352 | .unwrap(); |
|
1363 | .unwrap(); | |
1353 | assert_eq!( |
|
1364 | assert_eq!( | |
1354 | m.visit_children_set(HgPath::new(b"dir/subdir/x")), |
|
1365 | m.visit_children_set(HgPath::new(b"dir/subdir/x")), | |
1355 |
VisitChildrenSet:: |
|
1366 | VisitChildrenSet::This | |
1356 | ); |
|
1367 | ); | |
1357 | assert_eq!( |
|
1368 | assert_eq!( | |
1358 | m.visit_children_set(HgPath::new(b"folder")), |
|
1369 | m.visit_children_set(HgPath::new(b"folder")), | |
1359 | VisitChildrenSet::Empty |
|
1370 | VisitChildrenSet::Empty | |
1360 | ); |
|
1371 | ); | |
1361 | // FIXME: These should probably be {'dir'}, {'subdir'} and This, |
|
1372 | // FIXME: These should probably be {'dir'}, {'subdir'} and This, | |
1362 | // respectively, or at least This for all three. |
|
1373 | // respectively | |
1363 | assert_eq!( |
|
1374 | assert_eq!( | |
1364 | m.visit_children_set(HgPath::new(b"")), |
|
1375 | m.visit_children_set(HgPath::new(b"")), | |
1365 |
VisitChildrenSet:: |
|
1376 | VisitChildrenSet::This | |
1366 | ); |
|
1377 | ); | |
1367 | assert_eq!( |
|
1378 | assert_eq!( | |
1368 | m.visit_children_set(HgPath::new(b"dir")), |
|
1379 | m.visit_children_set(HgPath::new(b"dir")), | |
1369 |
VisitChildrenSet:: |
|
1380 | VisitChildrenSet::This | |
1370 | ); |
|
1381 | ); | |
1371 | assert_eq!( |
|
1382 | assert_eq!( | |
1372 | m.visit_children_set(HgPath::new(b"dir/subdir")), |
|
1383 | m.visit_children_set(HgPath::new(b"dir/subdir")), | |
1373 |
VisitChildrenSet:: |
|
1384 | VisitChildrenSet::This | |
1374 | ); |
|
1385 | ); | |
1375 |
|
1386 | |||
1376 | // VisitdirGlob |
|
1387 | // VisitdirGlob | |
@@ -1384,10 +1395,9 b' mod tests {' | |||||
1384 | m.visit_children_set(HgPath::new(b"")), |
|
1395 | m.visit_children_set(HgPath::new(b"")), | |
1385 | VisitChildrenSet::This |
|
1396 | VisitChildrenSet::This | |
1386 | ); |
|
1397 | ); | |
1387 | // FIXME: This probably should be This |
|
|||
1388 | assert_eq!( |
|
1398 | assert_eq!( | |
1389 | m.visit_children_set(HgPath::new(b"dir")), |
|
1399 | m.visit_children_set(HgPath::new(b"dir")), | |
1390 |
VisitChildrenSet:: |
|
1400 | VisitChildrenSet::This | |
1391 | ); |
|
1401 | ); | |
1392 | assert_eq!( |
|
1402 | assert_eq!( | |
1393 | m.visit_children_set(HgPath::new(b"folder")), |
|
1403 | m.visit_children_set(HgPath::new(b"folder")), | |
@@ -1418,10 +1428,9 b' mod tests {' | |||||
1418 | m.visit_children_set(HgPath::new(b"folder")), |
|
1428 | m.visit_children_set(HgPath::new(b"folder")), | |
1419 | VisitChildrenSet::Empty |
|
1429 | VisitChildrenSet::Empty | |
1420 | ); |
|
1430 | ); | |
1421 | // FIXME: This probably should be This |
|
|||
1422 | assert_eq!( |
|
1431 | assert_eq!( | |
1423 | m.visit_children_set(HgPath::new(b"dir")), |
|
1432 | m.visit_children_set(HgPath::new(b"dir")), | |
1424 |
VisitChildrenSet:: |
|
1433 | VisitChildrenSet::This | |
1425 | ); |
|
1434 | ); | |
1426 | // OPT: these should probably be Empty |
|
1435 | // OPT: these should probably be Empty | |
1427 | assert_eq!( |
|
1436 | assert_eq!( | |
@@ -1529,7 +1538,7 b' mod tests {' | |||||
1529 |
|
1538 | |||
1530 | // VisitchildrensetRootfilesin |
|
1539 | // VisitchildrensetRootfilesin | |
1531 | let matcher = IncludeMatcher::new(vec![IgnorePattern::new( |
|
1540 | let matcher = IncludeMatcher::new(vec![IgnorePattern::new( | |
1532 | PatternSyntax::RootFiles, |
|
1541 | PatternSyntax::RootFilesIn, | |
1533 | b"dir/subdir", |
|
1542 | b"dir/subdir", | |
1534 | Path::new(""), |
|
1543 | Path::new(""), | |
1535 | )]) |
|
1544 | )]) | |
@@ -1664,7 +1673,7 b' mod tests {' | |||||
1664 | )]) |
|
1673 | )]) | |
1665 | .unwrap(); |
|
1674 | .unwrap(); | |
1666 | let m2 = IncludeMatcher::new(vec![IgnorePattern::new( |
|
1675 | let m2 = IncludeMatcher::new(vec![IgnorePattern::new( | |
1667 | PatternSyntax::RootFiles, |
|
1676 | PatternSyntax::RootFilesIn, | |
1668 | b"dir", |
|
1677 | b"dir", | |
1669 | Path::new(""), |
|
1678 | Path::new(""), | |
1670 | )]) |
|
1679 | )]) | |
@@ -1825,7 +1834,7 b' mod tests {' | |||||
1825 | ); |
|
1834 | ); | |
1826 | let m2 = Box::new( |
|
1835 | let m2 = Box::new( | |
1827 | IncludeMatcher::new(vec![IgnorePattern::new( |
|
1836 | IncludeMatcher::new(vec![IgnorePattern::new( | |
1828 | PatternSyntax::RootFiles, |
|
1837 | PatternSyntax::RootFilesIn, | |
1829 | b"dir", |
|
1838 | b"dir", | |
1830 | Path::new(""), |
|
1839 | Path::new(""), | |
1831 | )]) |
|
1840 | )]) | |
@@ -2076,7 +2085,7 b' mod tests {' | |||||
2076 | ); |
|
2085 | ); | |
2077 | let m2 = Box::new( |
|
2086 | let m2 = Box::new( | |
2078 | IncludeMatcher::new(vec![IgnorePattern::new( |
|
2087 | IncludeMatcher::new(vec![IgnorePattern::new( | |
2079 | PatternSyntax::RootFiles, |
|
2088 | PatternSyntax::RootFilesIn, | |
2080 | b"dir", |
|
2089 | b"dir", | |
2081 | Path::new("/repo"), |
|
2090 | Path::new("/repo"), | |
2082 | )]) |
|
2091 | )]) | |
@@ -2119,4 +2128,323 b' mod tests {' | |||||
2119 | VisitChildrenSet::This |
|
2128 | VisitChildrenSet::This | |
2120 | ); |
|
2129 | ); | |
2121 | } |
|
2130 | } | |
|
2131 | ||||
|
2132 | mod invariants { | |||
|
2133 | pub mod visit_children_set { | |||
|
2134 | ||||
|
2135 | use crate::{ | |||
|
2136 | matchers::{tests::Tree, Matcher, VisitChildrenSet}, | |||
|
2137 | utils::hg_path::HgPath, | |||
|
2138 | }; | |||
|
2139 | ||||
|
2140 | #[allow(dead_code)] | |||
|
2141 | #[derive(Debug)] | |||
|
2142 | struct Error<'a, M> { | |||
|
2143 | matcher: &'a M, | |||
|
2144 | path: &'a HgPath, | |||
|
2145 | matching: &'a Tree, | |||
|
2146 | visit_children_set: &'a VisitChildrenSet, | |||
|
2147 | } | |||
|
2148 | ||||
|
2149 | fn holds( | |||
|
2150 | matching: &Tree, | |||
|
2151 | not_matching: &Tree, | |||
|
2152 | vcs: &VisitChildrenSet, | |||
|
2153 | ) -> bool { | |||
|
2154 | match vcs { | |||
|
2155 | VisitChildrenSet::Empty => matching.is_empty(), | |||
|
2156 | VisitChildrenSet::This => { | |||
|
2157 | // `This` does not come with any obligations. | |||
|
2158 | true | |||
|
2159 | } | |||
|
2160 | VisitChildrenSet::Recursive => { | |||
|
2161 | // `Recursive` requires that *everything* in the | |||
|
2162 | // subtree matches. This | |||
|
2163 | // requirement is relied on for example in | |||
|
2164 | // DifferenceMatcher implementation. | |||
|
2165 | not_matching.is_empty() | |||
|
2166 | } | |||
|
2167 | VisitChildrenSet::Set(allowed_children) => { | |||
|
2168 | // `allowed_children` does not distinguish between | |||
|
2169 | // files and directories: if it's not included, it | |||
|
2170 | // must not be matched. | |||
|
2171 | for k in matching.dirs.keys() { | |||
|
2172 | if !(allowed_children.contains(k)) { | |||
|
2173 | return false; | |||
|
2174 | } | |||
|
2175 | } | |||
|
2176 | for k in matching.files.iter() { | |||
|
2177 | if !(allowed_children.contains(k)) { | |||
|
2178 | return false; | |||
|
2179 | } | |||
|
2180 | } | |||
|
2181 | true | |||
|
2182 | } | |||
|
2183 | } | |||
|
2184 | } | |||
|
2185 | ||||
|
2186 | pub fn check<M: Matcher + std::fmt::Debug>( | |||
|
2187 | matcher: &M, | |||
|
2188 | path: &HgPath, | |||
|
2189 | matching: &Tree, | |||
|
2190 | not_matching: &Tree, | |||
|
2191 | visit_children_set: &VisitChildrenSet, | |||
|
2192 | ) { | |||
|
2193 | if !holds(matching, not_matching, visit_children_set) { | |||
|
2194 | panic!( | |||
|
2195 | "{:#?}", | |||
|
2196 | Error { | |||
|
2197 | matcher, | |||
|
2198 | path, | |||
|
2199 | visit_children_set, | |||
|
2200 | matching | |||
|
2201 | } | |||
|
2202 | ) | |||
|
2203 | } | |||
|
2204 | } | |||
|
2205 | } | |||
|
2206 | } | |||
|
2207 | ||||
|
2208 | #[derive(Debug, Clone)] | |||
|
2209 | pub struct Tree { | |||
|
2210 | files: BTreeSet<HgPathBuf>, | |||
|
2211 | dirs: BTreeMap<HgPathBuf, Tree>, | |||
|
2212 | } | |||
|
2213 | ||||
|
2214 | impl Tree { | |||
|
2215 | fn len(&self) -> usize { | |||
|
2216 | let mut n = 0; | |||
|
2217 | n += self.files.len(); | |||
|
2218 | for d in self.dirs.values() { | |||
|
2219 | n += d.len(); | |||
|
2220 | } | |||
|
2221 | n | |||
|
2222 | } | |||
|
2223 | ||||
|
2224 | fn is_empty(&self) -> bool { | |||
|
2225 | self.files.is_empty() && self.dirs.is_empty() | |||
|
2226 | } | |||
|
2227 | ||||
|
2228 | fn make( | |||
|
2229 | files: BTreeSet<HgPathBuf>, | |||
|
2230 | dirs: BTreeMap<HgPathBuf, Tree>, | |||
|
2231 | ) -> Self { | |||
|
2232 | Self { | |||
|
2233 | files, | |||
|
2234 | dirs: dirs | |||
|
2235 | .into_iter() | |||
|
2236 | .filter(|(_k, v)| (!(v.is_empty()))) | |||
|
2237 | .collect(), | |||
|
2238 | } | |||
|
2239 | } | |||
|
2240 | ||||
|
2241 | fn filter_and_check<M: Matcher + Debug>( | |||
|
2242 | &self, | |||
|
2243 | m: &M, | |||
|
2244 | path: &HgPath, | |||
|
2245 | ) -> (Self, Self) { | |||
|
2246 | let (files1, files2): (BTreeSet<HgPathBuf>, BTreeSet<HgPathBuf>) = | |||
|
2247 | self.files | |||
|
2248 | .iter() | |||
|
2249 | .map(|v| v.to_owned()) | |||
|
2250 | .partition(|v| m.matches(&path.join(v))); | |||
|
2251 | let (dirs1, dirs2): ( | |||
|
2252 | BTreeMap<HgPathBuf, Tree>, | |||
|
2253 | BTreeMap<HgPathBuf, Tree>, | |||
|
2254 | ) = self | |||
|
2255 | .dirs | |||
|
2256 | .iter() | |||
|
2257 | .map(|(k, v)| { | |||
|
2258 | let path = path.join(k); | |||
|
2259 | let (t1, t2) = v.filter_and_check(m, &path); | |||
|
2260 | ((k.clone(), t1), (k.clone(), t2)) | |||
|
2261 | }) | |||
|
2262 | .unzip(); | |||
|
2263 | let matching = Self::make(files1, dirs1); | |||
|
2264 | let not_matching = Self::make(files2, dirs2); | |||
|
2265 | let vcs = m.visit_children_set(path); | |||
|
2266 | invariants::visit_children_set::check( | |||
|
2267 | m, | |||
|
2268 | path, | |||
|
2269 | &matching, | |||
|
2270 | ¬_matching, | |||
|
2271 | &vcs, | |||
|
2272 | ); | |||
|
2273 | (matching, not_matching) | |||
|
2274 | } | |||
|
2275 | ||||
|
2276 | fn check_matcher<M: Matcher + Debug>( | |||
|
2277 | &self, | |||
|
2278 | m: &M, | |||
|
2279 | expect_count: usize, | |||
|
2280 | ) { | |||
|
2281 | let res = self.filter_and_check(m, &HgPathBuf::new()); | |||
|
2282 | if expect_count != res.0.len() { | |||
|
2283 | eprintln!( | |||
|
2284 | "warning: expected {} matches, got {} for {:#?}", | |||
|
2285 | expect_count, | |||
|
2286 | res.0.len(), | |||
|
2287 | m | |||
|
2288 | ); | |||
|
2289 | } | |||
|
2290 | } | |||
|
2291 | } | |||
|
2292 | ||||
|
2293 | fn mkdir(children: &[(&[u8], &Tree)]) -> Tree { | |||
|
2294 | let p = HgPathBuf::from_bytes; | |||
|
2295 | let names = [ | |||
|
2296 | p(b"a"), | |||
|
2297 | p(b"b.txt"), | |||
|
2298 | p(b"file.txt"), | |||
|
2299 | p(b"c.c"), | |||
|
2300 | p(b"c.h"), | |||
|
2301 | p(b"dir1"), | |||
|
2302 | p(b"dir2"), | |||
|
2303 | p(b"subdir"), | |||
|
2304 | ]; | |||
|
2305 | let files: BTreeSet<HgPathBuf> = BTreeSet::from(names); | |||
|
2306 | let dirs = children | |||
|
2307 | .iter() | |||
|
2308 | .map(|(name, t)| (p(name), (*t).clone())) | |||
|
2309 | .collect(); | |||
|
2310 | Tree { files, dirs } | |||
|
2311 | } | |||
|
2312 | ||||
|
2313 | fn make_example_tree() -> Tree { | |||
|
2314 | let leaf = mkdir(&[]); | |||
|
2315 | let abc = mkdir(&[(b"d", &leaf)]); | |||
|
2316 | let ab = mkdir(&[(b"c", &abc)]); | |||
|
2317 | let a = mkdir(&[(b"b", &ab)]); | |||
|
2318 | let dir = mkdir(&[(b"subdir", &leaf), (b"subdir.c", &leaf)]); | |||
|
2319 | mkdir(&[(b"dir", &dir), (b"dir1", &dir), (b"dir2", &dir), (b"a", &a)]) | |||
|
2320 | } | |||
|
2321 | ||||
|
2322 | #[test] | |||
|
2323 | fn test_pattern_matcher_visit_children_set() { | |||
|
2324 | let tree = make_example_tree(); | |||
|
2325 | let pattern_dir1_glob_c = | |||
|
2326 | PatternMatcher::new(vec![IgnorePattern::new( | |||
|
2327 | PatternSyntax::Glob, | |||
|
2328 | b"dir1/*.c", | |||
|
2329 | Path::new(""), | |||
|
2330 | )]) | |||
|
2331 | .unwrap(); | |||
|
2332 | let pattern_dir1 = || { | |||
|
2333 | PatternMatcher::new(vec![IgnorePattern::new( | |||
|
2334 | PatternSyntax::Path, | |||
|
2335 | b"dir1", | |||
|
2336 | Path::new(""), | |||
|
2337 | )]) | |||
|
2338 | .unwrap() | |||
|
2339 | }; | |||
|
2340 | let pattern_dir1_a = PatternMatcher::new(vec![IgnorePattern::new( | |||
|
2341 | PatternSyntax::Glob, | |||
|
2342 | b"dir1/a", | |||
|
2343 | Path::new(""), | |||
|
2344 | )]) | |||
|
2345 | .unwrap(); | |||
|
2346 | let pattern_relglob_c = || { | |||
|
2347 | PatternMatcher::new(vec![IgnorePattern::new( | |||
|
2348 | PatternSyntax::RelGlob, | |||
|
2349 | b"*.c", | |||
|
2350 | Path::new(""), | |||
|
2351 | )]) | |||
|
2352 | .unwrap() | |||
|
2353 | }; | |||
|
2354 | let files = vec![HgPathBuf::from_bytes(b"dir/subdir/b.txt")]; | |||
|
2355 | let file_dir_subdir_b = FileMatcher::new(files).unwrap(); | |||
|
2356 | ||||
|
2357 | let files = vec![ | |||
|
2358 | HgPathBuf::from_bytes(b"file.txt"), | |||
|
2359 | HgPathBuf::from_bytes(b"a/file.txt"), | |||
|
2360 | HgPathBuf::from_bytes(b"a/b/file.txt"), | |||
|
2361 | // No file in a/b/c | |||
|
2362 | HgPathBuf::from_bytes(b"a/b/c/d/file.txt"), | |||
|
2363 | ]; | |||
|
2364 | let file_abcdfile = FileMatcher::new(files).unwrap(); | |||
|
2365 | let rootfilesin_dir = PatternMatcher::new(vec![IgnorePattern::new( | |||
|
2366 | PatternSyntax::RootFilesIn, | |||
|
2367 | b"dir", | |||
|
2368 | Path::new(""), | |||
|
2369 | )]) | |||
|
2370 | .unwrap(); | |||
|
2371 | ||||
|
2372 | let pattern_filepath_dir_subdir = | |||
|
2373 | PatternMatcher::new(vec![IgnorePattern::new( | |||
|
2374 | PatternSyntax::FilePath, | |||
|
2375 | b"dir/subdir", | |||
|
2376 | Path::new(""), | |||
|
2377 | )]) | |||
|
2378 | .unwrap(); | |||
|
2379 | ||||
|
2380 | let include_dir_subdir = | |||
|
2381 | IncludeMatcher::new(vec![IgnorePattern::new( | |||
|
2382 | PatternSyntax::RelPath, | |||
|
2383 | b"dir/subdir", | |||
|
2384 | Path::new(""), | |||
|
2385 | )]) | |||
|
2386 | .unwrap(); | |||
|
2387 | ||||
|
2388 | let more_includematchers = [ | |||
|
2389 | IncludeMatcher::new(vec![IgnorePattern::new( | |||
|
2390 | PatternSyntax::Glob, | |||
|
2391 | b"dir/s*", | |||
|
2392 | Path::new(""), | |||
|
2393 | )]) | |||
|
2394 | .unwrap(), | |||
|
2395 | // Test multiple patterns | |||
|
2396 | IncludeMatcher::new(vec![ | |||
|
2397 | IgnorePattern::new( | |||
|
2398 | PatternSyntax::RelPath, | |||
|
2399 | b"dir", | |||
|
2400 | Path::new(""), | |||
|
2401 | ), | |||
|
2402 | IgnorePattern::new(PatternSyntax::Glob, b"s*", Path::new("")), | |||
|
2403 | ]) | |||
|
2404 | .unwrap(), | |||
|
2405 | // Test multiple patterns | |||
|
2406 | IncludeMatcher::new(vec![IgnorePattern::new( | |||
|
2407 | PatternSyntax::Glob, | |||
|
2408 | b"**/*.c", | |||
|
2409 | Path::new(""), | |||
|
2410 | )]) | |||
|
2411 | .unwrap(), | |||
|
2412 | ]; | |||
|
2413 | ||||
|
2414 | tree.check_matcher(&pattern_dir1(), 25); | |||
|
2415 | tree.check_matcher(&pattern_dir1_a, 1); | |||
|
2416 | tree.check_matcher(&pattern_dir1_glob_c, 2); | |||
|
2417 | tree.check_matcher(&pattern_relglob_c(), 14); | |||
|
2418 | tree.check_matcher(&AlwaysMatcher, 112); | |||
|
2419 | tree.check_matcher(&NeverMatcher, 0); | |||
|
2420 | tree.check_matcher( | |||
|
2421 | &IntersectionMatcher::new( | |||
|
2422 | Box::new(pattern_relglob_c()), | |||
|
2423 | Box::new(pattern_dir1()), | |||
|
2424 | ), | |||
|
2425 | 3, | |||
|
2426 | ); | |||
|
2427 | tree.check_matcher( | |||
|
2428 | &UnionMatcher::new(vec![ | |||
|
2429 | Box::new(pattern_relglob_c()), | |||
|
2430 | Box::new(pattern_dir1()), | |||
|
2431 | ]), | |||
|
2432 | 36, | |||
|
2433 | ); | |||
|
2434 | tree.check_matcher( | |||
|
2435 | &DifferenceMatcher::new( | |||
|
2436 | Box::new(pattern_relglob_c()), | |||
|
2437 | Box::new(pattern_dir1()), | |||
|
2438 | ), | |||
|
2439 | 11, | |||
|
2440 | ); | |||
|
2441 | tree.check_matcher(&file_dir_subdir_b, 1); | |||
|
2442 | tree.check_matcher(&file_abcdfile, 4); | |||
|
2443 | tree.check_matcher(&rootfilesin_dir, 8); | |||
|
2444 | tree.check_matcher(&pattern_filepath_dir_subdir, 1); | |||
|
2445 | tree.check_matcher(&include_dir_subdir, 9); | |||
|
2446 | tree.check_matcher(&more_includematchers[0], 17); | |||
|
2447 | tree.check_matcher(&more_includematchers[1], 25); | |||
|
2448 | tree.check_matcher(&more_includematchers[2], 35); | |||
|
2449 | } | |||
2122 | } |
|
2450 | } |
@@ -120,6 +120,10 b' pub fn find_dirs(path: &HgPath) -> Ances' | |||||
120 | dirs |
|
120 | dirs | |
121 | } |
|
121 | } | |
122 |
|
122 | |||
|
123 | pub fn dir_ancestors(path: &HgPath) -> Ancestors { | |||
|
124 | Ancestors { next: Some(path) } | |||
|
125 | } | |||
|
126 | ||||
123 | /// Returns an iterator yielding ancestor directories of the given repository |
|
127 | /// Returns an iterator yielding ancestor directories of the given repository | |
124 | /// path. |
|
128 | /// path. | |
125 | /// |
|
129 | /// |
@@ -232,6 +232,10 b' class hgcommand:' | |||||
232 | print("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr) |
|
232 | print("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr) | |
233 | print(err, file=sys.stderr) |
|
233 | print(err, file=sys.stderr) | |
234 | if returncode != 0: |
|
234 | if returncode != 0: | |
|
235 | print( | |||
|
236 | "non zero-return '%s': %d" % (' '.join(cmd), returncode), | |||
|
237 | file=sys.stderr, | |||
|
238 | ) | |||
235 | return b'' |
|
239 | return b'' | |
236 | return out |
|
240 | return out | |
237 |
|
241 |
@@ -30,7 +30,7 b' class bannerserver(wireprotoserver.sshse' | |||||
30 |
|
30 | |||
31 | def serve_forever(self): |
|
31 | def serve_forever(self): | |
32 | for i in range(10): |
|
32 | for i in range(10): | |
33 | self._fout.write(b'banner: line %d\n' % i) |
|
33 | self._ui.fout.write(b'banner: line %d\n' % i) | |
34 |
|
34 | |||
35 | super(bannerserver, self).serve_forever() |
|
35 | super(bannerserver, self).serve_forever() | |
36 |
|
36 | |||
@@ -45,17 +45,16 b' class prehelloserver(wireprotoserver.ssh' | |||||
45 | """ |
|
45 | """ | |
46 |
|
46 | |||
47 | def serve_forever(self): |
|
47 | def serve_forever(self): | |
48 |
|
|
48 | ui = self._ui | |
|
49 | l = ui.fin.readline() | |||
49 | assert l == b'hello\n' |
|
50 | assert l == b'hello\n' | |
50 | # Respond to unknown commands with an empty reply. |
|
51 | # Respond to unknown commands with an empty reply. | |
51 |
wireprotoserver._sshv1respondbytes( |
|
52 | wireprotoserver._sshv1respondbytes(ui.fout, b'') | |
52 |
l = |
|
53 | l = ui.fin.readline() | |
53 | assert l == b'between\n' |
|
54 | assert l == b'between\n' | |
54 | proto = wireprotoserver.sshv1protocolhandler( |
|
55 | proto = wireprotoserver.sshv1protocolhandler(ui, ui.fin, ui.fout) | |
55 | self._ui, self._fin, self._fout |
|
|||
56 | ) |
|
|||
57 | rsp = wireprotov1server.dispatch(self._repo, proto, b'between') |
|
56 | rsp = wireprotov1server.dispatch(self._repo, proto, b'between') | |
58 |
wireprotoserver._sshv1respondbytes( |
|
57 | wireprotoserver._sshv1respondbytes(ui.fout, rsp.data) | |
59 |
|
58 | |||
60 | super(prehelloserver, self).serve_forever() |
|
59 | super(prehelloserver, self).serve_forever() | |
61 |
|
60 |
@@ -620,3 +620,93 b' Test controlling the changegroup version' | |||||
620 | b9f5f740a8cd76700020e3903ee55ecff78bd3e5 |
|
620 | b9f5f740a8cd76700020e3903ee55ecff78bd3e5 | |
621 | $ hg debugbundle ./v2-cg-03.hg --spec |
|
621 | $ hg debugbundle ./v2-cg-03.hg --spec | |
622 | bzip2-v2;cg.version=03 |
|
622 | bzip2-v2;cg.version=03 | |
|
623 | ||||
|
624 | tests controlling bundle contents | |||
|
625 | ================================= | |||
|
626 | ||||
|
627 | $ hg debugupdatecache -R t1 | |||
|
628 | ||||
|
629 | default content | |||
|
630 | --------------- | |||
|
631 | ||||
|
632 | $ hg -R t1 bundle --all --quiet --type 'v2' ./v2.hg | |||
|
633 | $ hg debugbundle ./v2.hg --spec | |||
|
634 | bzip2-v2 | |||
|
635 | $ hg debugbundle ./v2.hg --quiet | |||
|
636 | Stream params: {Compression: BZ} | |||
|
637 | changegroup -- {nbchanges: 7, version: 02} (mandatory: True) | |||
|
638 | hgtagsfnodes -- {} (mandatory: False) | |||
|
639 | cache:rev-branch-cache -- {} (mandatory: False) | |||
|
640 | ||||
|
641 | $ hg -R t1 bundle --all --quiet --type 'v3' ./v3.hg | |||
|
642 | $ hg debugbundle ./v3.hg --spec | |||
|
643 | bzip2-v2;cg.version=03 | |||
|
644 | $ hg debugbundle ./v3.hg --quiet | |||
|
645 | Stream params: {Compression: BZ} | |||
|
646 | changegroup -- {nbchanges: 7, targetphase: 2, version: 03} (mandatory: True) | |||
|
647 | hgtagsfnodes -- {} (mandatory: False) | |||
|
648 | cache:rev-branch-cache -- {} (mandatory: False) | |||
|
649 | phase-heads -- {} (mandatory: True) | |||
|
650 | ||||
|
651 | adding extra parts | |||
|
652 | ------------------ | |||
|
653 | ||||
|
654 | We should have a "phase-heads" part here that we did not had in the default content | |||
|
655 | ||||
|
656 | $ hg -R t1 bundle --all --quiet --type 'v2;phases=1' ./v2-phases.hg | |||
|
657 | $ hg debugbundle ./v2-phases.hg --spec | |||
|
658 | bzip2-v2 | |||
|
659 | $ hg debugbundle ./v2-phases.hg --quiet | |||
|
660 | Stream params: {Compression: BZ} | |||
|
661 | changegroup -- {nbchanges: 7, targetphase: 2, version: 02} (mandatory: True) | |||
|
662 | hgtagsfnodes -- {} (mandatory: False) | |||
|
663 | cache:rev-branch-cache -- {} (mandatory: False) | |||
|
664 | phase-heads -- {} (mandatory: True) | |||
|
665 | ||||
|
666 | skipping default inclusion | |||
|
667 | -------------------------- | |||
|
668 | ||||
|
669 | $ hg -R t1 bundle --all --quiet --type 'v2;tagsfnodescache=false' ./v2-no-tfc.hg | |||
|
670 | $ hg debugbundle ./v2-no-tfc.hg --spec | |||
|
671 | bzip2-v2 | |||
|
672 | $ hg debugbundle ./v2-no-tfc.hg --quiet | |||
|
673 | Stream params: {Compression: BZ} | |||
|
674 | changegroup -- {nbchanges: 7, version: 02} (mandatory: True) | |||
|
675 | cache:rev-branch-cache -- {} (mandatory: False) | |||
|
676 | ||||
|
677 | $ hg -R t1 bundle --all --quiet --type 'v3;phases=0' ./v3-no-phases.hg | |||
|
678 | $ hg debugbundle ./v3-no-phases.hg --spec | |||
|
679 | bzip2-v2;cg.version=03 | |||
|
680 | $ hg debugbundle ./v3-no-phases.hg --quiet | |||
|
681 | Stream params: {Compression: BZ} | |||
|
682 | changegroup -- {nbchanges: 7, version: 03} (mandatory: True) | |||
|
683 | hgtagsfnodes -- {} (mandatory: False) | |||
|
684 | cache:rev-branch-cache -- {} (mandatory: False) | |||
|
685 | ||||
|
686 | $ hg -R t1 bundle --all --quiet --type 'v3;phases=no;tagsfnodescache=0' ./v3-multi-no.hg | |||
|
687 | $ hg debugbundle ./v3-multi-no.hg --spec | |||
|
688 | bzip2-v2;cg.version=03 | |||
|
689 | $ hg debugbundle ./v3-multi-no.hg --quiet | |||
|
690 | Stream params: {Compression: BZ} | |||
|
691 | changegroup -- {nbchanges: 7, version: 03} (mandatory: True) | |||
|
692 | cache:rev-branch-cache -- {} (mandatory: False) | |||
|
693 | ||||
|
694 | skipping changegroup | |||
|
695 | -------------------- | |||
|
696 | ||||
|
697 | $ hg -R t1 bundle --all --quiet --type 'v2;changegroup=no' ./v2-no-cg.hg | |||
|
698 | $ hg debugbundle ./v2-no-cg.hg --spec | |||
|
699 | bzip2-v2;changegroup=no | |||
|
700 | $ hg debugbundle ./v2-no-cg.hg --quiet | |||
|
701 | Stream params: {Compression: BZ} | |||
|
702 | hgtagsfnodes -- {} (mandatory: False) | |||
|
703 | cache:rev-branch-cache -- {} (mandatory: False) | |||
|
704 | ||||
|
705 | $ hg -R t1 bundle --all --quiet --type 'v3;changegroup=0' ./v3-no-cg.hg | |||
|
706 | $ hg debugbundle ./v3-no-cg.hg --spec | |||
|
707 | bzip2-v2;changegroup=no | |||
|
708 | $ hg debugbundle ./v3-no-cg.hg --quiet | |||
|
709 | Stream params: {Compression: BZ} | |||
|
710 | hgtagsfnodes -- {} (mandatory: False) | |||
|
711 | cache:rev-branch-cache -- {} (mandatory: False) | |||
|
712 | phase-heads -- {} (mandatory: True) |
@@ -94,12 +94,14 b' class PatternMatcherTests(unittest.TestC' | |||||
94 | patterns=[b'rootfilesin:dir/subdir'], |
|
94 | patterns=[b'rootfilesin:dir/subdir'], | |
95 | ) |
|
95 | ) | |
96 | assert isinstance(m, matchmod.patternmatcher) |
|
96 | assert isinstance(m, matchmod.patternmatcher) | |
97 | self.assertFalse(m.visitdir(b'dir/subdir/x')) |
|
97 | # OPT: we shouldn't visit [x] as a directory, | |
|
98 | # but we should still visit it as a file. | |||
|
99 | # Unfortunately, `visitdir` is used for both. | |||
|
100 | self.assertTrue(m.visitdir(b'dir/subdir/x')) | |||
98 | self.assertFalse(m.visitdir(b'folder')) |
|
101 | self.assertFalse(m.visitdir(b'folder')) | |
99 | # FIXME: These should probably be True. |
|
102 | self.assertTrue(m.visitdir(b'')) | |
100 |
self.assert |
|
103 | self.assertTrue(m.visitdir(b'dir')) | |
101 |
self.assert |
|
104 | self.assertTrue(m.visitdir(b'dir/subdir')) | |
102 | self.assertFalse(m.visitdir(b'dir/subdir')) |
|
|||
103 |
|
105 | |||
104 | def testVisitchildrensetRootfilesin(self): |
|
106 | def testVisitchildrensetRootfilesin(self): | |
105 | m = matchmod.match( |
|
107 | m = matchmod.match( | |
@@ -108,13 +110,13 b' class PatternMatcherTests(unittest.TestC' | |||||
108 | patterns=[b'rootfilesin:dir/subdir'], |
|
110 | patterns=[b'rootfilesin:dir/subdir'], | |
109 | ) |
|
111 | ) | |
110 | assert isinstance(m, matchmod.patternmatcher) |
|
112 | assert isinstance(m, matchmod.patternmatcher) | |
111 |
self.assertEqual(m.visitchildrenset(b'dir/subdir/x'), |
|
113 | self.assertEqual(m.visitchildrenset(b'dir/subdir/x'), b'this') | |
112 | self.assertEqual(m.visitchildrenset(b'folder'), set()) |
|
114 | self.assertEqual(m.visitchildrenset(b'folder'), set()) | |
113 |
# |
|
115 | # OPT: These should probably be {'dir'}, {'subdir'} and 'this', | |
114 | # respectively, or at least 'this' for all three. |
|
116 | # respectively | |
115 |
self.assertEqual(m.visitchildrenset(b''), |
|
117 | self.assertEqual(m.visitchildrenset(b''), b'this') | |
116 |
self.assertEqual(m.visitchildrenset(b'dir'), |
|
118 | self.assertEqual(m.visitchildrenset(b'dir'), b'this') | |
117 |
self.assertEqual(m.visitchildrenset(b'dir/subdir'), |
|
119 | self.assertEqual(m.visitchildrenset(b'dir/subdir'), b'this') | |
118 |
|
120 | |||
119 | def testVisitdirGlob(self): |
|
121 | def testVisitdirGlob(self): | |
120 | m = matchmod.match( |
|
122 | m = matchmod.match( |
@@ -25,9 +25,8 b' class SSHServerGetArgsTests(unittest.Tes' | |||||
25 |
|
25 | |||
26 | def assertparse(self, cmd, input, expected): |
|
26 | def assertparse(self, cmd, input, expected): | |
27 | server = mockserver(input) |
|
27 | server = mockserver(input) | |
28 | proto = wireprotoserver.sshv1protocolhandler( |
|
28 | ui = server._ui | |
29 | server._ui, server._fin, server._fout |
|
29 | proto = wireprotoserver.sshv1protocolhandler(ui, ui.fin, ui.fout) | |
30 | ) |
|
|||
31 | _func, spec = wireprotov1server.commands[cmd] |
|
30 | _func, spec = wireprotov1server.commands[cmd] | |
32 | self.assertEqual(proto.getargs(spec), expected) |
|
31 | self.assertEqual(proto.getargs(spec), expected) | |
33 |
|
32 | |||
@@ -35,6 +34,9 b' class SSHServerGetArgsTests(unittest.Tes' | |||||
35 | def mockserver(inbytes): |
|
34 | def mockserver(inbytes): | |
36 | ui = mockui(inbytes) |
|
35 | ui = mockui(inbytes) | |
37 | repo = mockrepo(ui) |
|
36 | repo = mockrepo(ui) | |
|
37 | # note: this test unfortunately doesn't really test anything about | |||
|
38 | # `sshserver` class anymore: the entirety of logic of that class lives | |||
|
39 | # in `serveuntil`, and that function is not even called by this test. | |||
38 | return wireprotoserver.sshserver(ui, repo) |
|
40 | return wireprotoserver.sshserver(ui, repo) | |
39 |
|
41 | |||
40 |
|
42 |
@@ -842,6 +842,26 b' Check the output' | |||||
842 | C clean |
|
842 | C clean | |
843 | C subdir/clean |
|
843 | C subdir/clean | |
844 |
|
844 | |||
|
845 | Test various matchers interatction with dirstate code: | |||
|
846 | ||||
|
847 | $ hg status path:subdir | |||
|
848 | M subdir/modified | |||
|
849 | R subdir/removed | |||
|
850 | ! subdir/deleted | |||
|
851 | ? subdir/unknown | |||
|
852 | ||||
|
853 | $ hg status 'glob:subdir/*' | |||
|
854 | M subdir/modified | |||
|
855 | R subdir/removed | |||
|
856 | ! subdir/deleted | |||
|
857 | ? subdir/unknown | |||
|
858 | ||||
|
859 | $ hg status rootfilesin:subdir | |||
|
860 | M subdir/modified | |||
|
861 | R subdir/removed | |||
|
862 | ! subdir/deleted | |||
|
863 | ? subdir/unknown | |||
|
864 | ||||
845 | Note: `hg status some-name` creates a patternmatcher which is not supported |
|
865 | Note: `hg status some-name` creates a patternmatcher which is not supported | |
846 | yet by the Rust implementation of status, but includematcher is supported. |
|
866 | yet by the Rust implementation of status, but includematcher is supported. | |
847 | --include is used below for that reason |
|
867 | --include is used below for that reason |
General Comments 0
You need to be logged in to leave comments.
Login now