You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2013/01/06 03:33:39 UTC

svn commit: r1429457 [5/21] - in /subversion/branches/tree-read-api: ./ build/ build/ac-macros/ build/generator/templates/ build/win32/ contrib/server-side/svncutter/ doc/ subversion/bindings/cxxhl/include/ subversion/bindings/cxxhl/include/svncxxhl/ s...

Modified: subversion/branches/tree-read-api/subversion/libsvn_client/export.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_client/export.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_client/export.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_client/export.c Sun Jan  6 02:33:34 2013
@@ -43,8 +43,13 @@
 
 #include "svn_private_config.h"
 #include "private/svn_subr_private.h"
+#include "private/svn_delta_private.h"
 #include "private/svn_wc_private.h"
 
+#ifndef ENABLE_EV2_IMPL
+#define ENABLE_EV2_IMPL 0
+#endif
+
 
 /*** Code. ***/
 
@@ -859,26 +864,6 @@ close_file(void *file_baton,
 }
 
 static svn_error_t *
-fetch_kind_func(svn_kind_t *kind,
-                void *baton,
-                const char *path,
-                svn_revnum_t base_revision,
-                apr_pool_t *scratch_pool)
-{
-  /* We know the root of the edit is a directory. */
-  if (path[0] == '\0')
-    *kind = svn_kind_dir;
-
-  /* ### TODO: We could possibly fetch the kind of the object in question
-         from the server with a second ra_session, but right now this
-         seems to work. */
-  else
-    *kind = svn_kind_unknown;
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
 fetch_props_func(apr_hash_t **props,
                  void *baton,
                  const char *path,
@@ -908,16 +893,14 @@ fetch_base_func(const char **filename,
 }
 
 static svn_error_t *
-get_editor(const svn_delta_editor_t **export_editor,
-           void **edit_baton,
-           struct edit_baton *eb,
-           svn_client_ctx_t *ctx,
-           apr_pool_t *result_pool,
-           apr_pool_t *scratch_pool)
+get_editor_ev1(const svn_delta_editor_t **export_editor,
+               void **edit_baton,
+               struct edit_baton *eb,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool)
 {
   svn_delta_editor_t *editor = svn_delta_default_editor(result_pool);
-  svn_delta_shim_callbacks_t *shim_callbacks =
-                            svn_delta_shim_callbacks_default(result_pool);
   
   editor->set_target_revision = set_target_revision;
   editor->open_root = open_root;
@@ -936,19 +919,453 @@ get_editor(const svn_delta_editor_t **ex
                                             edit_baton,
                                             result_pool));
 
-  shim_callbacks->fetch_kind_func = fetch_kind_func;
-  shim_callbacks->fetch_props_func = fetch_props_func;
-  shim_callbacks->fetch_base_func = fetch_base_func;
-  shim_callbacks->fetch_baton = eb;
-
-  SVN_ERR(svn_editor__insert_shims(export_editor, edit_baton,
-                                   *export_editor, *edit_baton,
-                                   NULL, NULL, shim_callbacks,
-                                   result_pool, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+
+/*** The Ev2 Implementation ***/
+
+static svn_error_t *
+add_file_ev2(void *baton,
+             const char *relpath,
+             const svn_checksum_t *checksum,
+             svn_stream_t *contents,
+             apr_hash_t *props,
+             svn_revnum_t replaces_rev,
+             apr_pool_t *scratch_pool)
+{
+  struct edit_baton *eb = baton;
+  const char *full_path = svn_dirent_join(eb->root_path, relpath,
+                                          scratch_pool);
+  /* RELPATH is not canonicalized, i.e. it may still contain spaces etc.
+   * but EB->root_url is. */
+  const char *full_url = svn_path_url_add_component2(eb->root_url,
+                                                     relpath,
+                                                     scratch_pool);
+  const svn_string_t *val;
+  /* The four svn: properties we might actually care about. */
+  const svn_string_t *eol_style_val = NULL;
+  const svn_string_t *keywords_val = NULL;
+  const svn_string_t *executable_val = NULL;
+  svn_boolean_t special = FALSE;
+  /* Any keyword vals to be substituted */
+  const char *revision = NULL;
+  const char *author = NULL;
+  apr_time_t date = 0; 
+
+  /* Look at any properties for additional information. */
+  if ( (val = apr_hash_get(props, SVN_PROP_EOL_STYLE, APR_HASH_KEY_STRING)) )
+    eol_style_val = val;
+
+  if ( !eb->ignore_keywords && (val = apr_hash_get(props, SVN_PROP_KEYWORDS,
+                                                   APR_HASH_KEY_STRING)) )
+    keywords_val = val;
+
+  if ( (val = apr_hash_get(props, SVN_PROP_EXECUTABLE, APR_HASH_KEY_STRING)) )
+    executable_val = val;
+  
+  /* Try to fill out the baton's keywords-structure too. */
+  if ( (val = apr_hash_get(props, SVN_PROP_ENTRY_COMMITTED_REV,
+                           APR_HASH_KEY_STRING)) )
+    revision = val->data;
+
+  if ( (val = apr_hash_get(props, SVN_PROP_ENTRY_COMMITTED_DATE,
+                           APR_HASH_KEY_STRING)) )
+    SVN_ERR(svn_time_from_cstring(&date, val->data, scratch_pool));
+  
+  if ( (val = apr_hash_get(props, SVN_PROP_ENTRY_LAST_AUTHOR,
+                           APR_HASH_KEY_STRING)) )
+    author = val->data;
+
+  if ( (val = apr_hash_get(props, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING)) )
+    special = TRUE;
+
+  if (special)
+    {
+      svn_stream_t *tmp_stream;
+
+      SVN_ERR(svn_subst_create_specialfile(&tmp_stream, full_path,
+                                           scratch_pool, scratch_pool));
+      SVN_ERR(svn_stream_copy3(contents, tmp_stream, eb->cancel_func,
+                               eb->cancel_baton, scratch_pool));
+    }
+  else
+    {
+      svn_stream_t *tmp_stream;
+      const char *tmppath;
+
+      /* Create a temporary file in the same directory as the file. We're going
+         to rename the thing into place when we're done. */
+      SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmppath,
+                                     svn_dirent_dirname(full_path,
+                                                        scratch_pool),
+                                     svn_io_file_del_none,
+                                     scratch_pool, scratch_pool));
+
+      /* Possibly wrap the stream to be translated, as dictated by
+         the props. */
+      if (eol_style_val || keywords_val)
+        {
+          svn_subst_eol_style_t style;
+          const char *eol = NULL;
+          svn_boolean_t repair = FALSE;
+          apr_hash_t *final_kw = NULL;
+
+          if (eol_style_val)
+            {
+              SVN_ERR(get_eol_style(&style, &eol, eol_style_val->data,
+                                    eb->native_eol));
+              repair = TRUE;
+            }
+
+          if (keywords_val)
+            SVN_ERR(svn_subst_build_keywords2(&final_kw, keywords_val->data,
+                                              revision, full_url, date,
+                                              author, scratch_pool));
+
+          /* Writing through a translated stream is more efficient than
+             reading through one, so we wrap TMP_STREAM and not CONTENTS. */
+          tmp_stream = svn_subst_stream_translated(tmp_stream, eol, repair,
+                                                   final_kw, TRUE, /* expand */
+                                                   scratch_pool);
+        }
+
+      SVN_ERR(svn_stream_copy3(contents, tmp_stream, eb->cancel_func,
+                               eb->cancel_baton, scratch_pool));
+
+      /* Move the file into place. */
+      SVN_ERR(svn_io_file_rename(tmppath, full_path, scratch_pool));
+    }
+
+  if (executable_val)
+    SVN_ERR(svn_io_set_file_executable(full_path, TRUE, FALSE, scratch_pool));
+
+  if (date && (! special))
+    SVN_ERR(svn_io_set_file_affected_time(date, full_path, scratch_pool));
+
+  if (eb->notify_func)
+    {
+      svn_wc_notify_t *notify = svn_wc_create_notify(full_path,
+                                                     svn_wc_notify_update_add,
+                                                     scratch_pool);
+      notify->kind = svn_node_file;
+      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+add_directory_ev2(void *baton,
+                  const char *relpath,
+                  const apr_array_header_t *children,
+                  apr_hash_t *props,
+                  svn_revnum_t replaces_rev,
+                  apr_pool_t *scratch_pool)
+{
+  struct edit_baton *eb = baton;
+  svn_node_kind_t kind;
+  const char *full_path = svn_dirent_join(eb->root_path, relpath,
+                                          scratch_pool);
+  svn_string_t *val;
+
+  SVN_ERR(svn_io_check_path(full_path, &kind, scratch_pool));
+  if (kind == svn_node_none)
+    SVN_ERR(svn_io_dir_make(full_path, APR_OS_DEFAULT, scratch_pool));
+  else if (kind == svn_node_file)
+    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+                             _("'%s' exists and is not a directory"),
+                             svn_dirent_local_style(full_path, scratch_pool));
+  else if (! (kind == svn_node_dir && eb->force))
+    return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
+                             _("'%s' already exists"),
+                             svn_dirent_local_style(full_path, scratch_pool));
+
+  if ( (val = apr_hash_get(props, SVN_PROP_EXTERNALS, APR_HASH_KEY_STRING)) )
+    SVN_ERR(add_externals(eb->externals, full_path, val));
+  
+  if (eb->notify_func)
+    {
+      svn_wc_notify_t *notify = svn_wc_create_notify(full_path,
+                                                     svn_wc_notify_update_add,
+                                                     scratch_pool);
+      notify->kind = svn_node_dir;
+      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+target_revision_func(void *baton,
+                     svn_revnum_t target_revision,
+                     apr_pool_t *scratch_pool)
+{
+  struct edit_baton *eb = baton;
+
+  *eb->target_revision = target_revision;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+get_editor_ev2(const svn_delta_editor_t **export_editor,
+               void **edit_baton,
+               struct edit_baton *eb,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool)
+{
+  svn_editor_t *editor;
+  struct svn_delta__extra_baton *exb = apr_pcalloc(result_pool, sizeof(*exb));
+  svn_boolean_t *found_abs_paths = apr_palloc(result_pool,
+                                              sizeof(*found_abs_paths));
+
+  exb->baton = eb;
+  exb->target_revision = target_revision_func;
+
+  SVN_ERR(svn_editor_create(&editor, eb, ctx->cancel_func, ctx->cancel_baton,
+                            result_pool, scratch_pool));
+  SVN_ERR(svn_editor_setcb_add_directory(editor, add_directory_ev2,
+                                         scratch_pool));
+  SVN_ERR(svn_editor_setcb_add_file(editor, add_file_ev2, scratch_pool));
+
+  *found_abs_paths = TRUE;
+
+  SVN_ERR(svn_delta__delta_from_editor(export_editor, edit_baton,
+                                       editor, NULL, NULL, found_abs_paths,
+                                       NULL, NULL,
+                                       fetch_props_func, eb,
+                                       fetch_base_func, eb,
+                                       exb, result_pool));
+
+  /* Create the root of the export. */
+  SVN_ERR(open_root_internal(eb->root_path, eb->force, eb->notify_func,
+                             eb->notify_baton, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+export_file_ev2(const char *from_path_or_url,
+                const char *to_path,
+                struct edit_baton *eb,
+                svn_client__pathrev_t *loc,
+                svn_ra_session_t *ra_session,
+                svn_boolean_t overwrite,
+                apr_pool_t *scratch_pool)
+{
+  svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
+  apr_hash_t *props;
+  svn_stream_t *tmp_stream;
+  svn_node_kind_t to_kind;
+
+  if (svn_path_is_empty(to_path))
+    {
+      if (from_is_url)
+        to_path = svn_uri_basename(from_path_or_url, scratch_pool);
+      else
+        to_path = svn_dirent_basename(from_path_or_url, NULL);
+      eb->root_path = to_path;
+    }
+  else
+    {
+      SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url,
+                                     from_is_url, scratch_pool));
+      eb->root_path = to_path;
+    }
+
+  SVN_ERR(svn_io_check_path(to_path, &to_kind, scratch_pool));
+
+  if ((to_kind == svn_node_file || to_kind == svn_node_unknown) &&
+      ! overwrite)
+    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                             _("Destination file '%s' exists, and "
+                               "will not be overwritten unless forced"),
+                             svn_dirent_local_style(to_path, scratch_pool));
+  else if (to_kind == svn_node_dir)
+    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                             _("Destination '%s' exists. Cannot "
+                               "overwrite directory with non-directory"),
+                             svn_dirent_local_style(to_path, scratch_pool));
+
+  tmp_stream = svn_stream_buffered(scratch_pool);
+
+  SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
+                          tmp_stream, NULL, &props, scratch_pool));
+
+  /* Since you cannot actually root an editor at a file, we manually drive
+   * a function of our editor. */
+  SVN_ERR(add_file_ev2(eb, "", NULL, tmp_stream, props, SVN_INVALID_REVNUM,
+                       scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+export_file(const char *from_path_or_url,
+            const char *to_path,
+            struct edit_baton *eb,
+            svn_client__pathrev_t *loc,
+            svn_ra_session_t *ra_session,
+            svn_boolean_t overwrite,
+            apr_pool_t *scratch_pool)
+{
+  apr_hash_t *props;
+  apr_hash_index_t *hi;
+  struct file_baton *fb = apr_pcalloc(scratch_pool, sizeof(*fb));
+  svn_node_kind_t to_kind;
+  svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
+
+  if (svn_path_is_empty(to_path))
+    {
+      if (from_is_url)
+        to_path = svn_uri_basename(from_path_or_url, scratch_pool);
+      else
+        to_path = svn_dirent_basename(from_path_or_url, NULL);
+      eb->root_path = to_path;
+    }
+  else
+    {
+      SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url,
+                                     from_is_url, scratch_pool));
+      eb->root_path = to_path;
+    }
+
+  SVN_ERR(svn_io_check_path(to_path, &to_kind, scratch_pool));
+
+  if ((to_kind == svn_node_file || to_kind == svn_node_unknown) &&
+      ! overwrite)
+    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                             _("Destination file '%s' exists, and "
+                               "will not be overwritten unless forced"),
+                             svn_dirent_local_style(to_path, scratch_pool));
+  else if (to_kind == svn_node_dir)
+    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+                             _("Destination '%s' exists. Cannot "
+                               "overwrite directory with non-directory"),
+                             svn_dirent_local_style(to_path, scratch_pool));
+
+  /* Since you cannot actually root an editor at a file, we
+   * manually drive a few functions of our editor. */
+
+  /* This is the equivalent of a parentless add_file(). */
+  fb->edit_baton = eb;
+  fb->path = eb->root_path;
+  fb->url = eb->root_url;
+  fb->pool = scratch_pool;
+
+  /* Copied from apply_textdelta(). */
+  SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath,
+                                 svn_dirent_dirname(fb->path, scratch_pool),
+                                 svn_io_file_del_none,
+                                 fb->pool, fb->pool));
+
+  /* Step outside the editor-likeness for a moment, to actually talk
+   * to the repository. */
+  /* ### note: the stream will not be closed */
+  SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
+                          fb->tmp_stream,
+                          NULL, &props, scratch_pool));
+
+  /* Push the props into change_file_prop(), to update the file_baton
+   * with information. */
+  for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi))
+    {
+      const char *propname = svn__apr_hash_index_key(hi);
+      const svn_string_t *propval = svn__apr_hash_index_val(hi);
+
+      SVN_ERR(change_file_prop(fb, propname, propval, scratch_pool));
+    }
+
+  /* And now just use close_file() to do all the keyword and EOL
+   * work, and put the file into place. */
+  SVN_ERR(close_file(fb, NULL, scratch_pool));
 
