Author: rhuijben
Date: Tue Apr 19 15:49:37 2011
New Revision: 1095122

URL: http://svn.apache.org/viewvc?rev=1095122&view=rev
Log:
Resolve issue #3351, by automatically removing file externals when the removal
from the svn:externals property has been committed.

This 'small change' required quite a lot of cleanup of edge cases in the
externals code.

The only thing about issue #3351, that is not fixed by this commit is the
test for issue #3351. This requires some changes to the commit output
processing.

* subversion/libsvn_client/client.h
  (svn_client__handle_externals): Remove a few arguments that are not required.
    Retrieving them directly makes us handle switches properly.

* subversion/libsvn_client/commit.c
  (svn_client_commit5): Store externals information and call
    svn_client__handle_externals() on the collected information.

* subversion/libsvn_client/export.c
  (add_externals): Store absolute paths, like the rest of our API.
    (Removes edge cases in externals processing)
  (change_dir_prop): Update caller for return type change.

* subversion/libsvn_client/externals.c
  (handle_external_item_change_baton): Add delete_only flag.
  (handle_external_item_change): Add assertion and apply delete_only.
    Update comments and an ugly call to the notify handler.

  (handle_externals_desc_change_baton): Add flag to baton.
  (handle_externals_desc_change): Move code a tiny bit towards wc-ng standards.

* subversion/libsvn_client/switch.c
  (switch_internal): Don't pass unneeded values and ask for normal updates.

* subversion/libsvn_client/update.c
  (update_internal): Don't pass unneeded values and ask for normal updates.

* subversion/svn/notify.c
  (notify): Change notification for removing externals to show what is really
    happening.

Modified:
    subversion/trunk/subversion/libsvn_client/client.h
    subversion/trunk/subversion/libsvn_client/commit.c
    subversion/trunk/subversion/libsvn_client/export.c
    subversion/trunk/subversion/libsvn_client/externals.c
    subversion/trunk/subversion/libsvn_client/switch.c
    subversion/trunk/subversion/libsvn_client/update.c
    subversion/trunk/subversion/svn/notify.c

