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