rust/hg-core/src/dirstate_tree/status.rs
changeset 49520 eb02decdf0ab
parent 49421 7e5377bdb66e
child 49554 ecf9788cd9c4
child 49563 6b32d39e9a67
equal deleted inserted replaced
49519:943509a58d29 49520:eb02decdf0ab
    18 use crate::HgPathCow;
    18 use crate::HgPathCow;
    19 use crate::PatternFileWarning;
    19 use crate::PatternFileWarning;
    20 use crate::StatusError;
    20 use crate::StatusError;
    21 use crate::StatusOptions;
    21 use crate::StatusOptions;
    22 use micro_timer::timed;
    22 use micro_timer::timed;
       
    23 use once_cell::sync::OnceCell;
    23 use rayon::prelude::*;
    24 use rayon::prelude::*;
    24 use sha1::{Digest, Sha1};
    25 use sha1::{Digest, Sha1};
    25 use std::borrow::Cow;
    26 use std::borrow::Cow;
    26 use std::io;
    27 use std::io;
    27 use std::path::Path;
    28 use std::path::Path;
   124         outated_cached_directories: Default::default(),
   125         outated_cached_directories: Default::default(),
   125         filesystem_time_at_status_start,
   126         filesystem_time_at_status_start,
   126     };
   127     };
   127     let is_at_repo_root = true;
   128     let is_at_repo_root = true;
   128     let hg_path = &BorrowedPath::OnDisk(HgPath::new(""));
   129     let hg_path = &BorrowedPath::OnDisk(HgPath::new(""));
   129     let has_ignored_ancestor = false;
   130     let has_ignored_ancestor = HasIgnoredAncestor::create(None, hg_path);
   130     let root_cached_mtime = None;
   131     let root_cached_mtime = None;
   131     let root_dir_metadata = None;
   132     let root_dir_metadata = None;
   132     // If the path we have for the repository root is a symlink, do follow it.
   133     // If the path we have for the repository root is a symlink, do follow it.
   133     // (As opposed to symlinks within the working directory which are not
   134     // (As opposed to symlinks within the working directory which are not
   134     // followed, using `std::fs::symlink_metadata`.)
   135     // followed, using `std::fs::symlink_metadata`.)
   135     common.traverse_fs_directory_and_dirstate(
   136     common.traverse_fs_directory_and_dirstate(
   136         has_ignored_ancestor,
   137         &has_ignored_ancestor,
   137         dmap.root.as_ref(),
   138         dmap.root.as_ref(),
   138         hg_path,
   139         hg_path,
   139         &root_dir,
   140         &root_dir,
   140         root_dir_metadata,
   141         root_dir_metadata,
   141         root_cached_mtime,
   142         root_cached_mtime,
   192     Deleted,
   193     Deleted,
   193     Clean,
   194     Clean,
   194     Ignored,
   195     Ignored,
   195     Unknown,
   196     Unknown,
   196     Unsure,
   197     Unsure,
       
   198 }
       
   199 
       
   200 /// Lazy computation of whether a given path has a hgignored
       
   201 /// ancestor.
       
   202 struct HasIgnoredAncestor<'a> {
       
   203     /// `path` and `parent` constitute the inputs to the computation,
       
   204     /// `cache` stores the outcome.
       
   205     path: &'a HgPath,
       
   206     parent: Option<&'a HasIgnoredAncestor<'a>>,
       
   207     cache: OnceCell<bool>,
       
   208 }
       
   209 
       
   210 impl<'a> HasIgnoredAncestor<'a> {
       
   211     fn create(
       
   212         parent: Option<&'a HasIgnoredAncestor<'a>>,
       
   213         path: &'a HgPath,
       
   214     ) -> HasIgnoredAncestor<'a> {
       
   215         Self {
       
   216             path,
       
   217             parent,
       
   218             cache: OnceCell::new(),
       
   219         }
       
   220     }
       
   221 
       
   222     fn force<'b>(&self, ignore_fn: &IgnoreFnType<'b>) -> bool {
       
   223         match self.parent {
       
   224             None => false,
       
   225             Some(parent) => {
       
   226                 *(parent.cache.get_or_init(|| {
       
   227                     parent.force(ignore_fn) || ignore_fn(&self.path)
       
   228                 }))
       
   229             }
       
   230         }
       
   231     }
   197 }
   232 }
   198 
   233 
   199 impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> {
   234 impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> {
   200     fn push_outcome(
   235     fn push_outcome(
   201         &self,
   236         &self,
   316         false
   351         false
   317     }
   352     }
   318 
   353 
   319     /// Returns whether all child entries of the filesystem directory have a
   354     /// Returns whether all child entries of the filesystem directory have a
   320     /// corresponding dirstate node or are ignored.
   355     /// corresponding dirstate node or are ignored.
   321     fn traverse_fs_directory_and_dirstate(
   356     fn traverse_fs_directory_and_dirstate<'ancestor>(
   322         &self,
   357         &self,
   323         has_ignored_ancestor: bool,
   358         has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>,
   324         dirstate_nodes: ChildNodesRef<'tree, 'on_disk>,
   359         dirstate_nodes: ChildNodesRef<'tree, 'on_disk>,
   325         directory_hg_path: &BorrowedPath<'tree, 'on_disk>,
   360         directory_hg_path: &BorrowedPath<'tree, 'on_disk>,
   326         directory_fs_path: &Path,
   361         directory_fs_path: &Path,
   327         directory_metadata: Option<&std::fs::Metadata>,
   362         directory_metadata: Option<&std::fs::Metadata>,
   328         cached_directory_mtime: Option<TruncatedTimestamp>,
   363         cached_directory_mtime: Option<TruncatedTimestamp>,
   416                     self.traverse_dirstate_only(dirstate_node)?;
   451                     self.traverse_dirstate_only(dirstate_node)?;
   417                     has_dirstate_node_or_is_ignored = true;
   452                     has_dirstate_node_or_is_ignored = true;
   418                 }
   453                 }
   419                 Right(fs_entry) => {
   454                 Right(fs_entry) => {
   420                     has_dirstate_node_or_is_ignored = self.traverse_fs_only(
   455                     has_dirstate_node_or_is_ignored = self.traverse_fs_only(
   421                         has_ignored_ancestor,
   456                         has_ignored_ancestor.force(&self.ignore_fn),
   422                         directory_hg_path,
   457                         directory_hg_path,
   423                         fs_entry,
   458                         fs_entry,
   424                     )
   459                     )
   425                 }
   460                 }
   426             }
   461             }
   427             Ok(has_dirstate_node_or_is_ignored)
   462             Ok(has_dirstate_node_or_is_ignored)
   428         })
   463         })
   429         .try_reduce(|| true, |a, b| Ok(a && b))
   464         .try_reduce(|| true, |a, b| Ok(a && b))
   430     }
   465     }
   431 
   466 
   432     fn traverse_fs_and_dirstate(
   467     fn traverse_fs_and_dirstate<'ancestor>(
   433         &self,
   468         &self,
   434         fs_path: &Path,
   469         fs_path: &Path,
   435         fs_metadata: &std::fs::Metadata,
   470         fs_metadata: &std::fs::Metadata,
   436         dirstate_node: NodeRef<'tree, 'on_disk>,
   471         dirstate_node: NodeRef<'tree, 'on_disk>,
   437         has_ignored_ancestor: bool,
   472         has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>,
   438     ) -> Result<(), DirstateV2ParseError> {
   473     ) -> Result<(), DirstateV2ParseError> {
   439         self.check_for_outdated_directory_cache(&dirstate_node)?;
   474         self.check_for_outdated_directory_cache(&dirstate_node)?;
   440         let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?;
   475         let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?;
   441         let file_type = fs_metadata.file_type();
   476         let file_type = fs_metadata.file_type();
   442         let file_or_symlink = file_type.is_file() || file_type.is_symlink();
   477         let file_or_symlink = file_type.is_file() || file_type.is_symlink();
   452                     .lock()
   487                     .lock()
   453                     .unwrap()
   488                     .unwrap()
   454                     .traversed
   489                     .traversed
   455                     .push(hg_path.detach_from_tree())
   490                     .push(hg_path.detach_from_tree())
   456             }
   491             }
   457             let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path);
   492             let is_ignored = HasIgnoredAncestor::create(
       
   493                 Some(&has_ignored_ancestor),
       
   494                 hg_path,
       
   495             );
   458             let is_at_repo_root = false;
   496             let is_at_repo_root = false;
   459             let children_all_have_dirstate_node_or_are_ignored = self
   497             let children_all_have_dirstate_node_or_are_ignored = self
   460                 .traverse_fs_directory_and_dirstate(
   498                 .traverse_fs_directory_and_dirstate(
   461                     is_ignored,
   499                     &is_ignored,
   462                     dirstate_node.children(self.dmap.on_disk)?,
   500                     dirstate_node.children(self.dmap.on_disk)?,
   463                     hg_path,
   501                     hg_path,
   464                     fs_path,
   502                     fs_path,
   465                     Some(fs_metadata),
   503                     Some(fs_metadata),
   466                     dirstate_node.cached_directory_mtime()?,
   504                     dirstate_node.cached_directory_mtime()?,
   470                 children_all_have_dirstate_node_or_are_ignored,
   508                 children_all_have_dirstate_node_or_are_ignored,
   471                 fs_metadata,
   509                 fs_metadata,
   472                 dirstate_node,
   510                 dirstate_node,
   473             )?
   511             )?
   474         } else {
   512         } else {
   475             if file_or_symlink && self.matcher.matches(hg_path) {
   513             if file_or_symlink && self.matcher.matches(&hg_path) {
   476                 if let Some(entry) = dirstate_node.entry()? {
   514                 if let Some(entry) = dirstate_node.entry()? {
   477                     if !entry.any_tracked() {
   515                     if !entry.any_tracked() {
   478                         // Forward-compat if we start tracking unknown/ignored
   516                         // Forward-compat if we start tracking unknown/ignored
   479                         // files for caching reasons
   517                         // files for caching reasons
   480                         self.mark_unknown_or_ignored(
   518                         self.mark_unknown_or_ignored(
   481                             has_ignored_ancestor,
   519                             has_ignored_ancestor.force(&self.ignore_fn),
   482                             hg_path,
   520                             &hg_path,
   483                         );
   521                         );
   484                     }
   522                     }
   485                     if entry.added() {
   523                     if entry.added() {
   486                         self.push_outcome(Outcome::Added, &dirstate_node)?;
   524                         self.push_outcome(Outcome::Added, &dirstate_node)?;
   487                     } else if entry.removed() {
   525                     } else if entry.removed() {
   493                     }
   531                     }
   494                 } else {
   532                 } else {
   495                     // `node.entry.is_none()` indicates a "directory"
   533                     // `node.entry.is_none()` indicates a "directory"
   496                     // node, but the filesystem has a file
   534                     // node, but the filesystem has a file
   497                     self.mark_unknown_or_ignored(
   535                     self.mark_unknown_or_ignored(
   498                         has_ignored_ancestor,
   536                         has_ignored_ancestor.force(&self.ignore_fn),
   499                         hg_path,
   537                         hg_path,
   500                     );
   538                     );
   501                 }
   539                 }
   502             }
   540             }
   503 
   541