-   return SVN_NO_ERROR;
+  return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+export_directory(const char *from_path_or_url,
+                 const char *to_path,
+                 struct edit_baton *eb,
+                 svn_client__pathrev_t *loc,
+                 svn_ra_session_t *ra_session,
+                 svn_boolean_t overwrite,
+                 svn_boolean_t ignore_externals,
+                 svn_boolean_t ignore_keywords,
+                 svn_depth_t depth,
+                 const char *native_eol,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *scratch_pool)
+{
+  void *edit_baton;
+  const svn_delta_editor_t *export_editor;
+  const svn_ra_reporter3_t *reporter;
+  void *report_baton;
+  svn_boolean_t use_sleep = FALSE;
+  svn_node_kind_t kind;
+
+  if (!ENABLE_EV2_IMPL)
+    SVN_ERR(get_editor_ev1(&export_editor, &edit_baton, eb, ctx,
+                           scratch_pool, scratch_pool));
+  else
+    SVN_ERR(get_editor_ev2(&export_editor, &edit_baton, eb, ctx,
+                           scratch_pool, scratch_pool));
+
+  /* Manufacture a basic 'report' to the update reporter. */
+  SVN_ERR(svn_ra_do_update2(ra_session,
+                            &reporter, &report_baton,
+                            loc->rev,
+                            "", /* no sub-target */
+                            depth,
+                            FALSE, /* don't want copyfrom-args */
+                            export_editor, edit_baton, scratch_pool));
+
+  SVN_ERR(reporter->set_path(report_baton, "", loc->rev,
+                             /* Depth is irrelevant, as we're
+                                passing start_empty=TRUE anyway. */
+                             svn_depth_infinity,
+                             TRUE, /* "help, my dir is empty!" */
+                             NULL, scratch_pool));
+
+  SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
+
+  /* Special case: Due to our sly export/checkout method of updating an
+   * empty directory, no target will have been created if the exported
+   * item is itself an empty directory (export_editor->open_root never
+   * gets called, because there are no "changes" to make to the empty
+   * dir we reported to the repository).
+   *
+   * So we just create the empty dir manually; but we do it via
+   * open_root_internal(), in order to get proper notification.
+   */
+  SVN_ERR(svn_io_check_path(to_path, &kind, scratch_pool));
+  if (kind == svn_node_none)
+    SVN_ERR(open_root_internal
+            (to_path, overwrite, ctx->notify_func2,
+             ctx->notify_baton2, scratch_pool));
+
+  if (! ignore_externals && depth == svn_depth_infinity)
+    {
+      const char *repos_root_url;
+      const char *to_abspath;
+
+      SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
+                                     scratch_pool));
+      SVN_ERR(svn_dirent_get_absolute(&to_abspath, to_path, scratch_pool));
+      SVN_ERR(svn_client__export_externals(eb->externals,
+                                           from_path_or_url,
+                                           to_abspath, repos_root_url,
+                                           depth, native_eol,
+                                           ignore_keywords, &use_sleep,
+                                           ctx, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
 
 /*** Public Interfaces ***/
 
@@ -1009,135 +1426,19 @@ svn_client_export5(svn_revnum_t *result_
 
       if (kind == svn_node_file)
         {
-          apr_hash_t *props;
-          apr_hash_index_t *hi;
-          struct file_baton *fb = apr_pcalloc(pool, sizeof(*fb));
-          svn_node_kind_t to_kind;
-
-          if (svn_path_is_empty(to_path))
-            {
-              if (from_is_url)
-                to_path = svn_uri_basename(from_path_or_url, pool);
-              else
-                to_path = svn_dirent_basename(from_path_or_url, NULL);
-              eb->root_path = to_path;
-            }
+          if (!ENABLE_EV2_IMPL)
+            SVN_ERR(export_file(from_path_or_url, to_path, eb, loc, ra_session,
+                                overwrite, pool));
           else
-            {
-              SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url,
-                                             from_is_url, pool));
-              eb->root_path = to_path;
-            }
-
-          SVN_ERR(svn_io_check_path(to_path, &to_kind, pool));
-
-          if ((to_kind == svn_node_file || to_kind == svn_node_unknown) &&
-              ! overwrite)
-            return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                                     _("Destination file '%s' exists, and "
-                                       "will not be overwritten unless forced"),
-                                     svn_dirent_local_style(to_path, pool));
-          else if (to_kind == svn_node_dir)
-            return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                                     _("Destination '%s' exists. Cannot "
-                                       "overwrite directory with non-directory"),
-                                     svn_dirent_local_style(to_path, pool));
-
-          /* Since you cannot actually root an editor at a file, we
-           * manually drive a few functions of our editor. */
-
-          /* This is the equivalent of a parentless add_file(). */
-          fb->edit_baton = eb;
-          fb->path = eb->root_path;
-          fb->url = eb->root_url;
-          fb->pool = pool;
-
-          /* Copied from apply_textdelta(). */
-          SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath,
-                                         svn_dirent_dirname(fb->path, pool),
-                                         svn_io_file_del_none,
-                                         fb->pool, fb->pool));
-
-          /* Step outside the editor-likeness for a moment, to actually talk
-           * to the repository. */
-          /* ### note: the stream will not be closed */
-          SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
-                                  fb->tmp_stream,
-                                  NULL, &props, pool));
-
-          /* Push the props into change_file_prop(), to update the file_baton
-           * with information. */
-          for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
-            {
-              const char *propname = svn__apr_hash_index_key(hi);
-              const svn_string_t *propval = svn__apr_hash_index_val(hi);
-
-              SVN_ERR(change_file_prop(fb, propname, propval, pool));
-            }
-
-          /* And now just use close_file() to do all the keyword and EOL
-           * work, and put the file into place. */
-          SVN_ERR(close_file(fb, NULL, pool));
+            SVN_ERR(export_file_ev2(from_path_or_url, to_path, eb, loc,
+                                    ra_session, overwrite, pool));
         }
       else if (kind == svn_node_dir)
         {
-          void *edit_baton;
-          const svn_delta_editor_t *export_editor;
-          const svn_ra_reporter3_t *reporter;
-          void *report_baton;
-          svn_boolean_t use_sleep = FALSE;
-
-          SVN_ERR(get_editor(&export_editor, &edit_baton, eb, ctx,
-                             pool, pool));
-
-          /* Manufacture a basic 'report' to the update reporter. */
-          SVN_ERR(svn_ra_do_update2(ra_session,
-                                    &reporter, &report_baton,
-                                    loc->rev,
-                                    "", /* no sub-target */
-                                    depth,
-                                    FALSE, /* don't want copyfrom-args */
-                                    export_editor, edit_baton, pool));
-
-          SVN_ERR(reporter->set_path(report_baton, "", loc->rev,
-                                     /* Depth is irrelevant, as we're
-                                        passing start_empty=TRUE anyway. */
-                                     svn_depth_infinity,
-                                     TRUE, /* "help, my dir is empty!" */
-                                     NULL, pool));
-
-          SVN_ERR(reporter->finish_report(report_baton, pool));
-
-          /* Special case: Due to our sly export/checkout method of
-           * updating an empty directory, no target will have been created
-           * if the exported item is itself an empty directory
-           * (export_editor->open_root never gets called, because there
-           * are no "changes" to make to the empty dir we reported to the
-           * repository).
-           *
-           * So we just create the empty dir manually; but we do it via
-           * open_root_internal(), in order to get proper notification.
-           */
-          SVN_ERR(svn_io_check_path(to_path, &kind, pool));
-          if (kind == svn_node_none)
-            SVN_ERR(open_root_internal
-                    (to_path, overwrite, ctx->notify_func2,
-                     ctx->notify_baton2, pool));
-
-          if (! ignore_externals && depth == svn_depth_infinity)
-            {
-              const char *repos_root_url;
-              const char *to_abspath;
-
-              SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
-              SVN_ERR(svn_dirent_get_absolute(&to_abspath, to_path, pool));
-              SVN_ERR(svn_client__export_externals(eb->externals,
-                                                   from_path_or_url,
-                                                   to_abspath, repos_root_url,
-                                                   depth, native_eol,
-                                                   ignore_keywords, &use_sleep,
-                                                   ctx, pool));
-            }
+          SVN_ERR(export_directory(from_path_or_url, to_path,
+                                   eb, loc, ra_session, overwrite,
+                                   ignore_externals, ignore_keywords, depth,
+                                   native_eol, ctx, pool));
         }
       else if (kind == svn_node_none)
         {

Modified: subversion/branches/tree-read-api/subversion/libsvn_client/externals.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_client/externals.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_client/externals.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_client/externals.c Sun Jan  6 02:33:34 2013
@@ -399,8 +399,8 @@ switch_file_external(const char *local_a
     {
       const char *wcroot_abspath;
 
-      SVN_ERR(svn_wc__get_wc_root(&wcroot_abspath, ctx->wc_ctx, dir_abspath,
-                                  subpool, subpool));
+      SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, dir_abspath,
+                                 subpool, subpool));
 
       if (!svn_dirent_is_ancestor(wcroot_abspath, def_dir_abspath))
         return svn_error_createf(
@@ -484,7 +484,8 @@ switch_file_external(const char *local_a
                                               ctx, subpool));
     /* Get the external file's iprops. */
     SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "",
-                                       switch_loc->rev, subpool, subpool));
+                                       switch_loc->rev, TRUE,
+                                       subpool, subpool));
 
     SVN_ERR(svn_ra_reparent(ra_session, svn_uri_dirname(url, subpool),
                             subpool));

