mercurial/merge.py
changeset 34555 989e884d1be9
parent 34553 0217d66846f7
child 34560 1248aa48cac9
equal deleted inserted replaced
34554:6f11a74d489f 34555:989e884d1be9
   848 def driverconclude(repo, ms, wctx, labels=None):
   848 def driverconclude(repo, ms, wctx, labels=None):
   849     """run the conclude step of the merge driver, if any
   849     """run the conclude step of the merge driver, if any
   850 
   850 
   851     This is currently not implemented -- it's an extension point."""
   851     This is currently not implemented -- it's an extension point."""
   852     return True
   852     return True
       
   853 
       
   854 def _filesindirs(repo, manifest, dirs):
       
   855     """
       
   856     Generator that yields pairs of all the files in the manifest that are found
       
   857     inside the directories listed in dirs, and which directory they are found
       
   858     in.
       
   859     """
       
   860     for f in manifest:
       
   861         for p in util.finddirs(f):
       
   862             if p in dirs:
       
   863                 yield f, p
       
   864                 break
       
   865 
       
   866 def checkpathconflicts(repo, wctx, mctx, actions):
       
   867     """
       
   868     Check if any actions introduce path conflicts in the repository, updating
       
   869     actions to record or handle the path conflict accordingly.
       
   870     """
       
   871     mf = wctx.manifest()
       
   872 
       
   873     # The set of local files that conflict with a remote directory.
       
   874     localconflicts = set()
       
   875 
       
   876     # The set of directories that conflict with a remote file, and so may cause
       
   877     # conflicts if they still contain any files after the merge.
       
   878     remoteconflicts = set()
       
   879 
       
   880     # The set of directories that appear as both a file and a directory in the
       
   881     # remote manifest.  These indicate an invalid remote manifest, which
       
   882     # can't be updated to cleanly.
       
   883     invalidconflicts = set()
       
   884 
       
   885     # The set of files deleted by all the actions.
       
   886     deletedfiles = set()
       
   887 
       
   888     for f, (m, args, msg) in actions.items():
       
   889         if m in ('c', 'dc', 'm', 'cm'):
       
   890             # This action may create a new local file.
       
   891             if mf.hasdir(f):
       
   892                 # The file aliases a local directory.  This might be ok if all
       
   893                 # the files in the local directory are being deleted.  This
       
   894                 # will be checked once we know what all the deleted files are.
       
   895                 remoteconflicts.add(f)
       
   896             for p in util.finddirs(f):
       
   897                 if p in mf:
       
   898                     if p in mctx:
       
   899                         # The file is in a directory which aliases both a local
       
   900                         # and a remote file.  This is an internal inconsistency
       
   901                         # within the remote manifest.
       
   902                         invalidconflicts.add(p)
       
   903                     else:
       
   904                         # The file is in a directory which aliases a local file.
       
   905                         # We will need to rename the local file.
       
   906                         localconflicts.add(p)
       
   907                 if p in actions and actions[p][0] in ('c', 'dc', 'm', 'cm'):
       
   908                     # The file is in a directory which aliases a remote file.
       
   909                     # This is an internal inconsistency within the remote
       
   910                     # manifest.
       
   911                     invalidconflicts.add(p)
       
   912 
       
   913         # Track the names of all deleted files.
       
   914         if m == 'r':
       
   915             deletedfiles.add(f)
       
   916         if m == 'm':
       
   917             f1, f2, fa, move, anc = args
       
   918             if move:
       
   919                 deletedfiles.add(f1)
       
   920         if m == 'dm':
       
   921             f2, flags = args
       
   922             deletedfiles.add(f2)
       
   923 
       
   924     # Rename all local conflicting files that have not been deleted.
       
   925     for p in localconflicts:
       
   926         if p not in deletedfiles:
       
   927             ctxname = str(wctx).rstrip('+')
       
   928             pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
       
   929             actions[pnew] = ('pr', (p,), "local path conflict")
       
   930             actions[p] = ('p', (pnew, 'l'), "path conflict")
       
   931 
       
   932     if remoteconflicts:
       
   933         # Check if all files in the conflicting directories have been removed.
       
   934         ctxname = str(mctx).rstrip('+')
       
   935         for f, p in _filesindirs(repo, mf, remoteconflicts):
       
   936             if f not in deletedfiles:
       
   937                 m, args, msg = actions[p]
       
   938                 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
       
   939                 if m in ('dc', 'm'):
       
   940                     # Action was merge, just update target.
       
   941                     actions[pnew] = (m, args, msg)
       
   942                 else:
       
   943                     # Action was create, change to renamed get action.
       
   944                     fl = args[0]
       
   945                     actions[pnew] = ('dg', (p, fl), "remote path conflict")
       
   946                 actions[p] = ('p', (pnew, 'r'), "path conflict")
       
   947                 remoteconflicts.remove(p)
       
   948                 break
       
   949 
       
   950     if invalidconflicts:
       
   951         for p in invalidconflicts:
       
   952             repo.ui.warn(_("%s: is both a file and a directory\n") % p)
       
   953         raise error.Abort(_("destination manifest contains path conflicts"))
   853 
   954 
   854 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
   955 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
   855                   acceptremote, followcopies, forcefulldiff=False):
   956                   acceptremote, followcopies, forcefulldiff=False):
   856     """
   957     """
   857     Merge wctx and p2 with ancestor pa and generate merge action list
   958     Merge wctx and p2 with ancestor pa and generate merge action list
  1024                     actions[f] = ('c', (fl2,), "remote recreating")
  1125                     actions[f] = ('c', (fl2,), "remote recreating")
  1025                 else:
  1126                 else:
  1026                     actions[f] = ('dc', (None, f, f, False, pa.node()),
  1127                     actions[f] = ('dc', (None, f, f, False, pa.node()),
  1027                                   "prompt deleted/changed")
  1128                                   "prompt deleted/changed")
  1028 
  1129 
       
  1130     # If we are merging, look for path conflicts.
       
  1131     checkpathconflicts(repo, wctx, p2, actions)
       
  1132 
  1029     return actions, diverge, renamedelete
  1133     return actions, diverge, renamedelete
  1030 
  1134 
  1031 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
  1135 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
  1032     """Resolves false conflicts where the nodeid changed but the content
  1136     """Resolves false conflicts where the nodeid changed but the content
  1033        remained the same."""
  1137        remained the same."""