~ubuntu-branches/debian/sid/subversion/sid

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/copy.c

  • Committer: Package Import Robot
  • Author(s): James McCoy
  • Date: 2015-08-07 21:32:47 UTC
  • mfrom: (0.2.15) (4.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20150807213247-ozyewtmgsr6tkewl
Tags: 1.9.0-1
* Upload to unstable
* New upstream release.
  + Security fixes
    - CVE-2015-3184: Mixed anonymous/authenticated path-based authz with
      httpd 2.4
    - CVE-2015-3187: svn_repos_trace_node_locations() reveals paths hidden
      by authz
* Add >= 2.7 requirement for python-all-dev Build-Depends, needed to run
  tests.
* Remove Build-Conflicts against ruby-test-unit.  (Closes: #791844)
* Remove patches/apache_module_dependency in favor of expressing the
  dependencies in authz_svn.load/dav_svn.load.
* Build-Depend on apache2-dev (>= 2.4.16) to ensure ap_some_authn_required()
  is available when building mod_authz_svn and Depend on apache2-bin (>=
  2.4.16) for runtime support.

Show diffs side-by-side

added added

removed removed

Lines of Context:
177
177
  return SVN_NO_ERROR;
178
178
}
179
179
 
 
180
/* Quote a string if it would be handled as multiple or different tokens
 
181
   during externals parsing */
 
182
static const char *
 
183
maybe_quote(const char *value,
 
184
            apr_pool_t *result_pool)
 
185
{
 
186
  apr_status_t status;
 
187
  char **argv;
 
188
 
 
189
  status = apr_tokenize_to_argv(value, &argv, result_pool);
 
190
 
 
191
  if (!status && argv[0] && !argv[1] && strcmp(argv[0], value) == 0)
 
192
    return apr_pstrdup(result_pool, value);
 
193
 
 
194
  {
 
195
    svn_stringbuf_t *sb = svn_stringbuf_create_empty(result_pool);
 
196
    const char *c;
 
197
 
 
198
    svn_stringbuf_appendbyte(sb, '\"');
 
199
 
 
200
    for (c = value; *c; c++)
 
201
      {
 
202
        if (*c == '\\' || *c == '\"' || *c == '\'')
 
203
          svn_stringbuf_appendbyte(sb, '\\');
 
204
 
 
205
        svn_stringbuf_appendbyte(sb, *c);
 
206
      }
 
207
 
 
208
    svn_stringbuf_appendbyte(sb, '\"');
 
209
 
 
210
#ifdef SVN_DEBUG
 
211
    status = apr_tokenize_to_argv(sb->data, &argv, result_pool);
 
212
 
 
213
    SVN_ERR_ASSERT_NO_RETURN(!status && argv[0] && !argv[1]
 
214
                             && !strcmp(argv[0], value));
 
215
#endif
 
216
 
 
217
    return sb->data;
 
218
  }
 
219
}
 
220
 
 
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. */
 
225
static svn_error_t *
 
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,
 
231
                          apr_pool_t *pool)
 
232
{
 
233
  const char *rev_str;
 
234
  const char *peg_rev_str;
 
235
 
 
236
  switch (info->format)
 
237
    {
 
238
      case svn_wc__external_description_format_1:
 
239
        if (external_pegrev.kind == svn_opt_revision_unspecified)
 
240
          {
 
241
            /* If info->rev_str is NULL, this yields an empty string. */
 
242
            rev_str = apr_pstrcat(pool, info->rev_str, " ", SVN_VA_NULL);
 
243
          }
 
244
        else if (info->rev_str && item->revision.kind != svn_opt_revision_head)
 
245
          rev_str = apr_psprintf(pool, "%s ", info->rev_str);
 
246
        else
 
247
          {
 
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);
 
252
          }
 
253
 
 
254
        *new_external_description =
 
255
          apr_psprintf(pool, "%s %s%s\n", maybe_quote(item->target_dir, pool),
 
256
                                          rev_str,
 
257
                                          maybe_quote(item->url, pool));
 
258
        break;
 
259
 
 
260
      case svn_wc__external_description_format_2:
 
261
        if (external_pegrev.kind == svn_opt_revision_unspecified)
 
262
          {
 
263
            /* If info->rev_str is NULL, this yields an empty string. */
 
264
            rev_str = apr_pstrcat(pool, info->rev_str, " ", SVN_VA_NULL);
 
265
          }
 
266
        else if (info->rev_str && item->revision.kind != svn_opt_revision_head)
 
267
          rev_str = apr_psprintf(pool, "%s ", info->rev_str);
 
268
        else
 
269
          rev_str = "";
 
270
 
 
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;
 
276
        else
 
277
          {
 
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);
 
282
          }
 
283
 
 
284
        *new_external_description =
 
285
          apr_psprintf(pool, "%s%s %s\n", rev_str,
 
286
                       maybe_quote(apr_psprintf(pool, "%s%s", item->url,
 
287
                                                peg_rev_str),
 
288
                                   pool),
 
289
                       maybe_quote(item->target_dir, pool));
 
290
        break;
 
291
 
 
292
      default:
 
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));
 
298
    }
 
299
 
 
300
  return SVN_NO_ERROR;
 
301
}
 
302
 
 
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.
 
309
 */
 
310
static svn_error_t *
 
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)
 
319
{
 
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;
 
324
  int pinned_items;
 
325
  int i;
 
326
  apr_pool_t *iterpool;
 
327
 
 
328
  SVN_ERR(svn_wc__parse_externals_description(&external_items,
 
329
                                              &parser_infos,
 
330
                                              local_abspath_or_url,
 
331
                                              externals_prop_val->data,
 
332
                                              FALSE /* canonicalize_url */,
 
333
                                              scratch_pool));
 
334
 
 
335
  if (externals_to_pin)
 
336
    {
 
337
      items_to_pin = svn_hash_gets((apr_hash_t *)externals_to_pin,
 
338
                                   local_abspath_or_url);
 
339
      if (!items_to_pin)
 
340
        {
 
341
          /* No pinning at all for this path. */
 
342
          *pinned_externals = NULL;
 
343
          return SVN_NO_ERROR;
 
344
        }
 
345
    }
 
346
  else
 
347
    items_to_pin = NULL;
 
348
 
 
349
  buf = svn_stringbuf_create_empty(scratch_pool);
 
350
  iterpool = svn_pool_create(scratch_pool);
 
351
  pinned_items = 0;
 
352
  for (i = 0; i < external_items->nelts; i++)
 
353
    {
 
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;
 
358
 
 
359
      svn_pool_clear(iterpool);
 
360
 
 
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 *);
 
363
 
 
364
      if (items_to_pin)
 
365
        {
 
366
          int j;
 
367
          svn_wc_external_item2_t *item_to_pin = NULL;
 
368
 
 
369
          for (j = 0; j < items_to_pin->nelts; j++)
 
370
            {
 
371
              svn_wc_external_item2_t *const current =
 
372
                APR_ARRAY_IDX(items_to_pin, j, svn_wc_external_item2_t *);
 
373
 
 
374
 
 
375
              if (current
 
376
                  && 0 == strcmp(item->url, current->url)
 
377
                  && 0 == strcmp(item->target_dir, current->target_dir))
 
378
                {
 
379
                  item_to_pin = current;
 
380
                  break;
 
381
                }
 
382
            }
 
383
 
 
384
          /* If this item is not in our list of external items to pin then
 
385
           * simply keep the external at its original value. */
 
386
          if (!item_to_pin)
 
387
            {
 
388
              const char *desc;
 
389
 
 
390
              external_pegrev.kind = svn_opt_revision_unspecified;
 
391
              SVN_ERR(make_external_description(&desc, local_abspath_or_url,
 
392
                                                item, info, external_pegrev,
 
393
                                                iterpool));
 
394
              svn_stringbuf_appendcstr(buf, desc);
 
395
              continue;
 
396
            }
 
397
        }
 
398
 
 
399
      if (item->peg_revision.kind == svn_opt_revision_date)
 
400
        {
 
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;
 
404
        }
 
405
      else if (item->peg_revision.kind == svn_opt_revision_number)
 
406
        {
 
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;
 
410
        }
 
411
      else
 
412
        {
 
413
          SVN_ERR_ASSERT(
 
414
            item->peg_revision.kind == svn_opt_revision_head ||
 
415
            item->peg_revision.kind == svn_opt_revision_unspecified);
 
416
 
 
417
          /* We're actually going to change the peg revision. */
 
418
          ++pinned_items;
 
419
 
 
420
          if (svn_path_is_url(local_abspath_or_url))
 
421
            {
 
422
              const char *resolved_url;
 
423
              svn_ra_session_t *external_ra_session;
 
424
              svn_revnum_t latest_revnum;
 
425
 
 
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,
 
430
                                                           NULL, resolved_url,
 
431
                                                           NULL, NULL, FALSE,
 
432
                                                           FALSE, ctx,
 
433
                                                           iterpool,
 
434
                                                           iterpool));
 
435
              SVN_ERR(svn_ra_get_latest_revnum(external_ra_session,
 
436
                                               &latest_revnum,
 
437
                                               iterpool));
 
438
 
 
439
              external_pegrev.kind = svn_opt_revision_number;
 
440
              external_pegrev.value.number = latest_revnum;
 
441
            }
 
