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

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/commit.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:
30
30
#include <string.h>
31
31
#include <apr_strings.h>
32
32
#include <apr_hash.h>
33
 
#include <apr_md5.h>
 
33
#include "svn_hash.h"
34
34
#include "svn_wc.h"
35
35
#include "svn_ra.h"
36
 
#include "svn_delta.h"
37
 
#include "svn_subst.h"
38
36
#include "svn_client.h"
39
37
#include "svn_string.h"
40
38
#include "svn_pools.h"
42
40
#include "svn_error_codes.h"
43
41
#include "svn_dirent_uri.h"
44
42
#include "svn_path.h"
45
 
#include "svn_io.h"
46
 
#include "svn_time.h"
47
43
#include "svn_sorts.h"
48
 
#include "svn_props.h"
49
44
 
50
45
#include "client.h"
51
46
#include "private/svn_wc_private.h"
52
 
#include "private/svn_magic.h"
 
47
#include "private/svn_ra_private.h"
53
48
 
54
49
#include "svn_private_config.h"
55
50
 
56
 
/* Import context baton.
57
 
 
58
 
   ### TODO:  Add the following items to this baton:
59
 
      /` import editor/baton. `/
60
 
      const svn_delta_editor_t *editor;
61
 
      void *edit_baton;
62
 
 
63
 
      /` Client context baton `/
64
 
      svn_client_ctx_t `ctx;
65
 
 
66
 
      /` Paths (keys) excluded from the import (values ignored) `/
67
 
      apr_hash_t *excludes;
68
 
*/
69
 
typedef struct import_ctx_t
70
 
{
71
 
  /* Whether any changes were made to the repository */
72
 
  svn_boolean_t repos_changed;
73
 
 
74
 
  /* A magic cookie for mime-type detection. */
75
 
  svn_magic__cookie_t *magic_cookie;
76
 
} import_ctx_t;
77
 
 
78
 
 
79
 
/* Apply PATH's contents (as a delta against the empty string) to
80
 
   FILE_BATON in EDITOR.  Use POOL for any temporary allocation.
81
 
   PROPERTIES is the set of node properties set on this file.
82
 
 
83
 
   Fill DIGEST with the md5 checksum of the sent file; DIGEST must be
84
 
   at least APR_MD5_DIGESTSIZE bytes long. */
85
 
 
86
 
/* ### how does this compare against svn_wc_transmit_text_deltas2() ??? */
87
 
 
88
 
static svn_error_t *
89
 
send_file_contents(const char *path,
90
 
                   void *file_baton,
91
 
                   const svn_delta_editor_t *editor,
92
 
                   apr_hash_t *properties,
93
 
                   unsigned char *digest,
94
 
                   apr_pool_t *pool)
95
 
{
96
 
  svn_stream_t *contents;
97
 
  svn_txdelta_window_handler_t handler;
98
 
  void *handler_baton;
99
 
  const svn_string_t *eol_style_val = NULL, *keywords_val = NULL;
100
 
  svn_boolean_t special = FALSE;
101
 
  svn_subst_eol_style_t eol_style;
102
 
  const char *eol;
103
 
  apr_hash_t *keywords;
104
 
 
105
 
  /* If there are properties, look for EOL-style and keywords ones. */
106
 
  if (properties)
107
 
    {
108
 
      eol_style_val = apr_hash_get(properties, SVN_PROP_EOL_STYLE,
109
 
                                   sizeof(SVN_PROP_EOL_STYLE) - 1);
110
 
      keywords_val = apr_hash_get(properties, SVN_PROP_KEYWORDS,
111
 
                                  sizeof(SVN_PROP_KEYWORDS) - 1);
112
 
      if (apr_hash_get(properties, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING))
113
 
        special = TRUE;
114
 
    }
115
 
 
116
 
  /* Get an editor func that wants to consume the delta stream. */
117
 
  SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool,
118
 
                                  &handler, &handler_baton));
119
 
 
120
 
  if (eol_style_val)
121
 
    svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data);
122
 
  else
123
 
    {
124
 
      eol = NULL;
125
 
      eol_style = svn_subst_eol_style_none;
126
 
    }
127
 
 
128
 
  if (keywords_val)
129
 
    SVN_ERR(svn_subst_build_keywords2(&keywords, keywords_val->data,
130
 
                                      APR_STRINGIFY(SVN_INVALID_REVNUM),
131
 
                                      "", 0, "", pool));
132
 
  else
133
 
    keywords = NULL;
134
 
 
135
 
  if (special)
136
 
    {
137
 
      SVN_ERR(svn_subst_read_specialfile(&contents, path, pool, pool));
138
 
    }
139
 
  else
140
 
    {
141
 
      /* Open the working copy file. */
142
 
      SVN_ERR(svn_stream_open_readonly(&contents, path, pool, pool));
143
 
 
144
 
      /* If we have EOL styles or keywords, then detranslate the file. */
145
 
      if (svn_subst_translation_required(eol_style, eol, keywords,
146
 
                                         FALSE, TRUE))
147
 
        {
148
 
          if (eol_style == svn_subst_eol_style_unknown)
149
 
            return svn_error_createf(SVN_ERR_IO_UNKNOWN_EOL, NULL,
150
 
                                    _("%s property on '%s' contains "
151
 
                                      "unrecognized EOL-style '%s'"),
152
 
                                    SVN_PROP_EOL_STYLE, path,
153
 
                                    eol_style_val->data);
154
 
 
155
 
          /* We're importing, so translate files with 'native' eol-style to
156
 
           * repository-normal form, not to this platform's native EOL. */
157
 
          if (eol_style == svn_subst_eol_style_native)
158
 
            eol = SVN_SUBST_NATIVE_EOL_STR;
159
 
 
160
 
          /* Wrap the working copy stream with a filter to detranslate it. */
161
 
          contents = svn_subst_stream_translated(contents,
162
 
                                                 eol,
163
 
                                                 TRUE /* repair */,
164
 
                                                 keywords,
165
 
                                                 FALSE /* expand */,
166
 
                                                 pool);
167
 
        }
168
 
    }
169
 
 
170
 
  /* Send the file's contents to the delta-window handler. */
171
 
  return svn_error_trace(svn_txdelta_send_stream(contents, handler,
172
 
                                                 handler_baton, digest,
173
 
                                                 pool));
174
 
}
175
 
 
176
 
 
177
 
/* Import file PATH as EDIT_PATH in the repository directory indicated
178
 
 * by DIR_BATON in EDITOR.
179
 
 *
180
 
 * Accumulate file paths and their batons in FILES, which must be
181
 
 * non-null.  (These are used to send postfix textdeltas later).
182
 
 *
183
 
 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON
184
 
 * for each file.
185
 
 *
186
 
 * Use POOL for any temporary allocation.
187
 
 */
188
 
static svn_error_t *
189
 
import_file(const svn_delta_editor_t *editor,
190
 
            void *dir_baton,
191
 
            const char *path,
192
 
            const char *edit_path,
193
 
            import_ctx_t *import_ctx,
194
 
            svn_client_ctx_t *ctx,
195
 
            apr_pool_t *pool)
196
 
