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 2018/11/15 16:12:55 UTC

svn commit: r1846666 - in /subversion/trunk/subversion: include/private/svn_client_private.h libsvn_client/copy_foreign.c

Author: julianfoad
Date: Thu Nov 15 16:12:54 2018
New Revision: 1846666

URL: http://svn.apache.org/viewvc?rev=1846666&view=rev
Log:
Start issue SVN-4786: Create a WC working-mods editor.

* subversion/include/private/svn_client_private.h
  (svn_client__wc_editor): New.

* subversion/libsvn_client/copy_foreign.c
  Extract existing editor and expand it to handle more cases.

Modified:
    subversion/trunk/subversion/include/private/svn_client_private.h
    subversion/trunk/subversion/libsvn_client/copy_foreign.c

Modified: subversion/trunk/subversion/include/private/svn_client_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_client_private.h?rev=1846666&r1=1846665&r2=1846666&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_client_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_client_private.h Thu Nov 15 16:12:54 2018
@@ -434,6 +434,24 @@ svn_client__repos_to_wc_copy_dir(svn_boo
                                  svn_client_ctx_t *ctx,
                                  apr_pool_t *pool);
 
+/** Return an editor for applying local modifications to a WC.
+ *
+ * Return an editor in @a *editor_p, @a *edit_baton_p that will apply
+ * local modifications to the WC subdirectory at @a dst_abspath.
+ *
+ * Send notifications via @a notify_func / @a notify_baton.
+ *
+ * Ignore changes to non-regular property (entry-props, DAV/WC-props).
+ */
+svn_error_t *
+svn_client__wc_editor(const svn_delta_editor_t **editor_p,
+                      void **edit_baton_p,
+                      const char *dst_abspath,
+                      svn_wc_notify_func2_t notify_func,
+                      void *notify_baton,
+                      svn_client_ctx_t *ctx,
+                      apr_pool_t *result_pool);
+
 /** Send committable changes found in the WC to a delta-editor.
  *
  * Committable changes are found in TARGETS:DEPTH:CHANGELISTS.

Modified: subversion/trunk/subversion/libsvn_client/copy_foreign.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/copy_foreign.c?rev=1846666&r1=1846665&r2=1846666&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/copy_foreign.c (original)
+++ subversion/trunk/subversion/libsvn_client/copy_foreign.c Thu Nov 15 16:12:54 2018
@@ -45,12 +45,33 @@
 #include "private/svn_wc_private.h"
 #include "svn_private_config.h"
 
+
+/* ------------------------------------------------------------------ */
+
+/* WC Modifications Editor.
+ *
+ * TODO:
+ *   - tests
+ *   - use for all existing scenarios ('svn add', 'svn propset', etc.)
+ *   - copy-from (half done: in dir_add only, untested)
+ *   - text-delta
+ *   - Instead of 'root_dir_add' option, probably the driver should anchor
+ *     at the parent dir.
+ *   - Instead of 'ignore_mergeinfo' option, implement that as a wrapper.
+ */
+
 struct edit_baton_t
 {
   apr_pool_t *pool;
   const char *anchor_abspath;
 
+  /* True => 'open_root' method will act as 'add_directory' */
+  svn_boolean_t root_dir_add;
+  /* True => filter out any incoming svn:mergeinfo property changes */
+  svn_boolean_t ignore_mergeinfo_changes;
+
   svn_wc_context_t *wc_ctx;
+  svn_client_ctx_t *ctx;
   svn_wc_notify_func2_t notify_func;
   void *notify_baton;
 };
@@ -64,12 +85,33 @@ struct dir_baton_t
 
   const char *local_abspath;
 
-  svn_boolean_t created;
+  svn_boolean_t created;  /* already under version control in the WC */
   apr_hash_t *properties;
 
   int users;
 };
 