442
          else
 
443
            {
 
444
              const char *external_abspath;
 
445
              svn_node_kind_t external_kind;
 
446
              svn_revnum_t external_checked_out_rev;
 
447
 
 
448
              external_abspath = svn_dirent_join(local_abspath_or_url,
 
449
                                                 item->target_dir,
 
450
                                                 iterpool);
 
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,
 
455
                                                 iterpool,
 
456
                                                 iterpool));
 
457
              if (external_kind == svn_node_none)
 
458
                return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
 
459
                                         NULL,
 
460
                                         _("Cannot pin external '%s' defined "
 
461
                                           "in %s at '%s' because it is not "
 
462
                                           "checked out in the working copy "
 
463
                                           "at '%s'"),
 
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)
 
470
                {
 
471
                  svn_boolean_t is_switched;
 
472
                  svn_boolean_t is_modified;
 
473
                  svn_revnum_t min_rev;
 
474
                  svn_revnum_t max_rev;
 
475
 
 
476
                  /* Perform some sanity checks on the checked-out external. */
 
477
 
 
478
                  SVN_ERR(svn_wc__has_switched_subtrees(&is_switched,
 
479
                                                        ctx->wc_ctx,
 
480
                                                        external_abspath, NULL,
 
481
                                                        iterpool));
 
482
                  if (is_switched)
 
483
                    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
 
484
                                             NULL,
 
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),
 
494
                                             SVN_PROP_EXTERNALS);
 
495
 
 
496
                  SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
 
497
                                                 external_abspath, TRUE,
 
498
                                                 ctx->cancel_func,
 
499
                                                 ctx->cancel_baton,
 
500
                                                 iterpool));
 
501
                  if (is_modified)
 
502
                    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
 
503
                                             NULL,
 
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),
 
514
                                             SVN_PROP_EXTERNALS);
 
515
 
 
516
                  SVN_ERR(svn_wc__min_max_revisions(&min_rev, &max_rev, ctx->wc_ctx,
 
517
                                                    external_abspath, FALSE,
 
518
                                                    iterpool));
 
519
                  if (min_rev != max_rev)
 
520
                    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
 
521
                                             NULL,
 
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),
 
532
                                             SVN_PROP_EXTERNALS);
 
533
                  external_checked_out_rev = min_rev;
 
534
                }
 
535
              else
 
536
                {
 
537
                  SVN_ERR_ASSERT(external_kind == svn_node_file);
 
538
                  SVN_ERR(svn_wc__node_get_repos_info(&external_checked_out_rev,
 
539
                                                      NULL, NULL, NULL,
 
540
                                                      ctx->wc_ctx, external_abspath,
 
541
                                                      iterpool, iterpool));
 
542
                }
 
543
 
 
544
              external_pegrev.kind = svn_opt_revision_number;
 
545
              external_pegrev.value.number = external_checked_out_rev;
 
546
            }
 
547
        }
 
548
 
 
549
      SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_date ||
 
550
                     external_pegrev.kind == svn_opt_revision_number);
 
551
 
 
552
      SVN_ERR(make_external_description(&pinned_desc, local_abspath_or_url,
 
553
                                        item, info, external_pegrev, iterpool));
 
554
 
 
555
      svn_stringbuf_appendcstr(buf, pinned_desc);
 
556
    }
 
557
  svn_pool_destroy(iterpool);
 
558
 
 
559
  if (pinned_items > 0)
 
560
    *pinned_externals = svn_string_create_from_buf(buf, result_pool);
 
561
  else
 
562
    *pinned_externals = NULL;
 
563
 
 
564
  return SVN_NO_ERROR;
 
565
}
 
566
 
 
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. */
 
578
static svn_error_t *
 
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)
 
587
{
 
588
  const char *old_url = NULL;
 
589
  apr_hash_t *externals_props;
 
590
  apr_hash_index_t *hi;
 
591
  apr_pool_t *iterpool;
 
592
 
 
593
  *pinned_externals = apr_hash_make(result_pool);
 
594
 
 
595
  if (svn_path_is_url(pair->src_abspath_or_url))
 
596
    {
 
597
      SVN_ERR(svn_client__ensure_ra_session_url(&old_url, ra_session,
 
598
                                                pair->src_abspath_or_url,
 
599
                                                scratch_pool));
 
600
      externals_props = apr_hash_make(scratch_pool);
 
601
      SVN_ERR(svn_client__remote_propget(externals_props, NULL,
 
602
                                         SVN_PROP_EXTERNALS,
 
603
                                         pair->src_abspath_or_url, "",
 
604
                                         svn_node_dir,
 
605
                                         pair->src_revnum,
 
606
                                         ra_session,
 
607
                                         svn_depth_infinity,
 
608
                                         scratch_pool,
 
609
                                         scratch_pool));
 
610
    }
 
611
  else
 
612
    {
 
613
      SVN_ERR(svn_wc__externals_gather_definitions(&externals_props, NULL,
 
614
                                                   ctx->wc_ctx,
 
615
                                                   pair->src_abspath_or_url,
 
616
                                                   svn_depth_infinity,
 
617
                                                   scratch_pool, scratch_pool));
 
618
 
 
619
      /* ### gather_definitions returns propvals as const char * */
 
620
      for (hi = apr_hash_first(scratch_pool, externals_props);
 
621
           hi;
 
622
           hi = apr_hash_next(hi))
 
623
        {
 
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);
 
627
 
 
628
          svn_hash_sets(externals_props, local_abspath_or_url, new_propval);
 
629
        }
 
630
    }
 
631
 
 
632
  if (apr_hash_count(externals_props) == 0)
 
633
    {
 
634
      if (old_url)
 
635
        SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool));
 
636
      return SVN_NO_ERROR;
 
637
    }
 
638
 
 
639
  iterpool = svn_pool_create(scratch_pool);
 
640
  for (hi = apr_hash_first(scratch_pool, externals_props);
 
641
       hi;
 
642
       hi = apr_hash_next(hi))
 
