mercurial/merge.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43090 1f339b503a40
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    42 _unpack = struct.unpack
    42 _unpack = struct.unpack
    43 
    43 
    44 
    44 
    45 def _droponode(data):
    45 def _droponode(data):
    46     # used for compatibility for v1
    46     # used for compatibility for v1
    47     bits = data.split('\0')
    47     bits = data.split(b'\0')
    48     bits = bits[:-2] + bits[-1:]
    48     bits = bits[:-2] + bits[-1:]
    49     return '\0'.join(bits)
    49     return b'\0'.join(bits)
    50 
    50 
    51 
    51 
    52 # Merge state record types. See ``mergestate`` docs for more.
    52 # Merge state record types. See ``mergestate`` docs for more.
    53 RECORD_LOCAL = b'L'
    53 RECORD_LOCAL = b'L'
    54 RECORD_OTHER = b'O'
    54 RECORD_OTHER = b'O'
   136 
   136 
   137     The resolve command transitions between 'u' and 'r' for conflicts and
   137     The resolve command transitions between 'u' and 'r' for conflicts and
   138     'pu' and 'pr' for path conflicts.
   138     'pu' and 'pr' for path conflicts.
   139     '''
   139     '''
   140 
   140 
   141     statepathv1 = 'merge/state'
   141     statepathv1 = b'merge/state'
   142     statepathv2 = 'merge/state2'
   142     statepathv2 = b'merge/state2'
   143 
   143 
   144     @staticmethod
   144     @staticmethod
   145     def clean(repo, node=None, other=None, labels=None):
   145     def clean(repo, node=None, other=None, labels=None):
   146         """Initialize a brand new merge state, removing any existing state on
   146         """Initialize a brand new merge state, removing any existing state on
   147         disk."""
   147         disk."""
   168         self._state = {}
   168         self._state = {}
   169         self._stateextras = {}
   169         self._stateextras = {}
   170         self._local = None
   170         self._local = None
   171         self._other = None
   171         self._other = None
   172         self._labels = labels
   172         self._labels = labels
   173         for var in ('localctx', 'otherctx'):
   173         for var in (b'localctx', b'otherctx'):
   174             if var in vars(self):
   174             if var in vars(self):
   175                 delattr(self, var)
   175                 delattr(self, var)
   176         if node:
   176         if node:
   177             self._local = node
   177             self._local = node
   178             self._other = other
   178             self._other = other
   179         self._readmergedriver = None
   179         self._readmergedriver = None
   180         if self.mergedriver:
   180         if self.mergedriver:
   181             self._mdstate = MERGE_DRIVER_STATE_SUCCESS
   181             self._mdstate = MERGE_DRIVER_STATE_SUCCESS
   182         else:
   182         else:
   183             self._mdstate = MERGE_DRIVER_STATE_UNMARKED
   183             self._mdstate = MERGE_DRIVER_STATE_UNMARKED
   184         shutil.rmtree(self._repo.vfs.join('merge'), True)
   184         shutil.rmtree(self._repo.vfs.join(b'merge'), True)
   185         self._results = {}
   185         self._results = {}
   186         self._dirty = False
   186         self._dirty = False
   187 
   187 
   188     def _read(self):
   188     def _read(self):
   189         """Analyse each record content to restore a serialized state from disk
   189         """Analyse each record content to restore a serialized state from disk
   193         """
   193         """
   194         self._state = {}
   194         self._state = {}
   195         self._stateextras = {}
   195         self._stateextras = {}
   196         self._local = None
   196         self._local = None
   197         self._other = None
   197         self._other = None
   198         for var in ('localctx', 'otherctx'):
   198         for var in (b'localctx', b'otherctx'):
   199             if var in vars(self):
   199             if var in vars(self):
   200                 delattr(self, var)
   200                 delattr(self, var)
   201         self._readmergedriver = None
   201         self._readmergedriver = None
   202         self._mdstate = MERGE_DRIVER_STATE_SUCCESS
   202         self._mdstate = MERGE_DRIVER_STATE_SUCCESS
   203         unsupported = set()
   203         unsupported = set()
   206             if rtype == RECORD_LOCAL:
   206             if rtype == RECORD_LOCAL:
   207                 self._local = bin(record)
   207                 self._local = bin(record)
   208             elif rtype == RECORD_OTHER:
   208             elif rtype == RECORD_OTHER:
   209                 self._other = bin(record)
   209                 self._other = bin(record)
   210             elif rtype == RECORD_MERGE_DRIVER_STATE:
   210             elif rtype == RECORD_MERGE_DRIVER_STATE:
   211                 bits = record.split('\0', 1)
   211                 bits = record.split(b'\0', 1)
   212                 mdstate = bits[1]
   212                 mdstate = bits[1]
   213                 if len(mdstate) != 1 or mdstate not in (
   213                 if len(mdstate) != 1 or mdstate not in (
   214                     MERGE_DRIVER_STATE_UNMARKED,
   214                     MERGE_DRIVER_STATE_UNMARKED,
   215                     MERGE_DRIVER_STATE_MARKED,
   215                     MERGE_DRIVER_STATE_MARKED,
   216                     MERGE_DRIVER_STATE_SUCCESS,
   216                     MERGE_DRIVER_STATE_SUCCESS,
   224                 RECORD_MERGED,
   224                 RECORD_MERGED,
   225                 RECORD_CHANGEDELETE_CONFLICT,
   225                 RECORD_CHANGEDELETE_CONFLICT,
   226                 RECORD_PATH_CONFLICT,
   226                 RECORD_PATH_CONFLICT,
   227                 RECORD_MERGE_DRIVER_MERGE,
   227                 RECORD_MERGE_DRIVER_MERGE,
   228             ):
   228             ):
   229                 bits = record.split('\0')
   229                 bits = record.split(b'\0')
   230                 self._state[bits[0]] = bits[1:]
   230                 self._state[bits[0]] = bits[1:]
   231             elif rtype == RECORD_FILE_VALUES:
   231             elif rtype == RECORD_FILE_VALUES:
   232                 filename, rawextras = record.split('\0', 1)
   232                 filename, rawextras = record.split(b'\0', 1)
   233                 extraparts = rawextras.split('\0')
   233                 extraparts = rawextras.split(b'\0')
   234                 extras = {}
   234                 extras = {}
   235                 i = 0
   235                 i = 0
   236                 while i < len(extraparts):
   236                 while i < len(extraparts):
   237                     extras[extraparts[i]] = extraparts[i + 1]
   237                     extras[extraparts[i]] = extraparts[i + 1]
   238                     i += 2
   238                     i += 2
   239 
   239 
   240                 self._stateextras[filename] = extras
   240                 self._stateextras[filename] = extras
   241             elif rtype == RECORD_LABELS:
   241             elif rtype == RECORD_LABELS:
   242                 labels = record.split('\0', 2)
   242                 labels = record.split(b'\0', 2)
   243                 self._labels = [l for l in labels if len(l) > 0]
   243                 self._labels = [l for l in labels if len(l) > 0]
   244             elif not rtype.islower():
   244             elif not rtype.islower():
   245                 unsupported.add(rtype)
   245                 unsupported.add(rtype)
   246         self._results = {}
   246         self._results = {}
   247         self._dirty = False
   247         self._dirty = False
   276             # add place holder "other" file node information
   276             # add place holder "other" file node information
   277             # nobody is using it yet so we do no need to fetch the data
   277             # nobody is using it yet so we do no need to fetch the data
   278             # if mctx was wrong `mctx[bits[-2]]` may fails.
   278             # if mctx was wrong `mctx[bits[-2]]` may fails.
   279             for idx, r in enumerate(v1records):
   279             for idx, r in enumerate(v1records):
   280                 if r[0] == RECORD_MERGED:
   280                 if r[0] == RECORD_MERGED:
   281                     bits = r[1].split('\0')
   281                     bits = r[1].split(b'\0')
   282                     bits.insert(-2, '')
   282                     bits.insert(-2, b'')
   283                     v1records[idx] = (r[0], '\0'.join(bits))
   283                     v1records[idx] = (r[0], b'\0'.join(bits))
   284             return v1records
   284             return v1records
   285 
   285 
   286     def _v1v2match(self, v1records, v2records):
   286     def _v1v2match(self, v1records, v2records):
   287         oldv2 = set()  # old format version of v2 record
   287         oldv2 = set()  # old format version of v2 record
   288         for rec in v2records:
   288         for rec in v2records:
   344             off = 0
   344             off = 0
   345             end = len(data)
   345             end = len(data)
   346             while off < end:
   346             while off < end:
   347                 rtype = data[off : off + 1]
   347                 rtype = data[off : off + 1]
   348                 off += 1
   348                 off += 1
   349                 length = _unpack('>I', data[off : (off + 4)])[0]
   349                 length = _unpack(b'>I', data[off : (off + 4)])[0]
   350                 off += 4
   350                 off += 4
   351                 record = data[off : (off + length)]
   351                 record = data[off : (off + length)]
   352                 off += length
   352                 off += length
   353                 if rtype == RECORD_OVERRIDE:
   353                 if rtype == RECORD_OVERRIDE:
   354                     rtype, record = record[0:1], record[1:]
   354                     rtype, record = record[0:1], record[1:]
   367         # - A edits their hgrc to remove references to the merge driver
   367         # - A edits their hgrc to remove references to the merge driver
   368         # - A gives a copy of their entire repo, including .hg, to B
   368         # - A gives a copy of their entire repo, including .hg, to B
   369         # - B inspects .hgrc and finds it to be clean
   369         # - B inspects .hgrc and finds it to be clean
   370         # - B then continues the merge and the malicious merge driver
   370         # - B then continues the merge and the malicious merge driver
   371         #  gets invoked
   371         #  gets invoked
   372         configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
   372         configmergedriver = self._repo.ui.config(
       
   373             b'experimental', b'mergedriver'
       
   374         )
   373         if (
   375         if (
   374             self._readmergedriver is not None
   376             self._readmergedriver is not None
   375             and self._readmergedriver != configmergedriver
   377             and self._readmergedriver != configmergedriver
   376         ):
   378         ):
   377             raise error.ConfigError(
   379             raise error.ConfigError(
   378                 _("merge driver changed since merge started"),
   380                 _(b"merge driver changed since merge started"),
   379                 hint=_("revert merge driver change or abort merge"),
   381                 hint=_(b"revert merge driver change or abort merge"),
   380             )
   382             )
   381 
   383 
   382         return configmergedriver
   384         return configmergedriver
   383 
   385 
   384     @util.propertycache
   386     @util.propertycache
   385     def localctx(self):
   387     def localctx(self):
   386         if self._local is None:
   388         if self._local is None:
   387             msg = "localctx accessed but self._local isn't set"
   389             msg = b"localctx accessed but self._local isn't set"
   388             raise error.ProgrammingError(msg)
   390             raise error.ProgrammingError(msg)
   389         return self._repo[self._local]
   391         return self._repo[self._local]
   390 
   392 
   391     @util.propertycache
   393     @util.propertycache
   392     def otherctx(self):
   394     def otherctx(self):
   393         if self._other is None:
   395         if self._other is None:
   394             msg = "otherctx accessed but self._other isn't set"
   396             msg = b"otherctx accessed but self._other isn't set"
   395             raise error.ProgrammingError(msg)
   397             raise error.ProgrammingError(msg)
   396         return self._repo[self._other]
   398         return self._repo[self._other]
   397 
   399 
   398     def active(self):
   400     def active(self):
   399         """Whether mergestate is active.
   401         """Whether mergestate is active.
   423         records.append((RECORD_OTHER, hex(self._other)))
   425         records.append((RECORD_OTHER, hex(self._other)))
   424         if self.mergedriver:
   426         if self.mergedriver:
   425             records.append(
   427             records.append(
   426                 (
   428                 (
   427                     RECORD_MERGE_DRIVER_STATE,
   429                     RECORD_MERGE_DRIVER_STATE,
   428                     '\0'.join([self.mergedriver, self._mdstate]),
   430                     b'\0'.join([self.mergedriver, self._mdstate]),
   429                 )
   431                 )
   430             )
   432             )
   431         # Write out state items. In all cases, the value of the state map entry
   433         # Write out state items. In all cases, the value of the state map entry
   432         # is written as the contents of the record. The record type depends on
   434         # is written as the contents of the record. The record type depends on
   433         # the type of state that is stored, and capital-letter records are used
   435         # the type of state that is stored, and capital-letter records are used
   435         # from loading them.
   437         # from loading them.
   436         for filename, v in self._state.iteritems():
   438         for filename, v in self._state.iteritems():
   437             if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
   439             if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
   438                 # Driver-resolved merge. These are stored in 'D' records.
   440                 # Driver-resolved merge. These are stored in 'D' records.
   439                 records.append(
   441                 records.append(
   440                     (RECORD_MERGE_DRIVER_MERGE, '\0'.join([filename] + v))
   442                     (RECORD_MERGE_DRIVER_MERGE, b'\0'.join([filename] + v))
   441                 )
   443                 )
   442             elif v[0] in (
   444             elif v[0] in (
   443                 MERGE_RECORD_UNRESOLVED_PATH,
   445                 MERGE_RECORD_UNRESOLVED_PATH,
   444                 MERGE_RECORD_RESOLVED_PATH,
   446                 MERGE_RECORD_RESOLVED_PATH,
   445             ):
   447             ):
   446                 # Path conflicts. These are stored in 'P' records.  The current
   448                 # Path conflicts. These are stored in 'P' records.  The current
   447                 # resolution state ('pu' or 'pr') is stored within the record.
   449                 # resolution state ('pu' or 'pr') is stored within the record.
   448                 records.append(
   450                 records.append(
   449                     (RECORD_PATH_CONFLICT, '\0'.join([filename] + v))
   451                     (RECORD_PATH_CONFLICT, b'\0'.join([filename] + v))
   450                 )
   452                 )
   451             elif v[1] == nullhex or v[6] == nullhex:
   453             elif v[1] == nullhex or v[6] == nullhex:
   452                 # Change/Delete or Delete/Change conflicts. These are stored in
   454                 # Change/Delete or Delete/Change conflicts. These are stored in
   453                 # 'C' records. v[1] is the local file, and is nullhex when the
   455                 # 'C' records. v[1] is the local file, and is nullhex when the
   454                 # file is deleted locally ('dc'). v[6] is the remote file, and
   456                 # file is deleted locally ('dc'). v[6] is the remote file, and
   455                 # is nullhex when the file is deleted remotely ('cd').
   457                 # is nullhex when the file is deleted remotely ('cd').
   456                 records.append(
   458                 records.append(
   457                     (RECORD_CHANGEDELETE_CONFLICT, '\0'.join([filename] + v))
   459                     (RECORD_CHANGEDELETE_CONFLICT, b'\0'.join([filename] + v))
   458                 )
   460                 )
   459             else:
   461             else:
   460                 # Normal files.  These are stored in 'F' records.
   462                 # Normal files.  These are stored in 'F' records.
   461                 records.append((RECORD_MERGED, '\0'.join([filename] + v)))
   463                 records.append((RECORD_MERGED, b'\0'.join([filename] + v)))
   462         for filename, extras in sorted(self._stateextras.iteritems()):
   464         for filename, extras in sorted(self._stateextras.iteritems()):
   463             rawextras = '\0'.join(
   465             rawextras = b'\0'.join(
   464                 '%s\0%s' % (k, v) for k, v in extras.iteritems()
   466                 b'%s\0%s' % (k, v) for k, v in extras.iteritems()
   465             )
   467             )
   466             records.append(
   468             records.append(
   467                 (RECORD_FILE_VALUES, '%s\0%s' % (filename, rawextras))
   469                 (RECORD_FILE_VALUES, b'%s\0%s' % (filename, rawextras))
   468             )
   470             )
   469         if self._labels is not None:
   471         if self._labels is not None:
   470             labels = '\0'.join(self._labels)
   472             labels = b'\0'.join(self._labels)
   471             records.append((RECORD_LABELS, labels))
   473             records.append((RECORD_LABELS, labels))
   472         return records
   474         return records
   473 
   475 
   474     def _writerecords(self, records):
   476     def _writerecords(self, records):
   475         """Write current state on disk (both v1 and v2)"""
   477         """Write current state on disk (both v1 and v2)"""
   476         self._writerecordsv1(records)
   478         self._writerecordsv1(records)
   477         self._writerecordsv2(records)
   479         self._writerecordsv2(records)
   478 
   480 
   479     def _writerecordsv1(self, records):
   481     def _writerecordsv1(self, records):
   480         """Write current state on disk in a version 1 file"""
   482         """Write current state on disk in a version 1 file"""
   481         f = self._repo.vfs(self.statepathv1, 'wb')
   483         f = self._repo.vfs(self.statepathv1, b'wb')
   482         irecords = iter(records)
   484         irecords = iter(records)
   483         lrecords = next(irecords)
   485         lrecords = next(irecords)
   484         assert lrecords[0] == RECORD_LOCAL
   486         assert lrecords[0] == RECORD_LOCAL
   485         f.write(hex(self._local) + '\n')
   487         f.write(hex(self._local) + b'\n')
   486         for rtype, data in irecords:
   488         for rtype, data in irecords:
   487             if rtype == RECORD_MERGED:
   489             if rtype == RECORD_MERGED:
   488                 f.write('%s\n' % _droponode(data))
   490                 f.write(b'%s\n' % _droponode(data))
   489         f.close()
   491         f.close()
   490 
   492 
   491     def _writerecordsv2(self, records):
   493     def _writerecordsv2(self, records):
   492         """Write current state on disk in a version 2 file
   494         """Write current state on disk in a version 2 file
   493 
   495 
   494         See the docstring for _readrecordsv2 for why we use 't'."""
   496         See the docstring for _readrecordsv2 for why we use 't'."""
   495         # these are the records that all version 2 clients can read
   497         # these are the records that all version 2 clients can read
   496         allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
   498         allowlist = (RECORD_LOCAL, RECORD_OTHER, RECORD_MERGED)
   497         f = self._repo.vfs(self.statepathv2, 'wb')
   499         f = self._repo.vfs(self.statepathv2, b'wb')
   498         for key, data in records:
   500         for key, data in records:
   499             assert len(key) == 1
   501             assert len(key) == 1
   500             if key not in allowlist:
   502             if key not in allowlist:
   501                 key, data = RECORD_OVERRIDE, '%s%s' % (key, data)
   503                 key, data = RECORD_OVERRIDE, b'%s%s' % (key, data)
   502             format = '>sI%is' % len(data)
   504             format = b'>sI%is' % len(data)
   503             f.write(_pack(format, key, len(data), data))
   505             f.write(_pack(format, key, len(data), data))
   504         f.close()
   506         f.close()
   505 
   507 
   506     @staticmethod
   508     @staticmethod
   507     def getlocalkey(path):
   509     def getlocalkey(path):
   521         """
   523         """
   522         if fcl.isabsent():
   524         if fcl.isabsent():
   523             localkey = nullhex
   525             localkey = nullhex
   524         else:
   526         else:
   525             localkey = mergestate.getlocalkey(fcl.path())
   527             localkey = mergestate.getlocalkey(fcl.path())
   526             self._repo.vfs.write('merge/' + localkey, fcl.data())
   528             self._repo.vfs.write(b'merge/' + localkey, fcl.data())
   527         self._state[fd] = [
   529         self._state[fd] = [
   528             MERGE_RECORD_UNRESOLVED,
   530             MERGE_RECORD_UNRESOLVED,
   529             localkey,
   531             localkey,
   530             fcl.path(),
   532             fcl.path(),
   531             fca.path(),
   533             fca.path(),
   532             hex(fca.filenode()),
   534             hex(fca.filenode()),
   533             fco.path(),
   535             fco.path(),
   534             hex(fco.filenode()),
   536             hex(fco.filenode()),
   535             fcl.flags(),
   537             fcl.flags(),
   536         ]
   538         ]
   537         self._stateextras[fd] = {'ancestorlinknode': hex(fca.node())}
   539         self._stateextras[fd] = {b'ancestorlinknode': hex(fca.node())}
   538         self._dirty = True
   540         self._dirty = True
   539 
   541 
   540     def addpath(self, path, frename, forigin):
   542     def addpath(self, path, frename, forigin):
   541         """add a new conflicting path to the merge state
   543         """add a new conflicting path to the merge state
   542         path:    the path that conflicts
   544         path:    the path that conflicts
   591             return True, 0
   593             return True, 0
   592         stateentry = self._state[dfile]
   594         stateentry = self._state[dfile]
   593         state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
   595         state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
   594         octx = self._repo[self._other]
   596         octx = self._repo[self._other]
   595         extras = self.extras(dfile)
   597         extras = self.extras(dfile)
   596         anccommitnode = extras.get('ancestorlinknode')
   598         anccommitnode = extras.get(b'ancestorlinknode')
   597         if anccommitnode:
   599         if anccommitnode:
   598             actx = self._repo[anccommitnode]
   600             actx = self._repo[anccommitnode]
   599         else:
   601         else:
   600             actx = None
   602             actx = None
   601         fcd = self._filectxorabsent(localkey, wctx, dfile)
   603         fcd = self._filectxorabsent(localkey, wctx, dfile)
   603         # TODO: move this to filectxorabsent
   605         # TODO: move this to filectxorabsent
   604         fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
   606         fca = self._repo.filectx(afile, fileid=anode, changectx=actx)
   605         # "premerge" x flags
   607         # "premerge" x flags
   606         flo = fco.flags()
   608         flo = fco.flags()
   607         fla = fca.flags()
   609         fla = fca.flags()
   608         if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
   610         if b'x' in flags + flo + fla and b'l' not in flags + flo + fla:
   609             if fca.node() == nullid and flags != flo:
   611             if fca.node() == nullid and flags != flo:
   610                 if preresolve:
   612                 if preresolve:
   611                     self._repo.ui.warn(
   613                     self._repo.ui.warn(
   612                         _(
   614                         _(
   613                             'warning: cannot merge flags for %s '
   615                             b'warning: cannot merge flags for %s '
   614                             'without common ancestor - keeping local flags\n'
   616                             b'without common ancestor - keeping local flags\n'
   615                         )
   617                         )
   616                         % afile
   618                         % afile
   617                     )
   619                     )
   618             elif flags == fla:
   620             elif flags == fla:
   619                 flags = flo
   621                 flags = flo
   620         if preresolve:
   622         if preresolve:
   621             # restore local
   623             # restore local
   622             if localkey != nullhex:
   624             if localkey != nullhex:
   623                 f = self._repo.vfs('merge/' + localkey)
   625                 f = self._repo.vfs(b'merge/' + localkey)
   624                 wctx[dfile].write(f.read(), flags)
   626                 wctx[dfile].write(f.read(), flags)
   625                 f.close()
   627                 f.close()
   626             else:
   628             else:
   627                 wctx[dfile].remove(ignoremissing=True)
   629                 wctx[dfile].remove(ignoremissing=True)
   628             complete, r, deleted = filemerge.premerge(
   630             complete, r, deleted = filemerge.premerge(
   722             ACTION_ADD_MODIFIED: [],
   724             ACTION_ADD_MODIFIED: [],
   723             ACTION_GET: [],
   725             ACTION_GET: [],
   724         }
   726         }
   725         for f, (r, action) in self._results.iteritems():
   727         for f, (r, action) in self._results.iteritems():
   726             if action is not None:
   728             if action is not None:
   727                 actions[action].append((f, None, "merge result"))
   729                 actions[action].append((f, None, b"merge result"))
   728         return actions
   730         return actions
   729 
   731 
   730     def recordactions(self):
   732     def recordactions(self):
   731         """record remove/add/get actions in the dirstate"""
   733         """record remove/add/get actions in the dirstate"""
   732         branchmerge = self._repo.dirstate.p2() != nullid
   734         branchmerge = self._repo.dirstate.p2() != nullid
   751         self._results[f] = 0, ACTION_GET
   753         self._results[f] = 0, ACTION_GET
   752 
   754 
   753 
   755 
   754 def _getcheckunknownconfig(repo, section, name):
   756 def _getcheckunknownconfig(repo, section, name):
   755     config = repo.ui.config(section, name)
   757     config = repo.ui.config(section, name)
   756     valid = ['abort', 'ignore', 'warn']
   758     valid = [b'abort', b'ignore', b'warn']
   757     if config not in valid:
   759     if config not in valid:
   758         validstr = ', '.join(["'" + v + "'" for v in valid])
   760         validstr = b', '.join([b"'" + v + b"'" for v in valid])
   759         raise error.ConfigError(
   761         raise error.ConfigError(
   760             _("%s.%s not valid " "('%s' is none of %s)")
   762             _(b"%s.%s not valid " b"('%s' is none of %s)")
   761             % (section, name, config, validstr)
   763             % (section, name, config, validstr)
   762         )
   764         )
   763     return config
   765     return config
   764 
   766 
   765 
   767 
   846     """
   848     """
   847     fileconflicts = set()
   849     fileconflicts = set()
   848     pathconflicts = set()
   850     pathconflicts = set()
   849     warnconflicts = set()
   851     warnconflicts = set()
   850     abortconflicts = set()
   852     abortconflicts = set()
   851     unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
   853     unknownconfig = _getcheckunknownconfig(repo, b'merge', b'checkunknown')
   852     ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
   854     ignoredconfig = _getcheckunknownconfig(repo, b'merge', b'checkignored')
   853     pathconfig = repo.ui.configbool('experimental', 'merge.checkpathconflicts')
   855     pathconfig = repo.ui.configbool(
       
   856         b'experimental', b'merge.checkpathconflicts'
       
   857     )
   854     if not force:
   858     if not force:
   855 
   859 
   856         def collectconflicts(conflicts, config):
   860         def collectconflicts(conflicts, config):
   857             if config == 'abort':
   861             if config == b'abort':
   858                 abortconflicts.update(conflicts)
   862                 abortconflicts.update(conflicts)
   859             elif config == 'warn':
   863             elif config == b'warn':
   860                 warnconflicts.update(conflicts)
   864                 warnconflicts.update(conflicts)
   861 
   865 
   862         checkunknowndirs = _unknowndirschecker()
   866         checkunknowndirs = _unknowndirschecker()
   863         for f, (m, args, msg) in actions.iteritems():
   867         for f, (m, args, msg) in actions.iteritems():
   864             if m in (ACTION_CREATED, ACTION_DELETED_CHANGED):
   868             if m in (ACTION_CREATED, ACTION_DELETED_CHANGED):
   898                 # (1) this is probably the wrong behavior here -- we should
   902                 # (1) this is probably the wrong behavior here -- we should
   899                 #     probably abort, but some actions like rebases currently
   903                 #     probably abort, but some actions like rebases currently
   900                 #     don't like an abort happening in the middle of
   904                 #     don't like an abort happening in the middle of
   901                 #     merge.update.
   905                 #     merge.update.
   902                 if not different:
   906                 if not different:
   903                     actions[f] = (ACTION_GET, (fl2, False), 'remote created')
   907                     actions[f] = (ACTION_GET, (fl2, False), b'remote created')
   904                 elif mergeforce or config == 'abort':
   908                 elif mergeforce or config == b'abort':
   905                     actions[f] = (
   909                     actions[f] = (
   906                         ACTION_MERGE,
   910                         ACTION_MERGE,
   907                         (f, f, None, False, anc),
   911                         (f, f, None, False, anc),
   908                         'remote differs from untracked local',
   912                         b'remote differs from untracked local',
   909                     )
   913                     )
   910                 elif config == 'abort':
   914                 elif config == b'abort':
   911                     abortconflicts.add(f)
   915                     abortconflicts.add(f)
   912                 else:
   916                 else:
   913                     if config == 'warn':
   917                     if config == b'warn':
   914                         warnconflicts.add(f)
   918                         warnconflicts.add(f)
   915                     actions[f] = (ACTION_GET, (fl2, True), 'remote created')
   919                     actions[f] = (ACTION_GET, (fl2, True), b'remote created')
   916 
   920 
   917     for f in sorted(abortconflicts):
   921     for f in sorted(abortconflicts):
   918         warn = repo.ui.warn
   922         warn = repo.ui.warn
   919         if f in pathconflicts:
   923         if f in pathconflicts:
   920             if repo.wvfs.isfileorlink(f):
   924             if repo.wvfs.isfileorlink(f):
   921                 warn(_("%s: untracked file conflicts with directory\n") % f)
   925                 warn(_(b"%s: untracked file conflicts with directory\n") % f)
   922             else:
   926             else:
   923                 warn(_("%s: untracked directory conflicts with file\n") % f)
   927                 warn(_(b"%s: untracked directory conflicts with file\n") % f)
   924         else:
   928         else:
   925             warn(_("%s: untracked file differs\n") % f)
   929             warn(_(b"%s: untracked file differs\n") % f)
   926     if abortconflicts:
   930     if abortconflicts:
   927         raise error.Abort(
   931         raise error.Abort(
   928             _(
   932             _(
   929                 "untracked files in working directory "
   933                 b"untracked files in working directory "
   930                 "differ from files in requested revision"
   934                 b"differ from files in requested revision"
   931             )
   935             )
   932         )
   936         )
   933 
   937 
   934     for f in sorted(warnconflicts):
   938     for f in sorted(warnconflicts):
   935         if repo.wvfs.isfileorlink(f):
   939         if repo.wvfs.isfileorlink(f):
   936             repo.ui.warn(_("%s: replacing untracked file\n") % f)
   940             repo.ui.warn(_(b"%s: replacing untracked file\n") % f)
   937         else:
   941         else:
   938             repo.ui.warn(_("%s: replacing untracked files in directory\n") % f)
   942             repo.ui.warn(_(b"%s: replacing untracked files in directory\n") % f)
   939 
   943 
   940     for f, (m, args, msg) in actions.iteritems():
   944     for f, (m, args, msg) in actions.iteritems():
   941         if m == ACTION_CREATED:
   945         if m == ACTION_CREATED:
   942             backup = (
   946             backup = (
   943                 f in fileconflicts
   947                 f in fileconflicts
   967     m = ACTION_FORGET
   971     m = ACTION_FORGET
   968     if branchmerge:
   972     if branchmerge:
   969         m = ACTION_REMOVE
   973         m = ACTION_REMOVE
   970     for f in wctx.deleted():
   974     for f in wctx.deleted():
   971         if f not in mctx:
   975         if f not in mctx:
   972             actions[f] = m, None, "forget deleted"
   976             actions[f] = m, None, b"forget deleted"
   973 
   977 
   974     if not branchmerge:
   978     if not branchmerge:
   975         for f in wctx.removed():
   979         for f in wctx.removed():
   976             if f not in mctx:
   980             if f not in mctx:
   977                 actions[f] = ACTION_FORGET, None, "forget removed"
   981                 actions[f] = ACTION_FORGET, None, b"forget removed"
   978 
   982 
   979     return actions
   983     return actions
   980 
   984 
   981 
   985 
   982 def _checkcollision(repo, wmf, actions):
   986 def _checkcollision(repo, wmf, actions):
  1030     foldmap = {}
  1034     foldmap = {}
  1031     for f in pmmf:
  1035     for f in pmmf:
  1032         fold = util.normcase(f)
  1036         fold = util.normcase(f)
  1033         if fold in foldmap:
  1037         if fold in foldmap:
  1034             raise error.Abort(
  1038             raise error.Abort(
  1035                 _("case-folding collision between %s and %s")
  1039                 _(b"case-folding collision between %s and %s")
  1036                 % (f, foldmap[fold])
  1040                 % (f, foldmap[fold])
  1037             )
  1041             )
  1038         foldmap[fold] = f
  1042         foldmap[fold] = f
  1039 
  1043 
  1040     # check case-folding of directories
  1044     # check case-folding of directories
  1041     foldprefix = unfoldprefix = lastfull = ''
  1045     foldprefix = unfoldprefix = lastfull = b''
  1042     for fold, f in sorted(foldmap.items()):
  1046     for fold, f in sorted(foldmap.items()):
  1043         if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
  1047         if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
  1044             # the folded prefix matches but actual casing is different
  1048             # the folded prefix matches but actual casing is different
  1045             raise error.Abort(
  1049             raise error.Abort(
  1046                 _("case-folding collision between " "%s and directory of %s")
  1050                 _(b"case-folding collision between " b"%s and directory of %s")
  1047                 % (lastfull, f)
  1051                 % (lastfull, f)
  1048             )
  1052             )
  1049         foldprefix = fold + '/'
  1053         foldprefix = fold + b'/'
  1050         unfoldprefix = f + '/'
  1054         unfoldprefix = f + b'/'
  1051         lastfull = f
  1055         lastfull = f
  1052 
  1056 
  1053 
  1057 
  1054 def driverpreprocess(repo, ms, wctx, labels=None):
  1058 def driverpreprocess(repo, ms, wctx, labels=None):
  1055     """run the preprocess step of the merge driver, if any
  1059     """run the preprocess step of the merge driver, if any
  1152             invalidconflicts.add(p)
  1156             invalidconflicts.add(p)
  1153 
  1157 
  1154     # Rename all local conflicting files that have not been deleted.
  1158     # Rename all local conflicting files that have not been deleted.
  1155     for p in localconflicts:
  1159     for p in localconflicts:
  1156         if p not in deletedfiles:
  1160         if p not in deletedfiles:
  1157             ctxname = bytes(wctx).rstrip('+')
  1161             ctxname = bytes(wctx).rstrip(b'+')
  1158             pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
  1162             pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
  1159             actions[pnew] = (
  1163             actions[pnew] = (
  1160                 ACTION_PATH_CONFLICT_RESOLVE,
  1164                 ACTION_PATH_CONFLICT_RESOLVE,
  1161                 (p,),
  1165                 (p,),
  1162                 'local path conflict',
  1166                 b'local path conflict',
  1163             )
  1167             )
  1164             actions[p] = (ACTION_PATH_CONFLICT, (pnew, 'l'), 'path conflict')
  1168             actions[p] = (ACTION_PATH_CONFLICT, (pnew, b'l'), b'path conflict')
  1165 
  1169 
  1166     if remoteconflicts:
  1170     if remoteconflicts:
  1167         # Check if all files in the conflicting directories have been removed.
  1171         # Check if all files in the conflicting directories have been removed.
  1168         ctxname = bytes(mctx).rstrip('+')
  1172         ctxname = bytes(mctx).rstrip(b'+')
  1169         for f, p in _filesindirs(repo, mf, remoteconflicts):
  1173         for f, p in _filesindirs(repo, mf, remoteconflicts):
  1170             if f not in deletedfiles:
  1174             if f not in deletedfiles:
  1171                 m, args, msg = actions[p]
  1175                 m, args, msg = actions[p]
  1172                 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
  1176                 pnew = util.safename(p, ctxname, wctx, set(actions.keys()))
  1173                 if m in (ACTION_DELETED_CHANGED, ACTION_MERGE):
  1177                 if m in (ACTION_DELETED_CHANGED, ACTION_MERGE):
  1177                     # Action was create, change to renamed get action.
  1181                     # Action was create, change to renamed get action.
  1178                     fl = args[0]
  1182                     fl = args[0]
  1179                     actions[pnew] = (
  1183                     actions[pnew] = (
  1180                         ACTION_LOCAL_DIR_RENAME_GET,
  1184                         ACTION_LOCAL_DIR_RENAME_GET,
  1181                         (p, fl),
  1185                         (p, fl),
  1182                         'remote path conflict',
  1186                         b'remote path conflict',
  1183                     )
  1187                     )
  1184                 actions[p] = (
  1188                 actions[p] = (
  1185                     ACTION_PATH_CONFLICT,
  1189                     ACTION_PATH_CONFLICT,
  1186                     (pnew, ACTION_REMOVE),
  1190                     (pnew, ACTION_REMOVE),
  1187                     'path conflict',
  1191                     b'path conflict',
  1188                 )
  1192                 )
  1189                 remoteconflicts.remove(p)
  1193                 remoteconflicts.remove(p)
  1190                 break
  1194                 break
  1191 
  1195 
  1192     if invalidconflicts:
  1196     if invalidconflicts:
  1193         for p in invalidconflicts:
  1197         for p in invalidconflicts:
  1194             repo.ui.warn(_("%s: is both a file and a directory\n") % p)
  1198             repo.ui.warn(_(b"%s: is both a file and a directory\n") % p)
  1195         raise error.Abort(_("destination manifest contains path conflicts"))
  1199         raise error.Abort(_(b"destination manifest contains path conflicts"))
  1196 
  1200 
  1197 
  1201 
  1198 def _filternarrowactions(narrowmatch, branchmerge, actions):
  1202 def _filternarrowactions(narrowmatch, branchmerge, actions):
  1199     """
  1203     """
  1200     Filters out actions that can ignored because the repo is narrowed.
  1204     Filters out actions that can ignored because the repo is narrowed.
  1201 
  1205 
  1202     Raise an exception if the merge cannot be completed because the repo is
  1206     Raise an exception if the merge cannot be completed because the repo is
  1203     narrowed.
  1207     narrowed.
  1204     """
  1208     """
  1205     nooptypes = {'k'}  # TODO: handle with nonconflicttypes
  1209     nooptypes = {b'k'}  # TODO: handle with nonconflicttypes
  1206     nonconflicttypes = set('a am c cm f g r e'.split())
  1210     nonconflicttypes = set(b'a am c cm f g r e'.split())
  1207     # We mutate the items in the dict during iteration, so iterate
  1211     # We mutate the items in the dict during iteration, so iterate
  1208     # over a copy.
  1212     # over a copy.
  1209     for f, action in list(actions.items()):
  1213     for f, action in list(actions.items()):
  1210         if narrowmatch(f):
  1214         if narrowmatch(f):
  1211             pass
  1215             pass
  1214         elif action[0] in nooptypes:
  1218         elif action[0] in nooptypes:
  1215             del actions[f]  # merge does not affect file
  1219             del actions[f]  # merge does not affect file
  1216         elif action[0] in nonconflicttypes:
  1220         elif action[0] in nonconflicttypes:
  1217             raise error.Abort(
  1221             raise error.Abort(
  1218                 _(
  1222                 _(
  1219                     'merge affects file \'%s\' outside narrow, '
  1223                     b'merge affects file \'%s\' outside narrow, '
  1220                     'which is not yet supported'
  1224                     b'which is not yet supported'
  1221                 )
  1225                 )
  1222                 % f,
  1226                 % f,
  1223                 hint=_('merging in the other direction ' 'may work'),
  1227                 hint=_(b'merging in the other direction ' b'may work'),
  1224             )
  1228             )
  1225         else:
  1229         else:
  1226             raise error.Abort(
  1230             raise error.Abort(
  1227                 _('conflict in file \'%s\' is outside ' 'narrow clone') % f
  1231                 _(b'conflict in file \'%s\' is outside ' b'narrow clone') % f
  1228             )
  1232             )
  1229 
  1233 
  1230 
  1234 
  1231 def manifestmerge(
  1235 def manifestmerge(
  1232     repo,
  1236     repo,
  1263         copy, movewithdir, diverge, renamedelete, dirmove = ret
  1267         copy, movewithdir, diverge, renamedelete, dirmove = ret
  1264 
  1268 
  1265     boolbm = pycompat.bytestr(bool(branchmerge))
  1269     boolbm = pycompat.bytestr(bool(branchmerge))
  1266     boolf = pycompat.bytestr(bool(force))
  1270     boolf = pycompat.bytestr(bool(force))
  1267     boolm = pycompat.bytestr(bool(matcher))
  1271     boolm = pycompat.bytestr(bool(matcher))
  1268     repo.ui.note(_("resolving manifests\n"))
  1272     repo.ui.note(_(b"resolving manifests\n"))
  1269     repo.ui.debug(
  1273     repo.ui.debug(
  1270         " branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm)
  1274         b" branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm)
  1271     )
  1275     )
  1272     repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
  1276     repo.ui.debug(b" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
  1273 
  1277 
  1274     m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
  1278     m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
  1275     copied = set(copy.values())
  1279     copied = set(copy.values())
  1276     copied.update(movewithdir.values())
  1280     copied.update(movewithdir.values())
  1277 
  1281 
  1278     if '.hgsubstate' in m1 and wctx.rev() is None:
  1282     if b'.hgsubstate' in m1 and wctx.rev() is None:
  1279         # Check whether sub state is modified, and overwrite the manifest
  1283         # Check whether sub state is modified, and overwrite the manifest
  1280         # to flag the change. If wctx is a committed revision, we shouldn't
  1284         # to flag the change. If wctx is a committed revision, we shouldn't
  1281         # care for the dirty state of the working directory.
  1285         # care for the dirty state of the working directory.
  1282         if any(wctx.sub(s).dirty() for s in wctx.substate):
  1286         if any(wctx.sub(s).dirty() for s in wctx.substate):
  1283             m1['.hgsubstate'] = modifiednodeid
  1287             m1[b'.hgsubstate'] = modifiednodeid
  1284 
  1288 
  1285     # Don't use m2-vs-ma optimization if:
  1289     # Don't use m2-vs-ma optimization if:
  1286     # - ma is the same as m1 or m2, which we're just going to diff again later
  1290     # - ma is the same as m1 or m2, which we're just going to diff again later
  1287     # - The caller specifically asks for a full diff, which is useful during bid
  1291     # - The caller specifically asks for a full diff, which is useful during bid
  1288     #   merge.
  1292     #   merge.
  1310                 fa = copy.get(f, None)
  1314                 fa = copy.get(f, None)
  1311                 if fa is not None:
  1315                 if fa is not None:
  1312                     actions[f] = (
  1316                     actions[f] = (
  1313                         ACTION_MERGE,
  1317                         ACTION_MERGE,
  1314                         (f, f, fa, False, pa.node()),
  1318                         (f, f, fa, False, pa.node()),
  1315                         'both renamed from %s' % fa,
  1319                         b'both renamed from %s' % fa,
  1316                     )
  1320                     )
  1317                 else:
  1321                 else:
  1318                     actions[f] = (
  1322                     actions[f] = (
  1319                         ACTION_MERGE,
  1323                         ACTION_MERGE,
  1320                         (f, f, None, False, pa.node()),
  1324                         (f, f, None, False, pa.node()),
  1321                         'both created',
  1325                         b'both created',
  1322                     )
  1326                     )
  1323             else:
  1327             else:
  1324                 a = ma[f]
  1328                 a = ma[f]
  1325                 fla = ma.flags(f)
  1329                 fla = ma.flags(f)
  1326                 nol = 'l' not in fl1 + fl2 + fla
  1330                 nol = b'l' not in fl1 + fl2 + fla
  1327                 if n2 == a and fl2 == fla:
  1331                 if n2 == a and fl2 == fla:
  1328                     actions[f] = (ACTION_KEEP, (), 'remote unchanged')
  1332                     actions[f] = (ACTION_KEEP, (), b'remote unchanged')
  1329                 elif n1 == a and fl1 == fla:  # local unchanged - use remote
  1333                 elif n1 == a and fl1 == fla:  # local unchanged - use remote
  1330                     if n1 == n2:  # optimization: keep local content
  1334                     if n1 == n2:  # optimization: keep local content
  1331                         actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
  1335                         actions[f] = (
       
  1336                             ACTION_EXEC,
       
  1337                             (fl2,),
       
  1338                             b'update permissions',
       
  1339                         )
  1332                     else:
  1340                     else:
  1333                         actions[f] = (
  1341                         actions[f] = (
  1334                             ACTION_GET,
  1342                             ACTION_GET,
  1335                             (fl2, False),
  1343                             (fl2, False),
  1336                             'remote is newer',
  1344                             b'remote is newer',
  1337                         )
  1345                         )
  1338                 elif nol and n2 == a:  # remote only changed 'x'
  1346                 elif nol and n2 == a:  # remote only changed 'x'
  1339                     actions[f] = (ACTION_EXEC, (fl2,), 'update permissions')
  1347                     actions[f] = (ACTION_EXEC, (fl2,), b'update permissions')
  1340                 elif nol and n1 == a:  # local only changed 'x'
  1348                 elif nol and n1 == a:  # local only changed 'x'
  1341                     actions[f] = (ACTION_GET, (fl1, False), 'remote is newer')
  1349                     actions[f] = (ACTION_GET, (fl1, False), b'remote is newer')
  1342                 else:  # both changed something
  1350                 else:  # both changed something
  1343                     actions[f] = (
  1351                     actions[f] = (
  1344                         ACTION_MERGE,
  1352                         ACTION_MERGE,
  1345                         (f, f, f, False, pa.node()),
  1353                         (f, f, f, False, pa.node()),
  1346                         'versions differ',
  1354                         b'versions differ',
  1347                     )
  1355                     )
  1348         elif n1:  # file exists only on local side
  1356         elif n1:  # file exists only on local side
  1349             if f in copied:
  1357             if f in copied:
  1350                 pass  # we'll deal with it on m2 side
  1358                 pass  # we'll deal with it on m2 side
  1351             elif f in movewithdir:  # directory rename, move local
  1359             elif f in movewithdir:  # directory rename, move local
  1352                 f2 = movewithdir[f]
  1360                 f2 = movewithdir[f]
  1353                 if f2 in m2:
  1361                 if f2 in m2:
  1354                     actions[f2] = (
  1362                     actions[f2] = (
  1355                         ACTION_MERGE,
  1363                         ACTION_MERGE,
  1356                         (f, f2, None, True, pa.node()),
  1364                         (f, f2, None, True, pa.node()),
  1357                         'remote directory rename, both created',
  1365                         b'remote directory rename, both created',
  1358                     )
  1366                     )
  1359                 else:
  1367                 else:
  1360                     actions[f2] = (
  1368                     actions[f2] = (
  1361                         ACTION_DIR_RENAME_MOVE_LOCAL,
  1369                         ACTION_DIR_RENAME_MOVE_LOCAL,
  1362                         (f, fl1),
  1370                         (f, fl1),
  1363                         'remote directory rename - move from %s' % f,
  1371                         b'remote directory rename - move from %s' % f,
  1364                     )
  1372                     )
  1365             elif f in copy:
  1373             elif f in copy:
  1366                 f2 = copy[f]
  1374                 f2 = copy[f]
  1367                 actions[f] = (
  1375                 actions[f] = (
  1368                     ACTION_MERGE,
  1376                     ACTION_MERGE,
  1369                     (f, f2, f2, False, pa.node()),
  1377                     (f, f2, f2, False, pa.node()),
  1370                     'local copied/moved from %s' % f2,
  1378                     b'local copied/moved from %s' % f2,
  1371                 )
  1379                 )
  1372             elif f in ma:  # clean, a different, no remote
  1380             elif f in ma:  # clean, a different, no remote
  1373                 if n1 != ma[f]:
  1381                 if n1 != ma[f]:
  1374                     if acceptremote:
  1382                     if acceptremote:
  1375                         actions[f] = (ACTION_REMOVE, None, 'remote delete')
  1383                         actions[f] = (ACTION_REMOVE, None, b'remote delete')
  1376                     else:
  1384                     else:
  1377                         actions[f] = (
  1385                         actions[f] = (
  1378                             ACTION_CHANGED_DELETED,
  1386                             ACTION_CHANGED_DELETED,
  1379                             (f, None, f, False, pa.node()),
  1387                             (f, None, f, False, pa.node()),
  1380                             'prompt changed/deleted',
  1388                             b'prompt changed/deleted',
  1381                         )
  1389                         )
  1382                 elif n1 == addednodeid:
  1390                 elif n1 == addednodeid:
  1383                     # This extra 'a' is added by working copy manifest to mark
  1391                     # This extra 'a' is added by working copy manifest to mark
  1384                     # the file as locally added. We should forget it instead of
  1392                     # the file as locally added. We should forget it instead of
  1385                     # deleting it.
  1393                     # deleting it.
  1386                     actions[f] = (ACTION_FORGET, None, 'remote deleted')
  1394                     actions[f] = (ACTION_FORGET, None, b'remote deleted')
  1387                 else:
  1395                 else:
  1388                     actions[f] = (ACTION_REMOVE, None, 'other deleted')
  1396                     actions[f] = (ACTION_REMOVE, None, b'other deleted')
  1389         elif n2:  # file exists only on remote side
  1397         elif n2:  # file exists only on remote side
  1390             if f in copied:
  1398             if f in copied:
  1391                 pass  # we'll deal with it on m1 side
  1399                 pass  # we'll deal with it on m1 side
  1392             elif f in movewithdir:
  1400             elif f in movewithdir:
  1393                 f2 = movewithdir[f]
  1401                 f2 = movewithdir[f]
  1394                 if f2 in m1:
  1402                 if f2 in m1:
  1395                     actions[f2] = (
  1403                     actions[f2] = (
  1396                         ACTION_MERGE,
  1404                         ACTION_MERGE,
  1397                         (f2, f, None, False, pa.node()),
  1405                         (f2, f, None, False, pa.node()),
  1398                         'local directory rename, both created',
  1406                         b'local directory rename, both created',
  1399                     )
  1407                     )
  1400                 else:
  1408                 else:
  1401                     actions[f2] = (
  1409                     actions[f2] = (
  1402                         ACTION_LOCAL_DIR_RENAME_GET,
  1410                         ACTION_LOCAL_DIR_RENAME_GET,
  1403                         (f, fl2),
  1411                         (f, fl2),
  1404                         'local directory rename - get from %s' % f,
  1412                         b'local directory rename - get from %s' % f,
  1405                     )
  1413                     )
  1406             elif f in copy:
  1414             elif f in copy:
  1407                 f2 = copy[f]
  1415                 f2 = copy[f]
  1408                 if f2 in m2:
  1416                 if f2 in m2:
  1409                     actions[f] = (
  1417                     actions[f] = (
  1410                         ACTION_MERGE,
  1418                         ACTION_MERGE,
  1411                         (f2, f, f2, False, pa.node()),
  1419                         (f2, f, f2, False, pa.node()),
  1412                         'remote copied from %s' % f2,
  1420                         b'remote copied from %s' % f2,
  1413                     )
  1421                     )
  1414                 else:
  1422                 else:
  1415                     actions[f] = (
  1423                     actions[f] = (
  1416                         ACTION_MERGE,
  1424                         ACTION_MERGE,
  1417                         (f2, f, f2, True, pa.node()),
  1425                         (f2, f, f2, True, pa.node()),
  1418                         'remote moved from %s' % f2,
  1426                         b'remote moved from %s' % f2,
  1419                     )
  1427                     )
  1420             elif f not in ma:
  1428             elif f not in ma:
  1421                 # local unknown, remote created: the logic is described by the
  1429                 # local unknown, remote created: the logic is described by the
  1422                 # following table:
  1430                 # following table:
  1423                 #
  1431                 #
  1428                 #   y         y           y      |   merge
  1436                 #   y         y           y      |   merge
  1429                 #
  1437                 #
  1430                 # Checking whether the files are different is expensive, so we
  1438                 # Checking whether the files are different is expensive, so we
  1431                 # don't do that when we can avoid it.
  1439                 # don't do that when we can avoid it.
  1432                 if not force:
  1440                 if not force:
  1433                     actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
  1441                     actions[f] = (ACTION_CREATED, (fl2,), b'remote created')
  1434                 elif not branchmerge:
  1442                 elif not branchmerge:
  1435                     actions[f] = (ACTION_CREATED, (fl2,), 'remote created')
  1443                     actions[f] = (ACTION_CREATED, (fl2,), b'remote created')
  1436                 else:
  1444                 else:
  1437                     actions[f] = (
  1445                     actions[f] = (
  1438                         ACTION_CREATED_MERGE,
  1446                         ACTION_CREATED_MERGE,
  1439                         (fl2, pa.node()),
  1447                         (fl2, pa.node()),
  1440                         'remote created, get or merge',
  1448                         b'remote created, get or merge',
  1441                     )
  1449                     )
  1442             elif n2 != ma[f]:
  1450             elif n2 != ma[f]:
  1443                 df = None
  1451                 df = None
  1444                 for d in dirmove:
  1452                 for d in dirmove:
  1445                     if f.startswith(d):
  1453                     if f.startswith(d):
  1448                         break
  1456                         break
  1449                 if df is not None and df in m1:
  1457                 if df is not None and df in m1:
  1450                     actions[df] = (
  1458                     actions[df] = (
  1451                         ACTION_MERGE,
  1459                         ACTION_MERGE,
  1452                         (df, f, f, False, pa.node()),
  1460                         (df, f, f, False, pa.node()),
  1453                         'local directory rename - respect move ' 'from %s' % f,
  1461                         b'local directory rename - respect move '
       
  1462                         b'from %s' % f,
  1454                     )
  1463                     )
  1455                 elif acceptremote:
  1464                 elif acceptremote:
  1456                     actions[f] = (ACTION_CREATED, (fl2,), 'remote recreating')
  1465                     actions[f] = (ACTION_CREATED, (fl2,), b'remote recreating')
  1457                 else:
  1466                 else:
  1458                     actions[f] = (
  1467                     actions[f] = (
  1459                         ACTION_DELETED_CHANGED,
  1468                         ACTION_DELETED_CHANGED,
  1460                         (None, f, f, False, pa.node()),
  1469                         (None, f, f, False, pa.node()),
  1461                         'prompt deleted/changed',
  1470                         b'prompt deleted/changed',
  1462                     )
  1471                     )
  1463 
  1472 
  1464     if repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
  1473     if repo.ui.configbool(b'experimental', b'merge.checkpathconflicts'):
  1465         # If we are merging, look for path conflicts.
  1474         # If we are merging, look for path conflicts.
  1466         checkpathconflicts(repo, wctx, p2, actions)
  1475         checkpathconflicts(repo, wctx, p2, actions)
  1467 
  1476 
  1468     narrowmatch = repo.narrowmatch()
  1477     narrowmatch = repo.narrowmatch()
  1469     if not narrowmatch.always():
  1478     if not narrowmatch.always():
  1483             m == ACTION_CHANGED_DELETED
  1492             m == ACTION_CHANGED_DELETED
  1484             and f in ancestor
  1493             and f in ancestor
  1485             and not wctx[f].cmp(ancestor[f])
  1494             and not wctx[f].cmp(ancestor[f])
  1486         ):
  1495         ):
  1487             # local did change but ended up with same content
  1496             # local did change but ended up with same content
  1488             actions[f] = ACTION_REMOVE, None, 'prompt same'
  1497             actions[f] = ACTION_REMOVE, None, b'prompt same'
  1489         elif (
  1498         elif (
  1490             m == ACTION_DELETED_CHANGED
  1499             m == ACTION_DELETED_CHANGED
  1491             and f in ancestor
  1500             and f in ancestor
  1492             and not mctx[f].cmp(ancestor[f])
  1501             and not mctx[f].cmp(ancestor[f])
  1493         ):
  1502         ):
  1525         )
  1534         )
  1526         _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
  1535         _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
  1527 
  1536 
  1528     else:  # only when merge.preferancestor=* - the default
  1537     else:  # only when merge.preferancestor=* - the default
  1529         repo.ui.note(
  1538         repo.ui.note(
  1530             _("note: merging %s and %s using bids from ancestors %s\n")
  1539             _(b"note: merging %s and %s using bids from ancestors %s\n")
  1531             % (
  1540             % (
  1532                 wctx,
  1541                 wctx,
  1533                 mctx,
  1542                 mctx,
  1534                 _(' and ').join(pycompat.bytestr(anc) for anc in ancestors),
  1543                 _(b' and ').join(pycompat.bytestr(anc) for anc in ancestors),
  1535             )
  1544             )
  1536         )
  1545         )
  1537 
  1546 
  1538         # Call for bids
  1547         # Call for bids
  1539         fbids = (
  1548         fbids = (
  1540             {}
  1549             {}
  1541         )  # mapping filename to bids (action method to list af actions)
  1550         )  # mapping filename to bids (action method to list af actions)
  1542         diverge, renamedelete = None, None
  1551         diverge, renamedelete = None, None
  1543         for ancestor in ancestors:
  1552         for ancestor in ancestors:
  1544             repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
  1553             repo.ui.note(_(b'\ncalculating bids for ancestor %s\n') % ancestor)
  1545             actions, diverge1, renamedelete1 = manifestmerge(
  1554             actions, diverge1, renamedelete1 = manifestmerge(
  1546                 repo,
  1555                 repo,
  1547                 wctx,
  1556                 wctx,
  1548                 mctx,
  1557                 mctx,
  1549                 ancestor,
  1558                 ancestor,
  1563             if renamedelete is None or len(renamedelete) < len(renamedelete1):
  1572             if renamedelete is None or len(renamedelete) < len(renamedelete1):
  1564                 renamedelete = renamedelete1
  1573                 renamedelete = renamedelete1
  1565 
  1574 
  1566             for f, a in sorted(actions.iteritems()):
  1575             for f, a in sorted(actions.iteritems()):
  1567                 m, args, msg = a
  1576                 m, args, msg = a
  1568                 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
  1577                 repo.ui.debug(b' %s: %s -> %s\n' % (f, msg, m))
  1569                 if f in fbids:
  1578                 if f in fbids:
  1570                     d = fbids[f]
  1579                     d = fbids[f]
  1571                     if m in d:
  1580                     if m in d:
  1572                         d[m].append(a)
  1581                         d[m].append(a)
  1573                     else:
  1582                     else:
  1574                         d[m] = [a]
  1583                         d[m] = [a]
  1575                 else:
  1584                 else:
  1576                     fbids[f] = {m: [a]}
  1585                     fbids[f] = {m: [a]}
  1577 
  1586 
  1578         # Pick the best bid for each file
  1587         # Pick the best bid for each file
  1579         repo.ui.note(_('\nauction for merging merge bids\n'))
  1588         repo.ui.note(_(b'\nauction for merging merge bids\n'))
  1580         actions = {}
  1589         actions = {}
  1581         for f, bids in sorted(fbids.items()):
  1590         for f, bids in sorted(fbids.items()):
  1582             # bids is a mapping from action method to list af actions
  1591             # bids is a mapping from action method to list af actions
  1583             # Consensus?
  1592             # Consensus?
  1584             if len(bids) == 1:  # all bids are the same kind of method
  1593             if len(bids) == 1:  # all bids are the same kind of method
  1585                 m, l = list(bids.items())[0]
  1594                 m, l = list(bids.items())[0]
  1586                 if all(a == l[0] for a in l[1:]):  # len(bids) is > 1
  1595                 if all(a == l[0] for a in l[1:]):  # len(bids) is > 1
  1587                     repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
  1596                     repo.ui.note(_(b" %s: consensus for %s\n") % (f, m))
  1588                     actions[f] = l[0]
  1597                     actions[f] = l[0]
  1589                     continue
  1598                     continue
  1590             # If keep is an option, just do it.
  1599             # If keep is an option, just do it.
  1591             if ACTION_KEEP in bids:
  1600             if ACTION_KEEP in bids:
  1592                 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
  1601                 repo.ui.note(_(b" %s: picking 'keep' action\n") % f)
  1593                 actions[f] = bids[ACTION_KEEP][0]
  1602                 actions[f] = bids[ACTION_KEEP][0]
  1594                 continue
  1603                 continue
  1595             # If there are gets and they all agree [how could they not?], do it.
  1604             # If there are gets and they all agree [how could they not?], do it.
  1596             if ACTION_GET in bids:
  1605             if ACTION_GET in bids:
  1597                 ga0 = bids[ACTION_GET][0]
  1606                 ga0 = bids[ACTION_GET][0]
  1598                 if all(a == ga0 for a in bids[ACTION_GET][1:]):
  1607                 if all(a == ga0 for a in bids[ACTION_GET][1:]):
  1599                     repo.ui.note(_(" %s: picking 'get' action\n") % f)
  1608                     repo.ui.note(_(b" %s: picking 'get' action\n") % f)
  1600                     actions[f] = ga0
  1609                     actions[f] = ga0
  1601                     continue
  1610                     continue
  1602             # TODO: Consider other simple actions such as mode changes
  1611             # TODO: Consider other simple actions such as mode changes
  1603             # Handle inefficient democrazy.
  1612             # Handle inefficient democrazy.
  1604             repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
  1613             repo.ui.note(_(b' %s: multiple bids for merge action:\n') % f)
  1605             for m, l in sorted(bids.items()):
  1614             for m, l in sorted(bids.items()):
  1606                 for _f, args, msg in l:
  1615                 for _f, args, msg in l:
  1607                     repo.ui.note('  %s -> %s\n' % (msg, m))
  1616                     repo.ui.note(b'  %s -> %s\n' % (msg, m))
  1608             # Pick random action. TODO: Instead, prompt user when resolving
  1617             # Pick random action. TODO: Instead, prompt user when resolving
  1609             m, l = list(bids.items())[0]
  1618             m, l = list(bids.items())[0]
  1610             repo.ui.warn(
  1619             repo.ui.warn(
  1611                 _(' %s: ambiguous merge - picked %s action\n') % (f, m)
  1620                 _(b' %s: ambiguous merge - picked %s action\n') % (f, m)
  1612             )
  1621             )
  1613             actions[f] = l[0]
  1622             actions[f] = l[0]
  1614             continue
  1623             continue
  1615         repo.ui.note(_('end of auction\n\n'))
  1624         repo.ui.note(_(b'end of auction\n\n'))
  1616 
  1625 
  1617     if wctx.rev() is None:
  1626     if wctx.rev() is None:
  1618         fractions = _forgetremoved(wctx, mctx, branchmerge)
  1627         fractions = _forgetremoved(wctx, mctx, branchmerge)
  1619         actions.update(fractions)
  1628         actions.update(fractions)
  1620 
  1629 
  1642     """
  1651     """
  1643     verbose = repo.ui.verbose
  1652     verbose = repo.ui.verbose
  1644     cwd = _getcwd()
  1653     cwd = _getcwd()
  1645     i = 0
  1654     i = 0
  1646     for f, args, msg in actions:
  1655     for f, args, msg in actions:
  1647         repo.ui.debug(" %s: %s -> r\n" % (f, msg))
  1656         repo.ui.debug(b" %s: %s -> r\n" % (f, msg))
  1648         if verbose:
  1657         if verbose:
  1649             repo.ui.note(_("removing %s\n") % f)
  1658             repo.ui.note(_(b"removing %s\n") % f)
  1650         wctx[f].audit()
  1659         wctx[f].audit()
  1651         try:
  1660         try:
  1652             wctx[f].remove(ignoremissing=True)
  1661             wctx[f].remove(ignoremissing=True)
  1653         except OSError as inst:
  1662         except OSError as inst:
  1654             repo.ui.warn(
  1663             repo.ui.warn(
  1655                 _("update failed to remove %s: %s!\n") % (f, inst.strerror)
  1664                 _(b"update failed to remove %s: %s!\n") % (f, inst.strerror)
  1656             )
  1665             )
  1657         if i == 100:
  1666         if i == 100:
  1658             yield i, f
  1667             yield i, f
  1659             i = 0
  1668             i = 0
  1660         i += 1
  1669         i += 1
  1664     if cwd and not _getcwd():
  1673     if cwd and not _getcwd():
  1665         # cwd was removed in the course of removing files; print a helpful
  1674         # cwd was removed in the course of removing files; print a helpful
  1666         # warning.
  1675         # warning.
  1667         repo.ui.warn(
  1676         repo.ui.warn(
  1668             _(
  1677             _(
  1669                 "current directory was removed\n"
  1678                 b"current directory was removed\n"
  1670                 "(consider changing to repo root: %s)\n"
  1679                 b"(consider changing to repo root: %s)\n"
  1671             )
  1680             )
  1672             % repo.root
  1681             % repo.root
  1673         )
  1682         )
  1674 
  1683 
  1675 
  1684 
  1688     fctx = mctx.filectx
  1697     fctx = mctx.filectx
  1689     ui = repo.ui
  1698     ui = repo.ui
  1690     i = 0
  1699     i = 0
  1691     with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
  1700     with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
  1692         for f, (flags, backup), msg in actions:
  1701         for f, (flags, backup), msg in actions:
  1693             repo.ui.debug(" %s: %s -> g\n" % (f, msg))
  1702             repo.ui.debug(b" %s: %s -> g\n" % (f, msg))
  1694             if verbose:
  1703             if verbose:
  1695                 repo.ui.note(_("getting %s\n") % f)
  1704                 repo.ui.note(_(b"getting %s\n") % f)
  1696 
  1705 
  1697             if backup:
  1706             if backup:
  1698                 # If a file or directory exists with the same name, back that
  1707                 # If a file or directory exists with the same name, back that
  1699                 # up.  Otherwise, look to see if there is a file that conflicts
  1708                 # up.  Otherwise, look to see if there is a file that conflicts
  1700                 # with a directory this file is in, and if so, back that up.
  1709                 # with a directory this file is in, and if so, back that up.
  1707                 if repo.wvfs.lexists(conflicting):
  1716                 if repo.wvfs.lexists(conflicting):
  1708                     orig = scmutil.backuppath(ui, repo, conflicting)
  1717                     orig = scmutil.backuppath(ui, repo, conflicting)
  1709                     util.rename(repo.wjoin(conflicting), orig)
  1718                     util.rename(repo.wjoin(conflicting), orig)
  1710             wfctx = wctx[f]
  1719             wfctx = wctx[f]
  1711             wfctx.clearunknown()
  1720             wfctx.clearunknown()
  1712             atomictemp = ui.configbool("experimental", "update.atomic-file")
  1721             atomictemp = ui.configbool(b"experimental", b"update.atomic-file")
  1713             size = wfctx.write(
  1722             size = wfctx.write(
  1714                 fctx(f).data(),
  1723                 fctx(f).data(),
  1715                 flags,
  1724                 flags,
  1716                 backgroundclose=True,
  1725                 backgroundclose=True,
  1717                 atomictemp=atomictemp,
  1726                 atomictemp=atomictemp,
  1820     mergeactions = sorted(actions[ACTION_CHANGED_DELETED])
  1829     mergeactions = sorted(actions[ACTION_CHANGED_DELETED])
  1821     mergeactions.extend(sorted(actions[ACTION_DELETED_CHANGED]))
  1830     mergeactions.extend(sorted(actions[ACTION_DELETED_CHANGED]))
  1822     mergeactions.extend(actions[ACTION_MERGE])
  1831     mergeactions.extend(actions[ACTION_MERGE])
  1823     for f, args, msg in mergeactions:
  1832     for f, args, msg in mergeactions:
  1824         f1, f2, fa, move, anc = args
  1833         f1, f2, fa, move, anc = args
  1825         if f == '.hgsubstate':  # merged internally
  1834         if f == b'.hgsubstate':  # merged internally
  1826             continue
  1835             continue
  1827         if f1 is None:
  1836         if f1 is None:
  1828             fcl = filemerge.absentfilectx(wctx, fa)
  1837             fcl = filemerge.absentfilectx(wctx, fa)
  1829         else:
  1838         else:
  1830             repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
  1839             repo.ui.debug(b" preserving %s for resolve of %s\n" % (f1, f))
  1831             fcl = wctx[f1]
  1840             fcl = wctx[f1]
  1832         if f2 is None:
  1841         if f2 is None:
  1833             fco = filemerge.absentfilectx(mctx, fa)
  1842             fco = filemerge.absentfilectx(mctx, fa)
  1834         else:
  1843         else:
  1835             fco = mctx[f2]
  1844             fco = mctx[f2]
  1844             moves.append(f1)
  1853             moves.append(f1)
  1845 
  1854 
  1846     # remove renamed files after safely stored
  1855     # remove renamed files after safely stored
  1847     for f in moves:
  1856     for f in moves:
  1848         if wctx[f].lexists():
  1857         if wctx[f].lexists():
  1849             repo.ui.debug("removing %s\n" % f)
  1858             repo.ui.debug(b"removing %s\n" % f)
  1850             wctx[f].audit()
  1859             wctx[f].audit()
  1851             wctx[f].remove()
  1860             wctx[f].remove()
  1852 
  1861 
  1853     numupdates = sum(len(l) for m, l in actions.items() if m != ACTION_KEEP)
  1862     numupdates = sum(len(l) for m, l in actions.items() if m != ACTION_KEEP)
  1854     progress = repo.ui.makeprogress(
  1863     progress = repo.ui.makeprogress(
  1855         _('updating'), unit=_('files'), total=numupdates
  1864         _(b'updating'), unit=_(b'files'), total=numupdates
  1856     )
  1865     )
  1857 
  1866 
  1858     if [a for a in actions[ACTION_REMOVE] if a[0] == '.hgsubstate']:
  1867     if [a for a in actions[ACTION_REMOVE] if a[0] == b'.hgsubstate']:
  1859         subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
  1868         subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
  1860 
  1869 
  1861     # record path conflicts
  1870     # record path conflicts
  1862     for f, args, msg in actions[ACTION_PATH_CONFLICT]:
  1871     for f, args, msg in actions[ACTION_PATH_CONFLICT]:
  1863         f1, fo = args
  1872         f1, fo = args
  1864         s = repo.ui.status
  1873         s = repo.ui.status
  1865         s(
  1874         s(
  1866             _(
  1875             _(
  1867                 "%s: path conflict - a file or link has the same name as a "
  1876                 b"%s: path conflict - a file or link has the same name as a "
  1868                 "directory\n"
  1877                 b"directory\n"
  1869             )
  1878             )
  1870             % f
  1879             % f
  1871         )
  1880         )
  1872         if fo == 'l':
  1881         if fo == b'l':
  1873             s(_("the local file has been renamed to %s\n") % f1)
  1882             s(_(b"the local file has been renamed to %s\n") % f1)
  1874         else:
  1883         else:
  1875             s(_("the remote file has been renamed to %s\n") % f1)
  1884             s(_(b"the remote file has been renamed to %s\n") % f1)
  1876         s(_("resolve manually then use 'hg resolve --mark %s'\n") % f)
  1885         s(_(b"resolve manually then use 'hg resolve --mark %s'\n") % f)
  1877         ms.addpath(f, f1, fo)
  1886         ms.addpath(f, f1, fo)
  1878         progress.increment(item=f)
  1887         progress.increment(item=f)
  1879 
  1888 
  1880     # When merging in-memory, we can't support worker processes, so set the
  1889     # When merging in-memory, we can't support worker processes, so set the
  1881     # per-item cost at 0 in that case.
  1890     # per-item cost at 0 in that case.
  1889         progress.increment(step=i, item=item)
  1898         progress.increment(step=i, item=item)
  1890     removed = len(actions[ACTION_REMOVE])
  1899     removed = len(actions[ACTION_REMOVE])
  1891 
  1900 
  1892     # resolve path conflicts (must come before getting)
  1901     # resolve path conflicts (must come before getting)
  1893     for f, args, msg in actions[ACTION_PATH_CONFLICT_RESOLVE]:
  1902     for f, args, msg in actions[ACTION_PATH_CONFLICT_RESOLVE]:
  1894         repo.ui.debug(" %s: %s -> pr\n" % (f, msg))
  1903         repo.ui.debug(b" %s: %s -> pr\n" % (f, msg))
  1895         (f0,) = args
  1904         (f0,) = args
  1896         if wctx[f0].lexists():
  1905         if wctx[f0].lexists():
  1897             repo.ui.note(_("moving %s to %s\n") % (f0, f))
  1906             repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
  1898             wctx[f].audit()
  1907             wctx[f].audit()
  1899             wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
  1908             wctx[f].write(wctx.filectx(f0).data(), wctx.filectx(f0).flags())
  1900             wctx[f0].remove()
  1909             wctx[f0].remove()
  1901         progress.increment(item=f)
  1910         progress.increment(item=f)
  1902 
  1911 
  1903     # get in parallel.
  1912     # get in parallel.
  1904     threadsafe = repo.ui.configbool(
  1913     threadsafe = repo.ui.configbool(
  1905         'experimental', 'worker.wdir-get-thread-safe'
  1914         b'experimental', b'worker.wdir-get-thread-safe'
  1906     )
  1915     )
  1907     prog = worker.worker(
  1916     prog = worker.worker(
  1908         repo.ui,
  1917         repo.ui,
  1909         cost,
  1918         cost,
  1910         batchget,
  1919         batchget,
  1920         else:
  1929         else:
  1921             i, item = res
  1930             i, item = res
  1922             progress.increment(step=i, item=item)
  1931             progress.increment(step=i, item=item)
  1923     updated = len(actions[ACTION_GET])
  1932     updated = len(actions[ACTION_GET])
  1924 
  1933 
  1925     if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']:
  1934     if [a for a in actions[ACTION_GET] if a[0] == b'.hgsubstate']:
  1926         subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
  1935         subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels)
  1927 
  1936 
  1928     # forget (manifest only, just log it) (must come first)
  1937     # forget (manifest only, just log it) (must come first)
  1929     for f, args, msg in actions[ACTION_FORGET]:
  1938     for f, args, msg in actions[ACTION_FORGET]:
  1930         repo.ui.debug(" %s: %s -> f\n" % (f, msg))
  1939         repo.ui.debug(b" %s: %s -> f\n" % (f, msg))
  1931         progress.increment(item=f)
  1940         progress.increment(item=f)
  1932 
  1941 
  1933     # re-add (manifest only, just log it)
  1942     # re-add (manifest only, just log it)
  1934     for f, args, msg in actions[ACTION_ADD]:
  1943     for f, args, msg in actions[ACTION_ADD]:
  1935         repo.ui.debug(" %s: %s -> a\n" % (f, msg))
  1944         repo.ui.debug(b" %s: %s -> a\n" % (f, msg))
  1936         progress.increment(item=f)
  1945         progress.increment(item=f)
  1937 
  1946 
  1938     # re-add/mark as modified (manifest only, just log it)
  1947     # re-add/mark as modified (manifest only, just log it)
  1939     for f, args, msg in actions[ACTION_ADD_MODIFIED]:
  1948     for f, args, msg in actions[ACTION_ADD_MODIFIED]:
  1940         repo.ui.debug(" %s: %s -> am\n" % (f, msg))
  1949         repo.ui.debug(b" %s: %s -> am\n" % (f, msg))
  1941         progress.increment(item=f)
  1950         progress.increment(item=f)
  1942 
  1951 
  1943     # keep (noop, just log it)
  1952     # keep (noop, just log it)
  1944     for f, args, msg in actions[ACTION_KEEP]:
  1953     for f, args, msg in actions[ACTION_KEEP]:
  1945         repo.ui.debug(" %s: %s -> k\n" % (f, msg))
  1954         repo.ui.debug(b" %s: %s -> k\n" % (f, msg))
  1946         # no progress
  1955         # no progress
  1947 
  1956 
  1948     # directory rename, move local
  1957     # directory rename, move local
  1949     for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
  1958     for f, args, msg in actions[ACTION_DIR_RENAME_MOVE_LOCAL]:
  1950         repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
  1959         repo.ui.debug(b" %s: %s -> dm\n" % (f, msg))
  1951         progress.increment(item=f)
  1960         progress.increment(item=f)
  1952         f0, flags = args
  1961         f0, flags = args
  1953         repo.ui.note(_("moving %s to %s\n") % (f0, f))
  1962         repo.ui.note(_(b"moving %s to %s\n") % (f0, f))
  1954         wctx[f].audit()
  1963         wctx[f].audit()
  1955         wctx[f].write(wctx.filectx(f0).data(), flags)
  1964         wctx[f].write(wctx.filectx(f0).data(), flags)
  1956         wctx[f0].remove()
  1965         wctx[f0].remove()
  1957         updated += 1
  1966         updated += 1
  1958 
  1967 
  1959     # local directory rename, get
  1968     # local directory rename, get
  1960     for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
  1969     for f, args, msg in actions[ACTION_LOCAL_DIR_RENAME_GET]:
  1961         repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
  1970         repo.ui.debug(b" %s: %s -> dg\n" % (f, msg))
  1962         progress.increment(item=f)
  1971         progress.increment(item=f)
  1963         f0, flags = args
  1972         f0, flags = args
  1964         repo.ui.note(_("getting %s to %s\n") % (f0, f))
  1973         repo.ui.note(_(b"getting %s to %s\n") % (f0, f))
  1965         wctx[f].write(mctx.filectx(f0).data(), flags)
  1974         wctx[f].write(mctx.filectx(f0).data(), flags)
  1966         updated += 1
  1975         updated += 1
  1967 
  1976 
  1968     # exec
  1977     # exec
  1969     for f, args, msg in actions[ACTION_EXEC]:
  1978     for f, args, msg in actions[ACTION_EXEC]:
  1970         repo.ui.debug(" %s: %s -> e\n" % (f, msg))
  1979         repo.ui.debug(b" %s: %s -> e\n" % (f, msg))
  1971         progress.increment(item=f)
  1980         progress.increment(item=f)
  1972         (flags,) = args
  1981         (flags,) = args
  1973         wctx[f].audit()
  1982         wctx[f].audit()
  1974         wctx[f].setflags('l' in flags, 'x' in flags)
  1983         wctx[f].setflags(b'l' in flags, b'x' in flags)
  1975         updated += 1
  1984         updated += 1
  1976 
  1985 
  1977     # the ordering is important here -- ms.mergedriver will raise if the merge
  1986     # the ordering is important here -- ms.mergedriver will raise if the merge
  1978     # driver has changed, and we want to be able to bypass it when overwrite is
  1987     # driver has changed, and we want to be able to bypass it when overwrite is
  1979     # True
  1988     # True
  1980     usemergedriver = not overwrite and mergeactions and ms.mergedriver
  1989     usemergedriver = not overwrite and mergeactions and ms.mergedriver
  1981 
  1990 
  1982     if usemergedriver:
  1991     if usemergedriver:
  1983         if wctx.isinmemory():
  1992         if wctx.isinmemory():
  1984             raise error.InMemoryMergeConflictsError(
  1993             raise error.InMemoryMergeConflictsError(
  1985                 "in-memory merge does not " "support mergedriver"
  1994                 b"in-memory merge does not " b"support mergedriver"
  1986             )
  1995             )
  1987         ms.commit()
  1996         ms.commit()
  1988         proceed = driverpreprocess(repo, ms, wctx, labels=labels)
  1997         proceed = driverpreprocess(repo, ms, wctx, labels=labels)
  1989         # the driver might leave some files unresolved
  1998         # the driver might leave some files unresolved
  1990         unresolvedf = set(ms.unresolved())
  1999         unresolvedf = set(ms.unresolved())
  2002 
  2011 
  2003     try:
  2012     try:
  2004         # premerge
  2013         # premerge
  2005         tocomplete = []
  2014         tocomplete = []
  2006         for f, args, msg in mergeactions:
  2015         for f, args, msg in mergeactions:
  2007             repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
  2016             repo.ui.debug(b" %s: %s -> m (premerge)\n" % (f, msg))
  2008             progress.increment(item=f)
  2017             progress.increment(item=f)
  2009             if f == '.hgsubstate':  # subrepo states need updating
  2018             if f == b'.hgsubstate':  # subrepo states need updating
  2010                 subrepoutil.submerge(
  2019                 subrepoutil.submerge(
  2011                     repo, wctx, mctx, wctx.ancestor(mctx), overwrite, labels
  2020                     repo, wctx, mctx, wctx.ancestor(mctx), overwrite, labels
  2012                 )
  2021                 )
  2013                 continue
  2022                 continue
  2014             wctx[f].audit()
  2023             wctx[f].audit()
  2017                 numupdates += 1
  2026                 numupdates += 1
  2018                 tocomplete.append((f, args, msg))
  2027                 tocomplete.append((f, args, msg))
  2019 
  2028 
  2020         # merge
  2029         # merge
  2021         for f, args, msg in tocomplete:
  2030         for f, args, msg in tocomplete:
  2022             repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
  2031             repo.ui.debug(b" %s: %s -> m (merge)\n" % (f, msg))
  2023             progress.increment(item=f, total=numupdates)
  2032             progress.increment(item=f, total=numupdates)
  2024             ms.resolve(f, wctx)
  2033             ms.resolve(f, wctx)
  2025 
  2034 
  2026     finally:
  2035     finally:
  2027         ms.commit()
  2036         ms.commit()
  2080     assert len(getfiledata) == (len(actions[ACTION_GET]) if wantfiledata else 0)
  2089     assert len(getfiledata) == (len(actions[ACTION_GET]) if wantfiledata else 0)
  2081     return updateresult(updated, merged, removed, unresolved), getfiledata
  2090     return updateresult(updated, merged, removed, unresolved), getfiledata
  2082 
  2091 
  2083 
  2092 
  2084 def recordupdates(repo, actions, branchmerge, getfiledata):
  2093 def recordupdates(repo, actions, branchmerge, getfiledata):
  2085     "record merge actions to the dirstate"
  2094     b"record merge actions to the dirstate"
  2086     # remove (must come first)
  2095     # remove (must come first)
  2087     for f, args, msg in actions.get(ACTION_REMOVE, []):
  2096     for f, args, msg in actions.get(ACTION_REMOVE, []):
  2088         if branchmerge:
  2097         if branchmerge:
  2089             repo.dirstate.remove(f)
  2098             repo.dirstate.remove(f)
  2090         else:
  2099         else:
  2176             repo.dirstate.copy(f0, f)
  2185             repo.dirstate.copy(f0, f)
  2177         else:
  2186         else:
  2178             repo.dirstate.normal(f)
  2187             repo.dirstate.normal(f)
  2179 
  2188 
  2180 
  2189 
  2181 UPDATECHECK_ABORT = 'abort'  # handled at higher layers
  2190 UPDATECHECK_ABORT = b'abort'  # handled at higher layers
  2182 UPDATECHECK_NONE = 'none'
  2191 UPDATECHECK_NONE = b'none'
  2183 UPDATECHECK_LINEAR = 'linear'
  2192 UPDATECHECK_LINEAR = b'linear'
  2184 UPDATECHECK_NO_CONFLICT = 'noconflict'
  2193 UPDATECHECK_NO_CONFLICT = b'noconflict'
  2185 
  2194 
  2186 
  2195 
  2187 def update(
  2196 def update(
  2188     repo,
  2197     repo,
  2189     node,
  2198     node,
  2291         p1 = pl[0]
  2300         p1 = pl[0]
  2292         p2 = repo[node]
  2301         p2 = repo[node]
  2293         if ancestor is not None:
  2302         if ancestor is not None:
  2294             pas = [repo[ancestor]]
  2303             pas = [repo[ancestor]]
  2295         else:
  2304         else:
  2296             if repo.ui.configlist('merge', 'preferancestor') == ['*']:
  2305             if repo.ui.configlist(b'merge', b'preferancestor') == [b'*']:
  2297                 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
  2306                 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
  2298                 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
  2307                 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
  2299             else:
  2308             else:
  2300                 pas = [p1.ancestor(p2, warn=branchmerge)]
  2309                 pas = [p1.ancestor(p2, warn=branchmerge)]
  2301 
  2310 
  2303 
  2312 
  2304         overwrite = force and not branchmerge
  2313         overwrite = force and not branchmerge
  2305         ### check phase
  2314         ### check phase
  2306         if not overwrite:
  2315         if not overwrite:
  2307             if len(pl) > 1:
  2316             if len(pl) > 1:
  2308                 raise error.Abort(_("outstanding uncommitted merge"))
  2317                 raise error.Abort(_(b"outstanding uncommitted merge"))
  2309             ms = mergestate.read(repo)
  2318             ms = mergestate.read(repo)
  2310             if list(ms.unresolved()):
  2319             if list(ms.unresolved()):
  2311                 raise error.Abort(
  2320                 raise error.Abort(
  2312                     _("outstanding merge conflicts"),
  2321                     _(b"outstanding merge conflicts"),
  2313                     hint=_("use 'hg resolve' to resolve"),
  2322                     hint=_(b"use 'hg resolve' to resolve"),
  2314                 )
  2323                 )
  2315         if branchmerge:
  2324         if branchmerge:
  2316             if pas == [p2]:
  2325             if pas == [p2]:
  2317                 raise error.Abort(
  2326                 raise error.Abort(
  2318                     _(
  2327                     _(
  2319                         "merging with a working directory ancestor"
  2328                         b"merging with a working directory ancestor"
  2320                         " has no effect"
  2329                         b" has no effect"
  2321                     )
  2330                     )
  2322                 )
  2331                 )
  2323             elif pas == [p1]:
  2332             elif pas == [p1]:
  2324                 if not mergeancestor and wc.branch() == p2.branch():
  2333                 if not mergeancestor and wc.branch() == p2.branch():
  2325                     raise error.Abort(
  2334                     raise error.Abort(
  2326                         _("nothing to merge"),
  2335                         _(b"nothing to merge"),
  2327                         hint=_("use 'hg update' " "or check 'hg heads'"),
  2336                         hint=_(b"use 'hg update' " b"or check 'hg heads'"),
  2328                     )
  2337                     )
  2329             if not force and (wc.files() or wc.deleted()):
  2338             if not force and (wc.files() or wc.deleted()):
  2330                 raise error.Abort(
  2339                 raise error.Abort(
  2331                     _("uncommitted changes"),
  2340                     _(b"uncommitted changes"),
  2332                     hint=_("use 'hg status' to list changes"),
  2341                     hint=_(b"use 'hg status' to list changes"),
  2333                 )
  2342                 )
  2334             if not wc.isinmemory():
  2343             if not wc.isinmemory():
  2335                 for s in sorted(wc.substate):
  2344                 for s in sorted(wc.substate):
  2336                     wc.sub(s).bailifchanged()
  2345                     wc.sub(s).bailifchanged()
  2337 
  2346 
  2338         elif not overwrite:
  2347         elif not overwrite:
  2339             if p1 == p2:  # no-op update
  2348             if p1 == p2:  # no-op update
  2340                 # call the hooks and exit early
  2349                 # call the hooks and exit early
  2341                 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
  2350                 repo.hook(b'preupdate', throw=True, parent1=xp2, parent2=b'')
  2342                 repo.hook('update', parent1=xp2, parent2='', error=0)
  2351                 repo.hook(b'update', parent1=xp2, parent2=b'', error=0)
  2343                 return updateresult(0, 0, 0, 0)
  2352                 return updateresult(0, 0, 0, 0)
  2344 
  2353 
  2345             if updatecheck == UPDATECHECK_LINEAR and pas not in (
  2354             if updatecheck == UPDATECHECK_LINEAR and pas not in (
  2346                 [p1],
  2355                 [p1],
  2347                 [p2],
  2356                 [p2],
  2353                     foreground = obsutil.foreground(repo, [p1.node()])
  2362                     foreground = obsutil.foreground(repo, [p1.node()])
  2354                     # note: the <node> variable contains a random identifier
  2363                     # note: the <node> variable contains a random identifier
  2355                     if repo[node].node() in foreground:
  2364                     if repo[node].node() in foreground:
  2356                         pass  # allow updating to successors
  2365                         pass  # allow updating to successors
  2357                     else:
  2366                     else:
  2358                         msg = _("uncommitted changes")
  2367                         msg = _(b"uncommitted changes")
  2359                         hint = _("commit or update --clean to discard changes")
  2368                         hint = _(b"commit or update --clean to discard changes")
  2360                         raise error.UpdateAbort(msg, hint=hint)
  2369                         raise error.UpdateAbort(msg, hint=hint)
  2361                 else:
  2370                 else:
  2362                     # Allow jumping branches if clean and specific rev given
  2371                     # Allow jumping branches if clean and specific rev given
  2363                     pass
  2372                     pass
  2364 
  2373 
  2366             pas = [wc]
  2375             pas = [wc]
  2367         elif not branchmerge:
  2376         elif not branchmerge:
  2368             pas = [p1]
  2377             pas = [p1]
  2369 
  2378 
  2370         # deprecated config: merge.followcopies
  2379         # deprecated config: merge.followcopies
  2371         followcopies = repo.ui.configbool('merge', 'followcopies')
  2380         followcopies = repo.ui.configbool(b'merge', b'followcopies')
  2372         if overwrite:
  2381         if overwrite:
  2373             followcopies = False
  2382             followcopies = False
  2374         elif not pas[0]:
  2383         elif not pas[0]:
  2375             followcopies = False
  2384             followcopies = False
  2376         if not branchmerge and not wc.dirty(missing=True):
  2385         if not branchmerge and not wc.dirty(missing=True):
  2397                     ACTION_KEEP,
  2406                     ACTION_KEEP,
  2398                     ACTION_EXEC,
  2407                     ACTION_EXEC,
  2399                     ACTION_REMOVE,
  2408                     ACTION_REMOVE,
  2400                     ACTION_PATH_CONFLICT_RESOLVE,
  2409                     ACTION_PATH_CONFLICT_RESOLVE,
  2401                 ):
  2410                 ):
  2402                     msg = _("conflicting changes")
  2411                     msg = _(b"conflicting changes")
  2403                     hint = _("commit or update --clean to discard changes")
  2412                     hint = _(b"commit or update --clean to discard changes")
  2404                     raise error.Abort(msg, hint=hint)
  2413                     raise error.Abort(msg, hint=hint)
  2405 
  2414 
  2406         # Prompt and create actions. Most of this is in the resolve phase
  2415         # Prompt and create actions. Most of this is in the resolve phase
  2407         # already, but we can't handle .hgsubstate in filemerge or
  2416         # already, but we can't handle .hgsubstate in filemerge or
  2408         # subrepoutil.submerge yet so we have to keep prompting for it.
  2417         # subrepoutil.submerge yet so we have to keep prompting for it.
  2409         if '.hgsubstate' in actionbyfile:
  2418         if b'.hgsubstate' in actionbyfile:
  2410             f = '.hgsubstate'
  2419             f = b'.hgsubstate'
  2411             m, args, msg = actionbyfile[f]
  2420             m, args, msg = actionbyfile[f]
  2412             prompts = filemerge.partextras(labels)
  2421             prompts = filemerge.partextras(labels)
  2413             prompts['f'] = f
  2422             prompts[b'f'] = f
  2414             if m == ACTION_CHANGED_DELETED:
  2423             if m == ACTION_CHANGED_DELETED:
  2415                 if repo.ui.promptchoice(
  2424                 if repo.ui.promptchoice(
  2416                     _(
  2425                     _(
  2417                         "local%(l)s changed %(f)s which other%(o)s deleted\n"
  2426                         b"local%(l)s changed %(f)s which other%(o)s deleted\n"
  2418                         "use (c)hanged version or (d)elete?"
  2427                         b"use (c)hanged version or (d)elete?"
  2419                         "$$ &Changed $$ &Delete"
  2428                         b"$$ &Changed $$ &Delete"
  2420                     )
  2429                     )
  2421                     % prompts,
  2430                     % prompts,
  2422                     0,
  2431                     0,
  2423                 ):
  2432                 ):
  2424                     actionbyfile[f] = (ACTION_REMOVE, None, 'prompt delete')
  2433                     actionbyfile[f] = (ACTION_REMOVE, None, b'prompt delete')
  2425                 elif f in p1:
  2434                 elif f in p1:
  2426                     actionbyfile[f] = (ACTION_ADD_MODIFIED, None, 'prompt keep')
  2435                     actionbyfile[f] = (
       
  2436                         ACTION_ADD_MODIFIED,
       
  2437                         None,
       
  2438                         b'prompt keep',
       
  2439                     )
  2427                 else:
  2440                 else:
  2428                     actionbyfile[f] = (ACTION_ADD, None, 'prompt keep')
  2441                     actionbyfile[f] = (ACTION_ADD, None, b'prompt keep')
  2429             elif m == ACTION_DELETED_CHANGED:
  2442             elif m == ACTION_DELETED_CHANGED:
  2430                 f1, f2, fa, move, anc = args
  2443                 f1, f2, fa, move, anc = args
  2431                 flags = p2[f2].flags()
  2444                 flags = p2[f2].flags()
  2432                 if (
  2445                 if (
  2433                     repo.ui.promptchoice(
  2446                     repo.ui.promptchoice(
  2434                         _(
  2447                         _(
  2435                             "other%(o)s changed %(f)s which local%(l)s deleted\n"
  2448                             b"other%(o)s changed %(f)s which local%(l)s deleted\n"
  2436                             "use (c)hanged version or leave (d)eleted?"
  2449                             b"use (c)hanged version or leave (d)eleted?"
  2437                             "$$ &Changed $$ &Deleted"
  2450                             b"$$ &Changed $$ &Deleted"
  2438                         )
  2451                         )
  2439                         % prompts,
  2452                         % prompts,
  2440                         0,
  2453                         0,
  2441                     )
  2454                     )
  2442                     == 0
  2455                     == 0
  2443                 ):
  2456                 ):
  2444                     actionbyfile[f] = (
  2457                     actionbyfile[f] = (
  2445                         ACTION_GET,
  2458                         ACTION_GET,
  2446                         (flags, False),
  2459                         (flags, False),
  2447                         'prompt recreating',
  2460                         b'prompt recreating',
  2448                     )
  2461                     )
  2449                 else:
  2462                 else:
  2450                     del actionbyfile[f]
  2463                     del actionbyfile[f]
  2451 
  2464 
  2452         # Convert to dictionary-of-lists format
  2465         # Convert to dictionary-of-lists format
  2467 
  2480 
  2468         # divergent renames
  2481         # divergent renames
  2469         for f, fl in sorted(diverge.iteritems()):
  2482         for f, fl in sorted(diverge.iteritems()):
  2470             repo.ui.warn(
  2483             repo.ui.warn(
  2471                 _(
  2484                 _(
  2472                     "note: possible conflict - %s was renamed "
  2485                     b"note: possible conflict - %s was renamed "
  2473                     "multiple times to:\n"
  2486                     b"multiple times to:\n"
  2474                 )
  2487                 )
  2475                 % f
  2488                 % f
  2476             )
  2489             )
  2477             for nf in sorted(fl):
  2490             for nf in sorted(fl):
  2478                 repo.ui.warn(" %s\n" % nf)
  2491                 repo.ui.warn(b" %s\n" % nf)
  2479 
  2492 
  2480         # rename and delete
  2493         # rename and delete
  2481         for f, fl in sorted(renamedelete.iteritems()):
  2494         for f, fl in sorted(renamedelete.iteritems()):
  2482             repo.ui.warn(
  2495             repo.ui.warn(
  2483                 _(
  2496                 _(
  2484                     "note: possible conflict - %s was deleted "
  2497                     b"note: possible conflict - %s was deleted "
  2485                     "and renamed to:\n"
  2498                     b"and renamed to:\n"
  2486                 )
  2499                 )
  2487                 % f
  2500                 % f
  2488             )
  2501             )
  2489             for nf in sorted(fl):
  2502             for nf in sorted(fl):
  2490                 repo.ui.warn(" %s\n" % nf)
  2503                 repo.ui.warn(b" %s\n" % nf)
  2491 
  2504 
  2492         ### apply phase
  2505         ### apply phase
  2493         if not branchmerge:  # just jump to the new rev
  2506         if not branchmerge:  # just jump to the new rev
  2494             fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
  2507             fp1, fp2, xp1, xp2 = fp2, nullid, xp2, b''
  2495         if not partial and not wc.isinmemory():
  2508         if not partial and not wc.isinmemory():
  2496             repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
  2509             repo.hook(b'preupdate', throw=True, parent1=xp1, parent2=xp2)
  2497             # note that we're in the middle of an update
  2510             # note that we're in the middle of an update
  2498             repo.vfs.write('updatestate', p2.hex())
  2511             repo.vfs.write(b'updatestate', p2.hex())
  2499 
  2512 
  2500         # Advertise fsmonitor when its presence could be useful.
  2513         # Advertise fsmonitor when its presence could be useful.
  2501         #
  2514         #
  2502         # We only advertise when performing an update from an empty working
  2515         # We only advertise when performing an update from an empty working
  2503         # directory. This typically only occurs during initial clone.
  2516         # directory. This typically only occurs during initial clone.
  2505         # We give users a mechanism to disable the warning in case it is
  2518         # We give users a mechanism to disable the warning in case it is
  2506         # annoying.
  2519         # annoying.
  2507         #
  2520         #
  2508         # We only allow on Linux and MacOS because that's where fsmonitor is
  2521         # We only allow on Linux and MacOS because that's where fsmonitor is
  2509         # considered stable.
  2522         # considered stable.
  2510         fsmonitorwarning = repo.ui.configbool('fsmonitor', 'warn_when_unused')
  2523         fsmonitorwarning = repo.ui.configbool(b'fsmonitor', b'warn_when_unused')
  2511         fsmonitorthreshold = repo.ui.configint(
  2524         fsmonitorthreshold = repo.ui.configint(
  2512             'fsmonitor', 'warn_update_file_count'
  2525             b'fsmonitor', b'warn_update_file_count'
  2513         )
  2526         )
  2514         try:
  2527         try:
  2515             # avoid cycle: extensions -> cmdutil -> merge
  2528             # avoid cycle: extensions -> cmdutil -> merge
  2516             from . import extensions
  2529             from . import extensions
  2517 
  2530 
  2518             extensions.find('fsmonitor')
  2531             extensions.find(b'fsmonitor')
  2519             fsmonitorenabled = repo.ui.config('fsmonitor', 'mode') != 'off'
  2532             fsmonitorenabled = repo.ui.config(b'fsmonitor', b'mode') != b'off'
  2520             # We intentionally don't look at whether fsmonitor has disabled
  2533             # We intentionally don't look at whether fsmonitor has disabled
  2521             # itself because a) fsmonitor may have already printed a warning
  2534             # itself because a) fsmonitor may have already printed a warning
  2522             # b) we only care about the config state here.
  2535             # b) we only care about the config state here.
  2523         except KeyError:
  2536         except KeyError:
  2524             fsmonitorenabled = False
  2537             fsmonitorenabled = False
  2526         if (
  2539         if (
  2527             fsmonitorwarning
  2540             fsmonitorwarning
  2528             and not fsmonitorenabled
  2541             and not fsmonitorenabled
  2529             and p1.node() == nullid
  2542             and p1.node() == nullid
  2530             and len(actions[ACTION_GET]) >= fsmonitorthreshold
  2543             and len(actions[ACTION_GET]) >= fsmonitorthreshold
  2531             and pycompat.sysplatform.startswith(('linux', 'darwin'))
  2544             and pycompat.sysplatform.startswith((b'linux', b'darwin'))
  2532         ):
  2545         ):
  2533             repo.ui.warn(
  2546             repo.ui.warn(
  2534                 _(
  2547                 _(
  2535                     '(warning: large working directory being used without '
  2548                     b'(warning: large working directory being used without '
  2536                     'fsmonitor enabled; enable fsmonitor to improve performance; '
  2549                     b'fsmonitor enabled; enable fsmonitor to improve performance; '
  2537                     'see "hg help -e fsmonitor")\n'
  2550                     b'see "hg help -e fsmonitor")\n'
  2538                 )
  2551                 )
  2539             )
  2552             )
  2540 
  2553 
  2541         updatedirstate = not partial and not wc.isinmemory()
  2554         updatedirstate = not partial and not wc.isinmemory()
  2542         wantfiledata = updatedirstate and not branchmerge
  2555         wantfiledata = updatedirstate and not branchmerge
  2547         if updatedirstate:
  2560         if updatedirstate:
  2548             with repo.dirstate.parentchange():
  2561             with repo.dirstate.parentchange():
  2549                 repo.setparents(fp1, fp2)
  2562                 repo.setparents(fp1, fp2)
  2550                 recordupdates(repo, actions, branchmerge, getfiledata)
  2563                 recordupdates(repo, actions, branchmerge, getfiledata)
  2551                 # update completed, clear state
  2564                 # update completed, clear state
  2552                 util.unlink(repo.vfs.join('updatestate'))
  2565                 util.unlink(repo.vfs.join(b'updatestate'))
  2553 
  2566 
  2554                 if not branchmerge:
  2567                 if not branchmerge:
  2555                     repo.dirstate.setbranch(p2.branch())
  2568                     repo.dirstate.setbranch(p2.branch())
  2556 
  2569 
  2557     # If we're updating to a location, clean up any stale temporary includes
  2570     # If we're updating to a location, clean up any stale temporary includes
  2559     if not branchmerge:
  2572     if not branchmerge:
  2560         sparse.prunetemporaryincludes(repo)
  2573         sparse.prunetemporaryincludes(repo)
  2561 
  2574 
  2562     if not partial:
  2575     if not partial:
  2563         repo.hook(
  2576         repo.hook(
  2564             'update', parent1=xp1, parent2=xp2, error=stats.unresolvedcount
  2577             b'update', parent1=xp1, parent2=xp2, error=stats.unresolvedcount
  2565         )
  2578         )
  2566     return stats
  2579     return stats
  2567 
  2580 
  2568 
  2581 
  2569 def graft(
  2582 def graft(
  2588     # mergeancestor=True to update. This does two things: 1) allows the merge if
  2601     # mergeancestor=True to update. This does two things: 1) allows the merge if
  2589     # the destination is the same as the parent of the ctx (so we can use graft
  2602     # the destination is the same as the parent of the ctx (so we can use graft
  2590     # to copy commits), and 2) informs update that the incoming changes are
  2603     # to copy commits), and 2) informs update that the incoming changes are
  2591     # newer than the destination so it doesn't prompt about "remote changed foo
  2604     # newer than the destination so it doesn't prompt about "remote changed foo
  2592     # which local deleted".
  2605     # which local deleted".
  2593     mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
  2606     mergeancestor = repo.changelog.isancestor(repo[b'.'].node(), ctx.node())
  2594 
  2607 
  2595     stats = update(
  2608     stats = update(
  2596         repo,
  2609         repo,
  2597         ctx.node(),
  2610         ctx.node(),
  2598         True,
  2611         True,
  2610         if keepparent and len(parents) == 2 and pctx in parents:
  2623         if keepparent and len(parents) == 2 and pctx in parents:
  2611             parents.remove(pctx)
  2624             parents.remove(pctx)
  2612             pother = parents[0].node()
  2625             pother = parents[0].node()
  2613 
  2626 
  2614     with repo.dirstate.parentchange():
  2627     with repo.dirstate.parentchange():
  2615         repo.setparents(repo['.'].node(), pother)
  2628         repo.setparents(repo[b'.'].node(), pother)
  2616         repo.dirstate.write(repo.currenttransaction())
  2629         repo.dirstate.write(repo.currenttransaction())
  2617         # fix up dirstate for copies and renames
  2630         # fix up dirstate for copies and renames
  2618         copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev())
  2631         copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev())
  2619     return stats
  2632     return stats
  2620 
  2633 
  2651 
  2664 
  2652     def remove(removefn, path):
  2665     def remove(removefn, path):
  2653         try:
  2666         try:
  2654             removefn(path)
  2667             removefn(path)
  2655         except OSError:
  2668         except OSError:
  2656             m = _('%s cannot be removed') % path
  2669             m = _(b'%s cannot be removed') % path
  2657             if abortonerror:
  2670             if abortonerror:
  2658                 raise error.Abort(m)
  2671                 raise error.Abort(m)
  2659             else:
  2672             else:
  2660                 repo.ui.warn(_('warning: %s\n') % m)
  2673                 repo.ui.warn(_(b'warning: %s\n') % m)
  2661 
  2674 
  2662     # There's no API to copy a matcher. So mutate the passed matcher and
  2675     # There's no API to copy a matcher. So mutate the passed matcher and
  2663     # restore it when we're done.
  2676     # restore it when we're done.
  2664     oldexplicitdir = matcher.explicitdir
  2677     oldexplicitdir = matcher.explicitdir
  2665     oldtraversedir = matcher.traversedir
  2678     oldtraversedir = matcher.traversedir
  2674         status = repo.status(match=matcher, ignored=ignored, unknown=True)
  2687         status = repo.status(match=matcher, ignored=ignored, unknown=True)
  2675 
  2688 
  2676         if removefiles:
  2689         if removefiles:
  2677             for f in sorted(status.unknown + status.ignored):
  2690             for f in sorted(status.unknown + status.ignored):
  2678                 if not noop:
  2691                 if not noop:
  2679                     repo.ui.note(_('removing file %s\n') % f)
  2692                     repo.ui.note(_(b'removing file %s\n') % f)
  2680                     remove(repo.wvfs.unlink, f)
  2693                     remove(repo.wvfs.unlink, f)
  2681                 res.append(f)
  2694                 res.append(f)
  2682 
  2695 
  2683         if removeemptydirs:
  2696         if removeemptydirs:
  2684             for f in sorted(directories, reverse=True):
  2697             for f in sorted(directories, reverse=True):
  2685                 if matcher(f) and not repo.wvfs.listdir(f):
  2698                 if matcher(f) and not repo.wvfs.listdir(f):
  2686                     if not noop:
  2699                     if not noop:
  2687                         repo.ui.note(_('removing directory %s\n') % f)
  2700                         repo.ui.note(_(b'removing directory %s\n') % f)
  2688                         remove(repo.wvfs.rmdir, f)
  2701                         remove(repo.wvfs.rmdir, f)
  2689                     res.append(f)
  2702                     res.append(f)
  2690 
  2703 
  2691         return res
  2704         return res
  2692 
  2705