You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2014/12/14 12:52:16 UTC

svn commit: r1645441 [2/4] - in /subversion/branches/javahl-1.8-extensions: ./ subversion/ subversion/include/ subversion/include/private/ subversion/libsvn_auth_gnome_keyring/ subversion/libsvn_client/ subversion/libsvn_diff/ subversion/libsvn_fs_fs/ ...

Modified: subversion/branches/javahl-1.8-extensions/subversion/libsvn_subr/gpg_agent.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/libsvn_subr/gpg_agent.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/libsvn_subr/gpg_agent.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/libsvn_subr/gpg_agent.c Sun Dec 14 11:52:14 2014
@@ -72,6 +72,9 @@
 #include "svn_cmdline.h"
 #include "svn_checksum.h"
 #include "svn_string.h"
+#include "svn_hash.h"
+#include "svn_user.h"
+#include "svn_dirent_uri.h"
 
 #include "private/svn_auth_private.h"
 
@@ -80,6 +83,7 @@
 #ifdef SVN_HAVE_GPG_AGENT
 
 #define BUFFER_SIZE 1024
+#define ATTEMPT_PARAMETER "svn.simple.gpg_agent.attempt"
 
 /* Modify STR in-place such that blanks are escaped as required by the
  * gpg-agent protocol. Return a pointer to STR. */
@@ -98,6 +102,24 @@ escape_blanks(char *str)
   return str;
 }
 
+/* Generate the string CACHE_ID_P based on the REALMSTRING allocated in
+ * RESULT_POOL using SCRATCH_POOL for temporary allocations.  This is similar
+ * to other password caching mechanisms. */
+static svn_error_t *
+get_cache_id(const char **cache_id_p, const char *realmstring,
+             apr_pool_t *scratch_pool, apr_pool_t *result_pool)
+{
+  const char *cache_id = NULL;
+  svn_checksum_t *digest = NULL;
+
+  SVN_ERR(svn_checksum(&digest, svn_checksum_md5, realmstring,
+                       strlen(realmstring), scratch_pool));
+  cache_id = svn_checksum_to_cstring(digest, result_pool);
+  *cache_id_p = cache_id;
+
+  return SVN_NO_ERROR;
+}
+
 /* Attempt to read a gpg-agent response message from the socket SD into
  * buffer BUF. Buf is assumed to be N bytes large. Return TRUE if a response
  * message could be read that fits into the buffer. Else return FALSE.
@@ -156,6 +178,17 @@ send_option(int sd, char *buf, size_t n,
   return (strncmp(buf, "OK", 2) == 0);
 }
 
+/* Send the BYE command and disconnect from the gpg-agent.  Doing this avoids
+ * gpg-agent emitting a "Connection reset by peer" log message with some
+ * versions of gpg-agent. */
+static void
+bye_gpg_agent(int sd)
+{
+  /* don't bother to check the result of the write, it either worked or it
+   * didn't, but either way we're closing. */
+  write(sd, "BYE\n", 4);
+  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
@@ -173,17 +206,34 @@ find_running_gpg_agent(int *new_sd, apr_
 
   *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 misleadingly says the standard socket is 
+   * "named 'S.gpg-agent' located in the home directory."  The standard
+   * socket path is actually in the .gnupg directory in the home directory,
+   * i.e. ~/.gnupg/S.gpg-agent */
   gpg_agent_info = getenv("GPG_AGENT_INFO");
   if (gpg_agent_info != NULL)
     {
       apr_array_header_t *socket_details;
 
+      /* For reference GPG_AGENT_INFO consists of 3 : separated fields.
+       * 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);
       socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
     }
   else
-    return SVN_NO_ERROR;
+    {
+      const char *homedir = svn_user_get_homedir(pool);
+
+      if (!homedir)
+        return SVN_NO_ERROR;
+
+      socket_name = svn_dirent_join_many(pool, homedir, ".gnupg",
+                                         "S.gpg-agent", NULL);
+    }
 
   if (socket_name != NULL)
     {
@@ -210,13 +260,13 @@ find_running_gpg_agent(int *new_sd, apr_
   buffer = apr_palloc(pool, BUFFER_SIZE);
   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
 
   if (strncmp(buffer, "OK", 2) != 0)
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
 
@@ -226,19 +276,19 @@ find_running_gpg_agent(int *new_sd, apr_
   request = "GETINFO socket_name\n";
   if (write(sd, request, strlen(request)) == -1)
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
   if (strncmp(buffer, "D", 1) == 0)
     p = &buffer[2];
   if (!p)
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
   ep = strchr(p, '\n');
@@ -246,18 +296,18 @@ find_running_gpg_agent(int *new_sd, apr_
     *ep = '\0';
   if (strcmp(socket_name, p) != 0)
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
   /* The agent will terminate its response with "OK". */
   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
   if (strncmp(buffer, "OK", 2) != 0)
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
 
@@ -265,60 +315,28 @@ find_running_gpg_agent(int *new_sd, apr_
   return SVN_NO_ERROR;
 }
 
-/* Implementation of svn_auth__password_get_t that retrieves the password
-   from gpg-agent */
-static svn_error_t *
-password_get_gpg_agent(svn_boolean_t *done,
-                       const char **password,
-                       apr_hash_t *creds,
-                       const char *realmstring,
-                       const char *username,
-                       apr_hash_t *parameters,
-                       svn_boolean_t non_interactive,
-                       apr_pool_t *pool)
+static svn_boolean_t
+send_options(int sd, char *buf, size_t n, apr_pool_t *scratch_pool)
 {
-  int sd;
-  const char *p = NULL;
-  char *ep = NULL;
-  char *buffer;
-  const char *request = NULL;
-  const char *cache_id = NULL;
   const char *tty_name;
   const char *tty_type;
   const char *lc_ctype;
   const char *display;
-  svn_checksum_t *digest = NULL;
-  char *password_prompt;
-  char *realm_prompt;
-
-  *done = FALSE;
-
-  SVN_ERR(find_running_gpg_agent(&sd, pool));
-  if (sd == -1)
-    return SVN_NO_ERROR;
-
-  buffer = apr_palloc(pool, BUFFER_SIZE);
 
   /* Send TTY_NAME to the gpg-agent daemon. */
   tty_name = getenv("GPG_TTY");
   if (tty_name != NULL)
     {
-      if (!send_option(sd, buffer, BUFFER_SIZE, "ttyname", tty_name, pool))
-        {
-          close(sd);
-          return SVN_NO_ERROR;
-        }
+      if (!send_option(sd, buf, n, "ttyname", tty_name, scratch_pool))
+        return FALSE;
     }
 
   /* Send TTY_TYPE to the gpg-agent daemon. */
   tty_type = getenv("TERM");
   if (tty_type != NULL)
     {
-      if (!send_option(sd, buffer, BUFFER_SIZE, "ttytype", tty_type, pool))
-        {
-          close(sd);
-          return SVN_NO_ERROR;
-        }
+      if (!send_option(sd, buf, n, "ttytype", tty_type, scratch_pool))
+        return FALSE;
     }
 
   /* Compute LC_CTYPE. */
@@ -331,53 +349,92 @@ password_get_gpg_agent(svn_boolean_t *do
   /* Send LC_CTYPE to the gpg-agent daemon. */
   if (lc_ctype != NULL)
     {
-      if (!send_option(sd, buffer, BUFFER_SIZE, "lc-ctype", lc_ctype, pool))
-        {
-          close(sd);
-          return SVN_NO_ERROR;
-        }
+      if (!send_option(sd, buf, n, "lc-ctype", lc_ctype, scratch_pool))
+        return FALSE;
     }
 
   /* Send DISPLAY to the gpg-agent daemon. */
   display = getenv("DISPLAY");
   if (display != NULL)
     {
-      if (!send_option(sd, buffer, BUFFER_SIZE, "display", display, pool))
-        {
-          close(sd);
-          return SVN_NO_ERROR;
-        }
+      if (!send_option(sd, buf, n, "display", display, scratch_pool))
+        return FALSE;
     }
 
-  /* Create the CACHE_ID which will be generated based on REALMSTRING similar
-     to other password caching mechanisms. */
-  SVN_ERR(svn_checksum(&digest, svn_checksum_md5, realmstring,
-                       strlen(realmstring), pool));
-  cache_id = svn_checksum_to_cstring(digest, pool);
+  return TRUE;
+}
+
+/* Implementation of svn_auth__password_get_t that retrieves the password
+   from gpg-agent */
+static svn_error_t *
+password_get_gpg_agent(svn_boolean_t *done,
+                       const char **password,
+                       apr_hash_t *creds,
+                       const char *realmstring,
+                       const char *username,
+                       apr_hash_t *parameters,
+                       svn_boolean_t non_interactive,
+                       apr_pool_t *pool)
+{
+  int sd;
+  const char *p = NULL;
+  char *ep = NULL;
+  char *buffer;
+  const char *request = NULL;
+  const char *cache_id = NULL;
+  char *password_prompt;
+  char *realm_prompt;
+  char *error_prompt;
+  int *attempt;
+
+  *done = FALSE;
+
+  attempt = svn_hash_gets(parameters, ATTEMPT_PARAMETER);
+
+  SVN_ERR(find_running_gpg_agent(&sd, pool));
+  if (sd == -1)
+    return SVN_NO_ERROR;
+
+  buffer = apr_palloc(pool, BUFFER_SIZE);
+
+  if (!send_options(sd, buffer, BUFFER_SIZE, pool))
+    {
+      bye_gpg_agent(sd);
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(get_cache_id(&cache_id, realmstring, pool, pool));
 
   password_prompt = apr_psprintf(pool, _("Password for '%s': "), username);
   realm_prompt = apr_psprintf(pool, _("Enter your Subversion password for %s"),
                               realmstring);
+  if (*attempt == 1)
+    /* X means no error to the gpg-agent protocol */
+    error_prompt = apr_pstrdup(pool, "X");
+  else
+    error_prompt = apr_pstrdup(pool, _("Authentication failed"));
+
   request = apr_psprintf(pool,
-                         "GET_PASSPHRASE --data %s--repeat=1 "
-                         "%s X %s %s\n",
+                         "GET_PASSPHRASE --data %s"
+                         "%s %s %s %s\n",
                          non_interactive ? "--no-ask " : "",
                          cache_id,
+                         escape_blanks(error_prompt),
                          escape_blanks(password_prompt),
                          escape_blanks(realm_prompt));
 
   if (write(sd, request, strlen(request)) == -1)
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
   if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
     {
-      close(sd);
+      bye_gpg_agent(sd);
       return SVN_NO_ERROR;
     }
 
-  close(sd);
+  bye_gpg_agent(sd);
 
   if (strncmp(buffer, "ERR", 3) == 0)
     return SVN_NO_ERROR;
@@ -424,7 +481,7 @@ password_set_gpg_agent(svn_boolean_t *do
   if (sd == -1)
     return SVN_NO_ERROR;
 
-  close(sd);
+  bye_gpg_agent(sd);
   *done = TRUE;
 
   return SVN_NO_ERROR;
@@ -440,11 +497,108 @@ simple_gpg_agent_first_creds(void **cred
                              const char *realmstring,
                              apr_pool_t *pool)
 {
-  return svn_auth__simple_creds_cache_get(credentials, iter_baton,
-                                          provider_baton, parameters,
-                                          realmstring, password_get_gpg_agent,
-                                          SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
-                                          pool);
+  svn_error_t *err;
+  int *attempt = apr_palloc(pool, sizeof(*attempt));
+
+  *attempt = 1;
+  svn_hash_sets(parameters, ATTEMPT_PARAMETER, attempt);
+  err = svn_auth__simple_creds_cache_get(credentials, iter_baton,
+                                         provider_baton, parameters,
+                                         realmstring, password_get_gpg_agent,
+                                         SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
+                                         pool);
+  *iter_baton = attempt;
+
+  return err;
+}
+
+/* An implementation of svn_auth_provider_t::next_credentials() */
+static svn_error_t *
+simple_gpg_agent_next_creds(void **credentials,
+                            void *iter_baton,
+                            void *provider_baton,
+                            apr_hash_t *parameters,
+                            const char *realmstring,
+                            apr_pool_t *pool)
+{
+  int *attempt = (int *)iter_baton;
+  int sd;
+  char *buffer;
+  const char *cache_id = NULL;
+  const char *request = NULL;
+
+  *credentials = NULL;
+
+  /* The users previous credentials failed so first remove the cached entry,
+   * before trying to retrieve them again.  Because gpg-agent stores cached
+   * credentials immediately upon retrieving them, this gives us the
+   * opportunity to remove the invalid credentials and prompt the
+   * user again.  While it's possible that server side issues could trigger
+   * this, this cache is ephemeral so at worst we're just speeding up
+   * when the user would need to re-enter their password. */
+
+  if (svn_hash_gets(parameters, SVN_AUTH_PARAM_NON_INTERACTIVE))
+    {
+      /* In this case since we're running non-interactively we do not
+       * want to clear the cache since the user was never prompted by
+       * gpg-agent to set a password. */
+      return SVN_NO_ERROR;
+    }
+
+  *attempt = *attempt + 1;
+
+  SVN_ERR(find_running_gpg_agent(&sd, pool));
+  if (sd == -1)
+    return SVN_NO_ERROR;
+
+  buffer = apr_palloc(pool, BUFFER_SIZE);
+
+  if (!send_options(sd, buffer, BUFFER_SIZE, pool))
+    {
+      bye_gpg_agent(sd);
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(get_cache_id(&cache_id, realmstring, pool, pool));
+
+  request = apr_psprintf(pool, "CLEAR_PASSPHRASE %s\n", cache_id);
+
+  if (write(sd, request, strlen(request)) == -1)
+    {
+      bye_gpg_agent(sd);
+      return SVN_NO_ERROR;
+    }
+
+  if (!receive_from_gpg_agent(sd, buffer, BUFFER_SIZE))
+    {
+      bye_gpg_agent(sd);
+      return SVN_NO_ERROR;
+    }
+
+  if (strncmp(buffer, "OK\n", 3) != 0)
+    {
+      bye_gpg_agent(sd);
+      return SVN_NO_ERROR;
+    }
+
+  /* TODO: This attempt limit hard codes it at 3 attempts (or 2 retries)
+   * which matches svn command line client's retry_limit as set in
+   * svn_cmdline_create_auth_baton().  It would be nice to have that
+   * limit reflected here but that violates the boundry between the
+   * prompt provider and the cache provider.  gpg-agent is acting as
+   * both here due to the peculiarties of their design so we'll have to
+   * live with this for now.  Note that when these failures get exceeded
+   * it'll eventually fall back on the retry limits of whatever prompt
+   * provider is in effect, so this effectively doubles the limit. */
+  if (*attempt < 4)
+    return svn_auth__simple_creds_cache_get(credentials, &iter_baton,
+                                            provider_baton, parameters,
+                                            realmstring,
+                                            password_get_gpg_agent,
+                                            SVN_AUTH__GPG_AGENT_PASSWORD_TYPE,
+                                            pool);
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -468,7 +622,7 @@ simple_gpg_agent_save_creds(svn_boolean_
 static const svn_auth_provider_t gpg_agent_simple_provider = {
   SVN_AUTH_CRED_SIMPLE,
   simple_gpg_agent_first_creds,
-  NULL,
+  simple_gpg_agent_next_creds,
   simple_gpg_agent_save_creds
 };
 

Modified: subversion/branches/javahl-1.8-extensions/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/libsvn_subr/io.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/libsvn_subr/io.c Sun Dec 14 11:52:14 2014
@@ -4675,8 +4675,25 @@ svn_io_open_unique_file3(apr_file_t **fi
    * case, but only if the umask allows it. */
   if (!using_system_temp_dir)
     {
+      svn_error_t *err;
+
       SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
-      SVN_ERR(file_perms_set2(tempfile, perms, scratch_pool));
+      err = file_perms_set2(tempfile, perms, scratch_pool);
+      if (err)
+        {
+          if (APR_STATUS_IS_INCOMPLETE(err->apr_err) ||
+              APR_STATUS_IS_ENOTIMPL(err->apr_err))
+            svn_error_clear(err);
+          else
+            {
+              const char *message;
+              message = apr_psprintf(scratch_pool,
+                                     _("Can't set permissions on '%s'"),
+                                     svn_dirent_local_style(tempname,
+                                                            scratch_pool));
+              return svn_error_quick_wrap(err, message);
+            }
+        }
     }
 #endif
 

Modified: subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/cleanup.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/cleanup.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/cleanup.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/cleanup.c Sun Dec 14 11:52:14 2014
@@ -67,69 +67,13 @@ can_be_cleaned(int *wc_format,
   return SVN_NO_ERROR;
 }
 
-/* Do a modifed check for LOCAL_ABSPATH, and all working children, to force
-   timestamp repair. */
+/* Dummy svn_wc_status_func4_t implementation */
 static svn_error_t *
-repair_timestamps(svn_wc__db_t *db,
-                  const char *local_abspath,
-                  svn_cancel_func_t cancel_func,
-                  void *cancel_baton,
-                  apr_pool_t *scratch_pool)
+status_dummy_callback(void *baton,
+                      const char *local_abspath,
+                      const svn_wc_status3_t *status,
+                      apr_pool_t *scratch_pool)
 {
-  svn_node_kind_t kind;
-  svn_wc__db_status_t status;
-
-  if (cancel_func)
-    SVN_ERR(cancel_func(cancel_baton));
-
-  SVN_ERR(svn_wc__db_read_info(&status, &kind,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL,
-                               db, local_abspath, scratch_pool, scratch_pool));
-
-  if (status == svn_wc__db_status_server_excluded
-      || status == svn_wc__db_status_deleted
-      || status == svn_wc__db_status_excluded
-      || status == svn_wc__db_status_not_present)
-    return SVN_NO_ERROR;
-
-  if (kind == svn_node_file
-      || kind == svn_node_symlink)
-    {
-      svn_boolean_t modified;
-      SVN_ERR(svn_wc__internal_file_modified_p(&modified,
-                                               db, local_abspath, FALSE,
-                                               scratch_pool));
-    }
-  else if (kind == svn_node_dir)
-    {
-      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-      const apr_array_header_t *children;
-      int i;
-
-      SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db,
-                                                       local_abspath,
-                                                       scratch_pool,
-                                                       iterpool));
-      for (i = 0; i < children->nelts; ++i)
-        {
-          const char *child_abspath;
-
-          svn_pool_clear(iterpool);
-
-          child_abspath = svn_dirent_join(local_abspath,
-                                          APR_ARRAY_IDX(children, i,
-                                                        const char *),
-                                          iterpool);
-
-          SVN_ERR(repair_timestamps(db, child_abspath,
-                                    cancel_func, cancel_baton, iterpool));
-        }
-      svn_pool_destroy(iterpool);
-    }
-
   return SVN_NO_ERROR;
 }
 
@@ -184,8 +128,17 @@ cleanup_internal(svn_wc__db_t *db,
       SVN_ERR(svn_wc__db_pristine_cleanup(db, dir_abspath, scratch_pool));
     }
 
-  SVN_ERR(repair_timestamps(db, dir_abspath, cancel_func, cancel_baton,
-                            scratch_pool));
+  /* Instead of implementing a separate repair step here, use the standard
+     status walker's optimized implementation, which performs repairs when
+     there is a lock. */
+  SVN_ERR(svn_wc__internal_walk_status(db, dir_abspath, svn_depth_infinity,
+                                       FALSE /* get_all */,
+                                       FALSE /* no_ignore */,
+                                       FALSE /* ignore_text_mods */,
+                                       NULL /* ignore patterns */,
+                                       status_dummy_callback, NULL,
+                                       cancel_func, cancel_baton,
+                                       scratch_pool));
 
   /* All done, toss the lock */
   SVN_ERR(svn_wc__db_wclock_release(db, dir_abspath, scratch_pool));

Modified: subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff.h
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff.h?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff.h (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff.h Sun Dec 14 11:52:14 2014
@@ -47,9 +47,6 @@ extern "C" {
    svn_wc__db_status_added. When DIFF_PRISTINE is TRUE, report the pristine
    version of LOCAL_ABSPATH as ADDED. In this case an
    svn_wc__db_status_deleted may shadow an added or deleted node.
-
-   If CHANGELIST_HASH is not NULL and LOCAL_ABSPATH's changelist is not
-   in the changelist, don't report the node.
  */
 svn_error_t *
 svn_wc__diff_local_only_file(svn_wc__db_t *db,
@@ -57,7 +54,6 @@ svn_wc__diff_local_only_file(svn_wc__db_
                              const char *relpath,
                              const svn_diff_tree_processor_t *processor,
                              void *processor_parent_baton,
-                             apr_hash_t *changelist_hash,
                              svn_boolean_t diff_pristine,
                              svn_cancel_func_t cancel_func,
                              void *cancel_baton,
@@ -73,9 +69,6 @@ svn_wc__diff_local_only_file(svn_wc__db_
    svn_wc__db_status_added. When DIFF_PRISTINE is TRUE, report the pristine
    version of LOCAL_ABSPATH as ADDED. In this case an
    svn_wc__db_status_deleted may shadow an added or deleted node.
-
-   If CHANGELIST_HASH is not NULL and LOCAL_ABSPATH's changelist is not
-   in the changelist, don't report the node.
  */
 svn_error_t *
 svn_wc__diff_local_only_dir(svn_wc__db_t *db,
@@ -84,7 +77,6 @@ svn_wc__diff_local_only_dir(svn_wc__db_t
                             svn_depth_t depth,
                             const svn_diff_tree_processor_t *processor,
                             void *processor_parent_baton,
-                            apr_hash_t *changelist_hash,
                             svn_boolean_t diff_pristine,
                             svn_cancel_func_t cancel_func,
                             void *cancel_baton,
@@ -132,7 +124,6 @@ svn_wc__diff_base_working_diff(svn_wc__d
                                const char *local_abspath,
                                const char *relpath,
                                svn_revnum_t revision,
-                               apr_hash_t *changelist_hash,
                                const svn_diff_tree_processor_t *processor,
                                void *processor_dir_baton,
                                svn_boolean_t diff_pristine,
@@ -140,6 +131,32 @@ svn_wc__diff_base_working_diff(svn_wc__d
                                void *cancel_baton,
                                apr_pool_t *scratch_pool);
 
+/* Return a tree processor filter that filters by changelist membership.
+ *
+ * This filter only passes on the changes for a file if the file's path
+ * (in the WC) is assigned to one of the changelists in @a changelist_hash.
+ * It also passes on the opening and closing of each directory that contains
+ * such a change, and possibly also of other directories, but not addition
+ * or deletion or changes to a directory.
+ *
+ * If @a changelist_hash is null then no filtering is performed and the
+ * returned diff processor is driven exactly like the input @a processor.
+ *
+ * @a wc_ctx is the WC context and @a root_local_abspath is the WC path of
+ * the root of the diff (for which relpath = "" in the diff processor).
+ *
+ * Allocate the returned diff processor in @a result_pool, or if no
+ * filtering is required then the input pointer @a processor itself may be
+ * returned.
+ */
+const svn_diff_tree_processor_t *
+svn_wc__changelist_filter_tree_processor_create(
+                                const svn_diff_tree_processor_t *processor,
+                                svn_wc_context_t *wc_ctx,
+                                const char *root_local_abspath,
+                                apr_hash_t *changelist_hash,
+                                apr_pool_t *result_pool);
+
 
 #ifdef __cplusplus
 }

Modified: subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff_editor.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff_editor.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff_editor.c Sun Dec 14 11:52:14 2014
@@ -114,9 +114,6 @@ struct edit_baton_t
   /* Possibly diff repos against text-bases instead of working files. */
   svn_boolean_t diff_pristine;
 
-  /* Hash whose keys are const char * changelist names. */
-  apr_hash_t *changelist_hash;
-
   /* Cancel function/baton */
   svn_cancel_func_t cancel_func;
   void *cancel_baton;
@@ -238,43 +235,26 @@ struct file_baton_t
  * calculating diffs.  USE_TEXT_BASE defines whether to compare
  * against working files or text-bases.  REVERSE_ORDER defines which
  * direction to perform the diff.
- *
- * CHANGELIST_FILTER is a list of const char * changelist names, used to
- * filter diff output responses to only those items in one of the
- * specified changelists, empty (or NULL altogether) if no changelist
- * filtering is requested.
  */
 static svn_error_t *
 make_edit_baton(struct edit_baton_t **edit_baton,
                 svn_wc__db_t *db,
                 const char *anchor_abspath,
                 const char *target,
-                const svn_wc_diff_callbacks4_t *callbacks,
-                void *callback_baton,
+                const svn_diff_tree_processor_t *processor,
                 svn_depth_t depth,
                 svn_boolean_t ignore_ancestry,
                 svn_boolean_t show_copies_as_adds,
                 svn_boolean_t use_text_base,
                 svn_boolean_t reverse_order,
-                const apr_array_header_t *changelist_filter,
                 svn_cancel_func_t cancel_func,
                 void *cancel_baton,
                 apr_pool_t *pool)
 {
-  apr_hash_t *changelist_hash = NULL;
   struct edit_baton_t *eb;
-  const svn_diff_tree_processor_t *processor;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
 
-  if (changelist_filter && changelist_filter->nelts)
-    SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
-                                       pool));
-
-  SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
-                                      callbacks, callback_baton, TRUE,
-                                      pool, pool));
-
   if (reverse_order)
     processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
 
@@ -295,7 +275,6 @@ make_edit_baton(struct edit_baton_t **ed
   eb->ignore_ancestry = ignore_ancestry;
   eb->local_before_remote = reverse_order;
   eb->diff_pristine = use_text_base;
-  eb->changelist_hash = changelist_hash;
   eb->cancel_func = cancel_func;
   eb->cancel_baton = cancel_baton;
   eb->pool = pool;
@@ -409,7 +388,6 @@ svn_wc__diff_base_working_diff(svn_wc__d
                                const char *local_abspath,
                                const char *relpath,
                                svn_revnum_t revision,
-                               apr_hash_t *changelist_hash,
                                const svn_diff_tree_processor_t *processor,
                                void *processor_dir_baton,
                                svn_boolean_t diff_pristine,
@@ -436,12 +414,11 @@ svn_wc__diff_base_working_diff(svn_wc__d
   apr_hash_t *base_props;
   apr_hash_t *local_props;
   apr_array_header_t *prop_changes;
-  const char *changelist;
 
   SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
                                NULL, NULL, NULL, NULL, &working_checksum, NULL,
                                NULL, NULL, NULL, NULL, NULL, &recorded_size,
-                               &recorded_time, &changelist, NULL, NULL,
+                               &recorded_time, NULL, NULL, NULL,
                                &had_props, &props_mod, NULL, NULL, NULL,
                                db, local_abspath, scratch_pool, scratch_pool));
   checksum = working_checksum;
@@ -450,12 +427,6 @@ svn_wc__diff_base_working_diff(svn_wc__d
          || status == svn_wc__db_status_added
          || (status == svn_wc__db_status_deleted && diff_pristine));
 
-  /* If the item is not a member of a specified changelist (and there are
-     some specified changelists), skip it. */
-  if (changelist_hash && !svn_hash_gets(changelist_hash, changelist))
-    return SVN_NO_ERROR;
-
-
   if (status != svn_wc__db_status_normal)
     {
       SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
@@ -780,7 +751,6 @@ walk_local_nodes_diff(struct edit_baton_
                 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
                                                      child_relpath,
                                                      eb->processor, dir_baton,
-                                                     eb->changelist_hash,
                                                      eb->diff_pristine,
                                                      eb->cancel_func,
                                                      eb->cancel_baton,
@@ -790,7 +760,6 @@ walk_local_nodes_diff(struct edit_baton_
                                                     child_relpath,
                                                     depth_below_here,
                                                     eb->processor, dir_baton,
-                                                    eb->changelist_hash,
                                                     eb->diff_pristine,
                                                     eb->cancel_func,
                                                     eb->cancel_baton,
@@ -826,7 +795,6 @@ walk_local_nodes_diff(struct edit_baton_
                                                 db, child_abspath,
                                                 child_relpath,
                                                 eb->revnum,
-                                                eb->changelist_hash,
                                                 eb->processor, dir_baton,
                                                 eb->diff_pristine,
                                                 eb->cancel_func,
@@ -849,7 +817,6 @@ walk_local_nodes_diff(struct edit_baton_
                 SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
                                                      child_relpath,
                                                      eb->processor, dir_baton,
-                                                     eb->changelist_hash,
                                                      eb->diff_pristine,
                                                      eb->cancel_func,
                                                      eb->cancel_baton,
@@ -858,7 +825,6 @@ walk_local_nodes_diff(struct edit_baton_
                 SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
                                                      child_relpath, depth_below_here,
                                                      eb->processor, dir_baton,
-                                                     eb->changelist_hash,
                                                      eb->diff_pristine,
                                                      eb->cancel_func,
                                                      eb->cancel_baton,
@@ -870,13 +836,9 @@ walk_local_nodes_diff(struct edit_baton_
   if (compared)
     return SVN_NO_ERROR;
 
-    /* Check for local property mods on this directory, if we haven't
-     already reported them and we aren't changelist-filted.
-     ### it should be noted that we do not currently allow directories
-     ### to be part of changelists, so if a changelist is provided, the
-     ### changelist check will always fail. */
+  /* Check for local property mods on this directory, if we haven't
+     already reported them. */
   if (! skip
-      && ! eb->changelist_hash
       && ! in_anchor_not_target
       && props_mod)
     {
@@ -919,7 +881,6 @@ svn_wc__diff_local_only_file(svn_wc__db_
                              const char *relpath,
                              const svn_diff_tree_processor_t *processor,
                              void *processor_parent_baton,
-                             apr_hash_t *changelist_hash,
                              svn_boolean_t diff_pristine,
                              svn_cancel_func_t cancel_func,
                              void *cancel_baton,
@@ -932,7 +893,6 @@ svn_wc__diff_local_only_file(svn_wc__db_
   const svn_checksum_t *checksum;
   const char *original_repos_relpath;
   svn_revnum_t original_revision;
-  const char *changelist;
   svn_boolean_t had_props;
   svn_boolean_t props_mod;
   apr_hash_t *pristine_props;
@@ -948,7 +908,7 @@ svn_wc__diff_local_only_file(svn_wc__db_
                                NULL, NULL, NULL, NULL, &checksum, NULL,
                                &original_repos_relpath, NULL, NULL,
                                &original_revision, NULL, NULL, NULL,
-                               &changelist, NULL, NULL, &had_props,
+                               NULL, NULL, NULL, &had_props,
                                &props_mod, NULL, NULL, NULL,
                                db, local_abspath,
                                scratch_pool, scratch_pool));
@@ -959,10 +919,6 @@ svn_wc__diff_local_only_file(svn_wc__db_
              || (status == svn_wc__db_status_deleted && diff_pristine)));
 
 
-  if (changelist && changelist_hash
-      && !svn_hash_gets(changelist_hash, changelist))
-    return SVN_NO_ERROR;
-
   if (status == svn_wc__db_status_deleted)
     {
       assert(diff_pristine);
@@ -1065,7 +1021,6 @@ svn_wc__diff_local_only_dir(svn_wc__db_t
                             svn_depth_t depth,
                             const svn_diff_tree_processor_t *processor,
                             void *processor_parent_baton,
-                            apr_hash_t *changelist_hash,
                             svn_boolean_t diff_pristine,
                             svn_cancel_func_t cancel_func,
                             void *cancel_baton,
@@ -1094,6 +1049,7 @@ svn_wc__diff_local_only_dir(svn_wc__db_t
                                 processor_parent_baton,
                                 processor,
                                 scratch_pool, iterpool));
+  /* ### skip_children is not used */
 
   SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath,
                                         scratch_pool, iterpool));
@@ -1138,7 +1094,6 @@ svn_wc__diff_local_only_dir(svn_wc__db_t
           SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
                                                child_relpath,
                                                processor, pdb,
-                                               changelist_hash,
                                                diff_pristine,
                                                cancel_func, cancel_baton,
                                                scratch_pool));
@@ -1150,7 +1105,6 @@ svn_wc__diff_local_only_dir(svn_wc__db_t
               SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
                                                   child_relpath, depth_below_here,
                                                   processor, pdb,
-                                                  changelist_hash,
                                                   diff_pristine,
                                                   cancel_func, cancel_baton,
                                                   iterpool));
@@ -1246,7 +1200,6 @@ handle_local_only(struct dir_baton_t *pb
                       svn_relpath_join(pb->relpath, name, scratch_pool),
                       repos_delete ? svn_depth_infinity : depth,
                       eb->processor, pb->pdb,
-                      eb->changelist_hash,
                       eb->diff_pristine,
                       eb->cancel_func, eb->cancel_baton,
                       scratch_pool));
@@ -1257,7 +1210,6 @@ handle_local_only(struct dir_baton_t *pb
                       svn_dirent_join(pb->local_abspath, name, scratch_pool),
                       svn_relpath_join(pb->relpath, name, scratch_pool),
                       eb->processor, pb->pdb,
-                      eb->changelist_hash,
                       eb->diff_pristine,
                       eb->cancel_func, eb->cancel_baton,
                       scratch_pool));
@@ -2032,7 +1984,14 @@ close_file(void *file_baton,
   const char *repos_file;
   apr_hash_t *repos_props;
 
-  if (!fb->skip && expected_md5_digest != NULL)
+  if (fb->skip)
+    {
+      svn_pool_destroy(fb->pool); /* destroys scratch_pool and fb */
+      SVN_ERR(maybe_done(pb));
+      return SVN_NO_ERROR;
+    }
+
+  if (expected_md5_digest != NULL)
     {
       svn_checksum_t *expected_checksum;
       const svn_checksum_t *result_checksum;
@@ -2087,11 +2046,7 @@ close_file(void *file_baton,
       }
   }
 
-  if (fb->skip)
-    {
-      /* Diff processor requested skipping information */
-    }
-  else if (fb->repos_only)
+  if (fb->repos_only)
     {
       SVN_ERR(eb->processor->file_deleted(fb->relpath,
                                           fb->left_src,
@@ -2271,6 +2226,7 @@ svn_wc__get_diff_editor(const svn_delta_
   struct svn_wc__shim_fetch_baton_t *sfb;
   svn_delta_shim_callbacks_t *shim_callbacks =
                                 svn_delta_shim_callbacks_default(result_pool);
+  const svn_diff_tree_processor_t *diff_processor;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
 
@@ -2278,12 +2234,28 @@ svn_wc__get_diff_editor(const svn_delta_
   if (use_git_diff_format)
     show_copies_as_adds = TRUE;
 
+  SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
+                                      callbacks, callback_baton, TRUE,
+                                      result_pool, scratch_pool));
+
+  /* Apply changelist filtering to the output */
+  if (changelist_filter && changelist_filter->nelts)
+    {
+      apr_hash_t *changelist_hash;
+
+      SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
+                                         result_pool));
+      diff_processor = svn_wc__changelist_filter_tree_processor_create(
+                         diff_processor, wc_ctx, anchor_abspath,
+                         changelist_hash, result_pool);
+    }
+
   SVN_ERR(make_edit_baton(&eb,
                           wc_ctx->db,
                           anchor_abspath, target,
-                          callbacks, callback_baton,
+                          diff_processor,
                           depth, ignore_ancestry, show_copies_as_adds,
-                          use_text_base, reverse_order, changelist_filter,
+                          use_text_base, reverse_order,
                           cancel_func, cancel_baton,
                           result_pool));
 
@@ -2749,3 +2721,329 @@ svn_wc__wrap_diff_callbacks(const svn_di
   *diff_processor = processor;
   return SVN_NO_ERROR;
 }
+
+/* =====================================================================
+ * A tree processor filter that filters by changelist membership
+ * =====================================================================
+ *
+ * The current implementation queries the WC for the changelist of each
+ * file as it comes through, and sets the 'skip' flag for a non-matching
+ * file.
+ *
+ * (It doesn't set the 'skip' flag for a directory, as we need to receive
+ * the changed/added/deleted/closed call to know when it is closed, in
+ * order to preserve the strict open-close semantics for the wrapped tree
+ * processor.)
+ *
+ * It passes on the opening and closing of every directory, even if there
+ * are no file changes to be passed on inside that directory.
+ */
+
+typedef struct filter_tree_baton_t
+{
+  const svn_diff_tree_processor_t *processor;
+  svn_wc_context_t *wc_ctx;
+  /* WC path of the root of the diff (where relpath = "") */
+  const char *root_local_abspath;
+  /* Hash whose keys are const char * changelist names. */
+  apr_hash_t *changelist_hash;
+} filter_tree_baton_t;
+
+static svn_error_t *
+filter_dir_opened(void **new_dir_baton,
+                  svn_boolean_t *skip,
+                  svn_boolean_t *skip_children,
+                  const char *relpath,
+                  const svn_diff_source_t *left_source,
+                  const svn_diff_source_t *right_source,
+                  const svn_diff_source_t *copyfrom_source,
+                  void *parent_dir_baton,
+                  const svn_diff_tree_processor_t *processor,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
+                                    relpath,
+                                    left_source, right_source,
+                                    copyfrom_source,
+                                    parent_dir_baton,
+                                    fb->processor,
+                                    result_pool, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_added(const char *relpath,
+                 const svn_diff_source_t *copyfrom_source,
+                 const svn_diff_source_t *right_source,
+                 /*const*/ apr_hash_t *copyfrom_props,
+                 /*const*/ apr_hash_t *right_props,
+                 void *dir_baton,
+                 const svn_diff_tree_processor_t *processor,
+                 apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->dir_closed(relpath,
+                                    NULL,
+                                    right_source,
+                                    dir_baton,
+                                    fb->processor,
+                                    scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_deleted(const char *relpath,
+                   const svn_diff_source_t *left_source,
+                   /*const*/ apr_hash_t *left_props,
+                   void *dir_baton,
+                   const svn_diff_tree_processor_t *processor,
+                   apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->dir_closed(relpath,
+                                    left_source,
+                                    NULL,
+                                    dir_baton,
+                                    fb->processor,
+                                    scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_changed(const char *relpath,
+                   const svn_diff_source_t *left_source,
+                   const svn_diff_source_t *right_source,
+                   /*const*/ apr_hash_t *left_props,
+                   /*const*/ apr_hash_t *right_props,
+                   const apr_array_header_t *prop_changes,
+                   void *dir_baton,
+                   const struct svn_diff_tree_processor_t *processor,
+                   apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->dir_closed(relpath,
+                                    left_source,
+                                    right_source,
+                                    dir_baton,
+                                    fb->processor,
+                                    scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_closed(const char *relpath,
+                  const svn_diff_source_t *left_source,
+                  const svn_diff_source_t *right_source,
+                  void *dir_baton,
+                  const svn_diff_tree_processor_t *processor,
+                  apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->dir_closed(relpath,
+                                    left_source,
+                                    right_source,
+                                    dir_baton,
+                                    fb->processor,
+                                    scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_opened(void **new_file_baton,
+                   svn_boolean_t *skip,
+                   const char *relpath,
+                   const svn_diff_source_t *left_source,
+                   const svn_diff_source_t *right_source,
+                   const svn_diff_source_t *copyfrom_source,
+                   void *dir_baton,
+                   const svn_diff_tree_processor_t *processor,
+                   apr_pool_t *result_pool,
+                   apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+  const char *local_abspath
+    = svn_dirent_join(fb->root_local_abspath, relpath, scratch_pool);
+
+  /* Skip if not a member of a given changelist */
+  if (! svn_wc__changelist_match(fb->wc_ctx, local_abspath,
+                                 fb->changelist_hash, scratch_pool))
+    {
+      *skip = TRUE;
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(fb->processor->file_opened(new_file_baton,
+                                     skip,
+                                     relpath,
+                                     left_source,
+                                     right_source,
+                                     copyfrom_source,
+                                     dir_baton,
+                                     fb->processor,
+                                     result_pool,
+                                     scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_added(const char *relpath,
+                  const svn_diff_source_t *copyfrom_source,
+                  const svn_diff_source_t *right_source,
+                  const char *copyfrom_file,
+                  const char *right_file,
+                  /*const*/ apr_hash_t *copyfrom_props,
+                  /*const*/ apr_hash_t *right_props,
+                  void *file_baton,
+                  const svn_diff_tree_processor_t *processor,
+                  apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->file_added(relpath,
+                                    copyfrom_source,
+                                    right_source,
+                                    copyfrom_file,
+                                    right_file,
+                                    copyfrom_props,
+                                    right_props,
+                                    file_baton,
+                                    fb->processor,
+                                    scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_deleted(const char *relpath,
+                    const svn_diff_source_t *left_source,
+                    const char *left_file,
+                    /*const*/ apr_hash_t *left_props,
+                    void *file_baton,
+                    const svn_diff_tree_processor_t *processor,
+                    apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->file_deleted(relpath,
+                                      left_source,
+                                      left_file,
+                                      left_props,
+                                      file_baton,
+                                      fb->processor,
+                                      scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_changed(const char *relpath,
+                    const svn_diff_source_t *left_source,
+                    const svn_diff_source_t *right_source,
+                    const char *left_file,
+                    const char *right_file,
+                    /*const*/ apr_hash_t *left_props,
+                    /*const*/ apr_hash_t *right_props,
+                    svn_boolean_t file_modified,
+                    const apr_array_header_t *prop_changes,
+                    void *file_baton,
+                    const svn_diff_tree_processor_t *processor,
+                    apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->file_changed(relpath,
+                                      left_source,
+                                      right_source,
+                                      left_file,
+                                      right_file,
+                                      left_props,
+                                      right_props,
+                                      file_modified,
+                                      prop_changes,
+                                      file_baton,
+                                      fb->processor,
+                                      scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_closed(const char *relpath,
+                   const svn_diff_source_t *left_source,
+                   const svn_diff_source_t *right_source,
+                   void *file_baton,
+                   const svn_diff_tree_processor_t *processor,
+                   apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->file_closed(relpath,
+                                     left_source,
+                                     right_source,
+                                     file_baton,
+                                     fb->processor,
+                                     scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_node_absent(const char *relpath,
+                   void *dir_baton,
+                   const svn_diff_tree_processor_t *processor,
+                   apr_pool_t *scratch_pool)
+{
+  struct filter_tree_baton_t *fb = processor->baton;
+
+  SVN_ERR(fb->processor->node_absent(relpath,
+                                     dir_baton,
+                                     fb->processor,
+                                     scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+const svn_diff_tree_processor_t *
+svn_wc__changelist_filter_tree_processor_create(
+                                const svn_diff_tree_processor_t *processor,
+                                svn_wc_context_t *wc_ctx,
+                                const char *root_local_abspath,
+                                apr_hash_t *changelist_hash,
+                                apr_pool_t *result_pool)
+{
+  struct filter_tree_baton_t *fb;
+  svn_diff_tree_processor_t *filter;
+
+  if (! changelist_hash)
+    return processor;
+
+  fb = apr_pcalloc(result_pool, sizeof(*fb));
+  fb->processor = processor;
+  fb->wc_ctx = wc_ctx;
+  fb->root_local_abspath = root_local_abspath;
+  fb->changelist_hash = changelist_hash;
+
+  filter = svn_diff__tree_processor_create(fb, result_pool);
+  filter->dir_opened   = filter_dir_opened;
+  filter->dir_added    = filter_dir_added;
+  filter->dir_deleted  = filter_dir_deleted;
+  filter->dir_changed  = filter_dir_changed;
+  filter->dir_closed   = filter_dir_closed;
+
+  filter->file_opened   = filter_file_opened;
+  filter->file_added    = filter_file_added;
+  filter->file_deleted  = filter_file_deleted;
+  filter->file_changed  = filter_file_changed;
+  filter->file_closed   = filter_file_closed;
+
+  filter->node_absent   = filter_node_absent;
+
+  return filter;
+}
+

Modified: subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff_local.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff_local.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff_local.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/libsvn_wc/diff_local.c Sun Dec 14 11:52:14 2014
@@ -92,9 +92,6 @@ struct diff_baton
   /* Should this diff not compare copied files with their source? */
   svn_boolean_t show_copies_as_adds;
 
-  /* Hash whose keys are const char * changelist names. */
-  apr_hash_t *changelist_hash;
-
   /* Cancel function/baton */
   svn_cancel_func_t cancel_func;
   void *cancel_baton;
@@ -252,11 +249,6 @@ diff_status_callback(void *baton,
   if (eb->cur && eb->cur->skip_children)
     return SVN_NO_ERROR;
 
-  if (eb->changelist_hash != NULL
-      && (!status->changelist
-          || ! svn_hash_gets(eb->changelist_hash, status->changelist)))
-    return SVN_NO_ERROR; /* Filtered via changelist */
-
   /* This code does about the same thing as the inner body of
      walk_local_nodes_diff() in diff_editor.c, except that
      it is already filtered by the status walker, doesn't have to
@@ -361,7 +353,6 @@ diff_status_callback(void *baton,
             SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath,
                                                    child_relpath,
                                                    SVN_INVALID_REVNUM,
-                                                   eb->changelist_hash,
                                                    eb->processor,
                                                    eb->cur
                                                         ? eb->cur->baton
@@ -405,7 +396,6 @@ diff_status_callback(void *baton,
                                                child_relpath,
                                                eb->processor,
                                                eb->cur ? eb->cur->baton : NULL,
-                                               eb->changelist_hash,
                                                FALSE,
                                                eb->cancel_func,
                                                eb->cancel_baton,
@@ -415,7 +405,6 @@ diff_status_callback(void *baton,
                                               child_relpath, depth_below_here,
                                               eb->processor,
                                               eb->cur ? eb->cur->baton : NULL,
-                                              eb->changelist_hash,
                                               FALSE,
                                               eb->cancel_func,
                                               eb->cancel_baton,
@@ -482,16 +471,24 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx,
     processor = svn_diff__tree_processor_copy_as_changed_create(processor,
                                                                 scratch_pool);
 
+  /* Apply changelist filtering to the output */
+  if (changelist_filter && changelist_filter->nelts)
+    {
+      apr_hash_t *changelist_hash;
+
+      SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
+                                         scratch_pool));
+      processor = svn_wc__changelist_filter_tree_processor_create(
+                    processor, wc_ctx, local_abspath,
+                    changelist_hash, scratch_pool);
+    }
+
   eb.db = wc_ctx->db;
   eb.processor = processor;
   eb.ignore_ancestry = ignore_ancestry;
   eb.show_copies_as_adds = show_copies_as_adds;
   eb.pool = scratch_pool;
 
-  if (changelist_filter && changelist_filter->nelts)
-    SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter,
-                                       scratch_pool));
-
   if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry)
     get_all = TRUE; /* We need unmodified descendants of copies */
   else

Modified: subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/deleted-rev.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/deleted-rev.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/deleted-rev.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/deleted-rev.c Sun Dec 14 11:52:14 2014
@@ -56,6 +56,9 @@ dav_svn__get_deleted_rev_report(const da
   dav_error *derr = NULL;
 
   /* Sanity check. */
+  if (!resource->info->repos_path)
+    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                              "The request does not specify a repository path");
   ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
   if (ns == -1)
     return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,

Modified: subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/file-revs.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/file-revs.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/file-revs.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/file-revs.c Sun Dec 14 11:52:14 2014
@@ -254,6 +254,9 @@ dav_svn__file_revs_report(const dav_reso
   arb.repos = resource->info->repos;
 
   /* Sanity check. */
+  if (!resource->info->repos_path)
+    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                              "The request does not specify a repository path");
   ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
   /* ### This is done on other places, but the document element is
      in this namespace, so is this necessary at all? */

Modified: subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/get-location-segments.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/get-location-segments.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/get-location-segments.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/get-location-segments.c Sun Dec 14 11:52:14 2014
@@ -123,6 +123,9 @@ dav_svn__get_location_segments_report(co
   struct location_segment_baton location_segment_baton;
 
   /* Sanity check. */
+  if (!resource->info->repos_path)
+    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                              "The request does not specify a repository path");
   ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
   if (ns == -1)
     {

Modified: subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/get-locations.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/get-locations.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/get-locations.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/get-locations.c Sun Dec 14 11:52:14 2014
@@ -106,6 +106,9 @@ dav_svn__get_locations_report(const dav_
                                       sizeof(svn_revnum_t));
 
   /* Sanity check. */
+  if (!resource->info->repos_path)
+    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                              "The request does not specify a repository path");
   ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
   if (ns == -1)
     {

Modified: subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/inherited-props.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/inherited-props.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/inherited-props.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/inherited-props.c Sun Dec 14 11:52:14 2014
@@ -63,6 +63,9 @@ dav_svn__get_inherited_props_report(cons
   apr_pool_t *iterpool;
 
   /* Sanity check. */
+  if (!resource->info->repos_path)
+    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                              "The request does not specify a repository path");
   ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
   if (ns == -1)
     {

Modified: subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/log.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/log.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/log.c Sun Dec 14 11:52:14 2014
@@ -307,6 +307,9 @@ dav_svn__log_report(const dav_resource *
     = apr_array_make(resource->pool, 1, sizeof(const char *));
 
   /* Sanity check. */
+  if (!resource->info->repos_path)
+    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                              "The request does not specify a repository path");
   ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
   if (ns == -1)
     {

Modified: subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/mergeinfo.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/mergeinfo.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/reports/mergeinfo.c Sun Dec 14 11:52:14 2014
@@ -67,6 +67,9 @@ dav_svn__get_mergeinfo_report(const dav_
     = apr_array_make(resource->pool, 0, sizeof(const char *));
 
   /* Sanity check. */
+  if (!resource->info->repos_path)
+    return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0,
+                              "The request does not specify a repository path");
   ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE);
   if (ns == -1)
     {

Modified: subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/repos.c?rev=1645441&r1=1645440&r2=1645441&view=diff
==============================================================================
--- subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/repos.c (original)
+++ subversion/branches/javahl-1.8-extensions/subversion/mod_dav_svn/repos.c Sun Dec 14 11:52:14 2014
@@ -508,6 +508,9 @@ parse_vtxnstub_uri(dav_resource_combined
   if (parse_txnstub_uri(comb, path, label, use_checked_in))
     return TRUE;
 
+  if (!comb->priv.root.txn_name)
+    return TRUE;
+
   comb->priv.root.vtxn_name = comb->priv.root.txn_name;
   comb->priv.root.txn_name = dav_svn__get_txn(comb->priv.repos,
                                               comb->priv.root.vtxn_name);
@@ -576,6 +579,9 @@ parse_vtxnroot_uri(dav_resource_combined
   if (parse_txnroot_uri(comb, path, label, use_checked_in))
     return TRUE;
 
+  if (!comb->priv.root.txn_name)
+    return TRUE;
+
   comb->priv.root.vtxn_name = comb->priv.root.txn_name;
   comb->priv.root.txn_name = dav_svn__get_txn(comb->priv.repos,
                                               comb->priv.root.vtxn_name);
@@ -921,6 +927,10 @@ prep_working(dav_resource_combined *comb
      point. */
   if (txn_name == NULL)
     {
+      if (!comb->priv.root.activity_id)
+        return dav_svn__new_error(comb->res.pool, HTTP_BAD_REQUEST, 0,
+                                  "The request did not specify an activity ID");
+
       txn_name = dav_svn__get_txn(comb->priv.repos,
                                   comb->priv.root.activity_id);
       if (txn_name == NULL)
@@ -1031,8 +1041,13 @@ prep_working(dav_resource_combined *comb
 static dav_error *
 prep_activity(dav_resource_combined *comb)
 {
-  const char *txn_name = dav_svn__get_txn(comb->priv.repos,
-                                          comb->priv.root.activity_id);
+  const char *txn_name;
+
+  if (!comb->priv.root.activity_id)
+    return dav_svn__new_error(comb->res.pool, HTTP_BAD_REQUEST, 0,
+                              "The request did not specify an activity ID");
+
+  txn_name = dav_svn__get_txn(comb->priv.repos, comb->priv.root.activity_id);
 
   comb->priv.root.txn_name = txn_name;
   comb->res.exists = txn_name != NULL;
@@ -4144,7 +4159,9 @@ typedef struct walker_ctx_t {
 
 
 static dav_error *
-do_walk(walker_ctx_t *ctx, int depth)
+do_walk(walker_ctx_t *ctx,
+        int depth,
+        apr_pool_t *scratch_pool)
 {
   const dav_walk_params *params = ctx->params;
   int isdir = ctx->res.collection;
@@ -4217,19 +4234,19 @@ do_walk(walker_ctx_t *ctx, int depth)
                            svn_log__get_dir(ctx->info.repos_path,
                                             ctx->info.root.rev,
                                             TRUE, FALSE, SVN_DIRENT_ALL,
-                                            params->pool));
+                                            scratch_pool));
 
   /* fetch this collection's children */
   serr = svn_fs_dir_entries(&children, ctx->info.root.root,
-                            ctx->info.repos_path, params->pool);
+                            ctx->info.repos_path, scratch_pool);
   if (serr != NULL)
     return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
                                 "could not fetch collection members",
                                 params->pool);
 
   /* iterate over the children in this collection */
-  iterpool = svn_pool_create(params->pool);
-  for (hi = apr_hash_first(params->pool, children); hi; hi = apr_hash_next(hi))
+  iterpool = svn_pool_create(scratch_pool);
+  for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
     {
       const void *key;
       apr_ssize_t klen;
@@ -4282,7 +4299,7 @@ do_walk(walker_ctx_t *ctx, int depth)
           ctx->res.uri = ctx->uri->data;
 
           /* recurse on this collection */
-          err = do_walk(ctx, depth - 1);
+          err = do_walk(ctx, depth - 1, iterpool);
           if (err != NULL)
             return err;
 
@@ -4364,7 +4381,7 @@ walk(const dav_walk_params *params, int
   /* ### is the root already/always open? need to verify */
 
   /* always return the error, and any/all multistatus responses */
-  err = do_walk(&ctx, depth);
+  err = do_walk(&ctx, depth, params->pool);
   *response = ctx.wres.response;
 
   return err;