643
    {
 
644
      const char *local_abspath_or_url = apr_hash_this_key(hi);
 
645
      svn_string_t *externals_propval = apr_hash_this_val(hi);
 
646
      const char *relpath;
 
647
      svn_string_t *new_propval;
 
648
 
 
649
      svn_pool_clear(iterpool);
 
650
 
 
651
      SVN_ERR(pin_externals_prop(&new_propval, externals_propval,
 
652
                                 externals_to_pin,
 
653
                                 repos_root_url, local_abspath_or_url, ctx,
 
654
                                 result_pool, iterpool));
 
655
      if (new_propval)
 
656
        {
 
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,
 
660
                                            result_pool);
 
661
          else
 
662
            relpath = svn_dirent_skip_ancestor(pair->src_abspath_or_url,
 
663
                                               local_abspath_or_url);
 
664
          SVN_ERR_ASSERT(relpath);
 
665
 
 
666
          svn_hash_sets(*pinned_externals, relpath, new_propval);
 
667
        }
 
668
    }
 
669
  svn_pool_destroy(iterpool);
 
670
 
 
671
  if (old_url)
 
672
    SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool));
 
673
 
 
674
  return SVN_NO_ERROR;
 
675
}
 
676
 
 
677
 
180
678
 
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)
188
689
{
195
696
      const char *dst_abspath;
196
697
      svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
197
698
                                                    svn_client__copy_pair_t *);
 
699
      apr_hash_t *pinned_externals = NULL;
 
700
 
198
701
      svn_pool_clear(iterpool);
199
702
 
200
703
      /* Check for cancellation */
201
704
      if (ctx->cancel_func)
202
705
        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
203
706
 
 
707
      if (pin_externals)
 
708
        {
 
709
          const char *repos_root_url;
 
710
 
 
711
          SVN_ERR(svn_wc__node_get_origin(NULL, NULL, NULL, &repos_root_url,
 
712
                                          NULL, NULL, NULL, ctx->wc_ctx,
 
713
                                          pair->src_abspath_or_url, FALSE,
 
714
                                          scratch_pool, iterpool));
 
715
          SVN_ERR(resolve_pinned_externals(&pinned_externals,
 
716
                                           externals_to_pin, pair, NULL,
 
717
                                           repos_root_url, ctx,
 
718
                                           iterpool, iterpool));
 
719
        }
 
720
 
204
721
      /* Perform the copy */
205
722
      dst_abspath = svn_dirent_join(pair->dst_parent_abspath, pair->base_name,
206
723
                                    iterpool);
207
724
      *timestamp_sleep = TRUE;
208
725
      err = svn_wc_copy3(ctx->wc_ctx, pair->src_abspath_or_url, dst_abspath,
209
 
                         FALSE /* metadata_only */,
 
726
                         metadata_only,
210
727
                         ctx->cancel_func, ctx->cancel_baton,
211
728
                         ctx->notify_func2, ctx->notify_baton2, iterpool);
212
729
      if (err)
213
730
        break;
 
731
 
 
732
      if (pinned_externals)
 
733
        {
 
734
          apr_hash_index_t *hi;
 
735
 
 
736
          for (hi = apr_hash_first(iterpool, pinned_externals);
 
737
               hi;
 
738
               hi = apr_hash_next(hi))
 
739
            {
 
740
              const char *dst_relpath = apr_hash_this_key(hi);
 
741
              svn_string_t *externals_propval = apr_hash_this_val(hi);
 
742
              const char *local_abspath;
 
743
 
 
744
              local_abspath = svn_dirent_join(pair->dst_abspath_or_url,
 
745
                                              dst_relpath, iterpool);
 
746
              /* ### use a work queue? */
 
747
              SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath,
 
748
                                       SVN_PROP_EXTERNALS, externals_propval,
 
749
                                       svn_depth_empty, TRUE /* skip_checks */,
 
750
                                       NULL  /* changelist_filter */,
 
751
                                       ctx->cancel_func, ctx->cancel_baton,
 
752
                                       NULL, NULL, /* no extra notification */
 
753
                                       iterpool));
 
754
            }
 
755
        }
214
756
    }
215
757
  svn_pool_destroy(iterpool);
216
758
 
223
765
static svn_error_t *
224
766
do_wc_to_wc_copies(svn_boolean_t *timestamp_sleep,
225
767
                   const apr_array_header_t *copy_pairs,
 
768
                   svn_boolean_t metadata_only,
 
769
                   svn_boolean_t pin_externals,
 
770
                   const apr_hash_t *externals_to_pin,
226
771
                   svn_client_ctx_t *ctx,
227
772
                   apr_pool_t *pool)
228
773
{
236
781
 
237
782
  SVN_WC__CALL_WITH_WRITE_LOCK(
238
783
    do_wc_to_wc_copies_with_write_lock(timestamp_sleep, copy_pairs, dst_parent,
239
 
                                       ctx, pool),
 
784
                                       metadata_only, pin_externals,
 
785
                                       externals_to_pin, ctx, pool),
240
786
    ctx->wc_ctx, dst_parent_abspath, FALSE, pool);
241
787
 
242
788
  return SVN_NO_ERROR;
594
1140
  svn_boolean_t resurrection;
595
1141
  svn_boolean_t dir_add;
596
1142
  svn_string_t *mergeinfo;  /* the new mergeinfo for the target */
 
1143
  svn_string_t *externals; /* new externals definitions for the target */
 
1144
  svn_boolean_t only_pin_externals;
597
1145
} path_driver_info_t;
598
1146
 
599
1147
 
631
1179
     with such, the code is just plain wrong. */
632
1180
  SVN_ERR_ASSERT(! svn_path_is_empty(path));
633
1181
 
634
 
  /* Check to see if we need to add the path as a directory. */
 
1182
  /* Check to see if we need to add the path as a parent directory. */
635
1183
  if (path_info->dir_add)
636
1184
    {
637
1185
      return cb_baton->editor->add_directory(path, parent_baton, NULL,
662
1210
      /* Not a move?  This must just be the copy addition. */
663
1211
      else
664
1212
        {
665
 
          do_add = TRUE;
 
1213
          do_add = !path_info->only_pin_externals;
666
1214
        }
667
1215
    }
668
1216
 
702
1250
                                                      pool));
703
1251
        }
704
1252
    }
 
1253
 
 
1254
  if (path_info->externals)
 
1255
    {
 
1256
      if (*dir_baton == NULL)
 
1257
        SVN_ERR(cb_baton->editor->open_directory(path, parent_baton,
 
1258
                                                 SVN_INVALID_REVNUM,
 
1259
                                                 pool, dir_baton));
 
1260
 
 
1261
      SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton, SVN_PROP_EXTERNALS,
 
1262
                                                path_info->externals, pool));
 
1263
    }
 
1264
 
705
1265
  return SVN_NO_ERROR;
706
1266
}
707
1267
 
786
1346
  return SVN_NO_ERROR;
787
1347
}
788
1348
 
 
1349
/* Queue property changes for pinning svn:externals properties set on
 
1350
 * descendants of the path corresponding to PARENT_INFO. PINNED_EXTERNALS
 
1351
 * is keyed by the relative path of each descendant which should have some
 
1352
 * or all of its externals pinned, with the corresponding pinned svn:externals
 
1353
 * properties as values. Property changes are queued in a new list of path
 
1354
 * infos *NEW_PATH_INFOS, or in an existing item of the PATH_INFOS list if an
 
1355
 * existing item is found for the descendant. Allocate results in RESULT_POOL.
 
1356
 * Use SCRATCH_POOL for temporary allocations. */
 
