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