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

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/export.c

  • Committer: Package Import Robot
  • Author(s): James McCoy, Peter Samuelson, James McCoy
  • Date: 2014-01-12 19:48:33 UTC
  • mfrom: (0.2.10)
  • Revision ID: package-import@ubuntu.com-20140112194833-w3axfwksn296jn5x
Tags: 1.8.5-1
[ Peter Samuelson ]
* New upstream release.  (Closes: #725787) Rediff patches:
  - Remove apr-abi1 (applied upstream), rename apr-abi2 to apr-abi
  - Remove loosen-sqlite-version-check (shouldn't be needed)
  - Remove java-osgi-metadata (applied upstream)
  - svnmucc prompts for a changelog if none is provided. (Closes: #507430)
  - Remove fix-bdb-version-detection, upstream uses "apu-config --dbm-libs"
  - Remove ruby-test-wc (applied upstream)
  - Fix “svn diff -r N file” when file has svn:mime-type set.
    (Closes: #734163)
  - Support specifying an encoding for mod_dav_svn's environment in which
    hooks are run.  (Closes: #601544)
  - Fix ordering of “svnadmin dump” paths with certain APR versions.
    (Closes: #687291)
  - Provide a better error message when authentication fails with an
    svn+ssh:// URL.  (Closes: #273874)
  - Updated Polish translations.  (Closes: #690815)

[ James McCoy ]
* Remove all traces of libneon, replaced by libserf.
* patches/sqlite_3.8.x_workaround: Upstream fix for wc-queries-test test
  failurse.
* Run configure with --with-apache-libexecdir, which allows removing part of
  patches/rpath.
* Re-enable auth-test as upstream has fixed the problem of picking up
  libraries from the environment rather than the build tree.
  (Closes: #654172)
* Point LD_LIBRARY_PATH at the built auth libraries when running the svn
  command during the build.  (Closes: #678224)
* Add a NEWS entry describing how to configure mod_dav_svn to understand
  UTF-8.  (Closes: #566148)
* Remove ancient transitional package, libsvn-ruby.
* Enable compatibility with Sqlite3 versions back to Wheezy.
* Enable hardening flags.  (Closes: #734918)
* patches/build-fixes: Enable verbose build logs.
* Build against the default ruby version.  (Closes: #722393)

Show diffs side-by-side

added added

removed removed

Lines of Context:
34
34
#include "svn_string.h"
35
35
#include "svn_error.h"
36
36
#include "svn_dirent_uri.h"
 
37
#include "svn_hash.h"
37
38
#include "svn_path.h"
38
39
#include "svn_pools.h"
39
40
#include "svn_subst.h"
42
43
#include "client.h"
43
44
 
44
45
#include "svn_private_config.h"
 
46
#include "private/svn_subr_private.h"
 
47
#include "private/svn_delta_private.h"
45
48
#include "private/svn_wc_private.h"
46
49
 
 
50
#ifndef ENABLE_EV2_IMPL
 
51
#define ENABLE_EV2_IMPL 0
 
52
#endif
 
53
 
47
54
 
48
55
/*** Code. ***/
49
56
 
62
69
 
63
70
  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
64
71
 
65
 
  apr_hash_set(externals, local_abspath, APR_HASH_KEY_STRING,
66
 
               apr_pstrmemdup(pool, externals_prop_val->data,
67
 
                              externals_prop_val->len));
 
72
  svn_hash_sets(externals, local_abspath,
 
73
                apr_pstrmemdup(pool, externals_prop_val->data,
 
74
                               externals_prop_val->len));
68
75
 
69
76
  return SVN_NO_ERROR;
70
77
}
150
157
 * Set the destination file's 'executable' flag according to the source
151
158
 * file's 'svn:executable' property.
152
159
 */
 
160
 
 
161
/* baton for export_node */
 
162
struct export_info_baton
 
163
{
 
164
  const char *to_path;
 
165
  const svn_opt_revision_t *revision;
 
166
  svn_boolean_t ignore_keywords;
 
167
  svn_boolean_t overwrite;
 
168
  svn_wc_context_t *wc_ctx;
 
169
  const char *native_eol;
 
170
  svn_wc_notify_func2_t notify_func;
 
171
  void *notify_baton;
 
172
  const char *origin_abspath;
 
173
  svn_boolean_t exported;
 
174
};
 
175
 
 
176
/* Export a file or directory. Implements svn_wc_status_func4_t */
153
177
static svn_error_t *
154
 
copy_one_versioned_file(const char *from_abspath,
155
 
                        const char *to_abspath,
156
 
                        svn_client_ctx_t *ctx,
157
 
                        const svn_opt_revision_t *revision,
158
 
                        const char *native_eol,
159
 
                        svn_boolean_t ignore_keywords,
160
 
                        apr_pool_t *scratch_pool)
 
178
export_node(void *baton,
 
179
            const char *local_abspath,
 
180
            const svn_wc_status3_t *status,
 
181
            apr_pool_t *scratch_pool)
161
182
{
 
183
  struct export_info_baton *eib = baton;
 
184
  svn_wc_context_t *wc_ctx = eib->wc_ctx;
162
185
  apr_hash_t *kw = NULL;
163
186
  svn_subst_eol_style_t style;
164
187
  apr_hash_t *props;
170
193
  svn_stream_t *dst_stream;
171
194
  const char *dst_tmp;
172
195
  svn_error_t *err;
173
 
  svn_boolean_t is_deleted;
174
 
  svn_wc_context_t *wc_ctx = ctx->wc_ctx;
175
 
 
176
 
  SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, wc_ctx, from_abspath,
177
 
                                         scratch_pool));
 
196
 
 
197
  const char *to_abspath = svn_dirent_join(
 
198
                                eib->to_path,
 
199
                                svn_dirent_skip_ancestor(eib->origin_abspath,
 
200
                                                         local_abspath),
 
201
                                scratch_pool);
 
202
 
 
203
  eib->exported = TRUE;
178
204
 
179
205
  /* Don't export 'deleted' files and directories unless it's a
180
206
     revision other than WORKING.  These files and directories
181
207
     don't really exist in WORKING. */
182
 
  if (revision->kind == svn_opt_revision_working && is_deleted)
183
 
    return SVN_NO_ERROR;
184
 
 
185
 
  if (revision->kind != svn_opt_revision_working)
 
208
  if (eib->revision->kind == svn_opt_revision_working
 
209
      && status->node_status == svn_wc_status_deleted)
 
210
    return SVN_NO_ERROR;
 
211
 
 
212
  if (status->kind == svn_node_dir)
 
213
    {
 
214
      apr_fileperms_t perm = APR_OS_DEFAULT;
 
215
 
 
216
      /* Try to make the new directory.  If this fails because the
 
217
         directory already exists, check our FORCE flag to see if we
 
218
         care. */
 
219
 
 
220
      /* Keep the source directory's permissions if applicable.
 
221
         Skip retrieving the umask on windows. Apr does not implement setting
 
222
         filesystem privileges on Windows.
 
223
         Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER
 
224
         is documented to be 'incredibly expensive' */
 
225
#ifndef WIN32
 
226
      if (eib->revision->kind == svn_opt_revision_working)
 
227
        {
 
228
          apr_finfo_t finfo;
 
229
          SVN_ERR(svn_io_stat(&finfo, local_abspath, APR_FINFO_PROT,
 
230
                              scratch_pool));
 
231
          perm = finfo.protection;
 
232
        }
 
233
#endif
 
234
      err = svn_io_dir_make(to_abspath, perm, scratch_pool);
 
235
      if (err)
 
236
        {
 
237
          if (! APR_STATUS_IS_EEXIST(err->apr_err))
 
238
            return svn_error_trace(err);
 
239
          if (! eib->overwrite)
 
240
            SVN_ERR_W(err, _("Destination directory exists, and will not be "
 
241
                             "overwritten unless forced"));
 
242
          else
 
243
            svn_error_clear(err);
 
244
        }
 
245
 
 
246
      if (eib->notify_func
 
247
          && (strcmp(eib->origin_abspath, local_abspath) != 0))
 
248
        {
 
249
          svn_wc_notify_t *notify =
 
250
              svn_wc_create_notify(to_abspath,
 
251
                                   svn_wc_notify_update_add, scratch_pool);
 
252
 
 
253
          notify->kind = svn_node_dir;
 
254
          (eib->notify_func)(eib->notify_baton, notify, scratch_pool);
 
255
        }
 
256
 
 
257
      return SVN_NO_ERROR;
 
258
    }
 
259
  else if (status->kind != svn_node_file)
 
260
    {
 
261
      if (strcmp(eib->origin_abspath, local_abspath) != 0)
 
262
        return SVN_NO_ERROR;
 
263
 
 
264
      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
 
265
                               _("The node '%s' was not found."),
 
266
                               svn_dirent_local_style(local_abspath,
 
267
                                                      scratch_pool));
 
268
    }
 
269
 
 
270
  if (status->file_external)
 
271
    return SVN_NO_ERROR;
 
272
 
 
273
  /* Produce overwrite errors for the export root */
 
274
  if (strcmp(local_abspath, eib->origin_abspath) == 0)
 
275
    {
 
276
      svn_node_kind_t to_kind;
 
277
 
 
278
      SVN_ERR(svn_io_check_path(to_abspath, &to_kind, scratch_pool));
 
279
 
 
280
      if ((to_kind == svn_node_file || to_kind == svn_node_unknown)
 
281
          && !eib->overwrite)
 
282
        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
 
283
                                 _("Destination file '%s' exists, and "
 
284
                                   "will not be overwritten unless forced"),
 
285
                                 svn_dirent_local_style(to_abspath,
 
286
                                                        scratch_pool));
 
287
      else if (to_kind == svn_node_dir)
 
288
        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
 
289
                                 _("Destination '%s' exists. Cannot "
 
290
                                   "overwrite directory with non-directory"),
 
291
                                 svn_dirent_local_style(to_abspath,
 
292
                                                        scratch_pool));
 
293
    }
 
294
 
 
295
  if (eib->revision->kind != svn_opt_revision_working)
186
296
    {
187
297
      /* Only export 'added' files when the revision is WORKING. This is not
188
298
         WORKING, so skip the 'added' files, since they didn't exist
207
317
 
208
318
         We get all this for free from evaluating SOURCE == NULL:
209
319
       */
210
 
      SVN_ERR(svn_wc_get_pristine_contents2(&source, wc_ctx, from_abspath,
 
320
      SVN_ERR(svn_wc_get_pristine_contents2(&source, wc_ctx, local_abspath,
211
321
                                            scratch_pool, scratch_pool));
212
322
      if (source == NULL)
213
323
        return SVN_NO_ERROR;
214
324
 
215
 
      SVN_ERR(svn_wc_get_pristine_props(&props, wc_ctx, from_abspath,
 
325
      SVN_ERR(svn_wc_get_pristine_props(&props, wc_ctx, local_abspath,
216
326
                                        scratch_pool, scratch_pool));
217
327
    }
218
328
  else
219
329
    {
220
 
      svn_wc_status3_t *status;
221
 
 
222
330
      /* ### hmm. this isn't always a specialfile. this will simply open
223
331
         ### the file readonly if it is a regular file. */
224
 
      SVN_ERR(svn_subst_read_specialfile(&source, from_abspath, scratch_pool,
 
332
      SVN_ERR(svn_subst_read_specialfile(&source, local_abspath, scratch_pool,
225
333
                                         scratch_pool));
226
334
 
227
 
      SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, from_abspath, scratch_pool,
 
335
      SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, local_abspath, scratch_pool,
228
336
                                scratch_pool));
229
 
      SVN_ERR(svn_wc_status3(&status, wc_ctx, from_abspath, scratch_pool,
230
 
                             scratch_pool));
231
 
      if (status->text_status != svn_wc_status_normal)
 
337
      if (status->node_status != svn_wc_status_normal)
232
338
        local_mod = TRUE;
233
339
    }
234
340
 
235
341
  /* We can early-exit if we're creating a special file. */
236
 
  special = apr_hash_get(props, SVN_PROP_SPECIAL,
237
 
                         APR_HASH_KEY_STRING);
 
342
  special = svn_hash_gets(props, SVN_PROP_SPECIAL);
238
343
  if (special != NULL)
239
344
    {
240
345
      /* Create the destination as a special file, and copy the source
241
346
         details into the destination stream. */
 
347
      /* ### And forget the notification */
242
348
      SVN_ERR(svn_subst_create_specialfile(&dst_stream, to_abspath,
243
349
                                           scratch_pool, scratch_pool));
244
350
      return svn_error_trace(
246
352
    }
247
353
 
248
354
 
249
 
  eol_style = apr_hash_get(props, SVN_PROP_EOL_STYLE,
250
 
                           APR_HASH_KEY_STRING);
251
 
  keywords = apr_hash_get(props, SVN_PROP_KEYWORDS,
252
 
                          APR_HASH_KEY_STRING);
253
 
  executable = apr_hash_get(props, SVN_PROP_EXECUTABLE,
254
 
                            APR_HASH_KEY_STRING);
 
355
  eol_style = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
 
356
  keywords = svn_hash_gets(props, SVN_PROP_KEYWORDS);
 
357
  executable = svn_hash_gets(props, SVN_PROP_EXECUTABLE);
255
358
 
256
359
  if (eol_style)
257
 
    SVN_ERR(get_eol_style(&style, &eol, eol_style->data, native_eol));
 
360
    SVN_ERR(get_eol_style(&style, &eol, eol_style->data, eib->native_eol));
258
361
 
259
362
  if (local_mod)
260
363
    {
261
364
      /* Use the modified time from the working copy of
262
365
         the file */
263
 
      SVN_ERR(svn_io_file_affected_time(&tm, from_abspath, scratch_pool));
 
366
      SVN_ERR(svn_io_file_affected_time(&tm, local_abspath, scratch_pool));
264
367
    }
265
368
  else
266
369
    {
267
 
      SVN_ERR(svn_wc__node_get_changed_info(NULL, &tm, NULL, wc_ctx,
268
 
                                            from_abspath, scratch_pool,
269
 
                                            scratch_pool));
 
370
      tm = status->changed_date;
270
371
    }
271
372
 
272
373
  if (keywords)
273
374
    {
274
 
      svn_revnum_t changed_rev;
 
375
      svn_revnum_t changed_rev = status->changed_rev;
275
376
      const char *suffix;
276
 
      const char *url;
277
 
      const char *author;
278
 
 
279
 
      SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, NULL, &author,
280
 
                                            wc_ctx, from_abspath, scratch_pool,
281
 
                                            scratch_pool));
282
 
 
 
377
      const char *url = svn_path_url_add_component2(status->repos_root_url,
 
378
                                                    status->repos_relpath,
 
379
                                                    scratch_pool);
 
380
      const char *author = status->changed_author;
283
381
      if (local_mod)
284
382
        {
285
383
          /* For locally modified files, we'll append an 'M'
294
392
          suffix = "";
295
393
        }
296
394
 
297
 
      SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, from_abspath,
298
 
                                   scratch_pool, scratch_pool));
299
 
 
300
 
      SVN_ERR(svn_subst_build_keywords2
301
 
              (&kw, keywords->data,
302
 
               apr_psprintf(scratch_pool, "%ld%s", changed_rev, suffix),
303
 
               url, tm, author, scratch_pool));
 
395
      SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data,
 
396
                                        apr_psprintf(scratch_pool, "%ld%s",
 
397
                                                     changed_rev, suffix),
 
398
                                        url, status->repos_root_url, tm,
 
399
                                        author, scratch_pool));
304
400
    }
305
401
 
306
402
  /* For atomicity, we translate to a tmp file and then rename the tmp file
317
413
                                             eol,
318
414
                                             FALSE /* repair */,
319
415
                                             kw,
320
 
                                             ! ignore_keywords /* expand */,
 
416
                                             ! eib->ignore_keywords /* expand */,
321
417
                                             scratch_pool);
322
418
 
323
419
  /* ###: use cancel func/baton in place of NULL/NULL below. */
336
432
  /* Now that dst_tmp contains the translated data, do the atomic rename. */
337
433
  SVN_ERR(svn_io_file_rename(dst_tmp, to_abspath, scratch_pool));
338
434
 
339
 
  if (ctx->notify_func2)
 
435
  if (eib->notify_func)
340
436
    {
341
437
      svn_wc_notify_t *notify = svn_wc_create_notify(to_abspath,
342
438
                                      svn_wc_notify_update_add, scratch_pool);
343
439
      notify->kind = svn_node_file;
344
 
      (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
345
 
    }
346
 
 
347
 
  return SVN_NO_ERROR;
348
 
}
349
 
 
350
 
/* Make an unversioned copy of the versioned file or directory tree at the
351
 
 * source path FROM_ABSPATH.  Copy it to the destination path TO_ABSPATH.
352
 
 *
353
 
 * If REVISION is svn_opt_revision_working, copy the working version,
354
 
 * otherwise copy the base version.
355
 
 *
356
 
 * See copy_one_versioned_file() for details of file copying behaviour,
357
 
 * including IGNORE_KEYWORDS and NATIVE_EOL.
358
 
 *
359
 
 * Include externals unless IGNORE_EXTERNALS is true.
360
 
 *
361
 
 * Recurse according to DEPTH.
362
 
 *
363
 
 
364
 
 */
365
 
static svn_error_t *
366
 
copy_versioned_files(const char *from_abspath,
367
 
                     const char *to_abspath,
368
 
                     const svn_opt_revision_t *revision,
369
 
                     svn_boolean_t force,
370
 
                     svn_boolean_t ignore_externals,
371
 
                     svn_boolean_t ignore_keywords,
372
 
                     svn_depth_t depth,
373
 
                     const char *native_eol,
374
 
                     svn_client_ctx_t *ctx,
375
 
                     apr_pool_t *pool)
376
 
{
377
 
  svn_error_t *err;
378
 
  apr_pool_t *iterpool;
379
 
  const apr_array_header_t *children;
380
 
  svn_node_kind_t from_kind;
381
 
  svn_depth_t node_depth;
382
 
 
383
 
  SVN_ERR_ASSERT(svn_dirent_is_absolute(from_abspath));
384
 
  SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath));
385
 
 
386
 
  /* Only export 'added' and 'replaced' files when the revision is WORKING;
387
 
     when the revision is BASE (i.e. != WORKING), only export 'added' and
388
 
     'replaced' files when they are part of a copy-/move-here. Otherwise, skip
389
 
     them, since they don't have an associated text-base. This condition for
390
 
     added/replaced simply is an optimization. Added and replaced files would
391
 
     be handled similarly by svn_wc_get_pristine_contents2(), which would
392
 
     return NULL if they have no base associated.
393
 
     TODO: We may prefer not to duplicate this condition and rather use
394
 
     svn_wc_get_pristine_contents2() or a dedicated new function instead.
395
 
 
396
 
     Don't export 'deleted' files and directories unless it's a
397
 
     revision other than WORKING.  These files and directories
398
 
     don't really exist in WORKING. */
399
 
  if (revision->kind != svn_opt_revision_working)
400
 
    {
401
 
      svn_boolean_t is_added;
402
 
      const char *repos_relpath;
403
 
 
404
 
      SVN_ERR(svn_wc__node_get_origin(&is_added, NULL, &repos_relpath,
405
 
                                      NULL, NULL, NULL,
406
 
                                      ctx->wc_ctx, from_abspath, FALSE,
407
 
                                      pool, pool));
408
 
 
409
 
      if (is_added && !repos_relpath)
410
 
        return SVN_NO_ERROR; /* Local addition */
411
 
    }
412
 
  else
413
 
    {
414
 
      svn_boolean_t is_deleted;
415
 
 
416
 
      SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx,
417
 
                                             from_abspath, pool));
418
 
      if (is_deleted)
419
 
        return SVN_NO_ERROR;
420
 
    }
421
 
 
422
 
  SVN_ERR(svn_wc_read_kind(&from_kind, ctx->wc_ctx, from_abspath, FALSE,
423
 
                           pool));
424
 
 
425
 
  if (from_kind == svn_node_dir)
426
 
    {
427
 
      apr_fileperms_t perm = APR_OS_DEFAULT;
428
 
      int j;
429
 
 
430
 
      /* Try to make the new directory.  If this fails because the
431
 
         directory already exists, check our FORCE flag to see if we
432
 
         care. */
433
 
 
434
 
      /* Keep the source directory's permissions if applicable.
435
 
         Skip retrieving the umask on windows. Apr does not implement setting
436
 
         filesystem privileges on Windows.
437
 
         Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER
438
 
         is documented to be 'incredibly expensive' */
439
 
#ifndef WIN32
440
 
      if (revision->kind == svn_opt_revision_working)
441
 
        {
442
 
          apr_finfo_t finfo;
443
 
          SVN_ERR(svn_io_stat(&finfo, from_abspath, APR_FINFO_PROT, pool));
444
 
          perm = finfo.protection;
445
 
        }
446
 
#endif
447
 
      err = svn_io_dir_make(to_abspath, perm, pool);
448
 
      if (err)
449
 
        {
450
 
          if (! APR_STATUS_IS_EEXIST(err->apr_err))
451
 
            return svn_error_trace(err);
452
 
          if (! force)
453
 
            SVN_ERR_W(err, _("Destination directory exists, and will not be "
454
 
                             "overwritten unless forced"));
455
 
          else
456
 
            svn_error_clear(err);
457
 
        }
458
 
 
459
 
      SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx, from_abspath,
460
 
                                        FALSE, pool, pool));
461
 
 
462
 
      iterpool = svn_pool_create(pool);
463
 
      for (j = 0; j < children->nelts; j++)
464
 
        {
465
 
          const char *child_abspath = APR_ARRAY_IDX(children, j, const char *);
466
 
          const char *child_name = svn_dirent_basename(child_abspath, NULL);
467
 
          const char *target_abspath;
468
 
          svn_node_kind_t child_kind;
469
 
 
470
 
          svn_pool_clear(iterpool);
471
 
 
472
 
          if (ctx->cancel_func)
473
 
            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
474
 
 
475
 
          target_abspath = svn_dirent_join(to_abspath, child_name, iterpool);
476
 
 
477
 
          SVN_ERR(svn_wc_read_kind(&child_kind, ctx->wc_ctx, child_abspath,
478
 
                                   FALSE, iterpool));
479
 
 
480
 
          if (child_kind == svn_node_dir)
481
 
            {
482
 
              if (depth == svn_depth_infinity
483
 
                  || depth == svn_depth_immediates)
484
 
                {
485
 
                  if (ctx->notify_func2)
486
 
                    {
487
 
                      svn_wc_notify_t *notify =
488
 
                          svn_wc_create_notify(target_abspath,
489
 
                                               svn_wc_notify_update_add, pool);
490
 
                      notify->kind = svn_node_dir;
491
 
                      (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
492
 
                    }
493
 
 
494
 
                  if (depth == svn_depth_infinity)
495
 
                    SVN_ERR(copy_versioned_files(child_abspath, target_abspath,
496
 
                                                 revision, force,
497
 
                                                 ignore_externals,
498
 
                                                 ignore_keywords, depth,
499
 
                                                 native_eol, ctx, iterpool));
500
 
                  else
501
 
                    SVN_ERR(svn_io_make_dir_recursively(target_abspath,
502
 
                                                        iterpool));
503
 
                }
504
 
            }
505
 
          else if (child_kind == svn_node_file
506
 
                   && depth >= svn_depth_files)
507
 
            {
508
 
              svn_node_kind_t external_kind;
509
 
 
510
 
              SVN_ERR(svn_wc__read_external_info(&external_kind,
511
 
                                                 NULL, NULL, NULL,
512
 
                                                 NULL, ctx->wc_ctx,
513
 
                                                 child_abspath,
514
 
                                                 child_abspath, TRUE,
515
 
                                                 pool, pool));
516
 
 
517
 
              if (external_kind != svn_node_file)
518
 
                SVN_ERR(copy_one_versioned_file(child_abspath, target_abspath,
519
 
                                                ctx, revision,
520
 
                                                native_eol, ignore_keywords,
521
 
                                                iterpool));
522
 
            }
523
 
        }
524
 
 
525
 
      SVN_ERR(svn_wc__node_get_depth(&node_depth, ctx->wc_ctx,
526
 
                                     from_abspath, pool));
527
 
 
528
 
      /* Handle externals. */
529
 
      if (! ignore_externals && depth == svn_depth_infinity
530
 
          && node_depth == svn_depth_infinity)
531
 
        {
532
 
          apr_array_header_t *ext_items;
533
 
          const svn_string_t *prop_val;
534
 
 
535
 
          SVN_ERR(svn_wc_prop_get2(&prop_val, ctx->wc_ctx, from_abspath,
536
 
                                   SVN_PROP_EXTERNALS, pool, pool));
537
 
          if (prop_val != NULL)
538
 
            {
539
 
              int i;
540
 
 
541
 
              SVN_ERR(svn_wc_parse_externals_description3(&ext_items,
542
 
                                                          from_abspath,
543
 
                                                          prop_val->data,
544
 
                                                          FALSE, pool));
545
 
              for (i = 0; i < ext_items->nelts; ++i)
546
 
                {
547
 
                  svn_wc_external_item2_t *ext_item;
548
 
                  const char *new_from, *new_to;
549
 
 
550
 
                  svn_pool_clear(iterpool);
551
 
 
552
 
                  ext_item = APR_ARRAY_IDX(ext_items, i,
553
 
                                           svn_wc_external_item2_t *);
554
 
                  new_from = svn_dirent_join(from_abspath,
555
 
                                             ext_item->target_dir,
556
 
                                             iterpool);
557
 
                  new_to = svn_dirent_join(to_abspath, ext_item->target_dir,
558
 
                                           iterpool);
559
 
 
560
 
                   /* The target dir might have parents that don't exist.
561
 
                      Guarantee the path upto the last component. */
562
 
                  if (!svn_dirent_is_root(ext_item->target_dir,
563
 
                                          strlen(ext_item->target_dir)))
564
 
                    {
565
 
                      const char *parent = svn_dirent_dirname(new_to, iterpool);
566
 
                      SVN_ERR(svn_io_make_dir_recursively(parent, iterpool));
567
 
                    }
568
 
 
569
 
                  SVN_ERR(copy_versioned_files(new_from, new_to,
570
 
                                               revision, force, FALSE,
571
 
                                               ignore_keywords,
572
 
                                               svn_depth_infinity, native_eol,
573
 
                                               ctx, iterpool));
574
 
                }
575
 
            }
576
 
        }
577
 
 
578
 
      svn_pool_destroy(iterpool);
579
 
    }
580
 
  else if (from_kind == svn_node_file)
581
 
    {
582
 
      svn_node_kind_t to_kind;
583
 
 
584
 
      SVN_ERR(svn_io_check_path(to_abspath, &to_kind, pool));
585
 
 
586
 
      if ((to_kind == svn_node_file || to_kind == svn_node_unknown) && ! force)
587
 
        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
588
 
                                 _("Destination file '%s' exists, and "
589
 
                                   "will not be overwritten unless forced"),
590
 
                                 svn_dirent_local_style(to_abspath, pool));
591
 
      else if (to_kind == svn_node_dir)
592
 
        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
593
 
                                 _("Destination '%s' exists. Cannot "
594
 
                                   "overwrite directory with non-directory"),
595
 
                                 svn_dirent_local_style(to_abspath, pool));
596
 
 
597
 
      SVN_ERR(copy_one_versioned_file(from_abspath, to_abspath, ctx,
598
 
                                      revision, native_eol, ignore_keywords,
599
 
                                      pool));
600
 
    }
601
 
 
602
 
  return SVN_NO_ERROR;
603
 
}
604
 
 
 
440
      (eib->notify_func)(eib->notify_baton, notify, scratch_pool);
 
441
    }
 
442
 
 
443
  return SVN_NO_ERROR;
 
444
}
605
445
 
606
446
/* Abstraction of open_root.
607
447
 *
656
496
 
657
497
struct edit_baton
658
498
{
 
499
  const char *repos_root_url;
659
500
  const char *root_path;
660
501
  const char *root_url;
661
502
  svn_boolean_t force;
702
543
  /* Any keyword vals to be substituted */
703
544
  const char *revision;
704
545
  const char *url;
 
546
  const char *repos_root_url;
705
547
  const char *author;
706
548
  apr_time_t date;
707
549
 
823
665
  fb->edit_baton = eb;
824
666
  fb->path = full_path;
825
667
  fb->url = full_url;
 
668
  fb->repos_root_url = eb->repos_root_url;
826
669
  fb->pool = pool;
827
670
 
828
671
  *baton = fb;
840
683
  if (err)
841
684
    {
842
685
      /* We failed to apply the patch; clean up the temporary file.  */
843
 
      svn_error_clear(svn_io_remove_file2(hb->tmppath, TRUE, hb->pool));
 
686
      err = svn_error_compose_create(
 
687
                    err,
 
688
                    svn_io_remove_file2(hb->tmppath, TRUE, hb->pool));
844
689
    }
845
690
 
846
691
  return svn_error_trace(err);
957
802
 
958
803
  SVN_ERR(svn_checksum_parse_hex(&text_checksum, svn_checksum_md5, text_digest,
959
804
                                 pool));
960
 
  actual_checksum = svn_checksum__from_digest(fb->text_digest,
961
 
                                              svn_checksum_md5, pool);
 
805
  actual_checksum = svn_checksum__from_digest_md5(fb->text_digest, pool);
962
806
 
963
807
  /* Note that text_digest can be NULL when talking to certain repositories.
964
808
     In that case text_checksum will be NULL and the following match code
987
831
        }
988
832
 
989
833
      if (fb->keywords_val)
990
 
        SVN_ERR(svn_subst_build_keywords2(&final_kw, fb->keywords_val->data,
991
 
                                          fb->revision, fb->url, fb->date,
 
834
        SVN_ERR(svn_subst_build_keywords3(&final_kw, fb->keywords_val->data,
 
835
                                          fb->revision, fb->url,
 
836
                                          fb->repos_root_url, fb->date,
992
837
                                          fb->author, pool));
993
838
 
994
839
      SVN_ERR(svn_subst_copy_and_translate4(fb->tmppath, fb->path,
1020
865
  return SVN_NO_ERROR;
1021
866
}
1022
867
 
 
868
static svn_error_t *
 
869
fetch_props_func(apr_hash_t **props,
 
870
                 void *baton,
 
871
                 const char *path,
 
872
                 svn_revnum_t base_revision,
 
873
                 apr_pool_t *result_pool,
 
874
                 apr_pool_t *scratch_pool)
 
875
{
 
876
  /* Always use empty props, since the node won't have pre-existing props
 
877
     (This is an export, remember?) */
 
878
  *props = apr_hash_make(result_pool);
 
879
 
 
880
  return SVN_NO_ERROR;
 
881
}
 
882
 
 
883
static svn_error_t *
 
884
fetch_base_func(const char **filename,
 
885
                void *baton,
 
886
                const char *path,
 
887
                svn_revnum_t base_revision,
 
888
                apr_pool_t *result_pool,
 
889
                apr_pool_t *scratch_pool)
 
890
{
 
891
  /* An export always gets text against the empty stream (i.e, full texts). */
 
892
  *filename = NULL;
 
893
 
 
894
  return SVN_NO_ERROR;
 
895
}
 
896
 
 
897
static svn_error_t *
 
898
get_editor_ev1(const svn_delta_editor_t **export_editor,
 
899
               void **edit_baton,
 
900
               struct edit_baton *eb,
 
901
               svn_client_ctx_t *ctx,
 
902
               apr_pool_t *result_pool,
 
903
               apr_pool_t *scratch_pool)
 
904
{
 
905
  svn_delta_editor_t *editor = svn_delta_default_editor(result_pool);
 
906
 
 
907
  editor->set_target_revision = set_target_revision;
 
908
  editor->open_root = open_root;
 
909
  editor->add_directory = add_directory;
 
910
  editor->add_file = add_file;
 
911
  editor->apply_textdelta = apply_textdelta;
 
912
  editor->close_file = close_file;
 
913
  editor->change_file_prop = change_file_prop;
 
914
  editor->change_dir_prop = change_dir_prop;
 
915
 
 
916
  SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func,
 
917
                                            ctx->cancel_baton,
 
918
                                            editor,
 
919
                                            eb,
 
920
                                            export_editor,
 
921
                                            edit_baton,
 
922
                                            result_pool));
 
923
 
 
924
  return SVN_NO_ERROR;
 
925
}
 
926
 
 
927
 
 
928
/*** The Ev2 Implementation ***/
 
929
 
 
930
static svn_error_t *
 
931
add_file_ev2(void *baton,
 
932
             const char *relpath,
 
933
             const svn_checksum_t *checksum,
 
934
             svn_stream_t *contents,
 
935
             apr_hash_t *props,
 
936
             svn_revnum_t replaces_rev,
 
937
             apr_pool_t *scratch_pool)
 
938
{
 
939
  struct edit_baton *eb = baton;
 
940
  const char *full_path = svn_dirent_join(eb->root_path, relpath,
 
941
                                          scratch_pool);
 
942
  /* RELPATH is not canonicalized, i.e. it may still contain spaces etc.
 
943
   * but EB->root_url is. */
 
944
  const char *full_url = svn_path_url_add_component2(eb->root_url,
 
945
                                                     relpath,
 
946
                                                     scratch_pool);
 
947
  const svn_string_t *val;
 
948
  /* The four svn: properties we might actually care about. */
 
949
  const svn_string_t *eol_style_val = NULL;
 
950
  const svn_string_t *keywords_val = NULL;
 
951
  const svn_string_t *executable_val = NULL;
 
952
  svn_boolean_t special = FALSE;
 
953
  /* Any keyword vals to be substituted */
 
954
  const char *revision = NULL;
 
955
  const char *author = NULL;
 
956
  apr_time_t date = 0;
 
957
 
 
958
  /* Look at any properties for additional information. */
 
959
  if ( (val = svn_hash_gets(props, SVN_PROP_EOL_STYLE)) )
 
960
    eol_style_val = val;
 
961
 
 
962
  if ( !eb->ignore_keywords && (val = svn_hash_gets(props, SVN_PROP_KEYWORDS)) )
 
963
    keywords_val = val;
 
964
 
 
965
  if ( (val = svn_hash_gets(props, SVN_PROP_EXECUTABLE)) )
 
966
    executable_val = val;
 
967
 
 
968
  /* Try to fill out the baton's keywords-structure too. */
 
969
  if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_REV)) )
 
970
    revision = val->data;
 
971
 
 
972
  if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_DATE)) )
 
973
    SVN_ERR(svn_time_from_cstring(&date, val->data, scratch_pool));
 
974
 
 
975
  if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_LAST_AUTHOR)) )
 
976
    author = val->data;
 
977
 
 
978
  if ( (val = svn_hash_gets(props, SVN_PROP_SPECIAL)) )
 
979
    special = TRUE;
 
980
 
 
981
  if (special)
 
982
    {
 
983
      svn_stream_t *tmp_stream;
 
984
 
 
985
      SVN_ERR(svn_subst_create_specialfile(&tmp_stream, full_path,
 
986
                                           scratch_pool, scratch_pool));
 
987
      SVN_ERR(svn_stream_copy3(contents, tmp_stream, eb->cancel_func,
 
988
                               eb->cancel_baton, scratch_pool));
 
989
    }
 
990
  else
 
991
    {
 
992
      svn_stream_t *tmp_stream;
 
993
      const char *tmppath;
 
994
 
 
995
      /* Create a temporary file in the same directory as the file. We're going
 
996
         to rename the thing into place when we're done. */
 
997
      SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmppath,
 
998
                                     svn_dirent_dirname(full_path,
 
999
                                                        scratch_pool),
 
1000
                                     svn_io_file_del_none,
 
1001
                                     scratch_pool, scratch_pool));
 
1002
 
 
1003
      /* Possibly wrap the stream to be translated, as dictated by
 
1004
         the props. */
 
1005
      if (eol_style_val || keywords_val)
 
1006
        {
 
1007
          svn_subst_eol_style_t style;
 
1008
          const char *eol = NULL;
 
1009
          svn_boolean_t repair = FALSE;
 
1010
          apr_hash_t *final_kw = NULL;
 
1011
 
 
1012
          if (eol_style_val)
 
1013
            {
 
1014
              SVN_ERR(get_eol_style(&style, &eol, eol_style_val->data,
 
1015
                                    eb->native_eol));
 
1016
              repair = TRUE;
 
1017
            }
 
1018
 
 
1019
          if (keywords_val)
 
1020
            SVN_ERR(svn_subst_build_keywords3(&final_kw, keywords_val->data,
 
1021
                                              revision, full_url,
 
1022
                                              eb->repos_root_url,
 
1023
                                              date, author, scratch_pool));
 
1024
 
 
1025
          /* Writing through a translated stream is more efficient than
 
1026
             reading through one, so we wrap TMP_STREAM and not CONTENTS. */
 
1027
          tmp_stream = svn_subst_stream_translated(tmp_stream, eol, repair,
 
1028
                                                   final_kw, TRUE, /* expand */
 
1029
                                                   scratch_pool);
 
1030
        }
 
1031
 
 
1032
      SVN_ERR(svn_stream_copy3(contents, tmp_stream, eb->cancel_func,
 
1033
                               eb->cancel_baton, scratch_pool));
 
1034
 
 
1035
      /* Move the file into place. */
 
1036
      SVN_ERR(svn_io_file_rename(tmppath, full_path, scratch_pool));
 
1037
    }
 
1038
 
 
1039
  if (executable_val)
 
1040
    SVN_ERR(svn_io_set_file_executable(full_path, TRUE, FALSE, scratch_pool));
 
1041
 
 
1042
  if (date && (! special))
 
1043
    SVN_ERR(svn_io_set_file_affected_time(date, full_path, scratch_pool));
 
1044
 
 
1045
  if (eb->notify_func)
 
1046
    {
 
1047
      svn_wc_notify_t *notify = svn_wc_create_notify(full_path,
 
1048
                                                     svn_wc_notify_update_add,
 
1049
                                                     scratch_pool);
 
1050
      notify->kind = svn_node_file;
 
1051
      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
 
1052
    }
 
1053
 
 
1054
  return SVN_NO_ERROR;
 
1055
}
 
1056
 
 
1057
static svn_error_t *
 
1058
add_directory_ev2(void *baton,
 
1059
                  const char *relpath,
 
1060
                  const apr_array_header_t *children,
 
1061
                  apr_hash_t *props,
 
1062
                  svn_revnum_t replaces_rev,
 
1063
                  apr_pool_t *scratch_pool)
 
1064
{
 
1065
  struct edit_baton *eb = baton;
 
1066
  svn_node_kind_t kind;
 
1067
  const char *full_path = svn_dirent_join(eb->root_path, relpath,
 
1068
                                          scratch_pool);
 
1069
  svn_string_t *val;
 
1070
 
 
1071
  SVN_ERR(svn_io_check_path(full_path, &kind, scratch_pool));
 
1072
  if (kind == svn_node_none)
 
1073
    SVN_ERR(svn_io_dir_make(full_path, APR_OS_DEFAULT, scratch_pool));
 
1074
  else if (kind == svn_node_file)
 
1075
    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
 
1076
                             _("'%s' exists and is not a directory"),
 
1077
                             svn_dirent_local_style(full_path, scratch_pool));
 
1078
  else if (! (kind == svn_node_dir && eb->force))
 
1079
    return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
 
1080
                             _("'%s' already exists"),
 
1081
                             svn_dirent_local_style(full_path, scratch_pool));
 
1082
 
 
1083
  if ( (val = svn_hash_gets(props, SVN_PROP_EXTERNALS)) )
 
1084
    SVN_ERR(add_externals(eb->externals, full_path, val));
 
1085
 
 
1086
  if (eb->notify_func)
 
1087
    {
 
1088
      svn_wc_notify_t *notify = svn_wc_create_notify(full_path,
 
1089
                                                     svn_wc_notify_update_add,
 
1090
                                                     scratch_pool);
 
1091
      notify->kind = svn_node_dir;
 
1092
      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
 
1093
    }
 
1094
 
 
1095
  return SVN_NO_ERROR;
 
1096
}
 
1097
 
 
1098
static svn_error_t *
 
1099
target_revision_func(void *baton,
 
1100
                     svn_revnum_t target_revision,
 
1101
                     apr_pool_t *scratch_pool)
 
1102
{
 
1103
  struct edit_baton *eb = baton;
 
1104
 
 
1105
  *eb->target_revision = target_revision;
 
1106
 
 
1107
  return SVN_NO_ERROR;
 
1108
}
 
1109
 
 
1110
static svn_error_t *
 
1111
get_editor_ev2(const svn_delta_editor_t **export_editor,
 
1112
               void **edit_baton,
 
1113
               struct edit_baton *eb,
 
1114
               svn_client_ctx_t *ctx,
 
1115
               apr_pool_t *result_pool,
 
1116
               apr_pool_t *scratch_pool)
 
1117
{
 
1118
  svn_editor_t *editor;
 
1119
  struct svn_delta__extra_baton *exb = apr_pcalloc(result_pool, sizeof(*exb));
 
1120
  svn_boolean_t *found_abs_paths = apr_palloc(result_pool,
 
1121
                                              sizeof(*found_abs_paths));
 
1122
 
 
1123
  exb->baton = eb;
 
1124
  exb->target_revision = target_revision_func;
 
1125
 
 
1126
  SVN_ERR(svn_editor_create(&editor, eb, ctx->cancel_func, ctx->cancel_baton,
 
1127
                            result_pool, scratch_pool));
 
1128
  SVN_ERR(svn_editor_setcb_add_directory(editor, add_directory_ev2,
 
1129
                                         scratch_pool));
 
1130
  SVN_ERR(svn_editor_setcb_add_file(editor, add_file_ev2, scratch_pool));
 
1131
 
 
1132
  *found_abs_paths = TRUE;
 
1133
 
 
1134
  SVN_ERR(svn_delta__delta_from_editor(export_editor, edit_baton,
 
1135
                                       editor, NULL, NULL, found_abs_paths,
 
1136
                                       NULL, NULL,
 
1137
                                       fetch_props_func, eb,
 
1138
                                       fetch_base_func, eb,
 
1139
                                       exb, result_pool));
 
1140
 
 
1141
  /* Create the root of the export. */
 
1142
  SVN_ERR(open_root_internal(eb->root_path, eb->force, eb->notify_func,
 
1143
                             eb->notify_baton, scratch_pool));
 
1144
 
 
1145
  return SVN_NO_ERROR;
 
1146
}
 
1147
 
 
1148
static svn_error_t *
 
1149
export_file_ev2(const char *from_path_or_url,
 
1150
                const char *to_path,
 
1151
                struct edit_baton *eb,
 
1152
                svn_client__pathrev_t *loc,
 
1153
                svn_ra_session_t *ra_session,
 
1154
                svn_boolean_t overwrite,
 
1155
                apr_pool_t *scratch_pool)
 
1156
{
 
1157
  svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
 
1158
  apr_hash_t *props;
 
1159
  svn_stream_t *tmp_stream;
 
1160
  svn_node_kind_t to_kind;
 
1161
 
 
1162
  if (svn_path_is_empty(to_path))
 
1163
    {
 
1164
      if (from_is_url)
 
1165
        to_path = svn_uri_basename(from_path_or_url, scratch_pool);
 
1166
      else
 
1167
        to_path = svn_dirent_basename(from_path_or_url, NULL);
 
1168
      eb->root_path = to_path;
 
1169
    }
 
1170
  else
 
1171
    {
 
1172
      SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url,
 
1173
                                     from_is_url, scratch_pool));
 
1174
      eb->root_path = to_path;
 
1175
    }
 
1176
 
 
1177
  SVN_ERR(svn_io_check_path(to_path, &to_kind, scratch_pool));
 
1178
 
 
1179
  if ((to_kind == svn_node_file || to_kind == svn_node_unknown) &&
 
1180
      ! overwrite)
 
1181
    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
 
1182
                             _("Destination file '%s' exists, and "
 
1183
                               "will not be overwritten unless forced"),
 
1184
                             svn_dirent_local_style(to_path, scratch_pool));
 
1185
  else if (to_kind == svn_node_dir)
 
1186
    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
 
1187
                             _("Destination '%s' exists. Cannot "
 
1188
                               "overwrite directory with non-directory"),
 
1189
                             svn_dirent_local_style(to_path, scratch_pool));
 
1190
 
 
1191
  tmp_stream = svn_stream_buffered(scratch_pool);
 
1192
 
 
1193
  SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
 
1194
                          tmp_stream, NULL, &props, scratch_pool));
 
1195
 
 
1196
  /* Since you cannot actually root an editor at a file, we manually drive
 
1197
   * a function of our editor. */
 
1198
  SVN_ERR(add_file_ev2(eb, "", NULL, tmp_stream, props, SVN_INVALID_REVNUM,
 
1199
                       scratch_pool));
 
1200
 
 
1201
  return SVN_NO_ERROR;
 
1202
}
 
1203
 
 
1204
static svn_error_t *
 
1205
export_file(const char *from_path_or_url,
 
1206
            const char *to_path,
 
1207
            struct edit_baton *eb,
 
1208
            svn_client__pathrev_t *loc,
 
1209
            svn_ra_session_t *ra_session,
 
1210
            svn_boolean_t overwrite,
 
1211
            apr_pool_t *scratch_pool)
 
1212
{
 
1213
  apr_hash_t *props;
 
1214
  apr_hash_index_t *hi;
 
1215
  struct file_baton *fb = apr_pcalloc(scratch_pool, sizeof(*fb));
 
1216
  svn_node_kind_t to_kind;
 
1217
  svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
 
1218
 
 
1219
  if (svn_path_is_empty(to_path))
 
1220
    {
 
1221
      if (from_is_url)
 
1222
        to_path = svn_uri_basename(from_path_or_url, scratch_pool);
 
1223
      else
 
1224
        to_path = svn_dirent_basename(from_path_or_url, NULL);
 
1225
      eb->root_path = to_path;
 
1226
    }
 
1227
  else
 
1228
    {
 
1229
      SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url,
 
1230
                                     from_is_url, scratch_pool));
 
1231
      eb->root_path = to_path;
 
1232
    }
 
1233
 
 
1234
  SVN_ERR(svn_io_check_path(to_path, &to_kind, scratch_pool));
 
1235
 
 
1236
  if ((to_kind == svn_node_file || to_kind == svn_node_unknown) &&
 
1237
      ! overwrite)
 
1238
    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
 
1239
                             _("Destination file '%s' exists, and "
 
1240
                               "will not be overwritten unless forced"),
 
1241
                             svn_dirent_local_style(to_path, scratch_pool));
 
1242
  else if (to_kind == svn_node_dir)
 
1243
    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
 
1244
                             _("Destination '%s' exists. Cannot "
 
1245
                               "overwrite directory with non-directory"),
 
1246
                             svn_dirent_local_style(to_path, scratch_pool));
 
1247
 
 
1248
  /* Since you cannot actually root an editor at a file, we
 
1249
   * manually drive a few functions of our editor. */
 
1250
 
 
1251
  /* This is the equivalent of a parentless add_file(). */
 
1252
  fb->edit_baton = eb;
 
1253
  fb->path = eb->root_path;
 
1254
  fb->url = eb->root_url;
 
1255
  fb->pool = scratch_pool;
 
1256
  fb->repos_root_url = eb->repos_root_url;
 
1257
 
 
1258
  /* Copied from apply_textdelta(). */
 
1259
  SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath,
 
1260
                                 svn_dirent_dirname(fb->path, scratch_pool),
 
1261
                                 svn_io_file_del_none,
 
1262
                                 fb->pool, fb->pool));
 
1263
 
 
1264
  /* Step outside the editor-likeness for a moment, to actually talk
 
1265
   * to the repository. */
 
1266
  /* ### note: the stream will not be closed */
 
1267
  SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
 
1268
                          fb->tmp_stream,
 
1269
                          NULL, &props, scratch_pool));
 
1270
 
 
1271
  /* Push the props into change_file_prop(), to update the file_baton
 
1272
   * with information. */
 
1273
  for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi))
 
1274
    {
 
1275
      const char *propname = svn__apr_hash_index_key(hi);
 
1276
      const svn_string_t *propval = svn__apr_hash_index_val(hi);
 
1277
 
 
1278
      SVN_ERR(change_file_prop(fb, propname, propval, scratch_pool));
 
1279
    }
 
1280
 
 
1281
  /* And now just use close_file() to do all the keyword and EOL
 
1282
   * work, and put the file into place. */
 
1283
  SVN_ERR(close_file(fb, NULL, scratch_pool));
 
1284
 
 
1285
  return SVN_NO_ERROR;
 
1286
}
 
1287
 
 
1288
static svn_error_t *
 
1289
export_directory(const char *from_path_or_url,
 
1290
                 const char *to_path,
 
1291
                 struct edit_baton *eb,
 
1292
                 svn_client__pathrev_t *loc,
 
1293
                 svn_ra_session_t *ra_session,
 
1294
                 svn_boolean_t overwrite,
 
1295
                 svn_boolean_t ignore_externals,
 
1296
                 svn_boolean_t ignore_keywords,
 
1297
                 svn_depth_t depth,
 
1298
                 const char *native_eol,
 
1299
                 svn_client_ctx_t *ctx,
 
1300
                 apr_pool_t *scratch_pool)
 
1301
{
 
1302
  void *edit_baton;
 
1303
  const svn_delta_editor_t *export_editor;
 
1304
  const svn_ra_reporter3_t *reporter;
 
1305
  void *report_baton;
 
1306
  svn_node_kind_t kind;
 
1307
 
 
1308
  if (!ENABLE_EV2_IMPL)
 
1309
    SVN_ERR(get_editor_ev1(&export_editor, &edit_baton, eb, ctx,
 
1310
                           scratch_pool, scratch_pool));
 
1311
  else
 
1312
    SVN_ERR(get_editor_ev2(&export_editor, &edit_baton, eb, ctx,
 
1313
                           scratch_pool, scratch_pool));
 
1314
 
 
1315
  /* Manufacture a basic 'report' to the update reporter. */
 
1316
  SVN_ERR(svn_ra_do_update3(ra_session,
 
1317
                            &reporter, &report_baton,
 
1318
                            loc->rev,
 
1319
                            "", /* no sub-target */
 
1320
                            depth,
 
1321
                            FALSE, /* don't want copyfrom-args */
 
1322
                            FALSE, /* don't want ignore_ancestry */
 
1323
                            export_editor, edit_baton,
 
1324
                            scratch_pool, scratch_pool));
 
1325
 
 
1326
  SVN_ERR(reporter->set_path(report_baton, "", loc->rev,
 
1327
                             /* Depth is irrelevant, as we're
 
1328
                                passing start_empty=TRUE anyway. */
 
1329
                             svn_depth_infinity,
 
1330
                             TRUE, /* "help, my dir is empty!" */
 
1331
                             NULL, scratch_pool));
 
1332
 
 
1333
  SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
 
1334
 
 
1335
  /* Special case: Due to our sly export/checkout method of updating an
 
1336
   * empty directory, no target will have been created if the exported
 
1337
   * item is itself an empty directory (export_editor->open_root never
 
1338
   * gets called, because there are no "changes" to make to the empty
 
1339
   * dir we reported to the repository).
 
1340
   *
 
1341
   * So we just create the empty dir manually; but we do it via
 
1342
   * open_root_internal(), in order to get proper notification.
 
1343
   */
 
1344
  SVN_ERR(svn_io_check_path(to_path, &kind, scratch_pool));
 
1345
  if (kind == svn_node_none)
 
1346
    SVN_ERR(open_root_internal
 
1347
            (to_path, overwrite, ctx->notify_func2,
 
1348
             ctx->notify_baton2, scratch_pool));
 
1349
 
 
1350
  if (! ignore_externals && depth == svn_depth_infinity)
 
1351
    {
 
1352
      const char *to_abspath;
 
1353
 
 
1354
      SVN_ERR(svn_dirent_get_absolute(&to_abspath, to_path, scratch_pool));
 
1355
      SVN_ERR(svn_client__export_externals(eb->externals,
 
1356
                                           from_path_or_url,
 
1357
                                           to_abspath, eb->repos_root_url,
 
1358
                                           depth, native_eol,
 
1359
                                           ignore_keywords,
 
1360
                                           ctx, scratch_pool));
 
1361
    }
 
1362
 
 
1363
  return SVN_NO_ERROR;
 
1364
}
 
1365
 
1023
1366
 
1024
1367
 
1025
1368
/*** Public Interfaces ***/
1039
1382
                   apr_pool_t *pool)
1040
1383
{
1041
1384
  svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
1042
 
  const char *url;
1043
1385
  svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
1044
1386
 
1045
1387
  SVN_ERR_ASSERT(peg_revision != NULL);
1055
1397
 
1056
1398
  if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
1057
1399
    {
1058
 
      svn_revnum_t revnum;
 
1400
      svn_client__pathrev_t *loc;
1059
1401
      svn_ra_session_t *ra_session;
1060
1402
      svn_node_kind_t kind;
1061
1403
      struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
1062
 
      const char *repos_root_url;
1063
1404
 
1064
1405
      /* Get the RA connection. */
1065
 
      SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum,
1066
 
                                               &url, from_path_or_url, NULL,
1067
 
                                               peg_revision,
1068
 
                                               revision, ctx, pool));
1069
 
 
1070
 
      /* Get the repository root. */
1071
 
      SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
1072
 
 
 
1406
      SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
 
1407
                                                from_path_or_url, NULL,
 
1408
                                                peg_revision,
 
1409
                                                revision, ctx, pool));
 
1410
 
 
1411
      SVN_ERR(svn_ra_get_repos_root2(ra_session, &eb->repos_root_url, pool));
1073
1412
      eb->root_path = to_path;
1074
 
      eb->root_url = url;
 
1413
      eb->root_url = loc->url;
1075
1414
      eb->force = overwrite;
1076
1415
      eb->target_revision = &edit_revision;
1077
1416
      eb->externals = apr_hash_make(pool);
1082
1421
      eb->notify_func = ctx->notify_func2;
1083
1422
      eb->notify_baton = ctx->notify_baton2;
1084
1423
 
1085
 
      SVN_ERR(svn_ra_check_path(ra_session, "", revnum, &kind, pool));
 
1424
      SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, pool));
1086
1425
 
1087
1426
      if (kind == svn_node_file)
1088
1427
        {
1089
 
          apr_hash_t *props;
1090
 
          apr_hash_index_t *hi;
1091
 
          struct file_baton *fb = apr_pcalloc(pool, sizeof(*fb));
1092
 
          svn_node_kind_t to_kind;
1093
 
 
1094
 
          if (svn_path_is_empty(to_path))
1095
 
            {
1096
 
              if (from_is_url)
1097
 
                to_path = svn_uri_basename(from_path_or_url, pool);
1098
 
              else
1099
 
                to_path = svn_dirent_basename(from_path_or_url, NULL);
1100
 
              eb->root_path = to_path;
1101
 
            }
 
1428
          if (!ENABLE_EV2_IMPL)
 
1429
            SVN_ERR(export_file(from_path_or_url, to_path, eb, loc, ra_session,
 
1430
                                overwrite, pool));
1102
1431
          else
1103
 
            {
1104
 
              SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url,
1105
 
                                             from_is_url, pool));
1106
 
              eb->root_path = to_path;
1107
 
            }
1108
 
 
1109
 
          SVN_ERR(svn_io_check_path(to_path, &to_kind, pool));
1110
 
 
1111
 
          if ((to_kind == svn_node_file || to_kind == svn_node_unknown) &&
1112
 
              ! overwrite)
1113
 
            return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1114
 
                                     _("Destination file '%s' exists, and "
1115
 
                                       "will not be overwritten unless forced"),
1116
 
                                     svn_dirent_local_style(to_path, pool));
1117
 
          else if (to_kind == svn_node_dir)
1118
 
            return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1119
 
                                     _("Destination '%s' exists. Cannot "
1120
 
                                       "overwrite directory with non-directory"),
1121
 
                                     svn_dirent_local_style(to_path, pool));
1122
 
 
1123
 
          /* Since you cannot actually root an editor at a file, we
1124
 
           * manually drive a few functions of our editor. */
1125
 
 
1126
 
          /* This is the equivalent of a parentless add_file(). */
1127
 
          fb->edit_baton = eb;
1128
 
          fb->path = eb->root_path;
1129
 
          fb->url = eb->root_url;
1130
 
          fb->pool = pool;
1131
 
 
1132
 
          /* Copied from apply_textdelta(). */
1133
 
          SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath,
1134
 
                                         svn_dirent_dirname(fb->path, pool),
1135
 
                                         svn_io_file_del_none,
1136
 
                                         fb->pool, fb->pool));
1137
 
 
1138
 
          /* Step outside the editor-likeness for a moment, to actually talk
1139
 
           * to the repository. */
1140
 
          /* ### note: the stream will not be closed */
1141
 
          SVN_ERR(svn_ra_get_file(ra_session, "", revnum,
1142
 
                                  fb->tmp_stream,
1143
 
                                  NULL, &props, pool));
1144
 
 
1145
 
          /* Push the props into change_file_prop(), to update the file_baton
1146
 
           * with information. */
1147
 
          for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
1148
 
            {
1149
 
              const char *propname = svn__apr_hash_index_key(hi);
1150
 
              const svn_string_t *propval = svn__apr_hash_index_val(hi);
1151
 
 
1152
 
              SVN_ERR(change_file_prop(fb, propname, propval, pool));
1153
 
            }
1154
 
 
1155
 
          /* And now just use close_file() to do all the keyword and EOL
1156
 
           * work, and put the file into place. */
1157
 
          SVN_ERR(close_file(fb, NULL, pool));
 
1432
            SVN_ERR(export_file_ev2(from_path_or_url, to_path, eb, loc,
 
1433
                                    ra_session, overwrite, pool));
1158
1434
        }
1159
1435
      else if (kind == svn_node_dir)
1160
1436
        {
1161
 
          void *edit_baton;
1162
 
          const svn_delta_editor_t *export_editor;
1163
 
          const svn_ra_reporter3_t *reporter;
1164
 
          void *report_baton;
1165
 
          svn_delta_editor_t *editor = svn_delta_default_editor(pool);
1166
 
          svn_boolean_t use_sleep = FALSE;
1167
 
 
1168
 
          editor->set_target_revision = set_target_revision;
1169
 
          editor->open_root = open_root;
1170
 
          editor->add_directory = add_directory;
1171
 
          editor->add_file = add_file;
1172
 
          editor->apply_textdelta = apply_textdelta;
1173
 
          editor->close_file = close_file;
1174
 
          editor->change_file_prop = change_file_prop;
1175
 
          editor->change_dir_prop = change_dir_prop;
1176
 
 
1177
 
          SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func,
1178
 
                                                    ctx->cancel_baton,
1179
 
                                                    editor,
1180
 
                                                    eb,
1181
 
                                                    &export_editor,
1182
 
                                                    &edit_baton,
1183
 
                                                    pool));
1184
 
 
1185
 
 
1186
 
          /* Manufacture a basic 'report' to the update reporter. */
1187
 
          SVN_ERR(svn_ra_do_update2(ra_session,
1188
 
                                    &reporter, &report_baton,
1189
 
                                    revnum,
1190
 
                                    "", /* no sub-target */
1191
 
                                    depth,
1192
 
                                    FALSE, /* don't want copyfrom-args */
1193
 
                                    export_editor, edit_baton, pool));
1194
 
 
1195
 
          SVN_ERR(reporter->set_path(report_baton, "", revnum,
1196
 
                                     /* Depth is irrelevant, as we're
1197
 
                                        passing start_empty=TRUE anyway. */
1198
 
                                     svn_depth_infinity,
1199
 
                                     TRUE, /* "help, my dir is empty!" */
1200
 
                                     NULL, pool));
1201
 
 
1202
 
          SVN_ERR(reporter->finish_report(report_baton, pool));
1203
 
 
1204
 
          /* Special case: Due to our sly export/checkout method of
1205
 
           * updating an empty directory, no target will have been created
1206
 
           * if the exported item is itself an empty directory
1207
 
           * (export_editor->open_root never gets called, because there
1208
 
           * are no "changes" to make to the empty dir we reported to the
1209
 
           * repository).
1210
 
           *
1211
 
           * So we just create the empty dir manually; but we do it via
1212
 
           * open_root_internal(), in order to get proper notification.
1213
 
           */
1214
 
          SVN_ERR(svn_io_check_path(to_path, &kind, pool));
1215
 
          if (kind == svn_node_none)
1216
 
            SVN_ERR(open_root_internal
1217
 
                    (to_path, overwrite, ctx->notify_func2,
1218
 
                     ctx->notify_baton2, pool));
1219
 
 
1220
 
          if (! ignore_externals && depth == svn_depth_infinity)
1221
 
            {
1222
 
              const char *to_abspath;
1223
 
 
1224
 
              SVN_ERR(svn_dirent_get_absolute(&to_abspath, to_path, pool));
1225
 
              SVN_ERR(svn_client__export_externals(eb->externals,
1226
 
                                                   from_path_or_url,
1227
 
                                                   to_abspath, repos_root_url,
1228
 
                                                   depth, native_eol,
1229
 
                                                   ignore_keywords, &use_sleep,
1230
 
                                                   ctx, pool));
1231
 
            }
 
1437
          SVN_ERR(export_directory(from_path_or_url, to_path,
 
1438
                                   eb, loc, ra_session, overwrite,
 
1439
                                   ignore_externals, ignore_keywords, depth,
 
1440
                                   native_eol, ctx, pool));
1232
1441
        }
1233
1442
      else if (kind == svn_node_none)
1234
1443
        {
1240
1449
    }
1241
1450
  else
1242
1451
    {
 
1452
      struct export_info_baton eib;
1243
1453
      svn_node_kind_t kind;
 
1454
      apr_hash_t *externals = NULL;
 
1455
 
1244
1456
      /* This is a working copy export. */
1245
1457
      /* just copy the contents of the working copy into the target path. */
1246
1458
      SVN_ERR(svn_dirent_get_absolute(&from_path_or_url, from_path_or_url,
1284
1496
       * For a start, to detect the source kind, it looks at what is on disk
1285
1497
       * rather than the versioned working or base node.
1286
1498
       */
1287
 
 
1288
1499
      if (kind == svn_node_file)
1289
1500
        SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url, FALSE,
1290
1501
                                       pool));
1291
1502
 
1292
 
      SVN_ERR(copy_versioned_files(from_path_or_url, to_path, revision,
1293
 
                                   overwrite, ignore_externals, ignore_keywords,
1294
 
                                   depth, native_eol, ctx, pool));
 
1503
      eib.to_path = to_path;
 
1504
      eib.revision = revision;
 
1505
      eib.overwrite = overwrite;
 
1506
      eib.ignore_keywords = ignore_keywords;
 
1507
      eib.wc_ctx = ctx->wc_ctx;
 
1508
      eib.native_eol = native_eol;
 
1509
      eib.notify_func = ctx->notify_func2;;
 
1510
      eib.notify_baton = ctx->notify_baton2;
 
1511
      eib.origin_abspath = from_path_or_url;
 
1512
      eib.exported = FALSE;
 
1513
 
 
1514
      SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, from_path_or_url, depth,
 
1515
                                 TRUE /* get_all */,
 
1516
                                 TRUE /* no_ignore */,
 
1517
                                 FALSE /* ignore_text_mods */,
 
1518
                                 NULL,
 
1519
                                 export_node, &eib,
 
1520
                                 ctx->cancel_func, ctx->cancel_baton,
 
1521
                                 pool));
 
1522
 
 
1523
      if (!eib.exported)
 
1524
        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
 
1525
                                 _("The node '%s' was not found."),
 
1526
                                 svn_dirent_local_style(from_path_or_url,
 
1527
                                                        pool));
 
1528
 
 
1529
      if (!ignore_externals)
 
1530
        SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx,
 
1531
                                                from_path_or_url,
 
1532
                                                pool, pool));
 
1533
 
 
1534
      if (externals && apr_hash_count(externals))
 
1535
        {
 
1536
          apr_pool_t *iterpool = svn_pool_create(pool);
 
1537
          apr_hash_index_t *hi;
 
1538
 
 
1539
          for (hi = apr_hash_first(pool, externals);
 
1540
               hi;
 
1541
               hi = apr_hash_next(hi))
 
1542
            {
 
1543
              const char *external_abspath = svn__apr_hash_index_key(hi);
 
1544
              const char *relpath;
 
1545
              const char *target_abspath;
 
1546
 
 
1547
              svn_pool_clear(iterpool);
 
1548
 
 
1549
              relpath = svn_dirent_skip_ancestor(from_path_or_url,
 
1550
                                                 external_abspath);
 
1551
 
 
1552
              target_abspath = svn_dirent_join(to_path, relpath,
 
1553
                                                         iterpool);
 
1554
 
 
1555
              /* Ensure that the parent directory exists */
 
1556
              SVN_ERR(svn_io_make_dir_recursively(
 
1557
                            svn_dirent_dirname(target_abspath, iterpool),
 
1558
                            iterpool));
 
1559
 
 
1560
              SVN_ERR(svn_client_export5(NULL,
 
1561
                                         svn_dirent_join(from_path_or_url,
 
1562
                                                         relpath,
 
1563
                                                         iterpool),
 
1564
                                         target_abspath,
 
1565
                                         peg_revision, revision,
 
1566
                                         TRUE, ignore_externals,
 
1567
                                         ignore_keywords, depth, native_eol,
 
1568
                                         ctx, iterpool));
 
1569
            }
 
1570
 
 
1571
          svn_pool_destroy(iterpool);
 
1572
        }
1295
1573
    }
1296
1574
 
1297
1575