You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2014/12/08 08:54:02 UTC

svn commit: r1643757 - in /subversion/branches/pin-externals: ./ subversion/include/ subversion/libsvn_client/ subversion/svn/

Author: stsp
Date: Mon Dec  8 07:54:02 2014
New Revision: 1643757

URL: http://svn.apache.org/r1643757
Log:
Create the pin-externals branch, which is about automated pinning of URLs in
svn:externals properties during 'svn copy' operations (issue #1258, svncopy.pl).

This is work in progress. Basic functionality exists for all kinds of copy
operations (repos->repos, repos->WC, WC->repos, WC->WC). There are no tests
yet. The API could be improved (according to Branko) to serve needs of GUI
clients better. The code contains SVN_DBG print statements.

* subversion/include/svn_client.h
  (svn_client_copy7): Declare.
  (svn_client_copy6): Deprecate.

* subversion/libsvn_client/client.h
  (svn_client__remote_propget): Declare.

* subversion/libsvn_client/copy.c
  (pin_externals_prop, resolve_pinned_externals,
   queue_externals_change_path_infos,
   queue_prop_change_commit_items): New helpers.
  (do_wc_to_wc_copies_with_write_lock, do_wc_to_wc_copies): Implement externals
   pinning for WC->WC copies.
  (path_driver_info_t, path_driver_cb_func): Add functionality for pinning
   externals during copies involving the repository.
  (repos_to_repos_copy): Implement externals pinning for repos->repos copies.
  (wc_to_repos_copy): Implement externals pinning for wc->repos copies.
  (repos_to_wc_copy_single): Implement externals pinning for repos->wc copies.
  (repos_to_wc_copy_locked, repos_to_wc_copy, try_copy): Pass new parameters
   on to other functions.
  (svn_client_copy7): New variant of svn_client_copy() which supports a new
   'pin_externals' boolean argument.
  (svn_client_move7): Pass FALSE for pin_externals while copying.

   
* subversion/libsvn_client/deprecated.c
  (svn_client_copy6): Moved here.

* subversion/libsvn_client/prop_commands.c
  (remote_propget): Rename to svn_client__remote_propget().
  (svn_client_revprop_set2, svn_client_propget5): Update callers.

* subversion/svn/cl.h
  (svn_cl__opt_state_t): Add pin_externals option.

* subversion/svn/copy-cmd.c
  (svn_cl__copy): Use svn_client_copy7().

* subversion/svn/svn.c
  (svn_cl__longopt_t): Add opt_pin_externals.
  (svn_cl__options, svn_cl__cmd_table): Add --pin-externals option.
  (sub_main): Handle --pin-externals option.

Added:
    subversion/branches/pin-externals/   (props changed)
      - copied from r1643754, subversion/trunk/
Modified:
    subversion/branches/pin-externals/subversion/include/svn_client.h
    subversion/branches/pin-externals/subversion/libsvn_client/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/libsvn_client/prop_commands.c
    subversion/branches/pin-externals/subversion/svn/cl.h
    subversion/branches/pin-externals/subversion/svn/copy-cmd.c
    subversion/branches/pin-externals/subversion/svn/svn.c

Propchange: subversion/branches/pin-externals/
------------------------------------------------------------------------------
--- bugtraq:logregex (added)
+++ bugtraq:logregex Mon Dec  8 07:54:02 2014
@@ -0,0 +1,2 @@
+[Ii]ssues?:?(\s*(,|and)?\s*#\d+)+
+(\d+)

Propchange: subversion/branches/pin-externals/
------------------------------------------------------------------------------
    bugtraq:url = http://subversion.tigris.org/issues/show_bug.cgi?id=%BUGID%

Propchange: subversion/branches/pin-externals/
------------------------------------------------------------------------------
--- svn:auto-props (added)
+++ svn:auto-props Mon Dec  8 07:54:02 2014
@@ -0,0 +1,10 @@
+*.c = svn:eol-style=native
+*.cpp = svn:eol-style=native
+*.h = svn:eol-style=native
+*.hpp = svn:eol-style=native
+*.py = svn:eol-style=native
+*.pl = svn:eol-style=native
+*.rb = svn:eol-style=native
+*.txt = svn:eol-style=native
+README = svn:eol-style=native
+BRANCH-README = svn:eol-style=native

Propchange: subversion/branches/pin-externals/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Dec  8 07:54:02 2014
@@ -0,0 +1,57 @@
+ChangeLog*
+Makefile
+config.cache
+config.log
+config.nice
+config.status
+configure
+libtool
+.gdb_history
+.swig_checked
+*.orig
+*.rej
+TAGS
+tags
+neon
+build-outputs.mk
+autogen-standalone.mk
+autom4te.cache
+gen-make.opts
+tests.log*
+fails.log*
+db4-win32
+db
+*.o
+*~
+.*~
+apr
+apr-util
+apr-iconv
+Release
+Debug
+ipch
+subversion_msvc.dsw
+subversion_msvc.ncb
+subversion_msvc.opt
+subversion_msvc.plg
+subversion_vcnet.sln
+subversion_vcnet.ncb
+subversion_vcnet.suo
+subversion_vcnet.v11.suo
+subversion_vcnet.sdf
+subversion_vcnet.opensdf
+mkmf.log
+.project
+.classpath
+.cdtproject
+.settings
+.cproject
+zlib
+sqlite-amalgamation
+serf
+gmock-fused
+.git
+.gitignore
+compile_commands.json
+.kdev4
+*.kdev4

Propchange: subversion/branches/pin-externals/
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Mon Dec  8 07:54:02 2014
@@ -0,0 +1,86 @@
+/subversion/branches/1.5.x-r30215:870312
+/subversion/branches/1.7.x-fs-verify:1146708,1161180
+/subversion/branches/10Gb:1388102,1388163-1388190,1388195,1388202,1388205,1388211,1388276,1388362,1388375,1388394,1388636,1388639-1388640,1388643-1388644,1388654,1388720,1388789,1388795,1388801,1388805,1388807,1388810,1388816,1389044,1389276,1389289,1389662,1389867,1390017,1390209,1390216,1390407,1390409,1390414,1390419,1390955
+/subversion/branches/atomic-revprop:965046-1000689
+/subversion/branches/authzperf:1615360
+/subversion/branches/auto-props-sdc:1384106-1401643
+/subversion/branches/bdb-reverse-deltas:872050-872529
+/subversion/branches/cache-server:1458643-1476567
+/subversion/branches/diff-callbacks3:870059-870761
+/subversion/branches/diff-optimizations:1031270-1037352
+/subversion/branches/diff-optimizations-bytes:1037353-1067789
+/subversion/branches/dont-save-plaintext-passwords-by-default:870728-871118
+/subversion/branches/double-delete:870511-872970
+/subversion/branches/ev2-export:1325914,1332738,1413107
+/subversion/branches/explore-wc:875486,875493,875497,875507,875511,875514,875559,875580-875581,875584,875587,875611,875627,875647,875667-875668,875711-875712,875733-875734,875736,875744-875748,875751,875758,875782,875795-875796,875830,875836,875838,875842,875852,875855,875864,875870,875873,875880,875885-875888,875890,875897-875898,875905,875907-875909,875935,875943-875944,875946,875979,875982-875983,875985-875986,875990,875997
+/subversion/branches/file-externals:871779-873302
+/subversion/branches/fs-rep-sharing:869036-873803
+/subversion/branches/fsfs-format7:1426304,1430673,1433848,1438408,1438982,1441129,1442051,1442068,1442504,1442910,1443171,1443803,1444690,1444693,1444695,1445040,1445080,1446103,1451129,1453590,1454307,1460579,1461851,1461865,1462837,1462904,1463120,1467362,1467382,1469487,1471208,1477166,1478055,1481447,1489817,1489949,1490673-1490674,1491784,1493042,1498029,1498103,1498155,1500054,1507729-1507731,1507735-1507736
+/subversion/branches/fsfs-improvements:1499981-1547039
+/subversion/branches/fsfs-lock-many:1571740-1577217
+/subversion/branches/fsfs-pack:873717-874575
+/subversion/branches/fsx:1507845-1509914
+/subversion/branches/gnome-keyring:870558-871410
+/subversion/branches/gpg-agent-password-store:1005036-1150766
+/subversion/branches/gtest_addition:1452117-1502138
+/subversion/branches/http-protocol-v2:874395-876041
+/subversion/branches/in-memory-cache:869829-871452
+/subversion/branches/in-repo-authz:1414342-1424779
+/subversion/branches/inheritable-props:1297080-1395089
+/subversion/branches/integrate-cache-item-serialization:1068724-1068739
+/subversion/branches/integrate-cache-membuffer:998649-998852
+/subversion/branches/integrate-compression-level:1068651-1072287
+/subversion/branches/integrate-io-improvements:1068684-1072297
+/subversion/branches/integrate-is-cachable:1072568-1074082
+/subversion/branches/integrate-partial-getter:1072558-1076552
+/subversion/branches/integrate-readline-speedup:1072553-1072555
+/subversion/branches/integrate-stream-api-extensions:1068695-1072516
+/subversion/branches/integrate-string-improvements:1068251-1190617
+/subversion/branches/integrate-txdelta-caching:1072541-1078213
+/subversion/branches/issue-2779-dev:965496-984198
+/subversion/branches/issue-2843-dev:871432-874179
+/subversion/branches/issue-3000:871713,871716-871719,871721-871726,871728,871734
+/subversion/branches/issue-3067-deleted-subtrees:873375-874084
+/subversion/branches/issue-3148-dev:875193-875204
+/subversion/branches/issue-3220-dev:872210-872226
+/subversion/branches/issue-3242-dev:879653-896436
+/subversion/branches/issue-3334-dirs:875156-875867
+/subversion/branches/issue-3975:1152931-1160746
+/subversion/branches/issue-4116-dev:1424719-1425040
+/subversion/branches/issue-4194-dev:1410507-1414880
+/subversion/branches/javahl-ra:991978-1494640
+/subversion/branches/kwallet:870785-871314
+/subversion/branches/log-addressing:1509279-1546844
+/subversion/branches/log-g-performance:870941-871032
+/subversion/branches/merge-skips-obstructions:874525-874615
+/subversion/branches/move-tracking-2:1607334
+/subversion/branches/multi-layer-moves:1239019-1300930
+/subversion/branches/nfc-nfd-aware-client:870276,870376
+/subversion/branches/node_pool:1304828-1305388
+/subversion/branches/performance:979193,980118,981087,981090,981189,981194,981287,981684,981827,982043,982355,983398,983406,983430,983474,983488,983490,983760,983764,983766,983770,984927,984973,984984,985014,985037,985046,985472,985477,985482,985487-985488,985493,985497,985500,985514,985601,985603,985606,985669,985673,985695,985697,986453,986465,986485,986491-986492,986517,986521,986605,986608,986817,986832,987865,987868-987869,987872,987886-987888,987893,988319,988898,990330,990533,990535-990537,990541,990568,990572,990574-990575,990600,990759,992899,992904,992911,993127,993141,994956,995478,995507,995603,998012,998858,999098,1001413,1001417,1004291,1022668,1022670,1022676,1022715,1022719,1025660,1025672,1027193,1027203,1027206,1027214,1027227,1028077,1028092,1028094,1028104,1028107,1028111,1028354,1029038,1029042-1029043,1029054-1029055,1029062-1029063,1029078,1029080,1029090,1029092-1029093,1029111,1029151,1029158,1029229-1029230,1029232,1029335-1029336,1029339-1029340,1029342,10
 29344,1030763,1030827,1031203,1031235,1032285,1032333,1033040,1033057,1033294,1035869,1035882,1039511,1043705,1053735,1056015,1066452,1067683,1067697-1078365
+/subversion/branches/py-tests-as-modules:956579-1033052
+/subversion/branches/ra_serf-digest-authn:875693-876404
+/subversion/branches/reintegrate-improvements:873853-874164
+/subversion/branches/remote-only-status:1581845-1586090
+/subversion/branches/revprop-cache:1298521-1326293
+/subversion/branches/revprop-caching-ng:1620597,1620599
+/subversion/branches/revprop-packing:1143907,1143971,1143997,1144017,1144499,1144568,1146145
+/subversion/branches/subtree-mergeinfo:876734-878766
+/subversion/branches/svn-mergeinfo-enhancements:870119-870195,870197-870288
+/subversion/branches/svn-patch-improvements:918519-934609
+/subversion/branches/svn_mutex:1141683-1182099
+/subversion/branches/svnpatch-diff:865738-876477
+/subversion/branches/svnraisetc:874709-875149
+/subversion/branches/svnserve-logging:869828-870893
+/subversion/branches/tc-issue-3334:874697-874773
+/subversion/branches/tc-merge-notify:874017-874062
+/subversion/branches/tc-resolve:874191-874239
+/subversion/branches/tc_url_rev:874351-874483
+/subversion/branches/tree-conflicts:868291-873154
+/subversion/branches/tree-conflicts-notify:873926-874008
+/subversion/branches/tristate-chunked-request:1502394-1502681
+/subversion/branches/tweak-build-take-two:1424288-1425049,1425051-1425613
+/subversion/branches/uris-as-urls:1060426-1064427
+/subversion/branches/verify-at-commit:1462039-1462408
+/subversion/branches/verify-keep-going:1439280-1546110
+/subversion/branches/wc-collate-path:1402685-1480384

Propchange: subversion/branches/pin-externals/
------------------------------------------------------------------------------
    tsvn:logwidthmarker = 78

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=1643757&r1=1643754&r2=1643757&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/include/svn_client.h (original)
+++ subversion/branches/pin-externals/subversion/include/svn_client.h Mon Dec  8 07:54:02 2014
@@ -4486,6 +4486,10 @@ typedef struct svn_client_copy_source_t
  * If @a ignore_externals is set, don't process externals definitions
  * 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.
+ *
  * If non-NULL, @a revprop_table is a hash table holding additional,
  * custom revision properties (<tt>const char *</tt> names mapped to
  * <tt>svn_string_t *</tt> values) to be set on the new revision in
@@ -4504,7 +4508,26 @@ typedef struct svn_client_copy_source_t
  * @a commit_callback with @a commit_baton and a #svn_commit_info_t for
  * the commit.
  *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_client_copy7(const apr_array_header_t *sources,
+                 const char *dst_path,
+                 svn_boolean_t copy_as_child,
+                 svn_boolean_t make_parents,
+                 svn_boolean_t ignore_externals,
+                 svn_boolean_t pin_externals,
+                 const apr_hash_t *revprop_table,
+                 svn_commit_callback2_t commit_callback,
+                 void *commit_baton,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *pool);
+
+/**
+ * Similar to svn_client_copy7(), but cannot pin externals.
+ *
  * @since New in 1.7.
+ * @deprecated Provided for backward compatibility with the 1.7 API.
  */
 svn_error_t *
 svn_client_copy6(const apr_array_header_t *sources,

Modified: subversion/branches/pin-externals/subversion/libsvn_client/client.h
URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/libsvn_client/client.h?rev=1643757&r1=1643754&r2=1643757&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_client/client.h (original)
+++ subversion/branches/pin-externals/subversion/libsvn_client/client.h Mon Dec  8 07:54:02 2014
@@ -1179,6 +1179,40 @@ svn_client__arbitrary_nodes_diff(const c
                                  apr_pool_t *scratch_pool);
 
 
+/* Helper for the remote case of svn_client_propget.
+ *
+ * If PROPS is not null, then get the value of property PROPNAME in REVNUM,
+ * using RA_LIB and SESSION.  Store the value ('svn_string_t *') in PROPS,
+ * under the path key "TARGET_PREFIX/TARGET_RELATIVE" ('const char *').
+ *
+ * If INHERITED_PROPS is not null, then set *INHERITED_PROPS to a
+ * depth-first ordered array of svn_prop_inherited_item_t * structures
+ * representing the PROPNAME properties inherited by the target.  If
+ * INHERITABLE_PROPS in not null and no inheritable properties are found,
+ * then set *INHERITED_PROPS to an empty array.
+ *
+ * Recurse according to DEPTH, similarly to svn_client_propget3().
+ *
+ * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
+ * Yes, caller passes this; it makes the recursion more efficient :-).
+ *
+ * Allocate PROPS and *INHERITED_PROPS in RESULT_POOL, but do all temporary
+ * work in SCRATCH_POOL.  The two pools can be the same; recursive
+ * calls may use a different SCRATCH_POOL, however.
+ */
+svn_error_t *
+svn_client__remote_propget(apr_hash_t *props,
+                           apr_array_header_t **inherited_props,
+                           const char *propname,
+                           const char *target_prefix,
+                           const char *target_relative,
+                           svn_node_kind_t kind,
+                           svn_revnum_t revnum,
+                           svn_ra_session_t *ra_session,
+                           svn_depth_t depth,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

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=1643757&r1=1643754&r2=1643757&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_client/copy.c (original)
+++ subversion/branches/pin-externals/subversion/libsvn_client/copy.c Mon Dec  8 07:54:02 2014
@@ -177,12 +177,236 @@ get_copy_pair_ancestors(const apr_array_
   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 defining the
+ * svn:externals property. Use SCRATCH_POOL for temporary allocations.
+ */
+static svn_error_t *
+pin_externals_prop(svn_string_t **pinned_externals,
+                   svn_string_t *externals_prop_val,
+                   const char *repos_root_url,
+                   const char *local_abspath_or_url,
+                   svn_client_ctx_t *ctx,
+                   apr_pool_t *result_pool,
+                   apr_pool_t *scratch_pool)
+{
+  svn_stringbuf_t *buf;
+  apr_array_header_t *external_items;
+  const char *session_url;
+  int i;
+  apr_pool_t *iterpool;
+
+  SVN_DBG(("repos_root_url: %s", repos_root_url));
+  SVN_DBG(("local_abspath_or_url: %s", local_abspath_or_url));
+
+  SVN_ERR(svn_wc_parse_externals_description3(&external_items,
+                                              local_abspath_or_url,
+                                              externals_prop_val->data,
+                                              TRUE /* canonicalize_url */,
+                                              scratch_pool));
+
+  buf = svn_stringbuf_create_empty(scratch_pool);
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < external_items->nelts; i++)
+    {
+      svn_wc_external_item2_t *item;
+      const char *pinned_desc;
+      const char *resolved_url;
+      const char *defining_url;
+      svn_ra_session_t *external_ra_session;
+      svn_revnum_t external_youngest_rev;
+      svn_dirent_t *dirent;
+      
+      svn_pool_clear(iterpool);
+
+      if (!svn_path_is_url(local_abspath_or_url))
+        SVN_ERR(svn_wc__node_get_url(&defining_url, ctx->wc_ctx,
+                                     local_abspath_or_url,
+                                     iterpool, iterpool));
+      else
+        defining_url = local_abspath_or_url;
+
+      item = APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
+      SVN_ERR(svn_wc__resolve_relative_external_url(&resolved_url, item,
+                                                    repos_root_url,
+                                                    defining_url,
+                                                    iterpool,
+                                                    iterpool));
+      SVN_DBG(("resolved external url: %s", resolved_url));
+
+      SVN_ERR(svn_client__open_ra_session_internal(&external_ra_session,
+                                                   NULL, resolved_url,
+                                                   NULL, NULL, FALSE, FALSE,
+                                                   ctx, iterpool,
+                                                   iterpool));
+      SVN_ERR(svn_ra_get_session_url(external_ra_session, &session_url, scratch_pool));
+      SVN_DBG(("external ra session url: %s", session_url));
+      SVN_ERR(svn_ra_get_latest_revnum(external_ra_session,
+                                       &external_youngest_rev,
+                                       iterpool));
+      /* ### If the path was copied/moved since it's last-changed revision,
+         ### we should use the pre-copy/pre-move path.*/
+      SVN_ERR(svn_ra_stat(external_ra_session, "",
+                          external_youngest_rev,
+                          &dirent,
+                          iterpool));
+
+      if (dirent == NULL)
+        return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+                                 _("Cannot determine last-changed revision "
+                                   "of '%s'"), resolved_url);
+      SVN_DBG(("external's last-changed revision: %lu", dirent->created_rev));
+
+      if (item->revision.kind == svn_opt_revision_date)
+        {
+          item->revision.kind = svn_opt_revision_number;
+          SVN_ERR(svn_ra_get_dated_revision(external_ra_session,
+                                            &item->revision.value.number,
+                                            item->revision.value.date,
+                                            iterpool));
+        }
+
+      if (item->peg_revision.kind == svn_opt_revision_date)
+        {
+          item->peg_revision.kind = svn_opt_revision_number;
+          SVN_ERR(svn_ra_get_dated_revision(external_ra_session,
+                                            &item->peg_revision.value.number,
+                                            item->peg_revision.value.date,
+                                            iterpool));
+        }
+
+      if (item->revision.kind != svn_opt_revision_number)
+        {
+          item->revision.kind = svn_opt_revision_number;
+          item->revision.value.number = dirent->created_rev;
+        }
+
+      if (item->peg_revision.kind != svn_opt_revision_number)
+        {
+          item->peg_revision.kind = svn_opt_revision_number;
+          item->peg_revision.value.number = dirent->created_rev;
+        }
+
+      pinned_desc = apr_psprintf(iterpool, "-r%lu %s@%lu %s%s",
+                                 item->revision.value.number,
+                                 item->url,
+                                 item->peg_revision.value.number,
+                                 item->target_dir,
+                                 APR_EOL_STR);
+      SVN_DBG(("pinned external: %s", pinned_desc));
+      svn_stringbuf_appendcstr(buf, pinned_desc);
+    }
+  svn_pool_destroy(iterpool);
+
+  *pinned_externals = svn_string_create_from_buf(buf, result_pool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+resolve_pinned_externals(apr_hash_t **new_externals,
+                         svn_client__copy_pair_t *pair,
+                         svn_ra_session_t *ra_session,
+                         const char *repos_root_url,
+                         svn_client_ctx_t *ctx,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
+{
+  const char *old_url = NULL;
+  apr_hash_t *externals_props;
+  apr_hash_index_t *hi;
+  apr_pool_t *iterpool;
+
+  if (svn_path_is_url(pair->src_abspath_or_url))
+    {
+      SVN_ERR(svn_client__ensure_ra_session_url(&old_url, ra_session,
+                                                pair->src_abspath_or_url,
+                                                scratch_pool));
+      SVN_DBG(("old_url: %s", old_url));
+      externals_props = apr_hash_make(scratch_pool);
+      SVN_ERR(svn_client__remote_propget(externals_props, NULL,
+                                         SVN_PROP_EXTERNALS,
+                                         pair->src_abspath_or_url, "",
+                                         svn_node_dir,
+                                         pair->src_revnum,
+                                         ra_session,
+                                         svn_depth_infinity,
+                                         scratch_pool,
+                                         scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(svn_wc__externals_gather_definitions(&externals_props, NULL,
+                                                   ctx->wc_ctx,
+                                                   pair->src_abspath_or_url,
+                                                   svn_depth_infinity,
+                                                   scratch_pool, scratch_pool));
+
+      /* ### gather_definitions returns propvals as const char * */
+      for (hi = apr_hash_first(scratch_pool, externals_props);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *local_abspath_or_url = apr_hash_this_key(hi);
+          const char *propval = apr_hash_this_val(hi);
+          svn_string_t *new_propval = svn_string_create(propval, scratch_pool);
+
+          svn_hash_sets(externals_props, local_abspath_or_url, new_propval);
+        }
+    }
+
+  if (apr_hash_count(externals_props) == 0)
+    {
+      if (old_url)
+        SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool));
+      return SVN_NO_ERROR;
+    }
+  
+  *new_externals = apr_hash_make(result_pool);
+  iterpool = svn_pool_create(scratch_pool);
+  for (hi = apr_hash_first(scratch_pool, externals_props);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *local_abspath_or_url = apr_hash_this_key(hi);
+      svn_string_t *externals_propval = apr_hash_this_val(hi);
+      const char *relpath;
+      svn_string_t *new_propval;
+
+      svn_pool_clear(iterpool);
+
+      SVN_DBG(("repos root url:  %s", repos_root_url));
+      SVN_DBG(("pinning externals for %s: %s", local_abspath_or_url, externals_propval->data));
+      SVN_ERR(pin_externals_prop(&new_propval, externals_propval,
+                                 repos_root_url, local_abspath_or_url, ctx,
+                                 result_pool, iterpool));
+      if (svn_path_is_url(pair->src_abspath_or_url))
+        relpath = svn_uri_skip_ancestor(pair->src_abspath_or_url,
+                                        local_abspath_or_url,
+                                        result_pool);
+      else
+        relpath = svn_dirent_skip_ancestor(pair->src_abspath_or_url,
+                                           local_abspath_or_url);
+      SVN_ERR_ASSERT(relpath);
+      svn_hash_sets(*new_externals, relpath, new_propval);
+    }
+  svn_pool_destroy(iterpool);
+
+  if (old_url)
+    SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+
 
 /* The guts of do_wc_to_wc_copies */
 static svn_error_t *
 do_wc_to_wc_copies_with_write_lock(svn_boolean_t *timestamp_sleep,
                                    const apr_array_header_t *copy_pairs,
                                    const char *dst_parent,
+                                   svn_boolean_t pin_externals,
                                    svn_client_ctx_t *ctx,
                                    apr_pool_t *scratch_pool)
 {
@@ -195,12 +419,27 @@ do_wc_to_wc_copies_with_write_lock(svn_b
       const char *dst_abspath;
       svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
                                                     svn_client__copy_pair_t *);
+      apr_hash_t *pinned_externals = NULL;
+
       svn_pool_clear(iterpool);
 
       /* Check for cancellation */
       if (ctx->cancel_func)
         SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
 
+      if (pin_externals)
+        {
+          const char *repos_root_url;
+
+          SVN_ERR(svn_wc__node_get_origin(NULL, NULL, NULL, &repos_root_url,
+                                          NULL, NULL, NULL, ctx->wc_ctx,
+                                          pair->src_abspath_or_url, FALSE,
+                                          scratch_pool, iterpool));
+          SVN_ERR(resolve_pinned_externals(&pinned_externals, pair, NULL,
+                                           repos_root_url, ctx,
+                                           iterpool, iterpool));
+        }
+
       /* Perform the copy */
       dst_abspath = svn_dirent_join(pair->dst_parent_abspath, pair->base_name,
                                     iterpool);
@@ -211,6 +450,31 @@ do_wc_to_wc_copies_with_write_lock(svn_b
                          ctx->notify_func2, ctx->notify_baton2, iterpool);
       if (err)
         break;
+
+      if (pinned_externals)
+        {
+          apr_hash_index_t *hi;
+
+          for (hi = apr_hash_first(iterpool, pinned_externals);
+               hi;
+               hi = apr_hash_next(hi))
+            {
+              const char *dst_relpath = apr_hash_this_key(hi);
+              svn_string_t *externals_propval = apr_hash_this_val(hi);
+              const char *local_abspath;
+
+              local_abspath = svn_dirent_join(pair->dst_abspath_or_url,
+                                              dst_relpath, iterpool);
+              SVN_DBG(("New externals for %s: %s", local_abspath, externals_propval->data));
+              SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath,
+                                       SVN_PROP_EXTERNALS, externals_propval,
+                                       svn_depth_empty, TRUE /* skip_checks */,
+                                       NULL  /* changelist_filter */,
+                                       ctx->cancel_func, ctx->cancel_baton,
+                                       NULL, NULL, /* no extra notification */
+                                       iterpool));
+            }
+        }
     }
   svn_pool_destroy(iterpool);
 
@@ -223,6 +487,7 @@ do_wc_to_wc_copies_with_write_lock(svn_b
 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,
                    svn_client_ctx_t *ctx,
                    apr_pool_t *pool)
 {
@@ -236,7 +501,7 @@ 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,
-                                       ctx, pool),
+                                       pin_externals, ctx, pool),
     ctx->wc_ctx, dst_parent_abspath, FALSE, pool);
 
   return SVN_NO_ERROR;
@@ -594,6 +859,8 @@ typedef struct path_driver_info_t
   svn_boolean_t resurrection;
   svn_boolean_t dir_add;
   svn_string_t *mergeinfo;  /* the new mergeinfo for the target */
+  svn_string_t *externals; /* new externals definitions for the target */
+  svn_boolean_t only_pin_externals;
 } path_driver_info_t;
 
 
@@ -626,12 +893,13 @@ path_driver_cb_func(void **dir_baton,
   /* Initialize return value. */
   *dir_baton = NULL;
 
+  SVN_DBG(("%s: path=%s", __func__, path));
   /* This function should never get an empty PATH.  We can neither
      create nor delete the empty PATH, so if someone is calling us
      with such, the code is just plain wrong. */
   SVN_ERR_ASSERT(! svn_path_is_empty(path));
 
-  /* Check to see if we need to add the path as a directory. */
+  /* Check to see if we need to add the path as a parent directory. */
   if (path_info->dir_add)
     {
       return cb_baton->editor->add_directory(path, parent_baton, NULL,
@@ -662,7 +930,7 @@ path_driver_cb_func(void **dir_baton,
       /* Not a move?  This must just be the copy addition. */
       else
         {
-          do_add = TRUE;
+          do_add = !path_info->only_pin_externals;
         }
     }
 
@@ -702,6 +970,14 @@ path_driver_cb_func(void **dir_baton,
                                                       pool));
         }
     }
+
+  if (path_info->externals)
+    {
+      SVN_DBG(("New externals for %s: %s", path_info->dst_path, path_info->externals->data));
+      SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton, SVN_PROP_EXTERNALS,
+                                                path_info->externals, pool));
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -787,6 +1063,71 @@ find_absent_parents2(svn_ra_session_t *r
 }
 
 static svn_error_t *
+queue_externals_change_path_infos(apr_array_header_t *new_path_infos,
+                                  apr_array_header_t *path_infos,
+                                  apr_hash_t *pinned_externals,
+                                  path_driver_info_t *parent_info,
+                                  apr_pool_t *result_pool,
+                                  apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_hash_index_t *hi;
+
+  for (hi = apr_hash_first(scratch_pool, pinned_externals);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *dst_relpath = apr_hash_this_key(hi);
+      svn_string_t *externals_prop = apr_hash_this_val(hi);
+      const char *src_url;
+      path_driver_info_t *info;
+      int i;
+
+      svn_pool_clear(iterpool);
+
+      src_url = svn_path_url_add_component2(parent_info->src_url,
+                                            dst_relpath, iterpool);
+
+      /* Try to find a path info the external change can be applied to. */
+      info = NULL;
+      for (i = 0; i < path_infos->nelts; i++)
+        {
+          path_driver_info_t *existing_info;
+          
+          existing_info = APR_ARRAY_IDX(path_infos, i, path_driver_info_t *);
+          if (strcmp(src_url, existing_info->src_url) == 0)
+            {
+              info = existing_info;
+              break;
+            }
+        }
+
+      if (info == NULL)
+        {
+          /* A copied-along child needs its externals pinned.
+             Create a new path info for this property change. */
+          info = apr_pcalloc(result_pool, sizeof(*info));
+          info->src_url = svn_path_url_add_component2(
+                                parent_info->src_url, dst_relpath,
+                                result_pool);
+          info->src_path = svn_relpath_join(parent_info->src_path,
+                                            dst_relpath,
+                                            result_pool);
+          info->dst_path = svn_relpath_join(parent_info->dst_path,
+                                            dst_relpath,
+                                            result_pool);
+          info->src_kind = svn_node_dir;
+          info->only_pin_externals = TRUE;
+          APR_ARRAY_PUSH(new_path_infos, path_driver_info_t *) = info;
+        }
+
+      info->externals = externals_prop;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 repos_to_repos_copy(const apr_array_header_t *copy_pairs,
                     svn_boolean_t make_parents,
                     const apr_hash_t *revprop_table,
@@ -794,6 +1135,7 @@ repos_to_repos_copy(const apr_array_head
                     void *commit_baton,
                     svn_client_ctx_t *ctx,
                     svn_boolean_t is_move,
+                    svn_boolean_t pin_externals,
                     apr_pool_t *pool)
 {
   svn_error_t *err;
@@ -809,6 +1151,7 @@ repos_to_repos_copy(const apr_array_head
   struct path_driver_cb_baton cb_baton;
   apr_array_header_t *new_dirs = NULL;
   apr_hash_t *commit_revprops;
+  apr_array_header_t *pin_externals_only_infos = NULL;
   int i;
   svn_client__copy_pair_t *first_pair =
     APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
@@ -1068,6 +1411,24 @@ repos_to_repos_copy(const apr_array_head
       svn_hash_sets(action_hash, info->dst_path, info);
       if (is_move && (! info->resurrection))
         svn_hash_sets(action_hash, info->src_path, info);
+
+      if (pin_externals)
+        {
+          apr_hash_t *pinned_externals;
+
+          SVN_ERR(resolve_pinned_externals(&pinned_externals, pair,
+                                           ra_session, repos_root,
+                                           ctx, pool, pool));
+          if (pin_externals_only_infos == NULL)
+            {
+              pin_externals_only_infos =
+                apr_array_make(pool, 0, sizeof(path_driver_info_t *));
+            }
+          SVN_ERR(queue_externals_change_path_infos(pin_externals_only_infos,
+                                                    path_infos,
+                                                    pinned_externals,
+                                                    info, pool, pool));
+        }
     }
 
   if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
@@ -1153,6 +1514,16 @@ repos_to_repos_copy(const apr_array_head
         APR_ARRAY_PUSH(paths, const char *) = info->src_path;
     }
 
+  /* Add any items which only need their externals pinned. */
+  for (i = 0; i < pin_externals_only_infos->nelts; i++)
+    {
+      path_driver_info_t *info;
+      
+      info = APR_ARRAY_IDX(pin_externals_only_infos, i, path_driver_info_t *);
+      APR_ARRAY_PUSH(paths, const char *) = info->dst_path;
+      svn_hash_sets(action_hash, info->dst_path, info);
+    }
+
   SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
                                            message, ctx, pool));
 
@@ -1231,6 +1602,61 @@ check_url_kind(void *baton,
   return SVN_NO_ERROR;
 }
 
+/* Queue a property change to LOCAL_ABSPATH in the COMMIT_ITEMS list.
+ * If the list does not already have a commit item for LOCAL_ABSPATH
+ * add a new commit item for the property change.
+ * Allocate results in RESULT_POOL.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+queue_prop_change_commit_items(const char *commit_url,
+                               apr_array_header_t *commit_items,
+                               const char *propname,
+                               svn_string_t *propval,
+                               apr_pool_t *result_pool,
+                               apr_pool_t *scratch_pool)
+{
+  svn_client_commit_item3_t *item = NULL;
+  svn_prop_t *prop;
+  int i;
+
+  for (i = 0; i < commit_items->nelts; i++)
+    {
+      svn_client_commit_item3_t *existing_item;
+
+      existing_item = APR_ARRAY_IDX(commit_items, i,
+                                    svn_client_commit_item3_t *);
+      if (strcmp(existing_item->url, commit_url) == 0)
+        {
+          SVN_DBG(("using existing item for %s", commit_url));
+          item = existing_item;
+          break;
+        }
+    }
+
+  if (item == NULL)
+    {
+      SVN_DBG(("using new item for %s", commit_url));
+      item = svn_client_commit_item3_create(result_pool);
+      item->url = commit_url;
+      item->kind = svn_node_dir;
+      item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
+    }
+  else
+    item->state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
+
+  if (item->outgoing_prop_changes == NULL)
+    item->outgoing_prop_changes = apr_array_make(result_pool, 1,
+                                                 sizeof(svn_prop_t *));
+
+  SVN_DBG(("item->url=%s item->state_flags=0x%x", item->url, item->state_flags));
+  prop = apr_palloc(result_pool, sizeof(*prop));
+  prop->name = propname;
+  prop->value = propval;
+  APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *) = prop;
+
+  return SVN_NO_ERROR;
+}
+
 /* ### Copy ...
  * COMMIT_INFO_P is ...
  * COPY_PAIRS is ... such that each 'src_abspath_or_url' is a local abspath
@@ -1244,6 +1670,7 @@ wc_to_repos_copy(const apr_array_header_
                  const apr_hash_t *revprop_table,
                  svn_commit_callback2_t commit_callback,
                  void *commit_baton,
+                 svn_boolean_t pin_externals,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *scratch_pool)
 {
@@ -1481,6 +1908,38 @@ wc_to_repos_copy(const apr_array_header_
           APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *)
             = mergeinfo_prop;
         }
+
+      if (pin_externals)
+        {
+          apr_hash_t *pinned_externals;
+          apr_hash_index_t *hi;
+
+          SVN_ERR(resolve_pinned_externals(&pinned_externals, pair,
+                                           ra_session, cukb.repos_root_url,
+                                           ctx, scratch_pool, iterpool));
+          for (hi = apr_hash_first(scratch_pool, pinned_externals);
+               hi;
+               hi = apr_hash_next(hi))
+            {
+              const char *dst_relpath = apr_hash_this_key(hi);
+              svn_string_t *externals_propval = apr_hash_this_val(hi);
+              const char *dst_url;
+              const char *commit_url;
+
+              if (svn_path_is_url(pair->dst_abspath_or_url))
+                dst_url = pair->dst_abspath_or_url;
+              else
+                SVN_ERR(svn_wc__node_get_url(&dst_url, ctx->wc_ctx,
+                                             pair->dst_abspath_or_url,
+                                             scratch_pool, iterpool));
+              commit_url = svn_path_url_add_component2(dst_url, dst_relpath,
+                                                       scratch_pool);
+              SVN_ERR(queue_prop_change_commit_items(commit_url, commit_items,
+                                                     SVN_PROP_EXTERNALS,
+                                                     externals_propval,
+                                                     scratch_pool, iterpool));
+            }
+        }
     }
 
   /* Sort and condense our COMMIT_ITEMS. */
@@ -1584,6 +2043,7 @@ repos_to_wc_copy_single(svn_boolean_t *t
                         svn_client__copy_pair_t *pair,
                         svn_boolean_t same_repositories,
                         svn_boolean_t ignore_externals,
+                        svn_boolean_t pin_externals,
                         svn_ra_session_t *ra_session,
                         svn_client_ctx_t *ctx,
                         apr_pool_t *pool)
@@ -1692,6 +2152,43 @@ repos_to_wc_copy_single(svn_boolean_t *t
 
           return SVN_NO_ERROR;
         }
+
+      if (pin_externals)
+        {
+          apr_hash_t *pinned_externals;
+          apr_hash_index_t *hi;
+          apr_pool_t *iterpool;
+          const char *repos_root_url;
+
+          SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
+          SVN_ERR(resolve_pinned_externals(&pinned_externals, pair,
+                                           ra_session, repos_root_url,
+                                           ctx, pool, pool));
+
+          iterpool = svn_pool_create(pool);
+          for (hi = apr_hash_first(pool, pinned_externals);
+               hi;
+               hi = apr_hash_next(hi))
+            {
+              const char *dst_relpath = apr_hash_this_key(hi);
+              svn_string_t *externals_propval = apr_hash_this_val(hi);
+              const char *local_abspath;
+
+              svn_pool_clear(iterpool);
+
+              local_abspath = svn_dirent_join(pair->dst_abspath_or_url,
+                                              dst_relpath, iterpool);
+              SVN_DBG(("New externals for %s: %s", local_abspath, externals_propval->data));
+              SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath,
+                                       SVN_PROP_EXTERNALS, externals_propval,
+                                       svn_depth_empty, TRUE /* skip_checks */,
+                                       NULL  /* changelist_filter */,
+                                       ctx->cancel_func, ctx->cancel_baton,
+                                       NULL, NULL, /* no extra notification */
+                                       iterpool));
+            }
+          svn_pool_destroy(iterpool);
+        }
     } /* end directory case */
 
   else if (pair->src_kind == svn_node_file)
@@ -1751,6 +2248,7 @@ repos_to_wc_copy_locked(svn_boolean_t *t
                         const apr_array_header_t *copy_pairs,
                         const char *top_dst_path,
                         svn_boolean_t ignore_externals,
+                        svn_boolean_t pin_externals,
                         svn_ra_session_t *ra_session,
                         svn_client_ctx_t *ctx,
                         apr_pool_t *scratch_pool)
@@ -1815,7 +2313,7 @@ repos_to_wc_copy_locked(svn_boolean_t *t
                                       APR_ARRAY_IDX(copy_pairs, i,
                                                     svn_client__copy_pair_t *),
                                       same_repositories,
-                                      ignore_externals,
+                                      ignore_externals, pin_externals,
                                       ra_session, ctx, iterpool));
     }
   svn_pool_destroy(iterpool);
