You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2018/11/07 12:30:11 UTC

svn commit: r1846002 [16/44] - in /subversion/branches/ra-git: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ build/generator/util/ build/win32/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contr...

Modified: subversion/branches/ra-git/subversion/libsvn_subr/config_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/config_file.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/config_file.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/config_file.c Wed Nov  7 12:30:06 2018
@@ -25,6 +25,8 @@
 
 #include <apr_lib.h>
 #include <apr_env.h>
+#include <apr_tables.h>
+
 #include "config_impl.h"
 #include "svn_io.h"
 #include "svn_types.h"
@@ -37,8 +39,9 @@
 #include "svn_user.h"
 #include "svn_ctype.h"
 
-#include "svn_private_config.h"
+#include "private/svn_config_private.h"
 #include "private/svn_subr_private.h"
+#include "svn_private_config.h"
 
 #ifdef __HAIKU__
 #  include <FindDirectory.h>
@@ -52,8 +55,9 @@
 /* File parsing context */
 typedef struct parse_context_t
 {
-  /* This config struct */
-  svn_config_t *cfg;
+  /* The configuration constructor. */
+  svn_config__constructor_t *constructor;
+  void *constructor_baton;
 
   /* The stream struct */
   svn_stream_t *stream;
@@ -64,10 +68,14 @@ typedef struct parse_context_t
   /* Emulate an ungetc */
   int ungotten_char;
 
+  /* We're currently parsing a section */
+  svn_boolean_t in_section;
+
   /* Temporary strings */
   svn_stringbuf_t *section;
   svn_stringbuf_t *option;
   svn_stringbuf_t *value;
+  svn_stringbuf_t *line_read;
 
   /* Parser buffer for getc() to avoid call overhead into several libraries
      for every character */
@@ -80,6 +88,109 @@ typedef struct parse_context_t
 } parse_context_t;
 
 
+/* Config representation constructor */
+struct svn_config__constructor_t
+{
+  /* Constructor callbacks; see docs for svn_config__constructor_create. */
+  svn_config__open_section_fn open_section;
+  svn_config__close_section_fn close_section;
+  svn_config__add_value_fn add_value;
+};
+
+svn_config__constructor_t *
+svn_config__constructor_create(
+    svn_config__open_section_fn open_section_callback,
+    svn_config__close_section_fn close_section_callback,
+    svn_config__add_value_fn add_value_callback,
+    apr_pool_t *result_pool)
+{
+  svn_config__constructor_t *ctor = apr_palloc(result_pool, sizeof(*ctor));
+  ctor->open_section = open_section_callback;
+  ctor->close_section = close_section_callback;
+  ctor->add_value = add_value_callback;
+  return ctor;
+}
+
+/* Called after we've parsed a section name and before we start
+   parsing any options within that section. */
+static APR_INLINE svn_error_t *
+open_section(parse_context_t *ctx, svn_boolean_t *stop)
+{
+  if (ctx->constructor->open_section)
+    {
+      svn_error_t *err = ctx->constructor->open_section(
+          ctx->constructor_baton, ctx->section);
+      if (err)
+        {
+          if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
+            {
+              *stop = TRUE;
+              svn_error_clear(err);
+              return SVN_NO_ERROR;
+            }
+          else
+            return svn_error_trace(err);
+        }
+    }
+
+  *stop = FALSE;
+  ctx->in_section = TRUE;
+  return SVN_NO_ERROR;
+}
+
+/* Called after we've parsed all options within a section and before
+   we start parsing the next section. */
+static APR_INLINE svn_error_t *
+close_section(parse_context_t *ctx, svn_boolean_t *stop)
+{
+  ctx->in_section = FALSE;
+  if (ctx->constructor->close_section)
+    {
+      svn_error_t *err = ctx->constructor->close_section(
+          ctx->constructor_baton, ctx->section);
+      if (err)
+        {
+          if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
+            {
+              *stop = TRUE;
+              svn_error_clear(err);
+              return SVN_NO_ERROR;
+            }
+          else
+            return svn_error_trace(err);
+        }
+    }
+
+  *stop = FALSE;
+  return SVN_NO_ERROR;
+}
+
+/* Called every tyme we've parsed a complete (option, value) pair. */
+static APR_INLINE svn_error_t *
+add_value(parse_context_t *ctx, svn_boolean_t *stop)
+{
+  if (ctx->constructor->add_value)
+    {
+      svn_error_t *err =  ctx->constructor->add_value(
+          ctx->constructor_baton, ctx->section,
+          ctx->option, ctx->value);
+      if (err)
+        {
+          if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
+            {
+              *stop = TRUE;
+              svn_error_clear(err);
+              return SVN_NO_ERROR;
+            }
+          else
+            return svn_error_trace(err);
+        }
+    }
+
+  *stop = FALSE;
+  return SVN_NO_ERROR;
+}
+
 
 /* Emulate getc() because streams don't support it.
  *
@@ -161,6 +272,57 @@ parser_ungetc(parse_context_t *ctx, int
   return SVN_NO_ERROR;
 }
 
+/* Read from CTX to the end of the line (or file) and write the data
+   into LINE.  Previous contents of *LINE will be overwritten.
+   Returns the char that ended the line in *C; the char is either
+   EOF or newline. */
+static svn_error_t *
+parser_get_line(parse_context_t *ctx, svn_stringbuf_t *line, int *c)
+{
+  int ch;
+  svn_stringbuf_setempty(line);
+
+  /* Loop until EOF of newline. There is one extra iteration per
+   * parser buffer refill.  The initial parser_getc() also ensures
+   * that the unget buffer is emptied. */
+  SVN_ERR(parser_getc(ctx, &ch));
+  while (ch != '\n' && ch != EOF)
+    {
+      const char *start, *newline;
+
+      /* Save the char we just read. */
+      svn_stringbuf_appendbyte(line, ch);
+
+      /* Scan the parser buffer for NL. */
+      start = ctx->parser_buffer + ctx->buffer_pos;
+      newline = memchr(start, '\n', ctx->buffer_size - ctx->buffer_pos);
+      if (newline)
+        {
+          /* NL found before the end of the of the buffer. */
+          svn_stringbuf_appendbytes(line, start, newline - start);
+          ch = '\n';
+          ctx->buffer_pos = newline - ctx->parser_buffer + 1;
+          break;
+        }
+      else
+        {
+          /* Hit the end of the buffer.  This may be either due to
+           * buffer size or EOF.  In any case, all (remaining) current
+           * buffer data is part of the line to read. */
+          const char *end = ctx->parser_buffer + ctx->buffer_size;
+          ctx->buffer_pos = ctx->buffer_size;
+
+          svn_stringbuf_appendbytes(line, start, end - start);
+        }
+
+      /* refill buffer, check for EOF */
+      SVN_ERR(parser_getc_plain(ctx, &ch));
+    }
+
+  *c = ch;
+  return SVN_NO_ERROR;
+}
+
 /* Eat chars from STREAM until encounter non-whitespace, newline, or EOF.
    Set *PCOUNT to the number of characters eaten, not counting the
    last one, and return the last char read (the one that caused the
@@ -245,23 +407,16 @@ skip_bom(parse_context_t *ctx)
   return SVN_NO_ERROR;
 }
 
-/* Parse a single option value */
+/* Parse continuation lines of single option value if they exist.  Append
+ * their contents, including *PCH, to CTX->VALUE.  Return the first char
+ * of the next line or EOF in *PCH. */
 static svn_error_t *
