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