1357
static svn_error_t *
 
1358
queue_externals_change_path_infos(apr_array_header_t *new_path_infos,
 
1359
                                  apr_array_header_t *path_infos,
 
1360
                                  apr_hash_t *pinned_externals,
 
1361
                                  path_driver_info_t *parent_info,
 
1362
                                  apr_pool_t *result_pool,
 
1363
                                  apr_pool_t *scratch_pool)
 
1364
{
 
1365
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
1366
  apr_hash_index_t *hi;
 
1367
 
 
1368
  for (hi = apr_hash_first(scratch_pool, pinned_externals);
 
1369
       hi;
 
1370
       hi = apr_hash_next(hi))
 
1371
    {
 
1372
      const char *dst_relpath = apr_hash_this_key(hi);
 
1373
      svn_string_t *externals_prop = apr_hash_this_val(hi);
 
1374
      const char *src_url;
 
1375
      path_driver_info_t *info;
 
1376
      int i;
 
1377
 
 
1378
      svn_pool_clear(iterpool);
 
1379
 
 
1380
      src_url = svn_path_url_add_component2(parent_info->src_url,
 
1381
                                            dst_relpath, iterpool);
 
1382
 
 
1383
      /* Try to find a path info the external change can be applied to. */
 
1384
      info = NULL;
 
1385
      for (i = 0; i < path_infos->nelts; i++)
 
1386
        {
 
1387
          path_driver_info_t *existing_info;
 
1388
 
 
1389
          existing_info = APR_ARRAY_IDX(path_infos, i, path_driver_info_t *);
 
1390
          if (strcmp(src_url, existing_info->src_url) == 0)
 
1391
            {
 
1392
              info = existing_info;
 
1393
              break;
 
1394
            }
 
1395
        }
 
1396
 
 
1397
      if (info == NULL)
 
1398
        {
 
1399
          /* A copied-along child needs its externals pinned.
 
1400
             Create a new path info for this property change. */
 
1401
          info = apr_pcalloc(result_pool, sizeof(*info));
 
1402
          info->src_url = svn_path_url_add_component2(
 
1403
                                parent_info->src_url, dst_relpath,
 
1404
                                result_pool);
 
1405
          info->src_path = NULL; /* Only needed on copied dirs */
 
1406
          info->dst_path = svn_relpath_join(parent_info->dst_path,
 
1407
                                            dst_relpath,
 
1408
                                            result_pool);
 
1409
          info->src_kind = svn_node_dir;
 
1410
          info->only_pin_externals = TRUE;
 
1411
          APR_ARRAY_PUSH(new_path_infos, path_driver_info_t *) = info;
 
1412
        }
 
1413
 
 
1414
      info->externals = externals_prop;
 
1415
    }
 
1416
 
 
1417
  svn_pool_destroy(iterpool);
 
1418
 
 
1419
  return SVN_NO_ERROR;
 
1420
}
 
1421
 
789
1422
static svn_error_t *
790
1423
repos_to_repos_copy(const apr_array_header_t *copy_pairs,
791
1424
                    svn_boolean_t make_parents,
794
1427
                    void *commit_baton,
795
1428
                    svn_client_ctx_t *ctx,
796
1429
                    svn_boolean_t is_move,
 
1430
                    svn_boolean_t pin_externals,
 
1431
                    const apr_hash_t *externals_to_pin,
797
1432
                    apr_pool_t *pool)
798
1433
{
799
1434
  svn_error_t *err;
809
1444
  struct path_driver_cb_baton cb_baton;
810
1445
  apr_array_header_t *new_dirs = NULL;
811
1446
  apr_hash_t *commit_revprops;
 
1447
  apr_array_header_t *pin_externals_only_infos = NULL;
812
1448
  int i;
813
1449
  svn_client__copy_pair_t *first_pair =
814
1450
    APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
1006
1642
          && (relpath != NULL && *relpath != '\0'))
1007
1643
        {
1008
1644
          info->resurrection = TRUE;
1009
 
          top_url = svn_uri_dirname(top_url, pool);
 
1645
          top_url = svn_uri_get_longest_ancestor(
 
1646
                            top_url,
 
1647
                            svn_uri_dirname(pair->dst_abspath_or_url, pool),
 
1648
                            pool);
1010
1649
          SVN_ERR(svn_ra_reparent(ra_session, top_url, pool));
1011
1650
        }
1012
1651
    }
1058
1697
      SVN_ERR(svn_ra_check_path(ra_session, dst_rel, SVN_INVALID_REVNUM,
1059
1698
                                &dst_kind, pool));
1060
1699
      if (dst_kind != svn_node_none)
1061
 
        return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
1062
 
                                 _("Path '%s' already exists"), dst_rel);
 
1700
        {
 
1701
          const char *path = svn_uri_skip_ancestor(repos_root,
 
1702
                                                   pair->dst_abspath_or_url,
 
1703
                                                   pool);
 
1704
          return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
 
1705
                                   _("Path '/%s' already exists"), path);
 
1706
        }
1063
1707
 
1064
1708
      /* More info for our INFO structure.  */
1065
 
      info->src_path = src_rel;
 
1709
      info->src_path = src_rel; /* May be NULL, if outside RA session scope */
1066
1710
      info->dst_path = dst_rel;
1067
1711
 
1068
1712
      svn_hash_sets(action_hash, info->dst_path, info);
1069
1713
      if (is_move && (! info->resurrection))
1070
1714
        svn_hash_sets(action_hash, info->src_path, info);
 
1715
 
 
1716
      if (pin_externals)
 
1717
        {
 
1718
          apr_hash_t *pinned_externals;
 
1719
 
 
1720
          SVN_ERR(resolve_pinned_externals(&pinned_externals,
 
1721
                                           externals_to_pin, pair,
 
1722
                                           ra_session, repos_root,
 
1723
                                           ctx, pool, pool));
 
1724
          if (pin_externals_only_infos == NULL)
 
1725
            {
 
1726
              pin_externals_only_infos =
 
1727
                apr_array_make(pool, 0, sizeof(path_driver_info_t *));
 
1728
            }
 
1729
          SVN_ERR(queue_externals_change_path_infos(pin_externals_only_infos,
 
1730
                                                    path_infos,
 
1731
                                                    pinned_externals,
 
1732
                                                    info, pool, pool));
 
1733
        }
1071
1734
    }
1072
1735
 
1073
1736
  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
1088
1751
 
1089
1752
              item = svn_client_commit_item3_create(pool);
1090
1753
              item->url = svn_path_url_add_component2(top_url, relpath, pool);
 
1754
              item->kind = svn_node_dir;
1091
1755
              item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
1092
1756
              APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1093
1757
            }
1101
1765
          item = svn_client_commit_item3_create(pool);
1102
1766
          item->url = svn_path_url_add_component2(top_url, info->dst_path,
1103
1767
                                                  pool);
1104
 
          item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
 
1768
          item->kind = info->src_kind;
 
1769
          item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD
 
1770
                              | SVN_CLIENT_COMMIT_ITEM_IS_COPY;
 
1771
          item->copyfrom_url = info->src_url;
 
1772
          item->copyfrom_rev = info->src_revnum;
1105
1773
          APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1106
1774
 
1107
1775
          if (is_move && (! info->resurrection))
1108
1776
            {
1109
 
              item = apr_pcalloc(pool, sizeof(*item));
 
1777
              item = svn_client_commit_item3_create(pool);
1110
1778
              item->url = svn_path_url_add_component2(top_url, info->src_path,
1111
1779
                                                      pool);
 
1780
              item->kind = info->src_kind;
1112
1781
              item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
1113
1782
              APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1114
1783
            }
