changeset 40819:cb372d09d30a

merge with stable
author Augie Fackler <augie@google.com>
date Tue, 04 Dec 2018 17:13:01 -0500
parents e13ab4acf555 (current diff) 47719d7c581f (diff)
children 96be0ecad648
files hgext/extdiff.py hgext/rebase.py mercurial/cext/revlog.c mercurial/commandserver.py mercurial/context.py
diffstat 12 files changed, 200 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/.hgsigs	Thu Nov 29 09:13:13 2018 +0000
+++ b/.hgsigs	Tue Dec 04 17:13:01 2018 -0500
@@ -172,3 +172,4 @@
 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
+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
--- a/.hgtags	Thu Nov 29 09:13:13 2018 +0000
+++ b/.hgtags	Tue Dec 04 17:13:01 2018 -0500
@@ -185,3 +185,4 @@
 5405cb1a79010ac50c58cd84e6f50c4556bf2a4c 4.7.2
 956ec6f1320df26f3133ec40f3de866ea0695fd7 4.8rc0
 a91a2837150bdcb27ae76b3646e6c93cd6a15904 4.8
+1c8c54cf97256f4468da2eb4dbee24f7f3888e71 4.8.1
--- a/hgext/extdiff.py	Thu Nov 29 09:13:13 2018 +0000
+++ b/hgext/extdiff.py	Tue Dec 04 17:13:01 2018 -0500
@@ -430,6 +430,7 @@
             if args:
                 cmdline += ' ' + args
         command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd,
+                helpcategory=command.CATEGORY_FILE_CONTENTS,
                 inferrepo=True)(savedcmd(path, cmdline))
 
 # tell hggettext to extract docstrings from these functions:
--- a/hgext/rebase.py	Thu Nov 29 09:13:13 2018 +0000
+++ b/hgext/rebase.py	Tue Dec 04 17:13:01 2018 -0500
@@ -542,7 +542,7 @@
             p1, p2, base = defineparents(repo, rev, self.destmap,
                                          self.state, self.skipped,
                                          self.obsoletenotrebased)
-            if len(repo[None].parents()) == 2:
+            if not self.inmemory and len(repo[None].parents()) == 2:
                 repo.ui.debug('resuming interrupted rebase\n')
             else:
                 overrides = {('ui', 'forcemerge'): opts.get('tool', '')}
@@ -867,7 +867,11 @@
         except error.InMemoryMergeConflictsError:
             ui.warn(_('hit merge conflicts; re-running rebase without in-memory'
                       ' merge\n'))
-            _dorebase(ui, repo, action='abort', opts={})
+            # TODO: Make in-memory merge not use the on-disk merge state, so
+            # we don't have to clean it here
+            mergemod.mergestate.clean(repo)
+            clearstatus(repo)
+            clearcollapsemsg(repo)
             return _dorebase(ui, repo, action, opts, inmemory=False)
     else:
         return _dorebase(ui, repo, action, opts)
--- a/mercurial/cext/revlog.c	Thu Nov 29 09:13:13 2018 +0000
+++ b/mercurial/cext/revlog.c	Tue Dec 04 17:13:01 2018 -0500
@@ -158,6 +158,12 @@
 	return (const char *)(self->buf.buf) + pos * v1_hdrsize;
 }
 
+/*
+ * Get parents of the given rev.
+ *
+ * The specified rev must be valid and must not be nullrev. A returned
+ * parent revision may be nullrev, but is guaranteed to be in valid range.
+ */
 static inline int index_get_parents(indexObject *self, Py_ssize_t rev, int *ps,
                                     int maxrev)
 {
@@ -180,7 +186,7 @@
 	}
 	/* If index file is corrupted, ps[] may point to invalid revisions. So
 	 * there is a risk of buffer overflow to trust them unconditionally. */