{
197
 
  void *file_baton;
198
 
  const char *mimetype = NULL;
199
 
  unsigned char digest[APR_MD5_DIGESTSIZE];
200
 
  const char *text_checksum;
201
 
  apr_hash_t* properties;
202
 
  apr_hash_index_t *hi;
203
 
  svn_node_kind_t kind;
204
 
  svn_boolean_t is_special;
205
 
 
206
 
  SVN_ERR(svn_path_check_valid(path, pool));
207
 
 
208
 
  SVN_ERR(svn_io_check_special_path(path, &kind, &is_special, pool));
209
 
 
210
 
  /* Add the file, using the pool from the FILES hash. */
211
 
  SVN_ERR(editor->add_file(edit_path, dir_baton, NULL, SVN_INVALID_REVNUM,
212
 
                           pool, &file_baton));
213
 
 
214
 
  /* Remember that the repository was modified */
215
 
  import_ctx->repos_changed = TRUE;
216
 
 
217
 
  if (! is_special)
218
 
    {
219
 
      /* add automatic properties */
220
 
      SVN_ERR(svn_client__get_auto_props(&properties, &mimetype, path,
221
 
                                         import_ctx->magic_cookie,
222
 
                                         ctx, pool));
223
 
    }
224
 
  else
225
 
    properties = apr_hash_make(pool);
226
 
 
227
 
  if (properties)
228
 
    {
229
 
      for (hi = apr_hash_first(pool, properties); hi; hi = apr_hash_next(hi))
230
 
        {
231
 
          const char *pname = svn__apr_hash_index_key(hi);
232
 
          const svn_string_t *pval = svn__apr_hash_index_val(hi);
233
 
 
234
 
          SVN_ERR(editor->change_file_prop(file_baton, pname, pval, pool));
235
 
        }
236
 
    }
237
 
 
238
 
  if (ctx->notify_func2)
239
 
    {
240
 
      svn_wc_notify_t *notify
241
 
        = svn_wc_create_notify(path, svn_wc_notify_commit_added, pool);
242
 
      notify->kind = svn_node_file;
243
 
      notify->mime_type = mimetype;
244
 
      notify->content_state = notify->prop_state
245
 
        = svn_wc_notify_state_inapplicable;
246
 
      notify->lock_state = svn_wc_notify_lock_state_inapplicable;
247
 
      (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
248
 
    }
249
 
 
250
 
  /* If this is a special file, we need to set the svn:special
251
 
     property and create a temporary detranslated version in order to
252
 
     send to the server. */
253
 
  if (is_special)
254
 
    {
255
 
      apr_hash_set(properties, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING,
256
 
                   svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool));
257
 
      SVN_ERR(editor->change_file_prop(file_baton, SVN_PROP_SPECIAL,
258
 
                                       apr_hash_get(properties,
259
 
                                                    SVN_PROP_SPECIAL,
260
 
                                                    APR_HASH_KEY_STRING),
261
 
                                       pool));
262
 
    }
263
 
 
264
 
  /* Now, transmit the file contents. */
265
 
  SVN_ERR(send_file_contents(path, file_baton, editor,
266
 
                             properties, digest, pool));
267
 
 
268
 
  /* Finally, close the file. */
269
 
  text_checksum =
270
 
    svn_checksum_to_cstring(svn_checksum__from_digest(digest, svn_checksum_md5,
271
 
                                                      pool), pool);
272
 
 
273
 
  return editor->close_file(file_baton, text_checksum, pool);
274
 
}
275
 
 
276
 
 
277
 
/* Import directory PATH into the repository directory indicated by
278
 
 * DIR_BATON in EDITOR.  EDIT_PATH is the path imported as the root
279
 
 * directory, so all edits are relative to that.
280
 
 *
281
 
 * DEPTH is the depth at this point in the descent (it may be changed
282
 
 * for recursive calls).
283
 
 *
284
 
 * Accumulate file paths and their batons in FILES, which must be
285
 
 * non-null.  (These are used to send postfix textdeltas later).
286
 
 *
287
 
 * EXCLUDES is a hash whose keys are absolute paths to exclude from
288
 
 * the import (values are unused).
289
 
 *
290
 
 * If NO_IGNORE is FALSE, don't import files or directories that match
291
 
 * ignore patterns.
292
 
 *
293
 
 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each
294
 
 * directory.
295
 
 *
296
 
 * Use POOL for any temporary allocation.  */
297
 
static svn_error_t *
298
 
import_dir(const svn_delta_editor_t *editor,
299
 
           void *dir_baton,
300
 
           const char *path,
301
 
           const char *edit_path,
302
 
           svn_depth_t depth,
303
 
           apr_hash_t *excludes,
304
 
           svn_boolean_t no_ignore,
305
 
           svn_boolean_t ignore_unknown_node_types,
306
 
           import_ctx_t *import_ctx,
307
 
           svn_client_ctx_t *ctx,
308
 
           apr_pool_t *pool)
309
 
{
310
 
  apr_pool_t *subpool = svn_pool_create(pool);  /* iteration pool */
311
 
  apr_hash_t *dirents;
312
 
  apr_hash_index_t *hi;
313
 
  apr_array_header_t *ignores;
314
 
 
315
 
  SVN_ERR(svn_path_check_valid(path, pool));
316
 
 
317
 
  if (!no_ignore)
318
 
    SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));
319
 
 
320
 
  SVN_ERR(svn_io_get_dirents3(&dirents, path, TRUE, pool, pool));
321
 
 
322
 
  for (hi = apr_hash_first(pool, dirents); hi; hi = apr_hash_next(hi))
323
 
    {
324
 
      const char *this_path, *this_edit_path, *abs_path;
325
 
      const char *filename = svn__apr_hash_index_key(hi);
326
 
      const svn_io_dirent_t *dirent = svn__apr_hash_index_val(hi);
327
 
 
328
 
      svn_pool_clear(subpool);
329
 
 
330
 
      if (ctx->cancel_func)
331
 
        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
332
 
 
333
 
      if (svn_wc_is_adm_dir(filename, subpool))
334
 
        {
335
 
          /* If someone's trying to import a directory named the same
336
 
             as our administrative directories, that's probably not
337
 
             what they wanted to do.  If they are importing a file
338
 
             with that name, something is bound to blow up when they
339
 
             checkout what they've imported.  So, just skip items with
340
 
             that name.  */
341
 
          if (ctx->notify_func2)
342
 
            {
343
 
              svn_wc_notify_t *notify
344
 
                = svn_wc_create_notify(svn_dirent_join(path, filename,
345
 
                                                       subpool),
346
 
                                       svn_wc_notify_skip, subpool);
347
 
              notify->kind = svn_node_dir;
348
 
              notify->content_state = notify->prop_state
349
 
                = svn_wc_notify_state_inapplicable;
350
 
              notify->lock_state = svn_wc_notify_lock_state_inapplicable;
351
 
              (*ctx->notify_func2)(ctx->notify_baton2, notify, subpool);
352
 
            }
353
 
          continue;
354
 
        }
355
 
 
356
 
      /* Typically, we started importing from ".", in which case
357
 
         edit_path is "".  So below, this_path might become "./blah",
358
 
         and this_edit_path might become "blah", for example. */
359
 
      this_path = svn_dirent_join(path, filename, subpool);
360
 
      this_edit_path = svn_relpath_join(edit_path, filename, subpool);
361
 
 
362
 
      /* If this is an excluded path, exclude it. */
363
 
      SVN_ERR(svn_dirent_get_absolute(&abs_path, this_path, subpool));
364
 
      if (apr_hash_get(excludes, abs_path, APR_HASH_KEY_STRING))
365
 
        continue;
366
 
 
367
 
      if ((!no_ignore) && svn_wc_match_ignore_list(filename, ignores,
368
 
                                                   subpool))
369
 
        continue;
370
 
 
371
 
      if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates)
