Hello community, here is the log from the commit of package mercurial for openSUSE:Factory checked in at 2016-04-03 23:04:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mercurial (Old) and /work/SRC/openSUSE:Factory/.mercurial.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "mercurial" Changes: -------- --- /work/SRC/openSUSE:Factory/mercurial/mercurial.changes 2016-03-18 21:36:29.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.mercurial.new/mercurial.changes 2016-04-03 23:04:59.000000000 +0200 @@ -1,0 +2,33 @@ +Wed Mar 30 13:18:23 UTC 2016 - devel...@develop7.info + +- update to v3.7.3 + This is an out of cycle release to address three security issues: + * CVE-2016-3630 Mercurial: remote code execution in binary delta decoding + Mercurial prior to 3.7.3 contained two bounds-checking errors in its binary + delta decoder that may be exploitable via clone, push, or pull. + * CVE-2016-3068 Mercurial: arbitrary code execution with Git subrepos + Mercurial prior to 3.7.3 allowed URLs for Git subrepos that could result in + arbitrary code execution on clone. This is a further side-effect of Git + CVE-2015-7545. Reported by Blake Burkhart. + * CVE-2016-3069 Mercurial: arbitrary code execution when converting Git repos + Mercurial prior to 3.7.3 allowed arbitrary code execution when converting + Git repos with hostile names. This could affect automated conversion + services. Reported by Blake Burkhart. + + + bdiff: (pure) support array.array arrays (issue5130) + + convert: add new, non-clowny interface for shelling out to git (SEC) + + convert: dead code removal - old git calling functions (SEC) + + convert: rewrite calls to Git to use the new shelling mechanism (SEC) + + convert: rewrite gitpipe to use common.commandline (SEC) + + convert: test for shell injection in git calls (SEC) + + files: don't recurse into subrepos without a path or -S (issue5127) + + hg: perform update after pulling during clone with share (issue5103) + + mq: restrict generated patch name to 75 characters (issue5117) + + obsolete: fix n^2 marker computation behavior + + parsers: detect short records (SEC) + + parsers: fix list sizing rounding error (SEC) + + streamclone: fix error when store files grow while stream cloning + + subrepo: adapt to git's recent renames-by-default + + subrepo: set GIT_ALLOW_PROTOCOL to limit git clone protocols (SEC) + +------------------------------------------------------------------- Old: ---- mercurial-3.7.2.tar.gz New: ---- mercurial-3.7.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mercurial.spec ++++++ --- /var/tmp/diff_new_pack.EKpNvL/_old 2016-04-03 23:05:00.000000000 +0200 +++ /var/tmp/diff_new_pack.EKpNvL/_new 2016-04-03 23:05:00.000000000 +0200 @@ -17,7 +17,7 @@ Name: mercurial -Version: 3.7.2 +Version: 3.7.3 Release: 0 Summary: Scalable Distributed SCM License: GPL-2.0+ ++++++ mercurial-3.7.2.tar.gz -> mercurial-3.7.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/.hgsigs new/mercurial-3.7.3/.hgsigs --- old/mercurial-3.7.2/.hgsigs 2016-03-02 01:03:50.000000000 +0100 +++ new/mercurial-3.7.3/.hgsigs 2016-03-29 18:54:47.000000000 +0200 @@ -121,3 +121,4 @@ 158bdc8965720ca4061f8f8d806563cfc7cdb62e 0 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/.hgtags new/mercurial-3.7.3/.hgtags --- old/mercurial-3.7.2/.hgtags 2016-03-02 01:03:50.000000000 +0100 +++ new/mercurial-3.7.3/.hgtags 2016-03-29 18:54:47.000000000 +0200 @@ -134,3 +134,4 @@ 158bdc8965720ca4061f8f8d806563cfc7cdb62e 3.7-rc 2408645de650d8a29a6ce9e7dce601d8dd0d1474 3.7 b698abf971e7377d9b7ec7fc8c52df45255b0329 3.7.1 +d493d64757eb45ada99fcb3693e479a51b7782da 3.7.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/PKG-INFO new/mercurial-3.7.3/PKG-INFO --- old/mercurial-3.7.2/PKG-INFO 2016-03-02 01:04:02.000000000 +0100 +++ new/mercurial-3.7.3/PKG-INFO 2016-03-29 18:55:00.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: mercurial -Version: 3.7.2 +Version: 3.7.3 Summary: Fast scalable distributed SCM (revision control, version control) system Home-page: https://mercurial-scm.org/ Author: Matt Mackall and many others diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/hgext/convert/common.py new/mercurial-3.7.3/hgext/convert/common.py --- old/mercurial-3.7.2/hgext/convert/common.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/hgext/convert/common.py 2016-03-29 18:54:44.000000000 +0200 @@ -341,6 +341,9 @@ def _run2(self, cmd, *args, **kwargs): return self._dorun(util.popen2, cmd, *args, **kwargs) + def _run3(self, cmd, *args, **kwargs): + return self._dorun(util.popen3, cmd, *args, **kwargs) + def _dorun(self, openfunc, cmd, *args, **kwargs): cmdline = self._cmdline(cmd, *args, **kwargs) self.ui.debug('running: %s\n' % (cmdline,)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/hgext/convert/git.py new/mercurial-3.7.3/hgext/convert/git.py --- old/mercurial-3.7.2/hgext/convert/git.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/hgext/convert/git.py 2016-03-29 18:54:44.000000000 +0200 @@ -11,7 +11,7 @@ from mercurial.node import hex, nullid from mercurial.i18n import _ -from common import NoRepo, commit, converter_source, checktool +from common import NoRepo, commit, converter_source, checktool, commandline class submodule(object): def __init__(self, path, node, url): @@ -25,61 +25,28 @@ def hgsubstate(self): return "%s %s" % (self.node, self.path) -class convert_git(converter_source): +class convert_git(converter_source, commandline): # Windows does not support GIT_DIR= construct while other systems # cannot remove environment variable. Just assume none have # both issues. - if util.safehasattr(os, 'unsetenv'): - def gitopen(self, s, err=None): - prevgitdir = os.environ.get('GIT_DIR') - os.environ['GIT_DIR'] = self.path - try: - if err == subprocess.PIPE: - (stdin, stdout, stderr) = util.popen3(s) - return stdout - elif err == subprocess.STDOUT: - return self.popen_with_stderr(s) - else: - return util.popen(s, 'rb') - finally: - if prevgitdir is None: - del os.environ['GIT_DIR'] - else: - os.environ['GIT_DIR'] = prevgitdir - def gitpipe(self, s): - prevgitdir = os.environ.get('GIT_DIR') - os.environ['GIT_DIR'] = self.path - try: - return util.popen3(s) - finally: - if prevgitdir is None: - del os.environ['GIT_DIR'] - else: - os.environ['GIT_DIR'] = prevgitdir + def _gitcmd(self, cmd, *args, **kwargs): + return cmd('--git-dir=%s' % self.path, *args, **kwargs) - else: - def gitopen(self, s, err=None): - if err == subprocess.PIPE: - (sin, so, se) = util.popen3('GIT_DIR=%s %s' % (self.path, s)) - return so - elif err == subprocess.STDOUT: - return self.popen_with_stderr(s) - else: - return util.popen('GIT_DIR=%s %s' % (self.path, s), 'rb') + def gitrun0(self, *args, **kwargs): + return self._gitcmd(self.run0, *args, **kwargs) - def gitpipe(self, s): - return util.popen3('GIT_DIR=%s %s' % (self.path, s)) + def gitrun(self, *args, **kwargs): + return self._gitcmd(self.run, *args, **kwargs) - def popen_with_stderr(self, s): - p = subprocess.Popen(s, shell=True, bufsize=-1, - close_fds=util.closefds, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=False, - env=None) - return p.stdout + def gitrunlines0(self, *args, **kwargs): + return self._gitcmd(self.runlines0, *args, **kwargs) + + def gitrunlines(self, *args, **kwargs): + return self._gitcmd(self.runlines, *args, **kwargs) + + def gitpipe(self, *args, **kwargs): + return self._gitcmd(self._run3, *args, **kwargs) def gitread(self, s): fh = self.gitopen(s) @@ -88,6 +55,7 @@ def __init__(self, ui, path, revs=None): super(convert_git, self).__init__(ui, path, revs=revs) + commandline.__init__(self, ui, 'git') if os.path.isdir(path + "/.git"): path += "/.git" @@ -99,20 +67,20 @@ if similarity < 0 or similarity > 100: raise error.Abort(_('similarity must be between 0 and 100')) if similarity > 0: - self.simopt = '-C%d%%' % similarity + self.simopt = ['-C%d%%' % similarity] findcopiesharder = ui.configbool('convert', 'git.findcopiesharder', False) if findcopiesharder: - self.simopt += ' --find-copies-harder' + self.simopt.append('--find-copies-harder') else: - self.simopt = '' + self.simopt = [] checktool('git', 'git') self.path = path self.submodules = [] - self.catfilepipe = self.gitpipe('git cat-file --batch') + self.catfilepipe = self.gitpipe('cat-file', '--batch') def after(self): for f in self.catfilepipe: @@ -120,14 +88,14 @@ def getheads(self): if not self.revs: - heads, ret = self.gitread('git rev-parse --branches --remotes') - heads = heads.splitlines() - if ret: + output, status = self.gitrun('rev-parse', '--branches', '--remotes') + heads = output.splitlines() + if status: raise error.Abort(_('cannot retrieve git heads')) else: heads = [] for rev in self.revs: - rawhead, ret = self.gitread("git rev-parse --verify %s" % rev) + rawhead, ret = self.gitrun('rev-parse', '--verify', rev) heads.append(rawhead[:-1]) if ret: raise error.Abort(_('cannot retrieve git head "%s"') % rev) @@ -187,7 +155,7 @@ self.submodules.append(submodule(s['path'], '', s['url'])) def retrievegitmodules(self, version): - modules, ret = self.gitread("git show %s:%s" % (version, '.gitmodules')) + modules, ret = self.gitrun('show', '%s:%s' % (version, '.gitmodules')) if ret: # This can happen if a file is in the repo that has permissions # 160000, but there is no .gitmodules file. @@ -203,7 +171,7 @@ return for m in self.submodules: - node, ret = self.gitread("git rev-parse %s:%s" % (version, m.path)) + node, ret = self.gitrun('rev-parse', '%s:%s' % (version, m.path)) if ret: continue m.node = node.strip() @@ -212,15 +180,17 @@ if full: raise error.Abort(_("convert from git does not support --full")) self.modecache = {} - fh = self.gitopen("git diff-tree -z --root -m -r %s %s" % ( - self.simopt, version)) + cmd = ['diff-tree','-z', '--root', '-m', '-r'] + self.simopt + [version] + output, status = self.gitrun(*cmd) + if status: + raise error.Abort(_('cannot read changes in %s') % version) changes = [] copies = {} seen = set() entry = None subexists = [False] subdeleted = [False] - difftree = fh.read().split('\x00') + difftree = output.split('\x00') lcount = len(difftree) i = 0 @@ -282,8 +252,6 @@ if f != '.gitmodules' and fdest != '.gitmodules': copies[fdest] = f entry = None - if fh.close(): - raise error.Abort(_('cannot read changes in %s') % version) if subexists[0]: if subdeleted[0]: @@ -329,17 +297,23 @@ return c def numcommits(self): - return len([None for _ in self.gitopen('git rev-list --all')]) + output, ret = self.gitrunlines('rev-list', '--all') + if ret: + raise error.Abort(_('cannot retrieve number of commits in %s') \ + % self.path) + return len(output) def gettags(self): tags = {} alltags = {} - fh = self.gitopen('git ls-remote --tags "%s"' % self.path, - err=subprocess.STDOUT) + output, status = self.gitrunlines('ls-remote', '--tags', self.path) + + if status: + raise error.Abort(_('cannot read tags from %s') % self.path) prefix = 'refs/tags/' # Build complete list of tags, both annotated and bare ones - for line in fh: + for line in output: line = line.strip() if line.startswith("error:") or line.startswith("fatal:"): raise error.Abort(_('cannot read tags from %s') % self.path) @@ -347,8 +321,6 @@ if not tag.startswith(prefix): continue alltags[tag[len(prefix):]] = node - if fh.close(): - raise error.Abort(_('cannot read tags from %s') % self.path) # Filter out tag objects for annotated tag refs for tag in alltags: @@ -365,18 +337,20 @@ def getchangedfiles(self, version, i): changes = [] if i is None: - fh = self.gitopen("git diff-tree --root -m -r %s" % version) - for l in fh: + output, status = self.gitrunlines('diff-tree', '--root', '-m', + '-r', version) + if status: + raise error.Abort(_('cannot read changes in %s') % version) + for l in output: if "\t" not in l: continue m, f = l[:-1].split("\t") changes.append(f) else: - fh = self.gitopen('git diff-tree --name-only --root -r %s ' - '"%s^%s" --' % (version, version, i + 1)) - changes = [f.rstrip('\n') for f in fh] - if fh.close(): - raise error.Abort(_('cannot read changes in %s') % version) + output, status = self.gitrunlines('diff-tree', '--name-only', + '--root', '-r', version, + '%s^%s' % (version, i + 1), '--') + changes = [f.rstrip('\n') for f in output] return changes @@ -396,8 +370,8 @@ ]) try: - fh = self.gitopen('git show-ref', err=subprocess.PIPE) - for line in fh: + output, status = self.gitrunlines('show-ref') + for line in output: line = line.strip() rev, name = line.split(None, 1) # Process each type of branch diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/hgext/mq.py new/mercurial-3.7.3/hgext/mq.py --- old/mercurial-3.7.2/hgext/mq.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/hgext/mq.py 2016-03-29 18:54:44.000000000 +0200 @@ -1117,6 +1117,7 @@ """Return a suitable filename for title, adding a suffix to make it unique in the existing list""" namebase = re.sub('[\s\W_]+', '_', title.lower()).strip('_') + namebase = namebase[:75] # avoid too long name (issue5117) if namebase: try: self.checkreservedname(namebase) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/mercurial/__version__.py new/mercurial-3.7.3/mercurial/__version__.py --- old/mercurial-3.7.2/mercurial/__version__.py 2016-03-02 01:04:00.000000000 +0100 +++ new/mercurial-3.7.3/mercurial/__version__.py 2016-03-29 18:54:57.000000000 +0200 @@ -1,2 +1,2 @@ # this file is autogenerated by setup.py -version = "3.7.2" +version = "3.7.3" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/mercurial/cmdutil.py new/mercurial-3.7.3/mercurial/cmdutil.py --- old/mercurial-3.7.2/mercurial/cmdutil.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/mercurial/cmdutil.py 2016-03-29 18:54:44.000000000 +0200 @@ -2340,14 +2340,15 @@ for subpath in sorted(ctx.substate): def matchessubrepo(subpath): - return (m.always() or m.exact(subpath) + return (m.exact(subpath) or any(f.startswith(subpath + '/') for f in m.files())) if subrepos or matchessubrepo(subpath): sub = ctx.sub(subpath) try: submatch = matchmod.narrowmatcher(subpath, m) - if sub.printfiles(ui, submatch, fm, fmt, subrepos) == 0: + recurse = m.exact(subpath) or subrepos + if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0: ret = 0 except error.LookupError: ui.status(_("skipping missing subrepository: %s\n") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/mercurial/hg.py new/mercurial-3.7.3/mercurial/hg.py --- old/mercurial-3.7.2/mercurial/hg.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/mercurial/hg.py 2016-03-29 18:54:45.000000000 +0200 @@ -236,20 +236,7 @@ r = repository(ui, destwvfs.base) postshare(srcrepo, r, bookmarks=bookmarks) - - if update: - r.ui.status(_("updating working directory\n")) - if update is not True: - checkout = update - for test in (checkout, 'default', 'tip'): - if test is None: - continue - try: - uprev = r.lookup(test) - break - except error.RepoLookupError: - continue - _update(r, uprev) + _postshareupdate(r, update, checkout=checkout) def postshare(sourcerepo, destrepo, bookmarks=True): """Called after a new shared repo is created. @@ -272,6 +259,27 @@ fp.write('bookmarks\n') fp.close() +def _postshareupdate(repo, update, checkout=None): + """Maybe perform a working directory update after a shared repo is created. + + ``update`` can be a boolean or a revision to update to. + """ + if not update: + return + + repo.ui.status(_("updating working directory\n")) + if update is not True: + checkout = update + for test in (checkout, 'default', 'tip'): + if test is None: + continue + try: + uprev = repo.lookup(test) + break + except error.RepoLookupError: + continue + _update(repo, uprev) + def copystore(ui, srcrepo, destpath): '''copy files from store of srcrepo in destpath @@ -361,7 +369,7 @@ rev=rev, update=False, stream=stream) sharerepo = repository(ui, path=sharepath) - share(ui, sharerepo, dest=dest, update=update, bookmarks=False) + share(ui, sharerepo, dest=dest, update=False, bookmarks=False) # We need to perform a pull against the dest repo to fetch bookmarks # and other non-store data that isn't shared by default. In the case of @@ -371,6 +379,8 @@ destrepo = repository(ui, path=dest) exchange.pull(destrepo, srcpeer, heads=revs) + _postshareupdate(destrepo, update) + return srcpeer, peer(ui, peeropts, dest) def clone(ui, peeropts, source, dest=None, pull=False, rev=None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/mercurial/mpatch.c new/mercurial-3.7.3/mercurial/mpatch.c --- old/mercurial-3.7.2/mercurial/mpatch.c 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/mercurial/mpatch.c 2016-03-29 18:54:44.000000000 +0200 @@ -205,7 +205,7 @@ int pos = 0; /* assume worst case size, we won't have many of these lists */ - l = lalloc(len / 12); + l = lalloc(len / 12 + 1); if (!l) return NULL; @@ -215,10 +215,10 @@ lt->start = getbe32(bin + pos); lt->end = getbe32(bin + pos + 4); lt->len = getbe32(bin + pos + 8); - if (lt->start > lt->end) - break; /* sanity check */ lt->data = bin + pos + 12; pos += 12 + lt->len; + if (lt->start > lt->end || lt->len < 0) + break; /* sanity check */ lt++; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/mercurial/obsolete.py new/mercurial-3.7.3/mercurial/obsolete.py --- old/mercurial-3.7.2/mercurial/obsolete.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/mercurial/obsolete.py 2016-03-29 18:54:45.000000000 +0200 @@ -1225,6 +1225,7 @@ metadata['user'] = repo.ui.username() tr = repo.transaction('add-obsolescence-marker') try: + markerargs = [] for rel in relations: prec = rel[0] sucs = rel[1] @@ -1243,6 +1244,15 @@ npare = tuple(p.node() for p in prec.parents()) if nprec in nsucs: raise error.Abort("changeset %s cannot obsolete itself" % prec) + + # Creating the marker causes the hidden cache to become invalid, + # which causes recomputation when we ask for prec.parents() above. + # Resulting in n^2 behavior. So let's prepare all of the args + # first, then create the markers. + markerargs.append((nprec, nsucs, npare, localmetadata)) + + for args in markerargs: + nprec, nsucs, npare, localmetadata = args repo.obsstore.create(tr, nprec, nsucs, flag, parents=npare, date=date, metadata=localmetadata) repo.filteredrevcache.clear() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/mercurial/pure/bdiff.py new/mercurial-3.7.3/mercurial/pure/bdiff.py --- old/mercurial-3.7.2/mercurial/pure/bdiff.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/mercurial/pure/bdiff.py 2016-03-29 18:54:44.000000000 +0200 @@ -7,6 +7,7 @@ from __future__ import absolute_import +import array import difflib import re import struct @@ -50,9 +51,15 @@ r.append(prev) return r +def _tostring(c): + if type(c) is array.array: + # this copy overhead isn't ideal + return c.tostring() + return str(c) + def bdiff(a, b): - a = str(a).splitlines(True) - b = str(b).splitlines(True) + a = _tostring(a).splitlines(True) + b = _tostring(b).splitlines(True) if not a: s = "".join(b) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/mercurial/streamclone.py new/mercurial-3.7.3/mercurial/streamclone.py --- old/mercurial-3.7.2/mercurial/streamclone.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/mercurial/streamclone.py 2016-03-29 18:54:45.000000000 +0200 @@ -206,7 +206,8 @@ # partially encode name over the wire for backwards compat yield '%s\0%d\n' % (store.encodedir(name), size) if size <= 65536: - yield svfs.read(name) + with svfs(name, 'rb') as fp: + yield fp.read(size) else: for chunk in util.filechunkiter(svfs(name), limit=size): yield chunk diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/mercurial/subrepo.py new/mercurial-3.7.3/mercurial/subrepo.py --- old/mercurial-3.7.2/mercurial/subrepo.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/mercurial/subrepo.py 2016-03-29 18:54:45.000000000 +0200 @@ -1383,6 +1383,11 @@ are not supported and very probably fail. """ self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands))) + if env is None: + env = os.environ.copy() + # fix for Git CVE-2015-7545 + if 'GIT_ALLOW_PROTOCOL' not in env: + env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh' # unless ui.quiet is set, print git's stderr, # which is mostly progress and useful info errpipe = None @@ -1810,9 +1815,9 @@ modified, added, removed = [], [], [] self._gitupdatestat() if rev2: - command = ['diff-tree', '-r', rev1, rev2] + command = ['diff-tree', '--no-renames', '-r', rev1, rev2] else: - command = ['diff-index', rev1] + command = ['diff-index', '--no-renames', rev1] out = self._gitcommand(command) for line in out.split('\n'): tab = line.find('\t') @@ -1871,7 +1876,7 @@ @annotatesubrepoerror def diff(self, ui, diffopts, node2, match, prefix, **opts): node1 = self._state[1] - cmd = ['diff'] + cmd = ['diff', '--no-renames'] if opts['stat']: cmd.append('--stat') else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/tests/hypothesishelpers.py new/mercurial-3.7.3/tests/hypothesishelpers.py --- old/mercurial-3.7.2/tests/hypothesishelpers.py 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/tests/hypothesishelpers.py 2016-03-29 18:54:45.000000000 +0200 @@ -8,9 +8,16 @@ import sys import traceback -from hypothesis.settings import set_hypothesis_home_dir +try: + # hypothesis 2.x + from hypothesis.configuration import set_hypothesis_home_dir + from hypothesis import settings +except ImportError: + # hypothesis 1.x + from hypothesis.settings import set_hypothesis_home_dir + from hypothesis import Settings as settings import hypothesis.strategies as st -from hypothesis import given, Settings +from hypothesis import given # hypothesis store data regarding generate example and code set_hypothesis_home_dir(os.path.join( @@ -26,7 +33,7 @@ # Fixed in version 1.13 (released 2015 october 29th) f.__module__ = '__anon__' try: - given(*args, settings=Settings(max_examples=2000), **kwargs)(f)() + given(*args, settings=settings(max_examples=2000), **kwargs)(f)() except Exception: traceback.print_exc(file=sys.stdout) sys.exit(1) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/tests/test-clone-uncompressed.t new/mercurial-3.7.3/tests/test-clone-uncompressed.t --- old/mercurial-3.7.2/tests/test-clone-uncompressed.t 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/tests/test-clone-uncompressed.t 2016-03-29 18:54:45.000000000 +0200 @@ -1,5 +1,8 @@ #require serve +Initialize repository +the status call is to check for issue5130 + $ hg init server $ cd server $ touch foo @@ -8,6 +11,7 @@ ... with open(str(i), 'wb') as fh: ... fh.write(str(i)) $ hg -q commit -A -m 'add a lot of files' + $ hg st $ hg serve -p $HGPORT -d --pid-file=hg.pid $ cat hg.pid >> $DAEMON_PIDS $ cd .. @@ -46,3 +50,43 @@ preparing listkeys for "phases" sending listkeys command received listkey for "phases": 58 bytes + + +Stream clone while repo is changing: + + $ mkdir changing + $ cd changing + +extension for delaying the server process so we reliably can modify the repo +while cloning + + $ cat > delayer.py <<EOF + > import time + > from mercurial import extensions, scmutil + > def __call__(orig, self, path, *args, **kwargs): + > if path == 'data/f1.i': + > time.sleep(2) + > return orig(self, path, *args, **kwargs) + > extensions.wrapfunction(scmutil.vfs, '__call__', __call__) + > EOF + +prepare repo with small and big file to cover both code paths in emitrevlogdata + + $ hg init repo + $ touch repo/f1 + $ $TESTDIR/seq.py 50000 > repo/f2 + $ hg -R repo ci -Aqm "0" + $ hg -R repo serve -p $HGPORT1 -d --pid-file=hg.pid --config extensions.delayer=delayer.py + $ cat hg.pid >> $DAEMON_PIDS + +clone while modifying the repo between stating file with write lock and +actually serving file content + + $ hg clone -q --uncompressed -U http://localhost:$HGPORT1 clone & + $ sleep 1 + $ echo >> repo/f1 + $ echo >> repo/f2 + $ hg -R repo ci -m "1" + $ wait + $ hg -R clone id + 000000000000 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/tests/test-clone.t new/mercurial-3.7.3/tests/test-clone.t --- old/mercurial-3.7.2/tests/test-clone.t 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/tests/test-clone.t 2016-03-29 18:54:45.000000000 +0200 @@ -774,11 +774,11 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes no changes found adding remote bookmark bookA + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved The shared repo should have been created @@ -804,8 +804,6 @@ $ hg --config share.pool=share clone source1b share-dest1b (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1) - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes adding changesets adding manifests @@ -813,6 +811,8 @@ added 4 changesets with 4 changes to 1 files (+4 heads) adding remote bookmark head1 adding remote bookmark head2 + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ ls share b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1 @@ -831,6 +831,17 @@ $ hg -R share-dest1b config paths.default $TESTTMP/source1a (glob) +Checked out revision should be head of default branch + + $ hg -R share-dest1b log -r . + changeset: 4:99f71071f117 + bookmark: head2 + parent: 0:b5f04eac9d8f + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: head2 + + Clone from unrelated repo should result in new share $ hg --config share.pool=share clone source2 share-dest2 @@ -840,10 +851,10 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes no changes found + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ ls share 22aeff664783fd44c6d9b435618173c118c3448e @@ -858,11 +869,11 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes no changes found adding remote bookmark bookA + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ ls shareremote 195bb1fcdb595c14a6c13e0269129ed78f6debde @@ -874,12 +885,12 @@ adding manifests adding file changes added 6 changesets with 6 changes to 1 files (+4 heads) - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes no changes found adding remote bookmark head1 adding remote bookmark head2 + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ ls shareremote 195bb1fcdb595c14a6c13e0269129ed78f6debde @@ -893,10 +904,10 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved no changes found adding remote bookmark head1 + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R share-1arev log -G @ changeset: 1:4a8dc1ab4c13 @@ -916,8 +927,6 @@ $ hg --config share.pool=sharerevs clone -r 99f71071f117 source1b share-1brev (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1) - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes adding changesets adding manifests @@ -925,9 +934,11 @@ added 1 changesets with 1 changes to 1 files (+1 heads) adding remote bookmark head1 adding remote bookmark head2 + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R share-1brev log -G - o changeset: 2:99f71071f117 + @ changeset: 2:99f71071f117 | bookmark: head2 | tag: tip | parent: 0:b5f04eac9d8f @@ -935,7 +946,7 @@ | date: Thu Jan 01 00:00:00 1970 +0000 | summary: head2 | - | @ changeset: 1:4a8dc1ab4c13 + | o changeset: 1:4a8dc1ab4c13 |/ bookmark: head1 | user: test | date: Thu Jan 01 00:00:00 1970 +0000 @@ -955,9 +966,9 @@ adding manifests adding file changes added 2 changesets with 2 changes to 1 files + no changes found updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - no changes found $ hg -R share-1bbranch1 log -G o changeset: 1:5f92a6c1a1b1 @@ -975,13 +986,13 @@ $ hg --config share.pool=sharebranch clone -b branch2 source1b share-1bbranch2 (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1) - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg -R share-1bbranch2 log -G o changeset: 2:6bacf4683960 @@ -1056,18 +1067,18 @@ adding manifests adding file changes added 3 changesets with 3 changes to 1 files - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved searching for changes no changes found adding remote bookmark bookA + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cat race2.log (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1) - updating working directory - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved waiting for lock on repository share-destrace2 held by * (glob) got lock after \d+ seconds (re) searching for changes no changes found adding remote bookmark bookA + updating working directory + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/tests/test-convert-git.t new/mercurial-3.7.3/tests/test-convert-git.t --- old/mercurial-3.7.2/tests/test-convert-git.t 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/tests/test-convert-git.t 2016-03-29 18:54:45.000000000 +0200 @@ -714,7 +714,7 @@ $ COMMIT_OBJ=1c/0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd $ mv git-repo4/.git/objects/$COMMIT_OBJ git-repo4/.git/objects/$COMMIT_OBJ.tmp $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:' - abort: cannot read tags from git-repo4/.git + abort: cannot retrieve number of commits in git-repo4/.git $ mv git-repo4/.git/objects/$COMMIT_OBJ.tmp git-repo4/.git/objects/$COMMIT_OBJ damage git repository by renaming a blob object @@ -729,3 +729,20 @@ $ mv git-repo4/.git/objects/$TREE_OBJ git-repo4/.git/objects/$TREE_OBJ.tmp $ hg convert git-repo4 git-repo4-broken-hg 2>&1 | grep 'abort:' abort: cannot read changes in 1c0ce3c5886f83a1d78a7b517cdff5cf9ca17bdd + +test for escaping the repo name (CVE-2016-3069) + + $ git init '`echo pwned >COMMAND-INJECTION`' + Initialized empty Git repository in $TESTTMP/`echo pwned >COMMAND-INJECTION`/.git/ + $ cd '`echo pwned >COMMAND-INJECTION`' + $ git commit -q --allow-empty -m 'empty' + $ cd .. + $ hg convert '`echo pwned >COMMAND-INJECTION`' 'converted' + initializing destination converted repository + scanning source... + sorting... + converting... + 0 empty + updating bookmarks + $ test -f COMMAND-INJECTION + [1] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/tests/test-mq-qimport.t new/mercurial-3.7.3/tests/test-mq-qimport.t --- old/mercurial-3.7.2/tests/test-mq-qimport.t 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/tests/test-mq-qimport.t 2016-03-29 18:54:45.000000000 +0200 @@ -331,3 +331,12 @@ 2.diff taken__2 +check very long patch name + + $ hg qpop -qa + patch queue now empty + $ echo >> b + $ hg commit -m 'abcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi pqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + $ hg qimport -r . + $ hg qap + abcdefghi_pqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi_pqrstuvwxyzabcdefg diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/tests/test-revlog.t new/mercurial-3.7.3/tests/test-revlog.t --- old/mercurial-3.7.2/tests/test-revlog.t 1970-01-01 01:00:00.000000000 +0100 +++ new/mercurial-3.7.3/tests/test-revlog.t 2016-03-29 18:54:45.000000000 +0200 @@ -0,0 +1,15 @@ +Test for CVE-2016-3630 + + $ hg init + + >>> open("a.i", "w").write( + ... """eJxjYGZgZIAAYQYGxhgom+k/FMx8YKx9ZUaKSOyqo4cnuKb8mbqHV5cBCVTMWb1Cwqkhe4Gsg9AD + ... Joa3dYtcYYYBAQ8Qr4OqZAYRICPTSr5WKd/42rV36d+8/VmrNpv7NP1jQAXrQE4BqQUARngwVA==""" + ... .decode("base64").decode("zlib")) + + $ hg debugindex a.i + rev offset length delta linkrev nodeid p1 p2 + 0 0 19 -1 2 99e0332bd498 000000000000 000000000000 + 1 19 12 0 3 6674f57a23d8 99e0332bd498 000000000000 + $ hg debugdata a.i 1 2>&1 | grep decoded + mpatch.mpatchError: patch cannot be decoded diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/tests/test-subrepo-deep-nested-change.t new/mercurial-3.7.3/tests/test-subrepo-deep-nested-change.t --- old/mercurial-3.7.2/tests/test-subrepo-deep-nested-change.t 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/tests/test-subrepo-deep-nested-change.t 2016-03-29 18:54:45.000000000 +0200 @@ -297,6 +297,12 @@ sub1/sub2/folder/bar (glob) sub1/sub2/x.txt (glob) + $ hg files + .hgsub + .hgsubstate + foo/bar/abc (glob) + main + $ hg files -S -r '.^' sub1/sub2/folder sub1/sub2/folder/test.txt (glob) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mercurial-3.7.2/tests/test-subrepo-git.t new/mercurial-3.7.3/tests/test-subrepo-git.t --- old/mercurial-3.7.2/tests/test-subrepo-git.t 2016-03-02 01:03:43.000000000 +0100 +++ new/mercurial-3.7.3/tests/test-subrepo-git.t 2016-03-29 18:54:45.000000000 +0200 @@ -1132,4 +1132,36 @@ ? s/foobar.orig ? s/snake.python.orig +test for Git CVE-2016-3068 + $ hg init malicious-subrepository + $ cd malicious-subrepository + $ echo "s = [git]ext::sh -c echo% pwned% >&2" > .hgsub + $ git init s + Initialized empty Git repository in $TESTTMP/tc/malicious-subrepository/s/.git/ + $ cd s + $ git commit --allow-empty -m 'empty' + [master (root-commit) 153f934] empty $ cd .. + $ hg add .hgsub + $ hg commit -m "add subrepo" + $ cd .. + $ env -u GIT_ALLOW_PROTOCOL hg clone malicious-subrepository malicious-subrepository-protected + Cloning into '$TESTTMP/tc/malicious-subrepository-protected/s'... + fatal: transport 'ext' not allowed + updating to branch default + cloning subrepo s from ext::sh -c echo% pwned% >&2 + abort: git clone error 128 in s (in subrepo s) + [255] + +whitelisting of ext should be respected (that's the git submodule behaviour) + $ env GIT_ALLOW_PROTOCOL=ext hg clone malicious-subrepository malicious-subrepository-clone-allowed + Cloning into '$TESTTMP/tc/malicious-subrepository-clone-allowed/s'... + pwned + fatal: Could not read from remote repository. + + Please make sure you have the correct access rights + and the repository exists. + updating to branch default + cloning subrepo s from ext::sh -c echo% pwned% >&2 + abort: git clone error 128 in s (in subrepo s) + [255]