Author: stsp
Date: Wed Feb  4 14:58:59 2015
New Revision: 1657267

URL: http://svn.apache.org/r1657267
Log:
On the pin-externals branch, allow fine-grained API-level control over which
externals get pinned during a copy. This feature is intended for clients which
allow users to select specific externals for pinning, like TortoiseSVN does.

Suggested by: steveking

* subversion/include/svn_client.h
  (svn_client_copy7): Add externals_to_pin parameter. Extend docstring.

* subversion/libsvn_client/copy.c
  (make_external_description): New. Factored out from pin_externals_prop().
  (pin_externals_prop): Add externals_to_pin parameter. If set, only pin
   externals matching those in externals_to_pin.
  (resolve_pinned_externals, do_wc_to_wc_copies_with_write_lock,
   do_wc_to_wc_copies, repos_to_repos_copy, wc_to_repos_copy,
   repos_to_wc_copy_single, repos_to_wc_copy_locked, repos_to_wc_copy,
   try_copy, svn_client_copy7): Add new externals_to_pin parameter
    and pass it all the way down to pin_externals_prop().
  (svn_client_move7): Pass NULL for externals_to_pin.

* subversion/libsvn_client/deprecated.c
  (svn_client_copy6): Pass NULL for externals_to_pin.

* subversion/svn/copy-cmd.c
  (svn_cl__copy): Pass NULL for externals_to_pin.

* subversion/tests/libsvn_client/client-test.c
  (test_copy_pin_externals, test_funcs): New test.

Modified:
    subversion/branches/pin-externals/subversion/include/svn_client.h
    subversion/branches/pin-externals/subversion/libsvn_client/copy.c
    subversion/branches/pin-externals/subversion/libsvn_client/deprecated.c
    subversion/branches/pin-externals/subversion/svn/copy-cmd.c
    
subversion/branches/pin-externals/subversion/tests/libsvn_client/client-test.c

Modified: subversion/branches/pin-externals/subversion/include/svn_client.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/include/svn_client.h?rev=1657267&r1=1657266&r2=1657267&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/include/svn_client.h (original)
+++ subversion/branches/pin-externals/subversion/include/svn_client.h Wed Feb  
4 14:58:59 2015
@@ -4493,8 +4493,16 @@ typedef struct svn_client_copy_source_t
  * as part of this operation.
  *
  * If @a pin_externals is set, pin URLs in copied externals definitions
