##// END OF EJS Templates
copies-rust: remove an unnecessary format!() inside assert!()...
Martin von Zweigbergk -
r47363:f2fc34e8 default
parent child Browse files
Show More
@@ -1,761 +1,761
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::Entry;
7 7 use im_rc::ordmap::OrdMap;
8 8 use im_rc::OrdSet;
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)]
19 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 /// a set of previous `CopySource.rev` value directly or indirectly
26 26 /// overwritten by this one.
27 27 overwritten: OrdSet<Revision>,
28 28 }
29 29
30 30 impl CopySource {
31 31 /// create a new CopySource
32 32 ///
33 33 /// Use this when no previous copy source existed.
34 34 fn new(rev: Revision, path: Option<PathToken>) -> Self {
35 35 Self {
36 36 rev,
37 37 path,
38 38 overwritten: OrdSet::new(),
39 39 }
40 40 }
41 41
42 42 /// create a new CopySource from merging two others
43 43 ///
44 44 /// Use this when merging two InternalPathCopies requires active merging of
45 45 /// some entries.
46 46 fn new_from_merge(rev: Revision, winner: &Self, loser: &Self) -> Self {
47 47 let mut overwritten = OrdSet::new();
48 48 overwritten.extend(winner.overwritten.iter().copied());
49 49 overwritten.extend(loser.overwritten.iter().copied());
50 50 overwritten.insert(winner.rev);
51 51 overwritten.insert(loser.rev);
52 52 Self {
53 53 rev,
54 54 path: winner.path,
55 55 overwritten: overwritten,
56 56 }
57 57 }
58 58
59 59 /// Update the value of a pre-existing CopySource
60 60 ///
61 61 /// Use this when recording copy information from parent β†’ child edges
62 62 fn overwrite(&mut self, rev: Revision, path: Option<PathToken>) {
63 63 self.overwritten.insert(self.rev);
64 64 self.rev = rev;
65 65 self.path = path;
66 66 }
67 67
68 68 /// Mark pre-existing copy information as "dropped" by a file deletion
69 69 ///
70 70 /// Use this when recording copy information from parent β†’ child edges
71 71 fn mark_delete(&mut self, rev: Revision) {
72 72 self.overwritten.insert(self.rev);
73 73 self.rev = rev;
74 74 self.path = None;
75 75 }
76 76
77 77 /// Mark pre-existing copy information as "dropped" by a file deletion
78 78 ///
79 79 /// Use this when recording copy information from parent β†’ child edges
80 80 fn mark_delete_with_pair(&mut self, rev: Revision, other: &Self) {
81 81 self.overwritten.insert(self.rev);
82 82 if other.rev != rev {
83 83 self.overwritten.insert(other.rev);
84 84 }
85 85 self.overwritten.extend(other.overwritten.iter().copied());
86 86 self.rev = rev;
87 87 self.path = None;
88 88 }
89 89
90 90 fn is_overwritten_by(&self, other: &Self) -> bool {
91 91 other.overwritten.contains(&self.rev)
92 92 }
93 93 }
94 94
95 95 // For the same "dest", content generated for a given revision will always be
96 96 // the same.
97 97 impl PartialEq for CopySource {
98 98 fn eq(&self, other: &Self) -> bool {
99 99 #[cfg(debug_assertions)]
100 100 {
101 101 if self.rev == other.rev {
102 102 debug_assert!(self.path == other.path);
103 103 debug_assert!(self.overwritten == other.overwritten);
104 104 }
105 105 }
106 106 self.rev == other.rev
107 107 }
108 108 }
109 109
110 110 /// maps CopyDestination to Copy Source (+ a "timestamp" for the operation)
111 111 type InternalPathCopies = OrdMap<PathToken, CopySource>;
112 112
113 113 /// represent the files affected by a changesets
114 114 ///
115 115 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
116 116 /// all the data categories tracked by it.
117 117 /// This hold a subset of mercurial.metadata.ChangingFiles as we do not need
118 118 /// all the data categories tracked by it.
119 119 pub struct ChangedFiles<'a> {
120 120 nb_items: u32,
121 121 index: &'a [u8],
122 122 data: &'a [u8],
123 123 }
124 124
125 125 /// Represent active changes that affect the copy tracing.
126 126 enum Action<'a> {
127 127 /// The parent ? children edge is removing a file
128 128 ///
129 129 /// (actually, this could be the edge from the other parent, but it does
130 130 /// not matters)
131 131 Removed(&'a HgPath),
132 132 /// The parent ? children edge introduce copy information between (dest,
133 133 /// source)
134 134 CopiedFromP1(&'a HgPath, &'a HgPath),
135 135 CopiedFromP2(&'a HgPath, &'a HgPath),
136 136 }
137 137
138 138 /// This express the possible "special" case we can get in a merge
139 139 ///
140 140 /// See mercurial/metadata.py for details on these values.
141 141 #[derive(PartialEq)]
142 142 enum MergeCase {
143 143 /// Merged: file had history on both side that needed to be merged
144 144 Merged,
145 145 /// Salvaged: file was candidate for deletion, but survived the merge
146 146 Salvaged,
147 147 /// Normal: Not one of the two cases above
148 148 Normal,
149 149 }
150 150
151 151 type FileChange<'a> = (u8, &'a HgPath, &'a HgPath);
152 152
153 153 const EMPTY: &[u8] = b"";
154 154 const COPY_MASK: u8 = 3;
155 155 const P1_COPY: u8 = 2;
156 156 const P2_COPY: u8 = 3;
157 157 const ACTION_MASK: u8 = 28;
158 158 const REMOVED: u8 = 12;
159 159 const MERGED: u8 = 8;
160 160 const SALVAGED: u8 = 16;
161 161
162 162 impl<'a> ChangedFiles<'a> {
163 163 const INDEX_START: usize = 4;
164 164 const ENTRY_SIZE: u32 = 9;
165 165 const FILENAME_START: u32 = 1;
166 166 const COPY_SOURCE_START: u32 = 5;
167 167
168 168 pub fn new(data: &'a [u8]) -> Self {
169 169 assert!(
170 170 data.len() >= 4,
171 171 "data size ({}) is too small to contain the header (4)",
172 172 data.len()
173 173 );
174 174 let nb_items_raw: [u8; 4] = (&data[0..=3])
175 175 .try_into()
176 176 .expect("failed to turn 4 bytes into 4 bytes");
177 177 let nb_items = u32::from_be_bytes(nb_items_raw);
178 178
179 179 let index_size = (nb_items * Self::ENTRY_SIZE) as usize;
180 180 let index_end = Self::INDEX_START + index_size;
181 181
182 182 assert!(
183 183 data.len() >= index_end,
184 184 "data size ({}) is too small to fit the index_data ({})",
185 185 data.len(),
186 186 index_end
187 187 );
188 188
189 189 let ret = ChangedFiles {
190 190 nb_items,
191 191 index: &data[Self::INDEX_START..index_end],
192 192 data: &data[index_end..],
193 193 };
194 194 let max_data = ret.filename_end(nb_items - 1) as usize;
195 195 assert!(
196 196 ret.data.len() >= max_data,
197 197 "data size ({}) is too small to fit all data ({})",
198 198 data.len(),
199 199 index_end + max_data
200 200 );
201 201 ret
202 202 }
203 203
204 204 pub fn new_empty() -> Self {
205 205 ChangedFiles {
206 206 nb_items: 0,
207 207 index: EMPTY,
208 208 data: EMPTY,
209 209 }
210 210 }
211 211
212 212 /// internal function to return an individual entry at a given index
213 213 fn entry(&'a self, idx: u32) -> FileChange<'a> {
214 214 if idx >= self.nb_items {
215 215 panic!(
216 216 "index for entry is higher that the number of file {} >= {}",
217 217 idx, self.nb_items
218 218 )
219 219 }
220 220 let flags = self.flags(idx);
221 221 let filename = self.filename(idx);
222 222 let copy_idx = self.copy_idx(idx);
223 223 let copy_source = self.filename(copy_idx);
224 224 (flags, filename, copy_source)
225 225 }
226 226
227 227 /// internal function to return the filename of the entry at a given index
228 228 fn filename(&self, idx: u32) -> &HgPath {
229 229 let filename_start;
230 230 if idx == 0 {
231 231 filename_start = 0;
232 232 } else {
233 233 filename_start = self.filename_end(idx - 1)
234 234 }
235 235 let filename_end = self.filename_end(idx);
236 236 let filename_start = filename_start as usize;
237 237 let filename_end = filename_end as usize;
238 238 HgPath::new(&self.data[filename_start..filename_end])
239 239 }
240 240
241 241 /// internal function to return the flag field of the entry at a given
242 242 /// index
243 243 fn flags(&self, idx: u32) -> u8 {
244 244 let idx = idx as usize;
245 245 self.index[idx * (Self::ENTRY_SIZE as usize)]
246 246 }
247 247
248 248 /// internal function to return the end of a filename part at a given index
249 249 fn filename_end(&self, idx: u32) -> u32 {
250 250 let start = (idx * Self::ENTRY_SIZE) + Self::FILENAME_START;
251 251 let end = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
252 252 let start = start as usize;
253 253 let end = end as usize;
254 254 let raw = (&self.index[start..end])
255 255 .try_into()
256 256 .expect("failed to turn 4 bytes into 4 bytes");
257 257 u32::from_be_bytes(raw)
258 258 }
259 259
260 260 /// internal function to return index of the copy source of the entry at a
261 261 /// given index
262 262 fn copy_idx(&self, idx: u32) -> u32 {
263 263 let start = (idx * Self::ENTRY_SIZE) + Self::COPY_SOURCE_START;
264 264 let end = (idx + 1) * Self::ENTRY_SIZE;
265 265 let start = start as usize;
266 266 let end = end as usize;
267 267 let raw = (&self.index[start..end])
268 268 .try_into()
269 269 .expect("failed to turn 4 bytes into 4 bytes");
270 270 u32::from_be_bytes(raw)
271 271 }
272 272
273 273 /// Return an iterator over all the `Action` in this instance.
274 274 fn iter_actions(&self) -> ActionsIterator {
275 275 ActionsIterator {
276 276 changes: &self,
277 277 current: 0,
278 278 }
279 279 }
280 280
281 281 /// return the MergeCase value associated with a filename
282 282 fn get_merge_case(&self, path: &HgPath) -> MergeCase {
283 283 if self.nb_items == 0 {
284 284 return MergeCase::Normal;
285 285 }
286 286 let mut low_part = 0;
287 287 let mut high_part = self.nb_items;
288 288
289 289 while low_part < high_part {
290 290 let cursor = (low_part + high_part - 1) / 2;
291 291 let (flags, filename, _source) = self.entry(cursor);
292 292 match path.cmp(filename) {
293 293 Ordering::Less => low_part = cursor + 1,
294 294 Ordering::Greater => high_part = cursor,
295 295 Ordering::Equal => {
296 296 return match flags & ACTION_MASK {
297 297 MERGED => MergeCase::Merged,
298 298 SALVAGED => MergeCase::Salvaged,
299 299 _ => MergeCase::Normal,
300 300 };
301 301 }
302 302 }
303 303 }
304 304 MergeCase::Normal
305 305 }
306 306 }
307 307
308 308 struct ActionsIterator<'a> {
309 309 changes: &'a ChangedFiles<'a>,
310 310 current: u32,
311 311 }
312 312
313 313 impl<'a> Iterator for ActionsIterator<'a> {
314 314 type Item = Action<'a>;
315 315
316 316 fn next(&mut self) -> Option<Action<'a>> {
317 317 while self.current < self.changes.nb_items {
318 318 let (flags, file, source) = self.changes.entry(self.current);
319 319 self.current += 1;
320 320 if (flags & ACTION_MASK) == REMOVED {
321 321 return Some(Action::Removed(file));
322 322 }
323 323 let copy = flags & COPY_MASK;
324 324 if copy == P1_COPY {
325 325 return Some(Action::CopiedFromP1(file, source));
326 326 } else if copy == P2_COPY {
327 327 return Some(Action::CopiedFromP2(file, source));
328 328 }
329 329 }
330 330 return None;
331 331 }
332 332 }
333 333
334 334 /// A small "tokenizer" responsible of turning full HgPath into lighter
335 335 /// PathToken
336 336 ///
337 337 /// Dealing with small object, like integer is much faster, so HgPath input are
338 338 /// turned into integer "PathToken" and converted back in the end.
339 339 #[derive(Clone, Debug, Default)]
340 340 struct TwoWayPathMap {
341 341 token: HashMap<HgPathBuf, PathToken>,
342 342 path: Vec<HgPathBuf>,
343 343 }
344 344
345 345 impl TwoWayPathMap {
346 346 fn tokenize(&mut self, path: &HgPath) -> PathToken {
347 347 match self.token.get(path) {
348 348 Some(a) => *a,
349 349 None => {
350 350 let a = self.token.len();
351 351 let buf = path.to_owned();
352 352 self.path.push(buf.clone());
353 353 self.token.insert(buf, a);
354 354 a
355 355 }
356 356 }
357 357 }
358 358
359 359 fn untokenize(&self, token: PathToken) -> &HgPathBuf {
360 assert!(token < self.path.len(), format!("Unknown token: {}", token));
360 assert!(token < self.path.len(), "Unknown token: {}", token);
361 361 &self.path[token]
362 362 }
363 363 }
364 364
365 365 /// Same as mercurial.copies._combine_changeset_copies, but in Rust.
366 366 pub struct CombineChangesetCopies {
367 367 all_copies: HashMap<Revision, InternalPathCopies>,
368 368 path_map: TwoWayPathMap,
369 369 children_count: HashMap<Revision, usize>,
370 370 }
371 371
372 372 impl CombineChangesetCopies {
373 373 pub fn new(children_count: HashMap<Revision, usize>) -> Self {
374 374 Self {
375 375 all_copies: HashMap::new(),
376 376 path_map: TwoWayPathMap::default(),
377 377 children_count,
378 378 }
379 379 }
380 380
381 381 /// Combined the given `changes` data specific to `rev` with the data
382 382 /// previously given for its parents (and transitively, its ancestors).
383 383 pub fn add_revision(
384 384 &mut self,
385 385 rev: Revision,
386 386 p1: Revision,
387 387 p2: Revision,
388 388 changes: ChangedFiles<'_>,
389 389 ) {
390 390 self.add_revision_inner(rev, p1, p2, changes.iter_actions(), |path| {
391 391 changes.get_merge_case(path)
392 392 })
393 393 }
394 394
395 395 /// Separated out from `add_revsion` so that unit tests can call this
396 396 /// without synthetizing a `ChangedFiles` in binary format.
397 397 fn add_revision_inner<'a>(
398 398 &mut self,
399 399 rev: Revision,
400 400 p1: Revision,
401 401 p2: Revision,
402 402 copy_actions: impl Iterator<Item = Action<'a>>,
403 403 get_merge_case: impl Fn(&HgPath) -> MergeCase + Copy,
404 404 ) {
405 405 // Retrieve data computed in a previous iteration
406 406 let p1_copies = match p1 {
407 407 NULL_REVISION => None,
408 408 _ => get_and_clean_parent_copies(
409 409 &mut self.all_copies,
410 410 &mut self.children_count,
411 411 p1,
412 412 ), // will be None if the vertex is not to be traversed
413 413 };
414 414 let p2_copies = match p2 {
415 415 NULL_REVISION => None,
416 416 _ => get_and_clean_parent_copies(
417 417 &mut self.all_copies,
418 418 &mut self.children_count,
419 419 p2,
420 420 ), // will be None if the vertex is not to be traversed
421 421 };
422 422 // combine it with data for that revision
423 423 let (p1_copies, p2_copies) = chain_changes(
424 424 &mut self.path_map,
425 425 p1_copies,
426 426 p2_copies,
427 427 copy_actions,
428 428 rev,
429 429 );
430 430 let copies = match (p1_copies, p2_copies) {
431 431 (None, None) => None,
432 432 (c, None) => c,
433 433 (None, c) => c,
434 434 (Some(p1_copies), Some(p2_copies)) => Some(merge_copies_dict(
435 435 &self.path_map,
436 436 rev,
437 437 p2_copies,
438 438 p1_copies,
439 439 get_merge_case,
440 440 )),
441 441 };
442 442 if let Some(c) = copies {
443 443 self.all_copies.insert(rev, c);
444 444 }
445 445 }
446 446
447 447 /// Drop intermediate data (such as which revision a copy was from) and
448 448 /// return the final mapping.
449 449 pub fn finish(mut self, target_rev: Revision) -> PathCopies {
450 450 let tt_result = self
451 451 .all_copies
452 452 .remove(&target_rev)
453 453 .expect("target revision was not processed");
454 454 let mut result = PathCopies::default();
455 455 for (dest, tt_source) in tt_result {
456 456 if let Some(path) = tt_source.path {
457 457 let path_dest = self.path_map.untokenize(dest).to_owned();
458 458 let path_path = self.path_map.untokenize(path).to_owned();
459 459 result.insert(path_dest, path_path);
460 460 }
461 461 }
462 462 result
463 463 }
464 464 }
465 465
466 466 /// fetch previous computed information
467 467 ///
468 468 /// If no other children are expected to need this information, we drop it from
469 469 /// the cache.
470 470 ///
471 471 /// If parent is not part of the set we are expected to walk, return None.
472 472 fn get_and_clean_parent_copies(
473 473 all_copies: &mut HashMap<Revision, InternalPathCopies>,
474 474 children_count: &mut HashMap<Revision, usize>,
475 475 parent_rev: Revision,
476 476 ) -> Option<InternalPathCopies> {
477 477 let count = children_count.get_mut(&parent_rev)?;
478 478 *count -= 1;
479 479 if *count == 0 {
480 480 match all_copies.remove(&parent_rev) {
481 481 Some(c) => Some(c),
482 482 None => Some(InternalPathCopies::default()),
483 483 }
484 484 } else {
485 485 match all_copies.get(&parent_rev) {
486 486 Some(c) => Some(c.clone()),
487 487 None => Some(InternalPathCopies::default()),
488 488 }
489 489 }
490 490 }
491 491
492 492 /// Combine ChangedFiles with some existing PathCopies information and return
493 493 /// the result
494 494 fn chain_changes<'a>(
495 495 path_map: &mut TwoWayPathMap,
496 496 base_p1_copies: Option<InternalPathCopies>,
497 497 base_p2_copies: Option<InternalPathCopies>,
498 498 copy_actions: impl Iterator<Item = Action<'a>>,
499 499 current_rev: Revision,
500 500 ) -> (Option<InternalPathCopies>, Option<InternalPathCopies>) {
501 501 // Fast path the "nothing to do" case.
502 502 if let (None, None) = (&base_p1_copies, &base_p2_copies) {
503 503 return (None, None);
504 504 }
505 505
506 506 let mut p1_copies = base_p1_copies.clone();
507 507 let mut p2_copies = base_p2_copies.clone();
508 508 for action in copy_actions {
509 509 match action {
510 510 Action::CopiedFromP1(path_dest, path_source) => {
511 511 match &mut p1_copies {
512 512 None => (), // This is not a vertex we should proceed.
513 513 Some(copies) => add_one_copy(
514 514 current_rev,
515 515 path_map,
516 516 copies,
517 517 base_p1_copies.as_ref().unwrap(),
518 518 path_dest,
519 519 path_source,
520 520 ),
521 521 }
522 522 }
523 523 Action::CopiedFromP2(path_dest, path_source) => {
524 524 match &mut p2_copies {
525 525 None => (), // This is not a vertex we should proceed.
526 526 Some(copies) => add_one_copy(
527 527 current_rev,
528 528 path_map,
529 529 copies,
530 530 base_p2_copies.as_ref().unwrap(),
531 531 path_dest,
532 532 path_source,
533 533 ),
534 534 }
535 535 }
536 536 Action::Removed(deleted_path) => {
537 537 // We must drop copy information for removed file.
538 538 //
539 539 // We need to explicitly record them as dropped to
540 540 // propagate this information when merging two
541 541 // InternalPathCopies object.
542 542 let deleted = path_map.tokenize(deleted_path);
543 543
544 544 let p1_entry = match &mut p1_copies {
545 545 None => None,
546 546 Some(copies) => match copies.entry(deleted) {
547 547 Entry::Occupied(e) => Some(e),
548 548 Entry::Vacant(_) => None,
549 549 },
550 550 };
551 551 let p2_entry = match &mut p2_copies {
552 552 None => None,
553 553 Some(copies) => match copies.entry(deleted) {
554 554 Entry::Occupied(e) => Some(e),
555 555 Entry::Vacant(_) => None,
556 556 },
557 557 };
558 558
559 559 match (p1_entry, p2_entry) {
560 560 (None, None) => (),
561 561 (Some(mut e), None) => {
562 562 e.get_mut().mark_delete(current_rev)
563 563 }
564 564 (None, Some(mut e)) => {
565 565 e.get_mut().mark_delete(current_rev)
566 566 }
567 567 (Some(mut e1), Some(mut e2)) => {
568 568 let cs1 = e1.get_mut();
569 569 let cs2 = e2.get();
570 570 if cs1 == cs2 {
571 571 cs1.mark_delete(current_rev);
572 572 } else {
573 573 cs1.mark_delete_with_pair(current_rev, &cs2);
574 574 }
575 575 e2.insert(cs1.clone());
576 576 }
577 577 }
578 578 }
579 579 }
580 580 }
581 581 (p1_copies, p2_copies)
582 582 }
583 583
584 584 // insert one new copy information in an InternalPathCopies
585 585 //
586 586 // This deal with chaining and overwrite.
587 587 fn add_one_copy(
588 588 current_rev: Revision,
589 589 path_map: &mut TwoWayPathMap,
590 590 copies: &mut InternalPathCopies,
591 591 base_copies: &InternalPathCopies,
592 592 path_dest: &HgPath,
593 593 path_source: &HgPath,
594 594 ) {
595 595 let dest = path_map.tokenize(path_dest);
596 596 let source = path_map.tokenize(path_source);
597 597 let entry;
598 598 if let Some(v) = base_copies.get(&source) {
599 599 entry = match &v.path {
600 600 Some(path) => Some((*(path)).to_owned()),
601 601 None => Some(source.to_owned()),
602 602 }
603 603 } else {
604 604 entry = Some(source.to_owned());
605 605 }
606 606 // Each new entry is introduced by the children, we
607 607 // record this information as we will need it to take
608 608 // the right decision when merging conflicting copy
609 609 // information. See merge_copies_dict for details.
610 610 match copies.entry(dest) {
611 611 Entry::Vacant(slot) => {
612 612 let ttpc = CopySource::new(current_rev, entry);
613 613 slot.insert(ttpc);
614 614 }
615 615 Entry::Occupied(mut slot) => {
616 616 let ttpc = slot.get_mut();
617 617 ttpc.overwrite(current_rev, entry);
618 618 }
619 619 }
620 620 }
621 621
622 622 /// merge two copies-mapping together, minor and major
623 623 ///
624 624 /// In case of conflict, value from "major" will be picked, unless in some
625 625 /// cases. See inline documentation for details.
626 626 fn merge_copies_dict(
627 627 path_map: &TwoWayPathMap,
628 628 current_merge: Revision,
629 629 minor: InternalPathCopies,
630 630 major: InternalPathCopies,
631 631 get_merge_case: impl Fn(&HgPath) -> MergeCase + Copy,
632 632 ) -> InternalPathCopies {
633 633 use crate::utils::{ordmap_union_with_merge, MergeResult};
634 634
635 635 ordmap_union_with_merge(minor, major, |&dest, src_minor, src_major| {
636 636 let (pick, overwrite) = compare_value(
637 637 current_merge,
638 638 || get_merge_case(path_map.untokenize(dest)),
639 639 src_minor,
640 640 src_major,
641 641 );
642 642 if overwrite {
643 643 let (winner, loser) = match pick {
644 644 MergePick::Major | MergePick::Any => (src_major, src_minor),
645 645 MergePick::Minor => (src_minor, src_major),
646 646 };
647 647 MergeResult::UseNewValue(CopySource::new_from_merge(
648 648 current_merge,
649 649 winner,
650 650 loser,
651 651 ))
652 652 } else {
653 653 match pick {
654 654 MergePick::Any | MergePick::Major => {
655 655 MergeResult::UseRightValue
656 656 }
657 657 MergePick::Minor => MergeResult::UseLeftValue,
658 658 }
659 659 }
660 660 })
661 661 }
662 662
663 663 /// represent the side that should prevail when merging two
664 664 /// InternalPathCopies
665 665 #[derive(Debug, PartialEq)]
666 666 enum MergePick {
667 667 /// The "major" (p1) side prevails
668 668 Major,
669 669 /// The "minor" (p2) side prevails
670 670 Minor,
671 671 /// Any side could be used (because they are the same)
672 672 Any,
673 673 }
674 674
675 675 /// decide which side prevails in case of conflicting values
676 676 #[allow(clippy::if_same_then_else)]
677 677 fn compare_value(
678 678 current_merge: Revision,
679 679 merge_case_for_dest: impl Fn() -> MergeCase,
680 680 src_minor: &CopySource,
681 681 src_major: &CopySource,
682 682 ) -> (MergePick, bool) {
683 683 if src_major == src_minor {
684 684 (MergePick::Any, false)
685 685 } else if src_major.rev == current_merge {
686 686 // minor is different according to per minor == major check earlier
687 687 debug_assert!(src_minor.rev != current_merge);
688 688
689 689 // The last value comes the current merge, this value -will- win
690 690 // eventually.
691 691 (MergePick::Major, true)
692 692 } else if src_minor.rev == current_merge {
693 693 // The last value comes the current merge, this value -will- win
694 694 // eventually.
695 695 (MergePick::Minor, true)
696 696 } else if src_major.path == src_minor.path {
697 697 debug_assert!(src_major.rev != src_major.rev);
698 698 // we have the same value, but from other source;
699 699 if src_major.is_overwritten_by(src_minor) {
700 700 (MergePick::Minor, false)
701 701 } else if src_minor.is_overwritten_by(src_major) {
702 702 (MergePick::Major, false)
703 703 } else {
704 704 (MergePick::Any, true)
705 705 }
706 706 } else {
707 707 debug_assert!(src_major.rev != src_major.rev);
708 708 let action = merge_case_for_dest();
709 709 if src_minor.path.is_some()
710 710 && src_major.path.is_none()
711 711 && action == MergeCase::Salvaged
712 712 {
713 713 // If the file is "deleted" in the major side but was
714 714 // salvaged by the merge, we keep the minor side alive
715 715 (MergePick::Minor, true)
716 716 } else if src_major.path.is_some()
717 717 && src_minor.path.is_none()
718 718 && action == MergeCase::Salvaged
719 719 {
720 720 // If the file is "deleted" in the minor side but was
721 721 // salvaged by the merge, unconditionnaly preserve the
722 722 // major side.
723 723 (MergePick::Major, true)
724 724 } else if src_minor.is_overwritten_by(src_major) {
725 725 // The information from the minor version are strictly older than
726 726 // the major version
727 727 if action == MergeCase::Merged {
728 728 // If the file was actively merged, its means some non-copy
729 729 // activity happened on the other branch. It
730 730 // mean the older copy information are still relevant.
731 731 //
732 732 // The major side wins such conflict.
733 733 (MergePick::Major, true)
734 734 } else {
735 735 // No activity on the minor branch, pick the newer one.
736 736 (MergePick::Major, false)
737 737 }
738 738 } else if src_major.is_overwritten_by(src_minor) {
739 739 if action == MergeCase::Merged {
740 740 // If the file was actively merged, its means some non-copy
741 741 // activity happened on the other branch. It
742 742 // mean the older copy information are still relevant.
743 743 //
744 744 // The major side wins such conflict.
745 745 (MergePick::Major, true)
746 746 } else {
747 747 // No activity on the minor branch, pick the newer one.
748 748 (MergePick::Minor, false)
749 749 }
750 750 } else if src_minor.path.is_none() {
751 751 // the minor side has no relevant information, pick the alive one
752 752 (MergePick::Major, true)
753 753 } else if src_major.path.is_none() {
754 754 // the major side has no relevant information, pick the alive one
755 755 (MergePick::Minor, true)
756 756 } else {
757 757 // by default the major side wins
758 758 (MergePick::Major, true)
759 759 }
760 760 }
761 761 }
General Comments 0
You need to be logged in to leave comments. Login now