mercurial/merge.py
changeset 26649 f618b6aa8cdd
parent 26641 5c57d01fe64e
child 26650 6ff5534c8afc
equal deleted inserted replaced
26648:c347d532bb56 26649:f618b6aa8cdd
    59     Currently known record:
    59     Currently known record:
    60 
    60 
    61     L: the node of the "local" part of the merge (hexified version)
    61     L: the node of the "local" part of the merge (hexified version)
    62     O: the node of the "other" part of the merge (hexified version)
    62     O: the node of the "other" part of the merge (hexified version)
    63     F: a file to be merged entry
    63     F: a file to be merged entry
       
    64     m: the external merge driver defined for this merge plus its run state
       
    65        (experimental)
       
    66 
       
    67     Merge driver run states (experimental):
       
    68     u: driver-resolved files unmarked -- needs to be run next time we're about
       
    69        to resolve or commit
       
    70     m: driver-resolved files marked -- only needs to be run before commit
       
    71     s: success/skipped -- does not need to be run any more
    64     '''
    72     '''
    65     statepathv1 = 'merge/state'
    73     statepathv1 = 'merge/state'
    66     statepathv2 = 'merge/state2'
    74     statepathv2 = 'merge/state2'
    67 
    75 
    68     def __init__(self, repo):
    76     def __init__(self, repo):
    75         self._local = None
    83         self._local = None
    76         self._other = None
    84         self._other = None
    77         if node:
    85         if node:
    78             self._local = node
    86             self._local = node
    79             self._other = other
    87             self._other = other
       
    88         self._mdstate = 'u'
    80         shutil.rmtree(self._repo.join('merge'), True)
    89         shutil.rmtree(self._repo.join('merge'), True)
    81         self._dirty = False
    90         self._dirty = False
    82 
    91 
    83     def _read(self):
    92     def _read(self):
    84         """Analyse each record content to restore a serialized state from disk
    93         """Analyse each record content to restore a serialized state from disk
    87         of on disk file.
    96         of on disk file.
    88         """
    97         """
    89         self._state = {}
    98         self._state = {}
    90         self._local = None
    99         self._local = None
    91         self._other = None
   100         self._other = None
       
   101         self._mdstate = 'u'
    92         records = self._readrecords()
   102         records = self._readrecords()
    93         for rtype, record in records:
   103         for rtype, record in records:
    94             if rtype == 'L':
   104             if rtype == 'L':
    95                 self._local = bin(record)
   105                 self._local = bin(record)
    96             elif rtype == 'O':
   106             elif rtype == 'O':
    97                 self._other = bin(record)
   107                 self._other = bin(record)
       
   108             elif rtype == 'm':
       
   109                 bits = record.split('\0', 1)
       
   110                 mdstate = bits[1]
       
   111                 if len(mdstate) != 1 or mdstate not in 'ums':
       
   112                     # the merge driver should be idempotent, so just rerun it
       
   113                     mdstate = 'u'
       
   114 
       
   115                 # protect against the following:
       
   116                 # - A configures a malicious merge driver in their hgrc, then
       
   117                 #   pauses the merge
       
   118                 # - A edits their hgrc to remove references to the merge driver
       
   119                 # - A gives a copy of their entire repo, including .hg, to B
       
   120                 # - B inspects .hgrc and finds it to be clean
       
   121                 # - B then continues the merge and the malicious merge driver
       
   122                 #  gets invoked
       
   123                 if self.mergedriver != bits[0]:
       
   124                     raise error.ConfigError(
       
   125                         _("merge driver changed since merge started"),
       
   126                         hint=_("revert merge driver change or abort merge"))
       
   127                 self._mdstate = mdstate
    98             elif rtype == 'F':
   128             elif rtype == 'F':
    99                 bits = record.split('\0')
   129                 bits = record.split('\0')
   100                 self._state[bits[0]] = bits[1:]
   130                 self._state[bits[0]] = bits[1:]
   101             elif not rtype.islower():
   131             elif not rtype.islower():
   102                 raise error.Abort(_('unsupported merge state record: %s')
   132                 raise error.Abort(_('unsupported merge state record: %s')
   196         except IOError as err:
   226         except IOError as err:
   197             if err.errno != errno.ENOENT:
   227             if err.errno != errno.ENOENT:
   198                 raise
   228                 raise
   199         return records
   229         return records
   200 
   230 
       
   231     @util.propertycache
       
   232     def mergedriver(self):
       
   233         return self._repo.ui.config('experimental', 'mergedriver')
       
   234 
   201     def active(self):
   235     def active(self):
   202         """Whether mergestate is active.
   236         """Whether mergestate is active.
   203 
   237 
   204         Returns True if there appears to be mergestate. This is a rough proxy
   238         Returns True if there appears to be mergestate. This is a rough proxy
   205         for "is a merge in progress."
   239         for "is a merge in progress."
   214         """Write current state on disk (if necessary)"""
   248         """Write current state on disk (if necessary)"""
   215         if self._dirty:
   249         if self._dirty:
   216             records = []
   250             records = []
   217             records.append(('L', hex(self._local)))
   251             records.append(('L', hex(self._local)))
   218             records.append(('O', hex(self._other)))
   252             records.append(('O', hex(self._other)))
       
   253             if self.mergedriver:
       
   254                 records.append(('m', '\0'.join([
       
   255                     self.mergedriver, self._mdstate])))
   219             for d, v in self._state.iteritems():
   256             for d, v in self._state.iteritems():
   220                 records.append(('F', '\0'.join([d] + v)))
   257                 records.append(('F', '\0'.join([d] + v)))
   221             self._writerecords(records)
   258             self._writerecords(records)
   222             self._dirty = False
   259             self._dirty = False
   223 
   260