diff -r 5ca136bbd3f6 -r 87a34c767384 mercurial/merge.py --- a/mercurial/merge.py Wed Jun 12 13:10:52 2019 -0400 +++ b/mercurial/merge.py Mon May 27 16:55:46 2019 -0400 @@ -10,6 +10,7 @@ import errno import hashlib import shutil +import stat import struct from .i18n import _ @@ -683,7 +684,7 @@ def recordactions(self): """record remove/add/get actions in the dirstate""" branchmerge = self._repo.dirstate.p2() != nullid - recordupdates(self._repo, self.actions(), branchmerge) + recordupdates(self._repo, self.actions(), branchmerge, None) def queueremove(self, f): """queues a file to be removed from the dirstate @@ -1464,13 +1465,17 @@ repo.ui.warn(_("current directory was removed\n" "(consider changing to repo root: %s)\n") % repo.root) -def batchget(repo, mctx, wctx, actions): +def batchget(repo, mctx, wctx, wantfiledata, actions): """apply gets to the working directory mctx is the context to get from - yields tuples for progress updates + Yields arbitrarily many (False, tuple) for progress updates, followed by + exactly one (True, filedata). When wantfiledata is false, filedata is an + empty list. When wantfiledata is true, filedata[i] is a triple (mode, size, + mtime) of the file written for action[i]. """ + filedata = [] verbose = repo.ui.verbose fctx = mctx.filectx ui = repo.ui @@ -1494,16 +1499,24 @@ if repo.wvfs.lexists(conflicting): orig = scmutil.backuppath(ui, repo, conflicting) util.rename(repo.wjoin(conflicting), orig) - wctx[f].clearunknown() + wfctx = wctx[f] + wfctx.clearunknown() atomictemp = ui.configbool("experimental", "update.atomic-file") - wctx[f].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.append((mode, size, mtime)) # for dirstate.normal if i == 100: - yield i, f + yield False, (i, f) i = 0 i += 1 if i > 0: - yield i, f + yield False, (i, f) + yield True, filedata def _prefetchfiles(repo, ctx, actions): """Invoke ``scmutil.prefetchfiles()`` for the files relevant to the dict @@ -1550,14 +1563,17 @@ ACTION_PATH_CONFLICT, ACTION_PATH_CONFLICT_RESOLVE)) -def applyupdates(repo, actions, wctx, mctx, overwrite, 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 mctx is the context to be merged into the working copy - Return a tuple of counts (updated, merged, removed, unresolved) that - describes how many files were affected by the update. + Return a tuple of (counts, filedata), where counts is a tuple + (updated, merged, removed, unresolved) that describes how many + files were affected by the update, and filedata is as described in + batchget. """ _prefetchfiles(repo, mctx, actions) @@ -1649,11 +1665,18 @@ # get in parallel. threadsafe = repo.ui.configbool('experimental', 'worker.wdir-get-thread-safe') - prog = worker.worker(repo.ui, cost, batchget, (repo, mctx, wctx), + prog = worker.worker(repo.ui, cost, batchget, + (repo, mctx, wctx, wantfiledata), actions[ACTION_GET], - threadsafe=threadsafe) - for i, item in prog: - progress.increment(step=i, item=item) + threadsafe=threadsafe, + hasretval=True) + getfiledata = [] + for final, res in prog: + if final: + getfiledata = res + else: + i, item = res + progress.increment(step=i, item=item) updated = len(actions[ACTION_GET]) if [a for a in actions[ACTION_GET] if a[0] == '.hgsubstate']: @@ -1778,6 +1801,9 @@ mfiles = set(a[0] for a in actions[ACTION_MERGE]) for k, acts in extraactions.iteritems(): actions[k].extend(acts) + if k == ACTION_GET and wantfiledata: + # no filedata until mergestate is updated to provide it + getfiledata.extend([None] * len(acts)) # Remove these files from actions[ACTION_MERGE] as well. This is # important because in recordupdates, files in actions[ACTION_MERGE] # are processed after files in other actions, and the merge driver @@ -1800,9 +1826,10 @@ if a[0] in mfiles] progress.complete() - return updateresult(updated, merged, removed, unresolved) + assert len(getfiledata) == (len(actions[ACTION_GET]) if wantfiledata else 0) + return updateresult(updated, merged, removed, unresolved), getfiledata -def recordupdates(repo, actions, branchmerge): +def recordupdates(repo, actions, branchmerge, getfiledata): "record merge actions to the dirstate" # remove (must come first) for f, args, msg in actions.get(ACTION_REMOVE, []): @@ -1846,11 +1873,12 @@ pass # get - for f, args, msg in actions.get(ACTION_GET, []): + for i, (f, args, msg) in enumerate(actions.get(ACTION_GET, [])): if branchmerge: repo.dirstate.otherparent(f) else: - repo.dirstate.normal(f) + parentfiledata = getfiledata[i] if getfiledata else None + repo.dirstate.normal(f, parentfiledata=parentfiledata) # merge for f, args, msg in actions.get(ACTION_MERGE, []): @@ -2166,12 +2194,15 @@ 'fsmonitor enabled; enable fsmonitor to improve performance; ' 'see "hg help -e fsmonitor")\n')) - stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels) + updatedirstate = not partial and not wc.isinmemory() + wantfiledata = updatedirstate and not branchmerge + stats, getfiledata = applyupdates(repo, actions, wc, p2, overwrite, + wantfiledata, labels=labels) - if not partial and not wc.isinmemory(): + if updatedirstate: with repo.dirstate.parentchange(): repo.setparents(fp1, fp2) - recordupdates(repo, actions, branchmerge) + recordupdates(repo, actions, branchmerge, getfiledata) # update completed, clear state util.unlink(repo.vfs.join('updatestate'))