1150
1819
        APR_ARRAY_PUSH(paths, const char *) = info->src_path;
1151
1820
    }
1152
1821
 
 
1822
  /* Add any items which only need their externals pinned. */
 
1823
  if (pin_externals_only_infos)
 
1824
    {
 
1825
      for (i = 0; i < pin_externals_only_infos->nelts; i++)
 
1826
        {
 
1827
          path_driver_info_t *info;
 
1828
 
 
1829
          info = APR_ARRAY_IDX(pin_externals_only_infos, i, path_driver_info_t *);
 
1830
          APR_ARRAY_PUSH(paths, const char *) = info->dst_path;
 
1831
          svn_hash_sets(action_hash, info->dst_path, info);
 
1832
        }
 
1833
    }
 
1834
 
1153
1835
  SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
1154
1836
                                           message, ctx, pool));
1155
1837
 
1181
1863
                    editor->abort_edit(edit_baton, pool));
1182
1864
    }
1183
1865
 
 
1866
  if (ctx->notify_func2)
 
1867
    {
 
1868
      svn_wc_notify_t *notify;
 
1869
      notify = svn_wc_create_notify_url(top_url,
 
1870
                                        svn_wc_notify_commit_finalizing,
 
1871
                                        pool);
 
1872
      ctx->notify_func2(ctx->notify_baton2, notify, pool);
 
1873
    }
 
1874
 
1184
1875
  /* Close the edit. */
1185
1876
  return svn_error_trace(editor->close_edit(edit_baton, pool));
1186
1877
}
1219
1910
  return SVN_NO_ERROR;
1220
1911
}
1221
1912
 
 
1913
/* Queue a property change on a copy of LOCAL_ABSPATH to COMMIT_URL
 
1914
 * in the COMMIT_ITEMS list.
 
1915
 * If the list does not already have a commit item for COMMIT_URL
 
1916
 * add a new commit item for the property change.
 
1917
 * Allocate results in RESULT_POOL.
 
1918
 * Use SCRATCH_POOL for temporary allocations. */
 
1919
static svn_error_t *
 
1920
queue_prop_change_commit_items(const char *local_abspath,
 
1921
                               const char *commit_url,
 
1922
                               apr_array_header_t *commit_items,
 
1923
                               const char *propname,
 
1924
                               svn_string_t *propval,
 
1925
                               apr_pool_t *result_pool,
 
1926
                               apr_pool_t *scratch_pool)
 
1927
{
 
1928
  svn_client_commit_item3_t *item = NULL;
 
1929
  svn_prop_t *prop;
 
1930
  int i;
 
1931
 
 
1932
  for (i = 0; i < commit_items->nelts; i++)
 
1933
    {
 
1934
      svn_client_commit_item3_t *existing_item;
 
1935
 
 
1936
      existing_item = APR_ARRAY_IDX(commit_items, i,
 
1937
                                    svn_client_commit_item3_t *);
 
1938
      if (strcmp(existing_item->url, commit_url) == 0)
 
1939
        {
 
1940
          item = existing_item;
 
1941
          break;
 
1942
        }
 
1943
    }
 
1944
 
 
1945
  if (item == NULL)
 
1946
    {
 
1947
      item = svn_client_commit_item3_create(result_pool);
 
1948
      item->path = local_abspath;
 
1949
      item->url = commit_url;
 
1950
      item->kind = svn_node_dir;
 
1951
      item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
 
1952
 
 
1953
      item->incoming_prop_changes = apr_array_make(result_pool, 1,
 
1954
                                                   sizeof(svn_prop_t *));
 
1955
      APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
 
1956
    }
 
1957
  else
 
1958
    item->state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
 
1959
 
 
1960
  if (item->outgoing_prop_changes == NULL)
 
1961
    item->outgoing_prop_changes = apr_array_make(result_pool, 1,
 
1962
                                                 sizeof(svn_prop_t *));
 
1963
 
 
1964
  prop = apr_palloc(result_pool, sizeof(*prop));
 
1965
  prop->name = propname;
 
1966
  prop->value = propval;
 
1967
  APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *) = prop;
 
1968
 
 
1969
  return SVN_NO_ERROR;
 
1970
}
 
1971
 
1222
1972
/* ### Copy ...
1223
1973
 * COMMIT_INFO_P is ...
1224
1974
 * COPY_PAIRS is ... such that each 'src_abspath_or_url' is a local abspath
1232
1982
                 const apr_hash_t *revprop_table,
1233
1983
                 svn_commit_callback2_t commit_callback,
1234
1984
                 void *commit_baton,
 
1985
                 svn_boolean_t pin_externals,
 
1986
                 const apr_hash_t *externals_to_pin,
1235
1987
                 svn_client_ctx_t *ctx,
1236
1988
                 apr_pool_t *scratch_pool)
1237
1989
{
1241
1993
  const char *top_src_abspath;
1242
1994
  svn_ra_session_t *ra_session;
1243
1995
  const svn_delta_editor_t *editor;
 
1996
#ifdef ENABLE_EV2_SHIMS
1244
1997
  apr_hash_t *relpath_map = NULL;
 
1998
#endif
1245
1999
  void *edit_baton;
1246
2000
  svn_client__committables_t *committables;
1247
2001
  apr_array_header_t *commit_items;
1250
2004
  apr_hash_t *commit_revprops;
1251
2005
  svn_client__copy_pair_t *first_pair;
1252
2006
  apr_pool_t *session_pool = svn_pool_create(scratch_pool);
 
2007
  apr_array_header_t *commit_items_for_dav;
1253
2008
  int i;
1254
2009
 
1255
2010
  /* Find the common root of all the source paths */
1284
2039
 
1285
2040
  SVN_ERR(svn_dirent_get_absolute(&top_src_abspath, top_src_path, scratch_pool));
1286
2041
 
 
2042
  commit_items_for_dav = apr_array_make(session_pool, 0,
 
2043
                                        sizeof(svn_client_commit_item3_t*));
 
2044
 
1287
2045
  /* Open a session to help while determining the exact targets */
1288
2046
  SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_dst_url,
1289
 
                                               top_src_abspath, NULL,
 
2047
                                               top_src_abspath,
 
2048
                                               commit_items_for_dav,
1290
2049
                                               FALSE /* write_dav_props */,
1291
2050
                                               TRUE /* read_dav_props */,
1292
2051
                                               ctx,
1323
2082
        }
1324
2083
    }
1325
2084
 
1326
 
  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
1327
 
    {
1328
 
      /* Produce a list of new paths to add, and provide it to the
1329
 
         mechanism used to acquire a log message. */
1330
 
      svn_client_commit_item3_t *item;
1331
 
      const char *tmp_file;
1332
 
      commit_items = apr_array_make(scratch_pool, copy_pairs->nelts,
1333
 
                                    sizeof(item));
1334
 
 
1335
 
      /* Add any intermediate directories to the message */
1336
 
      if (make_parents)
1337
 
        {
1338
 
          for (i = 0; i < new_dirs->nelts; i++)
1339
 
            {
1340
 
              const char *url = APR_ARRAY_IDX(new_dirs, i, const char *);
1341
 
 
1342
 
              item = svn_client_commit_item3_create(scratch_pool);
1343
 
              item->url = url;
1344
 
              item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
1345
 
              APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1346
 
            }
1347
 
        }
1348
 
 
1349
 
      for (i = 0; i < copy_pairs->nelts; i++)
1350
 
        {
1351
 
          svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
1352
 
                                            svn_client__copy_pair_t *);
1353
 
 
1354
 
          item = svn_client_commit_item3_create(scratch_pool);
1355
 
          item->url = pair->dst_abspath_or_url;
1356
 
          item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
1357
 
          APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1358
 
        }