Modified: subversion/trunk/subversion/libsvn_client/client.h
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/client.h?rev=1095122&r1=1095121&r2=1095122&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/client.h (original)
+++ subversion/trunk/subversion/libsvn_client/client.h Tue Apr 19 15:49:37 2011
@@ -939,16 +939,17 @@ svn_client__do_commit(const char *base_u
    timestamp integrity, *TIMESTAMP_SLEEP will be unchanged if no sleep
    is required.
 
+   If DELETE_ONLY is TRUE, only process removals of externals.
+
    Use POOL for temporary allocation. */
 svn_error_t *
 svn_client__handle_externals(apr_hash_t *externals_old,
                              apr_hash_t *externals_new,
                              apr_hash_t *ambient_depths,
-                             const char *from_url,
-                             const char *to_abspath,
-                             const char *target,
+                             const char *anchor_abspath,
                              const char *repos_root_url,
                              svn_depth_t requested_depth,
+                             svn_boolean_t delete_only,
                              svn_boolean_t *timestamp_sleep,
                              svn_client_ctx_t *ctx,
                              apr_pool_t *pool);

Modified: subversion/trunk/subversion/libsvn_client/commit.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/commit.c?rev=1095122&r1=1095121&r2=1095122&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/commit.c (original)
+++ subversion/trunk/subversion/libsvn_client/commit.c Tue Apr 19 15:49:37 2011
@@ -1347,6 +1347,11 @@ svn_client_commit5(const apr_array_heade
       || (cmt_err->apr_err == SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED))
     {
       svn_wc_committed_queue_t *queue = svn_wc_committed_queue_create(pool);
+      svn_client__external_func_baton_t efb = {0};
+
+      efb.externals_old = apr_hash_make(pool);
+      efb.externals_new = apr_hash_make(pool);
+      efb.result_pool = pool;
 
       /* Make a note that our commit is finished. */
       commit_in_progress = FALSE;
@@ -1373,11 +1378,22 @@ svn_client_commit5(const apr_array_heade
                                          commit_info->revision,
                                          commit_info->date,
                                          commit_info->author,
-                                         NULL /* external_func */,
-                                         NULL /* external_baton */,
+                                         svn_client__external_info_gatherer,
+                                         &efb,
                                          ctx->cancel_func,
                                          ctx->cancel_baton,
                                          iterpool);
+
+      if (apr_hash_count(efb.externals_old))
+        {
+          /* Ok, we should process externals changes; but now we have */
+
+          SVN_ERR(svn_client__handle_externals(efb.externals_old,
+                                               efb.externals_new,
+                                               NULL, NULL, NULL,
+                                               svn_depth_unknown, TRUE,
+                                               NULL, ctx, iterpool));
+        }
     }
 
   /* Sleep to ensure timestamp integrity. */

Modified: subversion/trunk/subversion/libsvn_client/export.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/export.c?rev=1095122&r1=1095121&r2=1095122&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/export.c (original)
+++ subversion/trunk/subversion/libsvn_client/export.c Tue Apr 19 15:49:37 2011
@@ -49,21 +49,24 @@
 
 /* Add EXTERNALS_PROP_VAL for the export destination path PATH to
    TRAVERSAL_INFO.  */
-static void
+static svn_error_t *
 add_externals(apr_hash_t *externals,
               const char *path,
               const svn_string_t *externals_prop_val)
 {
   apr_pool_t *pool = apr_hash_pool_get(externals);
+  const char *local_abspath;
 
   if (! externals_prop_val)
-    return;
+    return SVN_NO_ERROR;
+
+  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
 
-  apr_hash_set(externals,
-               apr_pstrdup(pool, path),
-               APR_HASH_KEY_STRING,
+  apr_hash_set(externals, local_abspath, APR_HASH_KEY_STRING,
                apr_pstrmemdup(pool, externals_prop_val->data,
                               externals_prop_val->len));
+
+  return SVN_NO_ERROR;
 }
 
 /* Helper function that gets the eol style and optionally overrides the
@@ -951,7 +954,7 @@ change_dir_prop(void *dir_baton,
   struct edit_baton *eb = db->edit_baton;
 
   if (value && (strcmp(name, SVN_PROP_EXTERNALS) == 0))
-    add_externals(eb->externals, db->path, value);
+    SVN_ERR(add_externals(eb->externals, db->path, value));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/trunk/subversion/libsvn_client/externals.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/externals.c?rev=1095122&r1=1095121&r2=1095122&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/externals.c (original)
+++ subversion/trunk/subversion/libsvn_client/externals.c Tue Apr 19 15:49:37 
2011
@@ -68,6 +68,7 @@ struct handle_external_item_change_baton
 
   svn_boolean_t *timestamp_sleep;
   svn_boolean_t is_export;
+  svn_boolean_t delete_only;
 
   /* A long lived pool.  Put anything in here that needs to outlive
      the hash diffing callback, such as updates to the hash
@@ -687,10 +688,13 @@ handle_external_item_change(const void *
   const char *local_abspath = svn_dirent_join(ib->parent_dir_abspath,
                                               (const char *) key,
                                               ib->iter_pool);
+
   svn_ra_session_t *ra_session;
   svn_node_kind_t kind;
   svn_client__ra_session_from_path_results ra_cache = { 0 };
 
+  SVN_ERR_ASSERT(ib->repos_root_url && ib->parent_dir_url);
+
   /* Don't bother to check status, since we'll get that for free by
      attempting to retrieve the hash values anyway.  */
 
@@ -742,7 +746,7 @@ handle_external_item_change(const void *
 
   /* If the external is being checked out, exported or updated,
      determine if the external is a file or directory. */
-  if (new_item)
+  if (new_item && !ib->delete_only)
     {
       /* Get the RA connection. */
       SVN_ERR(svn_client__ra_session_from_path(&ra_session,
@@ -780,7 +784,7 @@ handle_external_item_change(const void *
      the global case is hard, and it should be pretty obvious to a
      user when it happens.  Worst case: your disk fills up :-). */
 
-  if (! old_item)
+  if (! old_item && !ib->delete_only)
     {
       /* This branch is only used during a checkout or an export. */
       const char *parent_abspath;
@@ -859,11 +863,7 @@ handle_external_item_change(const void *
   else if (! new_item)
     {
       /* This branch is only used when an external is deleted from the
-         repository and the working copy is updated. */
-
-      /* See comment in above case about fancy rename handling.  Here,
-         before removing an old subdir, we would see if it wants to
-         just be renamed to a new one. */
+         repository and the working copy is updated or committed. */
 
       svn_error_t *err;
       svn_boolean_t lock_existed;
@@ -902,39 +902,46 @@ handle_external_item_change(const void *
                                   notify, ib->iter_pool);
         }
 
-      /* ### Ugly. Unlock only if not going to return an error. Revisit */
-      if (! lock_existed
-          && (! err || err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
+      if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
+        {
+          svn_error_clear(err);
+          err = NULL;
+        }
+
+
+      /* Unlock if we acquired the lock */
+      if (! lock_existed)
         {
           svn_error_t *err2 = svn_wc__release_write_lock(ib->ctx->wc_ctx,
                                                          local_abspath,
                                                          ib->iter_pool);
-          if (err2)
+
+          if (err2 && err2->apr_err == SVN_ERR_WC_NOT_LOCKED)
             {
-              if (! err)
-                err = err2;
-              else
-                svn_error_clear(err2);
+              /* We removed the lock by removing the node, how nice! */
+              svn_error_clear(err2); 
             }
+          else
+            err = svn_error_compose_create(err, err2);
         }
 
-      if (err && (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD))
-        return svn_error_return(err);
-      svn_error_clear(err);
-
-      /* ### If there were multiple path components leading down to
-         that wc, we could try to remove them too. */
+      SVN_ERR(err);
     }
-  else
+  else if (!ib->delete_only)
     {
       /* This branch handles all other changes. */
 
       /* First notify that we're about to handle an external. */
       if (ib->ctx->notify_func2)
-        (*ib->ctx->notify_func2)
-          (ib->ctx->notify_baton2,
-           svn_wc_create_notify(local_abspath, svn_wc_notify_update_external,
-                                ib->iter_pool), ib->iter_pool);
+        {
+          svn_wc_notify_t *nt;
+
+          nt = svn_wc_create_notify(local_abspath,
+                                    svn_wc_notify_update_external,
+                                    ib->iter_pool);
+
+          ib->ctx->notify_func2(ib->ctx->notify_baton2, nt,ib->iter_pool);
+        }
 
       /* Either the URL changed, or the exact same item is present in
          both hashes, and caller wants to update such unchanged items.
@@ -1036,6 +1043,9 @@ struct handle_externals_desc_change_bato
   /* Passed to svn_client_exportX() */
   const char *native_eol;
 
+  /* Handling a delete only update (from commit) */
+  svn_boolean_t delete_only;
+
   apr_pool_t *pool;
 };
 
@@ -1059,18 +1069,20 @@ handle_externals_desc_change(const void 
   svn_wc_external_item2_t *item;
   const char *ambient_depth_w;
   svn_depth_t ambient_depth;
-  const char *url;
-  svn_error_t *err;
+  const char *local_abspath = key;
+  apr_pool_t *scratch_pool = cb->pool;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
   if (cb->ambient_depths)
     {
-      ambient_depth_w = apr_hash_get(cb->ambient_depths, key, klen);
+      ambient_depth_w = apr_hash_get(cb->ambient_depths, local_abspath, klen);
       if (ambient_depth_w == NULL)
         {
           return svn_error_createf
             (SVN_ERR_WC_CORRUPT, NULL,
              _("Traversal of '%s' found no ambient depth"),
-             (const char *) key);
+             svn_dirent_local_style(local_abspath, scratch_pool));
         }
       else
         {
@@ -1090,20 +1102,22 @@ handle_externals_desc_change(const void 
     return SVN_NO_ERROR;
 
   /* Only handle externals under TARGET. */
-  if (cb->target_abspath)
+  if (cb->target_abspath
+      && ! svn_dirent_is_ancestor(cb->target_abspath, local_abspath))
     {
-      if (! svn_dirent_is_ancestor(cb->target_abspath, (const char *) key))
         return SVN_NO_ERROR;
     }
 
-  if ((old_desc_text = apr_hash_get(cb->externals_old, key, klen)))
-    SVN_ERR(svn_wc_parse_externals_description3(&old_desc, key, old_desc_text,
+  if ((old_desc_text = apr_hash_get(cb->externals_old, local_abspath, klen)))
+    SVN_ERR(svn_wc_parse_externals_description3(&old_desc, local_abspath,
+                                                old_desc_text,
                                                 FALSE, cb->pool));
   else
     old_desc = NULL;
 
-  if ((new_desc_text = apr_hash_get(cb->externals_new, key, klen)))
-    SVN_ERR(svn_wc_parse_externals_description3(&new_desc, key, new_desc_text,
+  if ((new_desc_text = apr_hash_get(cb->externals_new, local_abspath, klen)))
+    SVN_ERR(svn_wc_parse_externals_description3(&new_desc, local_abspath,
+                                                new_desc_text,
                                                 FALSE, cb->pool));
   else
     new_desc = NULL;
@@ -1131,45 +1145,45 @@ handle_externals_desc_change(const void 
 
   ib.old_desc          = old_desc_hash;
   ib.new_desc          = new_desc_hash;
-  ib.repos_root_url    = cb->repos_root_url;
+  if (cb->repos_root_url)
+    ib.repos_root_url    = cb->repos_root_url;
+  else
+    SVN_ERR(svn_wc__node_get_repos_info(&ib.repos_root_url, NULL,
+                                        cb->ctx->wc_ctx, local_abspath,
+                                        TRUE, FALSE, cb->pool, scratch_pool));
   ib.ctx               = cb->ctx;
   ib.is_export         = cb->is_export;
   ib.native_eol        = cb->native_eol;
+  ib.delete_only       = cb->delete_only;
   ib.timestamp_sleep   = cb->timestamp_sleep;
   ib.pool              = cb->pool;
   ib.iter_pool         = svn_pool_create(cb->pool);
-  SVN_ERR(svn_dirent_get_absolute(&ib.parent_dir_abspath, (const char *) key,
-                                  cb->pool));
-
-  err = svn_wc__node_get_url(&url, cb->ctx->wc_ctx, ib.parent_dir_abspath,
-                             cb->pool, cb->pool);
+  ib.parent_dir_abspath = local_abspath;
 
-  /* If we're doing an 'svn export' the current dir will not be a
-     working copy. We can't get the parent_dir. */
-  if (err)
+  if (!cb->from_url)
+    SVN_ERR(svn_wc__node_get_url(&ib.parent_dir_url, cb->ctx->wc_ctx,
+                                 ib.parent_dir_abspath,
+                                 cb->pool, cb->pool));
+  else
     {
-      if (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY ||
-          err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-        {
-          /* Get the URL of the parent directory by appending a portion of
-             parent_dir to from_url.  from_url is the URL for to_path and
-             to_path is a substring of parent_dir, so append any characters in
-             parent_dir past strlen(to_path) to from_url (making sure to move
-             past a '/' in parent_dir, otherwise svn_path_url_add_component()
-             will error. */
-          len = strlen(cb->to_abspath);
-          if (ib.parent_dir_abspath[len] == '/')
-            ++len;
-          ib.parent_dir_url = svn_path_url_add_component2(cb->from_url,
-                                           ib.parent_dir_abspath + len,
-                                           cb->pool);
-          svn_error_clear(err);
-        }
-      else
-        return svn_error_return(err);
+      /* If we're doing an 'svn export' the current dir will not be a
+         working copy. We can't get the parent_dir.
+
+          Get the URL of the parent directory by appending a portion of
+          parent_dir to from_url.  from_url is the URL for to_path and
+          to_path is a substring of parent_dir, so append any characters in
+          parent_dir past strlen(to_path) to from_url (making sure to move
+          past a '/' in parent_dir, otherwise svn_path_url_add_component()
+          will error. */
+      len = strlen(cb->to_abspath);
+      if (ib.parent_dir_abspath[len] == '/')
+        ++len;
+      ib.parent_dir_url = svn_path_url_add_component2(cb->from_url,
+                                       ib.parent_dir_abspath + len,
+                                       cb->pool);
     }
-  else
-      ib.parent_dir_url = url;
+
+  SVN_ERR_ASSERT(ib.parent_dir_url && ib.repos_root_url);
 
   /* We must use a custom version of svn_hash_diff so that the diff
      entries are processed in the order they were originally specified
@@ -1212,36 +1226,29 @@ svn_error_t *
 svn_client__handle_externals(apr_hash_t *externals_old,
                              apr_hash_t *externals_new,
                              apr_hash_t *ambient_depths,
-                             const char *from_url,
-                             const char *to_abspath,
-                             const char *target,
+                             const char *anchor_abspath,
                              const char *repos_root_url,
                              svn_depth_t requested_depth,
+                             svn_boolean_t delete_only,
                              svn_boolean_t *timestamp_sleep,
                              svn_client_ctx_t *ctx,
                              apr_pool_t *pool)
 {
   struct handle_externals_desc_change_baton cb = { 0 };
 
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath));
-
-  /* Sanity check; see r870198. */
-  if (! svn_path_is_url(from_url))
-    return svn_error_createf
-      (SVN_ERR_BAD_URL, NULL, _("'%s' is not a URL"), from_url);
-
   cb.externals_new     = externals_new;
   cb.externals_old     = externals_old;
   cb.requested_depth   = requested_depth;
   cb.ambient_depths    = ambient_depths;
-  cb.from_url          = from_url;
-  cb.to_abspath        = to_abspath;
-  cb.target_abspath    = svn_dirent_join(to_abspath, target, pool);
+  cb.from_url          = NULL;
+  cb.to_abspath        = NULL;
+  cb.target_abspath    = anchor_abspath;
   cb.repos_root_url    = repos_root_url;
   cb.ctx               = ctx;
   cb.timestamp_sleep   = timestamp_sleep;
   cb.is_export         = FALSE;
   cb.native_eol        = NULL;
+  cb.delete_only       = delete_only;
   cb.pool              = pool;
 
   return svn_hash_diff(cb.externals_old, cb.externals_new,

Modified: subversion/trunk/subversion/libsvn_client/switch.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/switch.c?rev=1095122&r1=1095121&r2=1095122&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/switch.c (original)
+++ subversion/trunk/subversion/libsvn_client/switch.c Tue Apr 19 15:49:37 2011
@@ -269,9 +269,10 @@ switch_internal(svn_revnum_t *result_rev
                 depth, ctx, pool));
       err = svn_client__handle_externals(efb.externals_old,
                                          efb.externals_new, efb.ambient_depths,
-                                         switch_url, anchor_abspath, target,
+                                         svn_dirent_join(anchor_abspath, 
+                                                         target, pool),
                                          source_root,
-                                         depth, use_sleep, ctx, pool);
+                                         depth, FALSE, use_sleep, ctx, pool);
     }
 
   /* Sleep to ensure timestamp integrity (we do this regardless of

Modified: subversion/trunk/subversion/libsvn_client/update.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/update.c?rev=1095122&r1=1095121&r2=1095122&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/update.c (original)
+++ subversion/trunk/subversion/libsvn_client/update.c Tue Apr 19 15:49:37 2011
@@ -287,9 +287,11 @@ update_internal(svn_revnum_t *result_rev
       SVN_ERR(svn_client__handle_externals(efb.externals_old,
                                            efb.externals_new,
                                            efb.ambient_depths,
-                                           anchor_url, anchor_abspath,
-                                           target, repos_root,
-                                           depth, use_sleep, ctx, pool));
+                                           svn_dirent_join(anchor_abspath,
+                                                           target, pool),
+                                           repos_root,
+                                           depth, FALSE, use_sleep,
+                                           ctx, pool));
     }
 
   if (sleep_here)

Modified: subversion/trunk/subversion/svn/notify.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/notify.c?rev=1095122&r1=1095121&r2=1095122&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/notify.c (original)
+++ subversion/trunk/subversion/svn/notify.c Tue Apr 19 15:49:37 2011
@@ -164,12 +164,27 @@ notify(void *baton, const svn_wc_notify_
         goto print_error;
       break;
     case svn_wc_notify_update_delete:
-    case svn_wc_notify_update_external_removed:
       nb->received_some_change = TRUE;
       if ((err = svn_cmdline_printf(pool, "D    %s\n", path_local)))
         goto print_error;
       break;
 
+    case svn_wc_notify_update_external_removed:
+      nb->received_some_change = TRUE;
+      if (n->err && n->err->message)
+        {
+          if ((err = svn_cmdline_printf(pool, "Removing external '%s' -- %s\n",
+              path_local, n->err->message)))
+            goto print_error;
+        }
+      else
+        {
+          if ((err = svn_cmdline_printf(pool, "Removing external '%s'\n",
+                                        path_local)))
+            goto print_error;
+        }
+      break;
+
     case svn_wc_notify_update_replace:
       nb->received_some_change = TRUE;
       if ((err = svn_cmdline_printf(pool, "R    %s\n", path_local)))


Reply via email to