##// END OF EJS Templates
copies-rust: rename TimeStampedPathCopy to CopySource...
marmoute -
r47312:2bd06978 default
parent child Browse files
Show More
@@ -1,871 +1,871 b''
1 1 use crate::utils::hg_path::HgPath;
2 2 use crate::utils::hg_path::HgPathBuf;
3 3 use crate::Revision;
4 4 use crate::NULL_REVISION;
5 5
6 6 use im_rc::ordmap::DiffItem;
7 7 use im_rc::ordmap::Entry;
8 8 use im_rc::ordmap::OrdMap;
9 9
10 10 use std::cmp::Ordering;
11 11 use std::collections::HashMap;
12 12 use std::convert::TryInto;
13 13
14 14 pub type PathCopies = HashMap<HgPathBuf, HgPathBuf>;
15 15
16 16 type PathToken = usize;
17 17
18 18 #[derive(Clone, Debug, PartialEq, Copy)]
19 struct TimeStampedPathCopy {
19 struct CopySource {
20 20 /// revision at which the copy information was added
21 21 rev: Revision,
22 22 /// the copy source, (Set to None in case of deletion of the associated
23 23 /// key)
24 24 path: Option<PathToken>,
25 25 }
26 26
27 27 /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation)
28 type InternalPathCopies = OrdMap<PathToken, TimeStampedPathCopy>;
28 type InternalPathCopies = OrdMap<PathToken, CopySource>;
29 29
30 30 /// hold parent 1, parent 2 and relevant files actions.
31 31 pub type RevInfo<'a> = (Revision, Revision, ChangedFiles<'a>);
32 32
33 33 /// represent the files affected by a changesets
34 34 ///
35 35 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
36 36 /// all the data categories tracked by it.
37 37 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
38 38 /// all the data categories tracked by it.
39 39 pub struct ChangedFiles<'a> {
40 40 nb_items: u32,
41 41 index: &'a [u8],
42 42 data: &'a [u8],
43 43 }
44 44
45 45 /// Represent active changes that affect the copy tracing.
46 46 enum Action<'a> {
47 47 /// The parent ? children edge is removing a file
48 48 ///
49 49 /// (actually, this could be the edge from the other parent, but it does
50 50 /// not matters)
51 51 Removed(&'a HgPath),
52 52 /// The parent ? children edge introduce copy information between (dest,
53 53 /// source)
54 54 Copied(&'a HgPath, &'a HgPath),
55 55 }
56 56
57 57 /// This express the possible "special" case we can get in a merge
58 58 ///
59 59 /// See mercurial/metadata.py for details on these values.
60 60 #[derive(PartialEq)]
61 61 enum MergeCase {
62 62 /// Merged: file had history on both side that needed to be merged
63 63 Merged,
64 64 /// Salvaged: file was candidate for deletion, but survived the merge
65 65 Salvaged,
66 66 /// Normal: Not one of the two cases above
67 67 Normal,
68 68 }
69 69
70 70 type FileChange<'a> = (u8, &'a HgPath, &'a HgPath);
71 71
72 72 const EMPTY: &[u8] = b"";
73 73 const COPY_MASK: u8 = 3;
74 74 const P1_COPY: u8 = 2;
75 75 const P2_COPY: u8 = 3;
76 76 const ACTION_MASK: u8 = 28;
77 77 const REMOVED: u8 = 12;
78 78 const MERGED: u8 = 8;
79 79 const SALVAGED: u8 = 16;
80 80
81 81 impl<'a> ChangedFiles<'a> {
82 82 const INDEX_START: usize = 4;
83 83 const ENTRY_SIZE: u32 = 9;
84 84 const FILENAME_START: u32 = 1;
85 85 const COPY_SOURCE_START: u32 = 5;
86 86
87 87 pub fn new(data: &'a [u8]) -> Self {
88 88 assert!(
89 89 data.len() >= 4,
90 90 "data size ({}) is too small to contain the header (4)",
91 91 data.len()
92 92 );
93 93 let nb_items_raw: [u8; 4] = (&data[0..=3])
94 94 .try_into()
95 95 .expect("failed to turn 4 bytes into 4 bytes");
96 96 let nb_items = u32::from_be_bytes(nb_items_raw);
97 97
98 98 let index_size = (nb_items * Self::ENTRY_SIZE) as usize;
99 99 let index_end = Self::INDEX_START + index_size;
100 100
101 101 assert!(
102 102 data.len() >= index_end,
103 103 "data size ({}) is too small to fit the index_data ({})",
104 104 data.len(),
105 105 index_end
106 106 );
107 107
108 108 let ret = ChangedFiles {
109 109 nb_items,
110 110 index: &data[Self::INDEX_START..index_end],
111 111 data: &data[index_end..],
112 112 };
113 113 let max_data = ret.filename_end(nb_items - 1) as usize;
114 114 assert!(
115 115 ret.data.len() >= max_data,
116 116 "data size ({}) is too small to fit all data ({})",
117 117 data.len(),
118 118 index_end + max_data
119 119 );
120 120 ret
121 121 }
122 122
123 123 pub fn new_empty() -> Self {
124 124 ChangedFiles {
125 125 nb_items: 0,
126 126 index: EMPTY,
127 127 data: EMPTY,
128 128 }
129 129 }
130 130
131 131 /// internal function to return an individual entry at a given index
132 132 fn entry(&'a self, idx: u32) -> FileChange<'a> {
133 133 if idx >= self.nb_items {
134 134 panic!(
135 135 "index for entry is higher that the number of file {} >= {}",
136 136 idx, self.nb_items
137 137 )
138 138 }
139 139 let flags = self.flags(idx);
140 140 let filename = self.filename(idx);
141 141 let copy_idx = self.copy_idx(idx);
142 142 let copy_source = self.filename(copy_idx);
143 143 (flags, filename, copy_source)
144 144 }
145 145
146 146 /// internal function to return the filename of the entry at a given index
147 147 fn filename(&self, idx: u32) -> &HgPath {
148 148 let filename_start;
149 149 if idx == 0 {
150 150 filename_start = 0;
151 151 } else {
152 152 filename_start = self.filename_end(idx - 1)
153 153 }
154 154 let filename_end = self.filename_end(idx);
155 155 let filename_start = filename_start as usize;
156 156 let filename_end = filename_end as usize;
157 157 HgPath::new(&self.data[filename_start..filename_end])
158 158 }
159 159
160 160 /// internal function to return the flag field of the entry at a given
161 161 /// index
162 162 fn flags(&self, idx: u32) -> u8 {
163 163 let idx = idx as usize;
164 164 self.index[idx * (Self::ENTRY_SIZE as usize)]
165 165 }
166 166
167 167 /// internal function to return the end of a filename part at a given index
168 168 fn filename_end(&self, idx: u32) -> u32 {
169 169 let start = (idx * Self::ENTRY_SIZE) + Self::FILENAME_START;
170 170 let end = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
171 171 let start = start as usize;
172 172 let end = end as usize;
173 173 let raw = (&self.index[start..end])
174 174 .try_into()
175 175 .expect("failed to turn 4 bytes into 4 bytes");
176 176 u32::from_be_bytes(raw)
177 177 }
178 178
179 179 /// internal function to return index of the copy source of the entry at a
180 180 /// given index
181 181 fn copy_idx(&self, idx: u32) -> u32 {
182 182 let start = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
183 183 let end = (idx + 1) * Self::ENTRY_SIZE;
184 184 let start = start as usize;
185 185 let end = end as usize;
186 186 let raw = (&self.index[start..end])
187 187 .try_into()
188 188 .expect("failed to turn 4 bytes into 4 bytes");
189 189 u32::from_be_bytes(raw)
190 190 }
191 191
192 192 /// Return an iterator over all the `Action` in this instance.
193 193 fn iter_actions(&self, parent: Parent) -> ActionsIterator {
194 194 ActionsIterator {
195 195 changes: &self,
196 196 parent: parent,
197 197 current: 0,
198 198 }
199 199 }
200 200
201 201 /// return the MergeCase value associated with a filename
202 202 fn get_merge_case(&self, path: &HgPath) -> MergeCase {
203 203 if self.nb_items == 0 {
204 204 return MergeCase::Normal;
205 205 }
206 206 let mut low_part = 0;
207 207 let mut high_part = self.nb_items;
208 208
209 209 while low_part < high_part {
210 210 let cursor = (low_part + high_part - 1) / 2;
211 211 let (flags, filename, _source) = self.entry(cursor);
212 212 match path.cmp(filename) {
213 213 Ordering::Less => low_part = cursor + 1,
214 214 Ordering::Greater => high_part = cursor,
215 215 Ordering::Equal => {
216 216 return match flags & ACTION_MASK {
217 217 MERGED => MergeCase::Merged,
218 218 SALVAGED => MergeCase::Salvaged,
219 219 _ => MergeCase::Normal,
220 220 };
221 221 }
222 222 }
223 223 }
224 224 MergeCase::Normal
225 225 }
226 226 }
227 227
228 228 /// A struct responsible for answering "is X ancestors of Y" quickly
229 229 ///
230 230 /// The structure will delegate ancestors call to a callback, and cache the
231 231 /// result.
232 232 #[derive(Debug)]
233 233 struct AncestorOracle<'a, A: Fn(Revision, Revision) -> bool> {
234 234 inner: &'a A,
235 235 pairs: HashMap<(Revision, Revision), bool>,
236 236 }
237 237
238 238 impl<'a, A: Fn(Revision, Revision) -> bool> AncestorOracle<'a, A> {
239 239 fn new(func: &'a A) -> Self {
240 240 Self {
241 241 inner: func,
242 242 pairs: HashMap::default(),
243 243 }
244 244 }
245 245
246 246 fn record_overwrite(&mut self, anc: Revision, desc: Revision) {
247 247 self.pairs.insert((anc, desc), true);
248 248 }
249 249
250 250 /// returns `true` if `anc` is an ancestors of `desc`, `false` otherwise
251 251 fn is_overwrite(&mut self, anc: Revision, desc: Revision) -> bool {
252 252 if anc > desc {
253 253 false
254 254 } else if anc == desc {
255 255 true
256 256 } else {
257 257 if let Some(b) = self.pairs.get(&(anc, desc)) {
258 258 *b
259 259 } else {
260 260 let b = (self.inner)(anc, desc);
261 261 self.pairs.insert((anc, desc), b);
262 262 b
263 263 }
264 264 }
265 265 }
266 266 }
267 267
268 268 struct ActionsIterator<'a> {
269 269 changes: &'a ChangedFiles<'a>,
270 270 parent: Parent,
271 271 current: u32,
272 272 }
273 273
274 274 impl<'a> Iterator for ActionsIterator<'a> {
275 275 type Item = Action<'a>;
276 276
277 277 fn next(&mut self) -> Option<Action<'a>> {
278 278 let copy_flag = match self.parent {
279 279 Parent::FirstParent => P1_COPY,
280 280 Parent::SecondParent => P2_COPY,
281 281 };
282 282 while self.current < self.changes.nb_items {
283 283 let (flags, file, source) = self.changes.entry(self.current);
284 284 self.current += 1;
285 285 if (flags & ACTION_MASK) == REMOVED {
286 286 return Some(Action::Removed(file));
287 287 }
288 288 let copy = flags & COPY_MASK;
289 289 if copy == copy_flag {
290 290 return Some(Action::Copied(file, source));
291 291 }
292 292 }
293 293 return None;
294 294 }
295 295 }
296 296
297 297 /// A small struct whose purpose is to ensure lifetime of bytes referenced in
298 298 /// ChangedFiles
299 299 ///
300 300 /// It is passed to the RevInfoMaker callback who can assign any necessary
301 301 /// content to the `data` attribute. The copy tracing code is responsible for
302 302 /// keeping the DataHolder alive at least as long as the ChangedFiles object.
303 303 pub struct DataHolder<D> {
304 304 /// RevInfoMaker callback should assign data referenced by the
305 305 /// ChangedFiles struct it return to this attribute. The DataHolder
306 306 /// lifetime will be at least as long as the ChangedFiles one.
307 307 pub data: Option<D>,
308 308 }
309 309
310 310 pub type RevInfoMaker<'a, D> =
311 311 Box<dyn for<'r> Fn(Revision, &'r mut DataHolder<D>) -> RevInfo<'r> + 'a>;
312 312
313 313 /// enum used to carry information about the parent β†’ child currently processed
314 314 #[derive(Copy, Clone, Debug)]
315 315 enum Parent {
316 316 /// The `p1(x) β†’ x` edge
317 317 FirstParent,
318 318 /// The `p2(x) β†’ x` edge
319 319 SecondParent,
320 320 }
321 321
322 322 /// A small "tokenizer" responsible of turning full HgPath into lighter
323 323 /// PathToken
324 324 ///
325 325 /// Dealing with small object, like integer is much faster, so HgPath input are
326 326 /// turned into integer "PathToken" and converted back in the end.
327 327 #[derive(Clone, Debug, Default)]
328 328 struct TwoWayPathMap {
329 329 token: HashMap<HgPathBuf, PathToken>,
330 330 path: Vec<HgPathBuf>,
331 331 }
332 332
333 333 impl TwoWayPathMap {
334 334 fn tokenize(&mut self, path: &HgPath) -> PathToken {
335 335 match self.token.get(path) {
336 336 Some(a) => *a,
337 337 None => {
338 338 let a = self.token.len();
339 339 let buf = path.to_owned();
340 340 self.path.push(buf.clone());
341 341 self.token.insert(buf, a);
342 342 a
343 343 }
344 344 }
345 345 }
346 346
347 347 fn untokenize(&self, token: PathToken) -> &HgPathBuf {
348 348 assert!(token < self.path.len(), format!("Unknown token: {}", token));
349 349 &self.path[token]
350 350 }
351 351 }
352 352
353 353 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
354 354 ///
355 355 /// Arguments are:
356 356 ///
357 357 /// revs: all revisions to be considered
358 358 /// children: a {parent ? [childrens]} mapping
359 359 /// target_rev: the final revision we are combining copies to
360 360 /// rev_info(rev): callback to get revision information:
361 361 /// * first parent
362 362 /// * second parent
363 363 /// * ChangedFiles
364 364 /// isancestors(low_rev, high_rev): callback to check if a revision is an
365 365 /// ancestor of another
366 366 pub fn combine_changeset_copies<A: Fn(Revision, Revision) -> bool, D>(
367 367 revs: Vec<Revision>,
368 368 mut children_count: HashMap<Revision, usize>,
369 369 target_rev: Revision,
370 370 rev_info: RevInfoMaker<D>,
371 371 is_ancestor: &A,
372 372 ) -> PathCopies {
373 373 let mut all_copies = HashMap::new();
374 374 let mut oracle = AncestorOracle::new(is_ancestor);
375 375
376 376 let mut path_map = TwoWayPathMap::default();
377 377
378 378 for rev in revs {
379 379 let mut d: DataHolder<D> = DataHolder { data: None };
380 380 let (p1, p2, changes) = rev_info(rev, &mut d);
381 381
382 382 // We will chain the copies information accumulated for the parent with
383 383 // the individual copies information the curent revision. Creating a
384 384 // new TimeStampedPath for each `rev` β†’ `children` vertex.
385 385 let mut copies: Option<InternalPathCopies> = None;
386 386 if p1 != NULL_REVISION {
387 387 // Retrieve data computed in a previous iteration
388 388 let parent_copies = get_and_clean_parent_copies(
389 389 &mut all_copies,
390 390 &mut children_count,
391 391 p1,
392 392 );
393 393 if let Some(parent_copies) = parent_copies {
394 394 // combine it with data for that revision
395 395 let vertex_copies = add_from_changes(
396 396 &mut path_map,
397 397 &mut oracle,
398 398 &parent_copies,
399 399 &changes,
400 400 Parent::FirstParent,
401 401 rev,
402 402 );
403 403 // keep that data around for potential later combination
404 404 copies = Some(vertex_copies);
405 405 }
406 406 }
407 407 if p2 != NULL_REVISION {
408 408 // Retrieve data computed in a previous iteration
409 409 let parent_copies = get_and_clean_parent_copies(
410 410 &mut all_copies,
411 411 &mut children_count,
412 412 p2,
413 413 );
414 414 if let Some(parent_copies) = parent_copies {
415 415 // combine it with data for that revision
416 416 let vertex_copies = add_from_changes(
417 417 &mut path_map,
418 418 &mut oracle,
419 419 &parent_copies,
420 420 &changes,
421 421 Parent::SecondParent,
422 422 rev,
423 423 );
424 424
425 425 copies = match copies {
426 426 None => Some(vertex_copies),
427 427 // Merge has two parents needs to combines their copy
428 428 // information.
429 429 //
430 430 // If we got data from both parents, We need to combine
431 431 // them.
432 432 Some(copies) => Some(merge_copies_dict(
433 433 &path_map,
434 434 rev,
435 435 vertex_copies,
436 436 copies,
437 437 &changes,
438 438 &mut oracle,
439 439 )),
440 440 };
441 441 }
442 442 }
443 443 match copies {
444 444 Some(copies) => {
445 445 all_copies.insert(rev, copies);
446 446 }
447 447 _ => {}
448 448 }
449 449 }
450 450
451 451 // Drop internal information (like the timestamp) and return the final
452 452 // mapping.
453 453 let tt_result = all_copies
454 454 .remove(&target_rev)
455 455 .expect("target revision was not processed");
456 456 let mut result = PathCopies::default();
457 457 for (dest, tt_source) in tt_result {
458 458 if let Some(path) = tt_source.path {
459 459 let path_dest = path_map.untokenize(dest).to_owned();
460 460 let path_path = path_map.untokenize(path).to_owned();
461 461 result.insert(path_dest, path_path);
462 462 }
463 463 }
464 464 result
465 465 }
466 466
467 467 /// fetch previous computed information
468 468 ///
469 469 /// If no other children are expected to need this information, we drop it from
470 470 /// the cache.
471 471 ///
472 472 /// If parent is not part of the set we are expected to walk, return None.
473 473 fn get_and_clean_parent_copies(
474 474 all_copies: &mut HashMap<Revision, InternalPathCopies>,
475 475 children_count: &mut HashMap<Revision, usize>,
476 476 parent_rev: Revision,
477 477 ) -> Option<InternalPathCopies> {
478 478 let count = children_count.get_mut(&parent_rev)?;
479 479 *count -= 1;
480 480 if *count == 0 {
481 481 match all_copies.remove(&parent_rev) {
482 482 Some(c) => Some(c),
483 483 None => Some(InternalPathCopies::default()),
484 484 }
485 485 } else {
486 486 match all_copies.get(&parent_rev) {
487 487 Some(c) => Some(c.clone()),
488 488 None => Some(InternalPathCopies::default()),
489 489 }
490 490 }
491 491 }
492 492
493 493 /// Combine ChangedFiles with some existing PathCopies information and return
494 494 /// the result
495 495 fn add_from_changes<A: Fn(Revision, Revision) -> bool>(
496 496 path_map: &mut TwoWayPathMap,
497 497 oracle: &mut AncestorOracle<A>,
498 498 base_copies: &InternalPathCopies,
499 499 changes: &ChangedFiles,
500 500 parent: Parent,
501 501 current_rev: Revision,
502 502 ) -> InternalPathCopies {
503 503 let mut copies = base_copies.clone();
504 504 for action in changes.iter_actions(parent) {
505 505 match action {
506 506 Action::Copied(path_dest, path_source) => {
507 507 let dest = path_map.tokenize(path_dest);
508 508 let source = path_map.tokenize(path_source);
509 509 let entry;
510 510 if let Some(v) = base_copies.get(&source) {
511 511 entry = match &v.path {
512 512 Some(path) => Some((*(path)).to_owned()),
513 513 None => Some(source.to_owned()),
514 514 }
515 515 } else {
516 516 entry = Some(source.to_owned());
517 517 }
518 518 // Each new entry is introduced by the children, we
519 519 // record this information as we will need it to take
520 520 // the right decision when merging conflicting copy
521 521 // information. See merge_copies_dict for details.
522 522 match copies.entry(dest) {
523 523 Entry::Vacant(slot) => {
524 let ttpc = TimeStampedPathCopy {
524 let ttpc = CopySource {
525 525 rev: current_rev,
526 526 path: entry,
527 527 };
528 528 slot.insert(ttpc);
529 529 }
530 530 Entry::Occupied(mut slot) => {
531 531 let mut ttpc = slot.get_mut();
532 532 oracle.record_overwrite(ttpc.rev, current_rev);
533 533 ttpc.rev = current_rev;
534 534 ttpc.path = entry;
535 535 }
536 536 }
537 537 }
538 538 Action::Removed(deleted_path) => {
539 539 // We must drop copy information for removed file.
540 540 //
541 541 // We need to explicitly record them as dropped to
542 542 // propagate this information when merging two
543 543 // InternalPathCopies object.
544 544 let deleted = path_map.tokenize(deleted_path);
545 545 copies.entry(deleted).and_modify(|old| {
546 546 oracle.record_overwrite(old.rev, current_rev);
547 547 old.rev = current_rev;
548 548 old.path = None;
549 549 });
550 550 }
551 551 }
552 552 }
553 553 copies
554 554 }
555 555
556 556 /// merge two copies-mapping together, minor and major
557 557 ///
558 558 /// In case of conflict, value from "major" will be picked, unless in some
559 559 /// cases. See inline documentation for details.
560 560 fn merge_copies_dict<A: Fn(Revision, Revision) -> bool>(
561 561 path_map: &TwoWayPathMap,
562 562 current_merge: Revision,
563 563 mut minor: InternalPathCopies,
564 564 mut major: InternalPathCopies,
565 565 changes: &ChangedFiles,
566 566 oracle: &mut AncestorOracle<A>,
567 567 ) -> InternalPathCopies {
568 568 // This closure exist as temporary help while multiple developper are
569 569 // actively working on this code. Feel free to re-inline it once this
570 570 // code is more settled.
571 571 let cmp_value = |oracle: &mut AncestorOracle<A>,
572 572 dest: &PathToken,
573 src_minor: &TimeStampedPathCopy,
574 src_major: &TimeStampedPathCopy| {
573 src_minor: &CopySource,
574 src_major: &CopySource| {
575 575 compare_value(
576 576 path_map,
577 577 current_merge,
578 578 changes,
579 579 oracle,
580 580 dest,
581 581 src_minor,
582 582 src_major,
583 583 )
584 584 };
585 585 if minor.is_empty() {
586 586 major
587 587 } else if major.is_empty() {
588 588 minor
589 589 } else if minor.len() * 2 < major.len() {
590 590 // Lets says we are merging two InternalPathCopies instance A and B.
591 591 //
592 592 // If A contains N items, the merge result will never contains more
593 593 // than N values differents than the one in A
594 594 //
595 595 // If B contains M items, with M > N, the merge result will always
596 596 // result in a minimum of M - N value differents than the on in
597 597 // A
598 598 //
599 599 // As a result, if N < (M-N), we know that simply iterating over A will
600 600 // yield less difference than iterating over the difference
601 601 // between A and B.
602 602 //
603 603 // This help performance a lot in case were a tiny
604 604 // InternalPathCopies is merged with a much larger one.
605 605 for (dest, src_minor) in minor {
606 606 let src_major = major.get(&dest);
607 607 match src_major {
608 608 None => {
609 609 major.insert(dest, src_minor);
610 610 }
611 611 Some(src_major) => {
612 612 let (pick, overwrite) =
613 613 cmp_value(oracle, &dest, &src_minor, src_major);
614 614 if overwrite {
615 615 oracle.record_overwrite(src_minor.rev, current_merge);
616 616 oracle.record_overwrite(src_major.rev, current_merge);
617 617 let path = match pick {
618 618 MergePick::Major => src_major.path,
619 619 MergePick::Minor => src_minor.path,
620 620 MergePick::Any => src_major.path,
621 621 };
622 let src = TimeStampedPathCopy {
622 let src = CopySource {
623 623 rev: current_merge,
624 624 path,
625 625 };
626 626 major.insert(dest, src);
627 627 } else {
628 628 match pick {
629 629 MergePick::Any | MergePick::Major => None,
630 630 MergePick::Minor => major.insert(dest, src_minor),
631 631 };
632 632 }
633 633 }
634 634 };
635 635 }
636 636 major
637 637 } else if major.len() * 2 < minor.len() {
638 638 // This use the same rational than the previous block.
639 639 // (Check previous block documentation for details.)
640 640 for (dest, src_major) in major {
641 641 let src_minor = minor.get(&dest);
642 642 match src_minor {
643 643 None => {
644 644 minor.insert(dest, src_major);
645 645 }
646 646 Some(src_minor) => {
647 647 let (pick, overwrite) =
648 648 cmp_value(oracle, &dest, &src_major, src_minor);
649 649 if overwrite {
650 650 oracle.record_overwrite(src_minor.rev, current_merge);
651 651 oracle.record_overwrite(src_major.rev, current_merge);
652 652 let path = match pick {
653 653 MergePick::Major => src_minor.path,
654 654 MergePick::Minor => src_major.path,
655 655 MergePick::Any => src_major.path,
656 656 };
657 let src = TimeStampedPathCopy {
657 let src = CopySource {
658 658 rev: current_merge,
659 659 path,
660 660 };
661 661 minor.insert(dest, src);
662 662 } else {
663 663 match pick {
664 664 MergePick::Any | MergePick::Major => None,
665 665 MergePick::Minor => minor.insert(dest, src_major),
666 666 };
667 667 }
668 668 }
669 669 };
670 670 }
671 671 minor
672 672 } else {
673 673 let mut override_minor = Vec::new();
674 674 let mut override_major = Vec::new();
675 675
676 let mut to_major = |k: &PathToken, v: &TimeStampedPathCopy| {
676 let mut to_major = |k: &PathToken, v: &CopySource| {
677 677 override_major.push((k.clone(), v.clone()))
678 678 };
679 let mut to_minor = |k: &PathToken, v: &TimeStampedPathCopy| {
679 let mut to_minor = |k: &PathToken, v: &CopySource| {
680 680 override_minor.push((k.clone(), v.clone()))
681 681 };
682 682
683 683 // The diff function leverage detection of the identical subpart if
684 684 // minor and major has some common ancestors. This make it very
685 685 // fast is most case.
686 686 //
687 687 // In case where the two map are vastly different in size, the current
688 688 // approach is still slowish because the iteration will iterate over
689 689 // all the "exclusive" content of the larger on. This situation can be
690 690 // frequent when the subgraph of revision we are processing has a lot
691 691 // of roots. Each roots adding they own fully new map to the mix (and
692 692 // likely a small map, if the path from the root to the "main path" is
693 693 // small.
694 694 //
695 695 // We could do better by detecting such situation and processing them
696 696 // differently.
697 697 for d in minor.diff(&major) {
698 698 match d {
699 699 DiffItem::Add(k, v) => to_minor(k, v),
700 700 DiffItem::Remove(k, v) => to_major(k, v),
701 701 DiffItem::Update { old, new } => {
702 702 let (dest, src_major) = new;
703 703 let (_, src_minor) = old;
704 704 let (pick, overwrite) =
705 705 cmp_value(oracle, dest, src_minor, src_major);
706 706 if overwrite {
707 707 oracle.record_overwrite(src_minor.rev, current_merge);
708 708 oracle.record_overwrite(src_major.rev, current_merge);
709 709 let path = match pick {
710 710 MergePick::Major => src_major.path,
711 711 MergePick::Minor => src_minor.path,
712 712 // If the two entry are identical, no need to do
713 713 // anything (but diff should not have yield them)
714 714 MergePick::Any => src_major.path,
715 715 };
716 let src = TimeStampedPathCopy {
716 let src = CopySource {
717 717 rev: current_merge,
718 718 path,
719 719 };
720 720 to_minor(dest, &src);
721 721 to_major(dest, &src);
722 722 } else {
723 723 match pick {
724 724 MergePick::Major => to_minor(dest, src_major),
725 725 MergePick::Minor => to_major(dest, src_minor),
726 726 // If the two entry are identical, no need to do
727 727 // anything (but diff should not have yield them)
728 728 MergePick::Any => unreachable!(),
729 729 }
730 730 }
731 731 }
732 732 };
733 733 }
734 734
735 735 let updates;
736 736 let mut result;
737 737 if override_major.is_empty() {
738 738 result = major
739 739 } else if override_minor.is_empty() {
740 740 result = minor
741 741 } else {
742 742 if override_minor.len() < override_major.len() {
743 743 updates = override_minor;
744 744 result = minor;
745 745 } else {
746 746 updates = override_major;
747 747 result = major;
748 748 }
749 749 for (k, v) in updates {
750 750 result.insert(k, v);
751 751 }
752 752 }
753 753 result
754 754 }
755 755 }
756 756
757 757 /// represent the side that should prevail when merging two
758 758 /// InternalPathCopies
759 759 enum MergePick {
760 760 /// The "major" (p1) side prevails
761 761 Major,
762 762 /// The "minor" (p2) side prevails
763 763 Minor,
764 764 /// Any side could be used (because they are the same)
765 765 Any,
766 766 }
767 767
768 768 /// decide which side prevails in case of conflicting values
769 769 #[allow(clippy::if_same_then_else)]
770 770 fn compare_value<A: Fn(Revision, Revision) -> bool>(
771 771 path_map: &TwoWayPathMap,
772 772 current_merge: Revision,
773 773 changes: &ChangedFiles,
774 774 oracle: &mut AncestorOracle<A>,
775 775 dest: &PathToken,
776 src_minor: &TimeStampedPathCopy,
777 src_major: &TimeStampedPathCopy,
776 src_minor: &CopySource,
777 src_major: &CopySource,
778 778 ) -> (MergePick, bool) {
779 779 if src_major.rev == current_merge {
780 780 if src_minor.rev == current_merge {
781 781 if src_major.path.is_none() {
782 782 // We cannot get different copy information for both p1 and p2
783 783 // from the same revision. Unless this was a
784 784 // deletion
785 785 (MergePick::Any, false)
786 786 } else {
787 787 unreachable!();
788 788 }
789 789 } else {
790 790 // The last value comes the current merge, this value -will- win
791 791 // eventually.
792 792 (MergePick::Major, true)
793 793 }
794 794 } else if src_minor.rev == current_merge {
795 795 // The last value comes the current merge, this value -will- win
796 796 // eventually.
797 797 (MergePick::Minor, true)
798 798 } else if src_major.path == src_minor.path {
799 799 // we have the same value, but from other source;
800 800 if src_major.rev == src_minor.rev {
801 801 // If the two entry are identical, they are both valid
802 802 (MergePick::Any, false)
803 803 } else if oracle.is_overwrite(src_major.rev, src_minor.rev) {
804 804 (MergePick::Minor, false)
805 805 } else if oracle.is_overwrite(src_minor.rev, src_major.rev) {
806 806 (MergePick::Major, false)
807 807 } else {
808 808 (MergePick::Any, true)
809 809 }
810 810 } else if src_major.rev == src_minor.rev {
811 811 // We cannot get copy information for both p1 and p2 in the
812 812 // same rev. So this is the same value.
813 813 unreachable!(
814 814 "conflicting information from p1 and p2 in the same revision"
815 815 );
816 816 } else {
817 817 let dest_path = path_map.untokenize(*dest);
818 818 let action = changes.get_merge_case(dest_path);
819 819 if src_minor.path.is_some()
820 820 && src_major.path.is_none()
821 821 && action == MergeCase::Salvaged
822 822 {
823 823 // If the file is "deleted" in the major side but was
824 824 // salvaged by the merge, we keep the minor side alive
825 825 (MergePick::Minor, true)
826 826 } else if src_major.path.is_some()
827 827 && src_minor.path.is_none()
828 828 && action == MergeCase::Salvaged
829 829 {
830 830 // If the file is "deleted" in the minor side but was
831 831 // salvaged by the merge, unconditionnaly preserve the
832 832 // major side.
833 833 (MergePick::Major, true)
834 834 } else if oracle.is_overwrite(src_minor.rev, src_major.rev) {
835 835 // The information from the minor version are strictly older than
836 836 // the major version
837 837 if action == MergeCase::Merged {
838 838 // If the file was actively merged, its means some non-copy
839 839 // activity happened on the other branch. It
840 840 // mean the older copy information are still relevant.
841 841 //
842 842 // The major side wins such conflict.
843 843 (MergePick::Major, true)
844 844 } else {
845 845 // No activity on the minor branch, pick the newer one.
846 846 (MergePick::Major, false)
847 847 }
848 848 } else if oracle.is_overwrite(src_major.rev, src_minor.rev) {
849 849 if action == MergeCase::Merged {
850 850 // If the file was actively merged, its means some non-copy
851 851 // activity happened on the other branch. It
852 852 // mean the older copy information are still relevant.
853 853 //
854 854 // The major side wins such conflict.
855 855 (MergePick::Major, true)
856 856 } else {
857 857 // No activity on the minor branch, pick the newer one.
858 858 (MergePick::Minor, false)
859 859 }
860 860 } else if src_minor.path.is_none() {
861 861 // the minor side has no relevant information, pick the alive one
862 862 (MergePick::Major, true)
863 863 } else if src_major.path.is_none() {
864 864 // the major side has no relevant information, pick the alive one
865 865 (MergePick::Minor, true)
866 866 } else {
867 867 // by default the major side wins
868 868 (MergePick::Major, true)
869 869 }
870 870 }
871 871 }
General Comments 0
You need to be logged in to leave comments. Login now