Mercurial > public > mercurial-scm > hg-stable
diff mercurial/merge.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 71bb9363818c |
children | 687b865b95ad |
line wrap: on
line diff
--- a/mercurial/merge.py Sat Oct 05 10:29:34 2019 -0400 +++ b/mercurial/merge.py Sun Oct 06 09:45:02 2019 -0400 @@ -23,9 +23,7 @@ nullid, nullrev, ) -from .thirdparty import ( - attr, -) +from .thirdparty import attr from . import ( copies, encoding, @@ -43,12 +41,14 @@ _pack = struct.pack _unpack = struct.unpack + def _droponode(data): # used for compatibility for v1 bits = data.split('\0') bits = bits[:-2] + bits[-1:] return '\0'.join(bits) + # Merge state record types. See ``mergestate`` docs for more. RECORD_LOCAL = b'L' RECORD_OTHER = b'O' @@ -90,6 +90,7 @@ ACTION_EXEC = b'e' ACTION_CREATED_MERGE = b'cm' + class mergestate(object): '''track 3-way merge state of individual files @@ -136,6 +137,7 @@ The resolve command transitions between 'u' and 'r' for conflicts and 'pu' and 'pr' for path conflicts. ''' + statepathv1 = 'merge/state' statepathv2 = 'merge/state2' @@ -209,15 +211,21 @@ bits = record.split('\0', 1) mdstate = bits[1] if len(mdstate) != 1 or mdstate not in ( - MERGE_DRIVER_STATE_UNMARKED, MERGE_DRIVER_STATE_MARKED, - MERGE_DRIVER_STATE_SUCCESS): + MERGE_DRIVER_STATE_UNMARKED, + MERGE_DRIVER_STATE_MARKED, + MERGE_DRIVER_STATE_SUCCESS, + ): # the merge driver should be idempotent, so just rerun it mdstate = MERGE_DRIVER_STATE_UNMARKED self._readmergedriver = bits[0] self._mdstate = mdstate - elif rtype in (RECORD_MERGED, RECORD_CHANGEDELETE_CONFLICT, - RECORD_PATH_CONFLICT, RECORD_MERGE_DRIVER_MERGE): + elif rtype in ( + RECORD_MERGED, + RECORD_CHANGEDELETE_CONFLICT, + RECORD_PATH_CONFLICT, + RECORD_MERGE_DRIVER_MERGE, + ): bits = record.split('\0') self._state[bits[0]] = bits[1:] elif rtype == RECORD_FILE_VALUES: @@ -276,7 +284,7 @@ return v1records def _v1v2match(self, v1records, v2records): - oldv2 = set() # old format version of v2 record + oldv2 = set() # old format version of v2 record for rec in v2records: if rec[0] == RECORD_LOCAL: oldv2.add(rec) @@ -336,11 +344,11 @@ off = 0 end = len(data) while off < end: - rtype = data[off:off + 1] + rtype = data[off : off + 1] off += 1 - length = _unpack('>I', data[off:(off + 4)])[0] + length = _unpack('>I', data[off : (off + 4)])[0] off += 4 - record = data[off:(off + length)] + record = data[off : (off + length)] off += length if rtype == RECORD_OVERRIDE: rtype, record = record[0:1], record[1:] @@ -362,11 +370,14 @@ # - B then continues the merge and the malicious merge driver # gets invoked configmergedriver = self._repo.ui.config('experimental', 'mergedriver') - if (self._readmergedriver is not None - and self._readmergedriver != configmergedriver): + if ( + self._readmergedriver is not None + and self._readmergedriver != configmergedriver + ): raise error.ConfigError( _("merge driver changed since merge started"), - hint=_("revert merge driver change or abort merge")) + hint=_("revert merge driver change or abort merge"), + ) return configmergedriver @@ -392,9 +403,12 @@ """ # Check local variables before looking at filesystem for performance # reasons. - return (bool(self._local) or bool(self._state) or - self._repo.vfs.exists(self.statepathv1) or - self._repo.vfs.exists(self.statepathv2)) + return ( + bool(self._local) + or bool(self._state) + or self._repo.vfs.exists(self.statepathv1) + or self._repo.vfs.exists(self.statepathv2) + ) def commit(self): """Write current state on disk (if necessary)""" @@ -408,8 +422,12 @@ records.append((RECORD_LOCAL, hex(self._local))) records.append((RECORD_OTHER, hex(self._other))) if self.mergedriver: - records.append((RECORD_MERGE_DRIVER_STATE, '\0'.join([ - self.mergedriver, self._mdstate]))) + records.append( + ( + RECORD_MERGE_DRIVER_STATE, + '\0'.join([self.mergedriver, self._mdstate]), + ) + ) # Write out state items. In all cases, the value of the state map entry # is written as the contents of the record. The record type depends on # the type of state that is stored, and capital-letter records are used @@ -418,30 +436,36 @@ for filename, v in self._state.iteritems(): if v[0] == MERGE_RECORD_DRIVER_RESOLVED: # Driver-resolved merge. These are stored in 'D' records. - records.append((RECORD_MERGE_DRIVER_MERGE, - '\0'.join([filename] + v))) - elif v[0] in (MERGE_RECORD_UNRESOLVED_PATH, - MERGE_RECORD_RESOLVED_PATH): + records.append( + (RECORD_MERGE_DRIVER_MERGE, '\0'.join([filename] + v)) + ) + elif v[0] in ( + MERGE_RECORD_UNRESOLVED_PATH, + MERGE_RECORD_RESOLVED_PATH, + ): # Path conflicts. These are stored in 'P' records. The current # resolution state ('pu' or 'pr') is stored within the record. - records.append((RECORD_PATH_CONFLICT, - '\0'.join([filename] + v))) + records.append( + (RECORD_PATH_CONFLICT, '\0'.join([filename] + v)) + ) elif v[1] == nullhex or v[6] == nullhex: # Change/Delete or Delete/Change conflicts. These are stored in # 'C' records. v[1] is the local file, and is nullhex when the # file is deleted locally ('dc'). v[6] is the remote file, and # is nullhex when the file is deleted remotely ('cd'). - records.append((RECORD_CHANGEDELETE_CONFLICT, - '\0'.join([filename] + v))) + records.append( + (RECORD_CHANGEDELETE_CONFLICT, '\0'.join([filename] + v)) + ) else: # Normal files. These are stored in 'F' records. - records.append((RECORD_MERGED, - '\0'.join([filename] + v))) + records.append((RECORD_MERGED, '\0'.join([filename] + v))) for filename, extras in sorted(self._stateextras.iteritems()): - rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in - extras.iteritems()) - records.append((RECORD_FILE_VALUES, - '%s\0%s' % (filename, rawextras))) + rawextras = '\0'.join( + '%s\0%s' % (k, v) for k, v in extras.iteritems() + ) + records.append( + (RECORD_FILE_VALUES, '%s\0%s' % (filename, rawextras)) + ) if self._labels is not None: labels = '\0'.join(self._labels) records.append((RECORD_LABELS, labels)) @@ -500,10 +524,16 @@ else: localkey = mergestate.getlocalkey(fcl.path()) self._repo.vfs.write('merge/' + localkey, fcl.data()) - self._state[fd] = [MERGE_RECORD_UNRESOLVED, localkey, fcl.path(), - fca.path(), hex(fca.filenode()), - fco.path(), hex(fco.filenode()), - fcl.flags()] + self._state[fd] = [ + MERGE_RECORD_UNRESOLVED, + localkey, + fcl.path(), + fca.path(), + hex(fca.filenode()), + fco.path(), + hex(fco.filenode()), + fcl.flags(), + ] self._stateextras[fd] = {'ancestorlinknode': hex(fca.node())} self._dirty = True @@ -539,8 +569,10 @@ """Obtain the paths of unresolved files.""" for f, entry in self._state.iteritems(): - if entry[0] in (MERGE_RECORD_UNRESOLVED, - MERGE_RECORD_UNRESOLVED_PATH): + if entry[0] in ( + MERGE_RECORD_UNRESOLVED, + MERGE_RECORD_UNRESOLVED_PATH, + ): yield f def driverresolved(self): @@ -555,8 +587,7 @@ def _resolve(self, preresolve, dfile, wctx): """rerun merge process for file path `dfile`""" - if self[dfile] in (MERGE_RECORD_RESOLVED, - MERGE_RECORD_DRIVER_RESOLVED): + if self[dfile] in (MERGE_RECORD_RESOLVED, MERGE_RECORD_DRIVER_RESOLVED): return True, 0 stateentry = self._state[dfile] state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry @@ -578,9 +609,12 @@ if fca.node() == nullid and flags != flo: if preresolve: self._repo.ui.warn( - _('warning: cannot merge flags for %s ' - 'without common ancestor - keeping local flags\n') - % afile) + _( + 'warning: cannot merge flags for %s ' + 'without common ancestor - keeping local flags\n' + ) + % afile + ) elif flags == fla: flags = flo if preresolve: @@ -591,15 +625,27 @@ f.close() else: wctx[dfile].remove(ignoremissing=True) - complete, r, deleted = filemerge.premerge(self._repo, wctx, - self._local, lfile, fcd, - fco, fca, - labels=self._labels) + complete, r, deleted = filemerge.premerge( + self._repo, + wctx, + self._local, + lfile, + fcd, + fco, + fca, + labels=self._labels, + ) else: - complete, r, deleted = filemerge.filemerge(self._repo, wctx, - self._local, lfile, fcd, - fco, fca, - labels=self._labels) + complete, r, deleted = filemerge.filemerge( + self._repo, + wctx, + self._local, + lfile, + fcd, + fco, + fca, + labels=self._labels, + ) if r is None: # no real conflict del self._state[dfile] @@ -619,9 +665,9 @@ # cd: remote picked (or otherwise deleted) action = ACTION_REMOVE else: - if fcd.isabsent(): # dc: remote picked + if fcd.isabsent(): # dc: remote picked action = ACTION_GET - elif fco.isabsent(): # cd: local picked + elif fco.isabsent(): # cd: local picked if dfile in self.localctx: action = ACTION_ADD_MODIFIED else: @@ -704,16 +750,19 @@ Meant for use by custom merge drivers.""" self._results[f] = 0, ACTION_GET + def _getcheckunknownconfig(repo, section, name): config = repo.ui.config(section, name) valid = ['abort', 'ignore', 'warn'] if config not in valid: validstr = ', '.join(["'" + v + "'" for v in valid]) - raise error.ConfigError(_("%s.%s not valid " - "('%s' is none of %s)") - % (section, name, config, validstr)) + raise error.ConfigError( + _("%s.%s not valid " "('%s' is none of %s)") + % (section, name, config, validstr) + ) return config + def _checkunknownfile(repo, wctx, mctx, f, f2=None): if wctx.isinmemory(): # Nothing to do in IMM because nothing in the "working copy" can be an @@ -725,10 +774,13 @@ if f2 is None: f2 = f - return (repo.wvfs.audit.check(f) + return ( + repo.wvfs.audit.check(f) and repo.wvfs.isfileorlink(f) and repo.dirstate.normalize(f) not in repo.dirstate - and mctx[f2].cmp(wctx[f])) + and mctx[f2].cmp(wctx[f]) + ) + class _unknowndirschecker(object): """ @@ -740,6 +792,7 @@ Returns the shortest path at which a conflict occurs, or None if there is no conflict. """ + def __init__(self): # A set of paths known to be good. This prevents repeated checking of # dirs. It will be updated with any new dirs that are checked and found @@ -763,8 +816,10 @@ if p in self._unknowndircache: continue if repo.wvfs.audit.check(p): - if (repo.wvfs.isfileorlink(p) - and repo.dirstate.normalize(p) not in repo.dirstate): + if ( + repo.wvfs.isfileorlink(p) + and repo.dirstate.normalize(p) not in repo.dirstate + ): return p if not repo.wvfs.lexists(p): self._missingdircache.add(p) @@ -782,6 +837,7 @@ return f return None + def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce): """ Considers any actions that care about the presence of conflicting unknown @@ -796,6 +852,7 @@ ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored') pathconfig = repo.ui.configbool('experimental', 'merge.checkpathconflicts') if not force: + def collectconflicts(conflicts, config): if config == 'abort': abortconflicts.update(conflicts) @@ -816,8 +873,7 @@ fileconflicts.add(f) allconflicts = fileconflicts | pathconflicts - ignoredconflicts = {c for c in allconflicts - if repo.dirstate._ignore(c)} + ignoredconflicts = {c for c in allconflicts if repo.dirstate._ignore(c)} unknownconflicts = allconflicts - ignoredconflicts collectconflicts(ignoredconflicts, ignoredconfig) collectconflicts(unknownconflicts, unknownconfig) @@ -846,8 +902,11 @@ if not different: actions[f] = (ACTION_GET, (fl2, False), 'remote created') elif mergeforce or config == 'abort': - actions[f] = (ACTION_MERGE, (f, f, None, False, anc), - 'remote differs from untracked local') + actions[f] = ( + ACTION_MERGE, + (f, f, None, False, anc), + 'remote differs from untracked local', + ) elif config == 'abort': abortconflicts.add(f) else: @@ -865,8 +924,12 @@ else: warn(_("%s: untracked file differs\n") % f) if abortconflicts: - raise error.Abort(_("untracked files in working directory " - "differ from files in requested revision")) + raise error.Abort( + _( + "untracked files in working directory " + "differ from files in requested revision" + ) + ) for f in sorted(warnconflicts): if repo.wvfs.isfileorlink(f): @@ -876,11 +939,15 @@ for f, (m, args, msg) in actions.iteritems(): if m == ACTION_CREATED: - backup = (f in fileconflicts or f in pathconflicts or - any(p in pathconflicts for p in util.finddirs(f))) - flags, = args + backup = ( + f in fileconflicts + or f in pathconflicts + or any(p in pathconflicts for p in util.finddirs(f)) + ) + (flags,) = args actions[f] = (ACTION_GET, (flags, backup), msg) + def _forgetremoved(wctx, mctx, branchmerge): """ Forget removed files @@ -911,6 +978,7 @@ return actions + def _checkcollision(repo, wmf, actions): """ Check for case-folding collisions. @@ -934,8 +1002,14 @@ if actions: # KEEP and EXEC are no-op - for m in (ACTION_ADD, ACTION_ADD_MODIFIED, ACTION_FORGET, ACTION_GET, - ACTION_CHANGED_DELETED, ACTION_DELETED_CHANGED): + for m in ( + ACTION_ADD, + ACTION_ADD_MODIFIED, + ACTION_FORGET, + ACTION_GET, + ACTION_CHANGED_DELETED, + ACTION_DELETED_CHANGED, + ): for f, args, msg in actions[m]: pmmf.add(f) for f, args, msg in actions[ACTION_REMOVE]: @@ -957,8 +1031,10 @@ for f in pmmf: fold = util.normcase(f) if fold in foldmap: - raise error.Abort(_("case-folding collision between %s and %s") - % (f, foldmap[fold])) + raise error.Abort( + _("case-folding collision between %s and %s") + % (f, foldmap[fold]) + ) foldmap[fold] = f # check case-folding of directories @@ -966,24 +1042,29 @@ for fold, f in sorted(foldmap.items()): if fold.startswith(foldprefix) and not f.startswith(unfoldprefix): # the folded prefix matches but actual casing is different - raise error.Abort(_("case-folding collision between " - "%s and directory of %s") % (lastfull, f)) + raise error.Abort( + _("case-folding collision between " "%s and directory of %s") + % (lastfull, f) + ) foldprefix = fold + '/' unfoldprefix = f + '/' lastfull = f + def driverpreprocess(repo, ms, wctx, labels=None): """run the preprocess step of the merge driver, if any This is currently not implemented -- it's an extension point.""" return True + def driverconclude(repo, ms, wctx, labels=None): """run the conclude step of the merge driver, if any This is currently not implemented -- it's an extension point.""" return True + def _filesindirs(repo, manifest, dirs): """ Generator that yields pairs of all the files in the manifest that are found @@ -996,6 +1077,7 @@ yield f, p break + def checkpathconflicts(repo, wctx, mctx, actions): """ Check if any actions introduce path conflicts in the repository, updating @@ -1022,8 +1104,12 @@ deletedfiles = set() for f, (m, args, msg) in actions.items(): - if m in (ACTION_CREATED, ACTION_DELETED_CHANGED, ACTION_MERGE, - ACTION_CREATED_MERGE): + if m in ( + ACTION_CREATED, + ACTION_DELETED_CHANGED, + ACTION_MERGE, + ACTION_CREATED_MERGE, + ): # This action may create a new local file. createdfiledirs.update(util.finddirs(f)) if mf.hasdir(f): @@ -1054,10 +1140,12 @@ # A file is in a directory which aliases a local file. # We will need to rename the local file. localconflicts.add(p) - if p in actions and actions[p][0] in (ACTION_CREATED, - ACTION_DELETED_CHANGED, - ACTION_MERGE, - ACTION_CREATED_MERGE): + if p in actions and actions[p][0] in ( + ACTION_CREATED, + ACTION_DELETED_CHANGED, + ACTION_MERGE, + ACTION_CREATED_MERGE, + ): # The file is in a directory which aliases a remote file. # This is an internal inconsistency within the remote # manifest. @@ -1068,10 +1156,12 @@ if p not in deletedfiles: ctxname = bytes(wctx).rstrip('+') pnew = util.safename(p, ctxname, wctx, set(actions.keys())) - actions[pnew] = (ACTION_PATH_CONFLICT_RESOLVE, (p,), - 'local path conflict') - actions[p] = (ACTION_PATH_CONFLICT, (pnew, 'l'), - 'path conflict') + actions[pnew] = ( + ACTION_PATH_CONFLICT_RESOLVE, + (p,), + 'local path conflict', + ) + actions[p] = (ACTION_PATH_CONFLICT, (pnew, 'l'), 'path conflict') if remoteconflicts: # Check if all files in the conflicting directories have been removed. @@ -1086,10 +1176,16 @@ else: # Action was create, change to renamed get action. fl = args[0] - actions[pnew] = (ACTION_LOCAL_DIR_RENAME_GET, (p, fl), - 'remote path conflict') - actions[p] = (ACTION_PATH_CONFLICT, (pnew, ACTION_REMOVE), - 'path conflict') + actions[pnew] = ( + ACTION_LOCAL_DIR_RENAME_GET, + (p, fl), + 'remote path conflict', + ) + actions[p] = ( + ACTION_PATH_CONFLICT, + (pnew, ACTION_REMOVE), + 'path conflict', + ) remoteconflicts.remove(p) break @@ -1098,6 +1194,7 @@ repo.ui.warn(_("%s: is both a file and a directory\n") % p) raise error.Abort(_("destination manifest contains path conflicts")) + def _filternarrowactions(narrowmatch, branchmerge, actions): """ Filters out actions that can ignored because the repo is narrowed. @@ -1105,7 +1202,7 @@ Raise an exception if the merge cannot be completed because the repo is narrowed. """ - nooptypes = {'k'} # TODO: handle with nonconflicttypes + nooptypes = {'k'} # TODO: handle with nonconflicttypes nonconflicttypes = set('a am c cm f g r e'.split()) # We mutate the items in the dict during iteration, so iterate # over a copy. @@ -1113,20 +1210,36 @@ if narrowmatch(f): pass elif not branchmerge: - del actions[f] # just updating, ignore changes outside clone + del actions[f] # just updating, ignore changes outside clone elif action[0] in nooptypes: - del actions[f] # merge does not affect file + del actions[f] # merge does not affect file elif action[0] in nonconflicttypes: - raise error.Abort(_('merge affects file \'%s\' outside narrow, ' - 'which is not yet supported') % f, - hint=_('merging in the other direction ' - 'may work')) + raise error.Abort( + _( + 'merge affects file \'%s\' outside narrow, ' + 'which is not yet supported' + ) + % f, + hint=_('merging in the other direction ' 'may work'), + ) else: - raise error.Abort(_('conflict in file \'%s\' is outside ' - 'narrow clone') % f) + raise error.Abort( + _('conflict in file \'%s\' is outside ' 'narrow clone') % f + ) + -def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher, - acceptremote, followcopies, forcefulldiff=False): +def manifestmerge( + repo, + wctx, + p2, + pa, + branchmerge, + force, + matcher, + acceptremote, + followcopies, + forcefulldiff=False, +): """ Merge wctx and p2 with ancestor pa and generate merge action list @@ -1140,8 +1253,10 @@ copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {} # manifests fetched in order are going to be faster, so prime the caches - [x.manifest() for x in - sorted(wctx.parents() + [p2, pa], key=scmutil.intrev)] + [ + x.manifest() + for x in sorted(wctx.parents() + [p2, pa], key=scmutil.intrev) + ] if followcopies: ret = copies.mergecopies(repo, wctx, p2, pa) @@ -1151,8 +1266,9 @@ boolf = pycompat.bytestr(bool(force)) boolm = pycompat.bytestr(bool(matcher)) repo.ui.note(_("resolving manifests\n")) - repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n" - % (boolbm, boolf, boolm)) + repo.ui.debug( + " branchmerge: %s, force: %s, partial: %s\n" % (boolbm, boolf, boolm) + ) repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2)) m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest() @@ -1170,7 +1286,7 @@ # - ma is the same as m1 or m2, which we're just going to diff again later # - The caller specifically asks for a full diff, which is useful during bid # merge. - if (pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff): + if pa not in ([wctx, p2] + wctx.parents()) and not forcefulldiff: # Identify which files are relevant to the merge, so we can limit the # total m1-vs-m2 diff to just those files. This has significant # performance benefits in large repositories. @@ -1189,57 +1305,80 @@ actions = {} for f, ((n1, fl1), (n2, fl2)) in diff.iteritems(): - if n1 and n2: # file exists on both local and remote side + if n1 and n2: # file exists on both local and remote side if f not in ma: fa = copy.get(f, None) if fa is not None: - actions[f] = (ACTION_MERGE, (f, f, fa, False, pa.node()), - 'both renamed from %s' % fa) + actions[f] = ( + ACTION_MERGE, + (f, f, fa, False, pa.node()), + 'both renamed from %s' % fa, + ) else: - actions[f] = (ACTION_MERGE, (f, f, None, False, pa.node()), - 'both created') + actions[f] = ( + ACTION_MERGE, + (f, f, None, False, pa.node()), + 'both created', + ) else: a = ma[f] fla = ma.flags(f) nol = 'l' not in fl1 + fl2 + fla if n2 == a and fl2 == fla: actions[f] = (ACTION_KEEP, (), 'remote unchanged') - elif n1 == a and fl1 == fla: # local unchanged - use remote - if n1 == n2: # optimization: keep local content + elif n1 == a and fl1 == fla: # local unchanged - use remote + if n1 == n2: # optimization: keep local content actions[f] = (ACTION_EXEC, (fl2,), 'update permissions') else: - actions[f] = (ACTION_GET, (fl2, False), - 'remote is newer') - elif nol and n2 == a: # remote only changed 'x' + actions[f] = ( + ACTION_GET, + (fl2, False), + 'remote is newer', + ) + elif nol and n2 == a: # remote only changed 'x' actions[f] = (ACTION_EXEC, (fl2,), 'update permissions') - elif nol and n1 == a: # local only changed 'x' + elif nol and n1 == a: # local only changed 'x' actions[f] = (ACTION_GET, (fl1, False), 'remote is newer') - else: # both changed something - actions[f] = (ACTION_MERGE, (f, f, f, False, pa.node()), - 'versions differ') - elif n1: # file exists only on local side + else: # both changed something + actions[f] = ( + ACTION_MERGE, + (f, f, f, False, pa.node()), + 'versions differ', + ) + elif n1: # file exists only on local side if f in copied: - pass # we'll deal with it on m2 side - elif f in movewithdir: # directory rename, move local + pass # we'll deal with it on m2 side + elif f in movewithdir: # directory rename, move local f2 = movewithdir[f] if f2 in m2: - actions[f2] = (ACTION_MERGE, (f, f2, None, True, pa.node()), - 'remote directory rename, both created') + actions[f2] = ( + ACTION_MERGE, + (f, f2, None, True, pa.node()), + 'remote directory rename, both created', + ) else: - actions[f2] = (ACTION_DIR_RENAME_MOVE_LOCAL, (f, fl1), - 'remote directory rename - move from %s' % f) + actions[f2] = ( + ACTION_DIR_RENAME_MOVE_LOCAL, + (f, fl1), + 'remote directory rename - move from %s' % f, + ) elif f in copy: f2 = copy[f] - actions[f] = (ACTION_MERGE, (f, f2, f2, False, pa.node()), - 'local copied/moved from %s' % f2) - elif f in ma: # clean, a different, no remote + actions[f] = ( + ACTION_MERGE, + (f, f2, f2, False, pa.node()), + 'local copied/moved from %s' % f2, + ) + elif f in ma: # clean, a different, no remote if n1 != ma[f]: if acceptremote: actions[f] = (ACTION_REMOVE, None, 'remote delete') else: - actions[f] = (ACTION_CHANGED_DELETED, - (f, None, f, False, pa.node()), - 'prompt changed/deleted') + actions[f] = ( + ACTION_CHANGED_DELETED, + (f, None, f, False, pa.node()), + 'prompt changed/deleted', + ) elif n1 == addednodeid: # This extra 'a' is added by working copy manifest to mark # the file as locally added. We should forget it instead of @@ -1247,26 +1386,37 @@ actions[f] = (ACTION_FORGET, None, 'remote deleted') else: actions[f] = (ACTION_REMOVE, None, 'other deleted') - elif n2: # file exists only on remote side + elif n2: # file exists only on remote side if f in copied: - pass # we'll deal with it on m1 side + pass # we'll deal with it on m1 side elif f in movewithdir: f2 = movewithdir[f] if f2 in m1: - actions[f2] = (ACTION_MERGE, - (f2, f, None, False, pa.node()), - 'local directory rename, both created') + actions[f2] = ( + ACTION_MERGE, + (f2, f, None, False, pa.node()), + 'local directory rename, both created', + ) else: - actions[f2] = (ACTION_LOCAL_DIR_RENAME_GET, (f, fl2), - 'local directory rename - get from %s' % f) + actions[f2] = ( + ACTION_LOCAL_DIR_RENAME_GET, + (f, fl2), + 'local directory rename - get from %s' % f, + ) elif f in copy: f2 = copy[f] if f2 in m2: - actions[f] = (ACTION_MERGE, (f2, f, f2, False, pa.node()), - 'remote copied from %s' % f2) + actions[f] = ( + ACTION_MERGE, + (f2, f, f2, False, pa.node()), + 'remote copied from %s' % f2, + ) else: - actions[f] = (ACTION_MERGE, (f2, f, f2, True, pa.node()), - 'remote moved from %s' % f2) + actions[f] = ( + ACTION_MERGE, + (f2, f, f2, True, pa.node()), + 'remote moved from %s' % f2, + ) elif f not in ma: # local unknown, remote created: the logic is described by the # following table: @@ -1284,25 +1434,32 @@ elif not branchmerge: actions[f] = (ACTION_CREATED, (fl2,), 'remote created') else: - actions[f] = (ACTION_CREATED_MERGE, (fl2, pa.node()), - 'remote created, get or merge') + actions[f] = ( + ACTION_CREATED_MERGE, + (fl2, pa.node()), + 'remote created, get or merge', + ) elif n2 != ma[f]: df = None for d in dirmove: if f.startswith(d): # new file added in a directory that was moved - df = dirmove[d] + f[len(d):] + df = dirmove[d] + f[len(d) :] break if df is not None and df in m1: - actions[df] = (ACTION_MERGE, (df, f, f, False, pa.node()), - 'local directory rename - respect move ' - 'from %s' % f) + actions[df] = ( + ACTION_MERGE, + (df, f, f, False, pa.node()), + 'local directory rename - respect move ' 'from %s' % f, + ) elif acceptremote: actions[f] = (ACTION_CREATED, (fl2,), 'remote recreating') else: - actions[f] = (ACTION_DELETED_CHANGED, - (None, f, f, False, pa.node()), - 'prompt deleted/changed') + actions[f] = ( + ACTION_DELETED_CHANGED, + (None, f, f, False, pa.node()), + 'prompt deleted/changed', + ) if repo.ui.configbool('experimental', 'merge.checkpathconflicts'): # If we are merging, look for path conflicts. @@ -1315,48 +1472,88 @@ return actions, diverge, renamedelete + def _resolvetrivial(repo, wctx, mctx, ancestor, actions): """Resolves false conflicts where the nodeid changed but the content remained the same.""" # We force a copy of actions.items() because we're going to mutate # actions as we resolve trivial conflicts. for f, (m, args, msg) in list(actions.items()): - if (m == ACTION_CHANGED_DELETED and f in ancestor - and not wctx[f].cmp(ancestor[f])): + if ( + m == ACTION_CHANGED_DELETED + and f in ancestor + and not wctx[f].cmp(ancestor[f]) + ): # local did change but ended up with same content actions[f] = ACTION_REMOVE, None, 'prompt same' - elif (m == ACTION_DELETED_CHANGED and f in ancestor - and not mctx[f].cmp(ancestor[f])): + elif ( + m == ACTION_DELETED_CHANGED + and f in ancestor + and not mctx[f].cmp(ancestor[f]) + ): # remote did change but ended up with same content - del actions[f] # don't get = keep local deleted + del actions[f] # don't get = keep local deleted + -def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, - acceptremote, followcopies, matcher=None, - mergeforce=False): +def calculateupdates( + repo, + wctx, + mctx, + ancestors, + branchmerge, + force, + acceptremote, + followcopies, + matcher=None, + mergeforce=False, +): """Calculate the actions needed to merge mctx into wctx using ancestors""" # Avoid cycle. from . import sparse - if len(ancestors) == 1: # default + if len(ancestors) == 1: # default actions, diverge, renamedelete = manifestmerge( - repo, wctx, mctx, ancestors[0], branchmerge, force, matcher, - acceptremote, followcopies) + repo, + wctx, + mctx, + ancestors[0], + branchmerge, + force, + matcher, + acceptremote, + followcopies, + ) _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce) - else: # only when merge.preferancestor=* - the default + else: # only when merge.preferancestor=* - the default repo.ui.note( - _("note: merging %s and %s using bids from ancestors %s\n") % - (wctx, mctx, _(' and ').join(pycompat.bytestr(anc) - for anc in ancestors))) + _("note: merging %s and %s using bids from ancestors %s\n") + % ( + wctx, + mctx, + _(' and ').join(pycompat.bytestr(anc) for anc in ancestors), + ) + ) # Call for bids - fbids = {} # mapping filename to bids (action method to list af actions) + fbids = ( + {} + ) # mapping filename to bids (action method to list af actions) diverge, renamedelete = None, None for ancestor in ancestors: repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor) actions, diverge1, renamedelete1 = manifestmerge( - repo, wctx, mctx, ancestor, branchmerge, force, matcher, - acceptremote, followcopies, forcefulldiff=True) + repo, + wctx, + mctx, + ancestor, + branchmerge, + force, + matcher, + acceptremote, + followcopies, + forcefulldiff=True, + ) _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce) # Track the shortest set of warning on the theory that bid @@ -1384,9 +1581,9 @@ for f, bids in sorted(fbids.items()): # bids is a mapping from action method to list af actions # Consensus? - if len(bids) == 1: # all bids are the same kind of method + if len(bids) == 1: # all bids are the same kind of method m, l = list(bids.items())[0] - if all(a == l[0] for a in l[1:]): # len(bids) is > 1 + if all(a == l[0] for a in l[1:]): # len(bids) is > 1 repo.ui.note(_(" %s: consensus for %s\n") % (f, m)) actions[f] = l[0] continue @@ -1410,8 +1607,9 @@ repo.ui.note(' %s -> %s\n' % (msg, m)) # Pick random action. TODO: Instead, prompt user when resolving m, l = list(bids.items())[0] - repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') % - (f, m)) + repo.ui.warn( + _(' %s: ambiguous merge - picked %s action\n') % (f, m) + ) actions[f] = l[0] continue repo.ui.note(_('end of auction\n\n')) @@ -1420,12 +1618,14 @@ fractions = _forgetremoved(wctx, mctx, branchmerge) actions.update(fractions) - prunedactions = sparse.filterupdatesactions(repo, wctx, mctx, branchmerge, - actions) + prunedactions = sparse.filterupdatesactions( + repo, wctx, mctx, branchmerge, actions + ) _resolvetrivial(repo, wctx, mctx, ancestors[0], actions) return prunedactions, diverge, renamedelete + def _getcwd(): try: return encoding.getcwd() @@ -1434,6 +1634,7 @@ return None raise + def batchremove(repo, wctx, actions): """apply removes to the working directory @@ -1450,8 +1651,9 @@ try: wctx[f].remove(ignoremissing=True) except OSError as inst: - repo.ui.warn(_("update failed to remove %s: %s!\n") % - (f, inst.strerror)) + repo.ui.warn( + _("update failed to remove %s: %s!\n") % (f, inst.strerror) + ) if i == 100: yield i, f i = 0 @@ -1462,8 +1664,14 @@ if cwd and not _getcwd(): # cwd was removed in the course of removing files; print a helpful # warning. - repo.ui.warn(_("current directory was removed\n" - "(consider changing to repo root: %s)\n") % repo.root) + repo.ui.warn( + _( + "current directory was removed\n" + "(consider changing to repo root: %s)\n" + ) + % repo.root + ) + def batchget(repo, mctx, wctx, wantfiledata, actions): """apply gets to the working directory @@ -1502,14 +1710,17 @@ wfctx = wctx[f] wfctx.clearunknown() atomictemp = ui.configbool("experimental", "update.atomic-file") - size = wfctx.write(fctx(f).data(), flags, - backgroundclose=True, - atomictemp=atomictemp) + size = wfctx.write( + fctx(f).data(), + flags, + backgroundclose=True, + atomictemp=atomictemp, + ) if wantfiledata: s = wfctx.lstat() mode = s.st_mode mtime = s[stat.ST_MTIME] - filedata[f] = ((mode, size, mtime)) # for dirstate.normal + filedata[f] = (mode, size, mtime) # for dirstate.normal if i == 100: yield False, (i, f) i = 0 @@ -1518,6 +1729,7 @@ yield False, (i, f) yield True, filedata + def _prefetchfiles(repo, ctx, actions): """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict of merge actions. ``ctx`` is the context being merged in.""" @@ -1525,13 +1737,23 @@ # Skipping 'a', 'am', 'f', 'r', 'dm', 'e', 'k', 'p' and 'pr', because they # don't touch the context to be merged in. 'cd' is skipped, because # changed/deleted never resolves to something from the remote side. - oplist = [actions[a] for a in (ACTION_GET, ACTION_DELETED_CHANGED, - ACTION_LOCAL_DIR_RENAME_GET, ACTION_MERGE)] + oplist = [ + actions[a] + for a in ( + ACTION_GET, + ACTION_DELETED_CHANGED, + ACTION_LOCAL_DIR_RENAME_GET, + ACTION_MERGE, + ) + ] prefetch = scmutil.prefetchfiles matchfiles = scmutil.matchfiles - prefetch(repo, [ctx.rev()], - matchfiles(repo, - [f for sublist in oplist for f, args, msg in sublist])) + prefetch( + repo, + [ctx.rev()], + matchfiles(repo, [f for sublist in oplist for f, args, msg in sublist]), + ) + @attr.s(frozen=True) class updateresult(object): @@ -1541,30 +1763,40 @@ unresolvedcount = attr.ib() def isempty(self): - return not (self.updatedcount or self.mergedcount - or self.removedcount or self.unresolvedcount) + return not ( + self.updatedcount + or self.mergedcount + or self.removedcount + or self.unresolvedcount + ) + def emptyactions(): """create an actions dict, to be populated and passed to applyupdates()""" - return dict((m, []) - for m in ( - ACTION_ADD, - ACTION_ADD_MODIFIED, - ACTION_FORGET, - ACTION_GET, - ACTION_CHANGED_DELETED, - ACTION_DELETED_CHANGED, - ACTION_REMOVE, - ACTION_DIR_RENAME_MOVE_LOCAL, - ACTION_LOCAL_DIR_RENAME_GET, - ACTION_MERGE, - ACTION_EXEC, - ACTION_KEEP, - ACTION_PATH_CONFLICT, - ACTION_PATH_CONFLICT_RESOLVE)) + return dict( + (m, []) + for m in ( + ACTION_ADD, + ACTION_ADD_MODIFIED, + ACTION_FORGET, + ACTION_GET, + ACTION_CHANGED_DELETED, + ACTION_DELETED_CHANGED, + ACTION_REMOVE, + ACTION_DIR_RENAME_MOVE_LOCAL, + ACTION_LOCAL_DIR_RENAME_GET, + ACTION_MERGE, + ACTION_EXEC, + ACTION_KEEP, + ACTION_PATH_CONFLICT, + ACTION_PATH_CONFLICT_RESOLVE, + ) + ) -def applyupdates(repo, actions, wctx, mctx, overwrite, wantfiledata, - labels=None): + +def applyupdates( + repo, actions, wctx, mctx, overwrite, wantfiledata, labels=None +): """apply the merge action list to the working directory wctx is the working copy context @@ -1590,7 +1822,7 @@ mergeactions.extend(actions[ACTION_MERGE]) for f, args, msg in mergeactions: f1, f2, fa, move, anc = args - if f == '.hgsubstate': # merged internally + if f == '.hgsubstate': # merged internally continue if f1 is None: fcl = filemerge.absentfilectx(wctx, fa) @@ -1618,10 +1850,10 @@ wctx[f].audit() wctx[f].remove() - numupdates = sum(len(l) for m, l in actions.items() - if m != ACTION_KEEP) - progress = repo.ui.makeprogress(_('updating'), unit=_('files'), - total=numupdates) + numupdates = sum(len(l) for m, l in actions.items() if m != ACTION_KEEP) + progress = repo.ui.makeprogress( + _('updating'), unit=_('files'), total=numupdates + ) if [a for a in actions[ACTION_REMOVE] if a[0] == '.hgsubstate']: subrepoutil.submerge(repo, wctx, mctx, wctx, overwrite, labels) @@ -1630,8 +1862,13 @@ for f, args, msg in actions[ACTION_PATH_CONFLICT]: f1, fo = args s = repo.ui.status - s(_("%s: path conflict - a file or link has the same name as a " - "directory\n") % f) + s( + _( + "%s: path conflict - a file or link has the same name as a " + "directory\n" + ) + % f + ) if fo == 'l': s(_("the local file has been renamed to %s\n") % f1) else: @@ -1645,8 +1882,9 @@ cost = 0 if wctx.isinmemory() else 0.001 # remove in parallel (must come before resolving path conflicts and getting) - prog = worker.worker(repo.ui, cost, batchremove, (repo, wctx), - actions[ACTION_REMOVE]) + prog = worker.worker( + repo.ui, cost, batchremove, (repo, wctx), actions[ACTION_REMOVE] + ) for i, item in prog: progress.increment(step=i, item=item) removed = len(actions[ACTION_REMOVE]) @@ -1654,7 +1892,7 @@ # resolve path conflicts (must come before getting) for f, args, msg in actions[ACTION_PATH_CONFLICT_RESOLVE]: repo.ui.debug(" %s: %s -> pr\n" % (f, msg)) - f0, = args + (f0,) = args if wctx[f0].lexists(): repo.ui.note(_("moving %s to %s\n") % (f0, f)) wctx[f].audit() @@ -1663,13 +1901,18 @@ progress.increment(item=f) # get in parallel. - threadsafe = repo.ui.configbool('experimental', - 'worker.wdir-get-thread-safe') - prog = worker.worker(repo.ui, cost, batchget, - (repo, mctx, wctx, wantfiledata), - actions[ACTION_GET], - threadsafe=threadsafe, - hasretval=True) + threadsafe = repo.ui.configbool( + 'experimental', 'worker.wdir-get-thread-safe' + ) + prog = worker.worker( + repo.ui, + cost, + batchget, + (repo, mctx, wctx, wantfiledata), + actions[ACTION_GET], + threadsafe=threadsafe, + hasretval=True, + ) getfiledata = {} for final, res in prog: if final: @@ -1726,7 +1969,7 @@ for f, args, msg in actions[ACTION_EXEC]: repo.ui.debug(" %s: %s -> e\n" % (f, msg)) progress.increment(item=f) - flags, = args + (flags,) = args wctx[f].audit() wctx[f].setflags('l' in flags, 'x' in flags) updated += 1 @@ -1738,8 +1981,9 @@ if usemergedriver: if wctx.isinmemory(): - raise error.InMemoryMergeConflictsError("in-memory merge does not " - "support mergedriver") + raise error.InMemoryMergeConflictsError( + "in-memory merge does not " "support mergedriver" + ) ms.commit() proceed = driverpreprocess(repo, ms, wctx, labels=labels) # the driver might leave some files unresolved @@ -1747,8 +1991,9 @@ if not proceed: # XXX setting unresolved to at least 1 is a hack to make sure we # error out - return updateresult(updated, merged, removed, - max(len(unresolvedf), 1)) + return updateresult( + updated, merged, removed, max(len(unresolvedf), 1) + ) newactions = [] for f, args, msg in mergeactions: if f in unresolvedf: @@ -1761,9 +2006,10 @@ for f, args, msg in mergeactions: repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg)) progress.increment(item=f) - if f == '.hgsubstate': # subrepo states need updating - subrepoutil.submerge(repo, wctx, mctx, wctx.ancestor(mctx), - overwrite, labels) + if f == '.hgsubstate': # subrepo states need updating + subrepoutil.submerge( + repo, wctx, mctx, wctx.ancestor(mctx), overwrite, labels + ) continue wctx[f].audit() complete, r = ms.preresolve(f, wctx) @@ -1782,8 +2028,11 @@ unresolved = ms.unresolvedcount() - if (usemergedriver and not unresolved - and ms.mdstate() != MERGE_DRIVER_STATE_SUCCESS): + if ( + usemergedriver + and not unresolved + and ms.mdstate() != MERGE_DRIVER_STATE_SUCCESS + ): if not driverconclude(repo, ms, wctx, labels=labels): # XXX setting unresolved to at least 1 is a hack to make sure we # error out @@ -1823,13 +2072,15 @@ # those lists aren't consulted again. mfiles.difference_update(a[0] for a in acts) - actions[ACTION_MERGE] = [a for a in actions[ACTION_MERGE] - if a[0] in mfiles] + actions[ACTION_MERGE] = [ + a for a in actions[ACTION_MERGE] if a[0] in mfiles + ] progress.complete() assert len(getfiledata) == (len(actions[ACTION_GET]) if wantfiledata else 0) return updateresult(updated, merged, removed, unresolved), getfiledata + def recordupdates(repo, actions, branchmerge, getfiledata): "record merge actions to the dirstate" # remove (must come first) @@ -1845,7 +2096,7 @@ # resolve path conflicts for f, args, msg in actions.get(ACTION_PATH_CONFLICT_RESOLVE, []): - f0, = args + (f0,) = args origf0 = repo.dirstate.copied(f0) or f0 repo.dirstate.add(f) repo.dirstate.copy(origf0, f) @@ -1888,7 +2139,7 @@ # We've done a branch merge, mark this file as merged # so that we properly record the merger later repo.dirstate.merge(f) - if f1 != f2: # copy/rename + if f1 != f2: # copy/rename if move: repo.dirstate.remove(f1) if f1 != f: @@ -1901,7 +2152,7 @@ # of that file some time in the past. Thus our # merge will appear as a normal local file # modification. - if f2 == f: # file not locally copied/moved + if f2 == f: # file not locally copied/moved repo.dirstate.normallookup(f) if move: repo.dirstate.drop(f1) @@ -1926,14 +2177,26 @@ else: repo.dirstate.normal(f) + UPDATECHECK_ABORT = 'abort' # handled at higher layers UPDATECHECK_NONE = 'none' UPDATECHECK_LINEAR = 'linear' UPDATECHECK_NO_CONFLICT = 'noconflict' -def update(repo, node, branchmerge, force, ancestor=None, - mergeancestor=False, labels=None, matcher=None, mergeforce=False, - updatecheck=None, wc=None): + +def update( + repo, + node, + branchmerge, + force, + ancestor=None, + mergeancestor=False, + labels=None, + matcher=None, + mergeforce=False, + updatecheck=None, + wc=None, +): """ Perform a merge between the working directory and the given node @@ -1998,15 +2261,22 @@ # updatecheck='abort' to better suppport some of these callers. if updatecheck is None: updatecheck = UPDATECHECK_LINEAR - if updatecheck not in (UPDATECHECK_NONE, - UPDATECHECK_LINEAR, - UPDATECHECK_NO_CONFLICT, + if updatecheck not in ( + UPDATECHECK_NONE, + UPDATECHECK_LINEAR, + UPDATECHECK_NO_CONFLICT, ): - raise ValueError(r'Invalid updatecheck %r (can accept %r)' % ( - updatecheck, (UPDATECHECK_NONE, - UPDATECHECK_LINEAR, - UPDATECHECK_NO_CONFLICT, - ))) + raise ValueError( + r'Invalid updatecheck %r (can accept %r)' + % ( + updatecheck, + ( + UPDATECHECK_NONE, + UPDATECHECK_LINEAR, + UPDATECHECK_NO_CONFLICT, + ), + ) + ) # If we're doing a partial update, we need to skip updating # the dirstate, so make a note of any partial-ness to the # update here. @@ -2038,33 +2308,44 @@ raise error.Abort(_("outstanding uncommitted merge")) ms = mergestate.read(repo) if list(ms.unresolved()): - raise error.Abort(_("outstanding merge conflicts"), - hint=_("use 'hg resolve' to resolve")) + raise error.Abort( + _("outstanding merge conflicts"), + hint=_("use 'hg resolve' to resolve"), + ) if branchmerge: if pas == [p2]: - raise error.Abort(_("merging with a working directory ancestor" - " has no effect")) + raise error.Abort( + _( + "merging with a working directory ancestor" + " has no effect" + ) + ) elif pas == [p1]: if not mergeancestor and wc.branch() == p2.branch(): - raise error.Abort(_("nothing to merge"), - hint=_("use 'hg update' " - "or check 'hg heads'")) + raise error.Abort( + _("nothing to merge"), + hint=_("use 'hg update' " "or check 'hg heads'"), + ) if not force and (wc.files() or wc.deleted()): - raise error.Abort(_("uncommitted changes"), - hint=_("use 'hg status' to list changes")) + raise error.Abort( + _("uncommitted changes"), + hint=_("use 'hg status' to list changes"), + ) if not wc.isinmemory(): for s in sorted(wc.substate): wc.sub(s).bailifchanged() elif not overwrite: - if p1 == p2: # no-op update + if p1 == p2: # no-op update # call the hooks and exit early repo.hook('preupdate', throw=True, parent1=xp2, parent2='') repo.hook('update', parent1=xp2, parent2='', error=0) return updateresult(0, 0, 0, 0) - if (updatecheck == UPDATECHECK_LINEAR and - pas not in ([p1], [p2])): # nonlinear + if updatecheck == UPDATECHECK_LINEAR and pas not in ( + [p1], + [p2], + ): # nonlinear dirty = wc.dirty(missing=True) if dirty: # Branching is a bit strange to ensure we do the minimal @@ -2072,7 +2353,7 @@ foreground = obsutil.foreground(repo, [p1.node()]) # note: the <node> variable contains a random identifier if repo[node].node() in foreground: - pass # allow updating to successors + pass # allow updating to successors else: msg = _("uncommitted changes") hint = _("commit or update --clean to discard changes") @@ -2097,13 +2378,27 @@ ### calculate phase actionbyfile, diverge, renamedelete = calculateupdates( - repo, wc, p2, pas, branchmerge, force, mergeancestor, - followcopies, matcher=matcher, mergeforce=mergeforce) + repo, + wc, + p2, + pas, + branchmerge, + force, + mergeancestor, + followcopies, + matcher=matcher, + mergeforce=mergeforce, + ) if updatecheck == UPDATECHECK_NO_CONFLICT: for f, (m, args, msg) in actionbyfile.iteritems(): - if m not in (ACTION_GET, ACTION_KEEP, ACTION_EXEC, - ACTION_REMOVE, ACTION_PATH_CONFLICT_RESOLVE): + if m not in ( + ACTION_GET, + ACTION_KEEP, + ACTION_EXEC, + ACTION_REMOVE, + ACTION_PATH_CONFLICT_RESOLVE, + ): msg = _("conflicting changes") hint = _("commit or update --clean to discard changes") raise error.Abort(msg, hint=hint) @@ -2118,9 +2413,14 @@ prompts['f'] = f if m == ACTION_CHANGED_DELETED: if repo.ui.promptchoice( - _("local%(l)s changed %(f)s which other%(o)s deleted\n" - "use (c)hanged version or (d)elete?" - "$$ &Changed $$ &Delete") % prompts, 0): + _( + "local%(l)s changed %(f)s which other%(o)s deleted\n" + "use (c)hanged version or (d)elete?" + "$$ &Changed $$ &Delete" + ) + % prompts, + 0, + ): actionbyfile[f] = (ACTION_REMOVE, None, 'prompt delete') elif f in p1: actionbyfile[f] = (ACTION_ADD_MODIFIED, None, 'prompt keep') @@ -2129,12 +2429,23 @@ elif m == ACTION_DELETED_CHANGED: f1, f2, fa, move, anc = args flags = p2[f2].flags() - if repo.ui.promptchoice( - _("other%(o)s changed %(f)s which local%(l)s deleted\n" - "use (c)hanged version or leave (d)eleted?" - "$$ &Changed $$ &Deleted") % prompts, 0) == 0: - actionbyfile[f] = (ACTION_GET, (flags, False), - 'prompt recreating') + if ( + repo.ui.promptchoice( + _( + "other%(o)s changed %(f)s which local%(l)s deleted\n" + "use (c)hanged version or leave (d)eleted?" + "$$ &Changed $$ &Deleted" + ) + % prompts, + 0, + ) + == 0 + ): + actionbyfile[f] = ( + ACTION_GET, + (flags, False), + 'prompt recreating', + ) else: del actionbyfile[f] @@ -2147,28 +2458,39 @@ if not util.fscasesensitive(repo.path): # check collision between files only in p2 for clean update - if (not branchmerge and - (force or not wc.dirty(missing=True, branch=False))): + if not branchmerge and ( + force or not wc.dirty(missing=True, branch=False) + ): _checkcollision(repo, p2.manifest(), None) else: _checkcollision(repo, wc.manifest(), actions) # divergent renames for f, fl in sorted(diverge.iteritems()): - repo.ui.warn(_("note: possible conflict - %s was renamed " - "multiple times to:\n") % f) + repo.ui.warn( + _( + "note: possible conflict - %s was renamed " + "multiple times to:\n" + ) + % f + ) for nf in sorted(fl): repo.ui.warn(" %s\n" % nf) # rename and delete for f, fl in sorted(renamedelete.iteritems()): - repo.ui.warn(_("note: possible conflict - %s was deleted " - "and renamed to:\n") % f) + repo.ui.warn( + _( + "note: possible conflict - %s was deleted " + "and renamed to:\n" + ) + % f + ) for nf in sorted(fl): repo.ui.warn(" %s\n" % nf) ### apply phase - if not branchmerge: # just jump to the new rev + if not branchmerge: # just jump to the new rev fp1, fp2, xp1, xp2 = fp2, nullid, xp2, '' if not partial and not wc.isinmemory(): repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2) @@ -2186,11 +2508,13 @@ # We only allow on Linux and MacOS because that's where fsmonitor is # considered stable. fsmonitorwarning = repo.ui.configbool('fsmonitor', 'warn_when_unused') - fsmonitorthreshold = repo.ui.configint('fsmonitor', - 'warn_update_file_count') + fsmonitorthreshold = repo.ui.configint( + 'fsmonitor', 'warn_update_file_count' + ) try: # avoid cycle: extensions -> cmdutil -> merge from . import extensions + extensions.find('fsmonitor') fsmonitorenabled = repo.ui.config('fsmonitor', 'mode') != 'off' # We intentionally don't look at whether fsmonitor has disabled @@ -2199,20 +2523,26 @@ except KeyError: fsmonitorenabled = False - if (fsmonitorwarning - and not fsmonitorenabled - and p1.node() == nullid - and len(actions[ACTION_GET]) >= fsmonitorthreshold - and pycompat.sysplatform.startswith(('linux', 'darwin'))): + if ( + fsmonitorwarning + and not fsmonitorenabled + and p1.node() == nullid + and len(actions[ACTION_GET]) >= fsmonitorthreshold + and pycompat.sysplatform.startswith(('linux', 'darwin')) + ): repo.ui.warn( - _('(warning: large working directory being used without ' - 'fsmonitor enabled; enable fsmonitor to improve performance; ' - 'see "hg help -e fsmonitor")\n')) + _( + '(warning: large working directory being used without ' + 'fsmonitor enabled; enable fsmonitor to improve performance; ' + 'see "hg help -e fsmonitor")\n' + ) + ) updatedirstate = not partial and not wc.isinmemory() wantfiledata = updatedirstate and not branchmerge - stats, getfiledata = applyupdates(repo, actions, wc, p2, overwrite, - wantfiledata, labels=labels) + stats, getfiledata = applyupdates( + repo, actions, wc, p2, overwrite, wantfiledata, labels=labels + ) if updatedirstate: with repo.dirstate.parentchange(): @@ -2230,12 +2560,15 @@ sparse.prunetemporaryincludes(repo) if not partial: - repo.hook('update', parent1=xp1, parent2=xp2, - error=stats.unresolvedcount) + repo.hook( + 'update', parent1=xp1, parent2=xp2, error=stats.unresolvedcount + ) return stats -def graft(repo, ctx, pctx, labels=None, keepparent=False, - keepconflictparent=False): + +def graft( + repo, ctx, pctx, labels=None, keepparent=False, keepconflictparent=False +): """Do a graft-like merge. This is a merge where the merge ancestor is chosen such that one @@ -2259,9 +2592,15 @@ # which local deleted". mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node()) - stats = update(repo, ctx.node(), True, True, pctx.node(), - mergeancestor=mergeancestor, labels=labels) - + stats = update( + repo, + ctx.node(), + True, + True, + pctx.node(), + mergeancestor=mergeancestor, + labels=labels, + ) if keepconflictparent and stats.unresolvedcount: pother = ctx.node() @@ -2279,8 +2618,16 @@ copies.duplicatecopies(repo, repo[None], ctx.rev(), pctx.rev()) return stats -def purge(repo, matcher, ignored=False, removeemptydirs=True, - removefiles=True, abortonerror=False, noop=False): + +def purge( + repo, + matcher, + ignored=False, + removeemptydirs=True, + removefiles=True, + abortonerror=False, + noop=False, +): """Purge the working directory of untracked files. ``matcher`` is a matcher configured to scan the working directory -