@@ -1828,6 +2326,7 @@ repos_to_wc_copy(svn_boolean_t *timestam
                  const apr_array_header_t *copy_pairs,
                  svn_boolean_t make_parents,
                  svn_boolean_t ignore_externals,
+                 svn_boolean_t pin_externals,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool)
 {
@@ -1936,7 +2435,7 @@ 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,
-                            ra_session, ctx, pool),
+                            pin_externals, ra_session, ctx, pool),
     ctx->wc_ctx, lock_abspath, FALSE, pool);
   return SVN_NO_ERROR;
 }
@@ -1962,6 +2461,7 @@ try_copy(svn_boolean_t *timestamp_sleep,
          svn_boolean_t metadata_only,
          svn_boolean_t make_parents,
          svn_boolean_t ignore_externals,
+         svn_boolean_t pin_externals,
          const apr_hash_t *revprop_table,
          svn_commit_callback2_t commit_callback,
          void *commit_baton,
@@ -2262,28 +2762,31 @@ try_copy(svn_boolean_t *timestamp_sleep,
           /* We ignore these values, so assert the default value */
           SVN_ERR_ASSERT(allow_mixed_revisions && !metadata_only);
           return svn_error_trace(do_wc_to_wc_copies(timestamp_sleep,
-                                                    copy_pairs, ctx, pool));
+                                                    copy_pairs,
+                                                    pin_externals,
+                                                    ctx, pool));
         }
     }
   else if ((! srcs_are_urls) && (dst_is_url))
     {
       return svn_error_trace(
         wc_to_repos_copy(copy_pairs, make_parents, revprop_table,
-                         commit_callback, commit_baton, ctx, pool));
+                         commit_callback, commit_baton,
+                         pin_externals, 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,
-                         ctx, pool));
+                         pin_externals, ctx, pool));
     }
   else
     {
       return svn_error_trace(
         repos_to_repos_copy(copy_pairs, make_parents, revprop_table,
                             commit_callback, commit_baton, ctx, is_move,
-                            pool));
+                            pin_externals, pool));
     }
 }
 
@@ -2291,11 +2794,12 @@ try_copy(svn_boolean_t *timestamp_sleep,
 
 /* Public Interfaces */
 svn_error_t *
-svn_client_copy6(const apr_array_header_t *sources,
+svn_client_copy7(const apr_array_header_t *sources,
                  const char *dst_path,
                  svn_boolean_t copy_as_child,
                  svn_boolean_t make_parents,
                  svn_boolean_t ignore_externals,
+                 svn_boolean_t pin_externals,
                  const apr_hash_t *revprop_table,
                  svn_commit_callback2_t commit_callback,
                  void *commit_baton,
@@ -2317,6 +2821,7 @@ svn_client_copy6(const apr_array_header_
                  FALSE /* metadata_only */,
                  make_parents,
                  ignore_externals,
+                 pin_externals,
                  revprop_table,
                  commit_callback, commit_baton,
                  ctx,
@@ -2351,6 +2856,7 @@ svn_client_copy6(const apr_array_header_
                      FALSE /* metadata_only */,
                      make_parents,
                      ignore_externals,
+                     pin_externals,
                      revprop_table,
                      commit_callback, commit_baton,
                      ctx,
@@ -2412,6 +2918,7 @@ svn_client_move7(const apr_array_header_
                  metadata_only,
                  make_parents,
                  FALSE /* ignore_externals */,
+                 FALSE /* pin_externals */,
                  revprop_table,
                  commit_callback, commit_baton,
                  ctx,
@@ -2445,6 +2952,7 @@ svn_client_move7(const apr_array_header_
                      metadata_only,
                      make_parents,
                      FALSE /* ignore_externals */,
+                     FALSE /* pin_externals */,
                      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=1643757&r1=1643754&r2=1643757&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_client/deprecated.c (original)
+++ subversion/branches/pin-externals/subversion/libsvn_client/deprecated.c Mon Dec  8 07:54:02 2014
@@ -627,6 +627,25 @@ svn_client_commit(svn_client_commit_info
 
 /*** From copy.c ***/
 svn_error_t *
+svn_client_copy6(const apr_array_header_t *sources,
+                 const char *dst_path,
+                 svn_boolean_t copy_as_child,
+                 svn_boolean_t make_parents,
+                 svn_boolean_t ignore_externals,
+                 const apr_hash_t *revprop_table,
+                 svn_commit_callback2_t commit_callback,
+                 void *commit_baton,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *pool)
+{
+  return svn_error_trace(svn_client_copy7(sources, dst_path, copy_as_child,
+                                          make_parents, ignore_externals,
+                                          FALSE, revprop_table,
+                                          commit_callback, commit_baton,
+                                          ctx, pool));
+}
+
+svn_error_t *
 svn_client_copy5(svn_commit_info_t **commit_info_p,
                  const apr_array_header_t *sources,
                  const char *dst_path,

Modified: subversion/branches/pin-externals/subversion/libsvn_client/prop_commands.c
URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/libsvn_client/prop_commands.c?rev=1643757&r1=1643754&r2=1643757&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_client/prop_commands.c (original)
+++ subversion/branches/pin-externals/subversion/libsvn_client/prop_commands.c Mon Dec  8 07:54:02 2014
@@ -526,39 +526,18 @@ svn_client_revprop_set2(const char *prop
   return SVN_NO_ERROR;
 }
 
-/* Helper for the remote case of svn_client_propget.
- *
- * If PROPS is not null, then get the value of property PROPNAME in REVNUM,
-   using RA_LIB and SESSION.  Store the value ('svn_string_t *') in PROPS,
-   under the path key "TARGET_PREFIX/TARGET_RELATIVE" ('const char *').
- *
- * If INHERITED_PROPS is not null, then set *INHERITED_PROPS to a
- * depth-first ordered array of svn_prop_inherited_item_t * structures
- * representing the PROPNAME properties inherited by the target.  If
- * INHERITABLE_PROPS in not null and no inheritable properties are found,
- * then set *INHERITED_PROPS to an empty array.
- *
- * Recurse according to DEPTH, similarly to svn_client_propget3().
- *
- * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
- * Yes, caller passes this; it makes the recursion more efficient :-).
- *
- * Allocate PROPS and *INHERITED_PROPS in RESULT_POOL, but do all temporary
- * work in SCRATCH_POOL.  The two pools can be the same; recursive
- * calls may use a different SCRATCH_POOL, however.
- */
-static svn_error_t *
-remote_propget(apr_hash_t *props,
-               apr_array_header_t **inherited_props,
-               const char *propname,
-               const char *target_prefix,
-               const char *target_relative,
-               svn_node_kind_t kind,
-               svn_revnum_t revnum,
-               svn_ra_session_t *ra_session,
-               svn_depth_t depth,
-               apr_pool_t *result_pool,
-               apr_pool_t *scratch_pool)
+svn_error_t *
+svn_client__remote_propget(apr_hash_t *props,
+                           apr_array_header_t **inherited_props,
+                           const char *propname,
+                           const char *target_prefix,
+                           const char *target_relative,
+                           svn_node_kind_t kind,
+                           svn_revnum_t revnum,
+                           svn_ra_session_t *ra_session,
+                           svn_depth_t depth,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
 {
   apr_hash_t *dirents;
   apr_hash_t *prop_hash = NULL;
@@ -670,15 +649,15 @@ remote_propget(apr_hash_t *props,
           new_target_relative = svn_relpath_join(target_relative, this_name,
                                                  iterpool);
 
-          SVN_ERR(remote_propget(props, NULL,
-                                 propname,
-                                 target_prefix,
-                                 new_target_relative,
-                                 this_ent->kind,
-                                 revnum,
-                                 ra_session,
-                                 depth_below_here,
-                                 result_pool, iterpool));
+          SVN_ERR(svn_client__remote_propget(props, NULL,
+                                             propname,
+                                             target_prefix,
+                                             new_target_relative,
+                                             this_ent->kind,
+                                             revnum,
+                                             ra_session,
+                                             depth_below_here,
+                                             result_pool, iterpool));
         }
 
       svn_pool_destroy(iterpool);
@@ -968,7 +947,8 @@ svn_client_propget5(apr_hash_t **props,
           if (!local_explicit_props)
             *props = apr_hash_make(result_pool);
 
-          SVN_ERR(remote_propget(!local_explicit_props ? *props : NULL,
+          SVN_ERR(svn_client__remote_propget(
+                                 !local_explicit_props ? *props : NULL,
                                  !local_iprops ? inherited_props : NULL,
                                  propname, loc->url, "",
                                  kind, loc->rev, ra_session,

Modified: subversion/branches/pin-externals/subversion/svn/cl.h
URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svn/cl.h?rev=1643757&r1=1643754&r2=1643757&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/svn/cl.h (original)
+++ subversion/branches/pin-externals/subversion/svn/cl.h Mon Dec  8 07:54:02 2014
@@ -248,6 +248,7 @@ typedef struct svn_cl__opt_state_t
   svn_boolean_t remove_ignored;    /* remove ignored items */
   svn_boolean_t no_newline;        /* do not output the trailing newline */
   svn_boolean_t show_passwords;    /* show cached passwords */
+  svn_boolean_t pin_externals;     /* pin externals to last-changed revisions */
 } svn_cl__opt_state_t;
 
 

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=1643757&r1=1643754&r2=1643757&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/svn/copy-cmd.c (original)
+++ subversion/branches/pin-externals/subversion/svn/copy-cmd.c Mon Dec  8 07:54:02 2014
@@ -167,8 +167,9 @@ svn_cl__copy(apr_getopt_t *os,
     SVN_ERR(svn_cl__make_log_msg_baton(&(ctx->log_msg_baton3), opt_state,
                                        NULL, ctx->config, pool));
 
-  err = svn_client_copy6(sources, dst_path, TRUE,
+  err = svn_client_copy7(sources, dst_path, TRUE,
                          opt_state->parents, opt_state->ignore_externals,
+                         opt_state->pin_externals,
                          opt_state->revprop_table,
                          (opt_state->quiet ? NULL : svn_cl__print_commit_info),
                          NULL,

Modified: subversion/branches/pin-externals/subversion/svn/svn.c
URL: http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/svn/svn.c?rev=1643757&r1=1643754&r2=1643757&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/svn/svn.c (original)
+++ subversion/branches/pin-externals/subversion/svn/svn.c Mon Dec  8 07:54:02 2014
@@ -144,7 +144,8 @@ typedef enum svn_cl__longopt_t {
   opt_remove_unversioned,
   opt_remove_ignored,
   opt_no_newline,
-  opt_show_passwords
+  opt_show_passwords,
+  opt_pin_externals,
 } svn_cl__longopt_t;
 
 
@@ -418,6 +419,10 @@ const apr_getopt_option_t svn_cl__option
   {"remove-ignored", opt_remove_ignored, 0, N_("remove ignored items")},
   {"no-newline", opt_no_newline, 0, N_("do not output the trailing newline")},
   {"show-passwords", opt_show_passwords, 0, N_("show cached passwords")},
+  {"pin-externals", opt_pin_externals, 0,
+                       N_("pin externals with no explicit revision to their\n"
+                          "                             "
+                          "last-changed revision (recommended when tagging)")},
 
   /* Long-opt Aliases
    *
@@ -608,7 +613,8 @@ const svn_opt_subcommand_desc2_t svn_cl_
      "  contact the repository.  As such, they may not, by default, be able\n"
      "  to propagate merge tracking information from the source of the copy\n"
      "  to the destination.\n"),
-    {'r', 'q', opt_ignore_externals, opt_parents, SVN_CL__LOG_MSG_OPTIONS} },
+    {'r', 'q', opt_ignore_externals, opt_parents, SVN_CL__LOG_MSG_OPTIONS,
+     opt_pin_externals} },
 
   { "delete", svn_cl__delete, {"del", "remove", "rm"}, N_
     ("Remove files and directories from version control.\n"
@@ -2384,6 +2390,9 @@ sub_main(int *exit_code, int argc, const
       case opt_show_passwords:
         opt_state.show_passwords = TRUE;
         break;
+      case opt_pin_externals:
+        opt_state.pin_externals = TRUE;
+        break;
       default:
         /* Hmmm. Perhaps this would be a good place to squirrel away
            opts that commands like svn diff might need. Hmmm indeed. */