Modified: subversion/branches/inheritable-props/subversion/libsvn_client/diff.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_client/diff.c?rev=1312079&r1=1312078&r2=1312079&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_client/diff.c 
(original)
+++ subversion/branches/inheritable-props/subversion/libsvn_client/diff.c Wed 
Apr 11 02:56:02 2012
@@ -805,6 +805,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 *
@@ -855,8 +866,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)
@@ -1072,9 +1082,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);
         }
     }
 
@@ -1494,6 +1502,44 @@ 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)
+        {
+          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.
@@ -1560,34 +1606,38 @@ 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. */
@@ -1731,6 +1781,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_prop_diff)
+    {
+      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 +2284,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,
@@ -2167,8 +2691,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. */

Modified: 
subversion/branches/inheritable-props/subversion/libsvn_client/export.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_client/export.c?rev=1312079&r1=1312078&r2=1312079&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_client/export.c 
(original)
+++ subversion/branches/inheritable-props/subversion/libsvn_client/export.c Wed 
Apr 11 02:56:02 2012
@@ -1236,7 +1236,7 @@ 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, shim_callbacks, pool, pool));
 
           /* Manufacture a basic 'report' to the update reporter. */
           SVN_ERR(svn_ra_do_update2(ra_session,

Modified: subversion/branches/inheritable-props/subversion/libsvn_client/info.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/inheritable-props/subversion/libsvn_client/info.c?rev=1312079&r1=1312078&r2=1312079&view=diff
==============================================================================
--- subversion/branches/inheritable-props/subversion/libsvn_client/info.c 
(original)
+++ subversion/branches/inheritable-props/subversion/libsvn_client/info.c Wed 
Apr 11 02:56:02 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));
     }


Reply via email to