comparison mercurial/copies.py @ 44199:7f8bdee0034e

copies: define a type to return from mergecopies() We'll soon return two instances of many of the dicts from `copies.mergecopies()`. That will mean that we need to return 9 different dicts, which is clearly not manageable. This patch instead encapsulates the 4 dicts we'll duplicate in a new type. For now, we still just return one instance of it (plus the separate `diverge` dict). Differential Revision: https://phab.mercurial-scm.org/D7989
author Martin von Zweigbergk <martinvonz@google.com>
date Wed, 22 Jan 2020 14:35:30 -0800
parents 17e12938f8e7
children fa9ad1da2e77
comparison
equal deleted inserted replaced
44198:8ad263c3a358 44199:7f8bdee0034e
450 revision 4, and if user have copytrace disabled, we prints the following 450 revision 4, and if user have copytrace disabled, we prints the following
451 message: 451 message:
452 452
453 ```other changed <file> which local deleted``` 453 ```other changed <file> which local deleted```
454 454
455 Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and 455 Returns a tuple where:
456 "dirmove". 456
457 457 "branch_copies" an instance of branch_copies.
458 "copy" is a mapping from destination name -> source name,
459 where source is in c1 and destination is in c2 or vice-versa.
460
461 "movewithdir" is a mapping from source name -> destination name,
462 where the file at source present in one context but not the other
463 needs to be moved to destination by the merge process, because the
464 other context moved the directory it is in.
465 458
466 "diverge" is a mapping of source name -> list of destination names 459 "diverge" is a mapping of source name -> list of destination names
467 for divergent renames. 460 for divergent renames.
468 461
469 "renamedelete" is a mapping of source name -> list of destination
470 names for files deleted in c1 that were renamed in c2 or vice-versa.
471
472 "dirmove" is a mapping of detected source dir -> destination dir renames.
473 This is needed for handling changes to new files previously grafted into
474 renamed directories.
475
476 This function calls different copytracing algorithms based on config. 462 This function calls different copytracing algorithms based on config.
477 """ 463 """
478 # avoid silly behavior for update from empty dir 464 # avoid silly behavior for update from empty dir
479 if not c1 or not c2 or c1 == c2: 465 if not c1 or not c2 or c1 == c2:
480 return {}, {}, {}, {}, {} 466 return branch_copies(), {}
481 467
482 narrowmatch = c1.repo().narrowmatch() 468 narrowmatch = c1.repo().narrowmatch()
483 469
484 # avoid silly behavior for parent -> working dir 470 # avoid silly behavior for parent -> working dir
485 if c2.node() is None and c1.node() == repo.dirstate.p1(): 471 if c2.node() is None and c1.node() == repo.dirstate.p1():
486 return _dirstatecopies(repo, narrowmatch), {}, {}, {}, {} 472 return branch_copies(_dirstatecopies(repo, narrowmatch)), {}
487 473
488 copytracing = repo.ui.config(b'experimental', b'copytrace') 474 copytracing = repo.ui.config(b'experimental', b'copytrace')
489 if stringutil.parsebool(copytracing) is False: 475 if stringutil.parsebool(copytracing) is False:
490 # stringutil.parsebool() returns None when it is unable to parse the 476 # stringutil.parsebool() returns None when it is unable to parse the
491 # value, so we should rely on making sure copytracing is on such cases 477 # value, so we should rely on making sure copytracing is on such cases
492 return {}, {}, {}, {}, {} 478 return branch_copies(), {}
493 479
494 if usechangesetcentricalgo(repo): 480 if usechangesetcentricalgo(repo):
495 # The heuristics don't make sense when we need changeset-centric algos 481 # The heuristics don't make sense when we need changeset-centric algos
496 return _fullcopytracing(repo, c1, c2, base) 482 return _fullcopytracing(repo, c1, c2, base)
497 483
546 # dst not added on side 2 (handle as regular 532 # dst not added on side 2 (handle as regular
547 # "both created" case in manifestmerge otherwise) 533 # "both created" case in manifestmerge otherwise)
548 copy[dst] = src 534 copy[dst] = src
549 535
550 536
537 class branch_copies(object):
538 """Information about copies made on one side of a merge/graft.
539
540 "copy" is a mapping from destination name -> source name,
541 where source is in c1 and destination is in c2 or vice-versa.
542
543 "movewithdir" is a mapping from source name -> destination name,
544 where the file at source present in one context but not the other
545 needs to be moved to destination by the merge process, because the
546 other context moved the directory it is in.
547
548 "renamedelete" is a mapping of source name -> list of destination
549 names for files deleted in c1 that were renamed in c2 or vice-versa.
550
551 "dirmove" is a mapping of detected source dir -> destination dir renames.
552 This is needed for handling changes to new files previously grafted into
553 renamed directories.
554 """
555
556 def __init__(
557 self, copy=None, renamedelete=None, dirmove=None, movewithdir=None
558 ):
559 self.copy = {} if copy is None else copy
560 self.renamedelete = {} if renamedelete is None else renamedelete
561 self.dirmove = {} if dirmove is None else dirmove
562 self.movewithdir = {} if movewithdir is None else movewithdir
563
564
551 def _fullcopytracing(repo, c1, c2, base): 565 def _fullcopytracing(repo, c1, c2, base):
552 """ The full copytracing algorithm which finds all the new files that were 566 """ The full copytracing algorithm which finds all the new files that were
553 added from merge base up to the top commit and for each file it checks if 567 added from merge base up to the top commit and for each file it checks if
554 this file was copied from another file. 568 this file was copied from another file.
555 569
562 576
563 copies1 = pathcopies(base, c1) 577 copies1 = pathcopies(base, c1)
564 copies2 = pathcopies(base, c2) 578 copies2 = pathcopies(base, c2)
565 579
566 if not (copies1 or copies2): 580 if not (copies1 or copies2):
567 return {}, {}, {}, {}, {} 581 return branch_copies(), {}
568 582
569 inversecopies1 = {} 583 inversecopies1 = {}
570 inversecopies2 = {} 584 inversecopies2 = {}
571 for dst, src in copies1.items(): 585 for dst, src in copies1.items():
572 inversecopies1.setdefault(src, []).append(dst) 586 inversecopies1.setdefault(src, []).append(dst)
670 copy1.update(copy2) 684 copy1.update(copy2)
671 renamedelete1.update(renamedelete2) 685 renamedelete1.update(renamedelete2)
672 movewithdir1.update(movewithdir2) 686 movewithdir1.update(movewithdir2)
673 dirmove1.update(dirmove2) 687 dirmove1.update(dirmove2)
674 688
675 return copy1, movewithdir1, diverge, renamedelete1, dirmove1 689 return branch_copies(copy1, renamedelete1, dirmove1, movewithdir1), diverge
676 690
677 691
678 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles): 692 def _dir_renames(repo, ctx, copy, fullcopy, addedfiles):
679 """Finds moved directories and files that should move with them. 693 """Finds moved directories and files that should move with them.
680 694
844 # if there are a few related copies then we'll merge 858 # if there are a few related copies then we'll merge
845 # changes into all of them. This matches the behaviour 859 # changes into all of them. This matches the behaviour
846 # of upstream copytracing 860 # of upstream copytracing
847 copies[candidate] = f 861 copies[candidate] = f
848 862
849 return copies, {}, {}, {}, {} 863 return branch_copies(copies), {}
850 864
851 865
852 def _related(f1, f2): 866 def _related(f1, f2):
853 """return True if f1 and f2 filectx have a common ancestor 867 """return True if f1 and f2 filectx have a common ancestor
854 868