Modified: subversion/branches/tree-read-api/subversion/libsvn_client/info.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_client/info.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_client/info.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_client/info.c Sun Jan  6 02:33:34 2013
@@ -382,8 +382,8 @@ svn_client_get_wc_root(const char **wcro
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool)
 {
-  return svn_wc__get_wc_root(wcroot_abspath, ctx->wc_ctx, local_abspath,
-                             result_pool, scratch_pool);
+  return svn_wc__get_wcroot(wcroot_abspath, ctx->wc_ctx, local_abspath,
+                            result_pool, scratch_pool);
 }
 
 

Modified: subversion/branches/tree-read-api/subversion/libsvn_client/iprops.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_client/iprops.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_client/iprops.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_client/iprops.c Sun Jan  6 02:33:34 2013
@@ -41,10 +41,12 @@
 /*** Code. ***/
 
 /* Determine if LOCAL_ABSPATH needs an inherited property cache.  If it does,
-   then set *NEEDS_CACHE to TRUE, set it to FALSE otherwise. */
+   then set *NEEDS_CACHE to TRUE, set it to FALSE otherwise.  All other args
+   are as per svn_client__get_inheritable_props(). */
 static svn_error_t *
 need_to_cache_iprops(svn_boolean_t *needs_cache,
                      const char *local_abspath,
+                     svn_ra_session_t *ra_session,
                      svn_client_ctx_t *ctx,
                      apr_pool_t *scratch_pool)
 {
@@ -71,7 +73,25 @@ need_to_cache_iprops(svn_boolean_t *need
         }
     }
 