1359
 
 
1360
 
      SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
1361
 
                                      ctx, scratch_pool));
1362
 
      if (! message)
1363
 
        {
1364
 
          svn_pool_destroy(iterpool);
1365
 
          svn_pool_destroy(session_pool);
1366
 
          return SVN_NO_ERROR;
1367
 
        }
1368
 
    }
1369
 
  else
1370
 
    message = "";
1371
 
 
1372
2085
  cukb.session = ra_session;
1373
2086
  SVN_ERR(svn_ra_get_repos_root2(ra_session, &cukb.repos_root_url, session_pool));
1374
2087
  cukb.should_reparent = FALSE;
1399
2112
 
1400
2113
          item = svn_client_commit_item3_create(scratch_pool);
1401
2114
          item->url = url;
 
2115
          item->kind = svn_node_dir;
1402
2116
          item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
1403
2117
          item->incoming_prop_changes = apr_array_make(scratch_pool, 1,
1404
2118
                                                       sizeof(svn_prop_t *));
1426
2140
 
1427
2141
      /* Set the mergeinfo for the destination to the combined merge
1428
2142
         info known to the WC and the repository. */
1429
 
      item->outgoing_prop_changes = apr_array_make(scratch_pool, 1,
1430
 
                                                   sizeof(svn_prop_t *));
1431
2143
      /* Repository mergeinfo (or NULL if it's locally added)... */
1432
2144
      if (src_origin)
1433
2145
        SVN_ERR(svn_client__get_repos_mergeinfo(
1444
2156
                                     iterpool));
1445
2157
      else if (! mergeinfo)
1446
2158
        mergeinfo = wc_mergeinfo;
 
2159
 
1447
2160
      if (mergeinfo)
1448
2161
        {
1449
2162
          /* Push a mergeinfo prop representing MERGEINFO onto the
1450
2163
           * OUTGOING_PROP_CHANGES array. */
1451
2164
 
1452
2165
          svn_prop_t *mergeinfo_prop
1453
 
            = apr_palloc(item->outgoing_prop_changes->pool,
1454
 
                         sizeof(svn_prop_t));
 
2166
                            = apr_palloc(scratch_pool, sizeof(*mergeinfo_prop));
1455
2167
          svn_string_t *prop_value;
1456
2168
 
1457
2169
          SVN_ERR(svn_mergeinfo_to_string(&prop_value, mergeinfo,
1458
 
                                          item->outgoing_prop_changes->pool));
 
2170
                                          scratch_pool));
 
2171
 
 
2172
          if (!item->outgoing_prop_changes)
 
2173
            {
 
2174
              item->outgoing_prop_changes = apr_array_make(scratch_pool, 1,
 
2175
                                                           sizeof(svn_prop_t *));
 
2176
            }
1459
2177
 
1460
2178
          mergeinfo_prop->name = SVN_PROP_MERGEINFO;
1461
2179
          mergeinfo_prop->value = prop_value;
1462
2180
          APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *)
1463
2181
            = mergeinfo_prop;
1464
2182
        }
1465
 
    }
 
2183
 
 
2184
      if (pin_externals)
 
2185
        {
 
2186
          apr_hash_t *pinned_externals;
 
2187
          apr_hash_index_t *hi;
 
2188
 
 
2189
          SVN_ERR(resolve_pinned_externals(&pinned_externals,
 
2190
                                           externals_to_pin, pair,
 
2191
                                           ra_session, cukb.repos_root_url,
 
2192
                                           ctx, scratch_pool, iterpool));
 
2193
          for (hi = apr_hash_first(scratch_pool, pinned_externals);
 
2194
               hi;
 
2195
               hi = apr_hash_next(hi))
 
2196
            {
 
2197
              const char *dst_relpath = apr_hash_this_key(hi);
 
2198
              svn_string_t *externals_propval = apr_hash_this_val(hi);
 
2199
              const char *dst_url;
 
2200
              const char *commit_url;
 
2201
              const char *src_abspath;
 
2202
 
 
2203
              if (svn_path_is_url(pair->dst_abspath_or_url))
 
2204
                dst_url = pair->dst_abspath_or_url;
 
2205
              else
 
2206
                SVN_ERR(svn_wc__node_get_url(&dst_url, ctx->wc_ctx,
 
2207
                                             pair->dst_abspath_or_url,
 
2208
                                             scratch_pool, iterpool));
 
2209
              commit_url = svn_path_url_add_component2(dst_url, dst_relpath,
 
2210
                                                       scratch_pool);
 
2211
              src_abspath = svn_dirent_join(pair->src_abspath_or_url,
 
2212
                                            dst_relpath, iterpool);
 
2213
              SVN_ERR(queue_prop_change_commit_items(src_abspath,
 
2214
                                                     commit_url, commit_items,
 
2215
                                                     SVN_PROP_EXTERNALS,
 
2216
                                                     externals_propval,
 
2217
                                                     scratch_pool, iterpool));
 
2218
            }
 
2219
        }
 
2220
    }
 
2221
 
 
2222
  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
 
2223
    {
 
2224
      const char *tmp_file;
 
2225
 
 
2226
      SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
 
2227
                                      ctx, scratch_pool));
 
2228
      if (! message)
 
2229
        {
 
2230
          svn_pool_destroy(iterpool);
 
2231
          svn_pool_destroy(session_pool);
 
2232
          return SVN_NO_ERROR;
 
2233
        }
 
2234
    }
 
2235
  else
 
2236
    message = "";
1466
2237
 
1467
2238
  /* Sort and condense our COMMIT_ITEMS. */
1468
2239
  SVN_ERR(svn_client__condense_commit_items(&top_dst_url,
1469
2240
                                            commit_items, scratch_pool));
1470
2241
 
 
2242
  /* Add the commit items to the DAV commit item list to provide access
 
2243
     to dav properties (for pre http-v2 DAV) */
 
2244
  apr_array_cat(commit_items_for_dav, commit_items);
 
2245
 
1471
2246
#ifdef ENABLE_EV2_SHIMS
1472
2247
  if (commit_items)
1473
2248
    {
1474
 
      relpath_map = apr_hash_make(pool);
 
2249
      relpath_map = apr_hash_make(scratch_pool);
1475
2250
      for (i = 0; i < commit_items->nelts; i++)
1476
2251
        {
1477
2252
          svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i,
1482
2257
            continue;
1483
2258
 
1484
2259
          svn_pool_clear(iterpool);
1485
 
          SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL,
 
2260
          SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL,
 
2261
                                          NULL, NULL,
1486
2262
                                          ctx->wc_ctx, item->path, FALSE,
1487
2263
                                          scratch_pool, iterpool));
1488
2264
          if (relpath)
1491
2267
    }
1492
2268
#endif
1493
2269
 
1494
 
  /* Close the initial session, to reopen a new session with commit handling */
1495
 
  svn_pool_clear(session_pool);
1496
 
 
1497
 
  /* Open a new RA session to DST_URL. */
1498
 
  SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_dst_url,
1499
 
                                               NULL, commit_items,
1500
 
                                               FALSE, FALSE, ctx,
1501
 
                                               session_pool, session_pool));
 
2270
  SVN_ERR(svn_ra_reparent(ra_session, top_dst_url, session_pool));
1502
2271
 
1503
2272
  SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
1504
2273
                                           message, ctx, session_pool));
1505
2274
 
1506
2275
  /* Fetch RA commit editor. */
 
2276
#ifdef ENABLE_EV2_SHIMS
1507
2277
  SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