+/*  */
+static svn_error_t *
+get_path(const char **local_abspath_p,
+         const char *anchor_abspath,
+         const char *path,
+         apr_pool_t *result_pool)
+{
+  svn_boolean_t under_root;
+
+  SVN_ERR(svn_dirent_is_under_root(&under_root, local_abspath_p,
+                                   anchor_abspath, path, result_pool));
+  if (! under_root)
+    {
+      return svn_error_createf(
+                    SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
+                    _("Path '%s' is not in the working copy"),
+                    svn_dirent_local_style(path, result_pool));
+    }
+  return SVN_NO_ERROR;
+}
+
 /* svn_delta_editor_t function */
 static svn_error_t *
 edit_open(void *edit_baton,
@@ -86,7 +128,9 @@ edit_open(void *edit_baton,
   db->users = 1;
   db->local_abspath = eb->anchor_abspath;
 
-  SVN_ERR(svn_io_make_dir_recursively(eb->anchor_abspath, dir_pool));
+  db->created = !(eb->root_dir_add);
+  if (eb->root_dir_add)
+    SVN_ERR(svn_io_make_dir_recursively(eb->anchor_abspath, dir_pool));
 
   *root_baton = db;
 
@@ -101,19 +145,38 @@ edit_close(void *edit_baton,
   return SVN_NO_ERROR;
 }
 
+/*  */
 static svn_error_t *
-dir_add(const char *path,
-        void *parent_baton,
-        const char *copyfrom_path,
-        svn_revnum_t copyfrom_revision,
-        apr_pool_t *result_pool,
-        void **child_baton)
+delete_entry(const char *path,
+             svn_revnum_t revision,
+             void *parent_baton,
+             apr_pool_t *scratch_pool)
+{
+  struct dir_baton_t *pb = parent_baton;
+  struct edit_baton_t *eb = pb->eb;
+  const char *local_abspath;
+
+  SVN_ERR(get_path(&local_abspath,
+                   eb->anchor_abspath, path, scratch_pool));
+  SVN_ERR(svn_wc_delete4(eb->wc_ctx, local_abspath,
+                         FALSE /*keep_local*/,
+                         TRUE /*delete_unversioned*/,
+                         NULL, NULL, /*cancellation*/
+                         eb->notify_func, eb->notify_baton, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+dir_open_or_add(const char *path,
+                void *parent_baton,
+                struct dir_baton_t **child_baton)
 {
   struct dir_baton_t *pb = parent_baton;
   struct edit_baton_t *eb = pb->eb;
   apr_pool_t *dir_pool = svn_pool_create(pb->pool);
   struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
-  svn_boolean_t under_root;
 
   pb->users++;
 
@@ -122,18 +185,60 @@ dir_add(const char *path,
   db->pool = dir_pool;
   db->users = 1;
 
-  SVN_ERR(svn_dirent_is_under_root(&under_root, &db->local_abspath,
-                                   eb->anchor_abspath, path, db->pool));
-  if (! under_root)
-    {
-      return svn_error_createf(
-                    SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
-                    _("Path '%s' is not in the working copy"),
-                    svn_dirent_local_style(path, db->pool));
-    }
+  SVN_ERR(get_path(&db->local_abspath,
+                   eb->anchor_abspath, path, db->pool));
+
+  *child_baton = db;
+  return SVN_NO_ERROR;
+}
+
+/* An svn_delta_editor_t function. */
+static svn_error_t *
+dir_open(const char *path,
+         void *parent_baton,
+         svn_revnum_t base_revision,
+         apr_pool_t *result_pool,
+         void **child_baton)
+{
+  struct dir_baton_t *db;
+
+  SVN_ERR(dir_open_or_add(path, parent_baton, &db));
+  db->created = TRUE;
+
+  *child_baton = db;
+  return SVN_NO_ERROR;
+}
 
+static svn_error_t *
+dir_add(const char *path,
+        void *parent_baton,
+        const char *copyfrom_path,
+        svn_revnum_t copyfrom_revision,
+        apr_pool_t *result_pool,
+        void **child_baton)
+{
+  struct dir_baton_t *db;
+
+  SVN_ERR(dir_open_or_add(path, parent_baton, &db));
   SVN_ERR(svn_io_make_dir_recursively(db->local_abspath, db->pool));
 
+  if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_revision))
+    {
+      svn_opt_revision_t copy_src_peg_revision;
+
+      copy_src_peg_revision.kind = svn_opt_revision_number;
+      copy_src_peg_revision.value.number = copyfrom_revision;
+
+      SVN_ERR(svn_client__repos_to_wc_copy_dir(NULL /*timestamp_sleep*/,
+                                               copyfrom_path,
+                                               &copy_src_peg_revision,
+                                               &copy_src_peg_revision,
+                                               db->local_abspath,
+                                               TRUE /*ignore_externals*/,
+                                               NULL /*ra_session*/,
+                                               db->eb->ctx, db->pool));
+    }
+
   *child_baton = db;
   return SVN_NO_ERROR;
 }
@@ -151,7 +256,7 @@ dir_change_prop(void *dir_baton,
   prop_kind = svn_property_kind2(name);
 
   if (prop_kind != svn_prop_regular_kind
-      || ! strcmp(name, SVN_PROP_MERGEINFO))
+      || (eb->ignore_mergeinfo_changes && ! strcmp(name, SVN_PROP_MERGEINFO)))
     {
       /* We can't handle DAV, ENTRY and merge specific props here */
       return SVN_NO_ERROR;
@@ -159,8 +264,7 @@ dir_change_prop(void *dir_baton,
 
   if (! db->created)
     {
-      /* We can still store them in the hash for immediate addition
-         with the svn_wc_add_from_disk3() call */
+      /* Store properties to be added later in svn_wc_add_from_disk3() */
       if (! db->properties)
         db->properties = apr_hash_make(db->pool);
 
@@ -170,7 +274,6 @@ dir_change_prop(void *dir_baton,
     }
   else
     {
-      /* We have already notified for this directory, so don't do that again */
       SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, db->local_abspath, name, value,
                                svn_depth_empty, FALSE, NULL,
                                NULL, NULL, /* Cancellation */
@@ -246,6 +349,7 @@ struct file_baton_t
   struct edit_baton_t *eb;
 
   const char *local_abspath;
+  svn_boolean_t created;  /* already under version control in the WC */
   apr_hash_t *properties;
 
   svn_boolean_t writing;
@@ -254,19 +358,16 @@ struct file_baton_t
   const char *tmp_path;
 };
 
+/*  */
 static svn_error_t *
-file_add(const char *path,
-         void *parent_baton,
-         const char *copyfrom_path,
-         svn_revnum_t copyfrom_revision,
-         apr_pool_t *result_pool,
-         void **file_baton)
+file_open_or_add(const char *path,
+                 void *parent_baton,
+                 struct file_baton_t **file_baton)
 {
   struct dir_baton_t *pb = parent_baton;
   struct edit_baton_t *eb = pb->eb;
   apr_pool_t *file_pool = svn_pool_create(pb->pool);
   struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
-  svn_boolean_t under_root;
 
   pb->users++;
 
@@ -274,15 +375,40 @@ file_add(const char *path,
   fb->eb = eb;
   fb->pb = pb;
 
-  SVN_ERR(svn_dirent_is_under_root(&under_root, &fb->local_abspath,
-                                   eb->anchor_abspath, path, fb->pool));
-  if (! under_root)
-    {
-      return svn_error_createf(
-                    SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
-                    _("Path '%s' is not in the working copy"),
-                    svn_dirent_local_style(path, fb->pool));
-    }
+  SVN_ERR(get_path(&fb->local_abspath,
+                   eb->anchor_abspath, path, fb->pool));
+
+  *file_baton = fb;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+file_open(const char *path,
+          void *parent_baton,
+          svn_revnum_t base_revision,
+          apr_pool_t *result_pool,
+          void **file_baton)
+{
+  struct file_baton_t *fb;
+
+  SVN_ERR(file_open_or_add(path, parent_baton, &fb));
+  fb->created = TRUE;
+
+  *file_baton = fb;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+file_add(const char *path,
+         void *parent_baton,
+         const char *copyfrom_path,
+         svn_revnum_t copyfrom_revision,
+         apr_pool_t *result_pool,
+         void **file_baton)
+{
+  struct file_baton_t *fb;
+
+  SVN_ERR(file_open_or_add(path, parent_baton, &fb));
 
   *file_baton = fb;
   return SVN_NO_ERROR;
@@ -295,25 +421,36 @@ file_change_prop(void *file_baton,
                  apr_pool_t *scratch_pool)
 {
   struct file_baton_t *fb = file_baton;
+  struct edit_baton_t *eb = fb->eb;
   svn_prop_kind_t prop_kind;
 
   prop_kind = svn_property_kind2(name);
 
   if (prop_kind != svn_prop_regular_kind
-      || ! strcmp(name, SVN_PROP_MERGEINFO))
+      || (eb->ignore_mergeinfo_changes && ! strcmp(name, SVN_PROP_MERGEINFO)))
     {
       /* We can't handle DAV, ENTRY and merge specific props here */
       return SVN_NO_ERROR;
     }
 
-  /* We store all properties in the hash for immediate addition
-      with the svn_wc_add_from_disk3() call */
-  if (! fb->properties)
-    fb->properties = apr_hash_make(fb->pool);
-
-  if (value != NULL)
-    svn_hash_sets(fb->properties, apr_pstrdup(fb->pool, name),
-                  svn_string_dup(value, fb->pool));
+  if (! fb->created)
+    {
+      /* Store properties to be added later in svn_wc_add_from_disk3() */
+      if (! fb->properties)
+        fb->properties = apr_hash_make(fb->pool);
+
+      if (value != NULL)
+        svn_hash_sets(fb->properties, apr_pstrdup(fb->pool, name),
+                      svn_string_dup(value, fb->pool));
+    }
+  else
+    {
+      SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, fb->local_abspath, name, value,
+                               svn_depth_empty, FALSE, NULL,
+                               NULL, NULL, /* Cancellation */
+                               NULL, NULL, /* Notification */
+                               scratch_pool));
+    }
 
   return SVN_NO_ERROR;
 }
@@ -346,16 +483,36 @@ file_textdelta(void *file_baton,
 }
 
 static svn_error_t *
+ensure_added_file(struct file_baton_t *fb,
+                  apr_pool_t *scratch_pool)
+{
+  struct edit_baton_t *eb = fb->eb;
+
+  if (fb->created)
+    return SVN_NO_ERROR;
+
+  if (fb->pb)
+    SVN_ERR(ensure_added(fb->pb, scratch_pool));
+
+  fb->created = TRUE;
+
+  /* Add the file with all the already collected properties */
+  SVN_ERR(svn_wc_add_from_disk3(eb->wc_ctx, fb->local_abspath, fb->properties,
+                                TRUE /* skip checks */,
+                                eb->notify_func, eb->notify_baton,
+                                fb->pool));
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 file_close(void *file_baton,
            const char *text_checksum,
            apr_pool_t *scratch_pool)
 {
   struct file_baton_t *fb = file_baton;
-  struct edit_baton_t *eb = fb->eb;
   struct dir_baton_t *pb = fb->pb;
 
-  SVN_ERR(ensure_added(pb, fb->pool));
-
   if (text_checksum)
     {
       svn_checksum_t *expected_checksum;
@@ -376,10 +533,7 @@ file_close(void *file_baton,
                                                     fb->pool)));
     }
 
-  SVN_ERR(svn_wc_add_from_disk3(eb->wc_ctx, fb->local_abspath, fb->properties,
-                                TRUE /* skip checks */,
-                                eb->notify_func, eb->notify_baton,
-                                fb->pool));
+  SVN_ERR(ensure_added_file(fb, fb->pool));
 
   svn_pool_destroy(fb->pool);
   SVN_ERR(maybe_done(pb));
@@ -387,46 +541,117 @@ file_close(void *file_baton,
   return SVN_NO_ERROR;
 }
 
+/* Return an editor for applying local modifications to a WC.
+ *
+ * If @a root_dir_add is true, then create and schedule for addition
+ * the root directory of this edit, else assume it is already a versioned,
+ * existing directory.
+ *
+ * If @a ignore_mergeinfo_changes is true, ignore any incoming changes
+ * to the 'svn:mergeinfo' property.
+ */
+static svn_error_t *
+svn_client__wc_editor_internal(const svn_delta_editor_t **editor_p,
+                               void **edit_baton_p,
+                               const char *dst_abspath,
+                               svn_boolean_t root_dir_add,
+                               svn_boolean_t ignore_mergeinfo_changes,
+                               svn_wc_notify_func2_t notify_func,
+                               void *notify_baton,
+                               svn_client_ctx_t *ctx,
+                               apr_pool_t *result_pool)
+{
+  svn_delta_editor_t *editor = svn_delta_default_editor(result_pool);
+  struct edit_baton_t *eb = apr_pcalloc(result_pool, sizeof(*eb));
+
+  eb->pool = result_pool;
+  eb->anchor_abspath = apr_pstrdup(result_pool, dst_abspath);
+  eb->root_dir_add = root_dir_add;
+  eb->ignore_mergeinfo_changes = ignore_mergeinfo_changes;
+
+  eb->wc_ctx = ctx->wc_ctx;
+  eb->ctx = ctx;
+  eb->notify_func = notify_func;
+  eb->notify_baton  = notify_baton;
+
+  editor->open_root = edit_open;
+  editor->close_edit = edit_close;
+
+  editor->delete_entry = delete_entry;
+
+  editor->open_directory = dir_open;
+  editor->add_directory = dir_add;
+  editor->change_dir_prop = dir_change_prop;
+  editor->close_directory = dir_close;
+
+  editor->open_file = file_open;
+  editor->add_file = file_add;
+  editor->change_file_prop = file_change_prop;
+  editor->apply_textdelta = file_textdelta;
+  editor->close_file = file_close;
+
+  *editor_p = editor;
+  *edit_baton_p = eb;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__wc_editor(const svn_delta_editor_t **editor_p,
+                      void **edit_baton_p,
+                      const char *dst_abspath,
+                      svn_wc_notify_func2_t notify_func,
+                      void *notify_baton,
+                      svn_client_ctx_t *ctx,
+                      apr_pool_t *result_pool)
+{
+  SVN_ERR(svn_client__wc_editor_internal(editor_p, edit_baton_p,
+                                         dst_abspath,
+                                         FALSE /*root_dir_add*/,
+                                         FALSE /*ignore_mergeinfo_changes*/,
+                                         notify_func, notify_baton,
+                                         ctx, result_pool));
+  return SVN_NO_ERROR;
+}
+
+/** Copy a directory tree from a remote repository.
+ *
+ * Copy from RA_SESSION:LOCATION, depth DEPTH, to WC_CTX:DST_ABSPATH.
+ *
+ * Create the directory DST_ABSPATH, if not present. Its parent should be
+ * already under version control in the WC and in a suitable state for
+ * scheduling the addition of a child.
+ *
+ * Ignore any incoming non-regular properties (entry-props, DAV/WC-props).
+ * Remove any incoming 'svn:mergeinfo' properties.
+ */
 static svn_error_t *
 copy_foreign_dir(svn_ra_session_t *ra_session,
                  svn_client__pathrev_t *location,
-                 svn_wc_context_t *wc_ctx,
                  const char *dst_abspath,
                  svn_depth_t depth,
                  svn_wc_notify_func2_t notify_func,
                  void *notify_baton,
                  svn_cancel_func_t cancel_func,
                  void *cancel_baton,
+                 svn_client_ctx_t *ctx,
                  apr_pool_t *scratch_pool)
 {
-  struct edit_baton_t eb;
-  svn_delta_editor_t *editor = svn_delta_default_editor(scratch_pool);
+  const svn_delta_editor_t *editor;
+  void *eb;
   const svn_delta_editor_t *wrapped_editor;
   void *wrapped_baton;
   const svn_ra_reporter3_t *reporter;
   void *reporter_baton;
 
-  eb.pool = scratch_pool;
-  eb.anchor_abspath = dst_abspath;
-
-  eb.wc_ctx = wc_ctx;
-  eb.notify_func = notify_func;
-  eb.notify_baton  = notify_baton;
-
-  editor->open_root = edit_open;
-  editor->close_edit = edit_close;
-
-  editor->add_directory = dir_add;
-  editor->change_dir_prop = dir_change_prop;
-  editor->close_directory = dir_close;
-
-  editor->add_file = file_add;
-  editor->change_file_prop = file_change_prop;
-  editor->apply_textdelta = file_textdelta;
-  editor->close_file = file_close;
+  SVN_ERR(svn_client__wc_editor_internal(&editor, &eb,
+                                         dst_abspath,
+                                         TRUE /*root_dir_add*/,
+                                         TRUE /*ignore_mergeinfo_changes*/,
+                                         notify_func, notify_baton,
+                                         ctx, scratch_pool));
 
   SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
-                                            editor, &eb,
+                                            editor, eb,
                                             &wrapped_editor, &wrapped_baton,
                                             scratch_pool));
 
@@ -544,11 +769,11 @@ svn_client__copy_foreign(const char *url
   else
     {
       SVN_ERR(copy_foreign_dir(ra_session, loc,
-                               ctx->wc_ctx, dst_abspath,
+                               dst_abspath,
                                depth,
                                ctx->notify_func2, ctx->notify_baton2,
                                ctx->cancel_func, ctx->cancel_baton,
-                               scratch_pool));
+                               ctx, scratch_pool));
     }
 
   return SVN_NO_ERROR;