You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/02/13 07:37:56 UTC

svn commit: r1445479 [2/11] - in /subversion/branches/fsfs-format7: ./ build/generator/ build/generator/swig/ build/generator/templates/ notes/api-errata/1.7/ packages/ subversion/bindings/swig/include/ subversion/bindings/swig/perl/libsvn_swig_perl/ s...

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/diff.c?rev=1445479&r1=1445478&r2=1445479&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/diff.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/diff.c Wed Feb 13 06:37:54 2013
@@ -50,6 +50,7 @@
 
 #include "private/svn_wc_private.h"
 #include "private/svn_diff_private.h"
+#include "private/svn_subr_private.h"
 
 #include "svn_private_config.h"
 
@@ -92,9 +93,9 @@ make_repos_relpath(const char **repos_re
                                                       scratch_pool),
                                       scratch_pool));
 
-      err = svn_wc__node_get_repos_relpath(repos_relpath, wc_ctx,
-                                           local_abspath,
-                                           result_pool, scratch_pool);
+      err = svn_wc__node_get_repos_info(NULL, repos_relpath, NULL, NULL,
+                                        wc_ctx, local_abspath,
+                                        result_pool, scratch_pool);
 
       if (!ra_session
           || ! err
@@ -594,6 +595,12 @@ struct diff_cmd_baton {
   /* Whether deletion of a file is summarized versus showing a full diff. */
   svn_boolean_t no_diff_deleted;
 
+  /* Whether to ignore copyfrom information when showing adds */
+  svn_boolean_t no_copyfrom_on_add;
+
+  /* Empty files for creating diffs or NULL if not used yet */
+  const char *empty_file;
+
   svn_wc_context_t *wc_ctx;
 
   /* The RA session used during diffs involving the repository. */
@@ -996,6 +1003,30 @@ diff_file_added(svn_wc_notify_state_t *c
         rev2 = diff_cmd_baton->revnum2;
     }
 
+  if (diff_cmd_baton->no_copyfrom_on_add
+      && (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_revision)))
+    {
+      apr_hash_t *empty_hash = apr_hash_make(scratch_pool);
+      apr_array_header_t *new_changes;
+
+      /* Rebase changes on having no left source. */
+      if (!diff_cmd_baton->empty_file)
+        SVN_ERR(svn_io_open_unique_file3(NULL, &diff_cmd_baton->empty_file,
+                                         NULL, svn_io_file_del_on_pool_cleanup,
+                                         diff_cmd_baton->pool, scratch_pool));
+
+      SVN_ERR(svn_prop_diffs(&new_changes,
+                             svn_prop__patch(original_props, prop_changes,
+                                             scratch_pool),
+                             empty_hash,
+                             scratch_pool));
+
+      tmpfile1 = diff_cmd_baton->empty_file;
+      prop_changes = new_changes;
+      original_props = empty_hash;
+      copyfrom_revision = SVN_INVALID_REVNUM;
+    }
+
   if (diff_cmd_baton->no_diff_added)
     {
       const char *index_path = diff_relpath;
@@ -1192,37 +1223,6 @@ static const svn_wc_diff_callbacks4_t di
    the user specifies two dates that resolve to the same revision.  */
 
 
-
-
-/* Helper function: given a working-copy ABSPATH_OR_URL, return its
-   associated url in *URL, allocated in RESULT_POOL.  If ABSPATH_OR_URL is
-   *already* a URL, that's fine, return ABSPATH_OR_URL allocated in
-   RESULT_POOL.
-
-   Use SCRATCH_POOL for temporary allocations. */
-static svn_error_t *
-convert_to_url(const char **url,
-               svn_wc_context_t *wc_ctx,
-               const char *abspath_or_url,
-               apr_pool_t *result_pool,
-               apr_pool_t *scratch_pool)
-{
-  if (svn_path_is_url(abspath_or_url))
-    {
-      *url = apr_pstrdup(result_pool, abspath_or_url);
-      return SVN_NO_ERROR;
-    }
-
-  SVN_ERR(svn_wc__node_get_url(url, wc_ctx, abspath_or_url,
-                               result_pool, scratch_pool));
-  if (! *url)
-    return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
-                             _("Path '%s' has no URL"),
-                             svn_dirent_local_style(abspath_or_url,
-                                                    scratch_pool));
-  return SVN_NO_ERROR;
-}
-
 /** 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 not
@@ -1384,24 +1384,25 @@ diff_prepare_repos_repos(const char **ur
 {
   const char *abspath_or_url2;
   const char *abspath_or_url1;
+  const char *repos_root_url;
 
   if (!svn_path_is_url(path_or_url2))
-    SVN_ERR(svn_dirent_get_absolute(&abspath_or_url2, path_or_url2,
-                                    pool));
+    {
+      SVN_ERR(svn_dirent_get_absolute(&abspath_or_url2, path_or_url2, pool));
+      SVN_ERR(svn_wc__node_get_url(url2, ctx->wc_ctx, abspath_or_url2,
+                                   pool, pool));
+    }
   else
-    abspath_or_url2 = path_or_url2;
+    *url2 = abspath_or_url2 = apr_pstrdup(pool, path_or_url2);
 
   if (!svn_path_is_url(path_or_url1))
-    SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1,
-                                    pool));
+    {
+      SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool));
+      SVN_ERR(svn_wc__node_get_url(url1, ctx->wc_ctx, abspath_or_url1,
+                                   pool, pool));
+    }
   else
-    abspath_or_url1 = path_or_url1;
-
-  /* Figure out URL1 and URL2. */
-  SVN_ERR(convert_to_url(url1, ctx->wc_ctx, abspath_or_url1,
-                         pool, pool));
-  SVN_ERR(convert_to_url(url2, ctx->wc_ctx, abspath_or_url2,
-                         pool, pool));
+    *url1 = abspath_or_url1 = apr_pstrdup(pool, path_or_url1);
 
   /* We need exactly one BASE_PATH, so we'll let the BASE_PATH
      calculated for PATH_OR_URL2 override the one for PATH_OR_URL1
@@ -1488,18 +1489,23 @@ diff_prepare_repos_repos(const char **ur
   else if (*kind2 == svn_node_none)
     SVN_ERR(check_diff_target_exists(*url2, *rev1, *rev2, *ra_session, pool));
 
+  SVN_ERR(svn_ra_get_repos_root2(*ra_session, &repos_root_url, pool));
+
   /* Choose useful anchors and targets for our two URLs. */
   *anchor1 = *url1;
   *anchor2 = *url2;
   *target1 = "";
   *target2 = "";
 
-  /* If one of the targets is a file, use the parent directory as anchor. */
-  if (*kind1 == svn_node_file || *kind2 == svn_node_file)
+  /* If none of the targets is the repository root open the parent directory
+     to allow describing replacement of the target itself */
+  if (strcmp(*url1, repos_root_url) != 0
+      && strcmp(*url2, repos_root_url) != 0)
     {
       svn_uri_split(anchor1, target1, *url1, pool);
       svn_uri_split(anchor2, target2, *url2, pool);
-      if (*base_path)
+      if (*base_path
+          && (*kind1 == svn_node_file || *kind2 == svn_node_file))
         *base_path = svn_dirent_dirname(*base_path, pool);
       SVN_ERR(svn_ra_reparent(*ra_session, *anchor1, pool));
     }
@@ -1630,300 +1636,6 @@ 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.
 
    PATH_OR_URL1 and PATH_OR_URL2 may be either URLs or the working copy paths.
@@ -1977,6 +1689,14 @@ diff_repos_repos(const svn_wc_diff_callb
                                    revision1, revision2, peg_revision,
                                    pool));
 