-  *needs_cache = (is_wc_root || is_switched);
+  /* Starting assumption. */
+  *needs_cache = FALSE;
+
+  if (is_wc_root || is_switched)
+    {
+      const char *session_url;
+      const char *session_root_url;
+
+      /* Looks likely that we need an inherited properties cache...Unless
+         LOCAL_ABSPATH is a WC root that points to the repos root.  Then it
+         doesn't need a cache because it has nowhere to inherit from.  Check
+         for that case. */
+      SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
+      SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_root_url,
+                                     scratch_pool));
+
+      if (strcmp(session_root_url, session_url) != 0)
+        *needs_cache = TRUE;
+    }
 
   return SVN_NO_ERROR;
 }
@@ -81,6 +101,7 @@ svn_client__get_inheritable_props(apr_ha
                                   const char *local_abspath,
                                   svn_revnum_t revision,
                                   svn_depth_t depth,
+                                  svn_boolean_t use_relpath_keys,
                                   svn_ra_session_t *ra_session,
                                   svn_client_ctx_t *ctx,
                                   apr_pool_t *result_pool,
@@ -111,7 +132,7 @@ svn_client__get_inheritable_props(apr_ha
           svn_boolean_t needs_cached_iprops;
 
           SVN_ERR(need_to_cache_iprops(&needs_cached_iprops, local_abspath,
-                                       ctx, iterpool));
+                                       ra_session, ctx, iterpool));
           if (needs_cached_iprops)
             {
               const char *target_abspath = apr_pstrdup(scratch_pool,
@@ -163,8 +184,8 @@ svn_client__get_inheritable_props(apr_ha
             }
 
           SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props,
-                                             "", revision, result_pool,
-                                             iterpool));
+                                             "", revision, use_relpath_keys,
+                                             result_pool, iterpool));
           apr_hash_set(*wcroot_iprops,
                        apr_pstrdup(result_pool, child_abspath),
                        APR_HASH_KEY_STRING,

Modified: subversion/branches/tree-read-api/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_client/merge.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_client/merge.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_client/merge.c Sun Jan  6 02:33:34 2013
@@ -313,6 +313,23 @@ typedef struct merge_cmd_baton_t {
      meet the criteria or DRY_RUN is true. */
   apr_hash_t *paths_with_deleted_mergeinfo;
 
+  /* The list of absolute skipped paths, which should be examined and
+     cleared after each invocation of the callback.  The paths
+     are absolute.  Is NULL if MERGE_B->SOURCES_ANCESTRAL and
+     MERGE_B->REINTEGRATE_MERGE are both false. */
+  apr_hash_t *skipped_abspaths;
+
+  /* The list of absolute merged paths.  Unused if MERGE_B->SOURCES_ANCESTRAL
+     and MERGE_B->REINTEGRATE_MERGE are both false. */
+  apr_hash_t *merged_abspaths;
+
+  /* A hash of (const char *) absolute WC paths mapped to the same which
+     represent the roots of subtrees added by the merge. */
+  apr_hash_t *added_abspaths;
+
+  /* A list of tree conflict victim absolute paths which may be NULL. */
+  apr_hash_t *tree_conflicted_abspaths;
+
   /* The diff3_cmd in ctx->config, if any, else null.  We could just
      extract this as needed, but since more than one caller uses it,
      we just set it up when this baton is created. */
@@ -504,7 +521,7 @@ dry_run_added_parent_p(const merge_cmd_b
                        apr_pool_t *scratch_pool)
 {
   const char *abspath = local_abspath;
-  int i;
+  apr_size_t i;
 
   if (!merge_b->dry_run)
     return FALSE;
@@ -1234,8 +1251,14 @@ filter_self_referential_mergeinfo(apr_ar
 }
 
 /* Prepare a set of property changes PROPCHANGES to be used for a merge
-   operation on LOCAL_ABSPATH. Store the result in *PROP_UPDATES.
+   operation on LOCAL_ABSPATH.
 
+   Remove all non-regular prop-changes (entry-props and WC-props).
+   Remove all non-mergeinfo prop-changes if it's a record-only merge.
+   Remove self-referential mergeinfo (### in some cases...)
+   Remove foreign-repository mergeinfo (### in some cases...)
+
+   Store the resulting property changes in *PROP_UPDATES.
    Store information on where mergeinfo is updated in MERGE_B.
 
    Used for both file and directory property merges. */
@@ -1251,6 +1274,8 @@ prepare_merge_props_changed(const apr_ar
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
+  /* We only want to merge "regular" version properties:  by
+     definition, 'svn merge' shouldn't touch any data within .svn/  */
   SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
                                result_pool));
 
@@ -1275,8 +1300,6 @@ prepare_merge_props_changed(const apr_ar
       props = mergeinfo_props;
     }
 
-  /* We only want to merge "regular" version properties:  by
-     definition, 'svn merge' shouldn't touch any data within .svn/  */
   if (props->nelts)
     {
       /* If this is a forward merge then don't add new mergeinfo to
@@ -1430,8 +1453,6 @@ merge_dir_props_changed(svn_wc_notify_st
   SVN_ERR(prepare_merge_props_changed(&props, local_abspath, propchanges,
                                       merge_b, scratch_pool, scratch_pool));
 
-  /* We only want to merge "regular" version properties:  by
-     definition, 'svn merge' shouldn't touch any pristine data  */
   if (props->nelts)
     {
       const svn_wc_conflict_version_t *left;
@@ -1720,14 +1741,9 @@ merge_file_changed(svn_wc_notify_state_t
   if (prop_state)
     *prop_state = svn_wc_notify_state_unchanged;
 
-  if (prop_changes->nelts > 0)
-    {
-      /* Filter entry-props and unneeded properties in case of a record only
-         merge */
-      SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
-                                          prop_changes, merge_b,
-                                          scratch_pool, scratch_pool));
-    }
+  SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
+                                      prop_changes, merge_b,
+                                      scratch_pool, scratch_pool));
 
   SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
                                  svn_node_file, &merge_b->merge_source, merge_b->target, merge_b->pool));