- * to their last-changed revision unless they were already pinned to a
- * particular revision.
+ * to their current revision unless they were already pinned to a
+ * particular revision. If non-NULL, @a externals_to_pin restricts pinning
+ * to a subset of externals. It is a hash table keyed by either a local
+ * absolute path or a URL at which an svn:externals property is set.
+ * The hash table contains apr_array_header_t* elements as returned
+ * by svn_wc_parse_externals_description3(). These arrays contain elements
+ * of type svn_wc_external_item2_t*. Externals corresponding to these
+ * items will be pinned, other externals will not be pinned.
+ * If @a externals_to_pin is @c NULL then all externals are pinned.
+ * If @a pin_externals is @c FALSE then @a externals_to_pin is ignored.
  *
  * If non-NULL, @a revprop_table is a hash table holding additional,
  * custom revision properties (<tt>const char *</tt> names mapped to
@@ -4523,6 +4531,7 @@ svn_client_copy7(const apr_array_header_
                  svn_boolean_t make_parents,
                  svn_boolean_t ignore_externals,
                  svn_boolean_t pin_externals,
+                 const apr_hash_t *externals_to_pin,
                  const apr_hash_t *revprop_table,
                  svn_commit_callback2_t commit_callback,
                  void *commit_baton,

Modified: subversion/branches/pin-externals/subversion/libsvn_client/copy.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/libsvn_client/copy.c?rev=1657267&r1=1657266&r2=1657267&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_client/copy.c (original)
+++ subversion/branches/pin-externals/subversion/libsvn_client/copy.c Wed Feb  
4 14:58:59 2015
@@ -177,6 +177,73 @@ get_copy_pair_ancestors(const apr_array_
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+make_external_description(const char **new_external_description,
+                          const char *local_abspath_or_url,
+                          svn_wc_external_item2_t *item,
+                          svn_wc__externals_parser_info_t *info,
+                          svn_opt_revision_t external_pegrev,
+                          apr_pool_t *pool)
+{
+  const char *rev_str;
+  const char *peg_rev_str;
+
+  switch (info->format)
+    {
+      case svn_wc__external_description_format_1:
+        if (external_pegrev.kind == svn_opt_revision_unspecified)
+          rev_str = info->rev_str ? info->rev_str : "";
+        else if (info->rev_str && item->revision.kind != svn_opt_revision_head)
+          rev_str = apr_psprintf(pool, "%s ", info->rev_str);
+        else
+          {
+            /* ### can't handle svn_opt_revision_date without info->rev_str */
+            SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_number);
+            rev_str = apr_psprintf(pool, "-r%ld ",
+                                   external_pegrev.value.number);
+          }
+
+        *new_external_description =
+          apr_psprintf(pool, "%s %s%s\n", item->target_dir, rev_str, 
item->url);
+        break;
+
+      case svn_wc__external_description_format_2:
+        if (external_pegrev.kind == svn_opt_revision_unspecified)
+          rev_str = info->rev_str ? info->rev_str : "";
+        else if (info->rev_str && item->revision.kind != svn_opt_revision_head)
+          rev_str = apr_psprintf(pool, "%s ", info->rev_str);
+        else
+          rev_str = "";
+
+        if (external_pegrev.kind == svn_opt_revision_unspecified)
+          peg_rev_str = info->peg_rev_str ? info->peg_rev_str : "";
+        else if (info->peg_rev_str &&
+                 item->peg_revision.kind != svn_opt_revision_head)
+          peg_rev_str = info->peg_rev_str;
+        else
+          {
+            /* ### can't handle svn_opt_revision_date without info->rev_str */
+            SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_number);
+            peg_rev_str = apr_psprintf(pool, "@%ld",
+                                       external_pegrev.value.number);
+          }
+
+        *new_external_description =
+          apr_psprintf(pool, "%s%s%s %s\n", rev_str, item->url, peg_rev_str,
+                       item->target_dir);
+        break;
+
+      default:
+        return svn_error_createf(
+                 SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
+                 _("%s property defined at '%s' is using an unsupported "
+                   "syntax"), SVN_PROP_EXTERNALS,
+                 svn_dirent_local_style(local_abspath_or_url, pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Pin all externals listed in EXTERNALS_PROP_VAL to their last-changed
  * revision. Return a new property value in *PINNED_EXTERNALS allocated
  * in RESULT_POOL. LOCAL_ABSPATH_OR_URL is the path or URL defining the
@@ -185,6 +252,7 @@ get_copy_pair_ancestors(const apr_array_
 static svn_error_t *
 pin_externals_prop(svn_string_t **pinned_externals,
                    svn_string_t *externals_prop_val,
+                   const apr_hash_t *externals_to_pin,
                    const char *repos_root_url,
                    const char *local_abspath_or_url,
                    svn_client_ctx_t *ctx,
@@ -212,14 +280,50 @@ pin_externals_prop(svn_string_t **pinned
       svn_wc__externals_parser_info_t *info;
       svn_opt_revision_t external_pegrev;
       const char *pinned_desc;
-      const char *rev_str;
-      const char *peg_rev_str;
       
       svn_pool_clear(iterpool);
 
       item = APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
       info = APR_ARRAY_IDX(parser_infos, i, svn_wc__externals_parser_info_t *);
 
+      if (externals_to_pin)
+        {
+          apr_array_header_t *items_to_pin;
+
+          items_to_pin = svn_hash_gets((apr_hash_t *)externals_to_pin,
+                                       local_abspath_or_url);
+          if (items_to_pin)
+            {
+              int j;
+              svn_wc_external_item2_t *item_to_pin = NULL;
+
+              for (j = 0; j < items_to_pin->nelts; j++)
+                {
+                  item_to_pin = APR_ARRAY_IDX(items_to_pin, j,
+                                              svn_wc_external_item2_t *);
+                  if (item_to_pin && strcmp(item->url, item_to_pin->url) == 0 
&&
+                      strcmp(item->target_dir, item_to_pin->target_dir) == 0)
+                    break;
+                  else
+                    item_to_pin = NULL;
+                }
+
+              /* If this item is not in our list of external items to pin then
+               * simply keep the external at its original value. */
+              if (item_to_pin == NULL)
+                {
+                  const char *desc;
+
+                  external_pegrev.kind = svn_opt_revision_unspecified;
+                  SVN_ERR(make_external_description(&desc, 
local_abspath_or_url,
+                                                    item, info, 
external_pegrev,
+                                                    iterpool));
+                  svn_stringbuf_appendcstr(buf, desc);
+                  continue;
+                }
+            }
+        }
+
       if (item->peg_revision.kind == svn_opt_revision_date)
         {
           external_pegrev.kind = svn_opt_revision_date;
@@ -296,48 +400,8 @@ pin_externals_prop(svn_string_t **pinned
       SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_date ||
                      external_pegrev.kind == svn_opt_revision_number);
 
-      switch (info->format)
-        {
-          case svn_wc__external_description_format_1:
-            if (info->rev_str && item->revision.kind != svn_opt_revision_head)
-              rev_str = apr_psprintf(iterpool, "%s ", info->rev_str);
-            else
-              {
-                SVN_ERR_ASSERT(external_pegrev.kind == 
svn_opt_revision_number);
-                rev_str = apr_psprintf(iterpool, "-r%ld ",
-                                       external_pegrev.value.number);
-              }
-
-            pinned_desc = apr_psprintf(iterpool, "%s %s%s\n", item->target_dir,
-                                       rev_str, item->url);
-            break;
-
-          case svn_wc__external_description_format_2:
-            if (info->rev_str && item->revision.kind != svn_opt_revision_head)
-              rev_str = apr_psprintf(iterpool, "%s ", info->rev_str);
-            else
-              rev_str = "";
-
-            if (info->peg_rev_str &&
-                item->peg_revision.kind != svn_opt_revision_head)
-              peg_rev_str = info->peg_rev_str;
-            else
-              {
-                SVN_ERR_ASSERT(external_pegrev.kind == 
svn_opt_revision_number);
-                peg_rev_str = apr_psprintf(iterpool, "@%ld",
-                                           external_pegrev.value.number);
-              }
-            pinned_desc = apr_psprintf(iterpool, "%s%s%s %s\n", rev_str, 
item->url,
-                                       peg_rev_str, item->target_dir);
-            break;
-
-          default:
-            return svn_error_createf(
-                     SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
-                     _("%s property defined at '%s' is using an unsupported "
-                       "syntax"), SVN_PROP_EXTERNALS,
-                     svn_dirent_local_style(local_abspath_or_url, iterpool));
-        }
+      SVN_ERR(make_external_description(&pinned_desc, local_abspath_or_url,
+                                        item, info, external_pegrev, 
iterpool));
 
       svn_stringbuf_appendcstr(buf, pinned_desc);
     }
@@ -350,6 +414,7 @@ pin_externals_prop(svn_string_t **pinned
 
 static svn_error_t *
 resolve_pinned_externals(apr_hash_t **new_externals,
+                         const apr_hash_t *externals_to_pin,
                          svn_client__copy_pair_t *pair,
                          svn_ra_session_t *ra_session,
                          const char *repos_root_url,
@@ -421,6 +486,7 @@ resolve_pinned_externals(apr_hash_t **ne
       svn_pool_clear(iterpool);
 
       SVN_ERR(pin_externals_prop(&new_propval, externals_propval,
+                                 externals_to_pin,
                                  repos_root_url, local_abspath_or_url, ctx,
                                  result_pool, iterpool));
       if (svn_path_is_url(pair->src_abspath_or_url))
@@ -449,6 +515,7 @@ do_wc_to_wc_copies_with_write_lock(svn_b
                                    const apr_array_header_t *copy_pairs,
                                    const char *dst_parent,
                                    svn_boolean_t pin_externals,
+                                   const apr_hash_t *externals_to_pin,
                                    svn_client_ctx_t *ctx,
                                    apr_pool_t *scratch_pool)
 {
@@ -477,7 +544,8 @@ do_wc_to_wc_copies_with_write_lock(svn_b
                                           NULL, NULL, NULL, ctx->wc_ctx,
                                           pair->src_abspath_or_url, FALSE,
                                           scratch_pool, iterpool));
-          SVN_ERR(resolve_pinned_externals(&pinned_externals, pair, NULL,
+          SVN_ERR(resolve_pinned_externals(&pinned_externals,
+                                           externals_to_pin, pair, NULL,
                                            repos_root_url, ctx,
                                            iterpool, iterpool));
         }
@@ -529,6 +597,7 @@ static svn_error_t *
 do_wc_to_wc_copies(svn_boolean_t *timestamp_sleep,
                    const apr_array_header_t *copy_pairs,
                    svn_boolean_t pin_externals,
+                   const apr_hash_t *externals_to_pin,
                    svn_client_ctx_t *ctx,
                    apr_pool_t *pool)
 {
@@ -542,7 +611,8 @@ do_wc_to_wc_copies(svn_boolean_t *timest
 
   SVN_WC__CALL_WITH_WRITE_LOCK(
     do_wc_to_wc_copies_with_write_lock(timestamp_sleep, copy_pairs, dst_parent,
-                                       pin_externals, ctx, pool),
+                                       pin_externals, externals_to_pin,
+                                       ctx, pool),
     ctx->wc_ctx, dst_parent_abspath, FALSE, pool);
 
   return SVN_NO_ERROR;
@@ -1182,6 +1252,7 @@ repos_to_repos_copy(const apr_array_head
                     svn_client_ctx_t *ctx,
                     svn_boolean_t is_move,
                     svn_boolean_t pin_externals,
+                    const apr_hash_t *externals_to_pin,
                     apr_pool_t *pool)
 {
   svn_error_t *err;
@@ -1467,7 +1538,8 @@ repos_to_repos_copy(const apr_array_head
         {
           apr_hash_t *pinned_externals;
 
-          SVN_ERR(resolve_pinned_externals(&pinned_externals, pair,
+          SVN_ERR(resolve_pinned_externals(&pinned_externals,
+                                           externals_to_pin, pair,
                                            ra_session, repos_root,
                                            ctx, pool, pool));
           if (pin_externals_only_infos == NULL)
@@ -1729,6 +1801,7 @@ wc_to_repos_copy(const apr_array_header_
                  svn_commit_callback2_t commit_callback,
                  void *commit_baton,
                  svn_boolean_t pin_externals,
+                 const apr_hash_t *externals_to_pin,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *scratch_pool)
 {
@@ -1931,7 +2004,8 @@ wc_to_repos_copy(const apr_array_header_
           apr_hash_t *pinned_externals;
           apr_hash_index_t *hi;
 
-          SVN_ERR(resolve_pinned_externals(&pinned_externals, pair,
+          SVN_ERR(resolve_pinned_externals(&pinned_externals,
+                                           externals_to_pin, pair,
                                            ra_session, cukb.repos_root_url,
                                            ctx, scratch_pool, iterpool));
           for (hi = apr_hash_first(scratch_pool, pinned_externals);
@@ -2081,6 +2155,7 @@ repos_to_wc_copy_single(svn_boolean_t *t
                         svn_boolean_t same_repositories,
                         svn_boolean_t ignore_externals,
                         svn_boolean_t pin_externals,
+                        const apr_hash_t *externals_to_pin,
                         svn_ra_session_t *ra_session,
                         svn_client_ctx_t *ctx,
                         apr_pool_t *pool)
@@ -2208,7 +2283,8 @@ repos_to_wc_copy_single(svn_boolean_t *t
           apr_hash_t *new_depths;
 
           SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
-          SVN_ERR(resolve_pinned_externals(&pinned_externals, pair,
+          SVN_ERR(resolve_pinned_externals(&pinned_externals,
+                                           externals_to_pin, pair,
                                            ra_session, repos_root_url,
                                            ctx, pool, pool));
 
@@ -2310,6 +2386,7 @@ repos_to_wc_copy_locked(svn_boolean_t *t
                         const char *top_dst_path,
                         svn_boolean_t ignore_externals,
                         svn_boolean_t pin_externals,
+                        const apr_hash_t *externals_to_pin,
                         svn_ra_session_t *ra_session,
                         svn_client_ctx_t *ctx,
                         apr_pool_t *scratch_pool)
@@ -2374,7 +2451,8 @@ repos_to_wc_copy_locked(svn_boolean_t *t
                                       APR_ARRAY_IDX(copy_pairs, i,
                                                     svn_client__copy_pair_t *),
                                       same_repositories,
-                                      ignore_externals, pin_externals,
+                                      ignore_externals,
+                                      pin_externals, externals_to_pin,
                                       ra_session, ctx, iterpool));
     }
   svn_pool_destroy(iterpool);
@@ -2388,6 +2466,7 @@ repos_to_wc_copy(svn_boolean_t *timestam
                  svn_boolean_t make_parents,
                  svn_boolean_t ignore_externals,
                  svn_boolean_t pin_externals,
+                 const apr_hash_t *externals_to_pin,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool)
 {
@@ -2496,7 +2575,8 @@ repos_to_wc_copy(svn_boolean_t *timestam
   SVN_WC__CALL_WITH_WRITE_LOCK(
     repos_to_wc_copy_locked(timestamp_sleep,
                             copy_pairs, top_dst_path, ignore_externals,
-                            pin_externals, ra_session, ctx, pool),
+                            pin_externals, externals_to_pin,
+                            ra_session, ctx, pool),
     ctx->wc_ctx, lock_abspath, FALSE, pool);
   return SVN_NO_ERROR;
 }
@@ -2523,6 +2603,7 @@ try_copy(svn_boolean_t *timestamp_sleep,
          svn_boolean_t make_parents,
          svn_boolean_t ignore_externals,
          svn_boolean_t pin_externals,
+         const apr_hash_t *externals_to_pin,
          const apr_hash_t *revprop_table,
          svn_commit_callback2_t commit_callback,
          void *commit_baton,
@@ -2827,6 +2908,7 @@ try_copy(svn_boolean_t *timestamp_sleep,
           return svn_error_trace(do_wc_to_wc_copies(timestamp_sleep,
                                                     copy_pairs,
                                                     pin_externals,
+                                                    externals_to_pin,
                                                     ctx, pool));
         }
     }
@@ -2835,21 +2917,21 @@ try_copy(svn_boolean_t *timestamp_sleep,
       return svn_error_trace(
         wc_to_repos_copy(copy_pairs, make_parents, revprop_table,
                          commit_callback, commit_baton,
-                         pin_externals, ctx, pool));
+                         pin_externals, externals_to_pin, ctx, pool));
     }
   else if ((srcs_are_urls) && (! dst_is_url))
     {
       return svn_error_trace(
         repos_to_wc_copy(timestamp_sleep,
                          copy_pairs, make_parents, ignore_externals,
-                         pin_externals, ctx, pool));
+                         pin_externals, externals_to_pin, ctx, pool));
     }
   else
     {
       return svn_error_trace(
         repos_to_repos_copy(copy_pairs, make_parents, revprop_table,
                             commit_callback, commit_baton, ctx, is_move,
-                            pin_externals, pool));
+                            pin_externals, externals_to_pin, pool));
     }
 }
 
@@ -2863,6 +2945,7 @@ svn_client_copy7(const apr_array_header_
                  svn_boolean_t make_parents,
                  svn_boolean_t ignore_externals,
                  svn_boolean_t pin_externals,
+                 const apr_hash_t *externals_to_pin,
                  const apr_hash_t *revprop_table,
                  svn_commit_callback2_t commit_callback,
                  void *commit_baton,
@@ -2885,6 +2968,7 @@ svn_client_copy7(const apr_array_header_
                  make_parents,
                  ignore_externals,
                  pin_externals,
+                 externals_to_pin,
                  revprop_table,
                  commit_callback, commit_baton,
                  ctx,
@@ -2920,6 +3004,7 @@ svn_client_copy7(const apr_array_header_
                      make_parents,
                      ignore_externals,
                      pin_externals,
+                     externals_to_pin,
                      revprop_table,
                      commit_callback, commit_baton,
                      ctx,
@@ -2982,6 +3067,7 @@ svn_client_move7(const apr_array_header_
                  make_parents,
                  FALSE /* ignore_externals */,
                  FALSE /* pin_externals */,
+                 NULL /* externals_to_pin */,
                  revprop_table,
                  commit_callback, commit_baton,
                  ctx,
@@ -3016,6 +3102,7 @@ svn_client_move7(const apr_array_header_
                      make_parents,
                      FALSE /* ignore_externals */,
                      FALSE /* pin_externals */,
+                     NULL /* externals_to_pin */,
                      revprop_table,
                      commit_callback, commit_baton,
                      ctx,

Modified: 
subversion/branches/pin-externals/subversion/libsvn_client/deprecated.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/libsvn_client/deprecated.c?rev=1657267&r1=1657266&r2=1657267&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_client/deprecated.c 
(original)
+++ subversion/branches/pin-externals/subversion/libsvn_client/deprecated.c Wed 
Feb  4 14:58:59 2015
@@ -640,7 +640,7 @@ svn_client_copy6(const apr_array_header_
 {
   return svn_error_trace(svn_client_copy7(sources, dst_path, copy_as_child,
                                           make_parents, ignore_externals,
-                                          FALSE, revprop_table,
+                                          FALSE, NULL, revprop_table,
                                           commit_callback, commit_baton,
                                           ctx, pool));
 }

Modified: subversion/branches/pin-externals/subversion/svn/copy-cmd.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svn/copy-cmd.c?rev=1657267&r1=1657266&r2=1657267&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/svn/copy-cmd.c (original)
+++ subversion/branches/pin-externals/subversion/svn/copy-cmd.c Wed Feb  4 
14:58:59 2015
@@ -170,6 +170,7 @@ svn_cl__copy(apr_getopt_t *os,
   err = svn_client_copy7(sources, dst_path, TRUE,
                          opt_state->parents, opt_state->ignore_externals,
                          opt_state->pin_externals,
+                         NULL, /* pin all externals */
                          opt_state->revprop_table,
                          (opt_state->quiet ? NULL : svn_cl__print_commit_info),
                          NULL,

Modified: 
subversion/branches/pin-externals/subversion/tests/libsvn_client/client-test.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/tests/libsvn_client/client-test.c?rev=1657267&r1=1657266&r2=1657267&view=diff
==============================================================================
--- 
subversion/branches/pin-externals/subversion/tests/libsvn_client/client-test.c 
(original)
+++ 
subversion/branches/pin-externals/subversion/tests/libsvn_client/client-test.c 
Wed Feb  4 14:58:59 2015
@@ -35,6 +35,8 @@
 #include "svn_repos.h"
 #include "svn_subst.h"
 #include "private/svn_wc_private.h"
+#include "svn_props.h"
+#include "svn_hash.h"
 
 #include "../svn_test.h"
 #include "../svn_test_fs.h"
@@ -1055,6 +1057,151 @@ test_remote_only_status(const svn_test_o
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+test_copy_pin_externals(const svn_test_opts_t *opts,
+                        apr_pool_t *pool)
+{
+  svn_opt_revision_t rev;
+  svn_opt_revision_t peg_rev;
+  const char *repos_url;
+  const char *A_url;
+  const char *A_copy_url;
+  const char *wc_path;
+  svn_client_ctx_t *ctx;
+  const svn_string_t *propval;
+  apr_hash_t *externals_to_pin;
+  apr_array_header_t *external_items;
+  apr_array_header_t *copy_sources;
+  svn_wc_external_item2_t item1;
+  svn_wc_external_item2_t item2;
+  svn_wc_external_item2_t item3;
+  svn_client_copy_source_t copy_source;
+  apr_hash_t *props;
+  apr_array_header_t *pinned_externals;
+  int i;
+
+  /* Create a filesytem and repository containing the Greek tree. */
+  SVN_ERR(create_greek_repos(&repos_url, "pin-externals", opts, pool));
+
+  wc_path = svn_test_data_path("pin-externals-working-copy", pool);
+
+  /* Remove old test data from the previous run */
+  SVN_ERR(svn_io_remove_dir2(wc_path, TRUE, NULL, NULL, pool));
+
+  SVN_ERR(svn_io_make_dir_recursively(wc_path, pool));
+  svn_test_add_dir_cleanup(wc_path);
+
+  rev.kind = svn_opt_revision_head;
+  peg_rev.kind = svn_opt_revision_unspecified;
+  SVN_ERR(svn_client_create_context(&ctx, pool));
+
+  /* Configure some externals on ^/A */
+  propval = svn_string_create("^/A/D/gamma B/gamma\n"
+                              "^/A/D/G C/exdir_G\n"
+                              "^/A/D/H@1 C/exdir_H\n"
+                              "^/A/D/H C/exdir_H2\n"
+                              "^/A/B D/z/y/z/blah\n",
+                              pool);
+  A_url = apr_pstrcat(pool, repos_url, "/A", SVN_VA_NULL);
+  SVN_ERR(svn_client_propset_remote(SVN_PROP_EXTERNALS, propval,
+                                    A_url, TRUE, 1, NULL,
+                                    NULL, NULL, ctx, pool));
+
+  /* Set up parameters for pinning 2 externals. */
+  externals_to_pin = apr_hash_make(pool);
+  item1.url = "^/A/D/gamma";
+  item1.target_dir = "B/gamma";
+  item2.url = "^/A/B";
+  item2.target_dir = "D/z/y/z/blah";
+  item3.url = "^/A/D/H";
+  item3.target_dir = "C/exdir_H2";
+  external_items = apr_array_make(pool, 2, sizeof(svn_wc_external_item2_t *));
+  APR_ARRAY_PUSH(external_items, svn_wc_external_item2_t *) = &item1;
+  APR_ARRAY_PUSH(external_items, svn_wc_external_item2_t *) = &item2;
+  APR_ARRAY_PUSH(external_items, svn_wc_external_item2_t *) = &item3;
+  svn_hash_sets(externals_to_pin, A_url, external_items);
+
+  /* Copy ^/A to ^/A_copy, pinning two non-pinned externals. */
+  copy_source.path = A_url;
+  copy_source.revision = &rev;
+  copy_source.peg_revision = &peg_rev;
+  copy_sources = apr_array_make(pool, 1, sizeof(svn_client_copy_source_t *));
+  APR_ARRAY_PUSH(copy_sources, svn_client_copy_source_t *) = &copy_source;
+  A_copy_url = apr_pstrcat(pool, repos_url, "/A_copy", SVN_VA_NULL);
+  SVN_ERR(svn_client_copy7(copy_sources, A_copy_url, FALSE, FALSE,
+                           FALSE, TRUE, externals_to_pin,
+                           NULL, NULL, NULL, ctx, pool));
+
+  /* Verify that externals where pinned as expected. */
+  SVN_ERR(svn_client_propget5(&props, NULL, SVN_PROP_EXTERNALS,
+                              A_copy_url, &peg_rev, &rev, NULL,
+                              svn_depth_empty, NULL, ctx, pool, pool));
+  propval = svn_hash_gets(props, A_copy_url);
+  SVN_TEST_ASSERT(propval);
+
+  SVN_ERR(svn_wc_parse_externals_description3(&pinned_externals, A_copy_url,
+                                              propval->data, TRUE, pool));
+
+  for (i = 0; i < pinned_externals->nelts; i++)
+    {
+      svn_wc_external_item2_t *item;
+
+      item = APR_ARRAY_IDX(pinned_externals, i, svn_wc_external_item2_t *);
+      if (strcmp(item->url, "^/A/D/gamma") == 0)
+        {
+          SVN_TEST_STRING_ASSERT(item->target_dir, "B/gamma");
+          /* Pinned to r2. */
+          SVN_TEST_ASSERT(item->revision.kind == svn_opt_revision_number);
+          SVN_TEST_ASSERT(item->revision.value.number == 2);
+          SVN_TEST_ASSERT(item->peg_revision.kind == svn_opt_revision_number);
+          SVN_TEST_ASSERT(item->peg_revision.value.number == 2);
+        }
+      else if (strcmp(item->url, "^/A/D/G") == 0)
+        {
+          SVN_TEST_STRING_ASSERT(item->target_dir, "C/exdir_G");
+          /* Not pinned. */
+          SVN_TEST_ASSERT(item->revision.kind == svn_opt_revision_head);
+          SVN_TEST_ASSERT(item->peg_revision.kind == svn_opt_revision_head);
+        }
+      else if (strcmp(item->url, "^/A/D/H") == 0)
+        {
+          if (strcmp(item->target_dir, "C/exdir_H") == 0)
+            {
+              /* Was already pinned to r1. */
+              SVN_TEST_ASSERT(item->revision.kind == svn_opt_revision_number);
+              SVN_TEST_ASSERT(item->revision.value.number == 1);
+              SVN_TEST_ASSERT(item->peg_revision.kind ==
+                              svn_opt_revision_number);
+              SVN_TEST_ASSERT(item->peg_revision.value.number == 1);
+            }
+          else if (strcmp(item->target_dir, "C/exdir_H2") == 0)
+            {
+              /* Pinned to r2. */
+              SVN_TEST_ASSERT(item->revision.kind == svn_opt_revision_number);
+              SVN_TEST_ASSERT(item->revision.value.number == 2);
+              SVN_TEST_ASSERT(item->peg_revision.kind ==
+                              svn_opt_revision_number);
+              SVN_TEST_ASSERT(item->peg_revision.value.number == 2);
+            }
+          else
+            SVN_TEST_ASSERT(FALSE); /* unknown external */
+        }
+      else if (strcmp(item->url, "^/A/B") == 0)
+        {
+          SVN_TEST_STRING_ASSERT(item->target_dir, "D/z/y/z/blah");
+          /* Pinned to r2. */
+          SVN_TEST_ASSERT(item->revision.kind == svn_opt_revision_number);
+          SVN_TEST_ASSERT(item->revision.value.number == 2);
+          SVN_TEST_ASSERT(item->peg_revision.kind == svn_opt_revision_number);
+          SVN_TEST_ASSERT(item->peg_revision.value.number == 2);
+        }
+      else
+        SVN_TEST_ASSERT(FALSE); /* unknown URL */
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* ========================================================================== 
*/
 
 
@@ -1079,6 +1226,8 @@ static struct svn_test_descriptor_t test
                        "test svn_client_suggest_merge_sources"),
     SVN_TEST_OPTS_PASS(test_remote_only_status,
                        "test svn_client_status6 with ignore_local_mods"),
+    SVN_TEST_OPTS_PASS(test_copy_pin_externals,
+                       "test svn_client_copy7 with externals_to_pin"),
     SVN_TEST_NULL
   };
 


Reply via email to