372
 
        {
373
 
          void *this_dir_baton;
374
 
 
375
 
          /* Add the new subdirectory, getting a descent baton from
376
 
             the editor. */
377
 
          SVN_ERR(editor->add_directory(this_edit_path, dir_baton,
378
 
                                        NULL, SVN_INVALID_REVNUM, subpool,
379
 
                                        &this_dir_baton));
380
 
 
381
 
          /* Remember that the repository was modified */
382
 
          import_ctx->repos_changed = TRUE;
383
 
 
384
 
          /* By notifying before the recursive call below, we display
385
 
             a directory add before displaying adds underneath the
386
 
             directory.  To do it the other way around, just move this
387
 
             after the recursive call. */
388
 
          if (ctx->notify_func2)
389
 
            {
390
 
              svn_wc_notify_t *notify
391
 
                = svn_wc_create_notify(this_path, svn_wc_notify_commit_added,
392
 
                                       subpool);
393
 
              notify->kind = svn_node_dir;
394
 
              notify->content_state = notify->prop_state
395
 
                = svn_wc_notify_state_inapplicable;
396
 
              notify->lock_state = svn_wc_notify_lock_state_inapplicable;
397
 
              (*ctx->notify_func2)(ctx->notify_baton2, notify, subpool);
398
 
            }
399
 
 
400
 
          /* Recurse. */
401
 
          {
402
 
            svn_depth_t depth_below_here = depth;
403
 
            if (depth == svn_depth_immediates)
404
 
              depth_below_here = svn_depth_empty;
405
 
 
406
 
            SVN_ERR(import_dir(editor, this_dir_baton, this_path,
407
 
                               this_edit_path, depth_below_here, excludes,
408
 
                               no_ignore, ignore_unknown_node_types,
409
 
                               import_ctx, ctx,
410
 
                               subpool));
411
 
          }
412
 
 
413
 
          /* Finally, close the sub-directory. */
414
 
          SVN_ERR(editor->close_directory(this_dir_baton, subpool));
415
 
        }
416
 
      else if (dirent->kind == svn_node_file && depth >= svn_depth_files)
417
 
        {
418
 
          SVN_ERR(import_file(editor, dir_baton, this_path,
419
 
                              this_edit_path, import_ctx, ctx, subpool));
420
 
        }
421
 
      else if (dirent->kind != svn_node_dir && dirent->kind != svn_node_file)
422
 
        {
423
 
          if (ignore_unknown_node_types)
424
 
            {
425
 
              /*## warn about it*/
426
 
              if (ctx->notify_func2)
427
 
                {
428
 
                  svn_wc_notify_t *notify
429
 
                    = svn_wc_create_notify(this_path,
430
 
                                           svn_wc_notify_skip, subpool);
431
 
                  notify->kind = svn_node_dir;
432
 
                  notify->content_state = notify->prop_state
433
 
                    = svn_wc_notify_state_inapplicable;
434
 
                  notify->lock_state = svn_wc_notify_lock_state_inapplicable;
435
 
                  (*ctx->notify_func2)(ctx->notify_baton2, notify, subpool);
436
 
                }
437
 
            }
438
 
          else
439
 
            return svn_error_createf
440
 
              (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
441
 
               _("Unknown or unversionable type for '%s'"),
442
 
               svn_dirent_local_style(this_path, subpool));
443
 
        }
444
 
    }
445
 
 
446
 
  svn_pool_destroy(subpool);
447
 
  return SVN_NO_ERROR;
448
 
}
449
 
 
450
 
 
451
 
/* Recursively import PATH to a repository using EDITOR and
452
 
 * EDIT_BATON.  PATH can be a file or directory.
453
 
 *
454
 
 * DEPTH is the depth at which to import PATH; it behaves as for
455
 
 * svn_client_import4().
456
 
 *
457
 
 * NEW_ENTRIES is an ordered array of path components that must be
458
 
 * created in the repository (where the ordering direction is
459
 
 * parent-to-child).  If PATH is a directory, NEW_ENTRIES may be empty
460
 
 * -- the result is an import which creates as many new entries in the
461
 
 * top repository target directory as there are importable entries in
462
 
 * the top of PATH; but if NEW_ENTRIES is not empty, its last item is
463
 
 * the name of a new subdirectory in the repository to hold the
464
 
 * import.  If PATH is a file, NEW_ENTRIES may not be empty, and its
465
 
 * last item is the name used for the file in the repository.  If
466
 
 * NEW_ENTRIES contains more than one item, all but the last item are
467
 
 * the names of intermediate directories that are created before the
468
 
 * real import begins.  NEW_ENTRIES may NOT be NULL.
469
 
 *
470
 
 * EXCLUDES is a hash whose keys are absolute paths to exclude from
471
 
 * the import (values are unused).
472
 
 *
473
 
 * If NO_IGNORE is FALSE, don't import files or directories that match
474
 
 * ignore patterns.
475
 
 *
476
 
 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for
477
 
 * each imported path, passing actions svn_wc_notify_commit_added.
478
 
 *
479
 
 * Use POOL for any temporary allocation.
480
 
 *
481
 
 * Note: the repository directory receiving the import was specified
482
 
 * when the editor was fetched.  (I.e, when EDITOR->open_root() is
483
 
 * called, it returns a directory baton for that directory, which is
484
 
 * not necessarily the root.)
485
 
 */
486
 
static svn_error_t *
487
 
import(const char *path,
488
 
       const apr_array_header_t *new_entries,
489
 
       const svn_delta_editor_t *editor,
490
 
       void *edit_baton,
491
 
       svn_depth_t depth,
492
 
       apr_hash_t *excludes,
493
 
       svn_boolean_t no_ignore,
494
 
       svn_boolean_t ignore_unknown_node_types,
495
 
       svn_client_ctx_t *ctx,
496
 
       apr_pool_t *pool)
497
 
{
498
 
  void *root_baton;
499
 
  svn_node_kind_t kind;
500
 
  apr_array_header_t *ignores;
501
 
  apr_array_header_t *batons = NULL;
502
 
  const char *edit_path = "";
503
 
  import_ctx_t *import_ctx = apr_pcalloc(pool, sizeof(*import_ctx));
504
 
 
505
 
  svn_magic__init(&import_ctx->magic_cookie, pool);
506
 
 
507
 
  /* Get a root dir baton.  We pass an invalid revnum to open_root
508
 
     to mean "base this on the youngest revision".  Should we have an
509
 
     SVN_YOUNGEST_REVNUM defined for these purposes? */
510
 
  SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
511
 
                            pool, &root_baton));