@@ -1920,6 +1936,21 @@ merge_file_added(svn_wc_notify_state_t *
     {
     case svn_node_none:
       {
+        svn_node_kind_t parent_kind;
+
+        /* Does the parent exist on disk (vs missing). If no we should
+           report an obstruction. Or svn_wc_add_repos_file4() will just
+           do its work and the workqueue will create the missing dirs */
+        SVN_ERR(svn_io_check_path(
+                        svn_dirent_dirname(mine_abspath, scratch_pool), 
+                        &parent_kind, scratch_pool));
+
+        if (parent_kind != svn_node_dir)
+          {
+            *content_state = svn_wc_notify_state_obstructed;
+            return SVN_NO_ERROR;
+          }
+
         if (! merge_b->dry_run)
           {
             const char *copyfrom_url;
@@ -2013,8 +2044,6 @@ merge_file_added(svn_wc_notify_state_t *
                                                merge_b->ctx->cancel_func,
                                                merge_b->ctx->cancel_baton,
                                                scratch_pool));
-
-                /* ### delete 'yours' ? */
               }
           }
         if (content_state)
@@ -2723,9 +2752,45 @@ merge_dir_opened(svn_boolean_t *tree_con
 
   if (obstr_state != svn_wc_notify_state_inapplicable)
     {
-      if (skip_children)
-        *skip_children = TRUE;
-      /* But don't skip THIS, to allow a skip notification */
+      /* In Subversion <= 1.7 we always skipped descendants here */
+      if (obstr_state == svn_wc_notify_state_obstructed)
+        {
+          svn_boolean_t is_wcroot;
+
+          SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
+                                  merge_b->ctx->wc_ctx,
+                                  local_abspath, scratch_pool));
+
+          if (is_wcroot)
+            {
+              const char *skipped_path;
+
+              skipped_path = apr_pstrdup(apr_hash_pool_get(
+                                                  merge_b->skipped_abspaths),
+                                         local_abspath);
+
+              apr_hash_set(merge_b->skipped_abspaths, skipped_path,
+                           APR_HASH_KEY_STRING, skipped_path);
+
+              *skip = TRUE;
+              *skip_children = TRUE;
+
+              if (merge_b->ctx->notify_func2)
+                {
+                  svn_wc_notify_t *notify;
+
+                  notify = svn_wc_create_notify(
+                                        skipped_path,
+                                        svn_wc_notify_update_skip_obstruction,
+                                        scratch_pool);
+                  notify->kind = svn_node_dir;
+                  notify->content_state = obstr_state;
+                  merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
+                                             notify, scratch_pool);
+                }
+            }
+        }
+
       return SVN_NO_ERROR;
     }
 
@@ -2747,8 +2812,7 @@ merge_dir_opened(svn_boolean_t *tree_con
           if (parent_depth != svn_depth_unknown &&
               parent_depth < svn_depth_immediates)
             {
-              if (skip_children)
-                *skip_children = TRUE;
+              /* In Subversion <= 1.7 we skipped descendants here */
               return SVN_NO_ERROR;
             }
         }
@@ -2845,25 +2909,6 @@ typedef struct notification_receiver_bat
   /* The number of operative notifications received. */
   apr_uint32_t nbr_operative_notifications;
 
-  /* The list of absolute merged paths.  Is NULL if MERGE_B->SOURCES_ANCESTRAL
-     and MERGE_B->REINTEGRATE_MERGE are both false. */
-  apr_hash_t *merged_abspaths;
-
-  /* The list of absolute skipped paths, which should be examined and
-     cleared after each invocation of the callback.  The paths
-     are absolute.  Is NULL if MERGE_B->SOURCES_ANCESTRAL and
-     MERGE_B->REINTEGRATE_MERGE are both false. */
-  apr_hash_t *skipped_abspaths;
-
-  /* A hash of (const char *) absolute WC paths mapped to the same which
-     represent the roots of subtrees added by the merge.  May be NULL. */
-  apr_hash_t *added_abspaths;
-
-  /* A list of tree conflict victim absolute paths which may be NULL.  Is NULL
-     if MERGE_B->SOURCES_ANCESTRAL and MERGE_B->REINTEGRATE_MERGE are both
-     false. */
-  apr_hash_t *tree_conflicted_abspaths;
-
   /* Flag indicating whether it is a single file merge or not. */
   svn_boolean_t is_single_file_merge;
 