-parse_value(int *pch, parse_context_t *ctx)
+parse_value_continuation_lines(int *pch, parse_context_t *ctx)
 {
   svn_boolean_t end_of_val = FALSE;
-  int ch;
+  svn_boolean_t stop;
+  int ch = *pch;
 
-  /* Read the first line of the value */
-  svn_stringbuf_setempty(ctx->value);
-  SVN_ERR(parser_getc(ctx, &ch));
-  while (ch != EOF && ch != '\n')
-    /* last ch seen was ':' or '=' in parse_option. */
-    {
-      const char char_from_int = (char)ch;
-      svn_stringbuf_appendbyte(ctx->value, char_from_int);
-      SVN_ERR(parser_getc(ctx, &ch));
-    }
   /* Leading and trailing whitespace is ignored. */
   svn_stringbuf_strip_whitespace(ctx->value);
 
@@ -273,8 +428,9 @@ parse_value(int *pch, parse_context_t *c
         {
           /* At end of file. The value is complete, there can't be
              any continuation lines. */
-          svn_config_set(ctx->cfg, ctx->section->data,
-                         ctx->option->data, ctx->value->data);
+          SVN_ERR(add_value(ctx, &stop));
+          if (stop)
+            return SVN_NO_ERROR;
           break;
         }
       else
@@ -310,16 +466,15 @@ parse_value(int *pch, parse_context_t *c
               else
                 {
                   /* This is a continuation line. Read it. */
-                  svn_stringbuf_appendbyte(ctx->value, ' ');
+                  SVN_ERR(parser_ungetc(ctx, ch));
+                  SVN_ERR(parser_get_line(ctx, ctx->line_read, &ch));
 
-                  while (ch != EOF && ch != '\n')
-                    {
-                      const char char_from_int = (char)ch;
-                      svn_stringbuf_appendbyte(ctx->value, char_from_int);
-                      SVN_ERR(parser_getc(ctx, &ch));
-                    }
                   /* Trailing whitespace is ignored. */
-                  svn_stringbuf_strip_whitespace(ctx->value);
+                  svn_stringbuf_strip_whitespace(ctx->line_read);
+
+                  svn_stringbuf_appendbyte(ctx->value, ' ');
+                  svn_stringbuf_appendbytes(ctx->value, ctx->line_read->data,
+                                            ctx->line_read->len);
                 }
             }
         }
@@ -336,17 +491,33 @@ parse_option(int *pch, parse_context_t *
 {
   svn_error_t *err = SVN_NO_ERROR;
   int ch;
+  char *equals, *separator;
+
+  /* Read the first line. */
+  parser_ungetc(ctx, *pch); /* Yes, the first char is relevant. */
+  SVN_ERR(parser_get_line(ctx, ctx->line_read, &ch));
+
+  /* Extract the option name from it. */
+  equals = strchr(ctx->line_read->data, '=');
+  if (equals)
+    {
+      /* Efficiently look for a colon separator prior to the equals sign.
+       * Since there is no strnchr, we limit the search by temporarily
+       * limiting the string. */
+      char *colon;
+      *equals = 0;
+      colon = strchr(ctx->line_read->data, ':');
+      *equals = '=';
 
-  svn_stringbuf_setempty(ctx->option);
-  ch = *pch;   /* Yes, the first char is relevant. */
-  while (ch != EOF && ch != ':' && ch != '=' && ch != '\n')
+      separator = colon ? colon : equals;
+    }
+  else
     {
-      const char char_from_int = (char)ch;
-      svn_stringbuf_appendbyte(ctx->option, char_from_int);
-      SVN_ERR(parser_getc(ctx, &ch));
+      /* No '=' separator so far.  Look for colon. */
+      separator = strchr(ctx->line_read->data, ':');
     }
 
-  if (ch != ':' && ch != '=')
+  if (!separator)
     {
       ch = EOF;
       err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
@@ -356,8 +527,22 @@ parse_option(int *pch, parse_context_t *
   else
     {
       /* Whitespace around the name separator is ignored. */
-      svn_stringbuf_strip_whitespace(ctx->option);
-      err = parse_value(&ch, ctx);
+      const char *end = separator;
+      while (svn_ctype_isspace(*--end))
+        ;
+      while (svn_ctype_isspace(*++separator))
+        ;
+
+      /* Extract the option.  It can't contain whitespace chars. */
+      svn_stringbuf_setempty(ctx->option);
+      svn_stringbuf_appendbytes(ctx->option, ctx->line_read->data,
+                                end + 1 - ctx->line_read->data);
+
+      /* Extract the first line of the value. */
+      end = ctx->line_read->data + ctx->line_read->len;
+      svn_stringbuf_setempty(ctx->value);
+      svn_stringbuf_appendbytes(ctx->value, separator, end - separator);
+      err = parse_value_continuation_lines(&ch, ctx);
     }
 
   *pch = ch;
@@ -379,17 +564,12 @@ parse_section_name(int *pch, parse_conte
 {
   svn_error_t *err = SVN_NO_ERROR;
   int ch;
+  char *terminal;
 
-  svn_stringbuf_setempty(ctx->section);
-  SVN_ERR(parser_getc(ctx, &ch));
-  while (ch != EOF && ch != ']' && ch != '\n')
-    {
-      const char char_from_int = (char)ch;
-      svn_stringbuf_appendbyte(ctx->section, char_from_int);
-      SVN_ERR(parser_getc(ctx, &ch));
-    }
+  SVN_ERR(parser_get_line(ctx, ctx->section, &ch));
+  terminal = strchr(ctx->section->data, ']');
 
-  if (ch != ']')
+  if (!terminal)
     {
       ch = EOF;
       err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
@@ -399,9 +579,8 @@ parse_section_name(int *pch, parse_conte
   else
     {
       /* Everything from the ']' to the end of the line is ignored. */
-      SVN_ERR(skip_to_eoln(ctx, &ch));
-      if (ch != EOF)
-        ++ctx->line;
+      *terminal = 0;
+      ctx->section->len = terminal - ctx->section->data;
     }
 
   *pch = ch;
@@ -531,6 +710,7 @@ svn_config__shallow_replace_section(svn_
 }
 
 
+
 svn_error_t *
 svn_config__parse_file(svn_config_t *cfg, const char *file,
                        svn_boolean_t must_exist, apr_pool_t *result_pool)
@@ -554,7 +734,12 @@ svn_config__parse_file(svn_config_t *cfg
     SVN_ERR(err);
 
   stream = svn_stream_from_aprfile2(apr_file, FALSE, scratch_pool);
-  err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
+  err = svn_config__parse_stream(stream,
+                                 svn_config__constructor_create(
+                                     NULL, NULL,
+                                     svn_config__default_add_value_fn,
+                                     scratch_pool),
+                                 cfg, scratch_pool);
 
   if (err != SVN_NO_ERROR)
     {
@@ -571,21 +756,27 @@ svn_config__parse_file(svn_config_t *cfg
 }
 
 svn_error_t *
-svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream,
-                         apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+svn_config__parse_stream(svn_stream_t *stream,
+                         svn_config__constructor_t *constructor,
+                         void *constructor_baton,
+                         apr_pool_t *scratch_pool)
 {
   parse_context_t *ctx;
+  svn_boolean_t stop;
   int ch, count;
 
   ctx = apr_palloc(scratch_pool, sizeof(*ctx));
 
-  ctx->cfg = cfg;
+  ctx->constructor = constructor;
+  ctx->constructor_baton = constructor_baton;
   ctx->stream = stream;
   ctx->line = 1;
   ctx->ungotten_char = EOF;
+  ctx->in_section = FALSE;
   ctx->section = svn_stringbuf_create_empty(scratch_pool);
   ctx->option = svn_stringbuf_create_empty(scratch_pool);
   ctx->value = svn_stringbuf_create_empty(scratch_pool);
+  ctx->line_read = svn_stringbuf_create_empty(scratch_pool);
   ctx->buffer_pos = 0;
   ctx->buffer_size = 0;
   ctx->hit_stream_eof = FALSE;
@@ -599,13 +790,23 @@ svn_config__parse_stream(svn_config_t *c
       switch (ch)
         {
         case '[':               /* Start of section header */
-          if (count == 0)
-            SVN_ERR(parse_section_name(&ch, ctx, scratch_pool));
-          else
+          if (count != 0)
             return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
                                      _("line %d: Section header"
                                        " must start in the first column"),
                                      ctx->line);
+
+          /* Close the previous section before starting a new one. */
+          if (ctx->in_section)
+            {
+              SVN_ERR(close_section(ctx, &stop));
+              if (stop)
+                return SVN_NO_ERROR;
+            }
+          SVN_ERR(parse_section_name(&ch, ctx, scratch_pool));
+          SVN_ERR(open_section(ctx, &stop));
+          if (stop)
+            return SVN_NO_ERROR;
           break;
 
         case '#':               /* Comment */
@@ -644,6 +845,9 @@ svn_config__parse_stream(svn_config_t *c
     }
   while (ch != EOF);
 
+  /* Emit the last close-section call to wrap up. */
+  if (ctx->in_section)
+    SVN_ERR(close_section(ctx, &stop));
   return SVN_NO_ERROR;
 }
 
@@ -945,11 +1149,13 @@ svn_config_ensure(const char *config_dir
         "###   http-timeout               Timeout for HTTP requests in seconds"
                                                                              NL
         "###   http-compression           Whether to compress HTTP requests" NL
+        "###                              (yes/no/auto)."                    NL
         "###   http-max-connections       Maximum number of parallel server" NL
         "###                              connections to use for any given"  NL
         "###                              HTTP operation."                   NL
         "###   http-chunked-requests      Whether to use chunked transfer"   NL
         "###                              encoding for HTTP requests body."  NL
+        "###   http-auth-types            List of HTTP authentication types."NL
         "###   ssl-authority-files        List of files, each of a trusted CA"
                                                                              NL
         "###   ssl-trust-default-ca       Trust the system 'default' CAs"    NL
@@ -1104,7 +1310,7 @@ svn_config_ensure(const char *config_dir
         "# http-proxy-port = 7000"                                           NL
         "# http-proxy-username = defaultusername"                            NL
         "# http-proxy-password = defaultpassword"                            NL
-        "# http-compression = no"                                            NL
+        "# http-compression = auto"                                          NL
         "# No http-timeout, so just use the builtin default."                NL
         "# ssl-authority-files = /path/to/CAcert.pem;/path/to/CAcert2.pem"   NL
         "#"                                                                  NL
@@ -1244,12 +1450,12 @@ svn_config_ensure(const char *config_dir
         "### passed to the tunnel agent as <user>@<hostname>.)  If the"      NL
         "### built-in ssh scheme were not predefined, it could be defined"   NL
         "### as:"                                                            NL
-        "# ssh = $SVN_SSH ssh -q"                                            NL
+        "# ssh = $SVN_SSH ssh -q --"                                         NL
         "### If you wanted to define a new 'rsh' scheme, to be used with"    NL
         "### 'svn+rsh:' URLs, you could do so as follows:"                   NL
-        "# rsh = rsh"                                                        NL
+        "# rsh = rsh --"                                                     NL
         "### Or, if you wanted to specify a full path and arguments:"        NL
-        "# rsh = /path/to/rsh -l myusername"                                 NL
+        "# rsh = /path/to/rsh -l myusername --"                              NL
         "### On Windows, if you are specifying a full path to a command,"    NL
         "### use a forward slash (/) or a paired backslash (\\\\) as the"    NL
         "### path separator.  A single backslash will be treated as an"      NL

Modified: subversion/branches/ra-git/subversion/libsvn_subr/config_impl.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/config_impl.h?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/config_impl.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/config_impl.h Wed Nov  7 12:30:06 2018
@@ -76,18 +76,18 @@ struct svn_config_t
   svn_boolean_t read_only;
 };
 
+/* The default add-value constructor callback, used by the default
+   config parser that populates an svn_config_t. */
+svn_error_t *svn_config__default_add_value_fn(
+    void *baton, svn_stringbuf_t *section,
+    svn_stringbuf_t *option, svn_stringbuf_t *value);
+
 /* Read sections and options from a file. */
 svn_error_t *svn_config__parse_file(svn_config_t *cfg,
                                     const char *file,
                                     svn_boolean_t must_exist,
                                     apr_pool_t *pool);
 
-/* Read sections and options from a stream. */
-svn_error_t *svn_config__parse_stream(svn_config_t *cfg,
-                                      svn_stream_t *stream,
-                                      apr_pool_t *result_pool,
-                                      apr_pool_t *scratch_pool);
-
 /* The name of the magic [DEFAULT] section. */
 #define SVN_CONFIG__DEFAULT_SECTION "DEFAULT"
 

Modified: subversion/branches/ra-git/subversion/libsvn_subr/config_win.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/config_win.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/config_win.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/config_win.c Wed Nov  7 12:30:06 2018
@@ -137,7 +137,7 @@ parse_section(svn_config_t *cfg, HKEY hk
                                 _("Can't enumerate registry values"));
 
       /* Ignore option names that start with '#', see
-         http://subversion.tigris.org/issues/show_bug.cgi?id=671 */
+         https://issues.apache.org/jira/browse/SVN-671 */
       if (type == REG_SZ && option->data[0] != '#')
         {
           DWORD value_len = (DWORD)value->blocksize;
@@ -273,4 +273,11 @@ svn_config__parse_registry(svn_config_t
   return svn_err;
 }
 
+#else  /* !WIN32 */
+
+/* Silence OSX ranlib warnings about object files with no symbols. */
+#include <apr.h>
+extern const apr_uint32_t svn__fake__config_win;
+const apr_uint32_t svn__fake__config_win = 0xdeadbeef;
+
 #endif /* WIN32 */

Modified: subversion/branches/ra-git/subversion/libsvn_subr/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/deprecated.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/deprecated.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/deprecated.c Wed Nov  7 12:30:06 2018
@@ -390,6 +390,30 @@ print_command_info(const svn_opt_subcomm
   return SVN_NO_ERROR;
 }
 
+const svn_opt_subcommand_desc2_t *
+svn_opt_get_canonical_subcommand2(const svn_opt_subcommand_desc2_t *table,
+                                  const char *cmd_name)
+{
+  int i = 0;
+
+  if (cmd_name == NULL)
+    return NULL;
+
+  while (table[i].name) {
+    int j;
+    if (strcmp(cmd_name, table[i].name) == 0)
+      return table + i;
+    for (j = 0; (j < SVN_OPT_MAX_ALIASES) && table[i].aliases[j]; j++)
+      if (strcmp(cmd_name, table[i].aliases[j]) == 0)
+        return table + i;
+
+    i++;
+  }
+
+  /* If we get here, there was no matching subcommand name or alias. */
+  return NULL;
+}
+
 const svn_opt_subcommand_desc_t *
 svn_opt_get_canonical_subcommand(const svn_opt_subcommand_desc_t *table,
                                  const char *cmd_name)
@@ -414,6 +438,344 @@ svn_opt_get_canonical_subcommand(const s
   return NULL;
 }
 
+const apr_getopt_option_t *
+svn_opt_get_option_from_code2(int code,
+                              const apr_getopt_option_t *option_table,
+                              const svn_opt_subcommand_desc2_t *command,
+                              apr_pool_t *pool)
+{
+  apr_size_t i;
+
+  for (i = 0; option_table[i].optch; i++)
+    if (option_table[i].optch == code)
+      {
+        if (command)
+          {
+            int j;
+
+            for (j = 0; ((j < SVN_OPT_MAX_OPTIONS) &&
+                         command->desc_overrides[j].optch); j++)
+              if (command->desc_overrides[j].optch == code)
+                {
+                  apr_getopt_option_t *tmpopt =
+                      apr_palloc(pool, sizeof(*tmpopt));
+                  *tmpopt = option_table[i];
+                  tmpopt->description = command->desc_overrides[j].desc;
+                  return tmpopt;
+                }
+          }
+        return &(option_table[i]);
+      }
+
+  return NULL;
+}
+
+const apr_getopt_option_t *
+svn_opt_get_option_from_code(int code,
+                             const apr_getopt_option_t *option_table)
+{
+  apr_size_t i;
+
+  for (i = 0; option_table[i].optch; i++)
+    if (option_table[i].optch == code)
+      return &(option_table[i]);
+
+  return NULL;
+}
+
+/* Like svn_opt_get_option_from_code2(), but also, if CODE appears a second
+ * time in OPTION_TABLE with a different name, then set *LONG_ALIAS to that
+ * second name, else set it to NULL. */
+static const apr_getopt_option_t *
+get_option_from_code(const char **long_alias,
+                     int code,
+                     const apr_getopt_option_t *option_table,
+                     const svn_opt_subcommand_desc2_t *command,
+                     apr_pool_t *pool)
+{
+  const apr_getopt_option_t *i;
+  const apr_getopt_option_t *opt
+    = svn_opt_get_option_from_code2(code, option_table, command, pool);
+
+  /* Find a long alias in the table, if there is one. */
+  *long_alias = NULL;
+  for (i = option_table; i->optch; i++)
+    {
+      if (i->optch == code && i->name != opt->name)
+        {
+          *long_alias = i->name;
+          break;
+        }
+    }
+
+  return opt;
+}
+
+/* Print an option OPT nicely into a STRING allocated in POOL.
+ * If OPT has a single-character short form, then print OPT->name (if not
+ * NULL) as an alias, else print LONG_ALIAS (if not NULL) as an alias.
+ * If DOC is set, include the generic documentation string of OPT,
+ * localized to the current locale if a translation is available.
+ */
+static void
+format_option(const char **string,
+              const apr_getopt_option_t *opt,
+              const char *long_alias,
+              svn_boolean_t doc,
+              apr_pool_t *pool)
+{
+  char *opts;
+
+  if (opt == NULL)
+    {
+      *string = "?";
+      return;
+    }
+
+  /* We have a valid option which may or may not have a "short
+     name" (a single-character alias for the long option). */
+  if (opt->optch <= 255)
+    opts = apr_psprintf(pool, "-%c [--%s]", opt->optch, opt->name);
+  else if (long_alias)
+    opts = apr_psprintf(pool, "--%s [--%s]", opt->name, long_alias);
+  else
+    opts = apr_psprintf(pool, "--%s", opt->name);
+
+  if (opt->has_arg)
+    opts = apr_pstrcat(pool, opts, _(" ARG"), SVN_VA_NULL);
+
+  if (doc)
+    opts = apr_psprintf(pool, "%-24s : %s", opts, _(opt->description));
+
+  *string = opts;
+}
+
+/* Print the canonical command name for CMD, and all its aliases, to
+   STREAM.  If HELP is set, print CMD's help string too, in which case
+   obtain option usage from OPTIONS_TABLE. */
+static svn_error_t *
+print_command_info2(const svn_opt_subcommand_desc2_t *cmd,
+                    const apr_getopt_option_t *options_table,
+                    const int *global_options,
+                    svn_boolean_t help,
+                    apr_pool_t *pool,
+                    FILE *stream)
+{
+  svn_boolean_t first_time;
+  apr_size_t i;
+
+  /* Print the canonical command name. */
+  SVN_ERR(svn_cmdline_fputs(cmd->name, stream, pool));
+
+  /* Print the list of aliases. */
+  first_time = TRUE;
+  for (i = 0; i < SVN_OPT_MAX_ALIASES; i++)
+    {
+      if (cmd->aliases[i] == NULL)
+        break;
+
+      if (first_time) {
+        SVN_ERR(svn_cmdline_fputs(" (", stream, pool));
+        first_time = FALSE;
+      }
+      else
+        SVN_ERR(svn_cmdline_fputs(", ", stream, pool));
+
+      SVN_ERR(svn_cmdline_fputs(cmd->aliases[i], stream, pool));
+    }
+
+  if (! first_time)
+    SVN_ERR(svn_cmdline_fputs(")", stream, pool));
+
+  if (help)
+    {
+      const apr_getopt_option_t *option;
+      const char *long_alias;
+      svn_boolean_t have_options = FALSE;
+
+      SVN_ERR(svn_cmdline_fprintf(stream, pool, ": %s", _(cmd->help)));
+
+      /* Loop over all valid option codes attached to the subcommand */
+      for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
+        {
+          if (cmd->valid_options[i])
+            {
+              if (!have_options)
+                {
+                  SVN_ERR(svn_cmdline_fputs(_("\nValid options:\n"),
+                                            stream, pool));
+                  have_options = TRUE;
+                }
+
+              /* convert each option code into an option */
+              option = get_option_from_code(&long_alias, cmd->valid_options[i],
+                                            options_table, cmd, pool);
+
+              /* print the option's docstring */
+              if (option && option->description)
+                {
+                  const char *optstr;
+                  format_option(&optstr, option, long_alias, TRUE, pool);
+                  SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
+                                              optstr));
+                }
+            }
+        }
+      /* And global options too */
+      if (global_options && *global_options)
+        {
+          SVN_ERR(svn_cmdline_fputs(_("\nGlobal options:\n"),
+                                    stream, pool));
+          have_options = TRUE;
+
+          for (i = 0; global_options[i]; i++)
+            {
+
+              /* convert each option code into an option */
+              option = get_option_from_code(&long_alias, global_options[i],
+                                            options_table, cmd, pool);
+
+              /* print the option's docstring */
+              if (option && option->description)
+                {
+                  const char *optstr;
+                  format_option(&optstr, option, long_alias, TRUE, pool);
+                  SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
+                                              optstr));
+                }
+            }
+        }
+
+      if (have_options)
+        SVN_ERR(svn_cmdline_fprintf(stream, pool, "\n"));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* The body for svn_opt_print_generic_help2() function with standard error
+ * handling semantic. Handling of errors implemented at caller side. */
+static svn_error_t *
+print_generic_help_body(const char *header,
+                        const svn_opt_subcommand_desc2_t *cmd_table,
+                        const apr_getopt_option_t *opt_table,
+                        const char *footer,
+                        apr_pool_t *pool, FILE *stream)
+{
+  int i = 0;
+
+  if (header)
+    SVN_ERR(svn_cmdline_fputs(header, stream, pool));
+
+  while (cmd_table[i].name)
+    {
+      SVN_ERR(svn_cmdline_fputs("   ", stream, pool));
+      SVN_ERR(print_command_info2(cmd_table + i, opt_table,
+                                  NULL, FALSE,
+                                  pool, stream));
+      SVN_ERR(svn_cmdline_fputs("\n", stream, pool));
+      i++;
+    }
+
+  SVN_ERR(svn_cmdline_fputs("\n", stream, pool));
+
+  if (footer)
+    SVN_ERR(svn_cmdline_fputs(footer, stream, pool));
+
+  return SVN_NO_ERROR;
+}
+
+void
+svn_opt_print_generic_help2(const char *header,
+                            const svn_opt_subcommand_desc2_t *cmd_table,
+                            const apr_getopt_option_t *opt_table,
+                            const char *footer,
+                            apr_pool_t *pool, FILE *stream)
+{
+  svn_error_t *err;
+
+  err = print_generic_help_body(header, cmd_table, opt_table, footer, pool,
+                                stream);
+
+  /* Issue #3014:
+   * Don't print anything on broken pipes. The pipe was likely
+   * closed by the process at the other end. We expect that
+   * process to perform error reporting as necessary.
+   *
+   * ### This assumes that there is only one error in a chain for
+   * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
+  if (err && err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+    svn_handle_error2(err, stderr, FALSE, "svn: ");
+  svn_error_clear(err);
+}
+
+svn_boolean_t
+svn_opt_subcommand_takes_option3(const svn_opt_subcommand_desc2_t *command,
+                                 int option_code,
+                                 const int *global_options)
+{
+  apr_size_t i;
+
+  for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
+    if (command->valid_options[i] == option_code)
+      return TRUE;
+
+  if (global_options)
+    for (i = 0; global_options[i]; i++)
+      if (global_options[i] == option_code)
+        return TRUE;
+
+  return FALSE;
+}
+
+svn_boolean_t
+svn_opt_subcommand_takes_option2(const svn_opt_subcommand_desc2_t *command,
+                                 int option_code)
+{
+  return svn_opt_subcommand_takes_option3(command,
+                                          option_code,
+                                          NULL);
+}
+
+svn_boolean_t
+svn_opt_subcommand_takes_option(const svn_opt_subcommand_desc_t *command,
+                                int option_code)
+{
+  apr_size_t i;
+
+  for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
+    if (command->valid_options[i] == option_code)
+      return TRUE;
+
+  return FALSE;
+}
+
+void
+svn_opt_subcommand_help3(const char *subcommand,
+                         const svn_opt_subcommand_desc2_t *table,
+                         const apr_getopt_option_t *options_table,
+                         const int *global_options,
+                         apr_pool_t *pool)
+{
+  const svn_opt_subcommand_desc2_t *cmd =
+    svn_opt_get_canonical_subcommand2(table, subcommand);
+  svn_error_t *err;
+
+  if (cmd)
+    err = print_command_info2(cmd, options_table, global_options,
+                              TRUE, pool, stdout);
+  else
+    err = svn_cmdline_fprintf(stderr, pool,
+                              _("\"%s\": unknown command.\n\n"), subcommand);
+
+  if (err) {
+    /* Issue #3014: Don't print anything on broken pipes. */
+    if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
+      svn_handle_error2(err, stderr, FALSE, "svn: ");
+    svn_error_clear(err);
+  }
+}
+
 void
 svn_opt_subcommand_help2(const char *subcommand,
                          const svn_opt_subcommand_desc2_t *table,
@@ -522,6 +884,56 @@ svn_opt_args_to_target_array(apr_array_h
 }
 
 svn_error_t *
+svn_opt_print_help4(apr_getopt_t *os,
+                    const char *pgm_name,
+                    svn_boolean_t print_version,
+                    svn_boolean_t quiet,
+                    svn_boolean_t verbose,
+                    const char *version_footer,
+                    const char *header,
+                    const svn_opt_subcommand_desc2_t *cmd_table,
+                    const apr_getopt_option_t *option_table,
+                    const int *global_options,
+                    const char *footer,
+                    apr_pool_t *pool)
+{
+  apr_array_header_t *targets = NULL;
+
+  if (os)
+    SVN_ERR(svn_opt_parse_all_args(&targets, os, pool));
+
+  if (os && targets->nelts)  /* help on subcommand(s) requested */
+    {
+      int i;
+
+      for (i = 0; i < targets->nelts; i++)
+        {
+          svn_opt_subcommand_help3(APR_ARRAY_IDX(targets, i, const char *),
+                                   cmd_table, option_table,
+                                   global_options, pool);
+        }
+    }
+  else if (print_version)   /* just --version */
+    {
+      SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer,
+                                          svn_version_extended(verbose, pool),
+                                          quiet, verbose, pool));
+    }
+  else if (os && !targets->nelts)            /* `-h', `--help', or `help' */
+    svn_opt_print_generic_help2(header,
+                                cmd_table,
+                                option_table,
+                                footer,
+                                pool,
+                                stdout);
+  else                                       /* unknown option or cmd */
+    SVN_ERR(svn_cmdline_fprintf(stderr, pool,
+                                _("Type '%s help' for usage.\n"), pgm_name));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_opt_print_help3(apr_getopt_t *os,
                     const char *pgm_name,
                     svn_boolean_t print_version,
@@ -1288,7 +1700,7 @@ svn_rangelist_merge(svn_rangelist_t **ra
                                pool, pool));
 
   return svn_error_trace(
-            svn_rangelist__combine_adjacent_ranges(*rangelist, pool));
+            svn_rangelist__canonicalize(*rangelist, pool));
 }
 
 svn_error_t *
@@ -1592,3 +2004,12 @@ svn_base64_encode(svn_stream_t *output,
 {
   return svn_base64_encode2(output, TRUE, pool);
 }
+
+/*** From string.c ***/
+char *
+svn_cstring_join(const apr_array_header_t *strings,
+                 const char *separator,
+                 apr_pool_t *pool)
+{
+  return svn_cstring_join2(strings, separator, TRUE, pool);
+}

Modified: subversion/branches/ra-git/subversion/libsvn_subr/dirent_uri.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/dirent_uri.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/dirent_uri.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/dirent_uri.c Wed Nov  7 12:30:06 2018
@@ -1862,18 +1862,18 @@ svn_uri_is_canonical(const char *uri, ap
           ptr++;
         }
 
-      if (ptr == schema_data)
+      if (ptr == schema_data && (*ptr == '/' || *ptr == '\0'))
         return FALSE; /* Fail on "http://host:" */
 
-      if (*ptr && *ptr != '/')
-        return FALSE; /* Not a port number */
-
       if (port == 80 && strncmp(uri, "http:", 5) == 0)
         return FALSE;
       else if (port == 443 && strncmp(uri, "https:", 6) == 0)
         return FALSE;
       else if (port == 3690 && strncmp(uri, "svn:", 4) == 0)
         return FALSE;
+
+      while (*ptr && *ptr != '/')
+        ++ptr; /* Allow "http://host:stuff" */
     }
 
   schema_data = ptr;

Modified: subversion/branches/ra-git/subversion/libsvn_subr/fnv1a.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/fnv1a.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/fnv1a.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/fnv1a.c Wed Nov  7 12:30:06 2018
@@ -166,6 +166,12 @@ svn_fnv1a_32__context_create(apr_pool_t
 }
 
 void
+svn_fnv1a_32__context_reset(svn_fnv1a_32__context_t *context)
+{
+  context->hash = FNV1_BASE_32;
+}
+
+void
 svn_fnv1a_32__update(svn_fnv1a_32__context_t *context,
                      const void *data,
                      apr_size_t len)
@@ -203,6 +209,17 @@ svn_fnv1a_32x4__context_create(apr_pool_
 }
 
 void
+svn_fnv1a_32x4__context_reset(svn_fnv1a_32x4__context_t *context)
+{
+  context->hashes[0] = FNV1_BASE_32;
+  context->hashes[1] = FNV1_BASE_32;
+  context->hashes[2] = FNV1_BASE_32;
+  context->hashes[3] = FNV1_BASE_32;
+
+  context->buffered = 0;
+}
+
+void
 svn_fnv1a_32x4__update(svn_fnv1a_32x4__context_t *context,
                        const void *data,
                        apr_size_t len)

Modified: subversion/branches/ra-git/subversion/libsvn_subr/fnv1a.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/fnv1a.h?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/fnv1a.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/fnv1a.h Wed Nov  7 12:30:06 2018
@@ -41,6 +41,11 @@ typedef struct svn_fnv1a_32__context_t s
 svn_fnv1a_32__context_t *
 svn_fnv1a_32__context_create(apr_pool_t *pool);
 
+/* Reset the FNV-1a checksum CONTEXT to initial state.
+ */
+void
+svn_fnv1a_32__context_reset(svn_fnv1a_32__context_t *context);
+
 /* Feed LEN bytes from DATA into the FNV-1a checksum creation CONTEXT.
  */
 void
@@ -63,6 +68,11 @@ typedef struct svn_fnv1a_32x4__context_t
 svn_fnv1a_32x4__context_t *
 svn_fnv1a_32x4__context_create(apr_pool_t *pool);
 
+/* Reset the modified FNV-1a checksum CONTEXT to initial state.
+ */
+void
+svn_fnv1a_32x4__context_reset(svn_fnv1a_32x4__context_t *context);
+
 /* Feed LEN bytes from DATA into the modified FNV-1a checksum creation
  * CONTEXT.
  */

Modified: subversion/branches/ra-git/subversion/libsvn_subr/gpg_agent.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/gpg_agent.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/gpg_agent.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/gpg_agent.c Wed Nov  7 12:30:06 2018
@@ -65,9 +65,12 @@
 #include <sys/un.h>
 
 #include <apr_pools.h>
+#include <apr_strings.h>
+#include <apr_user.h>
 #include "svn_auth.h"
 #include "svn_config.h"
 #include "svn_error.h"
+#include "svn_io.h"
 #include "svn_pools.h"
 #include "svn_cmdline.h"
 #include "svn_checksum.h"
@@ -225,30 +228,31 @@ bye_gpg_agent(int sd)
   close(sd);
 }
 
-/* Locate a running GPG Agent, and return an open file descriptor
- * for communication with the agent in *NEW_SD. If no running agent
- * can be found, set *NEW_SD to -1. */
-static svn_error_t *
-find_running_gpg_agent(int *new_sd, apr_pool_t *pool)
+/* This implements a method of finding the socket which is a mix of the
+ * description from GPG 1.x's gpg-agent man page under the
+ * --use-standard-socket option and the logic from GPG 2.x's socket discovery
+ * code in common/homedir.c.
+ *
+ * The man page says the standard socket is "named 'S.gpg-agent' located
+ * in the home directory."  GPG's home directory is either the directory
+ * specified by $GNUPGHOME or ~/.gnupg.  GPG >= 2.1.13 will check for a
+ * socket under (/var)/run/UID/gnupg before ~/.gnupg if no environment
+ * variables are set.
+ *
+ * $GPG_AGENT_INFO takes precedence, if set, otherwise $GNUPGHOME will be
+ * used.  For GPG >= 2.1.13, $GNUPGHOME will be used directly only if it
+ * refers to the canonical home -- ~/.gnupg.  Otherwise, the path specified
+ * by $GNUPGHOME is hashed (SHA1 + z-base-32) and the socket is expected to
+ * be present under (/var)/run/UID/gnupg/d.HASH. This last mechanism is not
+ * yet supported here. */
+static const char *
+find_gpg_agent_socket(apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {
-  char *buffer;
   char *gpg_agent_info = NULL;
   char *gnupghome = NULL;
   const char *socket_name = NULL;
-  const char *request = NULL;
-  const char *p = NULL;
-  char *ep = NULL;
-  int sd;
-
-  *new_sd = -1;
 
-  /* This implements the method of finding the socket as described in
-   * the gpg-agent man page under the --use-standard-socket option.
-   * The manage page says the standard socket is "named 'S.gpg-agent' located
-   * in the home directory."  GPG's home directory is either the directory
-   * specified by $GNUPGHOME or ~/.gnupg. */
-  gpg_agent_info = getenv("GPG_AGENT_INFO");
-  if (gpg_agent_info != NULL)
+  if ((gpg_agent_info = getenv("GPG_AGENT_INFO")) != NULL)
     {
       apr_array_header_t *socket_details;
 
@@ -256,25 +260,78 @@ find_running_gpg_agent(int *new_sd, apr_
        * The path to the socket, the pid of the gpg-agent process and
        * finally the version of the protocol the agent talks. */
       socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE,
-                                         pool);
+                                         scratch_pool);
       socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
     }
   else if ((gnupghome = getenv("GNUPGHOME")) != NULL)
     {
-      const char *homedir = svn_dirent_canonicalize(gnupghome, pool);
-      socket_name = svn_dirent_join(homedir, "S.gpg-agent", pool);
+      const char *homedir = svn_dirent_canonicalize(gnupghome, scratch_pool);
+      socket_name = svn_dirent_join(homedir, "S.gpg-agent", scratch_pool);
     }
   else
     {
-      const char *homedir = svn_user_get_homedir(pool);
+      int i = 0;
+      const char *maybe_socket[] = {NULL, NULL, NULL, NULL};
+      const char *homedir;
+
+#ifdef APR_HAS_USER
+      apr_uid_t uid;
+      apr_gid_t gid;
+
+      if (apr_uid_current(&uid, &gid, scratch_pool) == APR_SUCCESS)
+        {
+          const char *uidbuf = apr_psprintf(scratch_pool, "%lu",
+                                            (unsigned long)uid);
+          maybe_socket[i++] = svn_dirent_join_many(scratch_pool, "/run/user",
+                                                   uidbuf, "gnupg",
+                                                   "S.gpg-agent",
+                                                   SVN_VA_NULL);
+          maybe_socket[i++] = svn_dirent_join_many(scratch_pool,
+                                                   "/var/run/user",
+                                                   uidbuf, "gnupg",
+                                                   "S.gpg-agent",
+                                                   SVN_VA_NULL);
+        }
+#endif
 
-      if (!homedir)
-        return SVN_NO_ERROR;
+      homedir = svn_user_get_homedir(scratch_pool);
+      if (homedir)
+        maybe_socket[i++] = svn_dirent_join_many(scratch_pool, homedir,
+                                                 ".gnupg", "S.gpg-agent",
+                                                 SVN_VA_NULL);
 
-      socket_name = svn_dirent_join_many(pool, homedir, ".gnupg",
-                                         "S.gpg-agent", SVN_VA_NULL);
+      for (i = 0; !socket_name && maybe_socket[i]; i++)
+        {
+          apr_finfo_t finfo;
+          svn_error_t *err = svn_io_stat(&finfo, maybe_socket[i],
+                                         APR_FINFO_TYPE, scratch_pool);
+          if (!err && finfo.filetype == APR_SOCK)
+            socket_name = maybe_socket[i];
+          svn_error_clear(err);
+        }
     }
 
+  if (socket_name)
+    socket_name = apr_pstrdup(result_pool, socket_name);
+
+  return socket_name;
+}
+
+/* Locate a running GPG Agent, and return an open file descriptor
+ * for communication with the agent in *NEW_SD. If no running agent
+ * can be found, set *NEW_SD to -1. */
+static svn_error_t *
+find_running_gpg_agent(int *new_sd, apr_pool_t *pool)
+{
+  char *buffer;
+  const char *socket_name = find_gpg_agent_socket(pool, pool);
+  const char *request = NULL;
+  const char *p = NULL;
+  char *ep = NULL;
+  int sd;
+
+  *new_sd = -1;
+
   if (socket_name != NULL)
     {
       struct sockaddr_un addr;

Modified: subversion/branches/ra-git/subversion/libsvn_subr/hash.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/hash.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/hash.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/hash.c Wed Nov  7 12:30:06 2018
@@ -564,14 +564,14 @@ svn_hash_from_cstring_keys(apr_hash_t **
 
 
 void *
-svn_hash__gets(apr_hash_t *ht, const char *key)
+svn_hash__gets_debug(apr_hash_t *ht, const char *key)
 {
   return apr_hash_get(ht, key, APR_HASH_KEY_STRING);
 }
 
 
 void
-svn_hash__sets(apr_hash_t *ht, const char *key, const void *val)
+svn_hash__sets_debug(apr_hash_t *ht, const char *key, const void *val)
 {
   apr_hash_set(ht, key, APR_HASH_KEY_STRING, val);
 }

Modified: subversion/branches/ra-git/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/io.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/io.c Wed Nov  7 12:30:06 2018
@@ -342,8 +342,13 @@ io_check_path(const char *path,
   /* Not using svn_io_stat() here because we want to check the
      apr_err return explicitly. */
   SVN_ERR(cstring_from_utf8(&path_apr, path, pool));
-
+#ifdef WIN32
+  /* on Windows, svn does not handle reparse points or hard links.
+     So ignore the 'resolve_symlinks' flag. */
+  flags = APR_FINFO_MIN;
+#else
   flags = resolve_symlinks ? APR_FINFO_MIN : (APR_FINFO_MIN | APR_FINFO_LINK);
+#endif
   apr_err = apr_stat(&finfo, path_apr, flags, pool);
 
   if (APR_STATUS_IS_ENOENT(apr_err))
@@ -2541,27 +2546,37 @@ stringbuf_from_aprfile(svn_stringbuf_t *
     {
       apr_finfo_t finfo = { 0 };
 
-      /* In some cases we get size 0 and no error for non files,
-          so we also check for the name. (= cached in apr_file_t) */
+      /* In some cases we get size 0 and no error for non files, so we
+         also check for the name. (= cached in apr_file_t) */
       if (! apr_file_info_get(&finfo, APR_FINFO_SIZE, file) && finfo.fname)
         {
-          /* we've got the file length. Now, read it in one go. */
+          /* In general, there is no guarantee that the given file size is
+             correct, for instance, because the underlying handle could be
+             pointing to a pipe.  We don't know that in advance, so attempt
+             to read *one more* byte than necessary.  If we get an EOF, then
+             we're done and we have succesfully avoided reading the file chunk-
+             by-chunk.  If we don't, we fall through and do so to read the
+             remaining part of the file. */
           svn_boolean_t eof;
-          res_initial_len = (apr_size_t)finfo.size;
+          res_initial_len = (apr_size_t)finfo.size + 1;
           res = svn_stringbuf_create_ensure(res_initial_len, pool);
           SVN_ERR(svn_io_file_read_full2(file, res->data,
                                          res_initial_len, &res->len,
                                          &eof, pool));
           res->data[res->len] = 0;
 
-          *result = res;
-          return SVN_NO_ERROR;
+          if (eof)
+            {
+              *result = res;
+              return SVN_NO_ERROR;
+            }
         }
     }
 
   /* XXX: We should check the incoming data for being of type binary. */
   buf = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE);
-  res = svn_stringbuf_create_ensure(res_initial_len, pool);
+  if (!res)
+    res = svn_stringbuf_create_ensure(res_initial_len, pool);
 
   /* apr_file_read will not return data and eof in the same call. So this loop
    * is safe from missing read data.  */
@@ -2707,8 +2722,8 @@ svn_io_remove_dir(const char *path, apr_
  directory scan.  A previous workaround involving rewinddir is
  problematic on Win32 and some NFS clients, notably NetBSD.
 
- See http://subversion.tigris.org/issues/show_bug.cgi?id=1896 and
- http://subversion.tigris.org/issues/show_bug.cgi?id=3501.
+ See https://issues.apache.org/jira/browse/SVN-1896 and
+ https://issues.apache.org/jira/browse/SVN-3501.
 */
 
 /* Neither windows nor unix allows us to delete a non-empty
@@ -3940,21 +3955,20 @@ svn_io_file_write_full(apr_file_t *file,
                        apr_size_t nbytes, apr_size_t *bytes_written,
                        apr_pool_t *pool)
 {
-  /* We cannot simply call apr_file_write_full on Win32 as it may fail
-     for larger values of NBYTES. In that case, we have to emulate the
-     "_full" part here. Thus, always call apr_file_write directly on
-     Win32 as this minimizes overhead for small data buffers. */
 #ifdef WIN32
 #define MAXBUFSIZE 30*1024
   apr_size_t bw = nbytes;
   apr_size_t to_write = nbytes;
+  apr_status_t rv;
 
-  /* try a simple "write everything at once" first */
-  apr_status_t rv = apr_file_write(file, buf, &bw);
+  rv = apr_file_write_full(file, buf, nbytes, &bw);
   buf = (char *)buf + bw;
   to_write -= bw;
 
-  /* if the OS cannot handle that, use smaller chunks */
+  /* Issue #1789: On Windows, writing may fail for large values of NBYTES.
+     If that is the case, keep track of how many bytes have been written
+     by the apr_file_write_full() call, and attempt to write the remaining
+     part in smaller chunks. */
   if (rv == APR_FROM_OS_ERROR(ERROR_NOT_ENOUGH_MEMORY)
       && nbytes > MAXBUFSIZE)
     {
@@ -4072,7 +4086,7 @@ svn_io_file_trunc(apr_file_t *file, apr_
 
      To prevent this, write 1 dummy byte just after the OFFSET at which we
      will trunc it.  That will force the APR file into write mode
-     internally and the flush() work-around below becomes affective. */
+     internally and the flush() work-around below becomes effective. */
   apr_off_t position = 0;
 
   /* A frequent usage is OFFSET==0, in which case we don't need to preserve
@@ -4632,7 +4646,7 @@ svn_io_dir_walk2(const char *dirname,
         }
       else if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
         {
-          /* some other directory. pass it to the callback. */
+          /* a regular file or a symlink. pass it to the callback. */
           SVN_ERR(entry_name_to_utf8(&name_utf8, finfo.name, dirname,
                                      subpool));
           full_path = svn_dirent_join(dirname, name_utf8, subpool);

Modified: subversion/branches/ra-git/subversion/libsvn_subr/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/log.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/log.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/log.c Wed Nov  7 12:30:06 2018
@@ -105,7 +105,7 @@ svn_log__get_file(const char *path, svn_
 const char *
 svn_log__get_dir(const char *path, svn_revnum_t rev,
                  svn_boolean_t want_contents, svn_boolean_t want_props,
-                 apr_uint64_t dirent_fields,
+                 apr_uint32_t dirent_fields,
                  apr_pool_t *pool)
 {
   return apr_psprintf(pool, "get-dir %s r%ld%s%s",
@@ -399,7 +399,7 @@ svn_log__get_inherited_props(const char
 const char *
 svn_log__list(const char *path, svn_revnum_t revision,
               apr_array_header_t *patterns, svn_depth_t depth,
-              apr_uint64_t dirent_fields, apr_pool_t *pool)
+              apr_uint32_t dirent_fields, apr_pool_t *pool)
 {
   svn_stringbuf_t *pattern_text = svn_stringbuf_create_empty(pool);
   const char *log_path;

Modified: subversion/branches/ra-git/subversion/libsvn_subr/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_subr/mergeinfo.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_subr/mergeinfo.c Wed Nov  7 12:30:06 2018
@@ -41,6 +41,14 @@
 #include "svn_hash.h"
 #include "private/svn_dep_compat.h"
 
+/* Return TRUE iff the forward revision range FIRST wholly contains the
+ * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has
+ * the same inheritability. */
+static svn_boolean_t
+range_contains(const svn_merge_range_t *first, const svn_merge_range_t *second,
+               svn_boolean_t consider_inheritance);
+
+
 /* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or
    overlapping, and their inheritability allows them to be combined, put
    the result in OUTPUT and return TRUE, otherwise return FALSE.
@@ -263,197 +271,186 @@ combine_with_lastrange(const svn_merge_r
       APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
         svn_merge_range_dup(new_range, result_pool);
     }
+  else if (combine_ranges(&combined_range, lastrange, new_range,
+                     consider_inheritance))
+    {
+      *lastrange = combined_range;
+    }
   else if (!consider_inheritance)
     {
       /* We are not considering inheritance so we can merge intersecting
          ranges of different inheritability.  Of course if the ranges
          don't intersect at all we simply push NEW_RANGE onto RANGELIST. */
-      if (combine_ranges(&combined_range, lastrange, new_range, FALSE))
-        {
-          *lastrange = combined_range;
-        }
-      else
-        {
-          APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
+      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
             svn_merge_range_dup(new_range, result_pool);
-        }
     }
   else /* Considering inheritance */
     {
-      if (combine_ranges(&combined_range, lastrange, new_range, TRUE))
-        {
-          /* Even when considering inheritance two intersection ranges
-             of the same inheritability can simply be combined. */
-          *lastrange = combined_range;
-        }
-      else
+      /* If we are here then the ranges either don't intersect or do
+          intersect but have differing inheritability.  Check for the
+          first case as that is easy to handle. */
+      intersection_type_t intersection_type;
+      svn_boolean_t sorted = FALSE;
+
+      SVN_ERR(get_type_of_intersection(new_range, lastrange,
+                                        &intersection_type));
+
+      switch (intersection_type)
         {
-          /* If we are here then the ranges either don't intersect or do
-             intersect but have differing inheritability.  Check for the
-             first case as that is easy to handle. */
-          intersection_type_t intersection_type;
-          svn_boolean_t sorted = FALSE;
+          case svn__no_intersection:
+            /* NEW_RANGE and *LASTRANGE *really* don't intersect so
+                just push NEW_RANGE onto RANGELIST. */
+            APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
+              svn_merge_range_dup(new_range, result_pool);
+            sorted = (svn_sort_compare_ranges(&lastrange,
+                                              &new_range) < 0);
+            break;
+
+          case svn__equal_intersection:
+            /* They range are equal so all we do is force the
+                inheritability of lastrange to true. */
+            lastrange->inheritable = TRUE;
+            sorted = TRUE;
+            break;
+
+          case svn__adjoining_intersection:
+            /* They adjoin but don't overlap so just push NEW_RANGE
+                onto RANGELIST. */
+            APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
+              svn_merge_range_dup(new_range, result_pool);
+            sorted = (svn_sort_compare_ranges(&lastrange,
+                                              &new_range) < 0);
+            break;
+
+          case svn__overlapping_intersection:
+            /* They ranges overlap but neither is a proper subset of
+                the other.  We'll end up pusing two new ranges onto
+                RANGELIST, the intersecting part and the part unique to
+                NEW_RANGE.*/
+            {
+              svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
+                                                          result_pool);
+              svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
+                                                          result_pool);
+
+              /* Pop off *LASTRANGE to make our manipulations
+                  easier. */
+              apr_array_pop(rangelist);
+
+              /* Ensure R1 is the older range. */
+              if (r2->start < r1->start)
+                {
+                  /* Swap R1 and R2. */
+                  *r2 = *r1;
+                  *r1 = *new_range;
+                }
 
-          SVN_ERR(get_type_of_intersection(new_range, lastrange,
-                                           &intersection_type));
+              /* Absorb the intersecting ranges into the
+                  inheritable range. */
+              if (r1->inheritable)
+                r2->start = r1->end;
+              else
+                r1->end = r2->start;
+
+              /* Push everything back onto RANGELIST. */
+              APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
+              sorted = (svn_sort_compare_ranges(&lastrange,
+                                                &r1) < 0);
+              APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
+              if (sorted)
+                sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
+              break;
+            }
 
-          switch (intersection_type)
+          default: /* svn__proper_subset_intersection */
             {
-              case svn__no_intersection:
-                /* NEW_RANGE and *LASTRANGE *really* don't intersect so
-                   just push NEW_RANGE onto RANGELIST. */
-                APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
-                  svn_merge_range_dup(new_range, result_pool);
-                sorted = (svn_sort_compare_ranges(&lastrange,
-                                                  &new_range) < 0);
-                break;
-
-              case svn__equal_intersection:
-                /* They range are equal so all we do is force the
-                   inheritability of lastrange to true. */
-                lastrange->inheritable = TRUE;
-                sorted = TRUE;
-                break;
-
-              case svn__adjoining_intersection:
-                /* They adjoin but don't overlap so just push NEW_RANGE
-                   onto RANGELIST. */
-                APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
-                  svn_merge_range_dup(new_range, result_pool);
-                sorted = (svn_sort_compare_ranges(&lastrange,
-                                                  &new_range) < 0);
-                break;
-
-              case svn__overlapping_intersection:
-                /* They ranges overlap but neither is a proper subset of
-                   the other.  We'll end up pusing two new ranges onto
-                   RANGELIST, the intersecting part and the part unique to
-                   NEW_RANGE.*/
+              /* One range is a proper subset of the other. */
+              svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
+                                                          result_pool);
+              svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
+                                                          result_pool);
+              svn_merge_range_t *r3 = NULL;
+
+              /* Pop off *LASTRANGE to make our manipulations
+                  easier. */
+              apr_array_pop(rangelist);
+
+              /* Ensure R1 is the superset. */
+              if (r2->start < r1->start || r2->end > r1->end)
                 {
-                  svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
-                                                              result_pool);
-                  svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
-                                                              result_pool);
-
-                  /* Pop off *LASTRANGE to make our manipulations
-                     easier. */
-                  apr_array_pop(rangelist);
+                  /* Swap R1 and R2. */
+                  *r2 = *r1;
+                  *r1 = *new_range;
+                }
 
-                  /* Ensure R1 is the older range. */
-                  if (r2->start < r1->start)
-                    {
-                      /* Swap R1 and R2. */
-                      *r2 = *r1;
-                      *r1 = *new_range;
-                    }
+              if (r1->inheritable)
+                {
+                  /* The simple case: The superset is inheritable, so
+                      just combine r1 and r2. */
+                  r1->start = MIN(r1->start, r2->start);
+                  r1->end = MAX(r1->end, r2->end);
+                  r2 = NULL;
+                }
+              else if (r1->start == r2->start)
+                {
+                  svn_revnum_t tmp_revnum;
 
-                  /* Absorb the intersecting ranges into the
-                     inheritable range. */
-                  if (r1->inheritable)
-                    r2->start = r1->end;
-                  else
-                    r1->end = r2->start;
+                  /* *LASTRANGE and NEW_RANGE share an end point. */
+                  tmp_revnum = r1->end;
+                  r1->end = r2->end;
+                  r2->inheritable = r1->inheritable;
+                  r1->inheritable = TRUE;
+                  r2->start = r1->end;
+                  r2->end = tmp_revnum;
+                }
+              else if (r1->end == r2->end)
+                {
+                  /* *LASTRANGE and NEW_RANGE share an end point. */
+                  r1->end = r2->start;
+                  r2->inheritable = TRUE;
+                }
+              else
+                {
+                  /* NEW_RANGE and *LASTRANGE share neither start
+                      nor end points. */
+                  r3 = apr_pcalloc(result_pool, sizeof(*r3));
+                  r3->start = r2->end;
+                  r3->end = r1->end;
+                  r3->inheritable = r1->inheritable;
+                  r2->inheritable = TRUE;
+                  r1->end = r2->start;
+                }
 
-                  /* Push everything back onto RANGELIST. */
-                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
-                  sorted = (svn_sort_compare_ranges(&lastrange,
-                                                    &r1) < 0);
+              /* Push everything back onto RANGELIST. */
+              APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
+              sorted = (svn_sort_compare_ranges(&lastrange, &r1) < 0);
+              if (r2)
+                {
                   APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
                   if (sorted)
                     sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
-                  break;
                 }
-
-              default: /* svn__proper_subset_intersection */
+              if (r3)
                 {
-                  /* One range is a proper subset of the other. */
-                  svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
-                                                              result_pool);
-                  svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
-                                                              result_pool);
-                  svn_merge_range_t *r3 = NULL;
-
-                  /* Pop off *LASTRANGE to make our manipulations
-                     easier. */
-                  apr_array_pop(rangelist);
-
-                  /* Ensure R1 is the superset. */
-                  if (r2->start < r1->start || r2->end > r1->end)
-                    {
-                      /* Swap R1 and R2. */
-                      *r2 = *r1;
-                      *r1 = *new_range;
-                    }
-
-                  if (r1->inheritable)
-                    {
-                      /* The simple case: The superset is inheritable, so
-                         just combine r1 and r2. */
-                      r1->start = MIN(r1->start, r2->start);
-                      r1->end = MAX(r1->end, r2->end);
-                      r2 = NULL;
-                    }
-                  else if (r1->start == r2->start)
-                    {
-                      svn_revnum_t tmp_revnum;
-
-                      /* *LASTRANGE and NEW_RANGE share an end point. */
-                      tmp_revnum = r1->end;
-                      r1->end = r2->end;
-                      r2->inheritable = r1->inheritable;
-                      r1->inheritable = TRUE;
-                      r2->start = r1->end;
-                      r2->end = tmp_revnum;
-                    }
-                  else if (r1->end == r2->end)
-                    {
-                      /* *LASTRANGE and NEW_RANGE share an end point. */
-                      r1->end = r2->start;
-                      r2->inheritable = TRUE;
-                    }
-                  else
-                    {
-                      /* NEW_RANGE and *LASTRANGE share neither start
-                         nor end points. */
-                      r3 = apr_pcalloc(result_pool, sizeof(*r3));
-                      r3->start = r2->end;
-                      r3->end = r1->end;
-                      r3->inheritable = r1->inheritable;
-                      r2->inheritable = TRUE;
-                      r1->end = r2->start;
-                    }
-
-                  /* Push everything back onto RANGELIST. */
-                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
-                  sorted = (svn_sort_compare_ranges(&lastrange, &r1) < 0);
-                  if (r2)
-                    {
-                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
-                      if (sorted)
-                        sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
-                    }
-                  if (r3)
+                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3;
+                  if (sorted)
                     {
-                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3;
-                      if (sorted)
-                        {
-                          if (r2)
-                            sorted = (svn_sort_compare_ranges(&r2,
-                                                              &r3) < 0);
-                          else
-                            sorted = (svn_sort_compare_ranges(&r1,
-                                                              &r3) < 0);
-                        }
+                      if (r2)
+                        sorted = (svn_sort_compare_ranges(&r2,
+                                                          &r3) < 0);
+                      else
+                        sorted = (svn_sort_compare_ranges(&r1,
+                                                          &r3) < 0);
                     }
-                  break;
                 }
+              break;
             }
-
-          /* Some of the above cases might have put *RANGELIST out of
-             order, so re-sort.*/
-          if (!sorted)
-            svn_sort__array(rangelist, svn_sort_compare_ranges);
         }
+
+      /* Some of the above cases might have put *RANGELIST out of
+          order, so re-sort.*/
+      if (!sorted)
+        svn_sort__array(rangelist, svn_sort_compare_ranges);
     }
 
   return SVN_NO_ERROR;
@@ -608,49 +605,48 @@ svn_rangelist__parse(svn_rangelist_t **r
   return SVN_NO_ERROR;
 }
 
-/* Return TRUE, if all ranges in RANGELIST are in ascending order and do
- * not overlap and are not adjacent.
- *
- * ### Can yield false negatives: ranges of differing inheritance are
- * allowed to be adjacent.
- *
- * If this returns FALSE, you probaly want to qsort() the
- * ranges and then call svn_rangelist__combine_adjacent_ranges().
- */
-static svn_boolean_t
-is_rangelist_normalized(svn_rangelist_t *rangelist)
+svn_boolean_t
+svn_rangelist__is_canonical(const svn_rangelist_t *rangelist)
 {
   int i;
   svn_merge_range_t **ranges = (svn_merge_range_t **)rangelist->elts;
 
-  for (i = 0; i < rangelist->nelts-1; ++i)
-    if (ranges[i]->end >= ranges[i+1]->start)
-      return FALSE;
-
-  return TRUE;
-}
-
-svn_error_t *
-svn_rangelist__canonicalize(svn_rangelist_t *rangelist,
-                            apr_pool_t *scratch_pool)
-{
-  if (! is_rangelist_normalized(rangelist))
+  /* Check for reversed and empty ranges */
+  for (i = 0; i < rangelist->nelts; ++i)
     {
-      svn_sort__array(rangelist, svn_sort_compare_ranges);
+      if (ranges[i]->start >= ranges[i]->end)
+        return FALSE;
+    }
 
-      SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool));
+  /* Check for overlapping ranges */
+  for (i = 0; i < rangelist->nelts - 1; ++i)
+    {
+      if (ranges[i]->end > ranges[i + 1]->start)
+        return FALSE; /* Overlapping range */
+      else if (ranges[i]->end == ranges[i+1]->start
+               && ranges[i]->inheritable == ranges[i + 1]->inheritable)
+        {
+          return FALSE; /* Ranges should have been combined */
+        }
     }
 
-  return SVN_NO_ERROR;
+  return TRUE;
 }
 
+/* In-place combines adjacent ranges in a rangelist.
+   SCRATCH_POOL is just used for providing error messages. */
 svn_error_t *
-svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist,
-                                       apr_pool_t *scratch_pool)
+svn_rangelist__canonicalize(svn_rangelist_t *rangelist,
+                            apr_pool_t *scratch_pool)
 {
   int i;
   svn_merge_range_t *range, *lastrange;
 
+  if (svn_rangelist__is_canonical(rangelist))
+    return SVN_NO_ERROR; /* Nothing to do */
+
+  svn_sort__array(rangelist, svn_sort_compare_ranges);
+
   lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
 
   for (i = 1; i < rangelist->nelts; i++)
@@ -895,7 +891,7 @@ adjust_remaining_ranges(svn_rangelist_t
               new_modified_range->end = modified_range->end;
               new_modified_range->inheritable = FALSE;
               modified_range->end = next_range->start;
-              (*range_index)+=2;
+              (*range_index) += 2 + elements_to_delete;
               svn_sort__array_insert(rangelist, &new_modified_range,
                                      *range_index);
               /* Recurse with the new range. */
@@ -955,25 +951,99 @@ adjust_remaining_ranges(svn_rangelist_t
     svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
 }
 
+#if 0 /* Temporary debug helper code */
+static svn_error_t *
+dual_dump(const char *prefix,
+  const svn_rangelist_t *rangelist,
+  const svn_rangelist_t *changes,
+  apr_pool_t *scratch_pool)
+{
+  svn_string_t *rls, *chg;
+
+  SVN_ERR(svn_rangelist_to_string(&rls, rangelist, scratch_pool));
+  SVN_ERR(svn_rangelist_to_string(&chg, changes, scratch_pool));
+
+  SVN_DBG(("%s: %s / %s", prefix, rls->data, chg->data));
+  return SVN_NO_ERROR;
+}
+#endif
+
 svn_error_t *
 svn_rangelist_merge2(svn_rangelist_t *rangelist,
-                     const svn_rangelist_t *changes,
+                     const svn_rangelist_t *chg,
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 {
+  svn_rangelist_t *changes;
   int i = 0;
-  int j = 0;
+  int j;
+
+  SVN_ERR(svn_rangelist__canonicalize(rangelist, scratch_pool));
 
   /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */
-  changes = svn_rangelist_dup(changes, scratch_pool);
+  changes = svn_rangelist_dup(chg, scratch_pool);
+  SVN_ERR(svn_rangelist__canonicalize(changes, scratch_pool));
 
-  while (i < rangelist->nelts && j < changes->nelts)
+  for (j = 0; j < changes->nelts; j++)
     {
-      svn_merge_range_t *range =
-        APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+      svn_merge_range_t *range;
       svn_merge_range_t *change =
         APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
-      int res = svn_sort_compare_ranges(&range, &change);
+      int res;
+
+      range = (i < rangelist->nelts)
+              ? APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *)
+              : NULL;
+
+      if (!range || change->end < range->start)
+        {
+          /* No overlap, nor adjoin, copy change to result range */
+          svn_merge_range_t *chg_copy = svn_merge_range_dup(change,
+                                                            result_pool);
+          svn_sort__array_insert(rangelist, &chg_copy, i++);
+          continue;
+        }
+      else if ((change->start > range->end)
+               || (change->start == range->end
+                   && change->inheritable != range->inheritable))
+        {
+          /* No overlap, nor adjoin. Check next range item against change */
+          i++;
+          j--;
+          continue;
+        }
+
+      if (change->start < range->start
+          && range->inheritable != change->inheritable
+          && ! (change->inheritable && range_contains(change, range, FALSE))
+          && ! (range->inheritable && range_contains(range, change, FALSE)))
+        {
+          /* Can't fold change into existing range.
+             Insert new range before range */
+
+          svn_merge_range_t *chg_copy = svn_merge_range_dup(change,
+                                                            result_pool);
+
+          chg_copy->start = MIN(change->start, range->start);
+          if (! change->inheritable)
+            chg_copy->end = range->start;
+          else
+            range->start = change->end;
+
+          svn_sort__array_insert(rangelist, &chg_copy, i++);
+
+          change->start = chg_copy->end;
+          if (change->start >= change->end)
+            continue; /* No overlap with range left */
+        }
+      else
+        {
+          range->start = MIN(range->start, change->start);
+        }
+
+      SVN_ERR_ASSERT(change->start >= range->start);
+
+      res = svn_sort_compare_ranges(&range, &change);
 
       if (res == 0)
         {
@@ -983,17 +1053,11 @@ svn_rangelist_merge2(svn_rangelist_t *ra
           if (range->inheritable || change->inheritable)
             range->inheritable = TRUE;
           i++;
-          j++;
+          continue;
         }
       else if (res < 0) /* CHANGE is younger than RANGE */
         {
-          if (range->end < change->start)
-            {
-              /* RANGE is older than CHANGE and the two do not
-                 adjoin or overlap */
-              i++;
-            }
-          else if (range->end == change->start)
+          if (range->end == change->start)
             {
               /* RANGE and CHANGE adjoin */
               if (range->inheritable == change->inheritable)
@@ -1002,14 +1066,14 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                      RANGE expands to absord CHANGE. */
                   range->end = change->end;
                   adjust_remaining_ranges(rangelist, &i, result_pool);
-                  j++;
+                  continue;
                 }
               else
                 {
                   /* RANGE and CHANGE adjoin, but have different
                      inheritability.  Since RANGE is older, just
                      move on to the next RANGE. */
-                  i++;
+                  SVN_ERR_MALFUNCTION();
                 }
             }
           else
@@ -1022,9 +1086,13 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                       with no adjustment otherwise only the intersection is
                       absorbed and CHANGE is truncated. */
                   if (range->end >= change->end)
-                    j++;
+                    continue;
                   else
-                    change->start = range->end;
+                    {
+                      change->start = range->end;
+                      j--;
+                      continue;
+                    }
                 }
               else
                 {
@@ -1038,6 +1106,8 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                       range_copy->end = change->start;
                       range->start = change->start;
                       svn_sort__array_insert(rangelist, &range_copy, i++);
+                      j--;
+                      continue;
                     }
                   else
                     {
@@ -1046,23 +1116,15 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                          is older. */
                       range->inheritable = TRUE;
                       change->start = range->end;
+                      j--;
+                      continue;
                     }
                 }
             }
         }
       else /* res > 0, CHANGE is older than RANGE */
         {
-          if (change->end < range->start)
-            {
-              /* CHANGE is older than RANGE and the two do not
-                 adjoin or overlap, so insert a copy of CHANGE
-                 into RANGELIST. */
-              svn_merge_range_t *change_copy =
-                svn_merge_range_dup(change, result_pool);
-              svn_sort__array_insert(rangelist, &change_copy, i++);
-              j++;
-            }
-          else if (change->end == range->start)
+          if (change->end == range->start)
             {
               /* RANGE and CHANGE adjoin */
               if (range->inheritable == change->inheritable)
@@ -1070,16 +1132,13 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                   /* RANGE and CHANGE have the same inheritability so we
                      can simply combine the two in place. */
                   range->start = change->start;
-                  j++;
+                  continue;
                 }
               else
                 {
                   /* RANGE and CHANGE have different inheritability so insert
                      a copy of CHANGE into RANGELIST. */
-                  svn_merge_range_t *change_copy =
-                    svn_merge_range_dup(change, result_pool);
-                  svn_sort__array_insert(rangelist, &change_copy, i);
-                  j++;
+                  SVN_ERR_MALFUNCTION(); /* Already handled */
                 }
             }
           else
@@ -1097,7 +1156,7 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                       range->end = change->end;
                       adjust_remaining_ranges(rangelist, &i, result_pool);
                     }
-                  j++;
+                  continue;
                 }
               else if (range->inheritable)
                 {
@@ -1106,18 +1165,14 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                       /* RANGE is inheritable so absorbs any part of CHANGE
                          it overlaps.  CHANGE is truncated and the remainder
                          inserted into RANGELIST. */
-                      svn_merge_range_t *change_copy =
-                        svn_merge_range_dup(change, result_pool);
-                      change_copy->end = range->start;
-                      change->start = range->start;
-                      svn_sort__array_insert(rangelist, &change_copy, i++);
+                      SVN_ERR_MALFUNCTION(); /* Already handled */
                     }
                   else
                     {
                       /* CHANGE and RANGE share the same start rev, but
                          CHANGE is considered older because CHANGE->END is
                          older than RANGE->END. */
-                      j++;
+                      continue;
                     }
                 }
               else
@@ -1131,7 +1186,7 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                              same end revision, so set RANGE equal to CHANGE. */
                           range->start = change->start;
                           range->inheritable = TRUE;
-                          j++;
+                          continue;
                         }
                       else if (change->end > range->end)
                         {
@@ -1141,6 +1196,8 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                           range->start = change->start;
                           range->inheritable = TRUE;
                           change->start = range->end;
+                          j--;
+                          continue;
                         }
                       else
                         {
@@ -1154,7 +1211,7 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                           range->end = change->end;
                           range->inheritable = TRUE;
                           svn_sort__array_insert(rangelist, &range_copy, ++i);
-                          j++;
+                          continue;
                         }
                     }
                   else
@@ -1172,22 +1229,17 @@ svn_rangelist_merge2(svn_rangelist_t *ra
                       range_copy->inheritable = TRUE;
                       range->start = change->end;
                       svn_sort__array_insert(rangelist, &range_copy, i++);
-                      j++;
+                      continue;
                     }
                 }
             }
         }
+      SVN_ERR_MALFUNCTION(); /* Unreachable */
     }
 
-  /* Copy any remaining elements in CHANGES into RANGELIST. */
-  for (; j < (changes)->nelts; j++)
-    {
-      svn_merge_range_t *change =
-        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
-      svn_merge_range_t *change_copy = svn_merge_range_dup(change,
-                                                           result_pool);
-      svn_sort__array_insert(rangelist, &change_copy, rangelist->nelts);
-    }
+#ifdef SVN_DEBUG
+  SVN_ERR_ASSERT(svn_rangelist__is_canonical(rangelist));
+#endif
 
   return SVN_NO_ERROR;
 }