mercurial/merge.py
changeset 28634 3ceac01bc29f
parent 28267 5408e532e50a
child 29099 bdc86e178255
equal deleted inserted replaced
28633:e35d7f131483 28634:3ceac01bc29f
    66     m: the external merge driver defined for this merge plus its run state
    66     m: the external merge driver defined for this merge plus its run state
    67        (experimental)
    67        (experimental)
    68     f: a (filename, dictonary) tuple of optional values for a given file
    68     f: a (filename, dictonary) tuple of optional values for a given file
    69     X: unsupported mandatory record type (used in tests)
    69     X: unsupported mandatory record type (used in tests)
    70     x: unsupported advisory record type (used in tests)
    70     x: unsupported advisory record type (used in tests)
       
    71     l: the labels for the parts of the merge.
    71 
    72 
    72     Merge driver run states (experimental):
    73     Merge driver run states (experimental):
    73     u: driver-resolved files unmarked -- needs to be run next time we're about
    74     u: driver-resolved files unmarked -- needs to be run next time we're about
    74        to resolve or commit
    75        to resolve or commit
    75     m: driver-resolved files marked -- only needs to be run before commit
    76     m: driver-resolved files marked -- only needs to be run before commit
    78     '''
    79     '''
    79     statepathv1 = 'merge/state'
    80     statepathv1 = 'merge/state'
    80     statepathv2 = 'merge/state2'
    81     statepathv2 = 'merge/state2'
    81 
    82 
    82     @staticmethod
    83     @staticmethod
    83     def clean(repo, node=None, other=None):
    84     def clean(repo, node=None, other=None, labels=None):
    84         """Initialize a brand new merge state, removing any existing state on
    85         """Initialize a brand new merge state, removing any existing state on
    85         disk."""
    86         disk."""
    86         ms = mergestate(repo)
    87         ms = mergestate(repo)
    87         ms.reset(node, other)
    88         ms.reset(node, other, labels)
    88         return ms
    89         return ms
    89 
    90 
    90     @staticmethod
    91     @staticmethod
    91     def read(repo):
    92     def read(repo):
    92         """Initialize the merge state, reading it from disk."""
    93         """Initialize the merge state, reading it from disk."""
    98         """Initialize the merge state.
    99         """Initialize the merge state.
    99 
   100 
   100         Do not use this directly! Instead call read() or clean()."""
   101         Do not use this directly! Instead call read() or clean()."""
   101         self._repo = repo
   102         self._repo = repo
   102         self._dirty = False
   103         self._dirty = False
   103 
   104         self._labels = None
   104     def reset(self, node=None, other=None):
   105 
       
   106     def reset(self, node=None, other=None, labels=None):
   105         self._state = {}
   107         self._state = {}
   106         self._stateextras = {}
   108         self._stateextras = {}
   107         self._local = None
   109         self._local = None
   108         self._other = None
   110         self._other = None
       
   111         self._labels = labels
   109         for var in ('localctx', 'otherctx'):
   112         for var in ('localctx', 'otherctx'):
   110             if var in vars(self):
   113             if var in vars(self):
   111                 delattr(self, var)
   114                 delattr(self, var)
   112         if node:
   115         if node:
   113             self._local = node
   116             self._local = node
   163                 while i < len(extraparts):
   166                 while i < len(extraparts):
   164                     extras[extraparts[i]] = extraparts[i + 1]
   167                     extras[extraparts[i]] = extraparts[i + 1]
   165                     i += 2
   168                     i += 2
   166 
   169 
   167                 self._stateextras[filename] = extras
   170                 self._stateextras[filename] = extras
       
   171             elif rtype == 'l':
       
   172                 labels = record.split('\0', 2)
       
   173                 self._labels = [l for l in labels if len(l) > 0]
   168             elif not rtype.islower():
   174             elif not rtype.islower():
   169                 unsupported.add(rtype)
   175                 unsupported.add(rtype)
   170         self._results = {}
   176         self._results = {}
   171         self._dirty = False
   177         self._dirty = False
   172 
   178 
   351                 records.append(('F', '\0'.join([d] + v)))
   357                 records.append(('F', '\0'.join([d] + v)))
   352         for filename, extras in sorted(self._stateextras.iteritems()):
   358         for filename, extras in sorted(self._stateextras.iteritems()):
   353             rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
   359             rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
   354                                   extras.iteritems())
   360                                   extras.iteritems())
   355             records.append(('f', '%s\0%s' % (filename, rawextras)))
   361             records.append(('f', '%s\0%s' % (filename, rawextras)))
       
   362         if self._labels is not None:
       
   363             labels = '\0'.join(self._labels)
       
   364             records.append(('l', labels))
   356         return records
   365         return records
   357 
   366 
   358     def _writerecords(self, records):
   367     def _writerecords(self, records):
   359         """Write current state on disk (both v1 and v2)"""
   368         """Write current state on disk (both v1 and v2)"""
   360         self._writerecordsv1(records)
   369         self._writerecordsv1(records)
   442                 yield f
   451                 yield f
   443 
   452 
   444     def extras(self, filename):
   453     def extras(self, filename):
   445         return self._stateextras.setdefault(filename, {})
   454         return self._stateextras.setdefault(filename, {})
   446 
   455 
   447     def _resolve(self, preresolve, dfile, wctx, labels=None):
   456     def _resolve(self, preresolve, dfile, wctx):
   448         """rerun merge process for file path `dfile`"""
   457         """rerun merge process for file path `dfile`"""
   449         if self[dfile] in 'rd':
   458         if self[dfile] in 'rd':
   450             return True, 0
   459             return True, 0
   451         stateentry = self._state[dfile]
   460         stateentry = self._state[dfile]
   452         state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
   461         state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
   479                 f.close()
   488                 f.close()
   480             else:
   489             else:
   481                 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
   490                 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
   482             complete, r, deleted = filemerge.premerge(self._repo, self._local,
   491             complete, r, deleted = filemerge.premerge(self._repo, self._local,
   483                                                       lfile, fcd, fco, fca,
   492                                                       lfile, fcd, fco, fca,
   484                                                       labels=labels)
   493                                                       labels=self._labels)
   485         else:
   494         else:
   486             complete, r, deleted = filemerge.filemerge(self._repo, self._local,
   495             complete, r, deleted = filemerge.filemerge(self._repo, self._local,
   487                                                        lfile, fcd, fco, fca,
   496                                                        lfile, fcd, fco, fca,
   488                                                        labels=labels)
   497                                                        labels=self._labels)
   489         if r is None:
   498         if r is None:
   490             # no real conflict
   499             # no real conflict
   491             del self._state[dfile]
   500             del self._state[dfile]
   492             self._stateextras.pop(dfile, None)
   501             self._stateextras.pop(dfile, None)
   493             self._dirty = True
   502             self._dirty = True
   521         if hexnode == nullhex:
   530         if hexnode == nullhex:
   522             return filemerge.absentfilectx(ctx, f)
   531             return filemerge.absentfilectx(ctx, f)
   523         else:
   532         else:
   524             return ctx[f]
   533             return ctx[f]
   525 
   534 
   526     def preresolve(self, dfile, wctx, labels=None):
   535     def preresolve(self, dfile, wctx):
   527         """run premerge process for dfile
   536         """run premerge process for dfile
   528 
   537 
   529         Returns whether the merge is complete, and the exit code."""
   538         Returns whether the merge is complete, and the exit code."""
   530         return self._resolve(True, dfile, wctx, labels=labels)
   539         return self._resolve(True, dfile, wctx)
   531 
   540 
   532     def resolve(self, dfile, wctx, labels=None):
   541     def resolve(self, dfile, wctx):
   533         """run merge process (assuming premerge was run) for dfile
   542         """run merge process (assuming premerge was run) for dfile
   534 
   543 
   535         Returns the exit code of the merge."""
   544         Returns the exit code of the merge."""
   536         return self._resolve(False, dfile, wctx, labels=labels)[1]
   545         return self._resolve(False, dfile, wctx)[1]
   537 
   546 
   538     def counts(self):
   547     def counts(self):
   539         """return counts for updated, merged and removed files in this
   548         """return counts for updated, merged and removed files in this
   540         session"""
   549         session"""
   541         updated, merged, removed = 0, 0, 0
   550         updated, merged, removed = 0, 0, 0
  1092     Return a tuple of counts (updated, merged, removed, unresolved) that
  1101     Return a tuple of counts (updated, merged, removed, unresolved) that
  1093     describes how many files were affected by the update.
  1102     describes how many files were affected by the update.
  1094     """
  1103     """
  1095 
  1104 
  1096     updated, merged, removed = 0, 0, 0
  1105     updated, merged, removed = 0, 0, 0
  1097     ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
  1106     ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
  1098     moves = []
  1107     moves = []
  1099     for m, l in actions.items():
  1108     for m, l in actions.items():
  1100         l.sort()
  1109         l.sort()
  1101 
  1110 
  1102     # 'cd' and 'dc' actions are treated like other merge conflicts
  1111     # 'cd' and 'dc' actions are treated like other merge conflicts
  1245         if f == '.hgsubstate': # subrepo states need updating
  1254         if f == '.hgsubstate': # subrepo states need updating
  1246             subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
  1255             subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
  1247                              overwrite)
  1256                              overwrite)
  1248             continue
  1257             continue
  1249         audit(f)
  1258         audit(f)
  1250         complete, r = ms.preresolve(f, wctx, labels=labels)
  1259         complete, r = ms.preresolve(f, wctx)
  1251         if not complete:
  1260         if not complete:
  1252             numupdates += 1
  1261             numupdates += 1
  1253             tocomplete.append((f, args, msg))
  1262             tocomplete.append((f, args, msg))
  1254 
  1263 
  1255     # merge
  1264     # merge
  1256     for f, args, msg in tocomplete:
  1265     for f, args, msg in tocomplete:
  1257         repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
  1266         repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
  1258         z += 1
  1267         z += 1
  1259         progress(_updating, z, item=f, total=numupdates, unit=_files)
  1268         progress(_updating, z, item=f, total=numupdates, unit=_files)
  1260         ms.resolve(f, wctx, labels=labels)
  1269         ms.resolve(f, wctx)
  1261 
  1270 
  1262     ms.commit()
  1271     ms.commit()
  1263 
  1272 
  1264     unresolved = ms.unresolvedcount()
  1273     unresolved = ms.unresolvedcount()
  1265 
  1274