mercurial/mergestate.py
changeset 45526 32ce4cbaec4b
parent 45512 0e75c088f0dc
child 45530 6877b0ee5f9d
equal deleted inserted replaced
45525:2a68a5ec8dd0 45526:32ce4cbaec4b
    46 #####
    46 #####
    47 RECORD_LOCAL = b'L'
    47 RECORD_LOCAL = b'L'
    48 RECORD_OTHER = b'O'
    48 RECORD_OTHER = b'O'
    49 # record merge labels
    49 # record merge labels
    50 RECORD_LABELS = b'l'
    50 RECORD_LABELS = b'l'
    51 # store info about merge driver used and it's state
       
    52 RECORD_MERGE_DRIVER_STATE = b'm'
       
    53 
    51 
    54 #####
    52 #####
    55 # record extra information about files, with one entry containing info about one
    53 # record extra information about files, with one entry containing info about one
    56 # file. Hence, multiple of them can exists
    54 # file. Hence, multiple of them can exists
    57 #####
    55 #####
    63 # Each record of these has info about one file. Hence multiple of them can
    61 # Each record of these has info about one file. Hence multiple of them can
    64 # exists
    62 # exists
    65 #####
    63 #####
    66 RECORD_MERGED = b'F'
    64 RECORD_MERGED = b'F'
    67 RECORD_CHANGEDELETE_CONFLICT = b'C'
    65 RECORD_CHANGEDELETE_CONFLICT = b'C'
    68 RECORD_MERGE_DRIVER_MERGE = b'D'
       
    69 # the path was dir on one side of merge and file on another
    66 # the path was dir on one side of merge and file on another
    70 RECORD_PATH_CONFLICT = b'P'
    67 RECORD_PATH_CONFLICT = b'P'
    71 
    68 
    72 #####
    69 #####
    73 # possible state which a merge entry can have. These are stored inside top-level
    70 # possible state which a merge entry can have. These are stored inside top-level
    75 #####
    72 #####
    76 MERGE_RECORD_UNRESOLVED = b'u'
    73 MERGE_RECORD_UNRESOLVED = b'u'
    77 MERGE_RECORD_RESOLVED = b'r'
    74 MERGE_RECORD_RESOLVED = b'r'
    78 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
    75 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
    79 MERGE_RECORD_RESOLVED_PATH = b'pr'
    76 MERGE_RECORD_RESOLVED_PATH = b'pr'
    80 MERGE_RECORD_DRIVER_RESOLVED = b'd'
       
    81 # represents that the file was automatically merged in favor
    77 # represents that the file was automatically merged in favor
    82 # of other version. This info is used on commit.
    78 # of other version. This info is used on commit.
    83 # This is now deprecated and commit related information is now
    79 # This is now deprecated and commit related information is now
    84 # stored in RECORD_FILE_VALUES
    80 # stored in RECORD_FILE_VALUES
    85 MERGE_RECORD_MERGED_OTHER = b'o'
    81 MERGE_RECORD_MERGED_OTHER = b'o'
    89 # exists
    85 # exists
    90 #####
    86 #####
    91 RECORD_OVERRIDE = b't'
    87 RECORD_OVERRIDE = b't'
    92 
    88 
    93 #####
    89 #####
    94 # possible states which a merge driver can have. These are stored inside a
       
    95 # RECORD_MERGE_DRIVER_STATE entry
       
    96 #####
       
    97 MERGE_DRIVER_STATE_UNMARKED = b'u'
       
    98 MERGE_DRIVER_STATE_MARKED = b'm'
       
    99 MERGE_DRIVER_STATE_SUCCESS = b's'
       
   100 
       
   101 #####
       
   102 # legacy records which are no longer used but kept to prevent breaking BC
    90 # legacy records which are no longer used but kept to prevent breaking BC
   103 #####
    91 #####
   104 # This record was release in 5.4 and usage was removed in 5.5
    92 # This record was release in 5.4 and usage was removed in 5.5
   105 LEGACY_RECORD_RESOLVED_OTHER = b'R'
    93 LEGACY_RECORD_RESOLVED_OTHER = b'R'
       
    94 # This record was release in 3.7 and usage was removed in 5.6
       
    95 LEGACY_RECORD_DRIVER_RESOLVED = b'd'
       
    96 # This record was release in 3.7 and usage was removed in 5.6
       
    97 LEGACY_MERGE_DRIVER_STATE = b'm'
       
    98 # This record was release in 3.7 and usage was removed in 5.6
       
    99 LEGACY_MERGE_DRIVER_MERGE = b'D'
   106 
   100 
   107 
   101 
   108 ACTION_FORGET = b'f'
   102 ACTION_FORGET = b'f'
   109 ACTION_REMOVE = b'r'
   103 ACTION_REMOVE = b'r'
   110 ACTION_ADD = b'a'
   104 ACTION_ADD = b'a'
   145 
   139 
   146     L: the node of the "local" part of the merge (hexified version)
   140     L: the node of the "local" part of the merge (hexified version)
   147     O: the node of the "other" part of the merge (hexified version)
   141     O: the node of the "other" part of the merge (hexified version)
   148     F: a file to be merged entry
   142     F: a file to be merged entry
   149     C: a change/delete or delete/change conflict
   143     C: a change/delete or delete/change conflict
   150     D: a file that the external merge driver will merge internally
       
   151        (experimental)
       
   152     P: a path conflict (file vs directory)
   144     P: a path conflict (file vs directory)
   153     m: the external merge driver defined for this merge plus its run state
       
   154        (experimental)
       
   155     f: a (filename, dictionary) tuple of optional values for a given file
   145     f: a (filename, dictionary) tuple of optional values for a given file
   156     l: the labels for the parts of the merge.
   146     l: the labels for the parts of the merge.
   157 
       
   158     Merge driver run states (experimental):
       
   159     u: driver-resolved files unmarked -- needs to be run next time we're about
       
   160        to resolve or commit
       
   161     m: driver-resolved files marked -- only needs to be run before commit
       
   162     s: success/skipped -- does not need to be run any more
       
   163 
   147 
   164     Merge record states (stored in self._state, indexed by filename):
   148     Merge record states (stored in self._state, indexed by filename):
   165     u: unresolved conflict
   149     u: unresolved conflict
   166     r: resolved conflict
   150     r: resolved conflict
   167     pu: unresolved path conflict (file conflicts with directory)
   151     pu: unresolved path conflict (file conflicts with directory)
   168     pr: resolved path conflict
   152     pr: resolved path conflict
   169     d: driver-resolved conflict
       
   170 
   153 
   171     The resolve command transitions between 'u' and 'r' for conflicts and
   154     The resolve command transitions between 'u' and 'r' for conflicts and
   172     'pu' and 'pr' for path conflicts.
   155     'pu' and 'pr' for path conflicts.
   173     '''
   156     '''
   174 
   157 
   180         self._state = {}
   163         self._state = {}
   181         self._stateextras = collections.defaultdict(dict)
   164         self._stateextras = collections.defaultdict(dict)
   182         self._local = None
   165         self._local = None
   183         self._other = None
   166         self._other = None
   184         self._labels = None
   167         self._labels = None
   185         self._readmergedriver = None
       
   186         self._mdstate = MERGE_DRIVER_STATE_UNMARKED
       
   187         # contains a mapping of form:
   168         # contains a mapping of form:
   188         # {filename : (merge_return_value, action_to_be_performed}
   169         # {filename : (merge_return_value, action_to_be_performed}
   189         # these are results of re-running merge process
   170         # these are results of re-running merge process
   190         # this dict is used to perform actions on dirstate caused by re-running
   171         # this dict is used to perform actions on dirstate caused by re-running
   191         # the merge
   172         # the merge
   197 
   178 
   198     def start(self, node, other, labels=None):
   179     def start(self, node, other, labels=None):
   199         self._local = node
   180         self._local = node
   200         self._other = other
   181         self._other = other
   201         self._labels = labels
   182         self._labels = labels
   202         if self.mergedriver:
       
   203             self._mdstate = MERGE_DRIVER_STATE_SUCCESS
       
   204 
       
   205     @util.propertycache
       
   206     def mergedriver(self):
       
   207         # protect against the following:
       
   208         # - A configures a malicious merge driver in their hgrc, then
       
   209         #   pauses the merge
       
   210         # - A edits their hgrc to remove references to the merge driver
       
   211         # - A gives a copy of their entire repo, including .hg, to B
       
   212         # - B inspects .hgrc and finds it to be clean
       
   213         # - B then continues the merge and the malicious merge driver
       
   214         #  gets invoked
       
   215         configmergedriver = self._repo.ui.config(
       
   216             b'experimental', b'mergedriver'
       
   217         )
       
   218         if (
       
   219             self._readmergedriver is not None
       
   220             and self._readmergedriver != configmergedriver
       
   221         ):
       
   222             raise error.ConfigError(
       
   223                 _(b"merge driver changed since merge started"),
       
   224                 hint=_(b"revert merge driver change or abort merge"),
       
   225             )
       
   226 
       
   227         return configmergedriver
       
   228 
   183 
   229     @util.propertycache
   184     @util.propertycache
   230     def local(self):
   185     def local(self):
   231         if self._local is None:
   186         if self._local is None:
   232             msg = b"local accessed but self._local isn't set"
   187             msg = b"local accessed but self._local isn't set"
   328 
   283 
   329     def mark(self, dfile, state):
   284     def mark(self, dfile, state):
   330         self._state[dfile][0] = state
   285         self._state[dfile][0] = state
   331         self._dirty = True
   286         self._dirty = True
   332 
   287 
   333     def mdstate(self):
       
   334         return self._mdstate
       
   335 
       
   336     def unresolved(self):
   288     def unresolved(self):
   337         """Obtain the paths of unresolved files."""
   289         """Obtain the paths of unresolved files."""
   338 
   290 
   339         for f, entry in pycompat.iteritems(self._state):
   291         for f, entry in pycompat.iteritems(self._state):
   340             if entry[0] in (
   292             if entry[0] in (
   341                 MERGE_RECORD_UNRESOLVED,
   293                 MERGE_RECORD_UNRESOLVED,
   342                 MERGE_RECORD_UNRESOLVED_PATH,
   294                 MERGE_RECORD_UNRESOLVED_PATH,
   343             ):
   295             ):
   344                 yield f
   296                 yield f
   345 
   297 
   346     def driverresolved(self):
       
   347         """Obtain the paths of driver-resolved files."""
       
   348 
       
   349         for f, entry in self._state.items():
       
   350             if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
       
   351                 yield f
       
   352 
       
   353     def extras(self, filename):
   298     def extras(self, filename):
   354         return self._stateextras[filename]
   299         return self._stateextras[filename]
   355 
   300 
   356     def _resolve(self, preresolve, dfile, wctx):
   301     def _resolve(self, preresolve, dfile, wctx):
   357         """rerun merge process for file path `dfile`.
   302         """rerun merge process for file path `dfile`.
   358         Returns whether the merge was completed and the return value of merge
   303         Returns whether the merge was completed and the return value of merge
   359         obtained from filemerge._filemerge().
   304         obtained from filemerge._filemerge().
   360         """
   305         """
   361         if self[dfile] in (MERGE_RECORD_RESOLVED, MERGE_RECORD_DRIVER_RESOLVED):
   306         if self[dfile] in (
       
   307             MERGE_RECORD_RESOLVED,
       
   308             LEGACY_RECORD_DRIVER_RESOLVED,
       
   309         ):
   362             return True, 0
   310             return True, 0
   363         stateentry = self._state[dfile]
   311         stateentry = self._state[dfile]
   364         state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
   312         state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
   365         octx = self._repo[self._other]
   313         octx = self._repo[self._other]
   366         extras = self.extras(dfile)
   314         extras = self.extras(dfile)
   488         for f, (r, action) in pycompat.iteritems(self._results):
   436         for f, (r, action) in pycompat.iteritems(self._results):
   489             if action is not None:
   437             if action is not None:
   490                 actions[action].append((f, None, b"merge result"))
   438                 actions[action].append((f, None, b"merge result"))
   491         return actions
   439         return actions
   492 
   440 
   493     def queueremove(self, f):
       
   494         """queues a file to be removed from the dirstate
       
   495 
       
   496         Meant for use by custom merge drivers."""
       
   497         self._results[f] = 0, ACTION_REMOVE
       
   498 
       
   499     def queueadd(self, f):
       
   500         """queues a file to be added to the dirstate
       
   501 
       
   502         Meant for use by custom merge drivers."""
       
   503         self._results[f] = 0, ACTION_ADD
       
   504 
       
   505     def queueget(self, f):
       
   506         """queues a file to be marked modified in the dirstate
       
   507 
       
   508         Meant for use by custom merge drivers."""
       
   509         self._results[f] = 0, ACTION_GET
       
   510 
       
   511 
   441 
   512 class mergestate(_mergestate_base):
   442 class mergestate(_mergestate_base):
   513 
   443 
   514     statepathv1 = b'merge/state'
   444     statepathv1 = b'merge/state'
   515     statepathv2 = b'merge/state2'
   445     statepathv2 = b'merge/state2'
   533         """Analyse each record content to restore a serialized state from disk
   463         """Analyse each record content to restore a serialized state from disk
   534 
   464 
   535         This function process "record" entry produced by the de-serialization
   465         This function process "record" entry produced by the de-serialization
   536         of on disk file.
   466         of on disk file.
   537         """
   467         """
   538         self._mdstate = MERGE_DRIVER_STATE_SUCCESS
       
   539         unsupported = set()
   468         unsupported = set()
   540         records = self._readrecords()
   469         records = self._readrecords()
   541         for rtype, record in records:
   470         for rtype, record in records:
   542             if rtype == RECORD_LOCAL:
   471             if rtype == RECORD_LOCAL:
   543                 self._local = bin(record)
   472                 self._local = bin(record)
   544             elif rtype == RECORD_OTHER:
   473             elif rtype == RECORD_OTHER:
   545                 self._other = bin(record)
   474                 self._other = bin(record)
   546             elif rtype == RECORD_MERGE_DRIVER_STATE:
   475             elif rtype == LEGACY_MERGE_DRIVER_STATE:
   547                 bits = record.split(b'\0', 1)
   476                 pass
   548                 mdstate = bits[1]
       
   549                 if len(mdstate) != 1 or mdstate not in (
       
   550                     MERGE_DRIVER_STATE_UNMARKED,
       
   551                     MERGE_DRIVER_STATE_MARKED,
       
   552                     MERGE_DRIVER_STATE_SUCCESS,
       
   553                 ):
       
   554                     # the merge driver should be idempotent, so just rerun it
       
   555                     mdstate = MERGE_DRIVER_STATE_UNMARKED
       
   556 
       
   557                 self._readmergedriver = bits[0]
       
   558                 self._mdstate = mdstate
       
   559             elif rtype in (
   477             elif rtype in (
   560                 RECORD_MERGED,
   478                 RECORD_MERGED,
   561                 RECORD_CHANGEDELETE_CONFLICT,
   479                 RECORD_CHANGEDELETE_CONFLICT,
   562                 RECORD_PATH_CONFLICT,
   480                 RECORD_PATH_CONFLICT,
   563                 RECORD_MERGE_DRIVER_MERGE,
   481                 LEGACY_MERGE_DRIVER_MERGE,
   564                 LEGACY_RECORD_RESOLVED_OTHER,
   482                 LEGACY_RECORD_RESOLVED_OTHER,
   565             ):
   483             ):
   566                 bits = record.split(b'\0')
   484                 bits = record.split(b'\0')
   567                 # merge entry type MERGE_RECORD_MERGED_OTHER is deprecated
   485                 # merge entry type MERGE_RECORD_MERGED_OTHER is deprecated
   568                 # and we now store related information in _stateextras, so
   486                 # and we now store related information in _stateextras, so
   708 
   626 
   709     def _makerecords(self):
   627     def _makerecords(self):
   710         records = []
   628         records = []
   711         records.append((RECORD_LOCAL, hex(self._local)))
   629         records.append((RECORD_LOCAL, hex(self._local)))
   712         records.append((RECORD_OTHER, hex(self._other)))
   630         records.append((RECORD_OTHER, hex(self._other)))
   713         if self.mergedriver:
       
   714             records.append(
       
   715                 (
       
   716                     RECORD_MERGE_DRIVER_STATE,
       
   717                     b'\0'.join([self.mergedriver, self._mdstate]),
       
   718                 )
       
   719             )
       
   720         # Write out state items. In all cases, the value of the state map entry
   631         # Write out state items. In all cases, the value of the state map entry
   721         # is written as the contents of the record. The record type depends on
   632         # is written as the contents of the record. The record type depends on
   722         # the type of state that is stored, and capital-letter records are used
   633         # the type of state that is stored, and capital-letter records are used
   723         # to prevent older versions of Mercurial that do not support the feature
   634         # to prevent older versions of Mercurial that do not support the feature
   724         # from loading them.
   635         # from loading them.
   725         for filename, v in pycompat.iteritems(self._state):
   636         for filename, v in pycompat.iteritems(self._state):
   726             if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
   637             if v[0] in (
   727                 # Driver-resolved merge. These are stored in 'D' records.
       
   728                 records.append(
       
   729                     (RECORD_MERGE_DRIVER_MERGE, b'\0'.join([filename] + v))
       
   730                 )
       
   731             elif v[0] in (
       
   732                 MERGE_RECORD_UNRESOLVED_PATH,
   638                 MERGE_RECORD_UNRESOLVED_PATH,
   733                 MERGE_RECORD_RESOLVED_PATH,
   639                 MERGE_RECORD_RESOLVED_PATH,
   734             ):
   640             ):
   735                 # Path conflicts. These are stored in 'P' records.  The current
   641                 # Path conflicts. These are stored in 'P' records.  The current
   736                 # resolution state ('pu' or 'pr') is stored within the record.
   642                 # resolution state ('pu' or 'pr') is stored within the record.
   812         self._backups[localkey] = fctx.data()
   718         self._backups[localkey] = fctx.data()
   813 
   719 
   814     def _restore_backup(self, fctx, localkey, flags):
   720     def _restore_backup(self, fctx, localkey, flags):
   815         fctx.write(self._backups[localkey], flags)
   721         fctx.write(self._backups[localkey], flags)
   816 
   722 
   817     @util.propertycache
       
   818     def mergedriver(self):
       
   819         configmergedriver = self._repo.ui.config(
       
   820             b'experimental', b'mergedriver'
       
   821         )
       
   822         if configmergedriver:
       
   823             raise error.InMemoryMergeConflictsError(
       
   824                 b"in-memory merge does not support mergedriver"
       
   825             )
       
   826         return None
       
   827 
       
   828 
   723 
   829 def recordupdates(repo, actions, branchmerge, getfiledata):
   724 def recordupdates(repo, actions, branchmerge, getfiledata):
   830     """record merge actions to the dirstate"""
   725     """record merge actions to the dirstate"""
   831     # remove (must come first)
   726     # remove (must come first)
   832     for f, args, msg in actions.get(ACTION_REMOVE, []):
   727     for f, args, msg in actions.get(ACTION_REMOVE, []):