+  /* Set up the repos_diff editor on BASE_PATH, if available.
+     Otherwise, we just use "". */
+
+  SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
+                                      callbacks, callback_baton,
+                                      TRUE /* walk_deleted_dirs */,
+                                      pool, pool));
+
   /* Get actual URLs. */
   callback_baton->orig_path_1 = url1;
   callback_baton->orig_path_2 = url2;
@@ -1988,21 +1708,41 @@ 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)
+  /* The repository can bring in a new working copy, but not delete
+     everything. Luckily our new diff handler can just be reversed. */
+  if (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;
+      const char *str_tmp;
+      svn_revnum_t rev_tmp;
+
+      str_tmp = url2;
+      url2 = url1;
+      url1 = str_tmp;
+
+      rev_tmp = rev2;
+      rev2 = rev1;
+      rev1 = rev_tmp;
+
+      str_tmp = anchor2;
+      anchor2 = anchor1;
+      anchor1 = str_tmp;
+
+      str_tmp = target2;
+      target2 = target1;
+      target1 = str_tmp;
+
+      diff_processor = svn_diff__tree_processor_reverse_create(diff_processor,
+                                                               NULL, pool);
     }
 
+  /* Filter the first path component using a filter processor, until we fixed
+     the diff processing to handle this directly */
+  if ((kind1 != svn_node_file && kind2 != svn_node_file) && target1[0] != '\0')
+  {
+    diff_processor = svn_diff__tree_processor_filter_create(diff_processor,
+                                                            target1, pool);
+  }
+
   /* 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.  */
@@ -2010,14 +1750,6 @@ diff_repos_repos(const svn_wc_diff_callb
                                                anchor1, NULL, NULL, FALSE,
                                                TRUE, ctx, pool));
 
-  /* Set up the repos_diff editor on BASE_PATH, if available.
-     Otherwise, we just use "". */
-
-  SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
-                                      callbacks, callback_baton,
-                                      TRUE /* walk_deleted_dirs */,
-                                      pool, pool));
-
   SVN_ERR(svn_client__get_diff_editor2(
                 &diff_editor, &diff_edit_baton,
                 extra_ra_session, depth,
@@ -2028,175 +1760,18 @@ diff_repos_repos(const svn_wc_diff_callb
                 pool));
 
   /* We want to switch our txn into URL2 */
-  SVN_ERR(svn_ra_do_diff3
-          (ra_session, &reporter, &reporter_baton, rev2, target1,
-           depth, ignore_ancestry, TRUE /* text_deltas */,
-           url2, diff_editor, diff_edit_baton, pool));
+  SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton,
+                          rev2, target1,
+                          depth, ignore_ancestry, TRUE /* text_deltas */,
+                          url2, diff_editor, diff_edit_baton, pool));
 
   /* Drive the reporter; do the diff. */
   SVN_ERR(reporter->set_path(reporter_baton, "", rev1,
                              svn_depth_infinity,
                              FALSE, NULL,
                              pool));