1508
2278
                        svn_client__get_shim_callbacks(ctx->wc_ctx, relpath_map,
1509
2279
                                                       session_pool)));
 
2280
#endif
1510
2281
  SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1511
2282
                                    commit_revprops,
1512
2283
                                    commit_callback,
1517
2288
  /* Perform the commit. */
1518
2289
  SVN_ERR_W(svn_client__do_commit(top_dst_url, commit_items,
1519
2290
                                  editor, edit_baton,
1520
 
                                  0, /* ### any notify_path_offset needed? */
 
2291
                                  NULL /* notify_path_prefix */,
1521
2292
                                  NULL, ctx, session_pool, session_pool),
1522
2293
            _("Commit failed (details follow):"));
1523
2294
 
1565
2336
                        svn_client__copy_pair_t *pair,
1566
2337
                        svn_boolean_t same_repositories,
1567
2338
                        svn_boolean_t ignore_externals,
 
2339
                        svn_boolean_t pin_externals,
 
2340
                        const apr_hash_t *externals_to_pin,
1568
2341
                        svn_ra_session_t *ra_session,
1569
2342
                        svn_client_ctx_t *ctx,
1570
2343
                        apr_pool_t *pool)
1593
2366
    {
1594
2367
      if (same_repositories)
1595
2368
        {
1596
 
          svn_boolean_t sleep_needed = FALSE;
1597
2369
          const char *tmpdir_abspath, *tmp_abspath;
1598
2370
 
1599
2371
          /* Find a temporary location in which to check out the copy source. */
1622
2394
            ctx->notify_func2 = notification_adjust_func;
1623
2395
            ctx->notify_baton2 = &nb;
1624
2396
 
1625
 
            err = svn_client__checkout_internal(&pair->src_revnum,
 
2397
            /* Avoid a chicken-and-egg problem:
 
2398
             * If pinning externals we'll need to adjust externals
 
2399
             * properties before checking out any externals.
 
2400
             * But copy needs to happen before pinning because else there
 
2401
             * are no svn:externals properties to pin. */
 
2402
            if (pin_externals)
 
2403
              ignore_externals = TRUE;
 
2404
 
 
2405
            err = svn_client__checkout_internal(&pair->src_revnum, timestamp_sleep,
1626
2406
                                                pair->src_original,
1627
2407
                                                tmp_abspath,
1628
2408
                                                &pair->src_peg_revision,
1629
2409
                                                &pair->src_op_revision,
1630
2410
                                                svn_depth_infinity,
1631
2411
                                                ignore_externals, FALSE,
1632
 
                                                &sleep_needed, ctx, pool);
 
2412
                                                ra_session, ctx, pool);
1633
2413
 
1634
2414
            ctx->notify_func2 = old_notify_func2;
1635
2415
            ctx->notify_baton2 = old_notify_baton2;
1674
2454
 
1675
2455
          return SVN_NO_ERROR;
1676
2456
        }
 
2457
 
 
2458
      if (pin_externals)
 
2459
        {
 
2460
          apr_hash_t *pinned_externals;
 
2461
          apr_hash_index_t *hi;
 
2462
          apr_pool_t *iterpool;
 
2463
          const char *repos_root_url;
 
2464
          apr_hash_t *new_externals;
 
2465
          apr_hash_t *new_depths;
 
2466
 
 
2467
          SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
 
2468
          SVN_ERR(resolve_pinned_externals(&pinned_externals,
 
2469
                                           externals_to_pin, pair,
 
2470
                                           ra_session, repos_root_url,
 
2471
                                           ctx, pool, pool));
 
2472
 
 
2473
          iterpool = svn_pool_create(pool);
 
2474
          for (hi = apr_hash_first(pool, pinned_externals);
 
2475
               hi;
 
2476
               hi = apr_hash_next(hi))
 
2477
            {
 
2478
              const char *dst_relpath = apr_hash_this_key(hi);
 
2479
              svn_string_t *externals_propval = apr_hash_this_val(hi);
 
2480
              const char *local_abspath;
 
2481
 
 
2482
              svn_pool_clear(iterpool);
 
2483
 
 
2484
              local_abspath = svn_dirent_join(pair->dst_abspath_or_url,
 
2485
                                              dst_relpath, iterpool);
 
2486
              /* ### use a work queue? */
 
2487
              SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath,
 
2488
                                       SVN_PROP_EXTERNALS, externals_propval,
 
2489
                                       svn_depth_empty, TRUE /* skip_checks */,
 
2490
                                       NULL  /* changelist_filter */,
 
2491
                                       ctx->cancel_func, ctx->cancel_baton,
 
2492
                                       NULL, NULL, /* no extra notification */
 
2493
                                       iterpool));
 
2494
            }
 
2495
 
 
2496
          /* Now update all externals in the newly created copy. */
 
2497
          SVN_ERR(svn_wc__externals_gather_definitions(&new_externals,
 
2498
                                                       &new_depths,
 
2499
                                                       ctx->wc_ctx,
 
2500
                                                       dst_abspath,
 
2501
                                                       svn_depth_infinity,
 
2502
                                                       iterpool, iterpool));
 
2503
          SVN_ERR(svn_client__handle_externals(new_externals,
 
2504
                                               new_depths,
 
2505
                                               repos_root_url, dst_abspath,
 
2506
                                               svn_depth_infinity,
 
2507
                                               timestamp_sleep,
 
2508
                                               ra_session,
 
2509
                                               ctx, iterpool));
 
2510
          svn_pool_destroy(iterpool);
 
2511
        }
1677
2512
    } /* end directory case */
1678
2513
 
1679
2514
  else if (pair->src_kind == svn_node_file)
1722
2557
      svn_wc_notify_t *notify = svn_wc_create_notify(
1723
2558
                                  dst_abspath, svn_wc_notify_add, pool);
1724
2559
      notify->kind = pair->src_kind;
1725
 
      (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
 
2560
      ctx->notify_func2(ctx->notify_baton2, notify, pool);
1726
2561
    }
1727
2562
 
1728
2563
  return SVN_NO_ERROR;
1733
2568
                        const apr_array_header_t *copy_pairs,
1734
2569
                        const char *top_dst_path,
1735
2570
                        svn_boolean_t ignore_externals,
 
2571
                        svn_boolean_t pin_externals,
 
2572
                        const apr_hash_t *externals_to_pin,
1736
2573
                        svn_ra_session_t *ra_session,
1737
2574
                        svn_client_ctx_t *ctx,
1738
2575
                        apr_pool_t *scratch_pool)
1798
2635
                                                    svn_client__copy_pair_t *),
1799
2636
                                      same_repositories,
1800
2637
                                      ignore_externals,
 
2638
                                      pin_externals, externals_to_pin,
1801
2639
                                      ra_session, ctx, iterpool));
1802
2640
    }
1803
2641
  svn_pool_destroy(iterpool);
1810
2648
                 const apr_array_header_t *copy_pairs,
1811
2649
                 svn_boolean_t make_parents,
1812
2650
                 svn_boolean_t ignore_externals,
 
2651
                 svn_boolean_t pin_externals,
 
2652
                 const apr_hash_t *externals_to_pin,
1813
2653
                 svn_client_ctx_t *ctx,
1814
2654
                 apr_pool_t *pool)
