Mercurial > public > mercurial-scm > hg-stable
changeset 45268:5780a04a1b46
merge stable in default
author | Pulkit Goyal <7895pulkit@gmail.com> |
---|---|
date | Fri, 31 Jul 2020 17:09:31 +0530 |
parents | e0bfde04f957 (diff) 53a6febafc66 (current diff) |
children | ad7006830106 |
files | tests/test-rebase-inmemory.t |
diffstat | 23 files changed, 637 insertions(+), 475 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/check-py3-compat.py Tue Jul 28 10:19:49 2020 -0700 +++ b/contrib/check-py3-compat.py Fri Jul 31 17:09:31 2020 +0530 @@ -97,6 +97,15 @@ if sys.version_info[0] == 2: fn = check_compat_py2 else: + # check_compat_py3 will import every filename we specify as long as it + # starts with one of a few prefixes. It does this by converting + # specified filenames like 'mercurial/foo.py' to 'mercurial.foo' and + # importing that. When running standalone (not as part of a test), this + # means we actually import the installed versions, not the files we just + # specified. When running as test-check-py3-compat.t, we technically + # would import the correct paths, but it's cleaner to have both cases + # use the same import logic. + sys.path.insert(0, '.') fn = check_compat_py3 for f in sys.argv[1:]:
--- a/hgext/fix.py Tue Jul 28 10:19:49 2020 -0700 +++ b/hgext/fix.py Fri Jul 31 17:09:31 2020 +0530 @@ -241,15 +241,15 @@ of files, unless the --whole flag is used. Some tools may always affect the whole file regardless of --whole. - If revisions are specified with --rev, those revisions will be checked, and - they may be replaced with new revisions that have fixed file content. It is - desirable to specify all descendants of each specified revision, so that the - fixes propagate to the descendants. If all descendants are fixed at the same - time, no merging, rebasing, or evolution will be required. + If --working-dir is used, files with uncommitted changes in the working copy + will be fixed. Note that no backup are made. - If --working-dir is used, files with uncommitted changes in the working copy - will be fixed. If the checked-out revision is also fixed, the working - directory will update to the replacement revision. + If revisions are specified with --source, those revisions and their + descendants will be checked, and they may be replaced with new revisions + that have fixed file content. By automatically including the descendants, + no merging, rebasing, or evolution will be required. If an ancestor of the + working copy is included, then the working copy itself will also be fixed, + and the working copy will be updated to the fixed parent. When determining what lines of each file to fix at each revision, the whole set of revisions being fixed is considered, so that fixes to earlier
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/commit.py Fri Jul 31 17:09:31 2020 +0530 @@ -0,0 +1,409 @@ +# commit.py - fonction to perform commit +# +# This software may be used and distributed according to the terms of the +# GNU General Public License version 2 or any later version. + +from __future__ import absolute_import + +import errno + +from .i18n import _ +from .node import ( + hex, + nullid, + nullrev, +) + +from . import ( + context, + mergestate, + metadata, + phases, + scmutil, + subrepoutil, +) + + +def _write_copy_meta(repo): + """return a (changelog, filelog) boolean tuple + + changelog: copy related information should be stored in the changeset + filelof: copy related information should be written in the file revision + """ + if repo.filecopiesmode == b'changeset-sidedata': + writechangesetcopy = True + writefilecopymeta = True + else: + writecopiesto = repo.ui.config(b'experimental', b'copies.write-to') + writefilecopymeta = writecopiesto != b'changeset-only' + writechangesetcopy = writecopiesto in ( + b'changeset-only', + b'compatibility', + ) + return writechangesetcopy, writefilecopymeta + + +def commitctx(repo, ctx, error=False, origctx=None): + """Add a new revision to the target repository. + Revision information is passed via the context argument. + + ctx.files() should list all files involved in this commit, i.e. + modified/added/removed files. On merge, it may be wider than the + ctx.files() to be committed, since any file nodes derived directly + from p1 or p2 are excluded from the committed ctx.files(). + + origctx is for convert to work around the problem that bug + fixes to the files list in changesets change hashes. For + convert to be the identity, it can pass an origctx and this + function will use the same files list when it makes sense to + do so. + """ + repo = repo.unfiltered() + + p1, p2 = ctx.p1(), ctx.p2() + user = ctx.user() + + with repo.lock(), repo.transaction(b"commit") as tr: + r = _prepare_files(tr, ctx, error=error, origctx=origctx) + mn, files, p1copies, p2copies, filesadded, filesremoved = r + + # update changelog + repo.ui.note(_(b"committing changelog\n")) + repo.changelog.delayupdate(tr) + n = repo.changelog.add( + mn, + files, + ctx.description(), + tr, + p1.node(), + p2.node(), + user, + ctx.date(), + ctx.extra().copy(), + p1copies, + p2copies, + filesadded, + filesremoved, + ) + xp1, xp2 = p1.hex(), p2 and p2.hex() or b'' + repo.hook( + b'pretxncommit', throw=True, node=hex(n), parent1=xp1, parent2=xp2, + ) + # set the new commit is proper phase + targetphase = subrepoutil.newcommitphase(repo.ui, ctx) + if targetphase: + # retract boundary do not alter parent changeset. + # if a parent have higher the resulting phase will + # be compliant anyway + # + # if minimal phase was 0 we don't need to retract anything + phases.registernew(repo, tr, targetphase, [n]) + return n + + +def _prepare_files(tr, ctx, error=False, origctx=None): + repo = ctx.repo() + p1 = ctx.p1() + + writechangesetcopy, writefilecopymeta = _write_copy_meta(repo) + + p1copies, p2copies = None, None + if writechangesetcopy: + p1copies = ctx.p1copies() + p2copies = ctx.p2copies() + filesadded, filesremoved = None, None + if ctx.manifestnode(): + # reuse an existing manifest revision + repo.ui.debug(b'reusing known manifest\n') + mn = ctx.manifestnode() + files = ctx.files() + if writechangesetcopy: + filesadded = ctx.filesadded() + filesremoved = ctx.filesremoved() + elif not ctx.files(): + repo.ui.debug(b'reusing manifest from p1 (no file change)\n') + mn = p1.manifestnode() + files = [] + else: + mn, files, added, removed = _process_files(tr, ctx, error=error) + if writechangesetcopy: + filesremoved = removed + filesadded = added + + if origctx and origctx.manifestnode() == mn: + files = origctx.files() + + if not writefilecopymeta: + # If writing only to changeset extras, use None to indicate that + # no entry should be written. If writing to both, write an empty + # entry to prevent the reader from falling back to reading + # filelogs. + p1copies = p1copies or None + p2copies = p2copies or None + filesadded = filesadded or None + filesremoved = filesremoved or None + + return mn, files, p1copies, p2copies, filesadded, filesremoved + + +def _process_files(tr, ctx, error=False): + repo = ctx.repo() + p1 = ctx.p1() + p2 = ctx.p2() + + writechangesetcopy, writefilecopymeta = _write_copy_meta(repo) + + m1ctx = p1.manifestctx() + m2ctx = p2.manifestctx() + mctx = m1ctx.copy() + + m = mctx.read() + m1 = m1ctx.read() + m2 = m2ctx.read() + + # check in files + added = [] + filesadded = [] + removed = list(ctx.removed()) + touched = [] + linkrev = len(repo) + repo.ui.note(_(b"committing files:\n")) + uipathfn = scmutil.getuipathfn(repo) + for f in sorted(ctx.modified() + ctx.added()): + repo.ui.note(uipathfn(f) + b"\n") + try: + fctx = ctx[f] + if fctx is None: + removed.append(f) + else: + added.append(f) + m[f], is_touched = _filecommit( + repo, fctx, m1, m2, linkrev, tr, writefilecopymeta, + ) + if is_touched: + touched.append(f) + if is_touched == 'added': + filesadded.append(f) + m.setflag(f, fctx.flags()) + except OSError: + repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f)) + raise + except IOError as inst: + errcode = getattr(inst, 'errno', errno.ENOENT) + if error or errcode and errcode != errno.ENOENT: + repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f)) + raise + + # update manifest + removed = [f for f in removed if f in m1 or f in m2] + drop = sorted([f for f in removed if f in m]) + for f in drop: + del m[f] + if p2.rev() != nullrev: + rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2)) + removed = [f for f in removed if not rf(f)] + + touched.extend(removed) + + files = touched + mn = _commit_manifest(tr, linkrev, ctx, mctx, files, added, drop) + + return mn, files, filesadded, removed + + +def _filecommit( + repo, fctx, manifest1, manifest2, linkrev, tr, includecopymeta, +): + """ + commit an individual file as part of a larger transaction + + input: + + fctx: a file context with the content we are trying to commit + manifest1: manifest of changeset first parent + manifest2: manifest of changeset second parent + linkrev: revision number of the changeset being created + tr: current transation + individual: boolean, set to False to skip storing the copy data + (only used by the Google specific feature of using + changeset extra as copy source of truth). + + output: (filenode, touched) + + filenode: the filenode that should be used by this changeset + touched: one of: None (mean untouched), 'added' or 'modified' + """ + + fname = fctx.path() + fparent1 = manifest1.get(fname, nullid) + fparent2 = manifest2.get(fname, nullid) + touched = None + if fparent1 == fparent2 == nullid: + touched = 'added' + + if isinstance(fctx, context.filectx): + # This block fast path most comparisons which are usually done. It + # assumes that bare filectx is used and no merge happened, hence no + # need to create a new file revision in this case. + node = fctx.filenode() + if node in [fparent1, fparent2]: + repo.ui.debug(b'reusing %s filelog entry\n' % fname) + if ( + fparent1 != nullid and manifest1.flags(fname) != fctx.flags() + ) or ( + fparent2 != nullid and manifest2.flags(fname) != fctx.flags() + ): + touched = 'modified' + return node, touched + + flog = repo.file(fname) + meta = {} + cfname = fctx.copysource() + fnode = None + + if cfname and cfname != fname: + # Mark the new revision of this file as a copy of another + # file. This copy data will effectively act as a parent + # of this new revision. If this is a merge, the first + # parent will be the nullid (meaning "look up the copy data") + # and the second one will be the other parent. For example: + # + # 0 --- 1 --- 3 rev1 changes file foo + # \ / rev2 renames foo to bar and changes it + # \- 2 -/ rev3 should have bar with all changes and + # should record that bar descends from + # bar in rev2 and foo in rev1 + # + # this allows this merge to succeed: + # + # 0 --- 1 --- 3 rev4 reverts the content change from rev2 + # \ / merging rev3 and rev4 should use bar@rev2 + # \- 2 --- 4 as the merge base + # + + cnode = manifest1.get(cfname) + newfparent = fparent2 + + if manifest2: # branch merge + if fparent2 == nullid or cnode is None: # copied on remote side + if cfname in manifest2: + cnode = manifest2[cfname] + newfparent = fparent1 + + # Here, we used to search backwards through history to try to find + # where the file copy came from if the source of a copy was not in + # the parent directory. However, this doesn't actually make sense to + # do (what does a copy from something not in your working copy even + # mean?) and it causes bugs (eg, issue4476). Instead, we will warn + # the user that copy information was dropped, so if they didn't + # expect this outcome it can be fixed, but this is the correct + # behavior in this circumstance. + + if cnode: + repo.ui.debug(b" %s: copy %s:%s\n" % (fname, cfname, hex(cnode))) + if includecopymeta: + meta[b"copy"] = cfname + meta[b"copyrev"] = hex(cnode) + fparent1, fparent2 = nullid, newfparent + else: + repo.ui.warn( + _( + b"warning: can't find ancestor for '%s' " + b"copied from '%s'!\n" + ) + % (fname, cfname) + ) + + elif fparent1 == nullid: + fparent1, fparent2 = fparent2, nullid + elif fparent2 != nullid: + # is one parent an ancestor of the other? + fparentancestors = flog.commonancestorsheads(fparent1, fparent2) + if fparent1 in fparentancestors: + fparent1, fparent2 = fparent2, nullid + elif fparent2 in fparentancestors: + fparent2 = nullid + elif not fparentancestors: + # TODO: this whole if-else might be simplified much more + ms = mergestate.mergestate.read(repo) + if ( + fname in ms + and ms[fname] == mergestate.MERGE_RECORD_MERGED_OTHER + ): + fparent1, fparent2 = fparent2, nullid + + # is the file changed? + text = fctx.data() + if fparent2 != nullid or meta or flog.cmp(fparent1, text): + if touched is None: # do not overwrite added + touched = 'modified' + fnode = flog.add(text, meta, tr, linkrev, fparent1, fparent2) + # are just the flags changed during merge? + elif fname in manifest1 and manifest1.flags(fname) != fctx.flags(): + touched = 'modified' + fnode = fparent1 + else: + fnode = fparent1 + return fnode, touched + + +def _commit_manifest(tr, linkrev, ctx, mctx, files, added, drop): + """make a new manifest entry (or reuse a new one) + + given an initialised manifest context and precomputed list of + - files: files affected by the commit + - added: new entries in the manifest + - drop: entries present in parents but absent of this one + + Create a new manifest revision, reuse existing ones if possible. + + Return the nodeid of the manifest revision. + """ + repo = ctx.repo() + + md = None + + # all this is cached, so it is find to get them all from the ctx. + p1 = ctx.p1() + p2 = ctx.p2() + m1ctx = p1.manifestctx() + + m1 = m1ctx.read() + + manifest = mctx.read() + + if not files: + # if no "files" actually changed in terms of the changelog, + # try hard to detect unmodified manifest entry so that the + # exact same commit can be reproduced later on convert. + md = m1.diff(manifest, scmutil.matchfiles(repo, ctx.files())) + if not files and md: + repo.ui.debug( + b'not reusing manifest (no file change in ' + b'changelog, but manifest differs)\n' + ) + if files or md: + repo.ui.note(_(b"committing manifest\n")) + # we're using narrowmatch here since it's already applied at + # other stages (such as dirstate.walk), so we're already + # ignoring things outside of narrowspec in most cases. The + # one case where we might have files outside the narrowspec + # at this point is merges, and we already error out in the + # case where the merge has files outside of the narrowspec, + # so this is safe. + mn = mctx.write( + tr, + linkrev, + p1.manifestnode(), + p2.manifestnode(), + added, + drop, + match=repo.narrowmatch(), + ) + else: + repo.ui.debug( + b'reusing manifest from p1 (listed files ' b'actually unchanged)\n' + ) + mn = p1.manifestnode() + + return mn
--- a/mercurial/config.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/config.py Fri Jul 31 17:09:31 2020 +0530 @@ -168,7 +168,7 @@ inc = os.path.normpath(os.path.join(base, expanded)) try: - include(inc, remap=remap, sections=sections) + include(expanded, inc, remap=remap, sections=sections) break except IOError as inst: if inst.errno != errno.ENOENT: @@ -216,8 +216,12 @@ b'config files must be opened in binary mode, got fp=%r mode=%r' % (fp, fp.mode,) ) + + def include(rel, abs, remap, sections): + self.read(abs, remap=remap, sections=sections) + self.parse( - path, fp.read(), sections=sections, remap=remap, include=self.read + path, fp.read(), sections=sections, remap=remap, include=include )
--- a/mercurial/debugcommands.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/debugcommands.py Fri Jul 31 17:09:31 2020 +0530 @@ -1668,8 +1668,8 @@ fm.data(re2=bool(util._re2)) # templates - p = templater.templatepaths() - fm.write(b'templatedirs', b'checking templates (%s)...\n', b' '.join(p)) + p = templater.templatedir() + fm.write(b'templatedirs', b'checking templates (%s)...\n', p or b'') fm.condwrite(not p, b'', _(b" no template directories found\n")) if p: m = templater.templatepath(b"map-cmdline.default")
--- a/mercurial/dirstate.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/dirstate.py Fri Jul 31 17:09:31 2020 +0530 @@ -1425,6 +1425,7 @@ self._opener = opener self._root = root self._filename = b'dirstate' + self._nodelen = 20 self._parents = None self._dirtyparents = False @@ -1609,7 +1610,7 @@ if not self._parents: try: fp = self._opendirstatefile() - st = fp.read(40) + st = fp.read(2 * self._nodelen) fp.close() except IOError as err: if err.errno != errno.ENOENT: @@ -1618,8 +1619,11 @@ st = b'' l = len(st) - if l == 40: - self._parents = (st[:20], st[20:40]) + if l == self._nodelen * 2: + self._parents = ( + st[: self._nodelen], + st[self._nodelen : 2 * self._nodelen], + ) elif l == 0: self._parents = (nullid, nullid) else: @@ -1655,15 +1659,17 @@ if util.safehasattr(parsers, b'dict_new_presized'): # Make an estimate of the number of files in the dirstate based on # its size. From a linear regression on a set of real-world repos, - # all over 10,000 files, the size of a dirstate entry is 85 - # bytes. The cost of resizing is significantly higher than the cost + # all over 10,000 files, the size of a dirstate entry is 2 nodes + # plus 45 bytes. The cost of resizing is significantly higher than the cost # of filling in a larger presized dict, so subtract 20% from the # size. # # This heuristic is imperfect in many ways, so in a future dirstate # format update it makes sense to just record the number of entries # on write. - self._map = parsers.dict_new_presized(len(st) // 71) + self._map = parsers.dict_new_presized( + len(st) // ((2 * self._nodelen + 45) * 4 // 5) + ) # Python's garbage collector triggers a GC each time a certain number # of container objects (the number being defined by
--- a/mercurial/hgweb/hgwebdir_mod.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/hgweb/hgwebdir_mod.py Fri Jul 31 17:09:31 2020 +0530 @@ -414,10 +414,9 @@ fname = req.qsparams[b'static'] static = self.ui.config(b"web", b"static", untrusted=False) if not static: - tp = self.templatepath or templater.templatepaths() - if isinstance(tp, bytes): - tp = [tp] - static = [os.path.join(p, b'static') for p in tp] + tp = self.templatepath or templater.templatedir() + if tp is not None: + static = [os.path.join(tp, b'static')] staticfile(static, fname, res) return res.sendresponse()
--- a/mercurial/hgweb/webcommands.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/hgweb/webcommands.py Fri Jul 31 17:09:31 2020 +0530 @@ -1319,10 +1319,8 @@ # readable by the user running the CGI script static = web.config(b"web", b"static", untrusted=False) if not static: - tp = web.templatepath or templater.templatepaths() - if isinstance(tp, bytes): - tp = [tp] - static = [os.path.join(p, b'static') for p in tp] + tp = web.templatepath or templater.templatedir() + static = [os.path.join(tp, b'static')] staticfile(static, fname, web.res) return web.res.sendresponse()
--- a/mercurial/localrepo.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/localrepo.py Fri Jul 31 17:09:31 2020 +0530 @@ -32,6 +32,7 @@ bundle2, changegroup, color, + commit, context, dirstate, dirstateguard, @@ -46,7 +47,6 @@ match as matchmod, mergestate as mergestatemod, mergeutil, - metadata, namespaces, narrowspec, obsolete, @@ -2771,140 +2771,6 @@ """Returns the wlock if it's held, or None if it's not.""" return self._currentlock(self._wlockref) - def _filecommit( - self, - fctx, - manifest1, - manifest2, - linkrev, - tr, - changelist, - includecopymeta, - ): - """ - commit an individual file as part of a larger transaction - - input: - - fctx: a file context with the content we are trying to commit - manifest1: manifest of changeset first parent - manifest2: manifest of changeset second parent - linkrev: revision number of the changeset being created - tr: current transation - changelist: list of file being changed (modified inplace) - individual: boolean, set to False to skip storing the copy data - (only used by the Google specific feature of using - changeset extra as copy source of truth). - - output: - - The resulting filenode - """ - - fname = fctx.path() - fparent1 = manifest1.get(fname, nullid) - fparent2 = manifest2.get(fname, nullid) - if isinstance(fctx, context.filectx): - node = fctx.filenode() - if node in [fparent1, fparent2]: - self.ui.debug(b'reusing %s filelog entry\n' % fname) - if ( - fparent1 != nullid - and manifest1.flags(fname) != fctx.flags() - ) or ( - fparent2 != nullid - and manifest2.flags(fname) != fctx.flags() - ): - changelist.append(fname) - return node - - flog = self.file(fname) - meta = {} - cfname = fctx.copysource() - if cfname and cfname != fname: - # Mark the new revision of this file as a copy of another - # file. This copy data will effectively act as a parent - # of this new revision. If this is a merge, the first - # parent will be the nullid (meaning "look up the copy data") - # and the second one will be the other parent. For example: - # - # 0 --- 1 --- 3 rev1 changes file foo - # \ / rev2 renames foo to bar and changes it - # \- 2 -/ rev3 should have bar with all changes and - # should record that bar descends from - # bar in rev2 and foo in rev1 - # - # this allows this merge to succeed: - # - # 0 --- 1 --- 3 rev4 reverts the content change from rev2 - # \ / merging rev3 and rev4 should use bar@rev2 - # \- 2 --- 4 as the merge base - # - - cnode = manifest1.get(cfname) - newfparent = fparent2 - - if manifest2: # branch merge - if fparent2 == nullid or cnode is None: # copied on remote side - if cfname in manifest2: - cnode = manifest2[cfname] - newfparent = fparent1 - - # Here, we used to search backwards through history to try to find - # where the file copy came from if the source of a copy was not in - # the parent directory. However, this doesn't actually make sense to - # do (what does a copy from something not in your working copy even - # mean?) and it causes bugs (eg, issue4476). Instead, we will warn - # the user that copy information was dropped, so if they didn't - # expect this outcome it can be fixed, but this is the correct - # behavior in this circumstance. - - if cnode: - self.ui.debug( - b" %s: copy %s:%s\n" % (fname, cfname, hex(cnode)) - ) - if includecopymeta: - meta[b"copy"] = cfname - meta[b"copyrev"] = hex(cnode) - fparent1, fparent2 = nullid, newfparent - else: - self.ui.warn( - _( - b"warning: can't find ancestor for '%s' " - b"copied from '%s'!\n" - ) - % (fname, cfname) - ) - - elif fparent1 == nullid: - fparent1, fparent2 = fparent2, nullid - elif fparent2 != nullid: - # is one parent an ancestor of the other? - fparentancestors = flog.commonancestorsheads(fparent1, fparent2) - if fparent1 in fparentancestors: - fparent1, fparent2 = fparent2, nullid - elif fparent2 in fparentancestors: - fparent2 = nullid - elif not fparentancestors: - # TODO: this whole if-else might be simplified much more - ms = mergestatemod.mergestate.read(self) - if ( - fname in ms - and ms[fname] == mergestatemod.MERGE_RECORD_MERGED_OTHER - ): - fparent1, fparent2 = fparent2, nullid - - # is the file changed? - text = fctx.data() - if fparent2 != nullid or meta or flog.cmp(fparent1, text): - changelist.append(fname) - return flog.add(text, meta, tr, linkrev, fparent1, fparent2) - # are just the flags changed during merge? - elif fname in manifest1 and manifest1.flags(fname) != fctx.flags(): - changelist.append(fname) - - return fparent1 - def checkcommitpatterns(self, wctx, match, status, fail): """check for commit arguments that aren't committable""" if match.isexact() or match.prefix(): @@ -3062,203 +2928,7 @@ @unfilteredmethod def commitctx(self, ctx, error=False, origctx=None): - """Add a new revision to current repository. - Revision information is passed via the context argument. - - ctx.files() should list all files involved in this commit, i.e. - modified/added/removed files. On merge, it may be wider than the - ctx.files() to be committed, since any file nodes derived directly - from p1 or p2 are excluded from the committed ctx.files(). - - origctx is for convert to work around the problem that bug - fixes to the files list in changesets change hashes. For - convert to be the identity, it can pass an origctx and this - function will use the same files list when it makes sense to - do so. - """ - - p1, p2 = ctx.p1(), ctx.p2() - user = ctx.user() - - if self.filecopiesmode == b'changeset-sidedata': - writechangesetcopy = True - writefilecopymeta = True - writecopiesto = None - else: - writecopiesto = self.ui.config(b'experimental', b'copies.write-to') - writefilecopymeta = writecopiesto != b'changeset-only' - writechangesetcopy = writecopiesto in ( - b'changeset-only', - b'compatibility', - ) - p1copies, p2copies = None, None - if writechangesetcopy: - p1copies = ctx.p1copies() - p2copies = ctx.p2copies() - filesadded, filesremoved = None, None - with self.lock(), self.transaction(b"commit") as tr: - trp = weakref.proxy(tr) - - if ctx.manifestnode(): - # reuse an existing manifest revision - self.ui.debug(b'reusing known manifest\n') - mn = ctx.manifestnode() - files = ctx.files() - if writechangesetcopy: - filesadded = ctx.filesadded() - filesremoved = ctx.filesremoved() - elif ctx.files(): - m1ctx = p1.manifestctx() - m2ctx = p2.manifestctx() - mctx = m1ctx.copy() - - m = mctx.read() - m1 = m1ctx.read() - m2 = m2ctx.read() - - # check in files - added = [] - changed = [] - removed = list(ctx.removed()) - linkrev = len(self) - self.ui.note(_(b"committing files:\n")) - uipathfn = scmutil.getuipathfn(self) - for f in sorted(ctx.modified() + ctx.added()): - self.ui.note(uipathfn(f) + b"\n") - try: - fctx = ctx[f] - if fctx is None: - removed.append(f) - else: - added.append(f) - m[f] = self._filecommit( - fctx, - m1, - m2, - linkrev, - trp, - changed, - writefilecopymeta, - ) - m.setflag(f, fctx.flags()) - except OSError: - self.ui.warn( - _(b"trouble committing %s!\n") % uipathfn(f) - ) - raise - except IOError as inst: - errcode = getattr(inst, 'errno', errno.ENOENT) - if error or errcode and errcode != errno.ENOENT: - self.ui.warn( - _(b"trouble committing %s!\n") % uipathfn(f) - ) - raise - - # update manifest - removed = [f for f in removed if f in m1 or f in m2] - drop = sorted([f for f in removed if f in m]) - for f in drop: - del m[f] - if p2.rev() != nullrev: - rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2)) - removed = [f for f in removed if not rf(f)] - - files = changed + removed - md = None - if not files: - # if no "files" actually changed in terms of the changelog, - # try hard to detect unmodified manifest entry so that the - # exact same commit can be reproduced later on convert. - md = m1.diff(m, scmutil.matchfiles(self, ctx.files())) - if not files and md: - self.ui.debug( - b'not reusing manifest (no file change in ' - b'changelog, but manifest differs)\n' - ) - if files or md: - self.ui.note(_(b"committing manifest\n")) - # we're using narrowmatch here since it's already applied at - # other stages (such as dirstate.walk), so we're already - # ignoring things outside of narrowspec in most cases. The - # one case where we might have files outside the narrowspec - # at this point is merges, and we already error out in the - # case where the merge has files outside of the narrowspec, - # so this is safe. - mn = mctx.write( - trp, - linkrev, - p1.manifestnode(), - p2.manifestnode(), - added, - drop, - match=self.narrowmatch(), - ) - - if writechangesetcopy: - filesadded = [ - f for f in changed if not (f in m1 or f in m2) - ] - filesremoved = removed - else: - self.ui.debug( - b'reusing manifest from p1 (listed files ' - b'actually unchanged)\n' - ) - mn = p1.manifestnode() - else: - self.ui.debug(b'reusing manifest from p1 (no file change)\n') - mn = p1.manifestnode() - files = [] - - if writecopiesto == b'changeset-only': - # If writing only to changeset extras, use None to indicate that - # no entry should be written. If writing to both, write an empty - # entry to prevent the reader from falling back to reading - # filelogs. - p1copies = p1copies or None - p2copies = p2copies or None - filesadded = filesadded or None - filesremoved = filesremoved or None - - if origctx and origctx.manifestnode() == mn: - files = origctx.files() - - # update changelog - self.ui.note(_(b"committing changelog\n")) - self.changelog.delayupdate(tr) - n = self.changelog.add( - mn, - files, - ctx.description(), - trp, - p1.node(), - p2.node(), - user, - ctx.date(), - ctx.extra().copy(), - p1copies, - p2copies, - filesadded, - filesremoved, - ) - xp1, xp2 = p1.hex(), p2 and p2.hex() or b'' - self.hook( - b'pretxncommit', - throw=True, - node=hex(n), - parent1=xp1, - parent2=xp2, - ) - # set the new commit is proper phase - targetphase = subrepoutil.newcommitphase(self.ui, ctx) - if targetphase: - # retract boundary do not alter parent changeset. - # if a parent have higher the resulting phase will - # be compliant anyway - # - # if minimal phase was 0 we don't need to retract anything - phases.registernew(self, tr, targetphase, [n]) - return n + return commit.commitctx(self, ctx, error=error, origctx=origctx) @unfilteredmethod def destroying(self):
--- a/mercurial/manifest.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/manifest.py Fri Jul 31 17:09:31 2020 +0530 @@ -315,16 +315,9 @@ b"Manifest values must be a tuple of (node, flags)." ) hashval = value[0] - # hashes are either 20 or 32 bytes (sha1 or its replacement), - # and allow one extra byte taht won't be persisted to disk but - # is sometimes used in memory. - if not isinstance(hashval, bytes) or not ( - 20 <= len(hashval) <= 22 or 32 <= len(hashval) <= 34 - ): + if not isinstance(hashval, bytes) or len(hashval) not in (20, 32): raise TypeError(b"node must be a 20-byte or 32-byte byte string") flags = value[1] - if len(hashval) == 22: - hashval = hashval[:-1] if not isinstance(flags, bytes) or len(flags) > 1: raise TypeError(b"flags must a 0 or 1 byte string, got %r", flags) needle, found = self.bsearch2(key)
--- a/mercurial/state.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/state.py Fri Jul 31 17:09:31 2020 +0530 @@ -164,10 +164,19 @@ operation """ if not self._cmdhint: - return _(b"use 'hg %s --continue' or 'hg %s --abort'") % ( - self._opname, - self._opname, - ) + if not self._stopflag: + return _(b"use 'hg %s --continue' or 'hg %s --abort'") % ( + self._opname, + self._opname, + ) + else: + return _(b"use 'hg %s --continue', 'hg %s --abort', " + b"or 'hg %s --stop'") % ( + self._opname, + self._opname, + self._opname, + ) + return self._cmdhint def msg(self):
--- a/mercurial/subrepoutil.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/subrepoutil.py Fri Jul 31 17:09:31 2020 +0530 @@ -36,7 +36,7 @@ p = config.config() repo = ctx.repo() - def read(f, sections=None, remap=None): + def read(rel, f, sections=None, remap=None): if f in ctx: try: data = ctx[f].data() @@ -56,7 +56,7 @@ ) if b'.hgsub' in ctx: - read(b'.hgsub') + read(b'.hgsub', b'.hgsub') for path, src in ui.configitems(b'subpaths'): p.set(b'subpaths', path, src, ui.configsource(b'subpaths', path))
--- a/mercurial/templater.py Tue Jul 28 10:19:49 2020 -0700 +++ b/mercurial/templater.py Fri Jul 31 17:09:31 2020 +0530 @@ -800,10 +800,10 @@ def stylelist(): - paths = templatepaths() - if not paths: + path = templatedir() + if not path: return _(b'no templates found, try `hg debuginstall` for more info') - dirlist = os.listdir(paths[0]) + dirlist = os.listdir(path) stylelist = [] for file in dirlist: split = file.split(b".") @@ -823,8 +823,23 @@ ) base = os.path.dirname(mapfile) - conf = config.config(includepaths=templatepaths()) - conf.read(mapfile, remap={b'': b'templates'}) + conf = config.config() + + def include(rel, abs, remap, sections): + templatedirs = [base, templatedir()] + for dir in templatedirs: + if dir is None: + continue + abs = os.path.normpath(os.path.join(dir, rel)) + if os.path.isfile(abs): + data = util.posixfile(abs, b'rb').read() + conf.parse( + abs, data, sections=sections, remap=remap, include=include + ) + break + + data = util.posixfile(mapfile, b'rb').read() + conf.parse(mapfile, data, remap={b'': b'templates'}, include=include) cache = {} tmap = {} @@ -837,15 +852,15 @@ # fallback check in template paths if not os.path.exists(path): - for p in templatepaths(): - p2 = util.normpath(os.path.join(p, val)) + dir = templatedir() + if dir is not None: + p2 = util.normpath(os.path.join(dir, val)) if os.path.isfile(p2): path = p2 - break - p3 = util.normpath(os.path.join(p2, b"map")) - if os.path.isfile(p3): - path = p3 - break + else: + p3 = util.normpath(os.path.join(p2, b"map")) + if os.path.isfile(p3): + path = p3 cache, tmap, aliases = _readmapfile(path) @@ -1045,26 +1060,24 @@ return stream -def templatepaths(): - '''return locations used for template files.''' - pathsrel = [b'templates'] - paths = [ - os.path.normpath(os.path.join(resourceutil.datapath, f)) - for f in pathsrel - ] - return [p for p in paths if os.path.isdir(p)] +def templatedir(): + '''return the directory used for template files, or None.''' + path = os.path.normpath(os.path.join(resourceutil.datapath, b'templates')) + return path if os.path.isdir(path) else None def templatepath(name): '''return location of template file. returns None if not found.''' - for p in templatepaths(): - f = os.path.join(p, name) - if os.path.exists(f): - return f + dir = templatedir() + if dir is None: + return None + f = os.path.join(templatedir(), name) + if f and os.path.exists(f): + return f return None -def stylemap(styles, paths=None): +def stylemap(styles, path=None): """Return path to mapfile for a given style. Searches mapfile in the following locations: @@ -1073,31 +1086,29 @@ 3. templatepath/map """ - if paths is None: - paths = templatepaths() - elif isinstance(paths, bytes): - paths = [paths] + if path is None: + path = templatedir() if isinstance(styles, bytes): styles = [styles] - for style in styles: - # only plain name is allowed to honor template paths - if ( - not style - or style in (pycompat.oscurdir, pycompat.ospardir) - or pycompat.ossep in style - or pycompat.osaltsep - and pycompat.osaltsep in style - ): - continue - locations = [os.path.join(style, b'map'), b'map-' + style] - locations.append(b'map') + if path is not None: + for style in styles: + # only plain name is allowed to honor template paths + if ( + not style + or style in (pycompat.oscurdir, pycompat.ospardir) + or pycompat.ossep in style + or pycompat.osaltsep + and pycompat.osaltsep in style + ): + continue + locations = [os.path.join(style, b'map'), b'map-' + style] + locations.append(b'map') - for path in paths: for location in locations: mapfile = os.path.join(path, location) if os.path.isfile(mapfile): return style, mapfile - raise RuntimeError(b"No hgweb templates found in %r" % paths) + raise RuntimeError(b"No hgweb templates found in %r" % path)
--- a/tests/hghave.py Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/hghave.py Fri Jul 31 17:09:31 2020 +0530 @@ -886,8 +886,11 @@ return False -@check("virtualenv", "Python virtualenv support") -def has_virtualenv(): +@check("py2virtualenv", "Python2 virtualenv support") +def has_py2virtualenv(): + if sys.version_info[0] != 2: + return False + try: import virtualenv
--- a/tests/test-absorb-unfinished.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-absorb-unfinished.t Fri Jul 31 17:09:31 2020 +0530 @@ -25,6 +25,6 @@ $ hg --config extensions.rebase= absorb abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255]
--- a/tests/test-annotate.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-annotate.t Fri Jul 31 17:09:31 2020 +0530 @@ -479,26 +479,24 @@ $ cat > ../legacyrepo.py <<EOF > from __future__ import absolute_import - > from mercurial import error, node - > def reposetup(ui, repo): - > class legacyrepo(repo.__class__): - > def _filecommit(self, fctx, manifest1, manifest2, - > linkrev, tr, changelist, includecopymeta): - > fname = fctx.path() - > text = fctx.data() - > flog = self.file(fname) - > fparent1 = manifest1.get(fname, node.nullid) - > fparent2 = manifest2.get(fname, node.nullid) - > meta = {} - > copy = fctx.copysource() - > if copy and copy != fname: - > raise error.Abort('copying is not supported') - > if fparent2 != node.nullid: - > changelist.append(fname) - > return flog.add(text, meta, tr, linkrev, - > fparent1, fparent2) - > raise error.Abort('only merging is supported') - > repo.__class__ = legacyrepo + > from mercurial import commit, error, extensions, node + > def _filecommit(orig, repo, fctx, manifest1, manifest2, + > linkrev, tr, includecopymeta): + > fname = fctx.path() + > text = fctx.data() + > flog = repo.file(fname) + > fparent1 = manifest1.get(fname, node.nullid) + > fparent2 = manifest2.get(fname, node.nullid) + > meta = {} + > copy = fctx.copysource() + > if copy and copy != fname: + > raise error.Abort('copying is not supported') + > if fparent2 != node.nullid: + > return flog.add(text, meta, tr, linkrev, + > fparent1, fparent2), 'modified' + > raise error.Abort('only merging is supported') + > def uisetup(ui): + > extensions.wrapfunction(commit, '_filecommit', _filecommit) > EOF $ cat > baz <<EOF
--- a/tests/test-fastannotate-hg.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-fastannotate-hg.t Fri Jul 31 17:09:31 2020 +0530 @@ -481,26 +481,25 @@ and its ancestor by overriding "repo._filecommit". $ cat > ../legacyrepo.py <<EOF - > from mercurial import error, node - > def reposetup(ui, repo): - > class legacyrepo(repo.__class__): - > def _filecommit(self, fctx, manifest1, manifest2, - > linkrev, tr, changelist, includecopymeta): - > fname = fctx.path() - > text = fctx.data() - > flog = self.file(fname) - > fparent1 = manifest1.get(fname, node.nullid) - > fparent2 = manifest2.get(fname, node.nullid) - > meta = {} - > copy = fctx.renamed() - > if copy and copy[0] != fname: - > raise error.Abort('copying is not supported') - > if fparent2 != node.nullid: - > changelist.append(fname) - > return flog.add(text, meta, tr, linkrev, - > fparent1, fparent2) - > raise error.Abort('only merging is supported') - > repo.__class__ = legacyrepo + > from __future__ import absolute_import + > from mercurial import commit, error, extensions, node + > def _filecommit(orig, repo, fctx, manifest1, manifest2, + > linkrev, tr, includecopymeta): + > fname = fctx.path() + > text = fctx.data() + > flog = repo.file(fname) + > fparent1 = manifest1.get(fname, node.nullid) + > fparent2 = manifest2.get(fname, node.nullid) + > meta = {} + > copy = fctx.copysource() + > if copy and copy != fname: + > raise error.Abort('copying is not supported') + > if fparent2 != node.nullid: + > return flog.add(text, meta, tr, linkrev, + > fparent1, fparent2), 'modified' + > raise error.Abort('only merging is supported') + > def uisetup(ui): + > extensions.wrapfunction(commit, '_filecommit', _filecommit) > EOF $ cat > baz <<EOF
--- a/tests/test-fix.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-fix.t Fri Jul 31 17:09:31 2020 +0530 @@ -84,15 +84,15 @@ lines of files, unless the --whole flag is used. Some tools may always affect the whole file regardless of --whole. - If revisions are specified with --rev, those revisions will be checked, - and they may be replaced with new revisions that have fixed file content. - It is desirable to specify all descendants of each specified revision, so - that the fixes propagate to the descendants. If all descendants are fixed - at the same time, no merging, rebasing, or evolution will be required. + If --working-dir is used, files with uncommitted changes in the working + copy will be fixed. Note that no backup are made. - If --working-dir is used, files with uncommitted changes in the working - copy will be fixed. If the checked-out revision is also fixed, the working - directory will update to the replacement revision. + If revisions are specified with --source, those revisions and their + descendants will be checked, and they may be replaced with new revisions + that have fixed file content. By automatically including the descendants, + no merging, rebasing, or evolution will be required. If an ancestor of the + working copy is included, then the working copy itself will also be fixed, + and the working copy will be updated to the fixed parent. When determining what lines of each file to fix at each revision, the whole set of revisions being fixed is considered, so that fixes to earlier @@ -878,7 +878,7 @@ $ hg --config extensions.rebase= fix -r . abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255] $ cd ..
--- a/tests/test-install.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-install.t Fri Jul 31 17:09:31 2020 +0530 @@ -214,7 +214,7 @@ no problems detected #endif -#if no-py3 virtualenv +#if py2virtualenv Note: --no-site-packages is deprecated, but some places have an ancient virtualenv from their linux distro or similar and it's not yet
--- a/tests/test-rebase-abort.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-rebase-abort.t Fri Jul 31 17:09:31 2020 +0530 @@ -327,7 +327,7 @@ $ echo new > a $ hg up 1 # user gets an error saying to run hg rebase --abort abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255] $ cat a @@ -397,20 +397,20 @@ $ hg rebase -s 3 -d tip abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255] $ hg up . abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255] $ hg up -C . abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255] $ hg graft 3 abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255] $ hg abort
--- a/tests/test-rebase-inmemory.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-rebase-inmemory.t Fri Jul 31 17:09:31 2020 +0530 @@ -901,7 +901,7 @@ [1] $ hg rebase -r 3 -d 1 -t:merge3 abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255] $ hg resolve --list U foo
--- a/tests/test-rebase-obsolete.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-rebase-obsolete.t Fri Jul 31 17:09:31 2020 +0530 @@ -2055,7 +2055,7 @@ $ hg rebase -s 3 -d 5 abort: rebase in progress - (use 'hg rebase --continue' or 'hg rebase --abort') + (use 'hg rebase --continue', 'hg rebase --abort', or 'hg rebase --stop') [255] $ hg rebase --stop --continue abort: cannot specify both --stop and --continue
--- a/tests/test-template-map.t Tue Jul 28 10:19:49 2020 -0700 +++ b/tests/test-template-map.t Fri Jul 31 17:09:31 2020 +0530 @@ -125,6 +125,54 @@ date: Wed Jan 01 10:01:00 2020 +0000 summary: third +Test map inheritance with non-existent base + + $ echo "__base__ = non-existent" > map-base-nonexistent + $ hg log -l1 -T./map-base-nonexistent + abort: style '$TESTTMP/a/non-existent' not found + (available styles: bisect, changelog, compact, default, phases, show, status, xml) + [255] + +Test map inheritance with directory as base + + $ mkdir somedir + $ echo "__base__ = somedir" > map-base-dir + $ hg log -l1 -T./map-base-dir + abort: Is a directory: '$TESTTMP/a/somedir' + [255] + +Test including a built-in template map + + $ cat <<'EOF' > map-include-builtin + > %include map-cmdline.default + > [templates] + > changeset = "{changeset_quiet}\n" + > EOF + $ hg log -l1 -T./map-include-builtin + 8:95c24699272e + + +Test including a nonexistent template map +BROKEN: This should probably be an error just like the bad __base__ above + + $ cat <<'EOF' > map-include-nonexistent + > %include nonexistent + > [templates] + > changeset = "test\n" + > EOF + $ hg log -l1 -T./map-include-nonexistent + test + +Test including a directory as template map +BROKEN: This should probably be an error just like the bad __base__ above + + $ cat <<'EOF' > map-include-dir + > %include somedir + > [templates] + > changeset = "test\n" + > EOF + $ hg log -l1 -T./map-include-dir + test Test docheader, docfooter and separator in template map @@ -1227,6 +1275,12 @@ abort: specify a template [255] +Error if style is a directory: + + $ hg log --style somedir + abort: Is a directory: 'somedir' + [255] + Error if style missing key: $ echo 'q = q' > t