-  return reporter->finish_report(reporter_baton, pool);
-}
-
-
-/* Using CALLBACKS, show a REPOS->WC diff for a file TARGET, which in the
- * working copy is at FILE2_ABSPATH. KIND1 is the node kind of the repository
- * target (either svn_node_file or svn_node_none). REV is the revision the
- * working file is diffed against. RA_SESSION points at the URL of the file
- * in the repository and is used to get the file's repository-version content,
- * if necessary. The other parameters are as in diff_repos_wc(). */
-static svn_error_t *
-diff_repos_wc_file_target(const char *target,
-                          const char *file2_abspath,
-                          svn_node_kind_t kind1,
-                          svn_revnum_t rev,
-                          svn_boolean_t reverse,
-                          svn_boolean_t show_copies_as_adds,
-                          const svn_wc_diff_callbacks4_t *callbacks,
-                          void *callback_baton,
-                          svn_ra_session_t *ra_session,
-                          svn_client_ctx_t *ctx,
-                          apr_pool_t *scratch_pool)
-{
-  const char *file1_abspath;
-  svn_stream_t *file1_content;
-  svn_stream_t *file2_content;
-  apr_hash_t *file1_props = NULL;
-  apr_hash_t *file2_props;
-  svn_boolean_t is_copy = FALSE;
-  apr_hash_t *keywords = NULL;
-  svn_string_t *keywords_prop;
-  svn_subst_eol_style_t eol_style;
-  const char *eol_str;
-
-  /* Get content and props of file 1 (the remote file). */
-  SVN_ERR(svn_stream_open_unique(&file1_content, &file1_abspath, NULL,
-                                 svn_io_file_del_on_pool_cleanup,
-                                 scratch_pool, scratch_pool));
-  if (kind1 == svn_node_file)
-    {
-      if (show_copies_as_adds)
-        SVN_ERR(svn_wc__node_get_origin(&is_copy, 
-                                        NULL, NULL, NULL, NULL, NULL,
-                                        ctx->wc_ctx, file2_abspath,
-                                        FALSE, scratch_pool, scratch_pool));
-      /* If showing copies as adds, diff against the empty file. */
-      if (!(show_copies_as_adds && is_copy))
-        SVN_ERR(svn_ra_get_file(ra_session, "", rev, file1_content,
-                                NULL, &file1_props, scratch_pool));
-    }
-
-  SVN_ERR(svn_stream_close(file1_content));
-
-  SVN_ERR(svn_wc_prop_list2(&file2_props, ctx->wc_ctx, file2_abspath,
-                            scratch_pool, scratch_pool));
-
-  /* We might have to create a normalised version of the working file. */
-  svn_subst_eol_style_from_value(&eol_style, &eol_str,
-                                 apr_hash_get(file2_props,
-                                              SVN_PROP_EOL_STYLE,
-                                              APR_HASH_KEY_STRING));
-  keywords_prop = apr_hash_get(file2_props, SVN_PROP_KEYWORDS,
-                               APR_HASH_KEY_STRING);
-  if (keywords_prop)
-    SVN_ERR(svn_subst_build_keywords2(&keywords, keywords_prop->data,
-                                      NULL, NULL, 0, NULL,
-                                      scratch_pool));
-  if (svn_subst_translation_required(eol_style, SVN_SUBST_NATIVE_EOL_STR,
-                                     keywords, FALSE, TRUE))
-    {
-      svn_stream_t *working_content;
-      svn_stream_t *normalized_content;
-
-      SVN_ERR(svn_stream_open_readonly(&working_content, file2_abspath,
-                                       scratch_pool, scratch_pool));
-
-      /* Create a temporary file and copy normalised data into it. */
-      SVN_ERR(svn_stream_open_unique(&file2_content, &file2_abspath, NULL,
-                                     svn_io_file_del_on_pool_cleanup,
-                                     scratch_pool, scratch_pool));
-      normalized_content = svn_subst_stream_translated(
-                             file2_content, SVN_SUBST_NATIVE_EOL_STR,
-                             TRUE, keywords, FALSE, scratch_pool);
-      SVN_ERR(svn_stream_copy3(working_content, normalized_content,
-                               ctx->cancel_func, ctx->cancel_baton,
-                               scratch_pool));
-    }
 
-  if (kind1 == svn_node_file && !(show_copies_as_adds && is_copy))
-    {
-      SVN_ERR(callbacks->file_opened(NULL, NULL, target,
-                                     reverse ? SVN_INVALID_REVNUM : rev,
-                                     callback_baton, scratch_pool));
-
-      if (reverse)
-        SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, target,
-                                        file2_abspath, file1_abspath,
-                                        SVN_INVALID_REVNUM, rev,
-                                        apr_hash_get(file2_props,
-                                                     SVN_PROP_MIME_TYPE,
-                                                     APR_HASH_KEY_STRING),
-                                        apr_hash_get(file1_props,
-                                                     SVN_PROP_MIME_TYPE,
-                                                     APR_HASH_KEY_STRING),
-                                        make_regular_props_array(
-                                          file1_props, scratch_pool,
-                                          scratch_pool),
-                                        file2_props,
-                                        callback_baton, scratch_pool));
-      else
-        SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, target,
-                                        file1_abspath, file2_abspath,
-                                        rev, SVN_INVALID_REVNUM,
-                                        apr_hash_get(file1_props,
-                                                     SVN_PROP_MIME_TYPE,
-                                                     APR_HASH_KEY_STRING),
-                                        apr_hash_get(file2_props,
-                                                     SVN_PROP_MIME_TYPE,
-                                                     APR_HASH_KEY_STRING),
-                                        make_regular_props_array(
-                                          file2_props, scratch_pool,
-                                          scratch_pool),
-                                        file1_props,
-                                        callback_baton, scratch_pool));
-    }
-  else
-    {
-      if (reverse)
-        {
-          SVN_ERR(callbacks->file_deleted(NULL, NULL,
-                                          target, file2_abspath, file1_abspath,
-                                          apr_hash_get(file2_props,
-                                                       SVN_PROP_MIME_TYPE,
-                                                       APR_HASH_KEY_STRING),
-                                          NULL,
-                                          make_regular_props_hash(
-                                            file2_props, scratch_pool,
-                                            scratch_pool),
-                                          callback_baton, scratch_pool));
-        }
-      else
-        {
-          SVN_ERR(callbacks->file_added(NULL, NULL, NULL, target,
-                                        file1_abspath, file2_abspath,
-                                        rev, SVN_INVALID_REVNUM,
-                                        NULL,
-                                        apr_hash_get(file2_props,
-                                                     SVN_PROP_MIME_TYPE,
-                                                     APR_HASH_KEY_STRING),
-                                        NULL, SVN_INVALID_REVNUM,
-                                        make_regular_props_array(
-                                          file2_props, scratch_pool,
-                                          scratch_pool),
-                                        NULL,
-                                        callback_baton, scratch_pool));
-        }
-    }
-
-  return SVN_NO_ERROR;
+  return svn_error_trace(reporter->finish_report(reporter_baton, pool));
 }
 
 /* Perform a diff between a repository path and a working-copy path.
@@ -2225,8 +1800,9 @@ diff_repos_wc(const char *path_or_url1,
               void *callback_baton,
               struct diff_cmd_baton *cmd_baton,
               svn_client_ctx_t *ctx,
-              apr_pool_t *pool)
+              apr_pool_t *scratch_pool)
 {
+  apr_pool_t *pool = scratch_pool;
   const char *url1, *anchor, *anchor_url, *target;
   svn_revnum_t rev;
   svn_ra_session_t *ra_session;
@@ -2243,22 +1819,27 @@ diff_repos_wc(const char *path_or_url1,
   svn_node_kind_t kind1;
   svn_node_kind_t kind2;
   svn_boolean_t is_copy;
-  svn_revnum_t copyfrom_rev;
-  const char *copy_source_repos_relpath;
-  const char *copy_source_repos_root_url;
+  svn_revnum_t cf_revision;
+  const char *cf_repos_relpath;
+  const char *cf_repos_root_url;
 
   SVN_ERR_ASSERT(! svn_path_is_url(path2));
 
   if (!svn_path_is_url(path_or_url1))
-    SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool));
+    {
+      SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool));
+      SVN_ERR(svn_wc__node_get_url(&url1, ctx->wc_ctx, abspath_or_url1,
+                                   pool, pool));
+    }
   else
-    abspath_or_url1 = path_or_url1;
+    {
+      url1 = path_or_url1;
+      abspath_or_url1 = path_or_url1;
+    }
 
   SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, pool));
 
   /* Convert path_or_url1 to a URL to feed to do_diff. */
