mercurial/mergestate.py
changeset 45498 cc5f811b1f15
parent 45497 e833ff4dd0ea
child 45499 19590b126764
equal deleted inserted replaced
45497:e833ff4dd0ea 45498:cc5f811b1f15
   125 ACTION_KEEP_ABSENT = b'ka'
   125 ACTION_KEEP_ABSENT = b'ka'
   126 ACTION_EXEC = b'e'
   126 ACTION_EXEC = b'e'
   127 ACTION_CREATED_MERGE = b'cm'
   127 ACTION_CREATED_MERGE = b'cm'
   128 
   128 
   129 
   129 
   130 class mergestate(object):
   130 class _mergestate_base(object):
   131     '''track 3-way merge state of individual files
   131     '''track 3-way merge state of individual files
   132 
   132 
   133     The merge state is stored on disk when needed. Two files are used: one with
   133     The merge state is stored on disk when needed. Two files are used: one with
   134     an old format (version 1), and one with a new format (version 2). Version 2
   134     an old format (version 1), and one with a new format (version 2). Version 2
   135     stores a superset of the data in version 1, including new kinds of records
   135     stores a superset of the data in version 1, including new kinds of records
   169     d: driver-resolved conflict
   169     d: driver-resolved conflict
   170 
   170 
   171     The resolve command transitions between 'u' and 'r' for conflicts and
   171     The resolve command transitions between 'u' and 'r' for conflicts and
   172     'pu' and 'pr' for path conflicts.
   172     'pu' and 'pr' for path conflicts.
   173     '''
   173     '''
   174 
       
   175     statepathv1 = b'merge/state'
       
   176     statepathv2 = b'merge/state2'
       
   177 
       
   178     @staticmethod
       
   179     def clean(repo):
       
   180         """Initialize a brand new merge state, removing any existing state on
       
   181         disk."""
       
   182         ms = mergestate(repo)
       
   183         ms.reset()
       
   184         return ms
       
   185 
       
   186     @staticmethod
       
   187     def read(repo):
       
   188         """Initialize the merge state, reading it from disk."""
       
   189         ms = mergestate(repo)
       
   190         ms._read()
       
   191         return ms
       
   192 
   174 
   193     def __init__(self, repo):
   175     def __init__(self, repo):
   194         """Initialize the merge state.
   176         """Initialize the merge state.
   195 
   177 
   196         Do not use this directly! Instead call read() or clean()."""
   178         Do not use this directly! Instead call read() or clean()."""
   217         self._local = node
   199         self._local = node
   218         self._other = other
   200         self._other = other
   219         self._labels = labels
   201         self._labels = labels
   220         if self.mergedriver:
   202         if self.mergedriver:
   221             self._mdstate = MERGE_DRIVER_STATE_SUCCESS
   203             self._mdstate = MERGE_DRIVER_STATE_SUCCESS
   222 
       
   223     def _read(self):
       
   224         """Analyse each record content to restore a serialized state from disk
       
   225 
       
   226         This function process "record" entry produced by the de-serialization
       
   227         of on disk file.
       
   228         """
       
   229         self._mdstate = MERGE_DRIVER_STATE_SUCCESS
       
   230         unsupported = set()
       
   231         records = self._readrecords()
       
   232         for rtype, record in records:
       
   233             if rtype == RECORD_LOCAL:
       
   234                 self._local = bin(record)
       
   235             elif rtype == RECORD_OTHER:
       
   236                 self._other = bin(record)
       
   237             elif rtype == RECORD_MERGE_DRIVER_STATE:
       
   238                 bits = record.split(b'\0', 1)
       
   239                 mdstate = bits[1]
       
   240                 if len(mdstate) != 1 or mdstate not in (
       
   241                     MERGE_DRIVER_STATE_UNMARKED,
       
   242                     MERGE_DRIVER_STATE_MARKED,
       
   243                     MERGE_DRIVER_STATE_SUCCESS,
       
   244                 ):
       
   245                     # the merge driver should be idempotent, so just rerun it
       
   246                     mdstate = MERGE_DRIVER_STATE_UNMARKED
       
   247 
       
   248                 self._readmergedriver = bits[0]
       
   249                 self._mdstate = mdstate
       
   250             elif rtype in (
       
   251                 RECORD_MERGED,
       
   252                 RECORD_CHANGEDELETE_CONFLICT,
       
   253                 RECORD_PATH_CONFLICT,
       
   254                 RECORD_MERGE_DRIVER_MERGE,
       
   255                 LEGACY_RECORD_RESOLVED_OTHER,
       
   256             ):
       
   257                 bits = record.split(b'\0')
       
   258                 # merge entry type MERGE_RECORD_MERGED_OTHER is deprecated
       
   259                 # and we now store related information in _stateextras, so
       
   260                 # lets write to _stateextras directly
       
   261                 if bits[1] == MERGE_RECORD_MERGED_OTHER:
       
   262                     self._stateextras[bits[0]][b'filenode-source'] = b'other'
       
   263                 else:
       
   264                     self._state[bits[0]] = bits[1:]
       
   265             elif rtype == RECORD_FILE_VALUES:
       
   266                 filename, rawextras = record.split(b'\0', 1)
       
   267                 extraparts = rawextras.split(b'\0')
       
   268                 extras = {}
       
   269                 i = 0
       
   270                 while i < len(extraparts):
       
   271                     extras[extraparts[i]] = extraparts[i + 1]
       
   272                     i += 2
       
   273 
       
   274                 self._stateextras[filename] = extras
       
   275             elif rtype == RECORD_LABELS:
       
   276                 labels = record.split(b'\0', 2)
       
   277                 self._labels = [l for l in labels if len(l) > 0]
       
   278             elif not rtype.islower():
       
   279                 unsupported.add(rtype)
       
   280 
       
   281         if unsupported:
       
   282             raise error.UnsupportedMergeRecords(unsupported)
       
   283 
       
   284     def _readrecords(self):
       
   285         """Read merge state from disk and return a list of record (TYPE, data)
       
   286 
       
   287         We read data from both v1 and v2 files and decide which one to use.
       
   288 
       
   289         V1 has been used by version prior to 2.9.1 and contains less data than
       
   290         v2. We read both versions and check if no data in v2 contradicts
       
   291         v1. If there is not contradiction we can safely assume that both v1
       
   292         and v2 were written at the same time and use the extract data in v2. If
       
   293         there is contradiction we ignore v2 content as we assume an old version
       
   294         of Mercurial has overwritten the mergestate file and left an old v2
       
   295         file around.
       
   296 
       
   297         returns list of record [(TYPE, data), ...]"""
       
   298         v1records = self._readrecordsv1()
       
   299         v2records = self._readrecordsv2()
       
   300         if self._v1v2match(v1records, v2records):
       
   301             return v2records
       
   302         else:
       
   303             # v1 file is newer than v2 file, use it
       
   304             # we have to infer the "other" changeset of the merge
       
   305             # we cannot do better than that with v1 of the format
       
   306             mctx = self._repo[None].parents()[-1]
       
   307             v1records.append((RECORD_OTHER, mctx.hex()))
       
   308             # add place holder "other" file node information
       
   309             # nobody is using it yet so we do no need to fetch the data
       
   310             # if mctx was wrong `mctx[bits[-2]]` may fails.
       
   311             for idx, r in enumerate(v1records):
       
   312                 if r[0] == RECORD_MERGED:
       
   313                     bits = r[1].split(b'\0')
       
   314                     bits.insert(-2, b'')
       
   315                     v1records[idx] = (r[0], b'\0'.join(bits))
       
   316             return v1records
       
   317 
       
   318     def _v1v2match(self, v1records, v2records):
       
   319         oldv2 = set()  # old format version of v2 record
       
   320         for rec in v2records:
       
   321             if rec[0] == RECORD_LOCAL:
       
   322                 oldv2.add(rec)
       
   323             elif rec[0] == RECORD_MERGED:
       
   324                 # drop the onode data (not contained in v1)
       
   325                 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
       
   326         for rec in v1records:
       
   327             if rec not in oldv2:
       
   328                 return False
       
   329         else:
       
   330             return True
       
   331 
       
   332     def _readrecordsv1(self):
       
   333         """read on disk merge state for version 1 file
       
   334 
       
   335         returns list of record [(TYPE, data), ...]
       
   336 
       
   337         Note: the "F" data from this file are one entry short
       
   338               (no "other file node" entry)
       
   339         """
       
   340         records = []
       
   341         try:
       
   342             f = self._repo.vfs(self.statepathv1)
       
   343             for i, l in enumerate(f):
       
   344                 if i == 0:
       
   345                     records.append((RECORD_LOCAL, l[:-1]))
       
   346                 else:
       
   347                     records.append((RECORD_MERGED, l[:-1]))
       
   348             f.close()
       
   349         except IOError as err:
       
   350             if err.errno != errno.ENOENT:
       
   351                 raise
       
   352         return records
       
   353 
       
   354     def _readrecordsv2(self):
       
   355         """read on disk merge state for version 2 file
       
   356 
       
   357         This format is a list of arbitrary records of the form:
       
   358 
       
   359           [type][length][content]
       
   360 
       
   361         `type` is a single character, `length` is a 4 byte integer, and
       
   362         `content` is an arbitrary byte sequence of length `length`.
       
   363 
       
   364         Mercurial versions prior to 3.7 have a bug where if there are
       
   365         unsupported mandatory merge records, attempting to clear out the merge
       
   366         state with hg update --clean or similar aborts. The 't' record type
       
   367         works around that by writing out what those versions treat as an
       
   368         advisory record, but later versions interpret as special: the first
       
   369         character is the 'real' record type and everything onwards is the data.
       
   370 
       
   371         Returns list of records [(TYPE, data), ...]."""
       
   372         records = []
       
   373         try:
       
   374             f = self._repo.vfs(self.statepathv2)
       
   375             data = f.read()
       
   376             off = 0
       
   377             end = len(data)
       
   378             while off < end:
       
   379                 rtype = data[off : off + 1]
       
   380                 off += 1
       
   381                 length = _unpack(b'>I', data[off : (off + 4)])[0]
       
   382                 off += 4
       
   383                 record = data[off : (off + length)]
       
   384                 off += length
       
   385                 if rtype == RECORD_OVERRIDE:
       
   386                     rtype, record = record[0:1], record[1:]
       
   387                 records.append((rtype, record))
       
   388             f.close()
       
   389         except IOError as err:
       
   390             if err.errno != errno.ENOENT:
       
   391                 raise
       
   392         return records
       
   393 
   204 
   394     @util.propertycache
   205     @util.propertycache
   395     def mergedriver(self):
   206     def mergedriver(self):
   396         # protect against the following:
   207         # protect against the following:
   397         # - A configures a malicious merge driver in their hgrc, then
   208         # - A configures a malicious merge driver in their hgrc, then
   504         if self._labels is not None:
   315         if self._labels is not None:
   505             labels = b'\0'.join(self._labels)
   316             labels = b'\0'.join(self._labels)
   506             records.append((RECORD_LABELS, labels))
   317             records.append((RECORD_LABELS, labels))
   507         return records
   318         return records
   508 
   319 
   509     def _writerecords(self, records):
       
   510         """Write current state on disk (both v1 and v2)"""
       
   511         self._writerecordsv1(records)
       
   512         self._writerecordsv2(records)
       
   513 
       
   514     def _writerecordsv1(self, records):
       
   515         """Write current state on disk in a version 1 file"""
       
   516         f = self._repo.vfs(self.statepathv1, b'wb')
       
   517         irecords = iter(records)
       
   518         lrecords = next(irecords)
       
   519         assert lrecords[0] == RECORD_LOCAL
       
   520         f.write(hex(self._local) + b'\n')
       
   521         for rtype, data in irecords:
       
   522             if rtype == RECORD_MERGED:
       
   523                 f.write(b'%s\n' % _droponode(data))
       
   524         f.close()
       
   525 
       
   526     def _writerecordsv2(self, records):
       
   527         """Write current state on disk in a version 2 file
       
   528 
       
   529         See the docstring for _readrecordsv2 for why we use 't'."""
       
   530         # these are the records that all version 2 clients can read
       
   531         allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
       
   532         f = self._repo.vfs(self.statepathv2, b'wb')
       
   533         for key, data in records:
       
   534             assert len(key) == 1
       
   535             if key not in allowlist:
       
   536                 key, data = RECORD_OVERRIDE, b'%s%s' % (key, data)
       
   537             format = b'>sI%is' % len(data)
       
   538             f.write(_pack(format, key, len(data), data))
       
   539         f.close()
       
   540 
       
   541     @staticmethod
   320     @staticmethod
   542     def getlocalkey(path):
   321     def getlocalkey(path):
   543         """hash the path of a local file context for storage in the .hg/merge
   322         """hash the path of a local file context for storage in the .hg/merge
   544         directory."""
   323         directory."""
   545 
   324 
   546         return hex(hashutil.sha1(path).digest())
   325         return hex(hashutil.sha1(path).digest())
   547 
   326 
   548     def _make_backup(self, fctx, localkey):
   327     def _make_backup(self, fctx, localkey):
   549         self._repo.vfs.write(b'merge/' + localkey, fctx.data())
   328         raise NotImplementedError()
   550 
   329 
   551     def _restore_backup(self, fctx, localkey, flags):
   330     def _restore_backup(self, fctx, localkey, flags):
   552         with self._repo.vfs(b'merge/' + localkey) as f:
   331         raise NotImplementedError()
   553             fctx.write(f.read(), flags)
       
   554 
   332 
   555     def add(self, fcl, fco, fca, fd):
   333     def add(self, fcl, fco, fca, fd):
   556         """add a new (potentially?) conflicting file the merge state
   334         """add a new (potentially?) conflicting file the merge state
   557         fcl: file context for local,
   335         fcl: file context for local,
   558         fco: file context for remote,
   336         fco: file context for remote,
   787 
   565 
   788         Meant for use by custom merge drivers."""
   566         Meant for use by custom merge drivers."""
   789         self._results[f] = 0, ACTION_GET
   567         self._results[f] = 0, ACTION_GET
   790 
   568 
   791 
   569 
       
   570 class mergestate(_mergestate_base):
       
   571 
       
   572     statepathv1 = b'merge/state'
       
   573     statepathv2 = b'merge/state2'
       
   574 
       
   575     @staticmethod
       
   576     def clean(repo):
       
   577         """Initialize a brand new merge state, removing any existing state on
       
   578         disk."""
       
   579         ms = mergestate(repo)
       
   580         ms.reset()
       
   581         return ms
       
   582 
       
   583     @staticmethod
       
   584     def read(repo):
       
   585         """Initialize the merge state, reading it from disk."""
       
   586         ms = mergestate(repo)
       
   587         ms._read()
       
   588         return ms
       
   589 
       
   590     def _read(self):
       
   591         """Analyse each record content to restore a serialized state from disk
       
   592 
       
   593         This function process "record" entry produced by the de-serialization
       
   594         of on disk file.
       
   595         """
       
   596         self._mdstate = MERGE_DRIVER_STATE_SUCCESS
       
   597         unsupported = set()
       
   598         records = self._readrecords()
       
   599         for rtype, record in records:
       
   600             if rtype == RECORD_LOCAL:
       
   601                 self._local = bin(record)
       
   602             elif rtype == RECORD_OTHER:
       
   603                 self._other = bin(record)
       
   604             elif rtype == RECORD_MERGE_DRIVER_STATE:
       
   605                 bits = record.split(b'\0', 1)
       
   606                 mdstate = bits[1]
       
   607                 if len(mdstate) != 1 or mdstate not in (
       
   608                     MERGE_DRIVER_STATE_UNMARKED,
       
   609                     MERGE_DRIVER_STATE_MARKED,
       
   610                     MERGE_DRIVER_STATE_SUCCESS,
       
   611                 ):
       
   612                     # the merge driver should be idempotent, so just rerun it
       
   613                     mdstate = MERGE_DRIVER_STATE_UNMARKED
       
   614 
       
   615                 self._readmergedriver = bits[0]
       
   616                 self._mdstate = mdstate
       
   617             elif rtype in (
       
   618                 RECORD_MERGED,
       
   619                 RECORD_CHANGEDELETE_CONFLICT,
       
   620                 RECORD_PATH_CONFLICT,
       
   621                 RECORD_MERGE_DRIVER_MERGE,
       
   622                 LEGACY_RECORD_RESOLVED_OTHER,
       
   623             ):
       
   624                 bits = record.split(b'\0')
       
   625                 # merge entry type MERGE_RECORD_MERGED_OTHER is deprecated
       
   626                 # and we now store related information in _stateextras, so
       
   627                 # lets write to _stateextras directly
       
   628                 if bits[1] == MERGE_RECORD_MERGED_OTHER:
       
   629                     self._stateextras[bits[0]][b'filenode-source'] = b'other'
       
   630                 else:
       
   631                     self._state[bits[0]] = bits[1:]
       
   632             elif rtype == RECORD_FILE_VALUES:
       
   633                 filename, rawextras = record.split(b'\0', 1)
       
   634                 extraparts = rawextras.split(b'\0')
       
   635                 extras = {}
       
   636                 i = 0
       
   637                 while i < len(extraparts):
       
   638                     extras[extraparts[i]] = extraparts[i + 1]
       
   639                     i += 2
       
   640 
       
   641                 self._stateextras[filename] = extras
       
   642             elif rtype == RECORD_LABELS:
       
   643                 labels = record.split(b'\0', 2)
       
   644                 self._labels = [l for l in labels if len(l) > 0]
       
   645             elif not rtype.islower():
       
   646                 unsupported.add(rtype)
       
   647 
       
   648         if unsupported:
       
   649             raise error.UnsupportedMergeRecords(unsupported)
       
   650 
       
   651     def _readrecords(self):
       
   652         """Read merge state from disk and return a list of record (TYPE, data)
       
   653 
       
   654         We read data from both v1 and v2 files and decide which one to use.
       
   655 
       
   656         V1 has been used by version prior to 2.9.1 and contains less data than
       
   657         v2. We read both versions and check if no data in v2 contradicts
       
   658         v1. If there is not contradiction we can safely assume that both v1
       
   659         and v2 were written at the same time and use the extract data in v2. If
       
   660         there is contradiction we ignore v2 content as we assume an old version
       
   661         of Mercurial has overwritten the mergestate file and left an old v2
       
   662         file around.
       
   663 
       
   664         returns list of record [(TYPE, data), ...]"""
       
   665         v1records = self._readrecordsv1()
       
   666         v2records = self._readrecordsv2()
       
   667         if self._v1v2match(v1records, v2records):
       
   668             return v2records
       
   669         else:
       
   670             # v1 file is newer than v2 file, use it
       
   671             # we have to infer the "other" changeset of the merge
       
   672             # we cannot do better than that with v1 of the format
       
   673             mctx = self._repo[None].parents()[-1]
       
   674             v1records.append((RECORD_OTHER, mctx.hex()))
       
   675             # add place holder "other" file node information
       
   676             # nobody is using it yet so we do no need to fetch the data
       
   677             # if mctx was wrong `mctx[bits[-2]]` may fails.
       
   678             for idx, r in enumerate(v1records):
       
   679                 if r[0] == RECORD_MERGED:
       
   680                     bits = r[1].split(b'\0')
       
   681                     bits.insert(-2, b'')
       
   682                     v1records[idx] = (r[0], b'\0'.join(bits))
       
   683             return v1records
       
   684 
       
   685     def _v1v2match(self, v1records, v2records):
       
   686         oldv2 = set()  # old format version of v2 record
       
   687         for rec in v2records:
       
   688             if rec[0] == RECORD_LOCAL:
       
   689                 oldv2.add(rec)
       
   690             elif rec[0] == RECORD_MERGED:
       
   691                 # drop the onode data (not contained in v1)
       
   692                 oldv2.add((RECORD_MERGED, _droponode(rec[1])))
       
   693         for rec in v1records:
       
   694             if rec not in oldv2:
       
   695                 return False
       
   696         else:
       
   697             return True
       
   698 
       
   699     def _readrecordsv1(self):
       
   700         """read on disk merge state for version 1 file
       
   701 
       
   702         returns list of record [(TYPE, data), ...]
       
   703 
       
   704         Note: the "F" data from this file are one entry short
       
   705               (no "other file node" entry)
       
   706         """
       
   707         records = []
       
   708         try:
       
   709             f = self._repo.vfs(self.statepathv1)
       
   710             for i, l in enumerate(f):
       
   711                 if i == 0:
       
   712                     records.append((RECORD_LOCAL, l[:-1]))
       
   713                 else:
       
   714                     records.append((RECORD_MERGED, l[:-1]))
       
   715             f.close()
       
   716         except IOError as err:
       
   717             if err.errno != errno.ENOENT:
       
   718                 raise
       
   719         return records
       
   720 
       
   721     def _readrecordsv2(self):
       
   722         """read on disk merge state for version 2 file
       
   723 
       
   724         This format is a list of arbitrary records of the form:
       
   725 
       
   726           [type][length][content]
       
   727 
       
   728         `type` is a single character, `length` is a 4 byte integer, and
       
   729         `content` is an arbitrary byte sequence of length `length`.
       
   730 
       
   731         Mercurial versions prior to 3.7 have a bug where if there are
       
   732         unsupported mandatory merge records, attempting to clear out the merge
       
   733         state with hg update --clean or similar aborts. The 't' record type
       
   734         works around that by writing out what those versions treat as an
       
   735         advisory record, but later versions interpret as special: the first
       
   736         character is the 'real' record type and everything onwards is the data.
       
   737 
       
   738         Returns list of records [(TYPE, data), ...]."""
       
   739         records = []
       
   740         try:
       
   741             f = self._repo.vfs(self.statepathv2)
       
   742             data = f.read()
       
   743             off = 0
       
   744             end = len(data)
       
   745             while off < end:
       
   746                 rtype = data[off : off + 1]
       
   747                 off += 1
       
   748                 length = _unpack(b'>I', data[off : (off + 4)])[0]
       
   749                 off += 4
       
   750                 record = data[off : (off + length)]
       
   751                 off += length
       
   752                 if rtype == RECORD_OVERRIDE:
       
   753                     rtype, record = record[0:1], record[1:]
       
   754                 records.append((rtype, record))
       
   755             f.close()
       
   756         except IOError as err:
       
   757             if err.errno != errno.ENOENT:
       
   758                 raise
       
   759         return records
       
   760 
       
   761     def _writerecords(self, records):
       
   762         """Write current state on disk (both v1 and v2)"""
       
   763         self._writerecordsv1(records)
       
   764         self._writerecordsv2(records)
       
   765 
       
   766     def _writerecordsv1(self, records):
       
   767         """Write current state on disk in a version 1 file"""
       
   768         f = self._repo.vfs(self.statepathv1, b'wb')
       
   769         irecords = iter(records)
       
   770         lrecords = next(irecords)
       
   771         assert lrecords[0] == RECORD_LOCAL
       
   772         f.write(hex(self._local) + b'\n')
       
   773         for rtype, data in irecords:
       
   774             if rtype == RECORD_MERGED:
       
   775                 f.write(b'%s\n' % _droponode(data))
       
   776         f.close()
       
   777 
       
   778     def _writerecordsv2(self, records):
       
   779         """Write current state on disk in a version 2 file
       
   780 
       
   781         See the docstring for _readrecordsv2 for why we use 't'."""
       
   782         # these are the records that all version 2 clients can read
       
   783         allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
       
   784         f = self._repo.vfs(self.statepathv2, b'wb')
       
   785         for key, data in records:
       
   786             assert len(key) == 1
       
   787             if key not in allowlist:
       
   788                 key, data = RECORD_OVERRIDE, b'%s%s' % (key, data)
       
   789             format = b'>sI%is' % len(data)
       
   790             f.write(_pack(format, key, len(data), data))
       
   791         f.close()
       
   792 
       
   793     def _make_backup(self, fctx, localkey):
       
   794         self._repo.vfs.write(b'merge/' + localkey, fctx.data())
       
   795 
       
   796     def _restore_backup(self, fctx, localkey, flags):
       
   797         with self._repo.vfs(b'merge/' + localkey) as f:
       
   798             fctx.write(f.read(), flags)
       
   799 
       
   800     def reset(self):
       
   801         shutil.rmtree(self._repo.vfs.join(b'merge'), True)
       
   802 
       
   803 
   792 def recordupdates(repo, actions, branchmerge, getfiledata):
   804 def recordupdates(repo, actions, branchmerge, getfiledata):
   793     """record merge actions to the dirstate"""
   805     """record merge actions to the dirstate"""
   794     # remove (must come first)
   806     # remove (must come first)
   795     for f, args, msg in actions.get(ACTION_REMOVE, []):
   807     for f, args, msg in actions.get(ACTION_REMOVE, []):
   796         if branchmerge:
   808         if branchmerge: