You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2012/05/16 22:32:54 UTC

svn commit: r1339349 [7/37] - in /subversion/branches/fix-rdump-editor: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/client-side/vim/ contrib/server-side/ notes/ notes/api-errata...

Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_client/diff.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_client/diff.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_client/diff.c Wed May 16 20:32:43 2012
@@ -708,8 +708,7 @@ display_prop_diffs(const apr_array_heade
         if (!val_has_eol)
           {
             const char *s = "\\ No newline at end of property" APR_EOL_STR;
-            apr_size_t len = strlen(s);
-            SVN_ERR(svn_stream_write(outstream, s, &len));
+            SVN_ERR(svn_stream_puts(outstream, s));
           }
       }
     }
@@ -778,7 +777,10 @@ struct diff_cmd_baton {
   const char *relative_to_dir;
 
   /* Whether property differences are ignored. */
-  svn_boolean_t ignore_prop_diff;
+  svn_boolean_t ignore_properties;
+
+  /* Whether to show only property changes. */
+  svn_boolean_t properties_only;
 
   /* Whether we're producing a git-style diff. */
   svn_boolean_t use_git_diff_format;
@@ -805,6 +807,17 @@ struct diff_cmd_baton {
 };
 
 
+/* A helper function that marks a path as visited. It copies PATH
+ * into the correct pool before referencing it from the hash table. */
+static void
+mark_path_as_visited(struct diff_cmd_baton *diff_cmd_baton, const char *path)
+{
+  const char *p;
+
+  p = apr_pstrdup(apr_hash_pool_get(diff_cmd_baton->visited_paths), path);
+  apr_hash_set(diff_cmd_baton->visited_paths, p, APR_HASH_KEY_STRING, p);
+}
+
 /* An helper for diff_dir_props_changed, diff_file_changed and diff_file_added
  */
 static svn_error_t *
@@ -821,7 +834,7 @@ diff_props_changed(svn_wc_notify_state_t
   svn_boolean_t show_diff_header;
 
   /* If property differences are ignored, there's nothing to do. */
-  if (diff_cmd_baton->ignore_prop_diff)
+  if (diff_cmd_baton->ignore_properties)
     return SVN_NO_ERROR;
 
   SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
@@ -855,8 +868,7 @@ diff_props_changed(svn_wc_notify_state_t
       /* We've printed the diff header so now we can mark the path as
        * visited. */
       if (show_diff_header)
-        apr_hash_set(diff_cmd_baton->visited_paths, path,
-                     APR_HASH_KEY_STRING, path);
+        mark_path_as_visited(diff_cmd_baton, path);
     }
 
   if (state)
@@ -920,6 +932,10 @@ diff_content_changed(const char *path,
   const char *path1 = diff_cmd_baton->orig_path_1;
   const char *path2 = diff_cmd_baton->orig_path_2;
 
+  /* If only property differences are shown, there's nothing to do. */
+  if (diff_cmd_baton->properties_only)
+    return SVN_NO_ERROR;
+
   /* Generate the diff headers. */
   SVN_ERR(adjust_paths_for_diff_labels(&path, &path1, &path2,
                                        rel_to_dir, subpool));
@@ -1072,9 +1088,7 @@ diff_content_changed(const char *path,
                      subpool));
 
           /* We have a printed a diff for this path, mark it as visited. */
-          apr_hash_set(diff_cmd_baton->visited_paths, path,
-                       APR_HASH_KEY_STRING, path);
-
+          mark_path_as_visited(diff_cmd_baton, path);
         }
     }
 
@@ -1401,7 +1415,8 @@ convert_to_url(const char **url,
 
 /** Check if paths PATH_OR_URL1 and PATH_OR_URL2 are urls and if the
  * revisions REVISION1 and REVISION2 are local. If PEG_REVISION is not
- * unspecified, ensure that at least one of the two revisions is non-local.
+ * unspecified, ensure that at least one of the two revisions is not
+ * BASE or WORKING.
  * If PATH_OR_URL1 can only be found in the repository, set *IS_REPOS1
  * to TRUE. If PATH_OR_URL2 can only be found in the repository, set
  * *IS_REPOS2 to TRUE. */
@@ -1422,8 +1437,8 @@ check_paths(svn_boolean_t *is_repos1,
     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
                             _("Not all required revisions are specified"));
 
-  /* Revisions can be said to be local or remote.  BASE and WORKING,
-     for example, are local.  */
+  /* Revisions can be said to be local or remote.
+   * BASE and WORKING are local revisions.  */
   is_local_rev1 =
     ((revision1->kind == svn_opt_revision_base)
      || (revision1->kind == svn_opt_revision_working));
@@ -1431,25 +1446,18 @@ check_paths(svn_boolean_t *is_repos1,
     ((revision2->kind == svn_opt_revision_base)
      || (revision2->kind == svn_opt_revision_working));
 
-  if (peg_revision->kind != svn_opt_revision_unspecified)
-    {
-      if (is_local_rev1 && is_local_rev2)
-        return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
-                                _("At least one revision must be non-local "
-                                  "for a pegged diff"));
-
-      *is_repos1 = ! is_local_rev1;
-      *is_repos2 = ! is_local_rev2;
-    }
-  else
-    {
-      /* Working copy paths with non-local revisions get turned into
-         URLs.  We don't do that here, though.  We simply record that it
-         needs to be done, which is information that helps us choose our
-         diff helper function.  */
-      *is_repos1 = ! is_local_rev1 || svn_path_is_url(path_or_url1);
-      *is_repos2 = ! is_local_rev2 || svn_path_is_url(path_or_url2);
-    }
+  if (peg_revision->kind != svn_opt_revision_unspecified &&
+      is_local_rev1 && is_local_rev2)
+    return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
+                            _("At least one revision must be something other "
+                              "than BASE or WORKING when diffing a URL"));
+
+  /* Working copy paths with non-local revisions get turned into
+     URLs.  We don't do that here, though.  We simply record that it
+     needs to be done, which is information that helps us choose our
+     diff helper function.  */
+  *is_repos1 = ! is_local_rev1 || svn_path_is_url(path_or_url1);
+  *is_repos2 = ! is_local_rev2 || svn_path_is_url(path_or_url2);
 
   return SVN_NO_ERROR;
 }
@@ -1494,11 +1502,52 @@ check_diff_target_exists(const char *url
   return SVN_NO_ERROR;
 }
 
+
+/* Return in *RESOLVED_URL the URL which PATH_OR_URL@PEG_REVISION has in
+ * REVISION. If the object has no location in REVISION, set *RESOLVED_URL
+ * to NULL. */
+static svn_error_t *
+resolve_pegged_diff_target_url(const char **resolved_url,
+                               svn_ra_session_t *ra_session,
+                               const char *path_or_url,
+                               const svn_opt_revision_t *peg_revision,
+                               const svn_opt_revision_t *revision,
+                               svn_client_ctx_t *ctx,
+                               apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+
+  /* Check if the PATH_OR_URL exists at REVISION. */
+  err = svn_client__repos_locations(resolved_url, NULL,
+                                    NULL, NULL,
+                                    ra_session,
+                                    path_or_url,
+                                    peg_revision,
+                                    revision,
+                                    NULL,
+                                    ctx, scratch_pool);
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES ||
+          err->apr_err == SVN_ERR_FS_NOT_FOUND)
+        {
+          svn_error_clear(err);
+          *resolved_url = NULL;
+        }
+      else
+        return svn_error_trace(err);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /** Prepare a repos repos diff between PATH_OR_URL1 and
  * PATH_OR_URL2@PEG_REVISION, in the revision range REVISION1:REVISION2.
  * Return URLs and peg revisions in *URL1, *REV1 and in *URL2, *REV2.
  * Return suitable anchors in *ANCHOR1 and *ANCHOR2, and targets in
  * *TARGET1 and *TARGET2, based on *URL1 and *URL2.
+ * Indicate the corresponding node kinds in *KIND1 and *KIND2, and verify
+ * that at least one of the diff targets exists.
  * Set *BASE_PATH corresponding to the URL opened in the new *RA_SESSION
  * which is pointing at *ANCHOR1.
  * Use client context CTX. Do all allocations in POOL. */
@@ -1512,6 +1561,8 @@ diff_prepare_repos_repos(const char **ur
                          const char **anchor2,
                          const char **target1,
                          const char **target2,
+                         svn_node_kind_t *kind1,
+                         svn_node_kind_t *kind2,
                          svn_ra_session_t **ra_session,
                          svn_client_ctx_t *ctx,
                          const char *path_or_url1,
@@ -1521,7 +1572,6 @@ diff_prepare_repos_repos(const char **ur
                          const svn_opt_revision_t *peg_revision,
                          apr_pool_t *pool)
 {
-  svn_node_kind_t kind1, kind2;
   const char *abspath_or_url2;
   const char *abspath_or_url1;
 
@@ -1560,52 +1610,56 @@ diff_prepare_repos_repos(const char **ur
      actual URLs will be. */
   if (peg_revision->kind != svn_opt_revision_unspecified)
     {
-      svn_error_t *err;
+      const char *resolved_url1;
+      const char *resolved_url2;
 
-      err = svn_client__repos_locations(url1, NULL,
-                                        url2, NULL,
-                                        *ra_session,
-                                        path_or_url2,
-                                        peg_revision,
-                                        revision1,
-                                        revision2,
-                                        ctx, pool);
-      if (err)
+      SVN_ERR(resolve_pegged_diff_target_url(&resolved_url2, *ra_session,
+                                             path_or_url2, peg_revision,
+                                             revision2, ctx, pool));
+
+      SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool));
+      SVN_ERR(resolve_pegged_diff_target_url(&resolved_url1, *ra_session,
+                                             path_or_url1, peg_revision,
+                                             revision1, ctx, pool));
+
+      /* Either or both URLs might have changed as a result of resolving
+       * the PATH_OR_URL@PEG_REVISION's history. If only one of the URLs
+       * could be resolved, use the same URL for URL1 and URL2, so we can
+       * show a diff that adds or removes the object (see issue #4153). */
+      if (resolved_url2)
         {
-          if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
-            {
-              /* Don't give up just yet. A missing path might translate
-               * into an addition in the diff. Below, we verify that each
-               * URL exists on at least one side of the diff. */
-              svn_error_clear(err);
-            }
-          else
-            return svn_error_trace(err);
+          *url2 = resolved_url2;
+          if (!resolved_url1)
+            *url1 = resolved_url2;
         }
-      else
+      if (resolved_url1)
         {
-          /* Reparent the session, since *URL2 might have changed as a result
-             the above call. */
-          SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool));
+          *url1 = resolved_url1;
+          if (!resolved_url2)
+            *url2 = resolved_url1;
         }
+
+      /* Reparent the session, since *URL2 might have changed as a result
+         the above call. */
+      SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool));
     }
 
   /* Resolve revision and get path kind for the second target. */
   SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx,
            (path_or_url2 == *url2) ? NULL : abspath_or_url2,
            *ra_session, revision2, pool));
-  SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, &kind2, pool));
+  SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, kind2, pool));
 
   /* Do the same for the first target. */
   SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool));
   SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx,
            (strcmp(path_or_url1, *url1) == 0) ? NULL : abspath_or_url1,
            *ra_session, revision1, pool));
-  SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, &kind1, pool));
+  SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, kind1, pool));
 
   /* Either both URLs must exist at their respective revisions,
    * or one of them may be missing from one side of the diff. */
-  if (kind1 == svn_node_none && kind2 == svn_node_none)
+  if (*kind1 == svn_node_none && *kind2 == svn_node_none)
     {
       if (strcmp(*url1, *url2) == 0)
         return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
@@ -1619,9 +1673,9 @@ diff_prepare_repos_repos(const char **ur
                                    "'%ld'"),
                                  *url1, *url2, *rev1, *rev2);
     }
-  else if (kind1 == svn_node_none)
+  else if (*kind1 == svn_node_none)
     SVN_ERR(check_diff_target_exists(*url1, *rev2, *rev1, *ra_session, pool));
-  else if (kind2 == svn_node_none)
+  else if (*kind2 == svn_node_none)
     SVN_ERR(check_diff_target_exists(*url2, *rev1, *rev2, *ra_session, pool));
 
   /* Choose useful anchors and targets for our two URLs. */
@@ -1629,57 +1683,9 @@ diff_prepare_repos_repos(const char **ur
   *anchor2 = *url2;
   *target1 = "";
   *target2 = "";
-  if ((kind1 == svn_node_none) || (kind2 == svn_node_none))
-    {
-      svn_node_kind_t kind;
-      const char *repos_root;
-      const char *new_anchor;
-      svn_revnum_t rev;
-
-      /* The diff target does not exist on one side of the diff.
-       * This can happen if the target was added or deleted within the
-       * revision range being diffed.
-       * However, we don't know how deep within a added/deleted subtree the
-       * diff target is. Find a common parent that exists on both sides of
-       * the diff and use it as anchor for the diff operation.
-       *
-       * ### This can fail due to authz restrictions (like in issue #3242).
-       * ### But it is the only option we have right now to try to get
-       * ### a usable diff in this situation. */
-
-      SVN_ERR(svn_ra_get_repos_root2(*ra_session, &repos_root, pool));
-
-      /* Since we already know that one of the URLs does exist,
-       * look for an existing parent of the URL which doesn't exist. */
-      new_anchor = (kind1 == svn_node_none ? *anchor1 : *anchor2);
-      rev = (kind1 == svn_node_none ? *rev1 : *rev2);
-      do
-        {
-          if (strcmp(new_anchor, repos_root) != 0)
-            {
-              new_anchor = svn_path_uri_decode(svn_uri_dirname(new_anchor,
-                                                               pool),
-                                               pool);
-              if (*base_path)
-                *base_path = svn_dirent_dirname(*base_path, pool);
-            }
 
-          SVN_ERR(svn_ra_reparent(*ra_session, new_anchor, pool));
-          SVN_ERR(svn_ra_check_path(*ra_session, "", rev, &kind, pool));
-
-        }
-      while (kind != svn_node_dir);
-      *anchor1 = *anchor2 = new_anchor;
-      /* Diff targets must be relative to the new anchor. */
-      *target1 = svn_uri_skip_ancestor(new_anchor, *url1, pool);
-      *target2 = svn_uri_skip_ancestor(new_anchor, *url2, pool);
-      SVN_ERR_ASSERT(*target1 && *target2);
-      if (kind1 == svn_node_none)
-        kind1 = svn_node_dir;
-      else
-        kind2 = svn_node_dir;
-    }
-  else if ((kind1 == svn_node_file) || (kind2 == svn_node_file))
+  /* If one of the targets is a file, use the parent directory as anchor. */
+  if (*kind1 == svn_node_file || *kind2 == svn_node_file)
     {
       svn_uri_split(anchor1, target1, *url1, pool);
       svn_uri_split(anchor2, target2, *url2, pool);
@@ -1731,6 +1737,478 @@ unsupported_diff_error(svn_error_t *chil
                             "that is not yet supported"));
 }
 
+/* Try to get properties for LOCAL_ABSPATH and return them in the property
+ * hash *PROPS. If there are no properties because LOCAL_ABSPATH is not
+ * versioned, return an empty property hash. */
+static svn_error_t *
+get_props(apr_hash_t **props,
+          const char *local_abspath,
+          svn_wc_context_t *wc_ctx,
+          apr_pool_t *result_pool,
+          apr_pool_t *scratch_pool)
+{
+  svn_error_t *err;
+
+  err = svn_wc_prop_list2(props, wc_ctx, local_abspath, result_pool,
+                          scratch_pool);
+  if (err)
+    {
+      if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
+          err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY ||
+          err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
+        {
+          svn_error_clear(err);
+          *props = apr_hash_make(result_pool);
+        }
+      else
+        return svn_error_trace(err);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Produce a diff between two arbitrary files at LOCAL_ABSPATH1 and
+ * LOCAL_ABSPATH2, using the diff callbacks from CALLBACKS.
+ * Use PATH as the name passed to diff callbacks.
+ * FILE1_IS_EMPTY and FILE2_IS_EMPTY are used as hints which diff callback
+ * function to use to compare the files (added/deleted/changed).
+ *
+ * If ORIGINAL_PROPS_OVERRIDE is not NULL, use it as original properties
+ * instead of reading properties from LOCAL_ABSPATH1. This is required when
+ * a file replaces a directory, where LOCAL_ABSPATH1 is an empty file that
+ * file content must be diffed against, but properties to diff against come
+ * from the replaced directory. */
+static svn_error_t *
+do_arbitrary_files_diff(const char *local_abspath1,
+                        const char *local_abspath2,
+                        const char *path,
+                        svn_boolean_t file1_is_empty,
+                        svn_boolean_t file2_is_empty,
+                        apr_hash_t *original_props_override,
+                        const svn_wc_diff_callbacks4_t *callbacks,
+                        struct diff_cmd_baton *diff_cmd_baton,
+                        svn_client_ctx_t *ctx,
+                        apr_pool_t *scratch_pool)
+{
+  apr_hash_t *original_props;
+  apr_hash_t *modified_props;
+  apr_array_header_t *prop_changes;
+  svn_string_t *original_mime_type = NULL;
+  svn_string_t *modified_mime_type = NULL;
+
+  if (ctx->cancel_func)
+    SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+
+  if (diff_cmd_baton->ignore_properties)
+    {
+      original_props = apr_hash_make(scratch_pool);
+      modified_props = apr_hash_make(scratch_pool);
+    }
+  else
+    {
+      /* Try to get properties from either file. It's OK if the files do not
+       * have properties, or if they are unversioned. */
+      if (original_props_override)
+        original_props = original_props_override;
+      else
+        SVN_ERR(get_props(&original_props, local_abspath1, ctx->wc_ctx,
+                          scratch_pool, scratch_pool));
+      SVN_ERR(get_props(&modified_props, local_abspath2, ctx->wc_ctx,
+                        scratch_pool, scratch_pool));
+    }
+
+  SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props,
+                         scratch_pool));
+
+  if (!diff_cmd_baton->force_binary)
+    {
+      /* Try to determine the mime-type of each file. */
+      original_mime_type = apr_hash_get(original_props, SVN_PROP_MIME_TYPE,
+                                        APR_HASH_KEY_STRING);
+      if (!file1_is_empty && !original_mime_type)
+        {
+          const char *mime_type;
+          SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1,
+                                          ctx->mimetypes_map, scratch_pool));
+
+          if (mime_type)
+            original_mime_type = svn_string_create(mime_type, scratch_pool);
+        }
+
+      modified_mime_type = apr_hash_get(modified_props, SVN_PROP_MIME_TYPE,
+                                        APR_HASH_KEY_STRING);
+      if (!file2_is_empty && !modified_mime_type)
+        {
+          const char *mime_type;
+          SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1,
+                                          ctx->mimetypes_map, scratch_pool));
+
+          if (mime_type)
+            modified_mime_type = svn_string_create(mime_type, scratch_pool);
+        }
+    }
+
+  /* Produce the diff. */
+  if (file1_is_empty && !file2_is_empty)
+    SVN_ERR(callbacks->file_added(NULL, NULL, NULL, path,
+                                  local_abspath1, local_abspath2,
+                                  /* ### TODO get real revision info
+                                   * for versioned files? */
+                                  SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
+                                  original_mime_type ?
+                                    original_mime_type->data : NULL,
+                                  modified_mime_type ?
+                                    modified_mime_type->data : NULL,
+                                  /* ### TODO get copyfrom? */
+                                  NULL, SVN_INVALID_REVNUM,
+                                  prop_changes, original_props,
+                                  diff_cmd_baton, scratch_pool));
+  else if (!file1_is_empty && file2_is_empty)
+    SVN_ERR(callbacks->file_deleted(NULL, NULL, path,
+                                    local_abspath1, local_abspath2,
+                                    original_mime_type ?
+                                      original_mime_type->data : NULL,
+                                    modified_mime_type ?
+                                      modified_mime_type->data : NULL,
+                                    original_props,
+                                    diff_cmd_baton, scratch_pool));
+  else
+    SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, path,
+                                    local_abspath1, local_abspath2,
+                                    /* ### TODO get real revision info
+                                     * for versioned files? */
+                                    SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
+                                    original_mime_type ?
+                                      original_mime_type->data : NULL,
+                                    modified_mime_type ?
+                                      modified_mime_type->data : NULL,
+                                    prop_changes, original_props,
+                                    diff_cmd_baton, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+struct arbitrary_diff_walker_baton {
+  /* The root directories of the trees being compared. */
+  const char *root1_abspath;
+  const char *root2_abspath;
+
+  /* TRUE if recursing within an added subtree of root2_abspath that
+   * does not exist in root1_abspath. */
+  svn_boolean_t recursing_within_added_subtree;
+
+  /* TRUE if recursing within an administrative (.i.e. .svn) directory. */
+  svn_boolean_t recursing_within_adm_dir;
+
+  /* The absolute path of the adm dir if RECURSING_WITHIN_ADM_DIR is TRUE.
+   * Else this is NULL.*/
+  const char *adm_dir_abspath;
+
+  /* A path to an empty file used for diffs that add/delete files. */
+  const char *empty_file_abspath;
+
+  const svn_wc_diff_callbacks4_t *callbacks;
+  struct diff_cmd_baton *callback_baton;
+  svn_client_ctx_t *ctx;
+  apr_pool_t *pool;
+} arbitrary_diff_walker_baton;
+
+/* Forward declaration needed because this function has a cyclic
+ * dependency with do_arbitrary_dirs_diff(). */
+static svn_error_t *
+arbitrary_diff_walker(void *baton, const char *local_abspath,
+                      const apr_finfo_t *finfo,
+                      apr_pool_t *scratch_pool);
+
+/* Produce a diff between two arbitrary directories at LOCAL_ABSPATH1 and
+ * LOCAL_ABSPATH2, using the provided diff callbacks to show file changes
+ * and, for versioned nodes, property changes.
+ *
+ * If ROOT_ABSPATH1 and ROOT_ABSPATH2 are not NULL, show paths in diffs
+ * relative to these roots, rather than relative to LOCAL_ABSPATH1 and
+ * LOCAL_ABSPATH2. This is needed when crawling a subtree that exists
+ * only within LOCAL_ABSPATH2. */
+static svn_error_t *
+do_arbitrary_dirs_diff(const char *local_abspath1,
+                       const char *local_abspath2,
+                       const char *root_abspath1,
+                       const char *root_abspath2,
+                       const svn_wc_diff_callbacks4_t *callbacks,
+                       struct diff_cmd_baton *callback_baton,
+                       svn_client_ctx_t *ctx,
+                       apr_pool_t *scratch_pool)
+{
+  apr_file_t *empty_file;
+  svn_node_kind_t kind1;
+
+  struct arbitrary_diff_walker_baton b;
+
+  /* If LOCAL_ABSPATH1 is not a directory, crawl LOCAL_ABSPATH2 instead
+   * and compare it to LOCAL_ABSPATH1, showing only additions.
+   * This case can only happen during recursion from arbitrary_diff_walker(),
+   * because do_arbitrary_nodes_diff() prevents this from happening at
+   * the root of the comparison. */
+  SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
+  b.recursing_within_added_subtree = (kind1 != svn_node_dir);
+
+  b.root1_abspath = root_abspath1 ? root_abspath1 : local_abspath1;
+  b.root2_abspath = root_abspath2 ? root_abspath2 : local_abspath2;
+  b.recursing_within_adm_dir = FALSE;
+  b.adm_dir_abspath = NULL;
+  b.callbacks = callbacks;
+  b.callback_baton = callback_baton;
+  b.ctx = ctx;
+  b.pool = scratch_pool;
+
+  SVN_ERR(svn_io_open_unique_file3(&empty_file, &b.empty_file_abspath,
+                                   NULL, svn_io_file_del_on_pool_cleanup,
+                                   scratch_pool, scratch_pool));
+
+  SVN_ERR(svn_io_dir_walk2(b.recursing_within_added_subtree ? local_abspath2
+                                                            : local_abspath1,
+                           0, arbitrary_diff_walker, &b, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* An implementation of svn_io_walk_func_t.
+ * Note: LOCAL_ABSPATH is the path being crawled and can be on either side
+ * of the diff depending on baton->recursing_within_added_subtree. */
+static svn_error_t *
+arbitrary_diff_walker(void *baton, const char *local_abspath,
+                      const apr_finfo_t *finfo,
+                      apr_pool_t *scratch_pool)
+{
+  struct arbitrary_diff_walker_baton *b = baton;
+  const char *local_abspath1;
+  const char *local_abspath2;
+  svn_node_kind_t kind1;
+  svn_node_kind_t kind2;
+  const char *child_relpath;
+  apr_hash_t *dirents1;
+  apr_hash_t *dirents2;
+  apr_hash_t *merged_dirents;
+  apr_array_header_t *sorted_dirents;
+  int i;
+  apr_pool_t *iterpool;
+
+  if (b->ctx->cancel_func)
+    SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
+
+  if (finfo->filetype != APR_DIR)
+    return SVN_NO_ERROR;
+
+  if (b->recursing_within_adm_dir)
+    {
+      if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath))
+        return SVN_NO_ERROR;
+      else
+        {
+          b->recursing_within_adm_dir = FALSE;
+          b->adm_dir_abspath = NULL;
+        }
+    }
+  else if (strcmp(svn_dirent_basename(local_abspath, scratch_pool),
+                  SVN_WC_ADM_DIR_NAME) == 0)
+    {
+      b->recursing_within_adm_dir = TRUE;
+      b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath);
+      return SVN_NO_ERROR;
+    }
+
+  if (b->recursing_within_added_subtree)
+    child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath);
+  else
+    child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath);
+  if (!child_relpath)
+    return SVN_NO_ERROR;
+
+  local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath,
+                                   scratch_pool);
+  SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
+
+  local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath,
+                                   scratch_pool);
+  SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool));
+
+  if (kind1 == svn_node_dir)
+    SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1,
+                                TRUE, /* only_check_type */
+                                scratch_pool, scratch_pool));
+  else
+    dirents1 = apr_hash_make(scratch_pool);
+
+  if (kind2 == svn_node_dir)
+    {
+      apr_hash_t *original_props;
+      apr_hash_t *modified_props;
+      apr_array_header_t *prop_changes;
+
+      /* Show any property changes for this directory. */
+      SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx,
+                        scratch_pool, scratch_pool));
+      SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx,
+                        scratch_pool, scratch_pool));
+      SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props,
+                             scratch_pool));
+      if (prop_changes->nelts > 0)
+        SVN_ERR(diff_props_changed(NULL, NULL, child_relpath,
+                                   b->recursing_within_added_subtree,
+                                   prop_changes, original_props,
+                                   b->callback_baton, scratch_pool));
+
+      /* Read directory entries. */
+      SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2,
+                                  TRUE, /* only_check_type */
+                                  scratch_pool, scratch_pool));
+    }
+  else
+    dirents2 = apr_hash_make(scratch_pool);
+
+  /* Compare dirents1 to dirents2 and show added/deleted/changed files. */
+  merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2,
+                                  NULL, NULL);
+  sorted_dirents = svn_sort__hash(merged_dirents,
+                                  svn_sort_compare_items_as_paths,
+                                  scratch_pool);
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < sorted_dirents->nelts; i++)
+    {
+      svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t);
+      const char *name = elt.key;
+      svn_io_dirent2_t *dirent1;
+      svn_io_dirent2_t *dirent2;
+      const char *child1_abspath;
+      const char *child2_abspath;
+
+      svn_pool_clear(iterpool);
+
+      if (b->ctx->cancel_func)
+        SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
+
+      if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0)
+        continue;
+
+      dirent1 = apr_hash_get(dirents1, name, APR_HASH_KEY_STRING);
+      if (!dirent1)
+        {
+          dirent1 = svn_io_dirent2_create(iterpool);
+          dirent1->kind = svn_node_none;
+        }
+      dirent2 = apr_hash_get(dirents2, name, APR_HASH_KEY_STRING);
+      if (!dirent2)
+        {
+          dirent2 = svn_io_dirent2_create(iterpool);
+          dirent2->kind = svn_node_none;
+        }
+
+      child1_abspath = svn_dirent_join(local_abspath1, name, iterpool);
+      child2_abspath = svn_dirent_join(local_abspath2, name, iterpool);
+
+      if (dirent1->special)
+        SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind,
+                                           iterpool));
+      if (dirent2->special)
+        SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind,
+                                           iterpool));
+
+      if (dirent1->kind == svn_node_dir &&
+          dirent2->kind == svn_node_dir)
+        continue;
+
+      /* Files that exist only in dirents1. */
+      if (dirent1->kind == svn_node_file &&
+          (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none))
+        SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath,
+                                        svn_relpath_join(child_relpath, name,
+                                                         iterpool),
+                                        FALSE, TRUE, NULL,
+                                        b->callbacks, b->callback_baton,
+                                        b->ctx, iterpool));
+
+      /* Files that exist only in dirents2. */
+      if (dirent2->kind == svn_node_file &&
+          (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none))
+        {
+          apr_hash_t *original_props;
+
+          SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx,
+                            scratch_pool, scratch_pool));
+          SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath,
+                                          svn_relpath_join(child_relpath, name,
+                                                           iterpool),
+                                          TRUE, FALSE, original_props,
+                                          b->callbacks, b->callback_baton,
+                                          b->ctx, iterpool));
+        }
+
+      /* Files that exist in dirents1 and dirents2. */
+      if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file)
+        SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath,
+                                        svn_relpath_join(child_relpath, name,
+                                                         iterpool),
+                                        FALSE, FALSE, NULL,
+                                        b->callbacks, b->callback_baton,
+                                        b->ctx, scratch_pool));
+
+      /* Directories that only exist in dirents2. These aren't crawled
+       * by this walker so we have to crawl them separately. */
+      if (dirent2->kind == svn_node_dir &&
+          (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none))
+        SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
+                                       b->root1_abspath, b->root2_abspath,
+                                       b->callbacks, b->callback_baton,
+                                       b->ctx, iterpool));
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+/* Produce a diff between two files or two directories at LOCAL_ABSPATH1
+ * and LOCAL_ABSPATH2, using the provided diff callbacks to show changes
+ * in files. The files and directories involved may be part of a working
+ * copy or they may be unversioned. For versioned files, show property
+ * changes, too. */
+static svn_error_t *
+do_arbitrary_nodes_diff(const char *local_abspath1,
+                        const char *local_abspath2,
+                        const svn_wc_diff_callbacks4_t *callbacks,
+                        struct diff_cmd_baton *callback_baton,
+                        svn_client_ctx_t *ctx,
+                        apr_pool_t *scratch_pool)
+{
+  svn_node_kind_t kind1;
+  svn_node_kind_t kind2;
+
+  SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
+  SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool));
+  if (kind1 != kind2)
+    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                             _("'%s' is not the same node kind as '%s'"),
+                             local_abspath1, local_abspath2);
+
+  if (kind1 == svn_node_file)
+    SVN_ERR(do_arbitrary_files_diff(local_abspath1, local_abspath2,
+                                    svn_dirent_basename(local_abspath2,
+                                                        scratch_pool),
+                                    FALSE, FALSE, NULL,
+                                    callbacks, callback_baton,
+                                    ctx, scratch_pool));
+  else if (kind1 == svn_node_dir)
+    SVN_ERR(do_arbitrary_dirs_diff(local_abspath1, local_abspath2,
+                                   NULL, NULL,
+                                   callbacks, callback_baton,
+                                   ctx, scratch_pool));
+  else
+    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                             _("'%s' is not a file or directory"),
+                             kind1 == svn_node_none ?
+                              local_abspath1 : local_abspath2);
+  return SVN_NO_ERROR;
+}
+
 
 /* Perform a diff between two working-copy paths.
 
@@ -1762,16 +2240,18 @@ diff_wc_wc(const char *path1,
 
   SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool));
 
-  /* Currently we support only the case where path1 and path2 are the
-     same path. */
   if ((strcmp(path1, path2) != 0)
       || (! ((revision1->kind == svn_opt_revision_base)
              && (revision2->kind == svn_opt_revision_working))))
-    return unsupported_diff_error
-      (svn_error_create
-       (SVN_ERR_INCORRECT_PARAMS, NULL,
-        _("Only diffs between a path's text-base "
-          "and its working files are supported at this time")));
+    {
+      const char *abspath2;
+
+      SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, pool));
+      return svn_error_trace(do_arbitrary_nodes_diff(abspath1, abspath2,
+                                                     callbacks,
+                                                     callback_baton,
+                                                     ctx, pool));
+    }
 
   /* Resolve named revisions to real numbers. */
   err = svn_client__get_revision_number(&callback_baton->revnum1, NULL,
@@ -1810,6 +2290,299 @@ diff_wc_wc(const char *path1,
   return SVN_NO_ERROR;
 }
 
+/* Create an array of regular properties in PROP_HASH, filtering entry-props
+ * and wc-props. Allocate the returned array in RESULT_POOL.
+ * Use SCRATCH_POOL for temporary allocations. */
+static apr_array_header_t *
+make_regular_props_array(apr_hash_t *prop_hash,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
+{
+  apr_array_header_t *regular_props;
+  apr_hash_index_t *hi;
+
+  regular_props = apr_array_make(result_pool, 0, sizeof(svn_prop_t));
+  for (hi = apr_hash_first(scratch_pool, prop_hash); hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *name = svn__apr_hash_index_key(hi);
+      svn_string_t *value = svn__apr_hash_index_val(hi);
+      svn_prop_kind_t prop_kind = svn_property_kind2(name);
+
+      if (prop_kind == svn_prop_regular_kind)
+        {
+          svn_prop_t *prop = apr_palloc(scratch_pool, sizeof(svn_prop_t));
+
+          prop->name = name;
+          prop->value = value;
+          APR_ARRAY_PUSH(regular_props, svn_prop_t) = *prop;
+        }
+    }
+
+  return regular_props;
+}
+
+/* Create a hash of regular properties from PROP_HASH, filtering entry-props
+ * and wc-props. Allocate the returned hash in RESULT_POOL.
+ * Use SCRATCH_POOL for temporary allocations. */
+static apr_hash_t *
+make_regular_props_hash(apr_hash_t *prop_hash,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool)
+{
+  apr_hash_t *regular_props;
+  apr_hash_index_t *hi;
+
+  regular_props = apr_hash_make(result_pool);
+  for (hi = apr_hash_first(scratch_pool, prop_hash); hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *name = svn__apr_hash_index_key(hi);
+      svn_string_t *value = svn__apr_hash_index_val(hi);
+      svn_prop_kind_t prop_kind = svn_property_kind2(name);
+
+      if (prop_kind == svn_prop_regular_kind)
+        apr_hash_set(regular_props, name, APR_HASH_KEY_STRING, value);
+    }
+
+  return regular_props;
+}
+
+/* Handle an added or deleted diff target file for a repos<->repos diff.
+ *
+ * Using the provided diff CALLBACKS and the CALLBACK_BATON, show the file
+ * TARGET@PEG_REVISION as added or deleted, depending on SHOW_DELETION.
+ * TARGET is a path relative to RA_SESSION's URL.
+ * REV1 and REV2 are the revisions being compared.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+diff_repos_repos_added_or_deleted_file(const char *target,
+                                       svn_revnum_t peg_revision,
+                                       svn_revnum_t rev1,
+                                       svn_revnum_t rev2,
+                                       svn_boolean_t show_deletion,
+                                      const char *empty_file,
+                                       const svn_wc_diff_callbacks4_t
+                                         *callbacks,
+                                       struct diff_cmd_baton *callback_baton,
+                                       svn_ra_session_t *ra_session,
+                                       apr_pool_t *scratch_pool)
+{
+  const char *file_abspath;
+  svn_stream_t *content;
+  apr_hash_t *prop_hash;
+
+  SVN_ERR(svn_stream_open_unique(&content, &file_abspath, NULL,
+                                 svn_io_file_del_on_pool_cleanup,
+                                 scratch_pool, scratch_pool));
+  SVN_ERR(svn_ra_get_file(ra_session, target, peg_revision, content, NULL,
+                          &prop_hash, scratch_pool));
+  SVN_ERR(svn_stream_close(content));
+
+  if (show_deletion)
+    {
+      SVN_ERR(callbacks->file_deleted(NULL, NULL,
+                                      target, file_abspath, empty_file,
+                                      apr_hash_get(prop_hash,
+                                                   SVN_PROP_MIME_TYPE,
+                                                   APR_HASH_KEY_STRING),
+                                      NULL,
+                                      make_regular_props_hash(
+                                        prop_hash, scratch_pool, scratch_pool),
+                                      callback_baton, scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(callbacks->file_added(NULL, NULL, NULL,
+                                    target, empty_file, file_abspath,
+                                    rev1, rev2, NULL,
+                                    apr_hash_get(prop_hash, SVN_PROP_MIME_TYPE,
+                                                 APR_HASH_KEY_STRING),
+                                    NULL, SVN_INVALID_REVNUM,
+                                    make_regular_props_array(prop_hash,
+                                                             scratch_pool,
+                                                             scratch_pool),
+                                    NULL, callback_baton, scratch_pool));
+    }
+    
+  return SVN_NO_ERROR;
+}
+
+/* Handle an added or deleted diff target directory for a repos<->repos diff.
+ *
+ * Using the provided diff CALLBACKS and the CALLBACK_BATON, show the
+ * directory TARGET@PEG_REVISION, and all of its children, as added or deleted,
+ * depending on SHOW_DELETION. TARGET is a path relative to RA_SESSION's URL.
+ * REV1 and REV2 are the revisions being compared.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+diff_repos_repos_added_or_deleted_dir(const char *target,
+                                      svn_revnum_t revision,
+                                      svn_revnum_t rev1,
+                                      svn_revnum_t rev2,
+                                      svn_boolean_t show_deletion,
+                                      const char *empty_file,
+                                      const svn_wc_diff_callbacks4_t
+                                        *callbacks,
+                                      struct diff_cmd_baton *callback_baton,
+                                      svn_ra_session_t *ra_session,
+                                      apr_pool_t *scratch_pool)
+{
+  apr_hash_t *dirents;
+  apr_hash_t *props;
+  apr_pool_t *iterpool;
+  apr_hash_index_t *hi;
+
+  SVN_ERR(svn_ra_get_dir2(ra_session, &dirents, NULL, &props,
+                          target, revision, SVN_DIRENT_KIND,
+                          scratch_pool));
+
+  if (show_deletion)
+    SVN_ERR(callbacks->dir_deleted(NULL, NULL, target, callback_baton,
+                                   scratch_pool));
+  else
+    SVN_ERR(callbacks->dir_added(NULL, NULL, NULL, NULL,
+                                 target, revision,
+                                 NULL, SVN_INVALID_REVNUM,
+                                 callback_baton, scratch_pool));
+  if (props)
+    {
+      if (show_deletion)
+        SVN_ERR(callbacks->dir_props_changed(NULL, NULL, target, FALSE,
+                                             apr_array_make(scratch_pool, 0,
+                                                            sizeof(svn_prop_t)),
+                                             make_regular_props_hash(
+                                               props, scratch_pool,
+                                               scratch_pool),
+                                             callback_baton, scratch_pool));
+      else
+        SVN_ERR(callbacks->dir_props_changed(NULL, NULL, target, TRUE,
+                                             make_regular_props_array(
+                                               props, scratch_pool,
+                                               scratch_pool),
+                                             NULL,
+                                             callback_baton, scratch_pool));
+    }
+
+  iterpool = svn_pool_create(scratch_pool);
+  for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
+    {
+      const char *name = svn__apr_hash_index_key(hi);
+      svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
+      const char *child_target;
+
+      svn_pool_clear(iterpool);
+
+      child_target = svn_relpath_join(target, name, iterpool);
+
+      if (dirent->kind == svn_node_dir)
+        SVN_ERR(diff_repos_repos_added_or_deleted_dir(child_target,
+                                                      revision, rev1, rev2,
+                                                      show_deletion,
+                                                      empty_file,
+                                                      callbacks,
+                                                      callback_baton,
+                                                      ra_session,
+                                                      iterpool));
+      else if (dirent->kind == svn_node_file)
+        SVN_ERR(diff_repos_repos_added_or_deleted_file(child_target,
+                                                       revision, rev1, rev2,
+                                                       show_deletion,
+                                                       empty_file,
+                                                       callbacks,
+                                                       callback_baton,
+                                                       ra_session,
+                                                       iterpool));
+    }
+  svn_pool_destroy(iterpool);
+
+  if (!show_deletion)
+    SVN_ERR(callbacks->dir_closed(NULL, NULL, NULL, target, TRUE,
+                                  callback_baton, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Handle an added or deleted diff target for a repos<->repos diff.
+ *
+ * Using the provided diff CALLBACKS and the CALLBACK_BATON, show
+ * TARGET@PEG_REVISION, and all of its children, if any, as added or deleted.
+ * TARGET is a path relative to RA_SESSION's URL.
+ * REV1 and REV2 are the revisions being compared.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+diff_repos_repos_added_or_deleted_target(const char *target1,
+                                         const char *target2,
+                                         svn_revnum_t rev1,
+                                         svn_revnum_t rev2,
+                                         svn_node_kind_t kind1,
+                                         svn_node_kind_t kind2,
+                                         const svn_wc_diff_callbacks4_t
+                                           *callbacks,
+                                         struct diff_cmd_baton *callback_baton,
+                                         svn_ra_session_t *ra_session,
+                                         apr_pool_t *scratch_pool)
+{
+  const char *existing_target;
+  svn_revnum_t existing_rev;
+  svn_node_kind_t existing_kind;
+  svn_boolean_t show_deletion;
+  const char *empty_file;
+
+  SVN_ERR_ASSERT(kind1 == svn_node_none || kind2 == svn_node_none);
+
+  /* Are we showing an addition or deletion? */
+  show_deletion = (kind2 == svn_node_none);
+
+  /* Which target is being added/deleted? Is it a file or a directory? */
+  if (show_deletion)
+    {
+      existing_target = target1;
+      existing_rev = rev1;
+      existing_kind = kind1;
+    }
+  else
+    {
+      existing_target = target2;
+      existing_rev = rev2;
+      existing_kind = kind2;
+    }
+
+  /* All file content will be diffed against the empty file. */
+  SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file, NULL,
+                                   svn_io_file_del_on_pool_cleanup,
+                                   scratch_pool, scratch_pool));
+
+  if (existing_kind == svn_node_file)
+    {
+      /* Get file content and show a diff against the empty file. */
+      SVN_ERR(diff_repos_repos_added_or_deleted_file(existing_target,
+                                                     existing_rev,
+                                                     rev1, rev2,
+                                                     show_deletion,
+                                                     empty_file,
+                                                     callbacks,
+                                                     callback_baton,
+                                                     ra_session,
+                                                     scratch_pool));
+    }
+  else
+    {
+      /* Walk the added/deleted tree and show a diff for each child. */
+      SVN_ERR(diff_repos_repos_added_or_deleted_dir(existing_target,
+                                                    existing_rev,
+                                                    rev1, rev2,
+                                                    show_deletion,
+                                                    empty_file,
+                                                    callbacks,
+                                                    callback_baton,
+                                                    ra_session,
+                                                    scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
 
 /* Perform a diff between two repository paths.
 
@@ -1846,6 +2619,8 @@ diff_repos_repos(const svn_wc_diff_callb
   const char *base_path;
   svn_revnum_t rev1;
   svn_revnum_t rev2;
+  svn_node_kind_t kind1;
+  svn_node_kind_t kind2;
   const char *anchor1;
   const char *anchor2;
   const char *target1;
@@ -1855,7 +2630,8 @@ diff_repos_repos(const svn_wc_diff_callb
   /* Prepare info for the repos repos diff. */
   SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2,
                                    &anchor1, &anchor2, &target1, &target2,
-                                   &ra_session, ctx, path_or_url1, path_or_url2,
+                                   &kind1, &kind2, &ra_session,
+                                   ctx, path_or_url1, path_or_url2,
                                    revision1, revision2, peg_revision,
                                    pool));
 
@@ -1870,6 +2646,21 @@ diff_repos_repos(const svn_wc_diff_callb
   callback_baton->ra_session = ra_session;
   callback_baton->anchor = base_path;
 
+  if (kind1 == svn_node_none || kind2 == svn_node_none)
+    {
+      /* One side of the diff does not exist.
+       * Walk the tree that does exist, showing a series of additions
+       * or deletions. */
+      SVN_ERR(diff_repos_repos_added_or_deleted_target(target1, target2,
+                                                       rev1, rev2,
+                                                       kind1, kind2,
+                                                       callbacks,
+                                                       callback_baton,
+                                                       ra_session,
+                                                       pool));
+      return SVN_NO_ERROR;
+    }
+
   /* Now, we open an extra RA session to the correct anchor
      location for URL1.  This is used during the editor calls to fetch file
      contents.  */
@@ -2167,8 +2958,8 @@ diff_summarize_wc_wc(svn_client_diff_sum
     return unsupported_diff_error
       (svn_error_create
        (SVN_ERR_INCORRECT_PARAMS, NULL,
-        _("Only diffs between a path's text-base "
-          "and its working files are supported at this time")));
+        _("Summarized diffs are only supported between a path's text-base "
+          "and its working files at this time")));
 
   /* Find the node kind of PATH1 so that we know whether the diff drive will
      be anchored at PATH1 or its parent dir. */
@@ -2217,6 +3008,8 @@ diff_summarize_repos_repos(svn_client_di
   const char *base_path;
   svn_revnum_t rev1;
   svn_revnum_t rev2;
+  svn_node_kind_t kind1;
+  svn_node_kind_t kind2;
   const char *anchor1;
   const char *anchor2;
   const char *target1;
@@ -2228,10 +3021,29 @@ diff_summarize_repos_repos(svn_client_di
   /* Prepare info for the repos repos diff. */
   SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2,
                                    &anchor1, &anchor2, &target1, &target2,
-                                   &ra_session, ctx,
-                                   path_or_url1, path_or_url2, revision1, revision2,
+                                   &kind1, &kind2, &ra_session,
+                                   ctx, path_or_url1, path_or_url2,
+                                   revision1, revision2,
                                    peg_revision, pool));
 
+  if (kind1 == svn_node_none || kind2 == svn_node_none)
+    {
+      /* One side of the diff does not exist.
+       * Walk the tree that does exist, showing a series of additions
+       * or deletions. */
+      SVN_ERR(svn_client__get_diff_summarize_callbacks(
+                &callbacks, &callback_baton, target1,
+                summarize_func, summarize_baton, pool));
+      SVN_ERR(diff_repos_repos_added_or_deleted_target(target1, target2,
+                                                       rev1, rev2,
+                                                       kind1, kind2,
+                                                       callbacks,
+                                                       callback_baton,
+                                                       ra_session,
+                                                       pool));
+      return SVN_NO_ERROR;
+    }
+
   SVN_ERR(svn_client__get_diff_summarize_callbacks(
             &callbacks, &callback_baton,
             target1, summarize_func, summarize_baton, pool));
@@ -2420,7 +3232,8 @@ svn_client_diff6(const apr_array_header_
                  svn_boolean_t no_diff_deleted,
                  svn_boolean_t show_copies_as_adds,
                  svn_boolean_t ignore_content_type,
-                 svn_boolean_t ignore_prop_diff,
+                 svn_boolean_t ignore_properties,
+                 svn_boolean_t properties_only,
                  svn_boolean_t use_git_diff_format,
                  const char *header_encoding,
                  svn_stream_t *outstream,
@@ -2430,9 +3243,14 @@ svn_client_diff6(const apr_array_header_
                  apr_pool_t *pool)
 {
   struct diff_cmd_baton diff_cmd_baton = { 0 };
+  svn_opt_revision_t peg_revision;
+
+  if (ignore_properties && properties_only)
+    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+                            _("Cannot ignore properties and show only "
+                              "properties at the same time"));
 
   /* We will never do a pegged diff from here. */
-  svn_opt_revision_t peg_revision;
   peg_revision.kind = svn_opt_revision_unspecified;
 
   /* setup callback and baton */
@@ -2450,7 +3268,8 @@ svn_client_diff6(const apr_array_header_
 
   diff_cmd_baton.force_empty = FALSE;
   diff_cmd_baton.force_binary = ignore_content_type;
-  diff_cmd_baton.ignore_prop_diff = ignore_prop_diff;
+  diff_cmd_baton.ignore_properties = ignore_properties;
+  diff_cmd_baton.properties_only = properties_only;
   diff_cmd_baton.relative_to_dir = relative_to_dir;
   diff_cmd_baton.use_git_diff_format = use_git_diff_format;
   diff_cmd_baton.no_diff_deleted = no_diff_deleted;
@@ -2479,7 +3298,8 @@ svn_client_diff_peg6(const apr_array_hea
                      svn_boolean_t no_diff_deleted,
                      svn_boolean_t show_copies_as_adds,
                      svn_boolean_t ignore_content_type,
-                     svn_boolean_t ignore_prop_diff,
+                     svn_boolean_t ignore_properties,
+                     svn_boolean_t properties_only,
                      svn_boolean_t use_git_diff_format,
                      const char *header_encoding,
                      svn_stream_t *outstream,
@@ -2490,6 +3310,11 @@ svn_client_diff_peg6(const apr_array_hea
 {
   struct diff_cmd_baton diff_cmd_baton = { 0 };
 
+  if (ignore_properties && properties_only)
+    return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
+                            _("Cannot ignore properties and show only "
+                              "properties at the same time"));
+
   /* setup callback and baton */
   diff_cmd_baton.orig_path_1 = path_or_url;
   diff_cmd_baton.orig_path_2 = path_or_url;
@@ -2505,7 +3330,8 @@ svn_client_diff_peg6(const apr_array_hea
 
   diff_cmd_baton.force_empty = FALSE;
   diff_cmd_baton.force_binary = ignore_content_type;
-  diff_cmd_baton.ignore_prop_diff = ignore_prop_diff;
+  diff_cmd_baton.ignore_properties = ignore_properties;
+  diff_cmd_baton.properties_only = properties_only;
   diff_cmd_baton.relative_to_dir = relative_to_dir;
   diff_cmd_baton.use_git_diff_format = use_git_diff_format;
   diff_cmd_baton.no_diff_deleted = no_diff_deleted;

Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_client/export.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_client/export.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_client/export.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_client/export.c Wed May 16 20:32:43 2012
@@ -42,6 +42,7 @@
 #include "client.h"
 
 #include "svn_private_config.h"
+#include "private/svn_subr_private.h"
 #include "private/svn_wc_private.h"
 
 
@@ -957,8 +958,7 @@ close_file(void *file_baton,
 
   SVN_ERR(svn_checksum_parse_hex(&text_checksum, svn_checksum_md5, text_digest,
                                  pool));
-  actual_checksum = svn_checksum__from_digest(fb->text_digest,
-                                              svn_checksum_md5, pool);
+  actual_checksum = svn_checksum__from_digest_md5(fb->text_digest, pool);
 
   /* Note that text_digest can be NULL when talking to certain repositories.
      In that case text_checksum will be NULL and the following match code
@@ -1103,20 +1103,19 @@ svn_client_export5(svn_revnum_t *result_
 
   if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
     {
-      svn_revnum_t revnum;
-      const char *url;
+      svn_client__pathrev_t *loc;
       svn_ra_session_t *ra_session;
       svn_node_kind_t kind;
       struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
 
       /* Get the RA connection. */
-      SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum,
-                                               &url, from_path_or_url, NULL,
-                                               peg_revision,
-                                               revision, ctx, pool));
+      SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
+                                                from_path_or_url, NULL,
+                                                peg_revision,
+                                                revision, ctx, pool));
 
       eb->root_path = to_path;
-      eb->root_url = url;
+      eb->root_url = loc->url;
       eb->force = overwrite;
       eb->target_revision = &edit_revision;
       eb->externals = apr_hash_make(pool);
@@ -1127,7 +1126,7 @@ svn_client_export5(svn_revnum_t *result_
       eb->notify_func = ctx->notify_func2;
       eb->notify_baton = ctx->notify_baton2;
 
-      SVN_ERR(svn_ra_check_path(ra_session, "", revnum, &kind, pool));
+      SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, pool));
 
       if (kind == svn_node_file)
         {
@@ -1183,7 +1182,7 @@ svn_client_export5(svn_revnum_t *result_
           /* Step outside the editor-likeness for a moment, to actually talk
            * to the repository. */
           /* ### note: the stream will not be closed */
-          SVN_ERR(svn_ra_get_file(ra_session, "", revnum,
+          SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
                                   fb->tmp_stream,
                                   NULL, &props, pool));
 
@@ -1236,18 +1235,19 @@ svn_client_export5(svn_revnum_t *result_
 
           SVN_ERR(svn_editor__insert_shims(&export_editor, &edit_baton,
                                            export_editor, edit_baton,
-                                           shim_callbacks, pool, pool));
+                                           NULL, NULL, shim_callbacks,
+                                           pool, pool));
 
           /* Manufacture a basic 'report' to the update reporter. */
           SVN_ERR(svn_ra_do_update2(ra_session,
                                     &reporter, &report_baton,
-                                    revnum,
+                                    loc->rev,
                                     "", /* no sub-target */
                                     depth,
                                     FALSE, /* don't want copyfrom-args */
                                     export_editor, edit_baton, pool));
 
-          SVN_ERR(reporter->set_path(report_baton, "", revnum,
+          SVN_ERR(reporter->set_path(report_baton, "", loc->rev,
                                      /* Depth is irrelevant, as we're
                                         passing start_empty=TRUE anyway. */
                                      svn_depth_infinity,

Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_client/externals.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_client/externals.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_client/externals.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_client/externals.c Wed May 16 20:32:43 2012
@@ -24,7 +24,7 @@
 /* ==================================================================== */
 
 
-
+ 
 /*** Includes. ***/
 
 #include <apr_uri.h>
@@ -41,7 +41,7 @@
 
 #include "svn_private_config.h"
 #include "private/svn_wc_private.h"
-
+ 
 /* Closure for handle_external_item_change. */
 struct external_change_baton_t
 {
@@ -313,9 +313,6 @@ switch_file_external(const char *local_a
                      const svn_opt_revision_t *revision,
                      const char *def_dir_abspath,
                      svn_ra_session_t *ra_session,
-                     const char *ra_session_url,
-                     svn_revnum_t ra_revnum,
-                     const char *repos_root_url,
                      svn_boolean_t *timestamp_sleep,
                      svn_client_ctx_t *ctx,
                      apr_pool_t *scratch_pool)
@@ -437,8 +434,7 @@ switch_file_external(const char *local_a
     void *report_baton;
     const svn_delta_editor_t *switch_editor;
     void *switch_baton;
-    const char *switch_rev_url;
-    const char *repos_uuid;
+    svn_client__pathrev_t *switch_loc;
     svn_revnum_t revnum;
     /* ### TODO: Provide the real definition path (now available in
        ### def_dir_abspath) after switching to the new externals store.
@@ -447,22 +443,20 @@ switch_file_external(const char *local_a
     const char *definition_abspath = svn_dirent_dirname(local_abspath,subpool);
 
     /* Open an RA session to 'source' URL */
-    SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum,
-                                             &switch_rev_url,
-                                             url, dir_abspath,
-                                             peg_revision, revision,
-                                             ctx, subpool));
+    SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc,
+                                              url, dir_abspath,
+                                              peg_revision, revision,
+                                              ctx, subpool));
 
     SVN_ERR(svn_ra_reparent(ra_session, url, subpool));
-    SVN_ERR(svn_ra_get_uuid2(ra_session, &repos_uuid, subpool));
 
     SVN_ERR(svn_wc__get_file_external_editor(&switch_editor, &switch_baton,
                                              &revnum, ctx->wc_ctx,
                                              local_abspath,
                                              definition_abspath /* wri */,
-                                             switch_rev_url,
-                                             repos_root_url,
-                                             repos_uuid,
+                                             switch_loc->url,
+                                             switch_loc->repos_root_url,
+                                             switch_loc->repos_uuid,
                                              use_commit_times,
                                              diff3_cmd, preserved_exts,
                                              definition_abspath /* def */,
@@ -477,16 +471,17 @@ switch_file_external(const char *local_a
 
     /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an
      invalid revnum, that means RA will use the latest revision. */
-  SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton, revnum,
-                            target, svn_depth_unknown, url,
-                            switch_editor, switch_baton, subpool));
-
-  SVN_ERR(svn_wc__crawl_file_external(ctx->wc_ctx, local_abspath,
-                                      reporter, report_baton,
-                                      TRUE,  use_commit_times,
-                                      ctx->cancel_func, ctx->cancel_baton,
-                                      ctx->notify_func2, ctx->notify_baton2,
-                                      subpool));
+    SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton,
+                              switch_loc->rev,
+                              target, svn_depth_unknown, url,
+                              switch_editor, switch_baton, subpool));
+
+    SVN_ERR(svn_wc__crawl_file_external(ctx->wc_ctx, local_abspath,
+                                        reporter, report_baton,
+                                        TRUE,  use_commit_times,
+                                        ctx->cancel_func, ctx->cancel_baton,
+                                        ctx->notify_func2, ctx->notify_baton2,
+                                        subpool));
 
     if (ctx->notify_func2)
       {
@@ -603,10 +598,7 @@ handle_external_item_change(const struct
                             apr_pool_t *scratch_pool)
 {
   svn_ra_session_t *ra_session;
-  svn_revnum_t ra_revnum;
-  const char *ra_session_url;
-  const char *repos_root_url;
-  const char *repos_uuid;
+  svn_client__pathrev_t *new_loc;
   const char *new_url;
   svn_node_kind_t ext_kind;
 
@@ -627,29 +619,25 @@ handle_external_item_change(const struct
 
   /* Determine if the external is a file or directory. */
   /* Get the RA connection. */
-  SVN_ERR(svn_client__ra_session_from_path(&ra_session,
-                                           &ra_revnum,
-                                           &ra_session_url,
-                                           new_url, NULL,
-                                           &(new_item->peg_revision),
-                                           &(new_item->revision), eb->ctx,
-                                           scratch_pool));
-
-  SVN_ERR(svn_ra_get_uuid2(ra_session, &repos_uuid, scratch_pool));
-  SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, scratch_pool));
-  SVN_ERR(svn_ra_check_path(ra_session, "", ra_revnum, &ext_kind,
+  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
+                                            new_url, NULL,
+                                            &(new_item->peg_revision),
+                                            &(new_item->revision), eb->ctx,
+                                            scratch_pool));
+
+  SVN_ERR(svn_ra_check_path(ra_session, "", new_loc->rev, &ext_kind,
                             scratch_pool));
 
   if (svn_node_none == ext_kind)
     return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                              _("URL '%s' at revision %ld doesn't exist"),
-                             ra_session_url, ra_revnum);
+                             new_loc->url, new_loc->rev);
 
   if (svn_node_dir != ext_kind && svn_node_file != ext_kind)
     return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                              _("URL '%s' at revision %ld is not a file "
                                "or a directory"),
-                             ra_session_url, ra_revnum);
+                             new_loc->url, new_loc->rev);
 
 
   /* Not protecting against recursive externals.  Detecting them in
@@ -687,7 +675,7 @@ handle_external_item_change(const struct
                                     scratch_pool));
         break;
       case svn_node_file:
-        if (strcmp(eb->repos_root_url, repos_root_url))
+        if (strcmp(eb->repos_root_url, new_loc->repos_root_url))
           {
             const char *local_repos_root_url;
             const char *local_repos_uuid;
@@ -707,11 +695,11 @@ handle_external_item_change(const struct
                                                 eb->ctx->wc_ctx,
                                                 parent_dir_abspath,
                                                 scratch_pool, scratch_pool));
-            ext_repos_relpath = svn_uri_skip_ancestor(repos_root_url,
+            ext_repos_relpath = svn_uri_skip_ancestor(new_loc->repos_root_url,
                                                       new_url, scratch_pool);
             if (local_repos_uuid == NULL || local_repos_root_url == NULL ||
                 ext_repos_relpath == NULL ||
-                strcmp(local_repos_uuid, repos_uuid) != 0)
+                strcmp(local_repos_uuid, new_loc->repos_uuid) != 0)
               return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                         _("Unsupported external: URL of file external '%s' "
                           "is not in repository '%s'"),
@@ -720,16 +708,12 @@ handle_external_item_change(const struct
             new_url = svn_path_url_add_component2(local_repos_root_url,
                                                   ext_repos_relpath,
                                                   scratch_pool);
-            SVN_ERR(svn_client__ra_session_from_path(&ra_session,
-                                                     &ra_revnum,
-                                                     &ra_session_url,
-                                                     new_url,
-                                                     NULL,
-                                                     &(new_item->peg_revision),
-                                                     &(new_item->revision),
-                                                     eb->ctx, scratch_pool));
-            SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
-                                           scratch_pool));
+            SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
+                                                      new_url,
+                                                      NULL,
+                                                      &(new_item->peg_revision),
+                                                      &(new_item->revision),
+                                                      eb->ctx, scratch_pool));
           }
 
         SVN_ERR(switch_file_external(local_abspath,
@@ -738,9 +722,6 @@ handle_external_item_change(const struct
                                      &new_item->revision,
                                      parent_dir_abspath,
                                      ra_session,
-                                     ra_session_url,
-                                     ra_revnum,
-                                     repos_root_url,
                                      eb->timestamp_sleep, eb->ctx,
                                      scratch_pool));
         break;
@@ -1060,7 +1041,7 @@ svn_client__export_externals(apr_hash_t 
 
 svn_error_t *
 svn_client__do_external_status(svn_client_ctx_t *ctx,
-                               apr_hash_t *externals_new,
+                               apr_hash_t *external_map,
                                svn_depth_t depth,
                                svn_boolean_t get_all,
                                svn_boolean_t update,
@@ -1070,69 +1051,59 @@ svn_client__do_external_status(svn_clien
                                apr_pool_t *pool)
 {
   apr_hash_index_t *hi;
-  apr_pool_t *subpool = svn_pool_create(pool);
+  apr_pool_t *iterpool = svn_pool_create(pool);
 
   /* Loop over the hash of new values (we don't care about the old
      ones).  This is a mapping of versioned directories to property
      values. */
-  for (hi = apr_hash_first(pool, externals_new);
+  for (hi = apr_hash_first(pool, external_map);
        hi;
        hi = apr_hash_next(hi))
     {
-      apr_array_header_t *exts;
-      const char *path = svn__apr_hash_index_key(hi);
-      const char *propval = svn__apr_hash_index_val(hi);
-      apr_pool_t *iterpool;
-      int i;
+      svn_node_kind_t external_kind;
+      const char *local_abspath = svn__apr_hash_index_key(hi);
+      const char *defining_abspath = svn__apr_hash_index_val(hi);
+      svn_node_kind_t kind;
+      svn_opt_revision_t opt_rev;
 
-      /* Clear the subpool. */
-      svn_pool_clear(subpool);
+      svn_pool_clear(iterpool);
 
-      /* Parse the svn:externals property value.  This results in a
-         hash mapping subdirectories to externals structures. */
-      SVN_ERR(svn_wc_parse_externals_description3(&exts, path, propval,
-                                                  FALSE, subpool));
+      /* Obtain information on the expected external. */
+      SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
+                                         &opt_rev.value.number,
+                                         ctx->wc_ctx, defining_abspath,
+                                         local_abspath, FALSE,
+                                         iterpool, iterpool));
 
-      /* Make a sub-pool of SUBPOOL. */
-      iterpool = svn_pool_create(subpool);
+      if (external_kind != svn_node_dir)
+        continue;
 
-      /* Loop over the subdir array. */
-      for (i = 0; exts && (i < exts->nelts); i++)
-        {
-          const char *fullpath;
-          svn_wc_external_item2_t *external;
-          svn_node_kind_t kind;
-
-          svn_pool_clear(iterpool);
-
-          external = APR_ARRAY_IDX(exts, i, svn_wc_external_item2_t *);
-          fullpath = svn_dirent_join(path, external->target_dir, iterpool);
-
-          /* If the external target directory doesn't exist on disk,
-             just skip it. */
-          SVN_ERR(svn_io_check_path(fullpath, &kind, iterpool));
-          if (kind != svn_node_dir)
-            continue;
-
-          /* Tell the client we're starting an external status set. */
-          if (ctx->notify_func2)
-            (ctx->notify_func2)(
+      SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool));
+      if (kind != svn_node_dir)
+        continue;
+
+      if (SVN_IS_VALID_REVNUM(opt_rev.value.number))
+        opt_rev.kind = svn_opt_revision_number;
+      else
+        opt_rev.kind = svn_opt_revision_unspecified;
+
+      /* Tell the client we're starting an external status set. */
+      if (ctx->notify_func2)
+        ctx->notify_func2(
                ctx->notify_baton2,
-               svn_wc_create_notify(fullpath, svn_wc_notify_status_external,
+               svn_wc_create_notify(local_abspath,
+                                    svn_wc_notify_status_external,
                                     iterpool), iterpool);
 
-          /* And then do the status. */
-          SVN_ERR(svn_client_status5(NULL, ctx, fullpath,
-                                     &(external->revision),
-                                     depth, get_all, update,
-                                     no_ignore, FALSE, FALSE, NULL,
-                                     status_func, status_baton,
-                                     iterpool));
-        }
+      /* And then do the status. */
+      SVN_ERR(svn_client_status5(NULL, ctx, local_abspath, &opt_rev, depth,
+                                 get_all, update, no_ignore, FALSE, FALSE,
+                                 NULL, status_func, status_baton,
+                                 iterpool));
     }
 
   /* Destroy SUBPOOL and (implicitly) ITERPOOL. */
-  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_client/info.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_client/info.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_client/info.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_client/info.c Wed May 16 20:32:43 2012
@@ -66,19 +66,16 @@ static svn_error_t *
 build_info_from_dirent(svn_client_info2_t **info,
                        const svn_dirent_t *dirent,
                        svn_lock_t *lock,
-                       const char *URL,
-                       svn_revnum_t revision,
-                       const char *repos_UUID,
-                       const char *repos_root,
+                       const svn_client__pathrev_t *pathrev,
                        apr_pool_t *pool)
 {
   svn_client_info2_t *tmpinfo = apr_pcalloc(pool, sizeof(*tmpinfo));
 
-  tmpinfo->URL                  = URL;
-  tmpinfo->rev                  = revision;
+  tmpinfo->URL                  = pathrev->url;
+  tmpinfo->rev                  = pathrev->rev;
   tmpinfo->kind                 = dirent->kind;
-  tmpinfo->repos_UUID           = repos_UUID;
-  tmpinfo->repos_root_URL       = repos_root;
+  tmpinfo->repos_UUID           = pathrev->repos_uuid;
+  tmpinfo->repos_root_URL       = pathrev->repos_root_url;
   tmpinfo->last_changed_rev     = dirent->created_rev;
   tmpinfo->last_changed_date    = dirent->time;
   tmpinfo->last_changed_author  = dirent->last_author;
@@ -111,11 +108,8 @@ build_info_from_dirent(svn_client_info2_
 */
 static svn_error_t *
 push_dir_info(svn_ra_session_t *ra_session,
-              const char *session_URL,
+              const svn_client__pathrev_t *pathrev,
               const char *dir,
-              svn_revnum_t rev,
-              const char *repos_UUID,
-              const char *repos_root,
               svn_client_info_receiver2_t receiver,
               void *receiver_baton,
               svn_depth_t depth,
@@ -128,15 +122,16 @@ push_dir_info(svn_ra_session_t *ra_sessi
   apr_pool_t *subpool = svn_pool_create(pool);
 
   SVN_ERR(svn_ra_get_dir2(ra_session, &tmpdirents, NULL, NULL,
-                          dir, rev, DIRENT_FIELDS, pool));
+                          dir, pathrev->rev, DIRENT_FIELDS, pool));
 
   for (hi = apr_hash_first(pool, tmpdirents); hi; hi = apr_hash_next(hi))
     {
-      const char *path, *URL, *fs_path;
+      const char *path, *fs_path;
       svn_lock_t *lock;
       svn_client_info2_t *info;
       const char *name = svn__apr_hash_index_key(hi);
       svn_dirent_t *the_ent = svn__apr_hash_index_val(hi);
+      svn_client__pathrev_t *child_pathrev;
 
       svn_pool_clear(subpool);
 
@@ -144,14 +139,13 @@ push_dir_info(svn_ra_session_t *ra_sessi
         SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
 
       path = svn_relpath_join(dir, name, subpool);
-      URL = svn_path_url_add_component2(session_URL, name, subpool);
-      fs_path = svn_fspath__canonicalize(
-                  svn_uri_skip_ancestor(repos_root, URL, subpool), subpool);
+      child_pathrev = svn_client__pathrev_join_relpath(pathrev, name, subpool);
+      fs_path = svn_client__pathrev_fspath(child_pathrev, subpool);
 
       lock = apr_hash_get(locks, fs_path, APR_HASH_KEY_STRING);
 
-      SVN_ERR(build_info_from_dirent(&info, the_ent, lock, URL, rev,
-                                     repos_UUID, repos_root, subpool));
+      SVN_ERR(build_info_from_dirent(&info, the_ent, lock, child_pathrev,
+                                     subpool));
 
       if (depth >= svn_depth_immediates
           || (depth == svn_depth_files && the_ent->kind == svn_node_file))
@@ -161,8 +155,7 @@ push_dir_info(svn_ra_session_t *ra_sessi
 
       if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir)
         {
-          SVN_ERR(push_dir_info(ra_session, URL, path,
-                                rev, repos_UUID, repos_root,
+          SVN_ERR(push_dir_info(ra_session, child_pathrev, path,
                                 receiver, receiver_baton,
                                 depth, ctx, locks, subpool));
         }
@@ -338,9 +331,7 @@ svn_client_info3(const char *abspath_or_
                  apr_pool_t *pool)
 {
   svn_ra_session_t *ra_session;
-  svn_revnum_t rev;
-  const char *url;
-  const char *repos_root_URL, *repos_UUID;
+  svn_client__pathrev_t *pathrev;
   svn_lock_t *lock;
   svn_boolean_t related;
   const char *base_name;
@@ -370,18 +361,14 @@ svn_client_info3(const char *abspath_or_
   /* Trace rename history (starting at path_or_url@peg_revision) and
      return RA session to the possibly-renamed URL as it exists in REVISION.
      The ra_session returned will be anchored on this "final" URL. */
-  SVN_ERR(svn_client__ra_session_from_path(&ra_session, &rev,
-                                           &url, abspath_or_url, NULL,
-                                           peg_revision,
-                                           revision, ctx, pool));
+  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev,
+                                            abspath_or_url, NULL, peg_revision,
+                                            revision, ctx, pool));
 
-  SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_URL, pool));
-  SVN_ERR(svn_ra_get_uuid2(ra_session, &repos_UUID, pool));
-
-  svn_uri_split(NULL, &base_name, url, pool);
+  svn_uri_split(NULL, &base_name, pathrev->url, pool);
 
   /* Get the dirent for the URL itself. */
-  err = ra_stat_compatible(ra_session, rev, &the_ent, DIRENT_FIELDS,
+  err = ra_stat_compatible(ra_session, pathrev->rev, &the_ent, DIRENT_FIELDS,
                            ctx, pool);
   if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
     {
@@ -407,7 +394,7 @@ svn_client_info3(const char *abspath_or_
   if (! the_ent)
     return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                              _("URL '%s' non-existent in revision %ld"),
-                             url, rev);
+                             pathrev->url, pathrev->rev);
 
   /* Check if the URL exists in HEAD and refers to the same resource.
      In this case, we check the repository for a lock on this URL.
@@ -417,7 +404,8 @@ svn_client_info3(const char *abspath_or_
      ### check in a loop which only terminates if the HEAD revision is the same
      ### before and after this check.  That could, however, lead to a
      ### starvation situation instead.  */
-  SVN_ERR(same_resource_in_head(&related, url, rev, ra_session, ctx, pool));
+  SVN_ERR(same_resource_in_head(&related, pathrev->url, pathrev->rev,
+                                ra_session, ctx, pool));
   if (related)
     {
       err = svn_ra_get_lock(ra_session, &lock, "", pool);
@@ -437,8 +425,7 @@ svn_client_info3(const char *abspath_or_
     lock = NULL;
 
   /* Push the URL's dirent (and lock) at the callback.*/
-  SVN_ERR(build_info_from_dirent(&info, the_ent, lock, url, rev,
-                                 repos_UUID, repos_root_URL, pool));
+  SVN_ERR(build_info_from_dirent(&info, the_ent, lock, pathrev, pool));
   SVN_ERR(receiver(receiver_baton, base_name, info, pool));
 
   /* Possibly recurse, using the original RA session. */
@@ -466,8 +453,7 @@ pre_1_2_recurse:
       else
         locks = apr_hash_make(pool); /* use an empty hash */
 
-      SVN_ERR(push_dir_info(ra_session, url, "", rev,
-                            repos_UUID, repos_root_URL,
+      SVN_ERR(push_dir_info(ra_session, pathrev, "",
                             receiver, receiver_baton,
                             depth, ctx, locks, pool));
     }

Modified: subversion/branches/fix-rdump-editor/subversion/libsvn_client/list.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/subversion/libsvn_client/list.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/subversion/libsvn_client/list.c (original)
+++ subversion/branches/fix-rdump-editor/subversion/libsvn_client/list.c Wed May 16 20:32:43 2012
@@ -237,9 +237,8 @@ svn_client_list2(const char *path_or_url
                  apr_pool_t *pool)
 {
   svn_ra_session_t *ra_session;
-  svn_revnum_t rev;
+  svn_client__pathrev_t *loc;
   svn_dirent_t *dirent;
-  const char *url;
   const char *fs_path;
   svn_error_t *err;
   apr_hash_t *locks;
@@ -249,20 +248,19 @@ svn_client_list2(const char *path_or_url
   dirent_fields |= SVN_DIRENT_KIND;
 
   /* Get an RA plugin for this filesystem object. */
-  SVN_ERR(svn_client__ra_session_from_path(&ra_session, &rev,
-                                           &url, path_or_url, NULL,
-                                           peg_revision,
-                                           revision, ctx, pool));
+  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
+                                            path_or_url, NULL,
+                                            peg_revision,
+                                            revision, ctx, pool));
 
-  SVN_ERR(svn_ra__get_fspath_relative_to_root(ra_session, &fs_path, url,
-                                              pool));
+  fs_path = svn_client__pathrev_fspath(loc, pool);
 
-  SVN_ERR(ra_stat_compatible(ra_session, rev, &dirent, dirent_fields,
+  SVN_ERR(ra_stat_compatible(ra_session, loc->rev, &dirent, dirent_fields,
                              ctx, pool));
   if (! dirent)
     return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
                              _("URL '%s' non-existent in revision %ld"),
-                             url, rev);
+                             loc->url, loc->rev);
 
   /* Maybe get all locks under url. */
   if (fetch_locks)
@@ -292,7 +290,7 @@ svn_client_list2(const char *path_or_url
       && (depth == svn_depth_files
           || depth == svn_depth_immediates
           || depth == svn_depth_infinity))
-    SVN_ERR(get_dir_contents(dirent_fields, "", rev, ra_session, locks,
+    SVN_ERR(get_dir_contents(dirent_fields, "", loc->rev, ra_session, locks,
                              fs_path, depth, ctx, list_func, baton, pool));
 
   return SVN_NO_ERROR;