-  SVN_ERR(convert_to_url(&url1, ctx->wc_ctx, abspath_or_url1, pool, pool));
-
   SVN_ERR(svn_wc_get_actual_target2(&anchor, &target,
                                     ctx->wc_ctx, path2,
                                     pool, pool));
@@ -2267,10 +1848,7 @@ diff_repos_wc(const char *path_or_url1,
   SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, pool));
   SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath,
                                pool, pool));
-  if (! anchor_url)
-    return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
-                             _("Directory '%s' has no URL"),
-                             svn_dirent_local_style(anchor, pool));
+  SVN_ERR_ASSERT(anchor_url != NULL);
 
   /* If we are performing a pegged diff, we need to find out what our
      actual URLs will be. */
@@ -2319,32 +1897,14 @@ diff_repos_wc(const char *path_or_url1,
     cmd_baton->revnum2 = rev;
 
   /* Check if our diff target is a copied node. */
-  SVN_ERR(svn_wc__node_get_origin(&is_copy, 
-                                  &copyfrom_rev,
-                                  &copy_source_repos_relpath,
-                                  &copy_source_repos_root_url,
+  SVN_ERR(svn_wc__node_get_origin(&is_copy,
+                                  &cf_revision,
+                                  &cf_repos_relpath,
+                                  &cf_repos_root_url,
                                   NULL, NULL,
                                   ctx->wc_ctx, abspath2,
                                   FALSE, pool, pool));
 
-  /* If both diff targets can be diffed as files, fetch the appropriate
-   * file content from the repository and generate a diff against the
-   * local version of the file.
-   * However, if comparing the repository version of the file to the BASE
-   * tree version we can use the diff editor to transmit a delta instead
-   * of potentially huge file content. */
-  if ((!rev2_is_base || is_copy) &&
-      (kind1 == svn_node_file || kind1 == svn_node_none)
-       && kind2 == svn_node_file)
-    {
-      SVN_ERR(diff_repos_wc_file_target(target, abspath2, kind1, rev,
-                                        reverse, show_copies_as_adds,
-                                        callbacks, callback_baton,
-                                        ra_session, ctx, pool));
-
-      return SVN_NO_ERROR;
-    }
-
   /* Use the diff editor to generate the diff. */
   SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
                                 SVN_RA_CAPABILITY_DEPTH, pool));
@@ -2353,7 +1913,7 @@ diff_repos_wc(const char *path_or_url1,
                                   anchor_abspath,
                                   target,
                                   depth,
-                                  ignore_ancestry,
+                                  ignore_ancestry || is_copy,
                                   show_copies_as_adds,
                                   use_git_diff_format,
                                   rev2_is_base,
@@ -2372,30 +1932,40 @@ diff_repos_wc(const char *path_or_url1,
 
   if (is_copy)
     {
-      const char *copyfrom_url;
       const char *copyfrom_parent_url;
       const char *copyfrom_basename;
       svn_depth_t copy_depth;
 
       cmd_baton->repos_wc_diff_target_is_copy = TRUE;
 
-      /* We're diffing a locally copied/moved directory.
+      /* We're diffing a locally copied/moved node.
        * Describe the copy source to the reporter instead of the copy itself.
        * Doing the latter would generate a single add_directory() call to the
        * diff editor which results in an unexpected diff (the copy would
        * be shown as deleted). */
 
-      copyfrom_url = apr_pstrcat(pool, copy_source_repos_root_url, "/",
-                                 copy_source_repos_relpath, (char *)NULL);
-      svn_uri_split(&copyfrom_parent_url, &copyfrom_basename,
-                    copyfrom_url, pool);
+      if (cf_repos_relpath[0] == '\0')
+        {
+          copyfrom_parent_url = cf_repos_root_url;
+          copyfrom_basename = "";
+        }
+      else
+        {
+          const char *parent_relpath;
+          svn_relpath_split(&parent_relpath, &copyfrom_basename,
+                            cf_repos_relpath, scratch_pool);
+
+          copyfrom_parent_url = svn_path_url_add_component2(cf_repos_root_url,
+                                                            parent_relpath,
+                                                            scratch_pool);
+        }
       SVN_ERR(svn_ra_reparent(ra_session, copyfrom_parent_url, pool));
 
       /* Tell the RA layer we want a delta to change our txn to URL1 */ 
       SVN_ERR(svn_ra_do_diff3(ra_session,
                               &reporter, &reporter_baton,
                               rev,
-                              copyfrom_basename,
+                              target,
                               diff_depth,
                               ignore_ancestry,
                               TRUE,  /* text_deltas */
@@ -2405,9 +1975,23 @@ diff_repos_wc(const char *path_or_url1,
       /* Report the copy source. */
       SVN_ERR(svn_wc__node_get_depth(&copy_depth, ctx->wc_ctx, abspath2,
                                      pool));
-      SVN_ERR(reporter->set_path(reporter_baton, "", copyfrom_rev,
-                                 copy_depth, FALSE, NULL, pool));
-      
+
+      if (copy_depth == svn_depth_unknown)
+        copy_depth = svn_depth_infinity;
+
+      SVN_ERR(reporter->set_path(reporter_baton, "",
+                                 cf_revision,
+                                 copy_depth, FALSE, NULL, scratch_pool));
+
+      if (strcmp(target, copyfrom_basename) != 0)
+        SVN_ERR(reporter->link_path(reporter_baton, target,
+                                    svn_path_url_add_component2(
+                                                cf_repos_root_url,
+                                                cf_repos_relpath,
+                                                scratch_pool),
+                                    cf_revision,
+                                    copy_depth, FALSE, NULL, scratch_pool));
+
       /* Finish the report to generate the diff. */
       SVN_ERR(reporter->finish_report(reporter_baton, pool));
     }
@@ -2508,6 +2092,7 @@ do_diff(const svn_wc_diff_callbacks4_t *
               SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool));
 
               SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2,
+                                                       depth,
                                                        callbacks,
                                                        callback_baton,
                                                        ctx, pool));
@@ -2677,41 +2262,50 @@ diff_summarize_repos_repos(svn_client_di
                                    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, FALSE, 
-                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;
-    }
-
+  /* Set up the repos_diff editor. */
   SVN_ERR(svn_client__get_diff_summarize_callbacks(
             &callbacks, &callback_baton,
             target1, FALSE, summarize_func, summarize_baton, pool));
 
+  SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
+                                      callbacks, callback_baton,
+                                      TRUE /* walk_deleted_dirs */,
+                                      pool, pool));
+
+
+  /* The repository can bring in a new working copy, but not delete
+     everything. Luckily our new diff handler can just be reversed. */
+  if (kind2 == svn_node_none)
+    {
+      const char *str_tmp;
+      svn_revnum_t rev_tmp;
+
+      str_tmp = url2;
+      url2 = url1;
+      url1 = str_tmp;
+
+      rev_tmp = rev2;
+      rev2 = rev1;
+      rev1 = rev_tmp;
+
+      str_tmp = anchor2;
+      anchor2 = anchor1;
+      anchor1 = str_tmp;
+
+      str_tmp = target2;
+      target2 = target1;
+      target1 = str_tmp;
+
+      diff_processor = svn_diff__tree_processor_reverse_create(diff_processor,
+                                                               NULL, pool);
+    }
+
   /* Now, we open an extra RA session to the correct anchor
      location for URL1.  This is used to get the kind of deleted paths.  */
   SVN_ERR(svn_client__open_ra_session_internal(&extra_ra_session, NULL,
                                                anchor1, NULL, NULL, FALSE,
                                                TRUE, ctx, pool));
 
-  /* Set up the repos_diff editor. */
-
-  SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
-                                      callbacks, callback_baton,
-                                      TRUE /* walk_deleted_dirs */,
-                                      pool, pool));
-
   SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
                                        extra_ra_session,
                                        depth,
@@ -2732,7 +2326,7 @@ diff_summarize_repos_repos(svn_client_di
   SVN_ERR(reporter->set_path(reporter_baton, "", rev1,
                              svn_depth_infinity,
                              FALSE, NULL, pool));
-  return reporter->finish_report(reporter_baton, pool);
+  return svn_error_trace(reporter->finish_report(reporter_baton, pool));
 }
 
 /* This is basically just the guts of svn_client_diff_summarize[_peg]2(). */
@@ -2813,6 +2407,7 @@ do_diff_summarize(svn_client_diff_summar
                      summarize_func, summarize_baton, pool));
 
              SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2,
+                                                      depth,
                                                       callbacks,
                                                       callback_baton,
                                                       ctx, pool));
@@ -2985,6 +2580,8 @@ svn_client_diff6(const apr_array_header_
   diff_cmd_baton.use_git_diff_format = use_git_diff_format;
   diff_cmd_baton.no_diff_added = no_diff_added;
   diff_cmd_baton.no_diff_deleted = no_diff_deleted;
+  diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds;
+
   diff_cmd_baton.wc_ctx = ctx->wc_ctx;
   diff_cmd_baton.ra_session = NULL;
   diff_cmd_baton.anchor = NULL;
@@ -3046,6 +2643,8 @@ svn_client_diff_peg6(const apr_array_hea
   diff_cmd_baton.use_git_diff_format = use_git_diff_format;
   diff_cmd_baton.no_diff_added = no_diff_added;
   diff_cmd_baton.no_diff_deleted = no_diff_deleted;
+  diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds;
+
   diff_cmd_baton.wc_ctx = ctx->wc_ctx;
   diff_cmd_baton.ra_session = NULL;
   diff_cmd_baton.anchor = NULL;

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/diff_local.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/diff_local.c?rev=1445479&r1=1445478&r2=1445479&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/diff_local.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/diff_local.c Wed Feb 13 06:37:54 2013
@@ -287,9 +287,16 @@ arbitrary_diff_walker(void *baton, const
                       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.
+/* Another forward declaration. */
+static svn_error_t *
+arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b,
+                        const char *local_abspath,
+                        svn_depth_t depth,
+                        apr_pool_t *scratch_pool);
+
+/* Produce a diff of depth DEPTH 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
@@ -300,6 +307,7 @@ do_arbitrary_dirs_diff(const char *local
                        const char *local_abspath2,
                        const char *root_abspath1,
                        const char *root_abspath2,
+                       svn_depth_t depth,
                        const svn_wc_diff_callbacks4_t *callbacks,
                        void *diff_baton,
                        svn_client_ctx_t *ctx,
@@ -331,22 +339,25 @@ do_arbitrary_dirs_diff(const char *local
                                    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));
-
+  if (depth <= svn_depth_immediates)
+    SVN_ERR(arbitrary_diff_this_dir(&b, local_abspath1, depth, scratch_pool));
+  else if (depth == svn_depth_infinity)
+    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
+/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH,
+ * using information from the arbitrary_diff_walker_baton B.
+ * 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)
+arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b,
+                        const char *local_abspath,
+                        svn_depth_t depth,
+                        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;
@@ -359,12 +370,6 @@ arbitrary_diff_walker(void *baton, const
   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))
@@ -398,12 +403,15 @@ arbitrary_diff_walker(void *baton, const
                                    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 (depth > svn_depth_empty)
+    {
+      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)
     {
@@ -425,14 +433,20 @@ arbitrary_diff_walker(void *baton, const
                                                 b->diff_baton,
                                                 scratch_pool));
 
-      /* Read directory entries. */
-      SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2,
-                                  TRUE, /* only_check_type */
-                                  scratch_pool, scratch_pool));
+      if (depth > svn_depth_empty)
+        {
+          /* Read directory entries. */
+          SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2,
+                                      TRUE, /* only_check_type */
+                                      scratch_pool, scratch_pool));
+        }
     }
-  else
+  else if (depth > svn_depth_empty)
     dirents2 = apr_hash_make(scratch_pool);
 
+  if (depth <= svn_depth_empty)
+    return SVN_NO_ERROR;
+
   /* Compare dirents1 to dirents2 and show added/deleted/changed files. */
   merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2,
                                   NULL, NULL);
@@ -482,7 +496,25 @@ arbitrary_diff_walker(void *baton, const
 
       if (dirent1->kind == svn_node_dir &&
           dirent2->kind == svn_node_dir)
-        continue;
+        {
+          if (depth == svn_depth_immediates)
+            {
+              /* Not using the walker, so show property diffs on these dirs. */
+              SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
+                                             b->root1_abspath, b->root2_abspath,
+                                             svn_depth_empty,
+                                             b->callbacks, b->diff_baton,
+                                             b->ctx, iterpool));
+            }
+          else
+            {
+              /* Either the walker will visit these directories (with
+               * depth=infinity) and they will be processed as 'this dir'
+               * later, or we're showing file children only (depth=files). */
+              continue;
+            }
+
+        }
 
       /* Files that exist only in dirents1. */
       if (dirent1->kind == svn_node_file &&
@@ -521,10 +553,14 @@ arbitrary_diff_walker(void *baton, const
 
       /* 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 &&
+      if (depth > svn_depth_files &&
+          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,
+                                       depth <= svn_depth_immediates
+                                         ? svn_depth_empty
+                                         : svn_depth_infinity ,
                                        b->callbacks, b->diff_baton,
                                        b->ctx, iterpool));
     }
@@ -534,14 +570,32 @@ arbitrary_diff_walker(void *baton, const
   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. */
+/* 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;
+
+  if (b->ctx->cancel_func)
+    SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
+
+  if (finfo->filetype != APR_DIR)
+    return SVN_NO_ERROR;
+
+  SVN_ERR(arbitrary_diff_this_dir(b, local_abspath, svn_depth_infinity,
+                                  scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_client__arbitrary_nodes_diff(const char *local_abspath1,
                                  const char *local_abspath2,
+                                 svn_depth_t depth,
                                  const svn_wc_diff_callbacks4_t *callbacks,
                                  void *diff_baton,
                                  svn_client_ctx_t *ctx,
@@ -558,6 +612,9 @@ svn_client__arbitrary_nodes_diff(const c
                              _("'%s' is not the same node kind as '%s'"),
                              local_abspath1, local_abspath2);
 
+  if (depth == svn_depth_unknown)
+    depth = svn_depth_infinity;
+
   if (kind1 == svn_node_file)
     SVN_ERR(do_arbitrary_files_diff(local_abspath1, local_abspath2,
                                     svn_dirent_basename(local_abspath1,
@@ -567,7 +624,7 @@ svn_client__arbitrary_nodes_diff(const c
                                     ctx, scratch_pool));
   else if (kind1 == svn_node_dir)
     SVN_ERR(do_arbitrary_dirs_diff(local_abspath1, local_abspath2,
-                                   NULL, NULL,
+                                   NULL, NULL, depth,
                                    callbacks, diff_baton,
                                    ctx, scratch_pool));
   else

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/externals.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/externals.c?rev=1445479&r1=1445478&r2=1445479&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/externals.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/externals.c Wed Feb 13 06:37:54 2013
@@ -212,9 +212,21 @@ switch_dir_external(const char *local_ab
 
              To do so, we need to know the repository root URL of the
              external working copy as it currently sits. */
-          SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, &repos_uuid,
-                                              ctx->wc_ctx, local_abspath,
-                                              pool, subpool));
+          err = svn_wc__node_get_repos_info(NULL, NULL,
+                                            &repos_root_url, &repos_uuid,
+                                            ctx->wc_ctx, local_abspath,
+                                            pool, subpool);
+          if (err)
+            {
+              if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
+                  && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
+                return svn_error_trace(err);
+
+              svn_error_clear(err);
+              repos_root_url = NULL;
+              repos_uuid = NULL;
+            }
+
           if (repos_root_url)
             {
               /* If the new external target URL is not obviously a
@@ -315,7 +327,8 @@ switch_dir_external(const char *local_ab
                                         FALSE, FALSE, timestamp_sleep,
                                         ctx, pool));
 
-  SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url,
+  SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL,
+                                      &repos_root_url,
                                       &repos_uuid,
                                       ctx->wc_ctx, local_abspath,
                                       pool, pool));
@@ -739,6 +752,7 @@ handle_external_item_change(svn_client_c
             const char *local_repos_root_url;
             const char *local_repos_uuid;
             const char *ext_repos_relpath;
+            svn_error_t *err;
 
             /*
              * The working copy library currently requires that all files
@@ -749,11 +763,22 @@ handle_external_item_change(svn_client_c
              * sure both URLs point to the same repository. See issue #4087.
              */
 
-            SVN_ERR(svn_wc__node_get_repos_info(&local_repos_root_url,
-                                                &local_repos_uuid,
-                                                ctx->wc_ctx,
-                                                parent_dir_abspath,
-                                                scratch_pool, scratch_pool));
+            err = svn_wc__node_get_repos_info(NULL, NULL,
+                                              &local_repos_root_url,
+                                              &local_repos_uuid,
+                                              ctx->wc_ctx, parent_dir_abspath,
+                                              scratch_pool, scratch_pool);
+            if (err)
+              {
+                if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
+                    && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
+                  return svn_error_trace(err);
+
+                svn_error_clear(err);
+                local_repos_root_url = NULL;
+                local_repos_uuid = NULL;
+              }
+
             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 ||

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/locking_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/locking_commands.c?rev=1445479&r1=1445478&r2=1445479&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/locking_commands.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/locking_commands.c Wed Feb 13 06:37:54 2013
@@ -189,6 +189,14 @@ condense_targets(const char **common_par
   return SVN_NO_ERROR;
 }
 
+/* Lock info. Used in organize_lock_targets.
+   ### Maybe return this instead of the ugly hashes? */
+struct wc_lock_item_t
+{
+  svn_revnum_t revision;
+  const char *lock_token;
+};
+
 /* Set *COMMON_PARENT_URL to the nearest common parent URL of all TARGETS.
  * If TARGETS are local paths, then the entry for each path is examined
  * and *COMMON_PARENT is set to the common parent URL for all the
@@ -225,7 +233,7 @@ organize_lock_targets(const char **commo
                       const apr_array_header_t *targets,
                       svn_boolean_t do_lock,
                       svn_boolean_t force,
-                      svn_client_ctx_t *ctx,
+                      svn_wc_context_t *wc_ctx,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool)
 {
@@ -234,6 +242,7 @@ organize_lock_targets(const char **commo
   apr_hash_t *rel_targets_ret = apr_hash_make(result_pool);
   apr_hash_t *rel_fs_paths = NULL;
   apr_array_header_t *rel_targets;
+  apr_hash_t *wc_info = apr_hash_make(scratch_pool);
   svn_boolean_t url_mode;
   int i;
 
@@ -289,20 +298,38 @@ organize_lock_targets(const char **commo
                                    sizeof(const char *));
       for (i = 0; i < rel_targets->nelts; i++)
         {
-          const char *rel_target, *local_abspath, *target_url;
+          const char *rel_target;
+          const char *repos_relpath;
+          const char *repos_root_url;
+          const char *target_url;
+          struct wc_lock_item_t *wli;
+          const char *local_abspath;
 
           svn_pool_clear(iterpool);
 
           rel_target = APR_ARRAY_IDX(rel_targets, i, const char *);
-          local_abspath = svn_dirent_join(common_dirent, rel_target, iterpool);
-          SVN_ERR(svn_wc__node_get_url(&target_url, ctx->wc_ctx, local_abspath,
-                                       scratch_pool, iterpool));
-          if (! target_url)
-            return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
-                                     _("'%s' has no URL"),
+          local_abspath = svn_dirent_join(common_dirent, rel_target, scratch_pool);
+          wli = apr_pcalloc(scratch_pool, sizeof(*wli));
+
+          SVN_ERR(svn_wc__node_get_base(&wli->revision, &repos_relpath,
+                                        &repos_root_url, NULL,
+                                        &wli->lock_token,
+                                        wc_ctx, local_abspath,
+                                        result_pool, iterpool));
+
+          /* Node exists in BASE? */
+          if (! repos_root_url || !repos_relpath)
+            return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+                                     _("The node '%s' was not found."),
                                      svn_dirent_local_style(local_abspath,
                                                             iterpool));
 
+          apr_hash_set(wc_info, local_abspath, APR_HASH_KEY_STRING, wli);
+
+          target_url = svn_path_url_add_component2(repos_root_url,
+                                                   repos_relpath,
+                                                   scratch_pool);
+
           APR_ARRAY_PUSH(target_urls, const char *) = target_url;
         }
 
@@ -320,7 +347,8 @@ organize_lock_targets(const char **commo
       rel_fs_paths = apr_hash_make(result_pool);
       for (i = 0; i < rel_targets->nelts; i++)
         {
-          const char *rel_target, *rel_url, *abs_path;
+          const char *rel_target, *rel_url;
+          const char *local_abspath;
 
           svn_pool_clear(iterpool);
 
@@ -336,35 +364,49 @@ organize_lock_targets(const char **commo
              revision of the dirent target with which it is associated
              (if our caller is locking) or to a (possible empty) lock
              token string (if the caller is unlocking). */
-          abs_path = svn_dirent_join(common_dirent, rel_target, iterpool);
+          local_abspath = svn_dirent_join(common_dirent, rel_target, iterpool);
 
           if (do_lock) /* Lock. */
             {
               svn_revnum_t *revnum;
+              struct wc_lock_item_t *wli;
               revnum = apr_palloc(result_pool, sizeof(* revnum));
-              SVN_ERR(svn_wc__node_get_base(revnum, NULL, NULL, NULL,
-                                            ctx->wc_ctx, abs_path,
-                                            result_pool, iterpool));
+
+              wli = apr_hash_get(wc_info, local_abspath, APR_HASH_KEY_STRING);
+
+              SVN_ERR_ASSERT(wli != NULL);
+
+              *revnum = wli->revision;
+
               apr_hash_set(rel_targets_ret, rel_url,
                            APR_HASH_KEY_STRING, revnum);
             }
           else /* Unlock. */
             {
-              const char *lock_token = NULL;
+              const char *lock_token;
+              struct wc_lock_item_t *wli;
 
               /* If not forcing the unlock, get the lock token. */
               if (! force)
                 {
-                  SVN_ERR(svn_wc__node_get_lock_info(&lock_token, NULL, NULL,
-                                                     NULL, ctx->wc_ctx,
-                                                     abs_path, result_pool,
-                                                     iterpool));
-                  if (! lock_token)
+                  wli = apr_hash_get(wc_info, local_abspath,
+                                     APR_HASH_KEY_STRING);
+
+                  SVN_ERR_ASSERT(wli != NULL);
+
+                  if (! wli->lock_token)
                     return svn_error_createf(
                                SVN_ERR_CLIENT_MISSING_LOCK_TOKEN, NULL,
                                _("'%s' is not locked in this working copy"),
-                               abs_path);
+                               svn_dirent_local_style(local_abspath,
+                                                      scratch_pool));
+
+                  lock_token = wli->lock_token
+                                ? apr_pstrdup(result_pool, wli->lock_token)
+                                : NULL;
                 }
+              else
+                lock_token = NULL;
 
               /* If breaking a lock, we shouldn't pass any lock token. */
               apr_hash_set(rel_targets_ret, rel_url, APR_HASH_KEY_STRING,
@@ -444,7 +486,7 @@ svn_client_lock(const apr_array_header_t
 
   SVN_ERR(organize_lock_targets(&common_parent_url, &base_dir, &path_revs,
                                 &urls_to_paths, targets, TRUE, steal_lock,
-                                ctx, pool, pool));
+                                ctx->wc_ctx, pool, pool));
 
   /* Open an RA session to the common parent of TARGETS. */
   if (base_dir)
@@ -484,7 +526,7 @@ svn_client_unlock(const apr_array_header
 
   SVN_ERR(organize_lock_targets(&common_parent_url, &base_dir, &path_tokens,
                                 &urls_to_paths, targets, FALSE, break_lock,
-                                ctx, pool, pool));
+                                ctx->wc_ctx, pool, pool));
 
   /* Open an RA session. */
   if (base_dir)