512
 
 
513
 
  /* Import a file or a directory tree. */
514
 
  SVN_ERR(svn_io_check_path(path, &kind, pool));
515
 
 
516
 
  /* Make the intermediate directory components necessary for properly
517
 
     rooting our import source tree.  */
518
 
  if (new_entries->nelts)
519
 
    {
520
 
      int i;
521
 
 
522
 
      batons = apr_array_make(pool, new_entries->nelts, sizeof(void *));
523
 
      for (i = 0; i < new_entries->nelts; i++)
524
 
        {
525
 
          const char *component = APR_ARRAY_IDX(new_entries, i, const char *);
526
 
          edit_path = svn_relpath_join(edit_path, component, pool);
527
 
 
528
 
          /* If this is the last path component, and we're importing a
529
 
             file, then this component is the name of the file, not an
530
 
             intermediate directory. */
531
 
          if ((i == new_entries->nelts - 1) && (kind == svn_node_file))
532
 
            break;
533
 
 
534
 
          APR_ARRAY_PUSH(batons, void *) = root_baton;
535
 
          SVN_ERR(editor->add_directory(edit_path,
536
 
                                        root_baton,
537
 
                                        NULL, SVN_INVALID_REVNUM,
538
 
                                        pool, &root_baton));
539
 
 
540
 
          /* Remember that the repository was modified */
541
 
          import_ctx->repos_changed = TRUE;
542
 
        }
543
 
    }
544
 
  else if (kind == svn_node_file)
545
 
    {
546
 
      return svn_error_create
547
 
        (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
548
 
         _("New entry name required when importing a file"));
549
 
    }
550
 
 
551
 
  /* Note that there is no need to check whether PATH's basename is
552
 
     the same name that we reserve for our administrative
553
 
     subdirectories.  It would be strange -- though not illegal -- to
554
 
     import the contents of a directory of that name, because the
555
 
     directory's own name is not part of those contents.  Of course,
556
 
     if something underneath it also has our reserved name, then we'll
557
 
     error. */
558
 
 
559
 
  if (kind == svn_node_file)
560
 
    {
561
 
      svn_boolean_t ignores_match = FALSE;
562
 
 
563
 
      if (!no_ignore)
564
 
        {
565
 
          SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));
566
 
          ignores_match = svn_wc_match_ignore_list(path, ignores, pool);
567
 
        }
568
 
      if (!ignores_match)
569
 
        SVN_ERR(import_file(editor, root_baton, path, edit_path,
570
 
                            import_ctx, ctx, pool));
571
 
    }
572
 
  else if (kind == svn_node_dir)
573
 
    {
574
 
      SVN_ERR(import_dir(editor, root_baton, path, edit_path,
575
 
                         depth, excludes, no_ignore,
576
 
                         ignore_unknown_node_types, import_ctx, ctx, pool));
577
 
 
578
 
    }
579
 
  else if (kind == svn_node_none
580
 
           || kind == svn_node_unknown)
581
 
    {
582
 
      return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
583
 
                               _("'%s' does not exist"),
584
 
                               svn_dirent_local_style(path, pool));
585
 
    }
586
 
 
587
 
  /* Close up shop; it's time to go home. */
588
 
  SVN_ERR(editor->close_directory(root_baton, pool));
589
 
  if (batons && batons->nelts)
590
 
    {
591
 
      void **baton;
592
 
      while ((baton = (void **) apr_array_pop(batons)))
593
 
        {
594
 
          SVN_ERR(editor->close_directory(*baton, pool));
595
 
        }
596
 
    }
597
 
 
598
 
  if (import_ctx->repos_changed)
599
 
    return editor->close_edit(edit_baton, pool);
600
 
  else
601
 
    return editor->abort_edit(edit_baton, pool);
602
 
}
603
 
 
604
 
 
605
51
struct capture_baton_t {
606
52
  svn_commit_callback2_t original_callback;
607
53
  void *original_baton;
628
74
 
629
75
 
630
76
static svn_error_t *
631
 
get_ra_editor(svn_ra_session_t **ra_session,
632
 
              const svn_delta_editor_t **editor,
 
77
get_ra_editor(const svn_delta_editor_t **editor,
633
78
              void **edit_baton,
 
79
              svn_ra_session_t *ra_session,
634
80
              svn_client_ctx_t *ctx,
635
 
              const char *base_url,
636
 
              const char *base_dir_abspath,
637
81
              const char *log_msg,
638
82
              const apr_array_header_t *commit_items,
639
83
              const apr_hash_t *revprop_table,
640
 
              svn_boolean_t is_commit,
641
84
              apr_hash_t *lock_tokens,
642
85
              svn_boolean_t keep_locks,
643
86
              svn_commit_callback2_t commit_callback,
645
88
              apr_pool_t *pool)
646
89
{
647
90
  apr_hash_t *commit_revprops;
648
 
 
649
 
  /* Open an RA session to URL. */
650
 
  SVN_ERR(svn_client__open_ra_session_internal(ra_session, NULL, base_url,
651
 
                                               base_dir_abspath, commit_items,
652
 
                                               is_commit, !is_commit,
653
 
                                               ctx, pool));
654
 
 
655
 
  /* If this is an import (aka, not a commit), we need to verify that
656
 
     our repository URL exists. */
657
 
  if (! is_commit)
658
 
    {
659
 
      svn_node_kind_t kind;
660
 
 
661
 
      SVN_ERR(svn_ra_check_path(*ra_session, "", SVN_INVALID_REVNUM,
662
 
                                &kind, pool));
663
 
      if (kind == svn_node_none)
664
 
        return svn_error_createf(SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
665
 
                                 _("Path '%s' does not exist"),
666
 
                                 base_url);
667
 
    }
 
91
  apr_hash_t *relpath_map = NULL;
668
92
 
669
93
  SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
670
94
                                           log_msg, ctx, pool));
671
95
 
 
96
#ifdef ENABLE_EV2_SHIMS
 
97
  if (commit_items)
 
98
    {
 
99
      int i;
 
100
      apr_pool_t *iterpool = svn_pool_create(pool);
 
101
 
 
102
      relpath_map = apr_hash_make(pool);
 
103
      for (i = 0; i < commit_items->nelts; i++)
 
104
        {
 
105
          svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i,
 
106
                                                  svn_client_commit_item3_t *);
 
107
          const char *relpath;
 
108
 
 
109
          if (!item->path)
 
110
            continue;
 
111
 
 
112
          svn_pool_clear(iterpool);
 
113
          SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL,
 
114
                                          ctx->wc_ctx, item->path, FALSE, pool,
 
115
                                          iterpool));
 
116
          if (relpath)
 
117
            svn_hash_sets(relpath_map, relpath, item->path);
 
118
        }
 
119
      svn_pool_destroy(iterpool);
 
120
    }
 
121
#endif
 
122
 
672
123
  /* Fetch RA commit editor. */
673
 
  return svn_ra_get_commit_editor3(*ra_session, editor, edit_baton,
674
 
                                   commit_revprops, commit_callback,
675
 
                                   commit_baton, lock_tokens, keep_locks,
676
 
                                   pool);
 
124
  SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
 
125
                        svn_client__get_shim_callbacks(ctx->wc_ctx,
 
126
                                                       relpath_map, pool)));
 
127
  SVN_ERR(svn_ra_get_commit_editor3(ra_session, editor, edit_baton,
 
128
                                    commit_revprops, commit_callback,
 
129
                                    commit_baton, lock_tokens, keep_locks,
 
130
                                    pool));
 
131
 
 
132
  return SVN_NO_ERROR;
677
133
}
678
134
 
679
135
 
680
136
/*** Public Interfaces. ***/
681
137
 
682
 
svn_error_t *
683
 
svn_client_import4(const char *path,
684
 
                   const char *url,
685
 
                   svn_depth_t depth,
686
 
                   svn_boolean_t no_ignore,
687
 
                   svn_boolean_t ignore_unknown_node_types,
688
 
                   const apr_hash_t *revprop_table,
689
 
                   svn_commit_callback2_t commit_callback,
690
 
                   void *commit_baton,
691
 
                   svn_client_ctx_t *ctx,
692
 
                   apr_pool_t *pool)
693
 
{
694
 
  svn_error_t *err = SVN_NO_ERROR;
695
 
  const char *log_msg = "";
696
 
  const svn_delta_editor_t *editor;
697
 
  void *edit_baton;
698
 
  svn_ra_session_t *ra_session;
699
 
  apr_hash_t *excludes = apr_hash_make(pool);
700
 
  svn_node_kind_t kind;
701
 
  const char *local_abspath;
702
 
  apr_array_header_t *new_entries = apr_array_make(pool, 4,
703
 
                                                   sizeof(const char *));
704
 
  const char *temp;
705
 
  const char *dir;
706
 
  apr_pool_t *subpool;
707
 
 
708
 
  if (svn_path_is_url(path))
709
 
    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
710
 
                             _("'%s' is not a local path"), path);
711
 
 
712
 
  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
713
 
 
714
 
  /* Create a new commit item and add it to the array. */
715
 
  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
716
 
    {
717
 
      /* If there's a log message gatherer, create a temporary commit
718
 
         item array solely to help generate the log message.  The
719
 
         array is not used for the import itself. */
720
 
      svn_client_commit_item3_t *item;
721
 
      const char *tmp_file;
722
 
      apr_array_header_t *commit_items
723
 
        = apr_array_make(pool, 1, sizeof(item));
724
 
 
725
 
      item = svn_client_commit_item3_create(pool);
726
 
      item->path = apr_pstrdup(pool, path);
727
 
      item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
728
 
      APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
729
 
 
730
 
      SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
731
 
                                      ctx, pool));
732
 
      if (! log_msg)
733
 
        return SVN_NO_ERROR;
734
 
      if (tmp_file)
735
 
        {
736
 
          const char *abs_path;
737
 
          SVN_ERR(svn_dirent_get_absolute(&abs_path, tmp_file, pool));
738
 
          apr_hash_set(excludes, abs_path, APR_HASH_KEY_STRING, (void *)1);
739
 
        }
740
 
    }
741
 
 
742
 
  SVN_ERR(svn_io_check_path(local_abspath, &kind, pool));
743
 
 
744
 
  /* Figure out all the path components we need to create just to have
745
 
     a place to stick our imported tree. */
746
 
  subpool = svn_pool_create(pool);
747
 
  do
748
 
    {
749
 
      svn_pool_clear(subpool);
750
 
 
751
 
      /* See if the user is interested in cancelling this operation. */
752
 
      if (ctx->cancel_func)
753
 
        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
754
 
 
755
 
      if (err)
756
 
        {
757
 
          /* If get_ra_editor below failed we either tried to open
758
 
             an invalid url, or else some other kind of error.  In case
759
 
             the url was bad we back up a directory and try again. */
760
 
 
761
 
          if (err->apr_err != SVN_ERR_FS_NO_SUCH_ENTRY)
762
 
            return err;
763
 
          else
764
 
            svn_error_clear(err);
765
 
 
766
 
          svn_uri_split(&temp, &dir, url, pool);
767
 
          APR_ARRAY_PUSH(new_entries, const char *) = dir;
768
 
          url = temp;
769
 
        }
770
 
    }
771
 
  while ((err = get_ra_editor(&ra_session,
772
 
                              &editor, &edit_baton, ctx, url, NULL,
773
 
                              log_msg, NULL, revprop_table, FALSE, NULL, TRUE,
774
 
                              commit_callback, commit_baton, subpool)));
775
 
 
776
 
  /* Reverse the order of the components we added to our NEW_ENTRIES array. */
777
 
  if (new_entries->nelts)
778
 
    {
779
 
      int i, j;
780
 
      const char *component;
781
 
      for (i = 0; i < (new_entries->nelts / 2); i++)
782
 
        {
783
 
          j = new_entries->nelts - i - 1;
784
 
          component =
785
 
            APR_ARRAY_IDX(new_entries, i, const char *);
786
 
          APR_ARRAY_IDX(new_entries, i, const char *) =
787
 
            APR_ARRAY_IDX(new_entries, j, const char *);
788
 
          APR_ARRAY_IDX(new_entries, j, const char *) =
789
 
            component;
790
 
        }
791
 
    }
792
 
 
793
 
  /* An empty NEW_ENTRIES list the first call to get_ra_editor() above
794
 
     succeeded.  That means that URL corresponds to an already
795
 
     existing filesystem entity. */
796
 
  if (kind == svn_node_file && (! new_entries->nelts))
797
 
    return svn_error_createf
798
 
      (SVN_ERR_ENTRY_EXISTS, NULL,
799
 
       _("Path '%s' already exists"), url);
800
 
 
801
 
  /* The repository doesn't know about the reserved administrative
802
 
     directory. */
803
 
  if (new_entries->nelts
804
 
      /* What's this, what's this?  This assignment is here because we
805
 
         use the value to construct the error message just below.  It
806
 
         may not be aesthetically pleasing, but it's less ugly than
807
 
         calling APR_ARRAY_IDX twice. */
808
 
      && svn_wc_is_adm_dir(temp = APR_ARRAY_IDX(new_entries,
809
 
                                                new_entries->nelts - 1,
810
 
                                                const char *),
811
 
                           pool))
812
 
    return svn_error_createf
813
 
      (SVN_ERR_CL_ADM_DIR_RESERVED, NULL,
814
 
       _("'%s' is a reserved name and cannot be imported"),
815
 
       /* ### Is svn_path_local_style() really necessary for this? */
816
 
       svn_dirent_local_style(temp, pool));
817
 
 
818
 
 
819
 
  /* If an error occurred during the commit, abort the edit and return
820
 
     the error.  We don't even care if the abort itself fails.  */
821
 
  if ((err = import(path, new_entries, editor, edit_baton,
822
 
                    depth, excludes, no_ignore,
823
 
                    ignore_unknown_node_types, ctx, subpool)))
824
 
    {
825
 
      svn_error_clear(editor->abort_edit(edit_baton, subpool));
826
 
      return err;
827
 
    }
828
 
 
829
 
  svn_pool_destroy(subpool);
830
 
 
831
 
  return SVN_NO_ERROR;
832
 
}
833
 
 
834
 
 
835
138
static svn_error_t *
836
139
reconcile_errors(svn_error_t *commit_err,
837
140
                 svn_error_t *unlock_err,
902
205
    {
903
206
      const char *url = svn__apr_hash_index_key(hi);
904
207
      const char *token = svn__apr_hash_index_val(hi);
 
208
      const char *relpath = svn_uri_skip_ancestor(base_url, url, pool);
905
209
 
906
 
      if (svn_uri__is_ancestor(base_url, url))
 
210
      if (relpath)
907
211
        {
908
 
          url = svn_uri_skip_ancestor(base_url, url, pool);
909
 
 
910
 
          apr_hash_set(*result, url, APR_HASH_KEY_STRING, token);
 
212
          svn_hash_sets(*result, relpath, token);
911
213
        }
912
214
    }
913
215
 
938
240
  remove_lock = (! keep_locks && (item->state_flags
939
241
                                       & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN));
940
242
 
 
243
  /* When the node was deleted (or replaced), we need to always remove the 
 
244
     locks, as they're invalidated on the server. We cannot honor the 
 
245
     SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN flag here because it does not tell
 
246
     us whether we have locked children. */
 
247
  if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
 
248
    remove_lock = TRUE;
 
249
 
941
250
  return svn_wc_queue_committed3(queue, wc_ctx, item->path,
942
251
                                 loop_recurse, item->incoming_prop_changes,
943
252
                                 remove_lock, !keep_changelists,
955
264
 
956
265
  SVN_ERR_ASSERT(depth != svn_depth_infinity);
957
266
 
958
 
  SVN_ERR(svn_wc_read_kind(&kind, wc_ctx, target_abspath, FALSE,
959
 
                           scratch_pool));
 
267
  SVN_ERR(svn_wc_read_kind2(&kind, wc_ctx, target_abspath,
 
268
                            TRUE, FALSE, scratch_pool));
960
269
 
961
270
 
962
271
  /* ### TODO(sd): This check is slightly too strict.  It should be
1050
359
      target_abspath = svn_dirent_join(base_abspath, target_relpath,
1051
360
                                       scratch_pool);
1052
361
 
1053
 
      err = svn_wc__get_wc_root(&wcroot_abspath, wc_ctx, target_abspath,
1054
 
                                iterpool, iterpool);
 
362
      err = svn_wc__get_wcroot(&wcroot_abspath, wc_ctx, target_abspath,
 
363
                               iterpool, iterpool);
1055
364
 
1056
365
      if (err)
1057
366
        {
1063
372
          return svn_error_trace(err);
1064
373
        }
1065
374
 
1066
 
      wc_targets = apr_hash_get(wc_items, wcroot_abspath, APR_HASH_KEY_STRING);
 
375
      wc_targets = svn_hash_gets(wc_items, wcroot_abspath);
1067
376
 
1068
377
      if (! wc_targets)
1069
378
        {
1070
379
          wc_targets = apr_array_make(scratch_pool, 4, sizeof(const char *));
1071
 
          apr_hash_set(wc_items, apr_pstrdup(scratch_pool, wcroot_abspath),
1072
 
                       APR_HASH_KEY_STRING, wc_targets);
 
380
          svn_hash_sets(wc_items, apr_pstrdup(scratch_pool, wcroot_abspath),
 
381
                        wc_targets);
1073
382
        }
1074
383
 
1075
384
      APR_ARRAY_PUSH(wc_targets, const char *) = target_abspath;
1156
465
  /* If we don't have a session or can't use the session, get one */
1157
466
  if (!cukb->session || !svn_uri__is_ancestor(cukb->repos_root_url, url))
1158
467
    {
1159
 
      SVN_ERR(svn_client_open_ra_session(&cukb->session, url, cukb->ctx,
1160
 
                                         cukb->pool));
 
468
      SVN_ERR(svn_client_open_ra_session2(&cukb->session, url, NULL, cukb->ctx,
 
469
                                          cukb->pool, scratch_pool));
1161
470
      SVN_ERR(svn_ra_get_repos_root2(cukb->session, &cukb->repos_root_url,
1162
471
                                     cukb->pool));
1163
472
    }
1169
478
                                  kind, scratch_pool));
1170
479
}
1171
480
 
 
481
/* Recurse into every target in REL_TARGETS, finding committable externals
 
482
 * nested within. Append these to REL_TARGETS itself. The paths in REL_TARGETS
 
483
 * are assumed to be / will be created relative to BASE_ABSPATH. The remaining
 
484
 * arguments correspond to those of svn_client_commit6(). */
 
485
static svn_error_t*
 
486
append_externals_as_explicit_targets(apr_array_header_t *rel_targets,
 
487
                                     const char *base_abspath,
 
488
                                     svn_boolean_t include_file_externals,
 
489
                                     svn_boolean_t include_dir_externals,
 
490
                                     svn_depth_t depth,
 
491
                                     svn_client_ctx_t *ctx,
 
492
                                     apr_pool_t *result_pool,
 
493
                                     apr_pool_t *scratch_pool)
 
494
{
 
495
  int rel_targets_nelts_fixed;
 
496
  int i;
 
497
  apr_pool_t *iterpool;
 
498
 
 
499
  if (! (include_file_externals || include_dir_externals))
 
500
    return SVN_NO_ERROR;
 
501
 
 
502
  /* Easy part of applying DEPTH to externals. */
 
503
  if (depth == svn_depth_empty)
 
504
    {
 
505
      /* Don't recurse. */
 
506
      return SVN_NO_ERROR;
 
507
    }
 
508
 
 
509
  /* Iterate *and* grow REL_TARGETS at the same time. */
 
510
  rel_targets_nelts_fixed = rel_targets->nelts;
 
511
 
 
512
  iterpool = svn_pool_create(scratch_pool);
 
513
 
 
514
  for (i = 0; i < rel_targets_nelts_fixed; i++)
 
515
    {
 
516
      int j;
 
517
      const char *target;
 
518
      apr_array_header_t *externals = NULL;
 
519
 
 
520
      svn_pool_clear(iterpool);
 
521
 
 
522
      target = svn_dirent_join(base_abspath,
 
523
                               APR_ARRAY_IDX(rel_targets, i, const char *),
 
524
                               iterpool);
 
525
 
 
526
      /* ### TODO: Possible optimization: No need to do this for file targets.
 
527
       * ### But what's cheaper, stat'ing the file system or querying the db?
 
528
       * ### --> future. */
 
529
 
 
530
      SVN_ERR(svn_wc__committable_externals_below(&externals, ctx->wc_ctx,
 
531
                                                  target, depth,
 
532
                                                  iterpool, iterpool));
 
533
 
 
534
      if (externals != NULL)
 
535
        {
 
536
          const char *rel_target;
 
537
 
 
538
          for (j = 0; j < externals->nelts; j++)
 
539
            {
 
540
              svn_wc__committable_external_info_t *xinfo =
 
541
                         APR_ARRAY_IDX(externals, j,
 
542
                                       svn_wc__committable_external_info_t *);
 
543
 
 
544
              if ((xinfo->kind == svn_node_file && ! include_file_externals)
 
545
                  || (xinfo->kind == svn_node_dir && ! include_dir_externals))
 
546
                continue;
 
547
 
 
548
              rel_target = svn_dirent_skip_ancestor(base_abspath,
 
549
                                                    xinfo->local_abspath);
 
550
 
 
551
              SVN_ERR_ASSERT(rel_target != NULL && *rel_target != '\0');
 
552
 
 
553
              APR_ARRAY_PUSH(rel_targets, const char *) =
 
554
                                         apr_pstrdup(result_pool, rel_target);
 
555
            }
 
556
        }
 
557
    }
 
558
 
 
559
  svn_pool_destroy(iterpool);
 
560
  return SVN_NO_ERROR;
 
561
}
 
562
 
1172
563
svn_error_t *
1173
 
svn_client_commit5(const apr_array_header_t *targets,
 
564
svn_client_commit6(const apr_array_header_t *targets,
1174
565
                   svn_depth_t depth,
1175
566
                   svn_boolean_t keep_locks,
1176
567
                   svn_boolean_t keep_changelists,
1177
568
                   svn_boolean_t commit_as_operations,
 
569
                   svn_boolean_t include_file_externals,
 
570
                   svn_boolean_t include_dir_externals,
1178
571
                   const apr_array_header_t *changelists,
1179
572
                   const apr_hash_t *revprop_table,
1180
573
                   svn_commit_callback2_t commit_callback,
1189
582
  const char *log_msg;
1190
583
  const char *base_abspath;
1191
584
  const char *base_url;
1192
 
  const char *ra_session_wc;
1193
585
  apr_array_header_t *rel_targets;
1194
586
  apr_array_header_t *lock_targets;
1195
587
  apr_array_header_t *locks_obtained;
1201
593
  svn_error_t *bump_err = SVN_NO_ERROR;
1202
594
  svn_error_t *unlock_err = SVN_NO_ERROR;
1203
595
  svn_boolean_t commit_in_progress = FALSE;
 
596
  svn_boolean_t timestamp_sleep = FALSE;
1204
597
  svn_commit_info_t *commit_info = NULL;
1205
598
  apr_pool_t *iterpool = svn_pool_create(pool);
1206
599
  const char *current_abspath;
1207
600
  const char *notify_prefix;
 
601
  int depth_empty_after = -1;
1208
602
  int i;
1209
603
 
1210
604
  SVN_ERR_ASSERT(depth != svn_depth_unknown && depth != svn_depth_exclude);
1235
629
  if (rel_targets->nelts == 0)
1236
630
    APR_ARRAY_PUSH(rel_targets, const char *) = "";
1237
631
 
 
632
  if (include_file_externals || include_dir_externals)
 
633
    {
 
634
      if (depth != svn_depth_unknown && depth != svn_depth_infinity)
 
635
        {
 
636
          /* All targets after this will be handled as depth empty */
 
637
          depth_empty_after = rel_targets->nelts;
 
638
        }
 
639
 
 
640
      SVN_ERR(append_externals_as_explicit_targets(rel_targets, base_abspath,
 
641
                                                   include_file_externals,
 
642
                                                   include_dir_externals,
 
643
                                                   depth, ctx,
 
644
                                                   pool, pool));
 
645
    }
 
646
 
1238
647
  SVN_ERR(determine_lock_targets(&lock_targets, ctx->wc_ctx, base_abspath,
1239
648
                                 rel_targets, pool, iterpool));
1240
649
 
1299
708
                                                    &lock_tokens,
1300
709
                                                    base_abspath,
1301
710
                                                    rel_targets,
 
711
                                                    depth_empty_after,
1302
712
                                                    depth,
1303
713
                                                    ! keep_locks,
1304
714
                                                    changelists,
1356
766
      goto cleanup;
1357
767
  }
1358
768
 
 
769
  /* For every target that was moved verify that both halves of the
 
770
   * move are part of the commit. */
 
771
  for (i = 0; i < commit_items->nelts; i++)
 
772
    {
 
773
      svn_client_commit_item3_t *item =
 
774
        APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *);
 
775
 
 
776
      svn_pool_clear(iterpool);
 
777
 
 
778
      if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_MOVED_HERE)
 
779
        {
 
780
          /* ### item->moved_from_abspath contains the move origin */
 
781
          const char *moved_from_abspath;
 
782
          const char *delete_op_root_abspath;
 
783
 
 
784
          cmt_err = svn_error_trace(svn_wc__node_was_moved_here(
 
785
                                      &moved_from_abspath,
 
786
                                      &delete_op_root_abspath,
 
787
                                      ctx->wc_ctx, item->path,
 
788
                                      iterpool, iterpool));
 
789
          if (cmt_err)
 
790
            goto cleanup;
 
791
 
 
792
          if (moved_from_abspath && delete_op_root_abspath &&
 
793
              strcmp(moved_from_abspath, delete_op_root_abspath) == 0)
 
794
 
 
795
            {
 
796
              svn_boolean_t found_delete_half =
 
797
                (svn_hash_gets(committables->by_path, delete_op_root_abspath)
 
798
                 != NULL);
 
799
 
 
800
              if (!found_delete_half)
 
801
                {
 
802
                  const char *delete_half_parent_abspath;
 
803
 
 
804
                  /* The delete-half isn't in the commit target list.
 
805
                   * However, it might itself be the child of a deleted node,
 
806
                   * either because of another move or a deletion.
 
807
                   *
 
808
                   * For example, consider: mv A/B B; mv B/C C; commit;
 
809
                   * C's moved-from A/B/C is a child of the deleted A/B.
 
810
                   * A/B/C does not appear in the commit target list, but
 
811
                   * A/B does appear.
 
812
                   * (Note that moved-from information is always stored
 
813
                   * relative to the BASE tree, so we have 'C moved-from
 
814
                   * A/B/C', not 'C moved-from B/C'.)
 
815
                   *
 
816
                   * An example involving a move and a delete would be:
 
817
                   * mv A/B C; rm A; commit;
 
818
                   * Now C is moved-from A/B which does not appear in the
 
819
                   * commit target list, but A does appear.
 
820
                   */
 
821
 
 
822
                  /* Scan upwards for a deletion op-root from the
 
823
                   * delete-half's parent directory. */
 
824
                  delete_half_parent_abspath =
 
825
                    svn_dirent_dirname(delete_op_root_abspath, iterpool);
 
826
                  if (strcmp(delete_op_root_abspath,
 
827
                             delete_half_parent_abspath) != 0)
 
828
                    {
 
829
                      const char *parent_delete_op_root_abspath;
 
830
 
 
831
                      cmt_err = svn_error_trace(
 
832
                                  svn_wc__node_get_deleted_ancestor(
 
833
                                    &parent_delete_op_root_abspath,
 
834
                                    ctx->wc_ctx, delete_half_parent_abspath,
 
835
                                    iterpool, iterpool));
 
836
                      if (cmt_err)
 
837
                        goto cleanup;
 
838
 
 
839
                      if (parent_delete_op_root_abspath)
 
840
                        found_delete_half =
 
841
                          (svn_hash_gets(committables->by_path,
 
842
                                         parent_delete_op_root_abspath)
 
843
                           != NULL);
 
844
                    }
 
845
                }
 
846
 
 
847
              if (!found_delete_half)
 
848
                {
 
849
                  cmt_err = svn_error_createf(
 
850
                              SVN_ERR_ILLEGAL_TARGET, NULL,
 
851
                              _("Cannot commit '%s' because it was moved from "
 
852
                                "'%s' which is not part of the commit; both "
 
853
                                "sides of the move must be committed together"),
 
854
                              svn_dirent_local_style(item->path, iterpool),
 
855
                              svn_dirent_local_style(delete_op_root_abspath,
 
856
                                                     iterpool));
 
857
                  goto cleanup;
 
858
                }
 
859
            }
 
860
        }
 
861
 
 
862
      if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
 
863
        {
 
864
          const char *moved_to_abspath;
 
865
          const char *copy_op_root_abspath;
 
866
 
 
867
          cmt_err = svn_error_trace(svn_wc__node_was_moved_away(
 
868
                                      &moved_to_abspath,
 
869
                                      &copy_op_root_abspath,
 
870
                                      ctx->wc_ctx, item->path,
 
871
                                      iterpool, iterpool));
 
872
          if (cmt_err)
 
873
            goto cleanup;
 
874
 
 
875
          if (moved_to_abspath && copy_op_root_abspath &&
 
876
              strcmp(moved_to_abspath, copy_op_root_abspath) == 0 &&
 
877
              svn_hash_gets(committables->by_path, copy_op_root_abspath)
 
878
              == NULL)
 
879
            {
 
880
              cmt_err = svn_error_createf(
 
881
                          SVN_ERR_ILLEGAL_TARGET, NULL,
 
882
                         _("Cannot commit '%s' because it was moved to '%s' "
 
883
                           "which is not part of the commit; both sides of "
 
884
                           "the move must be committed together"),
 
885
                         svn_dirent_local_style(item->path, iterpool),
 
886
                         svn_dirent_local_style(copy_op_root_abspath,
 
887
                                                iterpool));
 
888
              goto cleanup;
 
889
            }
 
890
        }
 
891
    }
 
892
 
1359
893
  /* Go get a log message.  If an error occurs, or no log message is
1360
894
     specified, abort the operation. */
1361
895
  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
1391
925
  cb.info = &commit_info;
1392
926
  cb.pool = pool;
1393
927
 
1394
 
  /* When committing from multiple WCs, get the RA editor from
1395
 
   * the first WC, rather than the BASE_ABSPATH. The BASE_ABSPATH
1396
 
   * might be an unrelated parent of nested working copies.
1397
 
   * We don't support commits to multiple repositories so using
1398
 
   * the first WC to get the RA session is safe. */
1399
 
  if (lock_targets->nelts > 1)
1400
 
    ra_session_wc = APR_ARRAY_IDX(lock_targets, 0, const char *);
1401
 
  else
1402
 
    ra_session_wc = base_abspath;
1403
 
 
1404
 
  cmt_err = svn_error_trace(
1405
 
                 get_ra_editor(&ra_session, &editor, &edit_baton, ctx,
1406
 
                               base_url, ra_session_wc, log_msg,
1407
 
                               commit_items, revprop_table, TRUE, lock_tokens,
1408
 
                               keep_locks, capture_commit_info,
1409
 
                               &cb, pool));
 
928
  /* Get the RA editor from the first lock target, rather than BASE_ABSPATH.
 
929
   * When committing from multiple WCs, BASE_ABSPATH might be an unrelated
 
930
   * parent of nested working copies. We don't support commits to multiple
 
931
   * repositories so using the first WC to get the RA session is safe. */
 
932
  cmt_err = svn_error_trace(
 
933
              svn_client__open_ra_session_internal(&ra_session, NULL, base_url,
 
934
                                                   APR_ARRAY_IDX(lock_targets,
 
935
                                                                 0,
 
936
                                                                 const char *),
 
937
                                                   commit_items,
 
938
                                                   TRUE, TRUE, ctx,
 
939
                                                   pool, pool));
 
940
 
 
941
  if (cmt_err)
 
942
    goto cleanup;
 
943
 
 
944
  cmt_err = svn_error_trace(
 
945
              get_ra_editor(&editor, &edit_baton, ra_session, ctx,
 
946
                            log_msg, commit_items, revprop_table,
 
947
                            lock_tokens, keep_locks, capture_commit_info,
 
948
                            &cb, pool));
1410
949
 
1411
950
  if (cmt_err)
1412
951
    goto cleanup;
1414
953
  /* Make a note that we have a commit-in-progress. */
1415
954
  commit_in_progress = TRUE;
1416
955
 
 
956
  /* We'll assume that, once we pass this point, we are going to need to
 
957
   * sleep for timestamps.  Really, we may not need to do unless and until
 
958
   * we reach the point where we post-commit 'bump' the WC metadata. */
 
959
  timestamp_sleep = TRUE;
 
960
 
1417
961
  /* Perform the commit. */
1418
962
  cmt_err = svn_error_trace(
1419
 
            svn_client__do_commit(base_url, commit_items, editor, edit_baton,
1420
 
                                  notify_prefix, NULL,
1421
 
                                  &sha1_checksums, ctx, pool, iterpool));
 
963
              svn_client__do_commit(base_url, commit_items, editor, edit_baton,
 
964
                                    notify_prefix, &sha1_checksums, ctx, pool,
 
965
                                    iterpool));
1422
966
 
1423
967
  /* Handle a successful commit. */
1424
968
  if ((! cmt_err)
1438
982
          bump_err = post_process_commit_item(
1439
983
                       queue, item, ctx->wc_ctx,
1440
984
                       keep_changelists, keep_locks, commit_as_operations,
1441
 
                       apr_hash_get(sha1_checksums,
1442
 
                                    item->path,
1443
 
                                    APR_HASH_KEY_STRING),
 
985
                       svn_hash_gets(sha1_checksums, item->path),
1444
986
                       iterpool);
1445
987
          if (bump_err)
1446
988
            goto cleanup;
1456
998
                   iterpool);
1457
999
    }
1458
1000
 
1459
 
  /* Sleep to ensure timestamp integrity. */
1460
 
  svn_io_sleep_for_timestamps(base_abspath, pool);
1461
 
 
1462
1001
 cleanup:
 
1002
  /* Sleep to ensure timestamp integrity.  BASE_ABSPATH may have been
 
1003
     removed by the commit or it may the common ancestor of multiple
 
1004
     working copies. */
 
1005
  if (timestamp_sleep)
 
1006
    {
 
1007
      const char *wcroot_abspath;
 
1008
      svn_error_t *err = svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
 
1009
                                            base_abspath, pool, pool);
 
1010
      if (err)
 
1011
        {
 
1012
          svn_error_clear(err);
 
1013
          wcroot_abspath = NULL;
 
1014
        }
 
1015
 
 
1016
      svn_io_sleep_for_timestamps(wcroot_abspath, pool);
 
1017
    }
 
1018
 
1463
1019
  /* Abort the commit if it is still in progress. */
1464
1020
  svn_pool_clear(iterpool); /* Close open handles before aborting */
1465
1021
  if (commit_in_progress)