1815
2655
{
1918
2758
  SVN_WC__CALL_WITH_WRITE_LOCK(
1919
2759
    repos_to_wc_copy_locked(timestamp_sleep,
1920
2760
                            copy_pairs, top_dst_path, ignore_externals,
 
2761
                            pin_externals, externals_to_pin,
1921
2762
                            ra_session, ctx, pool),
1922
2763
    ctx->wc_ctx, lock_abspath, FALSE, pool);
1923
2764
  return SVN_NO_ERROR;
1944
2785
         svn_boolean_t metadata_only,
1945
2786
         svn_boolean_t make_parents,
1946
2787
         svn_boolean_t ignore_externals,
 
2788
         svn_boolean_t pin_externals,
 
2789
         const apr_hash_t *externals_to_pin,
1947
2790
         const apr_hash_t *revprop_table,
1948
2791
         svn_commit_callback2_t commit_callback,
1949
2792
         void *commit_baton,
1956
2799
  svn_boolean_t srcs_are_urls, dst_is_url;
1957
2800
  int i;
1958
2801
 
 
2802
  /* Assert instead of crashing if the sources list is empty. */
 
2803
  SVN_ERR_ASSERT(sources->nelts > 0);
 
2804
 
1959
2805
  /* Are either of our paths URLs?  Just check the first src_path.  If
1960
2806
     there are more than one, we'll check for homogeneity among them
1961
2807
     down below. */
1976
2822
        {
1977
2823
          svn_client_copy_source_t *source = APR_ARRAY_IDX(sources, i,
1978
2824
                                               svn_client_copy_source_t *);
1979
 
          svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair));
 
2825
          svn_client__copy_pair_t *pair = apr_pcalloc(pool, sizeof(*pair));
1980
2826
          const char *src_basename;
1981
2827
          svn_boolean_t src_is_url = svn_path_is_url(source->path);
1982
2828
 
1998
2844
 
1999
2845
          pair->src_op_revision = *source->revision;
2000
2846
          pair->src_peg_revision = *source->peg_revision;
 
2847
          pair->src_kind = svn_node_unknown;
2001
2848
 
2002
2849
          SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision,
2003
2850
                                            &pair->src_op_revision,
2026
2873
  else
2027
2874
    {
2028
2875
      /* Only one source path. */
2029
 
      svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair));
 
2876
      svn_client__copy_pair_t *pair = apr_pcalloc(pool, sizeof(*pair));
2030
2877
      svn_client_copy_source_t *source =
2031
2878
        APR_ARRAY_IDX(sources, 0, svn_client_copy_source_t *);
2032
2879
      svn_boolean_t src_is_url = svn_path_is_url(source->path);
2038
2885
                                        source->path, pool));
2039
2886
      pair->src_op_revision = *source->revision;
2040
2887
      pair->src_peg_revision = *source->peg_revision;
 
2888
      pair->src_kind = svn_node_unknown;
2041
2889
 
2042
2890
      SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision,
2043
2891
                                        &pair->src_op_revision,
2184
3032
                  SVN_ERR(svn_wc__node_get_origin(NULL, &copyfrom_rev,
2185
3033
                                                  &copyfrom_repos_relpath,
2186
3034
                                                  &copyfrom_repos_root_url,
2187
 
                                                  NULL, NULL,
 
3035
                                                  NULL, NULL, NULL,
2188
3036
                                                  ctx->wc_ctx,
2189
3037
                                                  pair->src_abspath_or_url,
2190
3038
                                                  TRUE, iterpool, iterpool));
2239
3087
      else
2240
3088
        {
2241
3089
          /* We ignore these values, so assert the default value */
2242
 
          SVN_ERR_ASSERT(allow_mixed_revisions && !metadata_only);
 
3090
          SVN_ERR_ASSERT(allow_mixed_revisions);
2243
3091
          return svn_error_trace(do_wc_to_wc_copies(timestamp_sleep,
2244
 
                                                    copy_pairs, ctx, pool));
 
3092
                                                    copy_pairs,
 
3093
                                                    metadata_only,
 
3094
                                                    pin_externals,
 
3095
                                                    externals_to_pin,
 
3096
                                                    ctx, pool));
2245
3097
        }
2246
3098
    }
2247
3099
  else if ((! srcs_are_urls) && (dst_is_url))
2248
3100
    {
2249
3101
      return svn_error_trace(
2250
3102
        wc_to_repos_copy(copy_pairs, make_parents, revprop_table,
2251
 
                         commit_callback, commit_baton, ctx, pool));
 
3103
                         commit_callback, commit_baton,
 
3104
                         pin_externals, externals_to_pin, ctx, pool));
2252
3105
    }
2253
3106
  else if ((srcs_are_urls) && (! dst_is_url))
2254
3107
    {
2255
3108
      return svn_error_trace(
2256
3109
        repos_to_wc_copy(timestamp_sleep,
2257
3110
                         copy_pairs, make_parents, ignore_externals,
2258
 
                         ctx, pool));
 
3111
                         pin_externals, externals_to_pin, ctx, pool));
2259
3112
    }
2260
3113
  else
2261
3114
    {
2262
3115
      return svn_error_trace(
2263
3116
        repos_to_repos_copy(copy_pairs, make_parents, revprop_table,
2264
3117
                            commit_callback, commit_baton, ctx, is_move,
2265
 
                            pool));
 
3118
                            pin_externals, externals_to_pin, pool));
2266
3119
    }
2267
3120
}
2268
3121
 
2270
3123
 
2271
3124
/* Public Interfaces */
2272
3125
svn_error_t *
2273
 
svn_client_copy6(const apr_array_header_t *sources,
 
3126
svn_client_copy7(const apr_array_header_t *sources,
2274
3127
                 const char *dst_path,
2275
3128
                 svn_boolean_t copy_as_child,
2276
3129
                 svn_boolean_t make_parents,
2277
3130
                 svn_boolean_t ignore_externals,
 
3131
                 svn_boolean_t metadata_only,
 
3132
                 svn_boolean_t pin_externals,
 
3133
                 const apr_hash_t *externals_to_pin,
2278
3134
                 const apr_hash_t *revprop_table,
2279
3135
                 svn_commit_callback2_t commit_callback,
2280
3136
                 void *commit_baton,
2293
3149
                 sources, dst_path,
2294
3150
                 FALSE /* is_move */,
2295
3151
                 TRUE /* allow_mixed_revisions */,
2296
 
                 FALSE /* metadata_only */,
 
3152
                 metadata_only,
2297
3153
                 make_parents,
2298
3154
                 ignore_externals,
 
3155
                 pin_externals,
 
3156
                 externals_to_pin,
2299
3157
                 revprop_table,
2300
3158
                 commit_callback, commit_baton,
2301
3159
                 ctx,
2327
3185
                     sources, dst_path,
2328
3186
                     FALSE /* is_move */,
2329
3187
                     TRUE /* allow_mixed_revisions */,
2330
 
                     FALSE /* metadata_only */,
 
3188
                     metadata_only,
2331
3189
                     make_parents,
2332
3190
                     ignore_externals,
 
3191
                     pin_externals,
 
3192
                     externals_to_pin,
2333
3193
                     revprop_table,
2334
3194
                     commit_callback, commit_baton,
2335
3195
                     ctx,
2391
3251
                 metadata_only,
2392
3252
                 make_parents,
2393
3253
                 FALSE /* ignore_externals */,
 
3254
                 FALSE /* pin_externals */,
 
3255
                 NULL /* externals_to_pin */,
2394
3256
                 revprop_table,
2395
3257
                 commit_callback, commit_baton,
2396
3258
                 ctx,
2424
3286
                     metadata_only,
2425
3287
                     make_parents,
2426
3288
                     FALSE /* ignore_externals */,
 
3289
                     FALSE /* pin_externals */,
 
3290
                     NULL /* externals_to_pin */,
2427
3291
                     revprop_table,
2428
3292
                     commit_callback, commit_baton,
2429
3293
                     ctx,