rust/hg-core/src/copy_tracing.rs
changeset 46151 c6bc77f7e593
parent 46149 294d5aca4ff5
child 46152 e166e8a035a7
equal deleted inserted replaced
46150:a132aa5979ec 46151:c6bc77f7e593
    10 use std::collections::HashMap;
    10 use std::collections::HashMap;
    11 use std::convert::TryInto;
    11 use std::convert::TryInto;
    12 
    12 
    13 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
    13 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
    14 
    14 
    15 type PathToken = HgPathBuf;
    15 type PathToken = usize;
    16 
    16 
    17 #[derive(Clone, Debug, PartialEq)]
    17 #[derive(Clone, Debug, PartialEq, Copy)]
    18 struct TimeStampedPathCopy {
    18 struct TimeStampedPathCopy {
    19     /// revision at which the copy information was added
    19     /// revision at which the copy information was added
    20     rev: Revision,
    20     rev: Revision,
    21     /// the copy source, (Set to None in case of deletion of the associated
    21     /// the copy source, (Set to None in case of deletion of the associated
    22     /// key)
    22     /// key)
   310 enum Parent {
   310 enum Parent {
   311     /// The `p1(x) → x` edge
   311     /// The `p1(x) → x` edge
   312     FirstParent,
   312     FirstParent,
   313     /// The `p2(x) → x` edge
   313     /// The `p2(x) → x` edge
   314     SecondParent,
   314     SecondParent,
       
   315 }
       
   316 
       
   317 /// A small "tokenizer" responsible of turning full HgPath into lighter
       
   318 /// PathToken
       
   319 ///
       
   320 /// Dealing with small object, like integer is much faster, so HgPath input are
       
   321 /// turned into integer "PathToken" and converted back in the end.
       
   322 #[derive(Clone, Debug, Default)]
       
   323 struct TwoWayPathMap {
       
   324     token: HashMap<HgPathBuf, PathToken>,
       
   325     path: Vec<HgPathBuf>,
       
   326 }
       
   327 
       
   328 impl TwoWayPathMap {
       
   329     fn tokenize(&mut self, path: &HgPath) -> PathToken {
       
   330         match self.token.get(path) {
       
   331             Some(a) => *a,
       
   332             None => {
       
   333                 let a = self.token.len();
       
   334                 let buf = path.to_owned();
       
   335                 self.path.push(buf.clone());
       
   336                 self.token.insert(buf, a);
       
   337                 a
       
   338             }
       
   339         }
       
   340     }
       
   341 
       
   342     fn untokenize(&self, token: PathToken) -> &HgPathBuf {
       
   343         assert!(token < self.path.len(), format!("Unknown token: {}", token));
       
   344         &self.path[token]
       
   345     }
   315 }
   346 }
   316 
   347 
   317 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
   348 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
   318 ///
   349 ///
   319 /// Arguments are:
   350 /// Arguments are:
   335     is_ancestor: &A,
   366     is_ancestor: &A,
   336 ) -> PathCopies {
   367 ) -> PathCopies {
   337     let mut all_copies = HashMap::new();
   368     let mut all_copies = HashMap::new();
   338     let mut oracle = AncestorOracle::new(is_ancestor);
   369     let mut oracle = AncestorOracle::new(is_ancestor);
   339 
   370 
       
   371     let mut path_map = TwoWayPathMap::default();
       
   372 
   340     for rev in revs {
   373     for rev in revs {
   341         let mut d: DataHolder<D> = DataHolder { data: None };
   374         let mut d: DataHolder<D> = DataHolder { data: None };
   342         let (p1, p2, changes) = rev_info(rev, &mut d);
   375         let (p1, p2, changes) = rev_info(rev, &mut d);
   343 
   376 
   344         // We will chain the copies information accumulated for the parent with
   377         // We will chain the copies information accumulated for the parent with
   353                 p1,
   386                 p1,
   354             );
   387             );
   355             if let Some(parent_copies) = parent_copies {
   388             if let Some(parent_copies) = parent_copies {
   356                 // combine it with data for that revision
   389                 // combine it with data for that revision
   357                 let vertex_copies = add_from_changes(
   390                 let vertex_copies = add_from_changes(
       
   391                     &mut path_map,
   358                     &parent_copies,
   392                     &parent_copies,
   359                     &changes,
   393                     &changes,
   360                     Parent::FirstParent,
   394                     Parent::FirstParent,
   361                     rev,
   395                     rev,
   362                 );
   396                 );
   372                 p2,
   406                 p2,
   373             );
   407             );
   374             if let Some(parent_copies) = parent_copies {
   408             if let Some(parent_copies) = parent_copies {
   375                 // combine it with data for that revision
   409                 // combine it with data for that revision
   376                 let vertex_copies = add_from_changes(
   410                 let vertex_copies = add_from_changes(
       
   411                     &mut path_map,
   377                     &parent_copies,
   412                     &parent_copies,
   378                     &changes,
   413                     &changes,
   379                     Parent::SecondParent,
   414                     Parent::SecondParent,
   380                     rev,
   415                     rev,
   381                 );
   416                 );
   386                     // information.
   421                     // information.
   387                     //
   422                     //
   388                     // If we got data from both parents, We need to combine
   423                     // If we got data from both parents, We need to combine
   389                     // them.
   424                     // them.
   390                     Some(copies) => Some(merge_copies_dict(
   425                     Some(copies) => Some(merge_copies_dict(
       
   426                         &path_map,
   391                         vertex_copies,
   427                         vertex_copies,
   392                         copies,
   428                         copies,
   393                         &changes,
   429                         &changes,
   394                         &mut oracle,
   430                         &mut oracle,
   395                     )),
   431                     )),
   410         .remove(&target_rev)
   446         .remove(&target_rev)
   411         .expect("target revision was not processed");
   447         .expect("target revision was not processed");
   412     let mut result = PathCopies::default();
   448     let mut result = PathCopies::default();
   413     for (dest, tt_source) in tt_result {
   449     for (dest, tt_source) in tt_result {
   414         if let Some(path) = tt_source.path {
   450         if let Some(path) = tt_source.path {
   415             result.insert(dest, path);
   451             let path_dest = path_map.untokenize(dest).to_owned();
       
   452             let path_path = path_map.untokenize(path).to_owned();
       
   453             result.insert(path_dest, path_path);
   416         }
   454         }
   417     }
   455     }
   418     result
   456     result
   419 }
   457 }
   420 
   458 
   445 }
   483 }
   446 
   484 
   447 /// Combine ChangedFiles with some existing PathCopies information and return
   485 /// Combine ChangedFiles with some existing PathCopies information and return
   448 /// the result
   486 /// the result
   449 fn add_from_changes(
   487 fn add_from_changes(
       
   488     path_map: &mut TwoWayPathMap,
   450     base_copies: &TimeStampedPathCopies,
   489     base_copies: &TimeStampedPathCopies,
   451     changes: &ChangedFiles,
   490     changes: &ChangedFiles,
   452     parent: Parent,
   491     parent: Parent,
   453     current_rev: Revision,
   492     current_rev: Revision,
   454 ) -> TimeStampedPathCopies {
   493 ) -> TimeStampedPathCopies {
   455     let mut copies = base_copies.clone();
   494     let mut copies = base_copies.clone();
   456     for action in changes.iter_actions(parent) {
   495     for action in changes.iter_actions(parent) {
   457         match action {
   496         match action {
   458             Action::Copied(dest, source) => {
   497             Action::Copied(path_dest, path_source) => {
       
   498                 let dest = path_map.tokenize(path_dest);
       
   499                 let source = path_map.tokenize(path_source);
   459                 let entry;
   500                 let entry;
   460                 if let Some(v) = base_copies.get(source) {
   501                 if let Some(v) = base_copies.get(&source) {
   461                     entry = match &v.path {
   502                     entry = match &v.path {
   462                         Some(path) => Some((*(path)).to_owned()),
   503                         Some(path) => Some((*(path)).to_owned()),
   463                         None => Some(source.to_owned()),
   504                         None => Some(source.to_owned()),
   464                     }
   505                     }
   465                 } else {
   506                 } else {
   473                     rev: current_rev,
   514                     rev: current_rev,
   474                     path: entry,
   515                     path: entry,
   475                 };
   516                 };
   476                 copies.insert(dest.to_owned(), ttpc);
   517                 copies.insert(dest.to_owned(), ttpc);
   477             }
   518             }
   478             Action::Removed(f) => {
   519             Action::Removed(deleted_path) => {
   479                 // We must drop copy information for removed file.
   520                 // We must drop copy information for removed file.
   480                 //
   521                 //
   481                 // We need to explicitly record them as dropped to
   522                 // We need to explicitly record them as dropped to
   482                 // propagate this information when merging two
   523                 // propagate this information when merging two
   483                 // TimeStampedPathCopies object.
   524                 // TimeStampedPathCopies object.
   484                 if copies.contains_key(f.as_ref()) {
   525                 let deleted = path_map.tokenize(deleted_path);
       
   526                 if copies.contains_key(&deleted) {
   485                     let ttpc = TimeStampedPathCopy {
   527                     let ttpc = TimeStampedPathCopy {
   486                         rev: current_rev,
   528                         rev: current_rev,
   487                         path: None,
   529                         path: None,
   488                     };
   530                     };
   489                     copies.insert(f.to_owned(), ttpc);
   531                     copies.insert(deleted, ttpc);
   490                 }
   532                 }
   491             }
   533             }
   492         }
   534         }
   493     }
   535     }
   494     copies
   536     copies
   497 /// merge two copies-mapping together, minor and major
   539 /// merge two copies-mapping together, minor and major
   498 ///
   540 ///
   499 /// In case of conflict, value from "major" will be picked, unless in some
   541 /// In case of conflict, value from "major" will be picked, unless in some
   500 /// cases. See inline documentation for details.
   542 /// cases. See inline documentation for details.
   501 fn merge_copies_dict<A: Fn(Revision, Revision) -> bool>(
   543 fn merge_copies_dict<A: Fn(Revision, Revision) -> bool>(
       
   544     path_map: &TwoWayPathMap,
   502     mut minor: TimeStampedPathCopies,
   545     mut minor: TimeStampedPathCopies,
   503     mut major: TimeStampedPathCopies,
   546     mut major: TimeStampedPathCopies,
   504     changes: &ChangedFiles,
   547     changes: &ChangedFiles,
   505     oracle: &mut AncestorOracle<A>,
   548     oracle: &mut AncestorOracle<A>,
   506 ) -> TimeStampedPathCopies {
   549 ) -> TimeStampedPathCopies {
   509     // code is more settled.
   552     // code is more settled.
   510     let mut cmp_value =
   553     let mut cmp_value =
   511         |dest: &PathToken,
   554         |dest: &PathToken,
   512          src_minor: &TimeStampedPathCopy,
   555          src_minor: &TimeStampedPathCopy,
   513          src_major: &TimeStampedPathCopy| {
   556          src_major: &TimeStampedPathCopy| {
   514             compare_value(changes, oracle, dest, src_minor, src_major)
   557             compare_value(
       
   558                 path_map, changes, oracle, dest, src_minor, src_major,
       
   559             )
   515         };
   560         };
   516     if minor.is_empty() {
   561     if minor.is_empty() {
   517         major
   562         major
   518     } else if major.is_empty() {
   563     } else if major.is_empty() {
   519         minor
   564         minor
   639 }
   684 }
   640 
   685 
   641 /// decide which side prevails in case of conflicting values
   686 /// decide which side prevails in case of conflicting values
   642 #[allow(clippy::if_same_then_else)]
   687 #[allow(clippy::if_same_then_else)]
   643 fn compare_value<A: Fn(Revision, Revision) -> bool>(
   688 fn compare_value<A: Fn(Revision, Revision) -> bool>(
       
   689     path_map: &TwoWayPathMap,
   644     changes: &ChangedFiles,
   690     changes: &ChangedFiles,
   645     oracle: &mut AncestorOracle<A>,
   691     oracle: &mut AncestorOracle<A>,
   646     dest: &PathToken,
   692     dest: &PathToken,
   647     src_minor: &TimeStampedPathCopy,
   693     src_minor: &TimeStampedPathCopy,
   648     src_major: &TimeStampedPathCopy,
   694     src_major: &TimeStampedPathCopy,
   662         // same rev. So this is the same value.
   708         // same rev. So this is the same value.
   663         unreachable!(
   709         unreachable!(
   664             "conflict information from p1 and p2 in the same revision"
   710             "conflict information from p1 and p2 in the same revision"
   665         );
   711         );
   666     } else {
   712     } else {
   667         let action = changes.get_merge_case(&dest);
   713         let dest_path = path_map.untokenize(*dest);
       
   714         let action = changes.get_merge_case(dest_path);
   668         if src_major.path.is_none() && action == MergeCase::Salvaged {
   715         if src_major.path.is_none() && action == MergeCase::Salvaged {
   669             // If the file is "deleted" in the major side but was
   716             // If the file is "deleted" in the major side but was
   670             // salvaged by the merge, we keep the minor side alive
   717             // salvaged by the merge, we keep the minor side alive
   671             MergePick::Minor
   718             MergePick::Minor
   672         } else if src_minor.path.is_none() && action == MergeCase::Salvaged {
   719         } else if src_minor.path.is_none() && action == MergeCase::Salvaged {