177
177
return SVN_NO_ERROR;
180
/* Quote a string if it would be handled as multiple or different tokens
181
during externals parsing */
183
maybe_quote(const char *value,
184
apr_pool_t *result_pool)
189
status = apr_tokenize_to_argv(value, &argv, result_pool);
191
if (!status && argv[0] && !argv[1] && strcmp(argv[0], value) == 0)
192
return apr_pstrdup(result_pool, value);
195
svn_stringbuf_t *sb = svn_stringbuf_create_empty(result_pool);
198
svn_stringbuf_appendbyte(sb, '\"');
200
for (c = value; *c; c++)
202
if (*c == '\\' || *c == '\"' || *c == '\'')
203
svn_stringbuf_appendbyte(sb, '\\');
205
svn_stringbuf_appendbyte(sb, *c);
208
svn_stringbuf_appendbyte(sb, '\"');
211
status = apr_tokenize_to_argv(sb->data, &argv, result_pool);
213
SVN_ERR_ASSERT_NO_RETURN(!status && argv[0] && !argv[1]
214
&& !strcmp(argv[0], value));
221
/* In *NEW_EXTERNALS_DESCRIPTION, return a new external description for
222
* use as a line in an svn:externals property, based on the external item
223
* ITEM and the additional parser information in INFO. Pin the external
224
* to EXTERNAL_PEGREV. Use POOL for all allocations. */
226
make_external_description(const char **new_external_description,
227
const char *local_abspath_or_url,
228
svn_wc_external_item2_t *item,
229
svn_wc__externals_parser_info_t *info,
230
svn_opt_revision_t external_pegrev,
234
const char *peg_rev_str;
236
switch (info->format)
238
case svn_wc__external_description_format_1:
239
if (external_pegrev.kind == svn_opt_revision_unspecified)
241
/* If info->rev_str is NULL, this yields an empty string. */
242
rev_str = apr_pstrcat(pool, info->rev_str, " ", SVN_VA_NULL);
244
else if (info->rev_str && item->revision.kind != svn_opt_revision_head)
245
rev_str = apr_psprintf(pool, "%s ", info->rev_str);
248
/* ### can't handle svn_opt_revision_date without info->rev_str */
249
SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_number);
250
rev_str = apr_psprintf(pool, "-r%ld ",
251
external_pegrev.value.number);
254
*new_external_description =
255
apr_psprintf(pool, "%s %s%s\n", maybe_quote(item->target_dir, pool),
257
maybe_quote(item->url, pool));
260
case svn_wc__external_description_format_2:
261
if (external_pegrev.kind == svn_opt_revision_unspecified)
263
/* If info->rev_str is NULL, this yields an empty string. */
264
rev_str = apr_pstrcat(pool, info->rev_str, " ", SVN_VA_NULL);
266
else if (info->rev_str && item->revision.kind != svn_opt_revision_head)
267
rev_str = apr_psprintf(pool, "%s ", info->rev_str);
271
if (external_pegrev.kind == svn_opt_revision_unspecified)
272
peg_rev_str = info->peg_rev_str ? info->peg_rev_str : "";
273
else if (info->peg_rev_str &&
274
item->peg_revision.kind != svn_opt_revision_head)
275
peg_rev_str = info->peg_rev_str;
278
/* ### can't handle svn_opt_revision_date without info->rev_str */
279
SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_number);
280
peg_rev_str = apr_psprintf(pool, "@%ld",
281
external_pegrev.value.number);
284
*new_external_description =
285
apr_psprintf(pool, "%s%s %s\n", rev_str,
286
maybe_quote(apr_psprintf(pool, "%s%s", item->url,
289
maybe_quote(item->target_dir, pool));
293
return svn_error_createf(
294
SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
295
_("%s property defined at '%s' is using an unsupported "
296
"syntax"), SVN_PROP_EXTERNALS,
297
svn_dirent_local_style(local_abspath_or_url, pool));
303
/* Pin all externals listed in EXTERNALS_PROP_VAL to their
304
* last-changed revision. Set *PINNED_EXTERNALS to a new property
305
* value allocated in RESULT_POOL, or to NULL if none of the externals
306
* in EXTERNALS_PROP_VAL were changed. LOCAL_ABSPATH_OR_URL is the
307
* path or URL defining the svn:externals property. Use SCRATCH_POOL
308
* for temporary allocations.
311
pin_externals_prop(svn_string_t **pinned_externals,
312
svn_string_t *externals_prop_val,
313
const apr_hash_t *externals_to_pin,
314
const char *repos_root_url,
315
const char *local_abspath_or_url,
316
svn_client_ctx_t *ctx,
317
apr_pool_t *result_pool,
318
apr_pool_t *scratch_pool)
320
svn_stringbuf_t *buf;
321
apr_array_header_t *external_items;
322
apr_array_header_t *parser_infos;
323
apr_array_header_t *items_to_pin;
326
apr_pool_t *iterpool;
328
SVN_ERR(svn_wc__parse_externals_description(&external_items,
330
local_abspath_or_url,
331
externals_prop_val->data,
332
FALSE /* canonicalize_url */,
335
if (externals_to_pin)
337
items_to_pin = svn_hash_gets((apr_hash_t *)externals_to_pin,
338
local_abspath_or_url);
341
/* No pinning at all for this path. */
342
*pinned_externals = NULL;
349
buf = svn_stringbuf_create_empty(scratch_pool);
350
iterpool = svn_pool_create(scratch_pool);
352
for (i = 0; i < external_items->nelts; i++)
354
svn_wc_external_item2_t *item;
355
svn_wc__externals_parser_info_t *info;
356
svn_opt_revision_t external_pegrev;
357
const char *pinned_desc;
359
svn_pool_clear(iterpool);
361
item = APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
362
info = APR_ARRAY_IDX(parser_infos, i, svn_wc__externals_parser_info_t *);
367
svn_wc_external_item2_t *item_to_pin = NULL;
369
for (j = 0; j < items_to_pin->nelts; j++)
371
svn_wc_external_item2_t *const current =
372
APR_ARRAY_IDX(items_to_pin, j, svn_wc_external_item2_t *);
376
&& 0 == strcmp(item->url, current->url)
377
&& 0 == strcmp(item->target_dir, current->target_dir))
379
item_to_pin = current;
384
/* If this item is not in our list of external items to pin then
385
* simply keep the external at its original value. */
390
external_pegrev.kind = svn_opt_revision_unspecified;
391
SVN_ERR(make_external_description(&desc, local_abspath_or_url,
392
item, info, external_pegrev,
394
svn_stringbuf_appendcstr(buf, desc);
399
if (item->peg_revision.kind == svn_opt_revision_date)
401
/* Already pinned ... copy the peg date. */
402
external_pegrev.kind = svn_opt_revision_date;
403
external_pegrev.value.date = item->peg_revision.value.date;
405
else if (item->peg_revision.kind == svn_opt_revision_number)
407
/* Already pinned ... copy the peg revision number. */
408
external_pegrev.kind = svn_opt_revision_number;
409
external_pegrev.value.number = item->peg_revision.value.number;
414
item->peg_revision.kind == svn_opt_revision_head ||
415
item->peg_revision.kind == svn_opt_revision_unspecified);
417
/* We're actually going to change the peg revision. */
420
if (svn_path_is_url(local_abspath_or_url))
422
const char *resolved_url;
423
svn_ra_session_t *external_ra_session;
424
svn_revnum_t latest_revnum;
426
SVN_ERR(svn_wc__resolve_relative_external_url(
427
&resolved_url, item, repos_root_url,
428
local_abspath_or_url, iterpool, iterpool));
429
SVN_ERR(svn_client__open_ra_session_internal(&external_ra_session,
435
SVN_ERR(svn_ra_get_latest_revnum(external_ra_session,
439
external_pegrev.kind = svn_opt_revision_number;
440
external_pegrev.value.number = latest_revnum;
444
const char *external_abspath;
445
svn_node_kind_t external_kind;
446
svn_revnum_t external_checked_out_rev;
448
external_abspath = svn_dirent_join(local_abspath_or_url,
451
SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL,
452
NULL, NULL, ctx->wc_ctx,
453
local_abspath_or_url,
454
external_abspath, TRUE,
457
if (external_kind == svn_node_none)
458
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
460
_("Cannot pin external '%s' defined "
461
"in %s at '%s' because it is not "
462
"checked out in the working copy "
464
item->url, SVN_PROP_EXTERNALS,
465
svn_dirent_local_style(
466
local_abspath_or_url, iterpool),
467
svn_dirent_local_style(
468
external_abspath, iterpool));
469
else if (external_kind == svn_node_dir)
471
svn_boolean_t is_switched;
472
svn_boolean_t is_modified;
473
svn_revnum_t min_rev;
474
svn_revnum_t max_rev;
476
/* Perform some sanity checks on the checked-out external. */
478
SVN_ERR(svn_wc__has_switched_subtrees(&is_switched,
480
external_abspath, NULL,
483
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
485
_("Cannot pin external '%s' defined "
486
"in %s at '%s' because '%s' has "
487
"switched subtrees (switches "
488
"cannot be represented in %s)"),
489
item->url, SVN_PROP_EXTERNALS,
490
svn_dirent_local_style(
491
local_abspath_or_url, iterpool),
492
svn_dirent_local_style(
493
external_abspath, iterpool),
496
SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
497
external_abspath, TRUE,
502
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
504
_("Cannot pin external '%s' defined "
505
"in %s at '%s' because '%s' has "
506
"local modifications (local "
507
"modifications cannot be "
508
"represented in %s)"),
509
item->url, SVN_PROP_EXTERNALS,
510
svn_dirent_local_style(
511
local_abspath_or_url, iterpool),
512
svn_dirent_local_style(
513
external_abspath, iterpool),
516
SVN_ERR(svn_wc__min_max_revisions(&min_rev, &max_rev, ctx->wc_ctx,
517
external_abspath, FALSE,
519
if (min_rev != max_rev)
520
return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
522
_("Cannot pin external '%s' defined "
523
"in %s at '%s' because '%s' is a "
524
"mixed-revision working copy "
525
"(mixed-revisions cannot be "
526
"represented in %s)"),
527
item->url, SVN_PROP_EXTERNALS,
528
svn_dirent_local_style(
529
local_abspath_or_url, iterpool),
530
svn_dirent_local_style(
531
external_abspath, iterpool),
533
external_checked_out_rev = min_rev;
537
SVN_ERR_ASSERT(external_kind == svn_node_file);
538
SVN_ERR(svn_wc__node_get_repos_info(&external_checked_out_rev,
540
ctx->wc_ctx, external_abspath,
541
iterpool, iterpool));
544
external_pegrev.kind = svn_opt_revision_number;
545
external_pegrev.value.number = external_checked_out_rev;
549
SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_date ||
550
external_pegrev.kind == svn_opt_revision_number);
552
SVN_ERR(make_external_description(&pinned_desc, local_abspath_or_url,
553
item, info, external_pegrev, iterpool));
555
svn_stringbuf_appendcstr(buf, pinned_desc);
557
svn_pool_destroy(iterpool);
559
if (pinned_items > 0)
560
*pinned_externals = svn_string_create_from_buf(buf, result_pool);
562
*pinned_externals = NULL;
567
/* Return, in *PINNED_EXTERNALS, a new hash mapping URLs or local abspaths
568
* to svn:externals property values (as const char *), where some or all
569
* external references have been pinned.
570
* If EXTERNALS_TO_PIN is NULL, pin all externals, else pin the externals
571
* mentioned in EXTERNALS_TO_PIN.
572
* The pinning operation takes place as part of the copy operation for
573
* the source/destination pair PAIR. Use RA_SESSION and REPOS_ROOT_URL
574
* to contact the repository containing the externals definition, if neccesary.
575
* Use CX to fopen additional RA sessions to external repositories, if
576
* neccessary. Allocate *NEW_EXTERNALS in RESULT_POOL.
577
* Use SCRATCH_POOL for temporary allocations. */
579
resolve_pinned_externals(apr_hash_t **pinned_externals,
580
const apr_hash_t *externals_to_pin,
581
svn_client__copy_pair_t *pair,
582
svn_ra_session_t *ra_session,
583
const char *repos_root_url,
584
svn_client_ctx_t *ctx,
585
apr_pool_t *result_pool,
586
apr_pool_t *scratch_pool)
588
const char *old_url = NULL;
589
apr_hash_t *externals_props;
590
apr_hash_index_t *hi;
591
apr_pool_t *iterpool;
593
*pinned_externals = apr_hash_make(result_pool);
595
if (svn_path_is_url(pair->src_abspath_or_url))
597
SVN_ERR(svn_client__ensure_ra_session_url(&old_url, ra_session,
598
pair->src_abspath_or_url,
600
externals_props = apr_hash_make(scratch_pool);
601
SVN_ERR(svn_client__remote_propget(externals_props, NULL,
603
pair->src_abspath_or_url, "",
613
SVN_ERR(svn_wc__externals_gather_definitions(&externals_props, NULL,
615
pair->src_abspath_or_url,
617
scratch_pool, scratch_pool));
619
/* ### gather_definitions returns propvals as const char * */
620
for (hi = apr_hash_first(scratch_pool, externals_props);
622
hi = apr_hash_next(hi))
624
const char *local_abspath_or_url = apr_hash_this_key(hi);
625
const char *propval = apr_hash_this_val(hi);
626
svn_string_t *new_propval = svn_string_create(propval, scratch_pool);
628
svn_hash_sets(externals_props, local_abspath_or_url, new_propval);
632
if (apr_hash_count(externals_props) == 0)
635
SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool));
639
iterpool = svn_pool_create(scratch_pool);
640
for (hi = apr_hash_first(scratch_pool, externals_props);
642
hi = apr_hash_next(hi))
644
const char *local_abspath_or_url = apr_hash_this_key(hi);
645
svn_string_t *externals_propval = apr_hash_this_val(hi);
647
svn_string_t *new_propval;
649
svn_pool_clear(iterpool);
651
SVN_ERR(pin_externals_prop(&new_propval, externals_propval,
653
repos_root_url, local_abspath_or_url, ctx,
654
result_pool, iterpool));
657
if (svn_path_is_url(pair->src_abspath_or_url))
658
relpath = svn_uri_skip_ancestor(pair->src_abspath_or_url,
659
local_abspath_or_url,
662
relpath = svn_dirent_skip_ancestor(pair->src_abspath_or_url,
663
local_abspath_or_url);
664
SVN_ERR_ASSERT(relpath);
666
svn_hash_sets(*pinned_externals, relpath, new_propval);
669
svn_pool_destroy(iterpool);
672
SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool));
181
679
/* The guts of do_wc_to_wc_copies */
182
680
static svn_error_t *
183
681
do_wc_to_wc_copies_with_write_lock(svn_boolean_t *timestamp_sleep,
184
682
const apr_array_header_t *copy_pairs,
185
683
const char *dst_parent,
684
svn_boolean_t metadata_only,
685
svn_boolean_t pin_externals,
686
const apr_hash_t *externals_to_pin,
186
687
svn_client_ctx_t *ctx,
187
688
apr_pool_t *scratch_pool)