355 """ |
355 """ |
356 |
356 |
357 alwaysmatch = match.always() |
357 alwaysmatch = match.always() |
358 |
358 |
359 if rustmod is not None and alwaysmatch: |
359 if rustmod is not None and alwaysmatch: |
360 return rustmod.combine_changeset_copies( |
360 final_copies = rustmod.combine_changeset_copies( |
361 list(revs), children_count, targetrev, revinfo, isancestor |
361 list(revs), children_count, targetrev, revinfo, isancestor |
362 ) |
362 ) |
363 |
363 else: |
364 isancestor = cached_is_ancestor(isancestor) |
364 isancestor = cached_is_ancestor(isancestor) |
365 |
365 |
366 all_copies = {} |
366 all_copies = {} |
367 # iterate over all the "children" side of copy tracing "edge" |
367 # iterate over all the "children" side of copy tracing "edge" |
368 for current_rev in revs: |
368 for current_rev in revs: |
369 p1, p2, changes = revinfo(current_rev) |
369 p1, p2, changes = revinfo(current_rev) |
370 current_copies = None |
370 current_copies = None |
371 |
371 # iterate over all parents to chain the existing data with the |
372 # iterate over all parents to chain the existing data with the |
372 # data from the parent → child edge. |
373 # data from the parent → child edge. |
373 for parent, parent_rev in ((1, p1), (2, p2)): |
374 for parent, parent_rev in ((1, p1), (2, p2)): |
374 if parent_rev == nullrev: |
375 if parent_rev == nullrev: |
375 continue |
376 continue |
376 remaining_children = children_count.get(parent_rev) |
377 remaining_children = children_count.get(parent_rev) |
377 if remaining_children is None: |
378 if remaining_children is None: |
378 continue |
379 continue |
379 remaining_children -= 1 |
380 remaining_children -= 1 |
380 children_count[parent_rev] = remaining_children |
381 children_count[parent_rev] = remaining_children |
381 if remaining_children: |
382 if remaining_children: |
382 copies = all_copies.get(parent_rev, None) |
383 copies = all_copies.get(parent_rev, None) |
383 else: |
384 else: |
384 copies = all_copies.pop(parent_rev, None) |
385 copies = all_copies.pop(parent_rev, None) |
385 |
386 |
386 if copies is None: |
387 if copies is None: |
387 # this is a root |
388 # this is a root |
388 copies = {} |
389 copies = {} |
389 |
390 |
390 newcopies = copies |
391 newcopies = copies |
391 # chain the data in the edge with the existing data |
392 # chain the data in the edge with the existing data |
392 if changes is not None: |
393 if changes is not None: |
393 childcopies = {} |
394 childcopies = {} |
394 if parent == 1: |
395 if parent == 1: |
395 childcopies = changes.copied_from_p1 |
396 childcopies = changes.copied_from_p1 |
396 elif parent == 2: |
397 elif parent == 2: |
397 childcopies = changes.copied_from_p2 |
398 childcopies = changes.copied_from_p2 |
398 |
399 |
399 if not alwaysmatch: |
400 if not alwaysmatch: |
400 childcopies = { |
401 childcopies = { |
401 dst: src |
402 dst: src |
402 for dst, src in childcopies.items() |
403 for dst, src in childcopies.items() |
403 if match(dst) |
404 if match(dst) |
404 } |
405 } |
405 if childcopies: |
406 if childcopies: |
|
407 newcopies = copies.copy() |
|
408 for dest, source in pycompat.iteritems(childcopies): |
|
409 prev = copies.get(source) |
|
410 if prev is not None and prev[1] is not None: |
|
411 source = prev[1] |
|
412 newcopies[dest] = (current_rev, source) |
|
413 assert newcopies is not copies |
|
414 if changes.removed: |
|
415 if newcopies is copies: |
|
416 newcopies = copies.copy() |
406 newcopies = copies.copy() |
417 for f in changes.removed: |
407 for dest, source in pycompat.iteritems(childcopies): |
418 if f in newcopies: |
408 prev = copies.get(source) |
419 if newcopies is copies: |
409 if prev is not None and prev[1] is not None: |
420 # copy on write to avoid affecting potential other |
410 source = prev[1] |
421 # branches. when there are no other branches, this |
411 newcopies[dest] = (current_rev, source) |
422 # could be avoided. |
412 assert newcopies is not copies |
423 newcopies = copies.copy() |
413 if changes.removed: |
424 newcopies[f] = (current_rev, None) |
414 if newcopies is copies: |
425 |
415 newcopies = copies.copy() |
426 # check potential need to combine the data from another parent (for |
416 for f in changes.removed: |
427 # that child). See comment below for details. |
417 if f in newcopies: |
428 if current_copies is None: |
418 if newcopies is copies: |
429 current_copies = newcopies |
419 # copy on write to avoid affecting potential other |
430 elif current_copies is newcopies: |
420 # branches. when there are no other branches, this |
431 # nothing to merge: |
421 # could be avoided. |
432 pass |
422 newcopies = copies.copy() |
433 else: |
423 newcopies[f] = (current_rev, None) |
434 # we are the second parent to work on c, we need to merge our |
424 # check potential need to combine the data from another parent (for |
435 # work with the other. |
425 # that child). See comment below for details. |
436 # |
426 if current_copies is None: |
437 # In case of conflict, parent 1 take precedence over parent 2. |
427 current_copies = newcopies |
438 # This is an arbitrary choice made anew when implementing |
428 elif current_copies is newcopies: |
439 # changeset based copies. It was made without regards with |
429 # nothing to merge: |
440 # potential filelog related behavior. |
430 pass |
441 assert parent == 2 |
431 else: |
442 current_copies = _merge_copies_dict( |
432 # we are the second parent to work on c, we need to merge our |
443 newcopies, current_copies, isancestor, changes |
433 # work with the other. |
444 ) |
434 # |
445 all_copies[current_rev] = current_copies |
435 # In case of conflict, parent 1 take precedence over parent 2. |
446 |
436 # This is an arbitrary choice made anew when implementing |
447 # filter out internal details and return a {dest: source mapping} |
437 # changeset based copies. It was made without regards with |
448 final_copies = {} |
438 # potential filelog related behavior. |
449 for dest, (tt, source) in all_copies[targetrev].items(): |
439 assert parent == 2 |
450 if source is not None: |
440 current_copies = _merge_copies_dict( |
451 final_copies[dest] = source |
441 newcopies, current_copies, isancestor, changes |
|
442 ) |
|
443 all_copies[current_rev] = current_copies |
|
444 |
|
445 # filter out internal details and return a {dest: source mapping} |
|
446 final_copies = {} |
|
447 for dest, (tt, source) in all_copies[targetrev].items(): |
|
448 if source is not None: |
|
449 final_copies[dest] = source |
452 return final_copies |
450 return final_copies |
453 |
451 |
454 |
452 |
455 def _merge_copies_dict(minor, major, isancestor, changes): |
453 def _merge_copies_dict(minor, major, isancestor, changes): |
456 """merge two copies-mapping together, minor and major |
454 """merge two copies-mapping together, minor and major |