@@ -3084,6 +3129,7 @@ notification_receiver(void *baton, const
                       apr_pool_t *pool)
 {
   notification_receiver_baton_t *notify_b = baton;
+  merge_cmd_baton_t *merge_b = notify_b->merge_b;
   svn_boolean_t is_operative_notification = IS_OPERATIVE_NOTIFICATION(notify);
   const char *notify_abspath;
 
@@ -3105,7 +3151,7 @@ notification_receiver(void *baton, const
    * not yet implemented.
    * ### We should stash the info about which moves have been followed and
    * retrieve that info here, instead of querying the WC again here. */
-  notify_abspath = svn_dirent_join(notify_b->merge_b->target->abspath,
+  notify_abspath = svn_dirent_join(merge_b->target->abspath,
                                    notify->path, pool);
   if (notify->action == svn_wc_notify_update_update
       && notify->kind == svn_node_file)
@@ -3114,7 +3160,7 @@ notification_receiver(void *baton, const
       const char *moved_to_abspath;
 
       err = svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
-                                        notify_b->merge_b->ctx->wc_ctx,
+                                        merge_b->ctx->wc_ctx,
                                         notify_abspath, pool, pool);
       if (err)
         {
@@ -3135,8 +3181,8 @@ notification_receiver(void *baton, const
     }
 
   /* Update the lists of merged, skipped, tree-conflicted and added paths. */
-  if (notify_b->merge_b->merge_source.ancestral
-      || notify_b->merge_b->reintegrate_merge)
+  if (merge_b->merge_source.ancestral
+      || merge_b->reintegrate_merge)
     {
       if (notify->content_state == svn_wc_notify_state_merged
           || notify->content_state == svn_wc_notify_state_changed
@@ -3147,10 +3193,7 @@ notification_receiver(void *baton, const
           const char *merged_path = apr_pstrdup(notify_b->pool,
                                                 notify_abspath);
 
-          if (notify_b->merged_abspaths == NULL)
-            notify_b->merged_abspaths = apr_hash_make(notify_b->pool);
-
-          apr_hash_set(notify_b->merged_abspaths, merged_path,
+          apr_hash_set(merge_b->merged_abspaths, merged_path,
                        APR_HASH_KEY_STRING, merged_path);
         }
 
@@ -3159,10 +3202,7 @@ notification_receiver(void *baton, const
           const char *skipped_path = apr_pstrdup(notify_b->pool,
                                                  notify_abspath);
 
-          if (notify_b->skipped_abspaths == NULL)
-            notify_b->skipped_abspaths = apr_hash_make(notify_b->pool);
-
-          apr_hash_set(notify_b->skipped_abspaths, skipped_path,
+          apr_hash_set(merge_b->skipped_abspaths, skipped_path,
                        APR_HASH_KEY_STRING, skipped_path);
         }
 
@@ -3171,30 +3211,26 @@ notification_receiver(void *baton, const
           const char *tree_conflicted_path = apr_pstrdup(notify_b->pool,
                                                          notify_abspath);
 
-          if (notify_b->tree_conflicted_abspaths == NULL)
-            notify_b->tree_conflicted_abspaths =
-              apr_hash_make(notify_b->pool);
-
-          apr_hash_set(notify_b->tree_conflicted_abspaths,
+          apr_hash_set(merge_b->tree_conflicted_abspaths,
                        tree_conflicted_path, APR_HASH_KEY_STRING,
                        tree_conflicted_path);
         }
 
       if (notify->action == svn_wc_notify_update_add)
         {
-          update_the_list_of_added_subtrees(notify_b->merge_b->target->abspath,
+          update_the_list_of_added_subtrees(merge_b->target->abspath,
                                             notify_abspath,
-                                            &(notify_b->added_abspaths),
+                                            &(merge_b->added_abspaths),
                                             notify_b->pool, pool);
         }
 
       if (notify->action == svn_wc_notify_update_delete
-          && notify_b->added_abspaths)
+          && merge_b->added_abspaths)
         {
           /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
              are now deleting it, then remove it from the list of added
              paths. */
-          apr_hash_set(notify_b->added_abspaths, notify_abspath,
+          apr_hash_set(merge_b->added_abspaths, notify_abspath,
                        APR_HASH_KEY_STRING, NULL);
         }
     }
@@ -3202,7 +3238,7 @@ notification_receiver(void *baton, const
   /* Notify that a merge is beginning, if we haven't already done so.
    * (A single-file merge is notified separately: see single_file_merge_notify().) */
   /* If our merge sources are ancestors of one another... */
-  if (notify_b->merge_b->merge_source.ancestral)
+  if (merge_b->merge_source.ancestral)
     {
       /* See if this is an operative directory merge. */
       if (!(notify_b->is_single_file_merge) && is_operative_notification)
@@ -3237,8 +3273,8 @@ notification_receiver(void *baton, const
                   notify_merge_begin(child->abspath,
                                      APR_ARRAY_IDX(child->remaining_ranges, 0,
                                                    svn_merge_range_t *),
-                                     notify_b->merge_b->same_repos,
-                                     notify_b->merge_b->ctx, pool);
+                                     merge_b->same_repos,
+                                     merge_b->ctx, pool);
                 }
             }
         }
@@ -3248,9 +3284,9 @@ notification_receiver(void *baton, const
            && notify_b->nbr_operative_notifications == 1
            && is_operative_notification)
     {
-      notify_merge_begin(notify_b->merge_b->target->abspath, NULL,
-                         notify_b->merge_b->same_repos,
-                         notify_b->merge_b->ctx, pool);
+      notify_merge_begin(merge_b->target->abspath, NULL,
+                         merge_b->same_repos,
+                         merge_b->ctx, pool);
     }
 
   if (notify_b->wrapped_func)
@@ -4429,13 +4465,24 @@ find_gaps_in_merge_source_history(svn_re
 
   if (rangelist->nelts > 1) /* Copy */
     {
+      const svn_merge_range_t *gap;
       /* As mentioned above, multiple gaps *shouldn't* be possible. */
       SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
 
+      gap = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
+                          const svn_merge_range_t *);
+
       *gap_start = MIN(source->loc1->rev, source->loc2->rev);
-      *gap_end = (APR_ARRAY_IDX(rangelist,
-                                rangelist->nelts - 1,
-                                svn_merge_range_t *))->start;
+      *gap_end = gap->start;
+
+      /* ### Issue #4132:
+         ### This assertion triggers in merge_tests.py svnmucc_abuse_1()
+         ### when a node is replaced by an older copy of itself.
+
+         BH: I think we should review this and the 'rename' case to find
+             out which behavior we really want, and if we can really
+             determine what happened this way. */
+      SVN_ERR_ASSERT(*gap_start < *gap_end);
     }
   else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
     {
@@ -4896,7 +4943,7 @@ update_wc_mergeinfo(svn_mergeinfo_catalo
 
    Record override mergeinfo on any paths skipped during a merge.
 
-   Set empty mergeinfo on each path in SKIPPED_ABSPATHS so the path
+   Set empty mergeinfo on each path in MERGE_B->SKIPPED_ABSPATHS so the path
    does not incorrectly inherit mergeinfo that will later be describing
    the merge.
 
@@ -4910,14 +4957,12 @@ static svn_error_t *
 record_skips(const char *mergeinfo_path,
              const svn_rangelist_t *rangelist,
              svn_boolean_t is_rollback,
-             apr_hash_t *skipped_abspaths,
              merge_cmd_baton_t *merge_b,
              apr_pool_t *scratch_pool)
 {
   apr_hash_index_t *hi;
   apr_hash_t *merges;
-  apr_size_t nbr_skips = (skipped_abspaths != NULL ?
-                          apr_hash_count(skipped_abspaths) : 0);
+  apr_size_t nbr_skips = apr_hash_count(merge_b->skipped_abspaths);
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
   if (nbr_skips == 0)
@@ -4926,7 +4971,7 @@ record_skips(const char *mergeinfo_path,
   merges = apr_hash_make(scratch_pool);
 
   /* Override the mergeinfo for child paths which weren't actually merged. */
-  for (hi = apr_hash_first(scratch_pool, skipped_abspaths); hi;
+  for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
        hi = apr_hash_next(hi))
     {
       const char *skipped_abspath = svn__apr_hash_index_key(hi);
@@ -7209,8 +7254,7 @@ do_file_merge(svn_mergeinfo_catalog_t re
          self-referential mergeinfo, but don't record mergeinfo if
          TARGET_WCPATH was skipped. */
       if (filtered_rangelist->nelts
-          && (!notify_b->skipped_abspaths
-              || (apr_hash_count(notify_b->skipped_abspaths) == 0)))
+          && (apr_hash_count(notify_b->merge_b->skipped_abspaths) == 0))
         {
           apr_hash_t *merges = apr_hash_make(iterpool);
 
@@ -7303,7 +7347,7 @@ process_children_with_new_mergeinfo(merg
       svn_mergeinfo_t path_explicit_mergeinfo;
       svn_client__merge_path_t *new_child;
 
-      apr_pool_clear(iterpool);
+      svn_pool_clear(iterpool);
 
       /* Get the path's new explicit mergeinfo... */
       SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL,
@@ -7410,11 +7454,11 @@ subtree_touched_by_merge(const char *loc
                          notification_receiver_baton_t *notify_b,
                          apr_pool_t *pool)
 {
-  return (path_is_subtree(local_abspath, notify_b->merged_abspaths, pool)
-          || path_is_subtree(local_abspath, notify_b->skipped_abspaths, pool)
-          || path_is_subtree(local_abspath, notify_b->added_abspaths, pool)
-          || path_is_subtree(local_abspath,
-                             notify_b->tree_conflicted_abspaths,
+  merge_cmd_baton_t *merge_b = notify_b->merge_b;
+  return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool)
+          || path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool)
+          || path_is_subtree(local_abspath, merge_b->added_abspaths, pool)
+          || path_is_subtree(local_abspath, merge_b->tree_conflicted_abspaths,
                              pool));
 }
 
@@ -7698,9 +7742,8 @@ flag_subtrees_needing_mergeinfo(svn_bool
         continue;
 
       /* Don't record mergeinfo on skipped paths. */
-      if (notify_b->skipped_abspaths
-          && apr_hash_get(notify_b->skipped_abspaths, child->abspath,
-                          APR_HASH_KEY_STRING))
+      if (apr_hash_get(notify_b->merge_b->skipped_abspaths, child->abspath,
+                       APR_HASH_KEY_STRING))
         continue;
 
       /* ### ptb: Yes, we could combine the following into a single
@@ -7751,7 +7794,7 @@ flag_subtrees_needing_mergeinfo(svn_bool
               if (!merge_b->reintegrate_merge
                   && child->missing_child
                   && !path_is_subtree(child->abspath,
-                                      notify_b->skipped_abspaths,
+                                      notify_b->merge_b->skipped_abspaths,
                                       iterpool))
                 {
                   child->missing_child = FALSE;
@@ -7980,8 +8023,7 @@ record_mergeinfo_for_dir_merge(svn_merge
              don't incorrectly inherit the mergeinfo we are about to set. */
           if (i == 0)
             SVN_ERR(record_skips(mergeinfo_fspath, child_merge_rangelist,
-                                 is_rollback, notify_b->skipped_abspaths,
-                                 merge_b, iterpool));
+                                 is_rollback, merge_b, iterpool));
 
           /* We may need to record non-inheritable mergeinfo that applies
              only to CHILD->ABSPATH. */
@@ -8188,7 +8230,7 @@ record_mergeinfo_for_added_subtrees(
       svn_mergeinfo_t parent_mergeinfo;
       svn_mergeinfo_t added_path_mergeinfo;
 
-      apr_pool_clear(iterpool);
+      svn_pool_clear(iterpool);
       dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
 
       /* Grab the added path's explicit mergeinfo. */
@@ -8511,6 +8553,8 @@ remove_noop_subtree_ranges(const merge_s
   svn_merge_range_t *youngest_gap_rev;
   svn_rangelist_t *inoperative_ranges;
   apr_pool_t *iterpool;
+  const char *longest_common_subtree_ancestor = NULL;
+  svn_error_t *err;
 
   assert(session_url_is(ra_session, source->loc2->url, scratch_pool));
 
@@ -8549,6 +8593,19 @@ remove_noop_subtree_ranges(const merge_s
 
       svn_pool_clear(iterpool);
 
+      /* Issue #4269: Keep track of the longest common ancestor of all the
+         subtrees which require merges.  This may be a child of
+         TARGET->ABSPATH, which will allow us to narrow the log request
+         below. */
+      if (child->remaining_ranges && child->remaining_ranges->nelts)
+        {
+          if (longest_common_subtree_ancestor)
+            longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
+              longest_common_subtree_ancestor, child->abspath, scratch_pool);
+          else
+            longest_common_subtree_ancestor = child->abspath;
+        }
+
       /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
       if (child->remaining_ranges && child->remaining_ranges->nelts)
         SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
@@ -8589,24 +8646,53 @@ remove_noop_subtree_ranges(const merge_s
                                                   sizeof(svn_revnum_t *));
   log_gap_baton.pool = svn_pool_create(scratch_pool);
 
+  /* Find the longest common ancestor of all subtrees relative to
+     RA_SESSION's URL. */
+  if (longest_common_subtree_ancestor)
+    longest_common_subtree_ancestor =
+      svn_dirent_skip_ancestor(target->abspath,
+                               longest_common_subtree_ancestor);
+  else
+    longest_common_subtree_ancestor = "";
+
   /* Invoke the svn_log_entry_receiver_t receiver log_noop_revs() from
      oldest to youngest.  The receiver is optimized to add ranges to
      log_gap_baton.merged_ranges and log_gap_baton.operative_ranges, but
      requires that the revs arrive oldest to youngest -- see log_noop_revs()
      and rangelist_merge_revision(). */
-  SVN_ERR(get_log(ra_session, "", oldest_gap_rev->start + 1,
-                  youngest_gap_rev->end, TRUE,
-                  log_noop_revs, &log_gap_baton, scratch_pool));
-
-  inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
-                                                 youngest_gap_rev->end,
-                                                 TRUE, scratch_pool);
-  SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
-                               log_gap_baton.operative_ranges,
-                               inoperative_ranges, FALSE, scratch_pool));
+  err = get_log(ra_session, longest_common_subtree_ancestor,
+                oldest_gap_rev->start + 1, youngest_gap_rev->end, TRUE,
+                log_noop_revs, &log_gap_baton, scratch_pool);
+
+  /* It's possible that the only subtrees with mergeinfo in TARGET don't have
+     any corresponding subtree in SOURCE between SOURCE->REV1 < SOURCE->REV2.
+     So it's also possible that we may ask for the logs of non-existent paths.
+     If we do, then assume that no subtree requires any ranges that are not
+     already required by the TARGET. */
+  if (err)
+    {
+      if (err->apr_err != SVN_ERR_FS_NOT_FOUND
+          && longest_common_subtree_ancestor[0] != '\0')
+        return svn_error_trace(err);
 
-  SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
-                               scratch_pool, scratch_pool));
+      /* Asked about a non-existent subtree in SOURCE. */
+      svn_error_clear(err);
+      log_gap_baton.merged_ranges =
+        svn_rangelist__initialize(oldest_gap_rev->start,
+                                  youngest_gap_rev->end,
+                                  TRUE, scratch_pool);
+    }
+  else
+    {
+      inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
+                                                     youngest_gap_rev->end,
+                                                     TRUE, scratch_pool);
+      SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
+                                   log_gap_baton.operative_ranges,
+                                   inoperative_ranges, FALSE, scratch_pool));
+      SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
+                                   scratch_pool, scratch_pool));
+    }
 
   for (i = 1; i < children_with_mergeinfo->nelts; i++)
     {
@@ -8962,7 +9048,7 @@ do_mergeinfo_aware_dir_merge(svn_mergein
           err = record_mergeinfo_for_added_subtrees(
                   &range, mergeinfo_path, depth,
                   squelch_mergeinfo_notifications,
-                  notify_b->added_abspaths, merge_b, scratch_pool);
+                  merge_b->added_abspaths, merge_b, scratch_pool);
         }
     }
 
@@ -9204,13 +9290,14 @@ do_merge(apr_hash_t **modified_subtrees,
   /* Do we already know the specific subtrees with mergeinfo we want
      to record-only mergeinfo on? */
   if (record_only && record_only_paths)
-    notify_baton.merged_abspaths = record_only_paths;
+    merge_cmd_baton.merged_abspaths = record_only_paths;
   else
-    notify_baton.merged_abspaths = NULL;
+    merge_cmd_baton.merged_abspaths = apr_hash_make(result_pool);
+
+  merge_cmd_baton.skipped_abspaths = apr_hash_make(result_pool);
+  merge_cmd_baton.added_abspaths = apr_hash_make(result_pool);
+  merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool);
 
-  notify_baton.skipped_abspaths = NULL;
-  notify_baton.added_abspaths = NULL;
-  notify_baton.tree_conflicted_abspaths = NULL;
   notify_baton.children_with_mergeinfo = NULL;
   notify_baton.cur_ancestor_abspath = NULL;
   notify_baton.merge_b = &merge_cmd_baton;
@@ -9298,22 +9385,18 @@ do_merge(apr_hash_t **modified_subtrees,
           /* ### Why only if the target is a dir and not a file? */
           if (modified_subtrees)
             {
-              if (notify_baton.merged_abspaths)
-                *modified_subtrees =
+              *modified_subtrees =
                   apr_hash_overlay(result_pool, *modified_subtrees,
-                                   notify_baton.merged_abspaths);
-              if (notify_baton.added_abspaths)
-                *modified_subtrees =
+                                   merge_cmd_baton.merged_abspaths);
+              *modified_subtrees =
                   apr_hash_overlay(result_pool, *modified_subtrees,
-                                   notify_baton.added_abspaths);
-              if (notify_baton.skipped_abspaths)
-                *modified_subtrees =
+                                   merge_cmd_baton.added_abspaths);
+              *modified_subtrees =
                   apr_hash_overlay(result_pool, *modified_subtrees,
-                                   notify_baton.skipped_abspaths);
-              if (notify_baton.tree_conflicted_abspaths)
-                *modified_subtrees =
+                                   merge_cmd_baton.skipped_abspaths);
+              *modified_subtrees =
                   apr_hash_overlay(result_pool, *modified_subtrees,
-                                   notify_baton.tree_conflicted_abspaths);
+                                   merge_cmd_baton.tree_conflicted_abspaths);
             }
         }
 

Modified: subversion/branches/tree-read-api/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_client/patch.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_client/patch.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_client/patch.c Sun Jan  6 02:33:34 2013
@@ -915,7 +915,7 @@ init_patch_target(patch_target_t **patch
       if (target->kind_on_disk == svn_node_file)
         {
           SVN_ERR(svn_io_file_open(&target->file, target->local_abspath,
-                                   APR_READ | APR_BINARY | APR_BUFFERED,
+                                   APR_READ | APR_BUFFERED,
                                    APR_OS_DEFAULT, result_pool));
           SVN_ERR(svn_wc_text_modified_p2(&target->local_mods, wc_ctx,
                                           target->local_abspath, FALSE,
@@ -2288,9 +2288,10 @@ create_missing_parents(patch_target_t *t
               if (ctx->cancel_func)
                 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
 
-              SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, local_abspath,
-                                           ctx->notify_func2, ctx->notify_baton2,
-                                           iterpool));
+              SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath,
+                                            NULL /*props*/,
+                                            ctx->notify_func2, ctx->notify_baton2,
+                                            iterpool));
             }
         }
     }
@@ -2402,8 +2403,9 @@ install_patched_target(patch_target_t *t
                * Suppress notification, we'll do that later (and also
                * during dry-run). Don't allow cancellation because
                * we'd rather notify about what we did before aborting. */
-              SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, target->local_abspath,
-                                           NULL, NULL, pool));
+              SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
+                                            NULL /*props*/,
+                                            NULL, NULL, pool));
             }
 
           /* Restore the target's executable bit if necessary. */
@@ -2494,10 +2496,11 @@ install_patched_prop_targets(patch_targe
             {
               SVN_ERR(svn_io_file_create(target->local_abspath, "",
                                          scratch_pool));
-              SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, target->local_abspath,
-                                           /* suppress notification */
-                                           NULL, NULL,
-                                           iterpool));
+              SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
+                                            NULL /*props*/,
+                                            /* suppress notification */
+                                            NULL, NULL,
+                                            iterpool));
             }
           target->added = TRUE;
         }
@@ -2617,8 +2620,8 @@ check_dir_empty(svn_boolean_t *empty, co
   int i;
 
   /* Working copy root cannot be deleted, so never consider it empty. */
-  SVN_ERR(svn_wc__strictly_is_wc_root(&is_wc_root, wc_ctx, local_abspath,
-                                      scratch_pool));
+  SVN_ERR(svn_wc__is_wcroot(&is_wc_root, wc_ctx, local_abspath,
+                            scratch_pool));
   if (is_wc_root)
     {
       *empty = FALSE;

Modified: subversion/branches/tree-read-api/subversion/libsvn_client/prop_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/libsvn_client/prop_commands.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/libsvn_client/prop_commands.c (original)
+++ subversion/branches/tree-read-api/subversion/libsvn_client/prop_commands.c Sun Jan  6 02:33:34 2013
@@ -639,7 +639,7 @@ remote_propget(apr_hash_t *props,
       /* We will filter out all but PROPNAME later, making a final copy
          in RESULT_POOL, so pass SCRATCH_POOL for both pools. */
       SVN_ERR(svn_ra_get_inherited_props(ra_session, inherited_props,
-                                         target_relative, revnum,
+                                         target_relative, revnum, FALSE,
                                          scratch_pool, scratch_pool));
     }
 
@@ -1125,7 +1125,7 @@ remote_proplist(const char *target_prefi
 
   if (get_target_inherited_props)
     SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props,
-                                       target_relative, revnum,
+                                       target_relative, revnum, FALSE,
                                        result_pool, scratch_pool));
   else
     inherited_props = NULL;