D616: context: add overlayworkingcontext and overlayworkingfilectx
phillco updated this revision to Diff 1708. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D616?vs=1707&id=1708 REVISION DETAIL https://phab.mercurial-scm.org/D616 AFFECTED FILES mercurial/context.py CHANGE DETAILS diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -1661,6 +1661,9 @@ listsubrepos=listsubrepos, badfn=badfn, icasefs=icasefs) +def flushall(self): +pass # For overlayworkingfilectx compatibility. + def _filtersuspectsymlink(self, files): if not files or self._repo.dirstate._checklink: return files @@ -1974,6 +1977,198 @@ def setflags(self, l, x): self._repo.wvfs.setflags(self._path, l, x) +class overlayworkingctx(workingctx): +"""Wraps another mutable context with a write-back cache that can be flushed +at a later time. + +self._cache[path] maps to a dict with keys: { +'exists': bool? +'date': date? +'data': str? +'flags': str? +} +If `exists` is True, `flags` must be non-None and 'date' is non-None. If it +is `False`, the file was deleted. +""" + +def __init__(self, repo, wrappedctx): +super(overlayworkingctx, self).__init__(repo) +self._repo = repo +self._wrappedctx = wrappedctx +self._clean() + +def data(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +if self._cache[path]['data']: +return self._cache[path]['data'] +else: +# Must fallback here, too, because we only set flags. +return self._wrappedctx[path].data() +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].data() + +def filedate(self, path): +if self.isdirty(path): +return self._cache[path]['date'] +else: +return self._wrappedctx[path].date() + +def flags(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return self._cache[path]['flags'] +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].flags() + +def write(self, path, data, flags=None): +if data is None: +raise error.ProgrammingError("data must be non-None") +self._markdirty(path) +self._cache[path]['exists'] = True +self._cache[path]['data'] = data +self._cache[path]['date'] = util.makedate() +if flags is not None: +self._cache[path]['flags'] = flags + +def setflags(self, path, l, x): +self._markdirty(path, exists=True, date=util.makedate(), +flags=(l and 'l' or '') + (x and 'x' or '')) + +def remove(self, path): +self._markdirty(path, exists=False) + +def exists(self, path): +"""exists behaves like `lexists`, but needs to follow symlinks and +return False if they are broken. +""" +if self.isdirty(path): +# If this path exists and is a symlink, "follow" it by calling +# exists on the destination path. +if (self._cache[path]['exists'] and +'l' in self._cache[path]['flags']): +return self.exists(self._cache[path]['data']) +else: +return self._cache[path]['exists'] +return self._wrappedctx[path].exists() + +def lexists(self, path): +"""lexists returns True if the path exists""" +if self.isdirty(path): +return self._cache[path]['exists'] +return self._wrappedctx[path].lexists() + +def size(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return len(self._cache[path]['data']) +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +return self._wrappedctx[path].size() + +def flushall(self): +for path in self._writeorder: +entry = self._cache[path] +if entry['exists']: +self._wrappedctx[path].clearunknown() +if entry['data'] is not None: +if entry['flags'] is None: +raise error.ProgrammingError('data set but not flags') +self._wrappedctx[path].write( +entry['data'], +entry['flags']) +else: +self._wrappedctx[path].setflags( +'l' in e
D616: context: add overlayworkingcontext and overlayworkingfilectx
phillco updated this revision to Diff 1707. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D616?vs=1706&id=1707 REVISION DETAIL https://phab.mercurial-scm.org/D616 AFFECTED FILES mercurial/context.py CHANGE DETAILS diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -1661,6 +1661,9 @@ listsubrepos=listsubrepos, badfn=badfn, icasefs=icasefs) +def flushall(self): +pass # For overlayworkingfilectx compatibility. + def _filtersuspectsymlink(self, files): if not files or self._repo.dirstate._checklink: return files @@ -1974,6 +1977,198 @@ def setflags(self, l, x): self._repo.wvfs.setflags(self._path, l, x) +class overlayworkingctx(workingctx): +"""Wraps another mutable context with a write-back cache that can be flushed +at a later time. + +self._cache[path] maps to a dict with keys: { +'exists': bool? +'date': date? +'data': str? +'flags': str? +} +If `exists` is True, `flags` must be non-None and 'date' is non-None. If it +is `False`, the file was deleted. +""" + +def __init__(self, repo, wrappedctx): +super(overlayworkingctx, self).__init__(repo) +self._repo = repo +self._wrappedctx = wrappedctx +self._clean() + +def data(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +if self._cache[path]['data']: +return self._cache[path]['data'] +else: +# Must fallback here, too, because we only set flags. +return self._wrappedctx[path].data() +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].data() + +def filedate(self, path): +if self.isdirty(path): +return self._cache[path]['date'] +else: +return self._wrappedctx[path].date() + +def flags(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return self._cache[path]['flags'] +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].flags() + +def write(self, path, data, flags=None): +if data is None: +raise error.ProgrammingError("data must be non-None") +self._markdirty(path) +self._cache[path]['exists'] = True +self._cache[path]['data'] = data +self._cache[path]['date'] = util.makedate() +if flags is not None: +self._cache[path]['flags'] = flags + +def setflags(self, path, l, x): +self._markdirty(path, exists=True, date=util.makedate(), +flags=(l and 'l' or '') + (x and 'x' or '')) + +def remove(self, path): +self._markdirty(path) + +def exists(self, path): +"""exists behaves like `lexists`, but needs to follow symlinks and +return False if they are broken. +""" +if self.isdirty(path): +# If this path exists and is a symlink, "follow" it by calling +# exists on the destination path. +if (self._cache[path]['exists'] and +'l' in self._cache[path]['flags']): +return self.exists(self._cache[path]['data']) +else: +return self._cache[path]['exists'] +return self._wrappedctx[path].exists() + +def lexists(self, path): +"""lexists returns True if the path exists""" +if self.isdirty(path): +return self._cache[path]['exists'] +return self._wrappedctx[path].lexists() + +def size(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return len(self._cache[path]['data']) +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +return self._wrappedctx[path].size() + +def flushall(self): +for path in self._writeorder: +entry = self._cache[path] +if entry['exists']: +self._wrappedctx[path].clearunknown() +if entry['data'] is not None: +if entry['flags'] is None: +raise error.ProgrammingError('data set but not flags') +self._wrappedctx[path].write( +entry['data'], +entry['flags']) +else: +self._wrappedctx[path].setflags( +'l' in entry['flags'],
D616: context: add overlayworkingcontext and overlayworkingfilectx
phillco updated this revision to Diff 1706. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D616?vs=1705&id=1706 REVISION DETAIL https://phab.mercurial-scm.org/D616 AFFECTED FILES mercurial/context.py CHANGE DETAILS diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -1661,6 +1661,9 @@ listsubrepos=listsubrepos, badfn=badfn, icasefs=icasefs) +def flushall(self): +pass # For overlayworkingfilectx compatibility. + def _filtersuspectsymlink(self, files): if not files or self._repo.dirstate._checklink: return files @@ -1974,6 +1977,194 @@ def setflags(self, l, x): self._repo.wvfs.setflags(self._path, l, x) +class overlayworkingctx(workingctx): +"""Wraps another mutable context with a write-back cache that can be flushed +at a later time. + +self._cache[path] maps to a dict with keys: { +'exists': bool? +'date': date? +'data': str? +'flags': str? +} +If `exists` is True, `flags` must be non-None and 'date' is non-None. If it +is `False`, the file was deleted. +""" + +def __init__(self, repo, wrappedctx): +super(overlayworkingctx, self).__init__(repo) +self._repo = repo +self._wrappedctx = wrappedctx +self._clean() + +def data(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +if self._cache[path]['data']: +return self._cache[path]['data'] +else: +# Must fallback here, too, because we only set flags. +return self._wrappedctx[path].data() +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].data() + +def filedate(self, path): +if self.isdirty(path): +return self._cache[path]['date'] +else: +return self._wrappedctx[path].date() + +def flags(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return self._cache[path]['flags'] +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].flags() + +def write(self, path, data, flags=None): +if data is None: +raise error.ProgrammingError("data must be non-None") +self._markdirty(path) +self._cache[path]['exists'] = True +self._cache[path]['data'] = data +self._cache[path]['date'] = util.makedate() +if flags is not None: +self._cache[path]['flags'] = flags + +def setflags(self, path, l, x): +self._markdirty(path, exists=True, date=util.makedate(), +flags=(l and 'l' or '') + (x and 'x' or '')) + +def remove(self, path): +self._markdirty(path) + +def exists(self, path): +"""exists behaves like `lexists`, but needs to follow symlinks and +return False if they are broken. +""" +if self.isdirty(path): +# If this path exists and is a symlink, "follow" it by calling +# exists on the destination path. +if (self._cache[path]['exists'] and +'l' in self._cache[path]['flags']): +return self.exists(self._cache[path]['data']) +else: +return self._cache[path]['exists'] +return self._wrappedctx[path].exists() + +def lexists(self, path): +"""lexists returns True if the path exists""" +if self.isdirty(path): +return self._cache[path]['exists'] +return self._wrappedctx[path].lexists() + +def size(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return len(self._cache[path]['data']) +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +return self._wrappedctx[path].size() + +def flushall(self): +for path in self._writeorder: +entry = self._cache[path] +if entry['exists']: +self._wrappedctx[path].clearunknown() +if entry['data'] is not None: +if entry['flags'] is None: +raise error.ProgrammingError('data set but not flags') +self._wrappedctx[path].write( +entry['data'], +entry['flags']) +else: +self._wrappedctx[path].setflags( +'l' in entry['flags'],
D616: context: add overlayworkingcontext and overlayworkingfilectx
phillco updated this revision to Diff 1705. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D616?vs=1699&id=1705 REVISION DETAIL https://phab.mercurial-scm.org/D616 AFFECTED FILES mercurial/context.py CHANGE DETAILS diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -1661,6 +1661,9 @@ listsubrepos=listsubrepos, badfn=badfn, icasefs=icasefs) +def flushall(self): +pass # For overlayworkingfilectx compatibility. + def _filtersuspectsymlink(self, files): if not files or self._repo.dirstate._checklink: return files @@ -1974,6 +1977,198 @@ def setflags(self, l, x): self._repo.wvfs.setflags(self._path, l, x) +class overlayworkingctx(workingctx): +"""Wraps another mutable context with a write-back cache that can be flushed +at a later time. + +self._cache[path] maps to a dict with keys: { +'exists': bool? +'date': date? +'data': str? +'flags': str? +} +If `exists` is True, `flags` must be non-None and 'date' is non-None. If it +is `False`, the file was deleted. +""" + +def __init__(self, repo, wrappedctx): +super(overlayworkingctx, self).__init__(repo) +self._repo = repo +self._wrappedctx = wrappedctx +self._clean() + +def data(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +if self._cache[path]['data']: +return self._cache[path]['data'] +else: +# Must fallback here, too, because we only set flags. +return self._wrappedctx[path].data() +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].data() + +def filedate(self, path): +if self.isdirty(path): +return self._cache[path]['date'] +else: +return self._wrappedctx[path].date() + +def flags(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return self._cache[path]['flags'] +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].flags() + +def write(self, path, data, flags=None): +if data is None: +raise error.ProgrammingError("data must be non-None") +self._markdirty(path) +self._cache[path]['exists'] = True +self._cache[path]['data'] = data +self._cache[path]['date'] = util.makedate() +if flags is not None: +self._cache[path]['flags'] = flags + +def setflags(self, path, l, x): +self._markdirty(path, exists=True, date=util.makedate(), +flags=(l and 'l' or '') + (x and 'x' or '')) + +def remove(self, path): +self._markdirty(path) + +def exists(self, path): +"""exists behaves like `lexists`, but needs to follow symlinks and +return False if they are broken. +""" +if self.isdirty(path): +# If this path exists and is a symlink, "follow" it by calling +# exists on the destination path. +if (self._cache[path]['exists'] and +'l' in self._cache[path]['flags']): +return self.exists(self._cache[path]['data']) +else: +return self._cache[path]['exists'] +return self._wrappedctx[path].exists() + +def lexists(self, path): +"""lexists returns True if the path exists""" +if self.isdirty(path): +return self._cache[path]['exists'] +return self._wrappedctx[path].lexists() + +def size(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return len(self._cache[path]['data']) +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +return self._wrappedctx[path].size() + +def flushall(self): +for path in self._writeorder: +entry = self._cache[path] +if entry['exists']: +self._wrappedctx[path].clearunknown() +if entry['data'] is not None: +if entry['flags'] is None: +raise error.ProgrammingError('data set but not flags') +self._wrappedctx[path].write( +entry['data'], +entry['flags']) +else: +self._wrappedctx[path].setflags( +'l' in entry['flags'],
D674: filemerge: use arbitraryfilectx for backup files
phillco created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D674 AFFECTED FILES mercurial/context.py mercurial/filemerge.py tests/test-dirstate-race.t CHANGE DETAILS diff --git a/tests/test-dirstate-race.t b/tests/test-dirstate-race.t --- a/tests/test-dirstate-race.t +++ b/tests/test-dirstate-race.t @@ -80,9 +80,9 @@ $ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py M d - M e ! b ! dir1/c + ! e $ hg debugdirstate n 644 2 * a (glob) n 0 -1 unset b diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py --- a/mercurial/filemerge.py +++ b/mercurial/filemerge.py @@ -7,7 +7,6 @@ from __future__ import absolute_import -import filecmp import os import re import tempfile @@ -226,9 +225,9 @@ return '\n' return None # unknown -def _matcheol(file, origfile): +def _matcheol(file, back): "Convert EOL markers in a file to match origfile" -tostyle = _eoltype(util.readfile(origfile)) +tostyle = _eoltype(back.data()) # No repo.wread filters? if tostyle: data = util.readfile(file) style = _eoltype(data) @@ -468,6 +467,12 @@ a = _workingpath(repo, fcd) fd = fcd.path() +# Run ``flushall()`` to make any missing folders the following wwrite +# calls might be depending on. +from . import context +if isinstance(fcd, context.overlayworkingfilectx): +fcd.ctx().flushall() + util.writefile(a + ".local", fcd.decodeddata()) repo.wwrite(fd + ".other", fco.data(), fco.flags()) repo.wwrite(fd + ".base", fca.data(), fca.flags()) @@ -505,7 +510,9 @@ args = _toolstr(ui, tool, "args", '$local $base $other') if "$output" in args: -out, a = a, back # read input from backup, write to original +# read input from backup, write to original +out = a +a = repo.wvfs.join(back.path()) replace = {'local': a, 'base': b, 'other': c, 'output': out} args = util.interpolate(r'\$', replace, args, lambda s: util.shellquote(util.localpath(s))) @@ -588,24 +595,39 @@ def _restorebackup(fcd, back): # TODO: Add a workingfilectx.write(otherfilectx) path so we can use # util.copy here instead. -fcd.write(util.readfile(back), fcd.flags()) +fcd.write(back.data(), fcd.flags()) def _makebackup(repo, ui, fcd, premerge): -"""Makes a backup of the local `fcd` file prior to merging. +"""Makes and returns a filectx-like object for ``fcd``'s backup file. In addition to preserving the user's pre-existing modifications to `fcd` (if any), the backup is used to undo certain premerges, confirm whether a merge changed anything, and determine what line endings the new file should have. """ if fcd.isabsent(): return None +from . import context +back = scmutil.origpath(ui, repo, repo.wjoin(fcd.path())) -a = _workingpath(repo, fcd) -back = scmutil.origpath(ui, repo, a) -if premerge: -util.copyfile(a, back) -return back +inworkingdir = (back.startswith(repo.wvfs.base) and not +back.startswith(repo.vfs.base)) + +if isinstance(fcd, context.overlayworkingfilectx) and inworkingdir: +# If the backup file is to be in the working directory, and we're +# merging in-memory, we must redirect the backup to the memory context +# so we don't disturb the working directory. +relpath = back[len(repo.wvfs.base) + 1:] +fcd.ctx()[relpath].write(fcd.data(), fcd.flags()) +return fcd.ctx()[relpath] +else: +# Otherwise, write to wherever the user specified the backups should go. +# +# A arbitraryfilectx is returned, so we can run the same functions on +# the backup context regardless of where it lives. +if premerge: +util.copyfile(_workingpath(repo, fcd), back) +return context.arbitraryfilectx(back) def _maketempfiles(repo, fco, fca): """Writes out `fco` and `fca` as temporary files, so an external merge @@ -719,7 +741,7 @@ return True, r, deleted finally: if not r and back is not None: -util.unlink(back) +back.remove() def _check(repo, r, ui, tool, fcd, files): fd = fcd.path() @@ -741,7 +763,7 @@ if not r and not checked and (_toolbool(ui, tool, "checkchanged") or 'changed' in _toollist(ui, tool, "check")): -if back is not None and filecmp.cmp(_workingpath(repo, fcd), back): +if back is not None and not fcd.cmp(back): if ui.promptchoice(_(" output file %s appears unchanged\n" "was merge successful (yn)?"
D673: merge: flush any deferred writes just before recordupdates()
phillco created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY ``recordupdates`` calls into the dirstate which requires the files to be there, so this is the last possible moment we can flush anything. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D673 AFFECTED FILES mercurial/merge.py CHANGE DETAILS diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -1699,6 +1699,7 @@ repo.vfs.write('updatestate', p2.hex()) stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels) +wc.flushall() if not partial: with repo.dirstate.parentchange(): To: phillco, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D672: merge: add `ondisk` to merge.update and pass it in largefiles
phillco created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY If set to false, parts of the merge might be performed in memory. Largefiles isn't a good candidate for in-memory merge (it uses a custom dirstate, matcher, and the files might not fit in memory) so have it always run an old-style merge. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D672 AFFECTED FILES hgext/largefiles/overrides.py mercurial/merge.py CHANGE DETAILS diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -1467,7 +1467,7 @@ def update(repo, node, branchmerge, force, ancestor=None, mergeancestor=False, labels=None, matcher=None, mergeforce=False, - updatecheck=None): + updatecheck=None, ondisk=True): """ Perform a merge between the working directory and the given node @@ -1516,6 +1516,8 @@ 3 = abort: uncommitted changes (checked in commands.py) Return the same tuple as applyupdates(). + +This function might run parts of the merge in memory if ``ondisk=False``. """ # Avoid cycle. from . import sparse diff --git a/hgext/largefiles/overrides.py b/hgext/largefiles/overrides.py --- a/hgext/largefiles/overrides.py +++ b/hgext/largefiles/overrides.py @@ -1432,7 +1432,9 @@ lfdirstate.write() oldstandins = lfutil.getstandinsstate(repo) - +# largefiles is not a good candidate for in-memory merge (large files, +# custom dirstate, matcher usage) so always force an on-disk merge. +kwargs["ondisk"] = True result = orig(repo, node, branchmerge, force, *args, **kwargs) newstandins = lfutil.getstandinsstate(repo) To: phillco, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D628: merge: flush any deferred writes before, and after, running any workers
phillco updated this revision to Diff 1701. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D628?vs=1672&id=1701 REVISION DETAIL https://phab.mercurial-scm.org/D628 AFFECTED FILES mercurial/merge.py CHANGE DETAILS diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -,6 +,10 @@ _warnifmissingcwd(repo, cwd) +# It's necessary to flush here in case we're inside a worker fork and will +# quit after this function. +wctx.flushall() + def batchget(repo, mctx, wctx, actions): """apply gets to the working directory @@ -1146,6 +1150,10 @@ if i > 0: yield i, f +# It's necessary to flush here in case we're inside a worker fork and will +# quit after this function. +wctx.flushall() + def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None): """apply the merge action list to the working directory @@ -1214,6 +1222,10 @@ progress(_updating, z, item=item, total=numupdates, unit=_files) removed = len(actions['r']) +# We should flush before forking into worker processes, since those workers +# flush when they complete, and we don't want to duplicate work. +wctx.flushall() + # get in parallel prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx, wctx), actions['g']) To: phillco, #hg-reviewers Cc: quark, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D628: merge: flush any deferred writes before, and after, running any workers
phillco added a comment. (Done on both) REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D628 To: phillco, #hg-reviewers Cc: quark, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D627: filemerge: flush if using deferred writes when running a merge tool
phillco updated this revision to Diff 1700. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D627?vs=1671&id=1700 REVISION DETAIL https://phab.mercurial-scm.org/D627 AFFECTED FILES mercurial/filemerge.py CHANGE DETAILS diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py --- a/mercurial/filemerge.py +++ b/mercurial/filemerge.py @@ -666,6 +666,11 @@ onfailure = _("merging %s failed!\n") precheck = None +# If using deferred writes, must flush any deferred contents if running +# an external merge tool since it has arbitrary access to the working +# copy. +wctx.flushall() + toolconf = tool, toolpath, binary, symlink if mergetype == nomerge: To: phillco, #hg-reviewers Cc: quark, martinvonz, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D616: context: add overlayworkingcontext and overlayworkingfilectx
phillco updated this revision to Diff 1699. phillco marked 10 inline comments as done. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D616?vs=1592&id=1699 REVISION DETAIL https://phab.mercurial-scm.org/D616 AFFECTED FILES mercurial/context.py CHANGE DETAILS diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -1661,6 +1661,9 @@ listsubrepos=listsubrepos, badfn=badfn, icasefs=icasefs) +def flushall(self): +pass # For overlayworkingfilectx compatibility. + def _filtersuspectsymlink(self, files): if not files or self._repo.dirstate._checklink: return files @@ -1974,6 +1977,194 @@ def setflags(self, l, x): self._repo.wvfs.setflags(self._path, l, x) +class overlayworkingctx(workingctx): +"""Wraps another mutable context with a write-back cache that can be flushed +at a later time. + +self._cache[path] maps to a dict with keys: { +'exists': bool? +'date': date? +'data': str? +'flags': str? +} +If `exists` is True, `flags` must be non-None and 'date' is non-None. If it +is `False`, the file was deleted. +""" + +def __init__(self, repo, wrappedctx): +super(overlayworkingctx, self).__init__(repo) +self._repo = repo +self._wrappedctx = wrappedctx +self._clean() + +def data(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +if self._cache[path]['data']: +return self._cache[path]['data'] +else: +# Must fallback here, too, because we only set flags. +return self._wrappedctx[path].data() +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].data() + +def filedate(self, path): +if self.isdirty(path): +return self._cache[path]['date'] +else: +return self._wrappedctx[path].date() + +def flags(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return self._cache[path]['flags'] +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +else: +return self._wrappedctx[path].flags() + +def write(self, path, data, flags=None): +if data is None: +raise error.ProgrammingError("data must be non-None") +self._markdirty(path) +self._cache[path]['exists'] = True +self._cache[path]['data'] = data +self._cache[path]['date'] = util.makedate() +if flags is not None: +self._cache[path]['flags'] = flags + +def setflags(self, path, l, x): +self._markdirty(path, exists=True, date=util.makedate(), +flags=(l and 'l' or '') + (x and 'x' or '')) + +def remove(self, path): +self._markdirty(path) + +def exists(self, path): +"""exists behaves like `lexists`, but needs to follow symlinks and +return False if they are broken. +""" +if self.isdirty(path): +# If this path exists and is a symlink, "follow" it by calling +# exists on the destination path. +if (self._cache[path]['exists'] and +'l' in self._cache[path]['flags']): +return self.exists(self._cache[path]['data']) +else: +return self._cache[path]['exists'] +return self._wrappedctx[path].exists() + +def lexists(self, path): +"""lexists returns True if the path exists""" +if self.isdirty(path): +return self._cache[path]['exists'] +return self._wrappedctx[path].lexists() + +def size(self, path): +if self.isdirty(path): +if self._cache[path]['exists']: +return len(self._cache[path]['data']) +else: +raise ProgrammingError("No such file or directory: %s" % + self._path) +return self._wrappedctx[path].size() + +def flushall(self): +for path in self._writeorder: +entry = self._cache[path] +if entry['exists']: +self._wrappedctx[path].clearunknown() +if entry['data'] is not None: +if entry['flags'] is None: +raise error.ProgrammingError('data set but not flags') +self._wrappedctx[path].write( +entry['data'], +entry['flags']) +else: +self._wrappedctx[path].setflags( +
D670: changegroup: remove external uses of getbundler
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Now that makestream and makechangegroup are the primary creation methods for changegroups, let's get rid of this rogue use of getbundler and getsubsetraw. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D670 AFFECTED FILES mercurial/exchange.py CHANGE DETAILS diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -1595,8 +1595,8 @@ raise ValueError(_('unsupported getbundle arguments: %s') % ', '.join(sorted(kwargs.keys( outgoing = _computeoutgoing(repo, heads, common) -bundler = changegroup.getbundler('01', repo, bundlecaps) -return changegroup.getsubsetraw(repo, outgoing, bundler, source) +return changegroup.makestream(repo, outgoing, '01', source, + bundlecaps=bundlecaps) # bundle20 case b2caps = {} To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D668: changegroup: replace changegroup with makechangegroup
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of reducing the number of changegroup creation APIs, let's replace the changegroup function with makechangegroup. This pushes the responsibility of creating the outgoing set to the caller, but that seems like a simple and reasonable concept for the caller to be aware of. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D668 AFFECTED FILES mercurial/changegroup.py mercurial/localrepo.py mercurial/wireproto.py CHANGE DETAILS diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py --- a/mercurial/wireproto.py +++ b/mercurial/wireproto.py @@ -795,7 +795,9 @@ @wireprotocommand('changegroup', 'roots') def changegroup(repo, proto, roots): nodes = decodelist(roots) -cg = changegroupmod.changegroup(repo, nodes, 'serve') +outgoing = discovery.outgoing(repo, missingroots=nodes, + missingheads=repo.heads()) +cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve') return streamres(reader=cg, v1compressible=True) @wireprotocommand('changegroupsubset', 'bases heads') diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -287,7 +287,9 @@ return self._repo.branches(nodes) def changegroup(self, basenodes, source): -return changegroup.changegroup(self._repo, basenodes, source) +outgoing = discovery.outgoing(self._repo, missingroots=basenodes, + missingheads=self._repo.heads()) +return changegroup.makechangegroup(self._repo, outgoing, '01', source) def changegroupsubset(self, bases, heads, source): outgoing = discovery.outgoing(self._repo, missingroots=bases, diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -21,7 +21,6 @@ from . import ( dagutil, -discovery, error, mdiff, phases, @@ -938,12 +937,6 @@ return makechangegroup(repo, outgoing, version, source, bundlecaps=bundlecaps) -def changegroup(repo, basenodes, source): -# to avoid a race we use changegroupsubset() (issue1320) -outgoing = discovery.outgoing(repo, missingroots=basenodes, - missingheads=repo.heads()) -return makechangegroup(repo, outgoing, '01', source) - def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles): revisions = 0 files = 0 To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D665: changegroup: replace changegroupsubset with makechangegroup
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of getting rid of all the permutations of changegroup creation, let's remove changegroupsubset and call makechangegroup instead. This moves the responsibility of creating the outgoing set to the caller, but that seems like a relatively reasonable unit of functionality for the caller to have to care about (i.e. what commits should be bundled). REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D665 AFFECTED FILES hgext/shelve.py mercurial/changegroup.py mercurial/localrepo.py mercurial/wireproto.py CHANGE DETAILS diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py --- a/mercurial/wireproto.py +++ b/mercurial/wireproto.py @@ -21,6 +21,7 @@ from . import ( bundle2, changegroup as changegroupmod, +discovery, encoding, error, exchange, @@ -801,7 +802,9 @@ def changegroupsubset(repo, proto, bases, heads): bases = decodelist(bases) heads = decodelist(heads) -cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve') +outgoing = discovery.outgoing(repo, missingroots=bases, + missingheads=heads) +cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve') return streamres(reader=cg, v1compressible=True) @wireprotocommand('debugwireargs', 'one two *') diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -31,6 +31,7 @@ context, dirstate, dirstateguard, +discovery, encoding, error, exchange, @@ -289,7 +290,9 @@ return changegroup.changegroup(self._repo, basenodes, source) def changegroupsubset(self, bases, heads, source): -return changegroup.changegroupsubset(self._repo, bases, heads, source) +outgoing = discovery.outgoing(self._repo, missingroots=bases, + missingheads=heads) +return changegroup.makechangegroup(self._repo, outgoing, '01', source) # End of baselegacywirecommands interface. diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -927,22 +927,6 @@ _changegroupinfo(repo, csets, source) return bundler.generate(commonrevs, csets, fastpathlinkrev, source) -def changegroupsubset(repo, roots, heads, source, version='01'): -"""Compute a changegroup consisting of all the nodes that are -descendants of any of the roots and ancestors of any of the heads. -Return a chunkbuffer object whose read() method will return -successive changegroup chunks. - -It is fairly complex as determining which filenodes and which -manifest nodes need to be included for the changeset to be complete -is non-trivial. - -Another wrinkle is doing the reverse, figuring out which changeset in -the changegroup a particular filenode or manifestnode belongs to. -""" -outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads) -return makechangegroup(repo, outgoing, version, source) - def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None, version='01'): """Like getbundle, but taking a discovery.outgoing as an argument. @@ -972,7 +956,9 @@ def changegroup(repo, basenodes, source): # to avoid a race we use changegroupsubset() (issue1320) -return changegroupsubset(repo, basenodes, repo.heads(), source) +outgoing = discovery.outgoing(repo, missingroots=basenodes, + missingheads=repo.heads()) +return makechangegroup(repo, outgoing, '01', source) def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles): revisions = 0 diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -33,6 +33,7 @@ bundlerepo, changegroup, cmdutil, +discovery, error, exchange, hg, @@ -145,8 +146,11 @@ btype = 'HG20' compression = 'BZ' -cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve', - version=cgversion) +outgoing = discovery.outgoing(self.repo, missingroots=bases, + missingheads=[node]) +cg = changegroup.makechangegroup(self.repo, outgoing, cgversion, + 'shelve') + bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs, compression=compression) To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D671: changegroup: rename getsubsetraw to makestream
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY Now that nothing uses getsubsetraw except makestream, let's move the functionality into the makestream. This removes the last remaining excess changegroup creation function, getsubsetraw. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D671 AFFECTED FILES mercurial/changegroup.py CHANGE DETAILS diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -898,19 +898,17 @@ for node in nodes: repo.ui.debug("%s\n" % hex(node)) -def makestream(repo, outgoing, version, source, fastpath=False, - bundlecaps=None): -bundler = getbundler(version, repo, bundlecaps=bundlecaps) -return getsubsetraw(repo, outgoing, bundler, source, fastpath=fastpath) - def makechangegroup(repo, outgoing, version, source, fastpath=False, bundlecaps=None): cgstream = makestream(repo, outgoing, version, source, fastpath=fastpath, bundlecaps=bundlecaps) return getunbundler(version, util.chunkbuffer(cgstream), None, {'clcount': len(outgoing.missing) }) -def getsubsetraw(repo, outgoing, bundler, source, fastpath=False): +def makestream(repo, outgoing, version, source, fastpath=False, + bundlecaps=None): +bundler = getbundler(version, repo, bundlecaps=bundlecaps) + repo = repo.unfiltered() commonrevs = outgoing.common csets = outgoing.missing To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D667: changegroup: delete getlocalchangegroup
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of reducing the number of changegroup creation APIs, let's go ahead and delete this unused function. It appears to have been deprecated in the last release anyway. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D667 AFFECTED FILES mercurial/changegroup.py CHANGE DETAILS diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -938,11 +938,6 @@ return makechangegroup(repo, outgoing, version, source, bundlecaps=bundlecaps) -def getlocalchangegroup(repo, *args, **kwargs): -repo.ui.deprecwarn('getlocalchangegroup is deprecated, use getchangegroup', - '4.3') -return getchangegroup(repo, *args, **kwargs) - def changegroup(repo, basenodes, source): # to avoid a race we use changegroupsubset() (issue1320) outgoing = discovery.outgoing(repo, missingroots=basenodes, To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D669: changegroup: replace getchangegroup with makechangegroup
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of reducing the number of changegroup creation APIs, let's replace getchangegroup with calls to makechangegroup. This is mostly a drop in replacement, but it does change the version specifier to be required, so it's more obvious which callers are creating old version 1 changegroups still. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D669 AFFECTED FILES mercurial/bundle2.py mercurial/changegroup.py mercurial/exchange.py tests/test-bundle2-format.t tests/test-bundle2-multiple-changegroups.t tests/test-bundle2-remote-changegroup.t CHANGE DETAILS diff --git a/tests/test-bundle2-remote-changegroup.t b/tests/test-bundle2-remote-changegroup.t --- a/tests/test-bundle2-remote-changegroup.t +++ b/tests/test-bundle2-remote-changegroup.t @@ -64,7 +64,8 @@ > common.extend(repo.lookup(r) for r in repo.revs(_common)) > heads = [repo.lookup(r) for r in repo.revs(heads)] > outgoing = discovery.outgoing(repo, common, heads) - > cg = changegroup.getchangegroup(repo, 'changegroup', outgoing) + > cg = changegroup.makechangegroup(repo, outgoing, '01', + > 'changegroup') > newpart('changegroup', cg.getchunks()) > else: > raise Exception('unknown verb') diff --git a/tests/test-bundle2-multiple-changegroups.t b/tests/test-bundle2-multiple-changegroups.t --- a/tests/test-bundle2-multiple-changegroups.t +++ b/tests/test-bundle2-multiple-changegroups.t @@ -13,13 +13,13 @@ > # in 'heads' as intermediate heads for the first changegroup. > intermediates = [repo[r].p1().node() for r in heads] > outgoing = discovery.outgoing(repo, common, intermediates) - > cg = changegroup.getchangegroup(repo, source, outgoing, - > bundlecaps=bundlecaps) + > cg = changegroup.makechangegroup(repo, outgoing, '01', + > source, bundlecaps=bundlecaps) > bundler.newpart('output', data='changegroup1') > bundler.newpart('changegroup', data=cg.getchunks()) > outgoing = discovery.outgoing(repo, common + intermediates, heads) - > cg = changegroup.getchangegroup(repo, source, outgoing, - > bundlecaps=bundlecaps) + > cg = changegroup.makechangegroup(repo, outgoing, '01', + > source, bundlecaps=bundlecaps) > bundler.newpart('output', data='changegroup2') > bundler.newpart('changegroup', data=cg.getchunks()) > diff --git a/tests/test-bundle2-format.t b/tests/test-bundle2-format.t --- a/tests/test-bundle2-format.t +++ b/tests/test-bundle2-format.t @@ -114,7 +114,8 @@ > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)] > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)] > outgoing = discovery.outgoing(repo, headcommon, headmissing) - > cg = changegroup.getchangegroup(repo, 'test:bundle2', outgoing, None) + > cg = changegroup.makechangegroup(repo, outgoing, '01', + > 'test:bundle2') > bundler.newpart('changegroup', data=cg.getchunks(), > mandatory=False) > diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -965,8 +965,8 @@ cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push', fastpath=True, bundlecaps=bundlecaps) else: -cg = changegroup.getchangegroup(pushop.repo, 'push', outgoing, -bundlecaps=bundlecaps) +cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', +'push', bundlecaps=bundlecaps) # apply changegroup to remote # local repo finds heads on server, finds out what diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -926,17 +926,6 @@ _changegroupinfo(repo, csets, source) return bundler.generate(commonrevs, csets, fastpathlinkrev, source) -def getchangegroup(repo, source, outgoing, bundlecaps=None, - version='01'): -"""Like getbundle, but taking a discovery.outgoing as an argument. - -This is only implemented for local repos and reuses potentially -precomputed sets in outgoing.""" -if not outgoing.missing: -return None -return makechangegroup(repo, outgoing, version, source, - bundlecaps=bundlecaps) - def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles): revisions = 0 f
D664: changegroup: replace getsubset with makechangegroup
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY The current changegroup APIs are a bit of a mess. Currently you can use getsubsetraw, getsubset, changegroupsubset, getlocalchangegroupraw, getchangegroup, and getlocalchangroup to produce changegroups. This patch is the beginning of a refactor to boil all of that away to just makechangegroup and makestream. The first step adds the new functions and replaces getsubset function with them. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D664 AFFECTED FILES mercurial/changegroup.py mercurial/exchange.py CHANGE DETAILS diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -963,12 +963,8 @@ or pushop.repo.changelog.filteredrevs): # push everything, # use the fast path, no race possible on push -bundler = changegroup.cg1packer(pushop.repo, bundlecaps) -cg = changegroup.getsubset(pushop.repo, - outgoing, - bundler, - 'push', - fastpath=True) +cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push', +fastpath=True, bundlecaps=bundlecaps) else: cg = changegroup.getchangegroup(pushop.repo, 'push', outgoing, bundlecaps=bundlecaps) diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -899,6 +899,18 @@ for node in nodes: repo.ui.debug("%s\n" % hex(node)) +def makestream(repo, outgoing, version, source, fastpath=False, + bundlecaps=None): +bundler = getbundler(version, repo, bundlecaps=bundlecaps) +return getsubsetraw(repo, outgoing, bundler, source, fastpath=fastpath) + +def makechangegroup(repo, outgoing, version, source, fastpath=False, +bundlecaps=None): +cgstream = makestream(repo, outgoing, version, source, + fastpath=fastpath, bundlecaps=bundlecaps) +return getunbundler(version, util.chunkbuffer(cgstream), None, +{'clcount': len(outgoing.missing) }) + def getsubsetraw(repo, outgoing, bundler, source, fastpath=False): repo = repo.unfiltered() commonrevs = outgoing.common @@ -915,11 +927,6 @@ _changegroupinfo(repo, csets, source) return bundler.generate(commonrevs, csets, fastpathlinkrev, source) -def getsubset(repo, outgoing, bundler, source, fastpath=False): -gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath) -return getunbundler(bundler.version, util.chunkbuffer(gengroup), None, -{'clcount': len(outgoing.missing)}) - def changegroupsubset(repo, roots, heads, source, version='01'): """Compute a changegroup consisting of all the nodes that are descendants of any of the roots and ancestors of any of the heads. @@ -934,8 +941,7 @@ the changegroup a particular filenode or manifestnode belongs to. """ outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads) -bundler = getbundler(version, repo) -return getsubset(repo, outgoing, bundler, source) +return makechangegroup(repo, outgoing, version, source) def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None, version='01'): @@ -956,8 +962,8 @@ precomputed sets in outgoing.""" if not outgoing.missing: return None -bundler = getbundler(version, repo, bundlecaps) -return getsubset(repo, outgoing, bundler, source) +return makechangegroup(repo, outgoing, version, source, + bundlecaps=bundlecaps) def getlocalchangegroup(repo, *args, **kwargs): repo.ui.deprecwarn('getlocalchangegroup is deprecated, use getchangegroup', To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D666: changegroup: replace getlocalchangegroupraw with makestream
durham created this revision. Herald added a subscriber: mercurial-devel. Herald added a reviewer: hg-reviewers. REVISION SUMMARY As part of reducing the number of changegroup creation apis, let's replace calls to getlocalchangegroupraw with calls to makestream. Aside from one case of checking if there are no outgoing commits and returning None, this is pretty much a drop in replacement. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D666 AFFECTED FILES mercurial/changegroup.py mercurial/exchange.py CHANGE DETAILS diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -755,10 +755,9 @@ if not cgversions: raise ValueError(_('no common changegroup version')) version = max(cgversions) -cg = changegroup.getlocalchangegroupraw(pushop.repo, 'push', -pushop.outgoing, -version=version) -cgpart = bundler.newpart('changegroup', data=cg) +cgstream = changegroup.makestream(pushop.repo, pushop.outgoing, version, + 'push') +cgpart = bundler.newpart('changegroup', data=cgstream) if cgversions: cgpart.addparam('version', version) if 'treemanifest' in pushop.repo.requirements: @@ -1621,7 +1620,7 @@ def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None, b2caps=None, heads=None, common=None, **kwargs): """add a changegroup part to the requested bundle""" -cg = None +cgstream = None if kwargs.get('cg', True): # build changegroup bundle here. version = '01' @@ -1633,12 +1632,11 @@ raise ValueError(_('no common changegroup version')) version = max(cgversions) outgoing = _computeoutgoing(repo, heads, common) -cg = changegroup.getlocalchangegroupraw(repo, source, outgoing, -bundlecaps=bundlecaps, -version=version) +cgstream = changegroup.makestream(repo, outgoing, version, source, + bundlecaps=bundlecaps) -if cg: -part = bundler.newpart('changegroup', data=cg) +if cgstream: +part = bundler.newpart('changegroup', data=cgstream) if cgversions: part.addparam('version', version) part.addparam('nbchanges', str(len(outgoing.missing)), mandatory=False) diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py --- a/mercurial/changegroup.py +++ b/mercurial/changegroup.py @@ -927,17 +927,6 @@ _changegroupinfo(repo, csets, source) return bundler.generate(commonrevs, csets, fastpathlinkrev, source) -def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None, - version='01'): -"""Like getbundle, but taking a discovery.outgoing as an argument. - -This is only implemented for local repos and reuses potentially -precomputed sets in outgoing. Returns a raw changegroup generator.""" -if not outgoing.missing: -return None -bundler = getbundler(version, repo, bundlecaps) -return getsubsetraw(repo, outgoing, bundler, source) - def getchangegroup(repo, source, outgoing, bundlecaps=None, version='01'): """Like getbundle, but taking a discovery.outgoing as an argument. To: durham, #hg-reviewers Cc: mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D663: dirstate: perform transactions with _map using single call, where possible
phillco accepted this revision. phillco added a comment. These look good to me. INLINE COMMENTS > dirstate.py:585-587 > # if there is a merge going on and the file was either > # in state 'm' (-1) or coming from other parent (-2) before > # being removed, restore that state. Possibly move this comment to just above line 590? REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D663 To: mbolin, #hg-reviewers, phillco Cc: phillco, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] extensions: fix wrapcommand/function of class instance
LGTM. Thanks! Excerpts from Yuya Nishihara's message of 2017-09-10 23:47:02 +0900: > # HG changeset patch > # User Yuya Nishihara > # Date 1505054234 -32400 > # Sun Sep 10 23:37:14 2017 +0900 > # Node ID 283ecec363f4901ec8948dbdc7dba564296d8582 > # Parent a763c891f36e55f4869f443c220227d1da747d18 > extensions: fix wrapcommand/function of class instance > > 5361771f9714 changed _updatewrapper() to copy the __name__ attribute, but > not all callable objects has __name__. > > Spotted by loading mq with extdiff. > > diff --git a/mercurial/extensions.py b/mercurial/extensions.py > --- a/mercurial/extensions.py > +++ b/mercurial/extensions.py > @@ -333,7 +333,10 @@ def bind(func, *args): > > def _updatewrapper(wrap, origfn, unboundwrapper): > '''Copy and add some useful attributes to wrapper''' > -wrap.__name__ = origfn.__name__ > +try: > +wrap.__name__ = origfn.__name__ > +except AttributeError: > +pass > wrap.__module__ = getattr(origfn, '__module__') > wrap.__doc__ = getattr(origfn, '__doc__') > wrap.__dict__.update(getattr(origfn, '__dict__', {})) > diff --git a/tests/test-extensions-wrapfunction.py > b/tests/test-extensions-wrapfunction.py > --- a/tests/test-extensions-wrapfunction.py > +++ b/tests/test-extensions-wrapfunction.py > @@ -54,3 +54,11 @@ with wrap1: > print('context manager', dummy.getstack()) > print('context manager', dummy.getstack()) > print('context manager', dummy.getstack()) > + > +# Wrap callable object which has no __name__ > +class callableobj(object): > +def __call__(self): > +return ['orig'] > +dummy.cobj = callableobj() > +extensions.wrapfunction(dummy, 'cobj', wrappers[0]) > +print('wrap callable object', dummy.cobj()) > diff --git a/tests/test-extensions-wrapfunction.py.out > b/tests/test-extensions-wrapfunction.py.out > --- a/tests/test-extensions-wrapfunction.py.out > +++ b/tests/test-extensions-wrapfunction.py.out > @@ -18,3 +18,4 @@ context manager [0, 1, 'orig'] > context manager [2, 0, 1, 'orig'] > context manager [2, 1, 'orig'] > context manager [2, 'orig'] > +wrap callable object [0, 'orig'] ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 2 stable] mq: test coverage of how [diff] configuration influence can break mq patches
# HG changeset patch # User Mads Kiilerich # Date 1505083342 -7200 # Mon Sep 11 00:42:22 2017 +0200 # Branch stable # Node ID e05e50fbdeaf7eb52a2936e1dfe98643d68c334e # Parent 3c3066367d72344935aabf9606a5b40e9950b5e7 mq: test coverage of how [diff] configuration influence can break mq patches diff --git a/tests/test-mq-git.t b/tests/test-mq-git.t --- a/tests/test-mq-git.t +++ b/tests/test-mq-git.t @@ -208,5 +208,40 @@ git=no: regular patch after qrefresh wit @@ -0,0 +1,1 @@ +a +Test how [diff] configuration influence and cause invalid or lossy patches: + + $ cat <> .hg/hgrc + > [mq] + > git = AUTO + > [diff] + > nobinary = True + > noprefix = True + > showfunc = True + > ignorews = True + > ignorewsamount = True + > ignoreblanklines = True + > unified = 1 + > EOF + + $ echo ' a' > a + $ hg qnew prepare -d '0 0' + $ echo ' a' > a + $ printf '\0' > b + $ echo >> c + $ hg qnew diff -d '0 0' + + $ cat .hg/patches/prepare + # HG changeset patch + # Date 0 0 + # Parent cf0bfe72686a47d8d7d7b4529a3adb8b0b449a9f + + $ cat .hg/patches/diff + # HG changeset patch + # Date 0 0 + # Parent fb9c4422b0f37dd576522dd9a3f99b825c177efe + + diff --git b b + Binary file b has changed + $ cd .. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 2 stable] mq: create non-lossy patches, also with custom global diff configuration
# HG changeset patch # User Mads Kiilerich # Date 1505083344 -7200 # Mon Sep 11 00:42:24 2017 +0200 # Branch stable # Node ID 987a85c42b08ab2a82cce39b004e00b708320d0e # Parent e05e50fbdeaf7eb52a2936e1dfe98643d68c334e mq: create non-lossy patches, also with custom global diff configuration Users with custom [diff] configuration most certainly didn't intend it to make mq lose changes. It could: * git is handled perfectly fine. * nobinary could make mq leave some files out from the patches. * noprefix could make mq itself (and probably also other tools) fail to apply patches without the usual a/b prefix. * ignorews, ignorewsamount, or ignoreblanklines could create patches with missing whitespace that could fail to apply correctly. Thus, when refreshing patches, use patch.difffeatureopts, optionally with git as before, but without the config options for whitespace and format changing that most likely will cause loss or problems. (patch.diffopts is just patch.difffeatureopts with all options enabled and can be replaced with that.) diff --git a/hgext/mq.py b/hgext/mq.py --- a/hgext/mq.py +++ b/hgext/mq.py @@ -503,8 +503,11 @@ class queue(object): self.guardsdirty = False self.activeguards = None -def diffopts(self, opts=None, patchfn=None): -diffopts = patchmod.diffopts(self.ui, opts) +def diffopts(self, opts=None, patchfn=None, plain=False): +"""Return diff options tweaked for this mq use, possibly upgrading to +git format, and possibly plain and without lossy options.""" +diffopts = patchmod.difffeatureopts(self.ui, opts, +git=True, whitespace=not plain, formatchanging=not plain) if self.gitmode == 'auto': diffopts.upgrade = True elif self.gitmode == 'keep': @@ -1177,7 +1180,7 @@ class queue(object): date = opts.get('date') if date: date = util.parsedate(date) -diffopts = self.diffopts({'git': opts.get('git')}) +diffopts = self.diffopts({'git': opts.get('git')}, plain=True) if opts.get('checkname', True): self.checkpatchname(patchfn) inclsubs = checksubstate(repo) @@ -1642,7 +1645,8 @@ class queue(object): substatestate = repo.dirstate['.hgsubstate'] ph = patchheader(self.join(patchfn), self.plainmode) -diffopts = self.diffopts({'git': opts.get('git')}, patchfn) +diffopts = self.diffopts({'git': opts.get('git')}, patchfn, + plain=True) if newuser: ph.setuser(newuser) if newdate: diff --git a/tests/test-mq-git.t b/tests/test-mq-git.t --- a/tests/test-mq-git.t +++ b/tests/test-mq-git.t @@ -235,13 +235,35 @@ Test how [diff] configuration influence # Date 0 0 # Parent cf0bfe72686a47d8d7d7b4529a3adb8b0b449a9f + diff -r cf0bfe72686a -r fb9c4422b0f3 a + --- a/a + +++ b/a + @@ -1,1 +1,1 @@ + -a + + a $ cat .hg/patches/diff # HG changeset patch # Date 0 0 # Parent fb9c4422b0f37dd576522dd9a3f99b825c177efe - diff --git b b - Binary file b has changed + diff --git a/a b/a + --- a/a + +++ b/a + @@ -1,1 +1,1 @@ + - a + + a + diff --git a/b b/b + index 78981922613b2afb6025042ff6bd878ac1994e85..f76dd238ade08917e6712764a16a22005a50573d + GIT binary patch + literal 1 + Ic${MZ000310RR91 + + diff --git a/c b/c + --- a/c + +++ b/c + @@ -1,1 +1,2 @@ + a + + $ cd .. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D544: releasenotes: update docstrings with information on additional flags
rishabhmadan96 updated this revision to Diff 1690. REPOSITORY rHG Mercurial CHANGES SINCE LAST UPDATE https://phab.mercurial-scm.org/D544?vs=1368&id=1690 REVISION DETAIL https://phab.mercurial-scm.org/D544 AFFECTED FILES hgext/releasenotes.py CHANGE DETAILS diff --git a/hgext/releasenotes.py b/hgext/releasenotes.py --- a/hgext/releasenotes.py +++ b/hgext/releasenotes.py @@ -550,6 +550,33 @@ this command and changes should not be lost when running this command on that file. A particular use case for this is to tweak the wording of a release note after it has been added to the release notes file. + +The -c/--check option checks the commit message for invalid admonitions. +In case of an invalid admonition, the flag returns the name of admonition +along with the changeset ID. For example:: + + .. abcd:: + + First paragraph under this admonition + +For the above commit message, using `hg releasenotes -r . --check` +returns: Invalid admonition 'abcd' present in changeset 3ea92981e103 + +If an invalid admonition is similar to the available admonitions upto +a certain threshold, the correct admonition will be suggested. +For example:: + + .. fixes:: + + Fixes issue1234 + +For the above commit message, using `hg releasenotes -r . --check` +returns: Invalid admonition 'fixes' present in changeset 687be3ff87c6 +(did you mean fix?) + +The -l/--list option, presents the user with a list of existing available +admonitions along with their title. This also includes the custom +admonitions (if any). """ sections = releasenotessections(ui, repo) if opts.get('list'): To: rishabhmadan96, #hg-reviewers Cc: dsp, durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
D544: releasenotes: update docstrings with information on additional flags
rishabhmadan96 added inline comments. INLINE COMMENTS > dsp wrote in releasenotes.py:554 > I think we usually don't address "you" directly. Maybe something like > > The -c/--check option checks the commit message for invalid admonitions. In > case of an invalid admonition, the flag returns the name of admonition along > with the changeset ID. For example:: I referred the patchbomb.py file while writing this part, and it made use of 'you' and 'your'. But I guess it's okay to have it without usage of 'you'. So I'll make the changes here. > dsp wrote in releasenotes.py:563 > I think the colon at the end is invalid. My bad! I'll correct this one. REPOSITORY rHG Mercurial REVISION DETAIL https://phab.mercurial-scm.org/D544 To: rishabhmadan96, #hg-reviewers Cc: dsp, durin42, mercurial-devel ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH evolve-ext STABLE] topic: check availability of obsutil.getmarkers() for portability
# HG changeset patch # User FUJIWARA Katsunori # Date 1505049726 -32400 # Sun Sep 10 22:22:06 2017 +0900 # Branch stable # Node ID 12654c26c4ed79e83661c92473d3f42bb4de5134 # Parent 0d16c89aa18584443d08fd07c587d4cb14677c4a # Available At https://fo...@bitbucket.org/foozy/hgext-evolve # hg pull https://fo...@bitbucket.org/foozy/hgext-evolve -r 12654c26c4ed # EXP-Topic topic-hg-version-portability topic: check availability of obsutil.getmarkers() for portability Before this patch, topic extension causes unintentional failure with Mercurial earlier than 4.3, because obsutil.getmarkers() has been available since Mercurial 4.3 (tests for topic on mercurial-4.* branches fail, too). This breaks "minimumhgversion = '4.0'" declaration of topic extension. This patch fixes this issue in a straightforward way for simplicity on stable branch. I'm planning to centralize such portability logic in topic extension into topic/compat.py or so on default branch for efficiency at runtime, like as evolve/compat.py. diff --git a/hgext3rd/topic/__init__.py b/hgext3rd/topic/__init__.py --- a/hgext3rd/topic/__init__.py +++ b/hgext3rd/topic/__init__.py @@ -69,7 +69,6 @@ from mercurial import ( namespaces, node, obsolete, -obsutil, patch, phases, registrar, @@ -555,6 +554,16 @@ def _showlasttouched(repo, fm, opts): fm.plain('\n') fm.end() +getmarkers = None +try: +from mercurial import obsutil +getmarkers = getattr(obsutil, 'getmarkers', None) +except ImportError: +pass + +if getmarkers is None: +getmarkers = obsolete.getmarkers + def _getlasttouched(repo, topics): """ Calculates the last time a topic was used. Returns a dictionary of seconds @@ -576,7 +585,7 @@ def _getlasttouched(repo, topics): maxtime = rt # looking on the markers also to get more information and accurate # last touch time. -obsmarkers = obsutil.getmarkers(repo, [repo[revs].node()]) +obsmarkers = getmarkers(repo, [repo[revs].node()]) for marker in obsmarkers: rt = marker.date() if rt[0] > maxtime[0]: ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH] extensions: fix wrapcommand/function of class instance
# HG changeset patch # User Yuya Nishihara # Date 1505054234 -32400 # Sun Sep 10 23:37:14 2017 +0900 # Node ID 283ecec363f4901ec8948dbdc7dba564296d8582 # Parent a763c891f36e55f4869f443c220227d1da747d18 extensions: fix wrapcommand/function of class instance 5361771f9714 changed _updatewrapper() to copy the __name__ attribute, but not all callable objects has __name__. Spotted by loading mq with extdiff. diff --git a/mercurial/extensions.py b/mercurial/extensions.py --- a/mercurial/extensions.py +++ b/mercurial/extensions.py @@ -333,7 +333,10 @@ def bind(func, *args): def _updatewrapper(wrap, origfn, unboundwrapper): '''Copy and add some useful attributes to wrapper''' -wrap.__name__ = origfn.__name__ +try: +wrap.__name__ = origfn.__name__ +except AttributeError: +pass wrap.__module__ = getattr(origfn, '__module__') wrap.__doc__ = getattr(origfn, '__doc__') wrap.__dict__.update(getattr(origfn, '__dict__', {})) diff --git a/tests/test-extensions-wrapfunction.py b/tests/test-extensions-wrapfunction.py --- a/tests/test-extensions-wrapfunction.py +++ b/tests/test-extensions-wrapfunction.py @@ -54,3 +54,11 @@ with wrap1: print('context manager', dummy.getstack()) print('context manager', dummy.getstack()) print('context manager', dummy.getstack()) + +# Wrap callable object which has no __name__ +class callableobj(object): +def __call__(self): +return ['orig'] +dummy.cobj = callableobj() +extensions.wrapfunction(dummy, 'cobj', wrappers[0]) +print('wrap callable object', dummy.cobj()) diff --git a/tests/test-extensions-wrapfunction.py.out b/tests/test-extensions-wrapfunction.py.out --- a/tests/test-extensions-wrapfunction.py.out +++ b/tests/test-extensions-wrapfunction.py.out @@ -18,3 +18,4 @@ context manager [0, 1, 'orig'] context manager [2, 0, 1, 'orig'] context manager [2, 1, 'orig'] context manager [2, 'orig'] +wrap callable object [0, 'orig'] ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel