Mercurial > public > mercurial-scm > hg
comparison mercurial/merge.py @ 49366:288de6f5d724 stable 6.2rc0
branching: merge default into stable
author | Rapha?l Gom?s <rgomes@octobus.net> |
---|---|
date | Thu, 16 Jun 2022 15:28:54 +0200 |
parents | 2e726c934fcd 0cc5f74ff7f0 |
children | becd16690cbe |
comparison
equal
deleted
inserted
replaced
49364:e8ea403b1c46 | 49366:288de6f5d724 |
---|---|
3 # Copyright 2006, 2007 Olivia Mackall <olivia@selenic.com> | 3 # Copyright 2006, 2007 Olivia Mackall <olivia@selenic.com> |
4 # | 4 # |
5 # This software may be used and distributed according to the terms of the | 5 # This software may be used and distributed according to the terms of the |
6 # GNU General Public License version 2 or any later version. | 6 # GNU General Public License version 2 or any later version. |
7 | 7 |
8 from __future__ import absolute_import | |
9 | 8 |
10 import collections | 9 import collections |
11 import errno | |
12 import struct | 10 import struct |
13 | 11 |
14 from .i18n import _ | 12 from .i18n import _ |
15 from .node import nullrev | 13 from .node import nullrev |
16 from .thirdparty import attr | 14 from .thirdparty import attr |
65 and repo.dirstate.normalize(f) not in repo.dirstate | 63 and repo.dirstate.normalize(f) not in repo.dirstate |
66 and mctx[f2].cmp(wctx[f]) | 64 and mctx[f2].cmp(wctx[f]) |
67 ) | 65 ) |
68 | 66 |
69 | 67 |
70 class _unknowndirschecker(object): | 68 class _unknowndirschecker: |
71 """ | 69 """ |
72 Look for any unknown files or directories that may have a path conflict | 70 Look for any unknown files or directories that may have a path conflict |
73 with a file. If any path prefix of the file exists as a file or link, | 71 with a file. If any path prefix of the file exists as a file or link, |
74 then it conflicts. If the file itself is a directory that contains any | 72 then it conflicts. If the file itself is a directory that contains any |
75 file that is not tracked, then it conflicts. | 73 file that is not tracked, then it conflicts. |
536 else: | 534 else: |
537 msg = _(b'conflict in file \'%s\' is outside narrow clone') | 535 msg = _(b'conflict in file \'%s\' is outside narrow clone') |
538 raise error.StateError(msg % f) | 536 raise error.StateError(msg % f) |
539 | 537 |
540 | 538 |
541 class mergeresult(object): | 539 class mergeresult: |
542 """An object representing result of merging manifests. | 540 """An object representing result of merging manifests. |
543 | 541 |
544 It has information about what actions need to be performed on dirstate | 542 It has information about what actions need to be performed on dirstate |
545 mapping of divergent renames and other such cases.""" | 543 mapping of divergent renames and other such cases.""" |
546 | 544 |
624 if sort: | 622 if sort: |
625 for f in sorted(self._actionmapping[a]): | 623 for f in sorted(self._actionmapping[a]): |
626 args, msg = self._actionmapping[a][f] | 624 args, msg = self._actionmapping[a][f] |
627 yield f, args, msg | 625 yield f, args, msg |
628 else: | 626 else: |
629 for f, (args, msg) in pycompat.iteritems( | 627 for f, (args, msg) in self._actionmapping[a].items(): |
630 self._actionmapping[a] | |
631 ): | |
632 yield f, args, msg | 628 yield f, args, msg |
633 | 629 |
634 def len(self, actions=None): | 630 def len(self, actions=None): |
635 """returns number of files which needs actions | 631 """returns number of files which needs actions |
636 | 632 |
642 | 638 |
643 return sum(len(self._actionmapping[a]) for a in actions) | 639 return sum(len(self._actionmapping[a]) for a in actions) |
644 | 640 |
645 def filemap(self, sort=False): | 641 def filemap(self, sort=False): |
646 if sorted: | 642 if sorted: |
647 for key, val in sorted(pycompat.iteritems(self._filemapping)): | 643 for key, val in sorted(self._filemapping.items()): |
648 yield key, val | 644 yield key, val |
649 else: | 645 else: |
650 for key, val in pycompat.iteritems(self._filemapping): | 646 for key, val in self._filemapping.items(): |
651 yield key, val | 647 yield key, val |
652 | 648 |
653 def addcommitinfo(self, filename, key, value): | 649 def addcommitinfo(self, filename, key, value): |
654 """adds key-value information about filename which will be required | 650 """adds key-value information about filename which will be required |
655 while committing this merge""" | 651 while committing this merge""" |
670 @property | 666 @property |
671 def actionsdict(self): | 667 def actionsdict(self): |
672 """returns a dictionary of actions to be perfomed with action as key | 668 """returns a dictionary of actions to be perfomed with action as key |
673 and a list of files and related arguments as values""" | 669 and a list of files and related arguments as values""" |
674 res = collections.defaultdict(list) | 670 res = collections.defaultdict(list) |
675 for a, d in pycompat.iteritems(self._actionmapping): | 671 for a, d in self._actionmapping.items(): |
676 for f, (args, msg) in pycompat.iteritems(d): | 672 for f, (args, msg) in d.items(): |
677 res[a].append((f, args, msg)) | 673 res[a].append((f, args, msg)) |
678 return res | 674 return res |
679 | 675 |
680 def setactions(self, actions): | 676 def setactions(self, actions): |
681 self._filemapping = actions | 677 self._filemapping = actions |
682 self._actionmapping = collections.defaultdict(dict) | 678 self._actionmapping = collections.defaultdict(dict) |
683 for f, (act, data, msg) in pycompat.iteritems(self._filemapping): | 679 for f, (act, data, msg) in self._filemapping.items(): |
684 self._actionmapping[act][f] = data, msg | 680 self._actionmapping[act][f] = data, msg |
685 | 681 |
686 def hasconflicts(self): | 682 def hasconflicts(self): |
687 """tells whether this merge resulted in some actions which can | 683 """tells whether this merge resulted in some actions which can |
688 result in conflicts or not""" | 684 result in conflicts or not""" |
785 # total m1-vs-m2 diff to just those files. This has significant | 781 # total m1-vs-m2 diff to just those files. This has significant |
786 # performance benefits in large repositories. | 782 # performance benefits in large repositories. |
787 relevantfiles = set(ma.diff(m2).keys()) | 783 relevantfiles = set(ma.diff(m2).keys()) |
788 | 784 |
789 # For copied and moved files, we need to add the source file too. | 785 # For copied and moved files, we need to add the source file too. |
790 for copykey, copyvalue in pycompat.iteritems(branch_copies1.copy): | 786 for copykey, copyvalue in branch_copies1.copy.items(): |
791 if copyvalue in relevantfiles: | 787 if copyvalue in relevantfiles: |
792 relevantfiles.add(copykey) | 788 relevantfiles.add(copykey) |
793 for movedirkey in branch_copies1.movewithdir: | 789 for movedirkey in branch_copies1.movewithdir: |
794 relevantfiles.add(movedirkey) | 790 relevantfiles.add(movedirkey) |
795 filesmatcher = scmutil.matchfiles(repo, relevantfiles) | 791 filesmatcher = scmutil.matchfiles(repo, relevantfiles) |
796 matcher = matchmod.intersectmatchers(matcher, filesmatcher) | 792 matcher = matchmod.intersectmatchers(matcher, filesmatcher) |
797 | 793 |
798 diff = m1.diff(m2, match=matcher) | 794 diff = m1.diff(m2, match=matcher) |
799 | 795 |
800 for f, ((n1, fl1), (n2, fl2)) in pycompat.iteritems(diff): | 796 for f, ((n1, fl1), (n2, fl2)) in diff.items(): |
801 if n1 and n2: # file exists on both local and remote side | 797 if n1 and n2: # file exists on both local and remote side |
802 if f not in ma: | 798 if f not in ma: |
803 # TODO: what if they're renamed from different sources? | 799 # TODO: what if they're renamed from different sources? |
804 fa = branch_copies1.copy.get( | 800 fa = branch_copies1.copy.get( |
805 f, None | 801 f, None |
1307 | 1303 |
1308 | 1304 |
1309 def _getcwd(): | 1305 def _getcwd(): |
1310 try: | 1306 try: |
1311 return encoding.getcwd() | 1307 return encoding.getcwd() |
1312 except OSError as err: | 1308 except FileNotFoundError: |
1313 if err.errno == errno.ENOENT: | 1309 return None |
1314 return None | |
1315 raise | |
1316 | 1310 |
1317 | 1311 |
1318 def batchremove(repo, wctx, actions): | 1312 def batchremove(repo, wctx, actions): |
1319 """apply removes to the working directory | 1313 """apply removes to the working directory |
1320 | 1314 |
1468 ], | 1462 ], |
1469 ) | 1463 ) |
1470 | 1464 |
1471 | 1465 |
1472 @attr.s(frozen=True) | 1466 @attr.s(frozen=True) |
1473 class updateresult(object): | 1467 class updateresult: |
1474 updatedcount = attr.ib() | 1468 updatedcount = attr.ib() |
1475 mergedcount = attr.ib() | 1469 mergedcount = attr.ib() |
1476 removedcount = attr.ib() | 1470 removedcount = attr.ib() |
1477 unresolvedcount = attr.ib() | 1471 unresolvedcount = attr.ib() |
1478 | 1472 |
1510 | 1504 |
1511 updated, merged, removed = 0, 0, 0 | 1505 updated, merged, removed = 0, 0, 0 |
1512 ms = wctx.mergestate(clean=True) | 1506 ms = wctx.mergestate(clean=True) |
1513 ms.start(wctx.p1().node(), mctx.node(), labels) | 1507 ms.start(wctx.p1().node(), mctx.node(), labels) |
1514 | 1508 |
1515 for f, op in pycompat.iteritems(mresult.commitinfo): | 1509 for f, op in mresult.commitinfo.items(): |
1516 # the other side of filenode was choosen while merging, store this in | 1510 # the other side of filenode was choosen while merging, store this in |
1517 # mergestate so that it can be reused on commit | 1511 # mergestate so that it can be reused on commit |
1518 ms.addcommitinfo(f, op) | 1512 ms.addcommitinfo(f, op) |
1519 | 1513 |
1520 num_no_op = mresult.len(mergestatemod.MergeAction.NO_OP_ACTIONS) | 1514 num_no_op = mresult.len(mergestatemod.MergeAction.NO_OP_ACTIONS) |
2071 _checkcollision(repo, p2.manifest(), None) | 2065 _checkcollision(repo, p2.manifest(), None) |
2072 else: | 2066 else: |
2073 _checkcollision(repo, wc.manifest(), mresult) | 2067 _checkcollision(repo, wc.manifest(), mresult) |
2074 | 2068 |
2075 # divergent renames | 2069 # divergent renames |
2076 for f, fl in sorted(pycompat.iteritems(mresult.diverge)): | 2070 for f, fl in sorted(mresult.diverge.items()): |
2077 repo.ui.warn( | 2071 repo.ui.warn( |
2078 _( | 2072 _( |
2079 b"note: possible conflict - %s was renamed " | 2073 b"note: possible conflict - %s was renamed " |
2080 b"multiple times to:\n" | 2074 b"multiple times to:\n" |
2081 ) | 2075 ) |
2083 ) | 2077 ) |
2084 for nf in sorted(fl): | 2078 for nf in sorted(fl): |
2085 repo.ui.warn(b" %s\n" % nf) | 2079 repo.ui.warn(b" %s\n" % nf) |
2086 | 2080 |
2087 # rename and delete | 2081 # rename and delete |
2088 for f, fl in sorted(pycompat.iteritems(mresult.renamedelete)): | 2082 for f, fl in sorted(mresult.renamedelete.items()): |
2089 repo.ui.warn( | 2083 repo.ui.warn( |
2090 _( | 2084 _( |
2091 b"note: possible conflict - %s was deleted " | 2085 b"note: possible conflict - %s was deleted " |
2092 b"and renamed to:\n" | 2086 b"and renamed to:\n" |
2093 ) | 2087 ) |
2123 labels=labels, | 2117 labels=labels, |
2124 ) | 2118 ) |
2125 | 2119 |
2126 if updatedirstate: | 2120 if updatedirstate: |
2127 if extraactions: | 2121 if extraactions: |
2128 for k, acts in pycompat.iteritems(extraactions): | 2122 for k, acts in extraactions.items(): |
2129 for a in acts: | 2123 for a in acts: |
2130 mresult.addfile(a[0], k, *a[1:]) | 2124 mresult.addfile(a[0], k, *a[1:]) |
2131 if k == mergestatemod.ACTION_GET and wantfiledata: | 2125 if k == mergestatemod.ACTION_GET and wantfiledata: |
2132 # no filedata until mergestate is updated to provide it | 2126 # no filedata until mergestate is updated to provide it |
2133 for a in acts: | 2127 for a in acts: |
2194 # the dirstate content anyway, no need to put cache | 2188 # the dirstate content anyway, no need to put cache |
2195 # information. | 2189 # information. |
2196 getfiledata = None | 2190 getfiledata = None |
2197 else: | 2191 else: |
2198 now_sec = now[0] | 2192 now_sec = now[0] |
2199 for f, m in pycompat.iteritems(getfiledata): | 2193 for f, m in getfiledata.items(): |
2200 if m is not None and m[2][0] >= now_sec: | 2194 if m is not None and m[2][0] >= now_sec: |
2201 ambiguous_mtime[f] = (m[0], m[1], None) | 2195 ambiguous_mtime[f] = (m[0], m[1], None) |
2202 for f, m in pycompat.iteritems(ambiguous_mtime): | 2196 for f, m in ambiguous_mtime.items(): |
2203 getfiledata[f] = m | 2197 getfiledata[f] = m |
2204 | 2198 |
2205 repo.setparents(fp1, fp2) | 2199 repo.setparents(fp1, fp2) |
2206 mergestatemod.recordupdates( | 2200 mergestatemod.recordupdates( |
2207 repo, mresult.actionsdict, branchmerge, getfiledata | 2201 repo, mresult.actionsdict, branchmerge, getfiledata |