mercurial/merge.py
changeset 45942 89a2afe31e82
parent 45940 10dbc80d4be7
child 46361 dfca84970da8
child 46692 39f23d20ea47
equal deleted inserted replaced
45941:346af7687c6f 45942:89a2afe31e82
   215                 abortconflicts.add(f)
   215                 abortconflicts.add(f)
   216             else:
   216             else:
   217                 if config == b'warn':
   217                 if config == b'warn':
   218                     warnconflicts.add(f)
   218                     warnconflicts.add(f)
   219                 mresult.addfile(
   219                 mresult.addfile(
   220                     f, mergestatemod.ACTION_GET, (fl2, True), b'remote created',
   220                     f,
       
   221                     mergestatemod.ACTION_GET,
       
   222                     (fl2, True),
       
   223                     b'remote created',
   221                 )
   224                 )
   222 
   225 
   223     for f in sorted(abortconflicts):
   226     for f in sorted(abortconflicts):
   224         warn = repo.ui.warn
   227         warn = repo.ui.warn
   225         if f in pathconflicts:
   228         if f in pathconflicts:
   279 
   282 
   280     if not branchmerge:
   283     if not branchmerge:
   281         for f in wctx.removed():
   284         for f in wctx.removed():
   282             if f not in mctx:
   285             if f not in mctx:
   283                 mresult.addfile(
   286                 mresult.addfile(
   284                     f, mergestatemod.ACTION_FORGET, None, b"forget removed",
   287                     f,
       
   288                     mergestatemod.ACTION_FORGET,
       
   289                     None,
       
   290                     b"forget removed",
   285                 )
   291                 )
   286 
   292 
   287 
   293 
   288 def _checkcollision(repo, wmf, mresult):
   294 def _checkcollision(repo, wmf, mresult):
   289     """
   295     """
   542                 _(b'conflict in file \'%s\' is outside narrow clone') % f
   548                 _(b'conflict in file \'%s\' is outside narrow clone') % f
   543             )
   549             )
   544 
   550 
   545 
   551 
   546 class mergeresult(object):
   552 class mergeresult(object):
   547     '''An object representing result of merging manifests.
   553     """An object representing result of merging manifests.
   548 
   554 
   549     It has information about what actions need to be performed on dirstate
   555     It has information about what actions need to be performed on dirstate
   550     mapping of divergent renames and other such cases.'''
   556     mapping of divergent renames and other such cases."""
   551 
   557 
   552     def __init__(self):
   558     def __init__(self):
   553         """
   559         """
   554         filemapping: dict of filename as keys and action related info as values
   560         filemapping: dict of filename as keys and action related info as values
   555         diverge: mapping of source name -> list of dest name for
   561         diverge: mapping of source name -> list of dest name for
   570     def updatevalues(self, diverge, renamedelete):
   576     def updatevalues(self, diverge, renamedelete):
   571         self._diverge = diverge
   577         self._diverge = diverge
   572         self._renamedelete = renamedelete
   578         self._renamedelete = renamedelete
   573 
   579 
   574     def addfile(self, filename, action, data, message):
   580     def addfile(self, filename, action, data, message):
   575         """ adds a new file to the mergeresult object
   581         """adds a new file to the mergeresult object
   576 
   582 
   577         filename: file which we are adding
   583         filename: file which we are adding
   578         action: one of mergestatemod.ACTION_*
   584         action: one of mergestatemod.ACTION_*
   579         data: a tuple of information like fctx and ctx related to this merge
   585         data: a tuple of information like fctx and ctx related to this merge
   580         message: a message about the merge
   586         message: a message about the merge
   587 
   593 
   588         self._filemapping[filename] = (action, data, message)
   594         self._filemapping[filename] = (action, data, message)
   589         self._actionmapping[action][filename] = (data, message)
   595         self._actionmapping[action][filename] = (data, message)
   590 
   596 
   591     def getfile(self, filename, default_return=None):
   597     def getfile(self, filename, default_return=None):
   592         """ returns (action, args, msg) about this file
   598         """returns (action, args, msg) about this file
   593 
   599 
   594         returns default_return if the file is not present """
   600         returns default_return if the file is not present"""
   595         if filename in self._filemapping:
   601         if filename in self._filemapping:
   596             return self._filemapping[filename]
   602             return self._filemapping[filename]
   597         return default_return
   603         return default_return
   598 
   604 
   599     def files(self, actions=None):
   605     def files(self, actions=None):
   600         """ returns files on which provided action needs to perfromed
   606         """returns files on which provided action needs to perfromed
   601 
   607 
   602         If actions is None, all files are returned
   608         If actions is None, all files are returned
   603         """
   609         """
   604         # TODO: think whether we should return renamedelete and
   610         # TODO: think whether we should return renamedelete and
   605         # diverge filenames also
   611         # diverge filenames also
   611             for a in actions:
   617             for a in actions:
   612                 for f in self._actionmapping[a]:
   618                 for f in self._actionmapping[a]:
   613                     yield f
   619                     yield f
   614 
   620 
   615     def removefile(self, filename):
   621     def removefile(self, filename):
   616         """ removes a file from the mergeresult object as the file might
   622         """removes a file from the mergeresult object as the file might
   617         not merging anymore """
   623         not merging anymore"""
   618         action, data, message = self._filemapping[filename]
   624         action, data, message = self._filemapping[filename]
   619         del self._filemapping[filename]
   625         del self._filemapping[filename]
   620         del self._actionmapping[action][filename]
   626         del self._actionmapping[action][filename]
   621 
   627 
   622     def getactions(self, actions, sort=False):
   628     def getactions(self, actions, sort=False):
   623         """ get list of files which are marked with these actions
   629         """get list of files which are marked with these actions
   624         if sort is true, files for each action is sorted and then added
   630         if sort is true, files for each action is sorted and then added
   625 
   631 
   626         Returns a list of tuple of form (filename, data, message)
   632         Returns a list of tuple of form (filename, data, message)
   627         """
   633         """
   628         for a in actions:
   634         for a in actions:
   635                     self._actionmapping[a]
   641                     self._actionmapping[a]
   636                 ):
   642                 ):
   637                     yield f, args, msg
   643                     yield f, args, msg
   638 
   644 
   639     def len(self, actions=None):
   645     def len(self, actions=None):
   640         """ returns number of files which needs actions
   646         """returns number of files which needs actions
   641 
   647 
   642         if actions is passed, total of number of files in that action
   648         if actions is passed, total of number of files in that action
   643         only is returned """
   649         only is returned"""
   644 
   650 
   645         if actions is None:
   651         if actions is None:
   646             return len(self._filemapping)
   652             return len(self._filemapping)
   647 
   653 
   648         return sum(len(self._actionmapping[a]) for a in actions)
   654         return sum(len(self._actionmapping[a]) for a in actions)
   654         else:
   660         else:
   655             for key, val in pycompat.iteritems(self._filemapping):
   661             for key, val in pycompat.iteritems(self._filemapping):
   656                 yield key, val
   662                 yield key, val
   657 
   663 
   658     def addcommitinfo(self, filename, key, value):
   664     def addcommitinfo(self, filename, key, value):
   659         """ adds key-value information about filename which will be required
   665         """adds key-value information about filename which will be required
   660         while committing this merge """
   666         while committing this merge"""
   661         self._commitinfo[filename][key] = value
   667         self._commitinfo[filename][key] = value
   662 
   668 
   663     @property
   669     @property
   664     def diverge(self):
   670     def diverge(self):
   665         return self._diverge
   671         return self._diverge
   672     def commitinfo(self):
   678     def commitinfo(self):
   673         return self._commitinfo
   679         return self._commitinfo
   674 
   680 
   675     @property
   681     @property
   676     def actionsdict(self):
   682     def actionsdict(self):
   677         """ returns a dictionary of actions to be perfomed with action as key
   683         """returns a dictionary of actions to be perfomed with action as key
   678         and a list of files and related arguments as values """
   684         and a list of files and related arguments as values"""
   679         res = collections.defaultdict(list)
   685         res = collections.defaultdict(list)
   680         for a, d in pycompat.iteritems(self._actionmapping):
   686         for a, d in pycompat.iteritems(self._actionmapping):
   681             for f, (args, msg) in pycompat.iteritems(d):
   687             for f, (args, msg) in pycompat.iteritems(d):
   682                 res[a].append((f, args, msg))
   688                 res[a].append((f, args, msg))
   683         return res
   689         return res
   687         self._actionmapping = collections.defaultdict(dict)
   693         self._actionmapping = collections.defaultdict(dict)
   688         for f, (act, data, msg) in pycompat.iteritems(self._filemapping):
   694         for f, (act, data, msg) in pycompat.iteritems(self._filemapping):
   689             self._actionmapping[act][f] = data, msg
   695             self._actionmapping[act][f] = data, msg
   690 
   696 
   691     def hasconflicts(self):
   697     def hasconflicts(self):
   692         """ tells whether this merge resulted in some actions which can
   698         """tells whether this merge resulted in some actions which can
   693         result in conflicts or not """
   699         result in conflicts or not"""
   694         for a in self._actionmapping.keys():
   700         for a in self._actionmapping.keys():
   695             if (
   701             if (
   696                 a
   702                 a
   697                 not in (
   703                 not in (
   698                     mergestatemod.ACTION_GET,
   704                     mergestatemod.ACTION_GET,
   837                 a = ma[f]
   843                 a = ma[f]
   838                 fla = ma.flags(f)
   844                 fla = ma.flags(f)
   839                 nol = b'l' not in fl1 + fl2 + fla
   845                 nol = b'l' not in fl1 + fl2 + fla
   840                 if n2 == a and fl2 == fla:
   846                 if n2 == a and fl2 == fla:
   841                     mresult.addfile(
   847                     mresult.addfile(
   842                         f, mergestatemod.ACTION_KEEP, (), b'remote unchanged',
   848                         f,
       
   849                         mergestatemod.ACTION_KEEP,
       
   850                         (),
       
   851                         b'remote unchanged',
   843                     )
   852                     )
   844                 elif n1 == a and fl1 == fla:  # local unchanged - use remote
   853                 elif n1 == a and fl1 == fla:  # local unchanged - use remote
   845                     if n1 == n2:  # optimization: keep local content
   854                     if n1 == n2:  # optimization: keep local content
   846                         mresult.addfile(
   855                         mresult.addfile(
   847                             f,
   856                             f,
   934                             )
   943                             )
   935                 elif n1 == addednodeid:
   944                 elif n1 == addednodeid:
   936                     # This file was locally added. We should forget it instead of
   945                     # This file was locally added. We should forget it instead of
   937                     # deleting it.
   946                     # deleting it.
   938                     mresult.addfile(
   947                     mresult.addfile(
   939                         f, mergestatemod.ACTION_FORGET, None, b'remote deleted',
   948                         f,
       
   949                         mergestatemod.ACTION_FORGET,
       
   950                         None,
       
   951                         b'remote deleted',
   940                     )
   952                     )
   941                 else:
   953                 else:
   942                     mresult.addfile(
   954                     mresult.addfile(
   943                         f, mergestatemod.ACTION_REMOVE, None, b'other deleted',
   955                         f,
       
   956                         mergestatemod.ACTION_REMOVE,
       
   957                         None,
       
   958                         b'other deleted',
   944                     )
   959                     )
   945                     if branchmerge:
   960                     if branchmerge:
   946                         # the file must be absent after merging,
   961                         # the file must be absent after merging,
   947                         # howeber the user might make
   962                         # howeber the user might make
   948                         # the file reappear using revert and if they does,
   963                         # the file reappear using revert and if they does,
  1084     return mresult
  1099     return mresult
  1085 
  1100 
  1086 
  1101 
  1087 def _resolvetrivial(repo, wctx, mctx, ancestor, mresult):
  1102 def _resolvetrivial(repo, wctx, mctx, ancestor, mresult):
  1088     """Resolves false conflicts where the nodeid changed but the content
  1103     """Resolves false conflicts where the nodeid changed but the content
  1089        remained the same."""
  1104     remained the same."""
  1090     # We force a copy of actions.items() because we're going to mutate
  1105     # We force a copy of actions.items() because we're going to mutate
  1091     # actions as we resolve trivial conflicts.
  1106     # actions as we resolve trivial conflicts.
  1092     for f in list(mresult.files((mergestatemod.ACTION_CHANGED_DELETED,))):
  1107     for f in list(mresult.files((mergestatemod.ACTION_CHANGED_DELETED,))):
  1093         if f in ancestor and not wctx[f].cmp(ancestor[f]):
  1108         if f in ancestor and not wctx[f].cmp(ancestor[f]):
  1094             # local did change but ended up with same content
  1109             # local did change but ended up with same content
  1421     )
  1436     )
  1422 
  1437 
  1423     prefetch = scmutil.prefetchfiles
  1438     prefetch = scmutil.prefetchfiles
  1424     matchfiles = scmutil.matchfiles
  1439     matchfiles = scmutil.matchfiles
  1425     prefetch(
  1440     prefetch(
  1426         repo, [(ctx.rev(), matchfiles(repo, files),)],
  1441         repo,
       
  1442         [
       
  1443             (
       
  1444                 ctx.rev(),
       
  1445                 matchfiles(repo, files),
       
  1446             )
       
  1447         ],
  1427     )
  1448     )
  1428 
  1449 
  1429 
  1450 
  1430 @attr.s(frozen=True)
  1451 @attr.s(frozen=True)
  1431 class updateresult(object):
  1452 class updateresult(object):
  1442             or self.unresolvedcount
  1463             or self.unresolvedcount
  1443         )
  1464         )
  1444 
  1465 
  1445 
  1466 
  1446 def applyupdates(
  1467 def applyupdates(
  1447     repo, mresult, wctx, mctx, overwrite, wantfiledata, labels=None,
  1468     repo,
       
  1469     mresult,
       
  1470     wctx,
       
  1471     mctx,
       
  1472     overwrite,
       
  1473     wantfiledata,
       
  1474     labels=None,
  1448 ):
  1475 ):
  1449     """apply the merge action list to the working directory
  1476     """apply the merge action list to the working directory
  1450 
  1477 
  1451     mresult is a mergeresult object representing result of the merge
  1478     mresult is a mergeresult object representing result of the merge
  1452     wctx is the working copy context
  1479     wctx is the working copy context
  1732     from . import dirstate
  1759     from . import dirstate
  1733 
  1760 
  1734     if dirstate.rustmod is not None:
  1761     if dirstate.rustmod is not None:
  1735         # When using rust status, fsmonitor becomes necessary at higher sizes
  1762         # When using rust status, fsmonitor becomes necessary at higher sizes
  1736         fsmonitorthreshold = repo.ui.configint(
  1763         fsmonitorthreshold = repo.ui.configint(
  1737             b'fsmonitor', b'warn_update_file_count_rust',
  1764             b'fsmonitor',
       
  1765             b'warn_update_file_count_rust',
  1738         )
  1766         )
  1739 
  1767 
  1740     try:
  1768     try:
  1741         # avoid cycle: extensions -> cmdutil -> merge
  1769         # avoid cycle: extensions -> cmdutil -> merge
  1742         from . import extensions
  1770         from . import extensions
  1999                     )
  2027                     )
  2000                     % prompts,
  2028                     % prompts,
  2001                     0,
  2029                     0,
  2002                 ):
  2030                 ):
  2003                     mresult.addfile(
  2031                     mresult.addfile(
  2004                         f, mergestatemod.ACTION_REMOVE, None, b'prompt delete',
  2032                         f,
       
  2033                         mergestatemod.ACTION_REMOVE,
       
  2034                         None,
       
  2035                         b'prompt delete',
  2005                     )
  2036                     )
  2006                 elif f in p1:
  2037                 elif f in p1:
  2007                     mresult.addfile(
  2038                     mresult.addfile(
  2008                         f,
  2039                         f,
  2009                         mergestatemod.ACTION_ADD_MODIFIED,
  2040                         mergestatemod.ACTION_ADD_MODIFIED,
  2010                         None,
  2041                         None,
  2011                         b'prompt keep',
  2042                         b'prompt keep',
  2012                     )
  2043                     )
  2013                 else:
  2044                 else:
  2014                     mresult.addfile(
  2045                     mresult.addfile(
  2015                         f, mergestatemod.ACTION_ADD, None, b'prompt keep',
  2046                         f,
       
  2047                         mergestatemod.ACTION_ADD,
       
  2048                         None,
       
  2049                         b'prompt keep',
  2016                     )
  2050                     )
  2017             elif m == mergestatemod.ACTION_DELETED_CHANGED:
  2051             elif m == mergestatemod.ACTION_DELETED_CHANGED:
  2018                 f1, f2, fa, move, anc = args
  2052                 f1, f2, fa, move, anc = args
  2019                 flags = p2[f2].flags()
  2053                 flags = p2[f2].flags()
  2020                 if (
  2054                 if (
  2087             repo, mresult.len((mergestatemod.ACTION_GET,)), p1.node()
  2121             repo, mresult.len((mergestatemod.ACTION_GET,)), p1.node()
  2088         )
  2122         )
  2089 
  2123 
  2090         wantfiledata = updatedirstate and not branchmerge
  2124         wantfiledata = updatedirstate and not branchmerge
  2091         stats, getfiledata = applyupdates(
  2125         stats, getfiledata = applyupdates(
  2092             repo, mresult, wc, p2, overwrite, wantfiledata, labels=labels,
  2126             repo,
       
  2127             mresult,
       
  2128             wc,
       
  2129             p2,
       
  2130             overwrite,
       
  2131             wantfiledata,
       
  2132             labels=labels,
  2093         )
  2133         )
  2094 
  2134 
  2095         if updatedirstate:
  2135         if updatedirstate:
  2096             with repo.dirstate.parentchange():
  2136             with repo.dirstate.parentchange():
  2097                 repo.setparents(fp1, fp2)
  2137                 repo.setparents(fp1, fp2)