-	if (ps[0] > maxrev || ps[1] > maxrev) {
+	if (ps[0] < -1 || ps[0] > maxrev || ps[1] < -1 || ps[1] > maxrev) {
 		PyErr_SetString(PyExc_ValueError, "parent out of range");
 		return -1;
 	}
@@ -2688,6 +2694,16 @@
 int rustlazyancestors_next(rustlazyancestorsObject *self);
 int rustlazyancestors_contains(rustlazyancestorsObject *self, long rev);
 
+static int index_get_parents_checked(indexObject *self, Py_ssize_t rev, int *ps,
+                                     int maxrev)
+{
+	if (rev < 0 || rev >= index_length(self)) {
+		PyErr_SetString(PyExc_ValueError, "rev out of range");
+		return -1;
+	}
+	return index_get_parents(self, rev, ps, maxrev);
+}
+
 /* CPython instance methods */
 static int rustla_init(rustlazyancestorsObject *self, PyObject *args)
 {
@@ -2729,7 +2745,8 @@
 	                                    initrevs, stoprev, inclusive);
 	if (self->iter == NULL) {
 		/* if this is because of GraphError::ParentOutOfRange
-		 * index_get_parents() has already set the proper ValueError */
+		 * index_get_parents_checked() has already set the proper
+		 * ValueError */
 		goto bail;
 	}
 
--- a/mercurial/commandserver.py	Thu Nov 29 09:13:13 2018 +0000
+++ b/mercurial/commandserver.py	Tue Dec 04 17:13:01 2018 -0500
@@ -526,7 +526,15 @@
                 # waiting for recv() will receive ECONNRESET.
                 self._unlinksocket()
                 exiting = True
-            ready = selector.select(timeout=h.pollinterval)
+            try:
+                ready = selector.select(timeout=h.pollinterval)
+            except OSError as inst:
+                # selectors2 raises ETIMEDOUT if timeout exceeded while
+                # handling signal interrupt. That's probably wrong, but
+                # we can easily get around it.
+                if inst.errno != errno.ETIMEDOUT:
+                    raise
+                ready = []
             if not ready:
                 # only exit if we completed all queued requests
                 if exiting:
--- a/mercurial/context.py	Thu Nov 29 09:13:13 2018 +0000
+++ b/mercurial/context.py	Tue Dec 04 17:13:01 2018 -0500
@@ -1843,6 +1843,11 @@
         else:
             return self._wrappedctx[path].flags()
 
+    def __contains__(self, key):
+        if key in self._cache:
+            return self._cache[key]['exists']
+        return key in self.p1()
+
     def _existsinparent(self, path):
         try:
             # ``commitctx` raises a ``ManifestLookupError`` if a path does not
@@ -1877,19 +1882,19 @@
         components = path.split('/')
         for i in pycompat.xrange(len(components)):
             component = "/".join(components[0:i])
-            if component in self.p1() and self._cache[component]['exists']:
+            if component in self:
                 fail(path, component)
 
         # Test the other direction -- that this path from p2 isn't a directory
-        # in p1 (test that p1 doesn't any paths matching `path/*`).
-        match = matchmod.match('/', '', [path + '/'], default=b'relpath')
+        # in p1 (test that p1 doesn't have any paths matching `path/*`).
+        match = self.match(pats=[path + '/'], default=b'path')
         matches = self.p1().manifest().matches(match)
         mfiles = matches.keys()
         if len(mfiles) > 0:
             if len(mfiles) == 1 and mfiles[0] == path:
                 return
             # omit the files which are deleted in current IMM wctx
-            mfiles = [m for m in mfiles if self._cache[m]['exists']]
+            mfiles = [m for m in mfiles if m in self]
             if not mfiles:
                 return
             raise error.Abort("error: file '%s' cannot be written because "
--- a/mercurial/thirdparty/selectors2.py	Thu Nov 29 09:13:13 2018 +0000
+++ b/mercurial/thirdparty/selectors2.py	Tue Dec 04 17:13:01 2018 -0500
@@ -708,7 +708,7 @@
                     if expires is not None:
                         current_time = monotonic()
                         if current_time > expires:
-                            raise OSError(errno=errno.ETIMEDOUT)
+                            raise OSError(errno.ETIMEDOUT, 'Connection timed out')
                         if recalc_timeout:
                             if "timeout" in kwargs:
                                 kwargs["timeout"] = expires - current_time
--- a/mercurial/wireprotov2peer.py	Thu Nov 29 09:13:13 2018 +0000
+++ b/mercurial/wireprotov2peer.py	Tue Dec 04 17:13:01 2018 -0500
@@ -377,25 +377,30 @@
         # This can raise. The caller can handle it.
         response._onresponsedata(meta['data'])
 
-        # If we got a content redirect response, we want to fetch it and
-        # expose the data as if we received it inline. But we also want to
-        # keep our internal request accounting in order. Our strategy is to
-        # basically put meaningful response handling on pause until EOS occurs
-        # and the stream accounting is in a good state. At that point, we follow
-        # the redirect and replace the response object with its data.
+        # We need to be careful about resolving futures prematurely. If a
+        # response is a redirect response, resolving the future before the
+        # redirect is processed would result in the consumer seeing an
+        # empty stream of objects, since they'd be consuming our
+        # response.objects() instead of the redirect's response.objects().
+        #
+        # Our strategy is to not resolve/finish the request until either
+        # EOS occurs or until the initial response object is fully received.
 
-        redirect = response._redirect
-        handlefuture = False if redirect else True
-
+        # Always react to eos.
         if meta['eos']:
             response._oninputcomplete()
             del self._requests[frame.requestid]
 
-            if redirect:
-                self._followredirect(frame.requestid, redirect)
-                return
+        # Not EOS but we haven't decoded the initial response object yet.
+        # Return and wait for more data.
+        elif not response._seeninitial:
+            return
 
-        if not handlefuture:
+        # The specification says no objects should follow the initial/redirect
+        # object. So it should be safe to handle the redirect object if one is
+        # decoded, without having to wait for EOS.
+        if response._redirect:
+            self._followredirect(frame.requestid, response._redirect)
             return
 
         # If the command has a decoder, we wait until all input has been
@@ -458,7 +463,10 @@
         self._redirects.append((requestid, res))
 
     def _processredirect(self, rid, res):
-        """Called to continue processing a response from a redirect."""
+        """Called to continue processing a response from a redirect.
+
+        Returns a bool indicating if the redirect is still serviceable.
+        """
         response = self._responses[rid]
 
         try:
@@ -470,7 +478,7 @@
                 response._oninputcomplete()
 
             if rid not in self._futures:
-                return
+                return bool(data)
 
             if response.command not in COMMAND_DECODERS:
                 self._futures[rid].set_result(response.objects())
--- a/tests/test-fuzz-targets.t	Thu Nov 29 09:13:13 2018 +0000
+++ b/tests/test-fuzz-targets.t	Tue Dec 04 17:13:01 2018 -0500
@@ -2,11 +2,36 @@
 
   $ cd $TESTDIR/../contrib/fuzz
 
+which(1) could exit nonzero, but that's fine because we'll still end
+up without a valid executable, so we don't need to check $? here.
+
+  $ if which gmake >/dev/null 2>&1; then
+  >     MAKE=gmake
+  > else
+  >     MAKE=make
+  > fi
+
+  $ havefuzz() {
+  >     cat > $TESTTMP/dummy.cc <<EOF
+  > #include <stdlib.h>
+  > #include <stdint.h>
+  > int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; }
+  > int main(int argc, char **argv) {
+  >     const char data[] = "asdf";
+  >     return LLVMFuzzerTestOneInput((const uint8_t *)data, 4);
+  > }
+  > EOF
+  >     $CXX $TESTTMP/dummy.cc -o $TESTTMP/dummy \
+  >        -fsanitize=fuzzer-no-link,address || return 1
+  > }
+
 #if clang-libfuzzer
-  $ make -s clean all
+  $ CXX=clang++ havefuzz || exit 80
+  $ $MAKE -s clean all
 #endif
 #if no-clang-libfuzzer clang-6.0
-  $ make -s clean all CC=clang-6.0 CXX=clang++-6.0
+  $ CXX=clang++-6.0 havefuzz || exit 80
+  $ $MAKE -s clean all CC=clang-6.0 CXX=clang++-6.0
 #endif
 #if no-clang-libfuzzer no-clang-6.0
   $ exit 80
--- a/tests/test-parseindex.t	Thu Nov 29 09:13:13 2018 +0000
+++ b/tests/test-parseindex.t	Tue Dec 04 17:13:01 2018 -0500
@@ -133,12 +133,18 @@
   $ cd invalidparent
 
   $ hg clone --pull -q --config phases.publish=False ../a limit
+  $ hg clone --pull -q --config phases.publish=False ../a neglimit
   $ hg clone --pull -q --config phases.publish=False ../a segv
-  $ rm -R limit/.hg/cache segv/.hg/cache
+  $ rm -R limit/.hg/cache neglimit/.hg/cache segv/.hg/cache
 
   $ "$PYTHON" <<EOF
   > data = open("limit/.hg/store/00changelog.i", "rb").read()
-  > for n, p in [(b'limit', b'\0\0\0\x02'), (b'segv', b'\0\x01\0\0')]:
+  > poisons = [
+  >     (b'limit', b'\0\0\0\x02'),
+  >     (b'neglimit', b'\xff\xff\xff\xfe'),
+  >     (b'segv', b'\0\x01\0\0'),
+  > ]
+  > for n, p in poisons:
   >     # corrupt p1 at rev0 and p2 at rev1
   >     d = data[:24] + p + data[28:127 + 28] + p + data[127 + 32:]
   >     open(n + b"/.hg/store/00changelog.i", "wb").write(d)
@@ -154,6 +160,11 @@
         0       1        1       -1    base         63         62         63   1.01613        63         0    0.00000
         1       2        1       -1    base         66         65         66   1.01538        66         0    0.00000
 
+  $ hg -R neglimit debugrevlogindex -f1 -c
+     rev flag     size   link     p1     p2       nodeid
+       0 0000       62      0     -2     -1 7c31755bf9b5
+       1 0000       65      1      0     -2 26333235a41c
+
   $ hg -R segv debugrevlogindex -f1 -c
      rev flag     size   link     p1     p2       nodeid
        0 0000       62      0  65536     -1 7c31755bf9b5
@@ -193,6 +204,12 @@
   index_headrevs: parent out of range
   find_gca_candidates: parent out of range
   find_deepest: parent out of range
+  $ "$PYTHON" test.py neglimit/.hg/store
+  reachableroots: parent out of range
+  compute_phases_map_sets: parent out of range
+  index_headrevs: parent out of range
+  find_gca_candidates: parent out of range
+  find_deepest: parent out of range
   $ "$PYTHON" test.py segv/.hg/store
   reachableroots: parent out of range
   compute_phases_map_sets: parent out of range
--- a/tests/test-rebase-inmemory.t	Thu Nov 29 09:13:13 2018 +0000
+++ b/tests/test-rebase-inmemory.t	Tue Dec 04 17:13:01 2018 -0500
@@ -1,5 +1,7 @@
 #require symlink execbit
   $ cat << EOF >> $HGRCPATH
+  > [phases]
+  > publish=False
   > [extensions]
   > amend=
   > rebase=
@@ -54,6 +56,7 @@
   b (no-eol)
   $ hg cat -r 2 c
   c (no-eol)
+  $ cd ..
 
 Case 2:
   $ hg init repo2
@@ -177,7 +180,7 @@
   
   $ hg rebase -r . -d 2
   rebasing 4:daf7dfc139cb "a/a" (tip)
-  saved backup bundle to $TESTTMP/repo1/repo2/.hg/strip-backup/daf7dfc139cb-fdbfcf4f-rebase.hg
+  saved backup bundle to $TESTTMP/repo2/.hg/strip-backup/daf7dfc139cb-fdbfcf4f-rebase.hg
 
   $ hg tglog
   @  4: c6ad37a4f250 'a/a'
@@ -218,7 +221,7 @@
   
   $ hg rebase -r . -d 5
   rebasing 7:855e9797387e "added a back!" (tip)
-  saved backup bundle to $TESTTMP/repo1/repo2/.hg/strip-backup/855e9797387e-81ee4c5d-rebase.hg
+  saved backup bundle to $TESTTMP/repo2/.hg/strip-backup/855e9797387e-81ee4c5d-rebase.hg
 
   $ hg tglog
   @  7: bb3f02be2688 'added a back!'
@@ -237,9 +240,48 @@
   |/
   o  0: b173517d0057 'a'
   
+  $ mkdir c
+  $ echo c > c/c
+  $ hg add c/c
+  $ hg ci -m 'c/c'
+  $ hg rebase -r . -d 3 -n
+  starting dry-run rebase; repository will not be changed
+  rebasing 8:755f0104af9b "c/c" (tip)
+  abort: error: 'c/c' conflicts with file 'c' in 3.
+  [255]
+  $ hg rebase -r 3 -d . -n
+  starting dry-run rebase; repository will not be changed
+  rebasing 3:844a7de3e617 "c"
+  abort: error: file 'c' cannot be written because  'c/' is a folder in 755f0104af9b (containing 1 entries: c/c)
+  [255]
 
   $ cd ..
 
+Test path auditing (issue5818)
+
+  $ mkdir lib_
+  $ ln -s lib_ lib
+  $ hg init repo
+  $ cd repo
+  $ mkdir -p ".$TESTTMP/lib"
+  $ touch ".$TESTTMP/lib/a"
+  $ hg add ".$TESTTMP/lib/a"
+  $ hg ci -m 'a'
+
+  $ touch ".$TESTTMP/lib/b"
+  $ hg add ".$TESTTMP/lib/b"
+  $ hg ci -m 'b'
+
+  $ hg up -q '.^'
+  $ touch ".$TESTTMP/lib/c"
+  $ hg add ".$TESTTMP/lib/c"
+  $ hg ci -m 'c'
+  created new head
+  $ hg rebase -s 1 -d .
+  rebasing 1:* "b" (glob)
+  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/*-rebase.hg (glob)
+  $ cd ..
+
 Test dry-run rebasing
 
   $ hg init repo3
@@ -420,7 +462,6 @@
   transaction abort!
   rollback completed
   hit merge conflicts; re-running rebase without in-memory merge
-  rebase aborted
   rebasing 2:177f92b77385 "c"
   rebasing 3:055a42cdd887 "d"
   rebasing 4:e860deea161a "e"
@@ -428,6 +469,46 @@
   warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
   unresolved conflicts (see hg resolve, then hg rebase --continue)
   [1]
+  $ hg rebase --abort
+  saved backup bundle to $TESTTMP/repo3/.hg/strip-backup/c1e524d4287c-f91f82e1-backup.hg
+  rebase aborted
+
+Retrying without in-memory merge won't lose working copy changes
+  $ cd ..
+  $ hg clone repo3 repo3-dirty -q
+  $ cd repo3-dirty
+  $ echo dirty > a
+  $ hg rebase -s 2 -d 7
+  rebasing 2:177f92b77385 "c"
+  rebasing 3:055a42cdd887 "d"
+  rebasing 4:e860deea161a "e"
+  merging e
+  transaction abort!
+  rollback completed
+  hit merge conflicts; re-running rebase without in-memory merge
+  abort: uncommitted changes
+  [255]
+  $ cat a
+  dirty
+
+Retrying without in-memory merge won't lose merge state
+  $ cd ..
+  $ hg clone repo3 repo3-merge-state -q
+  $ cd repo3-merge-state
+  $ hg merge 4
+  merging e
+  warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
+  2 files updated, 0 files merged, 0 files removed, 1 files unresolved
+  use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
+  [1]
+  $ hg resolve -l
+  U e
+  $ hg rebase -s 2 -d 7
+  rebasing 2:177f92b77385 "c"
+  abort: outstanding merge conflicts
+  [255]
+  $ hg resolve -l
+  U e
 
 ==========================
 Test for --confirm option|