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

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/import.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:
 
1
/*
 
2
 * import.c:  wrappers around import functionality.
 
3
 *
 
4
 * ====================================================================
 
5
 *    Licensed to the Apache Software Foundation (ASF) under one
 
6
 *    or more contributor license agreements.  See the NOTICE file
 
7
 *    distributed with this work for additional information
 
8
 *    regarding copyright ownership.  The ASF licenses this file
 
9
 *    to you under the Apache License, Version 2.0 (the
 
10
 *    "License"); you may not use this file except in compliance
 
11
 *    with the License.  You may obtain a copy of the License at
 
12
 *
 
13
 *      http://www.apache.org/licenses/LICENSE-2.0
 
14
 *
 
15
 *    Unless required by applicable law or agreed to in writing,
 
16
 *    software distributed under the License is distributed on an
 
17
 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 
18
 *    KIND, either express or implied.  See the License for the
 
19
 *    specific language governing permissions and limitations
 
20
 *    under the License.
 
21
 * ====================================================================
 
22
 */
 
23
 
 
24
/* ==================================================================== */
 
25
 
 
26
 
 
27
 
 
28
/*** Includes. ***/
 
29
 
 
30
#include <string.h>
 
31
#include <apr_strings.h>
 
32
#include <apr_hash.h>
 
33
#include <apr_md5.h>
 
34
 
 
35
#include "svn_hash.h"
 
36
#include "svn_ra.h"
 
37
#include "svn_delta.h"
 
38
#include "svn_subst.h"
 
39
#include "svn_client.h"
 
40
#include "svn_string.h"
 
41
#include "svn_pools.h"
 
42
#include "svn_error_codes.h"
 
43
#include "svn_dirent_uri.h"
 
44
#include "svn_path.h"
 
45
#include "svn_io.h"
 
46
#include "svn_sorts.h"
 
47
#include "svn_props.h"
 
48
 
 
49
#include "client.h"
 
50
#include "private/svn_subr_private.h"
 
51
#include "private/svn_ra_private.h"
 
52
#include "private/svn_magic.h"
 
53
 
 
54
#include "svn_private_config.h"
 
55
 
 
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
 
 
77
  /* Collection of all possible configuration file dictated auto-props and
 
78
     svn:auto-props.  A hash mapping const char * file patterns to a
 
79
     second hash which maps const char * property names to const char *
 
80
     property values.  Properties which don't have a value, e.g.
 
81
     svn:executable, simply map the property name to an empty string.
 
82
     May be NULL if autoprops are disabled. */
 
83
  apr_hash_t *autoprops;
 
84
} import_ctx_t;
 
85
 
 
86
 
 
87
/* Apply LOCAL_ABSPATH's contents (as a delta against the empty string) to
 
88
   FILE_BATON in EDITOR.  Use POOL for any temporary allocation.
 
89
   PROPERTIES is the set of node properties set on this file.
 
90
 
 
91
   Fill DIGEST with the md5 checksum of the sent file; DIGEST must be
 
92
   at least APR_MD5_DIGESTSIZE bytes long. */
 
93
 
 
94
/* ### how does this compare against svn_wc_transmit_text_deltas2() ??? */
 
95
 
 
96
static svn_error_t *
 
97
send_file_contents(const char *local_abspath,
 
98
                   void *file_baton,
 
99
                   const svn_delta_editor_t *editor,
 
100
                   apr_hash_t *properties,
 
101
                   unsigned char *digest,
 
102
                   apr_pool_t *pool)
 
103
{
 
104
  svn_stream_t *contents;
 
105
  svn_txdelta_window_handler_t handler;
 
106
  void *handler_baton;
 
107
  const svn_string_t *eol_style_val = NULL, *keywords_val = NULL;
 
108
  svn_boolean_t special = FALSE;
 
109
  svn_subst_eol_style_t eol_style;
 
110
  const char *eol;
 
111
  apr_hash_t *keywords;
 
112
 
 
113
  /* If there are properties, look for EOL-style and keywords ones. */
 
114
  if (properties)
 
115
    {
 
116
      eol_style_val = apr_hash_get(properties, SVN_PROP_EOL_STYLE,
 
117
                                   sizeof(SVN_PROP_EOL_STYLE) - 1);
 
118
      keywords_val = apr_hash_get(properties, SVN_PROP_KEYWORDS,
 
119
                                  sizeof(SVN_PROP_KEYWORDS) - 1);
 
120
      if (svn_hash_gets(properties, SVN_PROP_SPECIAL))
 
121
        special = TRUE;
 
122
    }
 
123
 
 
124
  /* Get an editor func that wants to consume the delta stream. */
 
125
  SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool,
 
126
                                  &handler, &handler_baton));
 
127
 
 
128
  if (eol_style_val)
 
129
    svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data);
 
130
  else
 
131
    {
 
132
      eol = NULL;
 
133
      eol_style = svn_subst_eol_style_none;
 
134
    }
 
135
 
 
136
  if (keywords_val)
 
137
    SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data,
 
138
                                      APR_STRINGIFY(SVN_INVALID_REVNUM),
 
139
                                      "", "", 0, "", pool));
 
140
  else
 
141
    keywords = NULL;
 
142
 
 
143
  if (special)
 
144
    {
 
145
      SVN_ERR(svn_subst_read_specialfile(&contents, local_abspath,
 
146
                                         pool, pool));
 
147
    }
 
148
  else
 
149
    {
 
150
      /* Open the working copy file. */
 
151
      SVN_ERR(svn_stream_open_readonly(&contents, local_abspath, pool, pool));
 
152
 
 
153
      /* If we have EOL styles or keywords, then detranslate the file. */
 
154
      if (svn_subst_translation_required(eol_style, eol, keywords,
 
155
                                         FALSE, TRUE))
 
156
        {
 
157
          if (eol_style == svn_subst_eol_style_unknown)
 
158
            return svn_error_createf(SVN_ERR_IO_UNKNOWN_EOL, NULL,
 
159
                                    _("%s property on '%s' contains "
 
160
                                      "unrecognized EOL-style '%s'"),
 
161
                                    SVN_PROP_EOL_STYLE,
 
162
                                    svn_dirent_local_style(local_abspath,
 
163
                                                           pool),
 
164
                                    eol_style_val->data);
 
165
 
 
166
          /* We're importing, so translate files with 'native' eol-style to
 
167
           * repository-normal form, not to this platform's native EOL. */
 
168
          if (eol_style == svn_subst_eol_style_native)
 
169
            eol = SVN_SUBST_NATIVE_EOL_STR;
 
170
 
 
171
          /* Wrap the working copy stream with a filter to detranslate it. */
 
172
          contents = svn_subst_stream_translated(contents,
 
173
                                                 eol,
 
174
                                                 TRUE /* repair */,
 
175
                                                 keywords,
 
176
                                                 FALSE /* expand */,
 
177
                                                 pool);
 
178
        }
 
179
    }
 
180
 
 
181
  /* Send the file's contents to the delta-window handler. */
 
182
  return svn_error_trace(svn_txdelta_send_stream(contents, handler,
 
183
                                                 handler_baton, digest,
 
184
                                                 pool));
 
185
}
 
186
 
 
187
 
 
188
/* Import file PATH as EDIT_PATH in the repository directory indicated
 
189
 * by DIR_BATON in EDITOR.
 
190
 *
 
191
 * Accumulate file paths and their batons in FILES, which must be
 
192
 * non-null.  (These are used to send postfix textdeltas later).
 
193
 *
 
194
 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON
 
195
 * for each file.
 
196
 *
 
197
 * Use POOL for any temporary allocation.
 
198
 */
 
199
static svn_error_t *
 
200
import_file(const svn_delta_editor_t *editor,
 
201
            void *dir_baton,
 
202
            const char *local_abspath,
 
203
            const char *edit_path,
 
204
            const svn_io_dirent2_t *dirent,
 
205
            import_ctx_t *import_ctx,
 
206
            svn_client_ctx_t *ctx,
 
207
            apr_pool_t *pool)
 
208
{
 
209
  void *file_baton;
 
210
  const char *mimetype = NULL;
 
211
  unsigned char digest[APR_MD5_DIGESTSIZE];
 
212
  const char *text_checksum;
 
213
  apr_hash_t* properties;
 
214
  apr_hash_index_t *hi;
 
215
 
 
216
  SVN_ERR(svn_path_check_valid(local_abspath, pool));
 
217
 
 
218
  /* Add the file, using the pool from the FILES hash. */
 
219
  SVN_ERR(editor->add_file(edit_path, dir_baton, NULL, SVN_INVALID_REVNUM,
 
220
                           pool, &file_baton));
 
221
 
 
222
  /* Remember that the repository was modified */
 
223
  import_ctx->repos_changed = TRUE;
 
224
 
 
225
  if (! dirent->special)
 
226
    {
 
227
      /* add automatic properties */
 
228
      SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype,
 
229
                                               local_abspath,
 
230
                                               import_ctx->magic_cookie,
 
231
                                               import_ctx->autoprops,
 
232
                                               ctx, pool, pool));
 
233
    }
 
234
  else
 
235
    properties = apr_hash_make(pool);
 
236
 
 
237
  if (properties)
 
238
    {
 
239
      for (hi = apr_hash_first(pool, properties); hi; hi = apr_hash_next(hi))
 
240
        {
 
241
          const char *pname = svn__apr_hash_index_key(hi);
 
242
          const svn_string_t *pval = svn__apr_hash_index_val(hi);
 
243
 
 
244
          SVN_ERR(editor->change_file_prop(file_baton, pname, pval, pool));
 
245
        }
 
246
    }
 
247
 
 
248
  if (ctx->notify_func2)
 
249
    {
 
250
      svn_wc_notify_t *notify
 
251
        = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added,
 
252
                               pool);
 
253
      notify->kind = svn_node_file;
 
254
      notify->mime_type = mimetype;
 
255
      notify->content_state = notify->prop_state
 
256
        = svn_wc_notify_state_inapplicable;
 
257
      notify->lock_state = svn_wc_notify_lock_state_inapplicable;
 
258
      (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
 
259
    }
 
260
 
 
261
  /* If this is a special file, we need to set the svn:special
 
262
     property and create a temporary detranslated version in order to
 
263
     send to the server. */
 
264
  if (dirent->special)
 
265
    {
 
266
      svn_hash_sets(properties, SVN_PROP_SPECIAL,
 
267
                    svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool));
 
268
      SVN_ERR(editor->change_file_prop(file_baton, SVN_PROP_SPECIAL,
 
269
                                       svn_hash_gets(properties,
 
270
                                                     SVN_PROP_SPECIAL),
 
271
                                       pool));
 
272
    }
 
273
 
 
274
  /* Now, transmit the file contents. */
 
275
  SVN_ERR(send_file_contents(local_abspath, file_baton, editor,
 
276
                             properties, digest, pool));
 
277
 
 
278
  /* Finally, close the file. */
 
279
  text_checksum =
 
280
    svn_checksum_to_cstring(svn_checksum__from_digest_md5(digest, pool), pool);
 
281
 
 
282
  return editor->close_file(file_baton, text_checksum, pool);
 
283
}
 
284
 
 
285
 
 
286
/* Return in CHILDREN a mapping of basenames to dirents for the importable
 
287
 * children of DIR_ABSPATH.  EXCLUDES is a hash of absolute paths to filter
 
288
 * out.  IGNORES and GLOBAL_IGNORES, if non-NULL, are lists of basename
 
289
 * patterns to filter out.
 
290
 * FILTER_CALLBACK and FILTER_BATON will be called for each absolute path,
 
291
 * allowing users to further filter the list of returned entries.
 
292
 *
 
293
 * Results are returned in RESULT_POOL; use SCRATCH_POOL for temporary data.*/
 
294
static svn_error_t *
 
295
get_filtered_children(apr_hash_t **children,
 
296
                      const char *dir_abspath,
 
297
                      apr_hash_t *excludes,
 
298
                      apr_array_header_t *ignores,
 
299
                      apr_array_header_t *global_ignores,
 
300
                      svn_client_import_filter_func_t filter_callback,
 
301
                      void *filter_baton,
 
302
                      svn_client_ctx_t *ctx,
 
303
                      apr_pool_t *result_pool,
 
304
                      apr_pool_t *scratch_pool)
 
305
{
 
306
  apr_hash_t *dirents;
 
307
  apr_hash_index_t *hi;
 
308
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
309
 
 
310
  SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, result_pool,
 
311
                              scratch_pool));
 
312
 
 
313
  for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
 
314
    {
 
315
      const char *base_name = svn__apr_hash_index_key(hi);
 
316
      const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
 
317
      const char *local_abspath;
 
318
 
 
319
      svn_pool_clear(iterpool);
 
320
 
 
321
      local_abspath = svn_dirent_join(dir_abspath, base_name, iterpool);
 
322
 
 
323
      if (svn_wc_is_adm_dir(base_name, iterpool))
 
324
        {
 
325
          /* If someone's trying to import a directory named the same
 
326
             as our administrative directories, that's probably not
 
327
             what they wanted to do.  If they are importing a file
 
328
             with that name, something is bound to blow up when they
 
329
             checkout what they've imported.  So, just skip items with
 
330
             that name.  */
 
331
          if (ctx->notify_func2)
 
332
            {
 
333
              svn_wc_notify_t *notify
 
334
                = svn_wc_create_notify(svn_dirent_join(local_abspath, base_name,
 
335
                                                       iterpool),
 
336
                                       svn_wc_notify_skip, iterpool);
 
337
              notify->kind = svn_node_dir;
 
338
              notify->content_state = notify->prop_state
 
339
                = svn_wc_notify_state_inapplicable;
 
340
              notify->lock_state = svn_wc_notify_lock_state_inapplicable;
 
341
              (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
 
342
            }
 
343
 
 
344
          svn_hash_sets(dirents, base_name, NULL);
 
345
          continue;
 
346
        }
 
347
            /* If this is an excluded path, exclude it. */
 
348
      if (svn_hash_gets(excludes, local_abspath))
 
349
        {
 
350
          svn_hash_sets(dirents, base_name, NULL);
 
351
          continue;
 
352
        }
 
353
 
 
354
      if (ignores && svn_wc_match_ignore_list(base_name, ignores, iterpool))
 
355
        {
 
356
          svn_hash_sets(dirents, base_name, NULL);
 
357
          continue;
 
358
        }
 
359
 
 
360
      if (global_ignores &&
 
361
          svn_wc_match_ignore_list(base_name, global_ignores, iterpool))
 
362
        {
 
363
          svn_hash_sets(dirents, base_name, NULL);
 
364
          continue;
 
365
        }
 
366
 
 
367
      if (filter_callback)
 
368
        {
 
369
          svn_boolean_t filter = FALSE;
 
370
 
 
371
          SVN_ERR(filter_callback(filter_baton, &filter, local_abspath,
 
372
                                  dirent, iterpool));
 
373
 
 
374
          if (filter)
 
375
            {
 
376
              svn_hash_sets(dirents, base_name, NULL);
 
377
              continue;
 
378
            }
 
379
        }
 
380
    }
 
381
  svn_pool_destroy(iterpool);
 
382
 
 
383
  *children = dirents;
 
384
  return SVN_NO_ERROR;
 
385
}
 
386
 
 
387
static svn_error_t *
 
388
import_dir(const svn_delta_editor_t *editor,
 
389
           void *dir_baton,
 
390
           const char *local_abspath,
 
391
           const char *edit_path,
 
392
           svn_depth_t depth,
 
393
           apr_hash_t *excludes,
 
394
           apr_array_header_t *global_ignores,
 
395
           svn_boolean_t no_ignore,
 
396
           svn_boolean_t no_autoprops,
 
397
           svn_boolean_t ignore_unknown_node_types,
 
398
           svn_client_import_filter_func_t filter_callback,
 
399
           void *filter_baton,
 
400
           import_ctx_t *import_ctx,
 
401
           svn_client_ctx_t *ctx,
 
402
           apr_pool_t *pool);
 
403
 
 
404
 
 
405
/* Import the children of DIR_ABSPATH, with other arguments similar to
 
406
 * import_dir(). */
 
407
static svn_error_t *
 
408
import_children(const char *dir_abspath,
 
409
                const char *edit_path,
 
410
                apr_hash_t *dirents,
 
411
                const svn_delta_editor_t *editor,
 
412
                void *dir_baton,
 
413
                svn_depth_t depth,
 
414
                apr_hash_t *excludes,
 
415
                apr_array_header_t *global_ignores,
 
416
                svn_boolean_t no_ignore,
 
417
                svn_boolean_t no_autoprops,
 
418
                svn_boolean_t ignore_unknown_node_types,
 
419
                svn_client_import_filter_func_t filter_callback,
 
420
                void *filter_baton,
 
421
                import_ctx_t *import_ctx,
 
422
                svn_client_ctx_t *ctx,
 
423
                apr_pool_t *scratch_pool)
 
424
{
 
425
  apr_array_header_t *sorted_dirents;
 
426
  int i;
 
427
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
428
 
 
429
  sorted_dirents = svn_sort__hash(dirents, svn_sort_compare_items_lexically,
 
430
                                  scratch_pool);
 
431
  for (i = 0; i < sorted_dirents->nelts; i++)
 
432
    {
 
433
      const char *this_abspath, *this_edit_path;
 
434
      svn_sort__item_t item = APR_ARRAY_IDX(sorted_dirents, i,
 
435
                                            svn_sort__item_t);
 
436
      const char *filename = item.key;
 
437
      const svn_io_dirent2_t *dirent = item.value;
 
438
 
 
439
      svn_pool_clear(iterpool);
 
440
 
 
441
      if (ctx->cancel_func)
 
442
        SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
 
443
 
 
444
      /* Typically, we started importing from ".", in which case
 
445
         edit_path is "".  So below, this_path might become "./blah",
 
446
         and this_edit_path might become "blah", for example. */
 
447
      this_abspath = svn_dirent_join(dir_abspath, filename, iterpool);
 
448
      this_edit_path = svn_relpath_join(edit_path, filename, iterpool);
 
449
 
 
450
      if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates)
 
451
        {
 
452
          /* Recurse. */
 
453
          svn_depth_t depth_below_here = depth;
 
454
          if (depth == svn_depth_immediates)
 
455
            depth_below_here = svn_depth_empty;
 
456
 
 
457
          SVN_ERR(import_dir(editor, dir_baton, this_abspath,
 
458
                             this_edit_path, depth_below_here, excludes,
 
459
                             global_ignores, no_ignore, no_autoprops,
 
460
                             ignore_unknown_node_types, filter_callback,
 
461
                             filter_baton, import_ctx, ctx, iterpool));
 
462
        }
 
463
      else if (dirent->kind == svn_node_file && depth >= svn_depth_files)
 
464
        {
 
465
          SVN_ERR(import_file(editor, dir_baton, this_abspath,
 
466
                              this_edit_path, dirent,
 
467
                              import_ctx, ctx, iterpool));
 
468
        }
 
469
      else if (dirent->kind != svn_node_dir && dirent->kind != svn_node_file)
 
470
        {
 
471
          if (ignore_unknown_node_types)
 
472
            {
 
473
              /*## warn about it*/
 
474
              if (ctx->notify_func2)
 
475
                {
 
476
                  svn_wc_notify_t *notify
 
477
                    = svn_wc_create_notify(this_abspath,
 
478
                                           svn_wc_notify_skip, iterpool);
 
479
                  notify->kind = svn_node_dir;
 
480
                  notify->content_state = notify->prop_state
 
481
                    = svn_wc_notify_state_inapplicable;
 
482
                  notify->lock_state = svn_wc_notify_lock_state_inapplicable;
 
483
                  (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
 
484
                }
 
485
            }
 
486
          else
 
487
            return svn_error_createf
 
488
              (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
 
489
               _("Unknown or unversionable type for '%s'"),
 
490
               svn_dirent_local_style(this_abspath, iterpool));
 
491
        }
 
492
    }
 
493
 
 
494
  svn_pool_destroy(iterpool);
 
495
  return SVN_NO_ERROR;
 
496
}
 
497
 
 
498
 
 
499
/* Import directory LOCAL_ABSPATH into the repository directory indicated by
 
500
 * DIR_BATON in EDITOR.  EDIT_PATH is the path imported as the root
 
501
 * directory, so all edits are relative to that.
 
502
 *
 
503
 * DEPTH is the depth at this point in the descent (it may be changed
 
504
 * for recursive calls).
 
505
 *
 
506
 * Accumulate file paths and their batons in FILES, which must be
 
507
 * non-null.  (These are used to send postfix textdeltas later).
 
508
 *
 
509
 * EXCLUDES is a hash whose keys are absolute paths to exclude from
 
510
 * the import (values are unused).
 
511
 *
 
512
 * GLOBAL_IGNORES is an array of const char * ignore patterns.  Any child
 
513
 * of LOCAL_ABSPATH which matches one or more of the patterns is not imported.
 
514
 *
 
515
 * If NO_IGNORE is FALSE, don't import files or directories that match
 
516
 * ignore patterns.
 
517
 *
 
518
 * If FILTER_CALLBACK is not NULL, call it with FILTER_BATON on each to be
 
519
 * imported node below LOCAL_ABSPATH to allow filtering nodes.
 
520
 *
 
521
 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each
 
522
 * directory.
 
523
 *
 
524
 * Use POOL for any temporary allocation.  */
 
525
static svn_error_t *
 
526
import_dir(const svn_delta_editor_t *editor,
 
527
           void *dir_baton,
 
528
           const char *local_abspath,
 
529
           const char *edit_path,
 
530
           svn_depth_t depth,
 
531
           apr_hash_t *excludes,
 
532
           apr_array_header_t *global_ignores,
 
533
           svn_boolean_t no_ignore,
 
534
           svn_boolean_t no_autoprops,
 
535
           svn_boolean_t ignore_unknown_node_types,
 
536
           svn_client_import_filter_func_t filter_callback,
 
537
           void *filter_baton,
 
538
           import_ctx_t *import_ctx,
 
539
           svn_client_ctx_t *ctx,
 
540
           apr_pool_t *pool)
 
541
{
 
542
  apr_hash_t *dirents;
 
543
  void *this_dir_baton;
 
544
 
 
545
  SVN_ERR(svn_path_check_valid(local_abspath, pool));
 
546
  SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes, NULL,
 
547
                                global_ignores, filter_callback,
 
548
                                filter_baton, ctx, pool, pool));
 
549
 
 
550
  /* Import this directory, but not yet its children. */
 
551
  {
 
552
    /* Add the new subdirectory, getting a descent baton from the editor. */
 
553
    SVN_ERR(editor->add_directory(edit_path, dir_baton, NULL,
 
554
                                  SVN_INVALID_REVNUM, pool, &this_dir_baton));
 
555
 
 
556
    /* Remember that the repository was modified */
 
557
    import_ctx->repos_changed = TRUE;
 
558
 
 
559
    /* By notifying before the recursive call below, we display
 
560
       a directory add before displaying adds underneath the
 
561
       directory.  To do it the other way around, just move this
 
562
       after the recursive call. */
 
563
    if (ctx->notify_func2)
 
564
      {
 
565
        svn_wc_notify_t *notify
 
566
          = svn_wc_create_notify(local_abspath, svn_wc_notify_commit_added,
 
567
                                 pool);
 
568
        notify->kind = svn_node_dir;
 
569
        notify->content_state = notify->prop_state
 
570
          = svn_wc_notify_state_inapplicable;
 
571
        notify->lock_state = svn_wc_notify_lock_state_inapplicable;
 
572
        (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
 
573
      }
 
574
  }
 
575
 
 
576
  /* Now import the children recursively. */
 
577
  SVN_ERR(import_children(local_abspath, edit_path, dirents, editor,
 
578
                          this_dir_baton, depth, excludes, global_ignores,
 
579
                          no_ignore, no_autoprops, ignore_unknown_node_types,
 
580
                          filter_callback, filter_baton,
 
581
                          import_ctx, ctx, pool));
 
582
 
 
583
  /* Finally, close the sub-directory. */
 
584
  SVN_ERR(editor->close_directory(this_dir_baton, pool));
 
585
 
 
586
  return SVN_NO_ERROR;
 
587
}
 
588
 
 
589
 
 
590
/* Recursively import PATH to a repository using EDITOR and
 
591
 * EDIT_BATON.  PATH can be a file or directory.
 
592
 *
 
593
 * DEPTH is the depth at which to import PATH; it behaves as for
 
594
 * svn_client_import4().
 
595
 *
 
596
 * NEW_ENTRIES is an ordered array of path components that must be
 
597
 * created in the repository (where the ordering direction is
 
598
 * parent-to-child).  If PATH is a directory, NEW_ENTRIES may be empty
 
599
 * -- the result is an import which creates as many new entries in the
 
600
 * top repository target directory as there are importable entries in
 
601
 * the top of PATH; but if NEW_ENTRIES is not empty, its last item is
 
602
 * the name of a new subdirectory in the repository to hold the
 
603
 * import.  If PATH is a file, NEW_ENTRIES may not be empty, and its
 
604
 * last item is the name used for the file in the repository.  If
 
605
 * NEW_ENTRIES contains more than one item, all but the last item are
 
606
 * the names of intermediate directories that are created before the
 
607
 * real import begins.  NEW_ENTRIES may NOT be NULL.
 
608
 *
 
609
 * EXCLUDES is a hash whose keys are absolute paths to exclude from
 
610
 * the import (values are unused).
 
611
 *
 
612
 * AUTOPROPS is hash of all config file autoprops and
 
613
 * svn:auto-props inherited by the import target, see the
 
614
 * IMPORT_CTX member of the same name.
 
615
 *
 
616
 * LOCAL_IGNORES is an array of const char * ignore patterns which
 
617
 * correspond to the svn:ignore property (if any) set on the root of the
 
618
 * repository target and thus dictates which immediate children of that
 
619
 * target should be ignored and not imported.
 
620
 *
 
621
 * GLOBAL_IGNORES is an array of const char * ignore patterns which
 
622
 * correspond to the svn:global-ignores properties (if any) set on
 
623
 * the root of the repository target or inherited by it.
 
624
 *
 
625
 * If NO_IGNORE is FALSE, don't import files or directories that match
 
626
 * ignore patterns.
 
627
 *
 
628
 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for
 
629
 * each imported path, passing actions svn_wc_notify_commit_added.
 
630
 *
 
631
 * Use POOL for any temporary allocation.
 
632
 *
 
633
 * Note: the repository directory receiving the import was specified
 
634
 * when the editor was fetched.  (I.e, when EDITOR->open_root() is
 
635
 * called, it returns a directory baton for that directory, which is
 
636
 * not necessarily the root.)
 
637
 */
 
638
static svn_error_t *
 
639
import(const char *local_abspath,
 
640
       const apr_array_header_t *new_entries,
 
641
       const svn_delta_editor_t *editor,
 
642
       void *edit_baton,
 
643
       svn_depth_t depth,
 
644
       apr_hash_t *excludes,
 
645
       apr_hash_t *autoprops,
 
646
       apr_array_header_t *local_ignores,
 
647
       apr_array_header_t *global_ignores,
 
648
       svn_boolean_t no_ignore,
 
649
       svn_boolean_t no_autoprops,
 
650
       svn_boolean_t ignore_unknown_node_types,
 
651
       svn_client_import_filter_func_t filter_callback,
 
652
       void *filter_baton,
 
653
       svn_client_ctx_t *ctx,
 
654
       apr_pool_t *pool)
 
655
{
 
656
  void *root_baton;
 
657
  apr_array_header_t *batons = NULL;
 
658
  const char *edit_path = "";
 
659
  import_ctx_t *import_ctx = apr_pcalloc(pool, sizeof(*import_ctx));
 
660
  const svn_io_dirent2_t *dirent;
 
661
 
 
662
  import_ctx->autoprops = autoprops;
 
663
  svn_magic__init(&import_ctx->magic_cookie, pool);
 
664
 
 
665
  /* Get a root dir baton.  We pass an invalid revnum to open_root
 
666
     to mean "base this on the youngest revision".  Should we have an
 
667
     SVN_YOUNGEST_REVNUM defined for these purposes? */
 
668
  SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
 
669
                            pool, &root_baton));
 
670
 
 
671
  /* Import a file or a directory tree. */
 
672
  SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, FALSE,
 
673
                              pool, pool));
 
674
 
 
675
  /* Make the intermediate directory components necessary for properly
 
676
     rooting our import source tree.  */
 
677
  if (new_entries->nelts)
 
678
    {
 
679
      int i;
 
680
 
 
681
      batons = apr_array_make(pool, new_entries->nelts, sizeof(void *));
 
682
      for (i = 0; i < new_entries->nelts; i++)
 
683
        {
 
684
          const char *component = APR_ARRAY_IDX(new_entries, i, const char *);
 
685
          edit_path = svn_relpath_join(edit_path, component, pool);
 
686
 
 
687
          /* If this is the last path component, and we're importing a
 
688
             file, then this component is the name of the file, not an
 
689
             intermediate directory. */
 
690
          if ((i == new_entries->nelts - 1) && (dirent->kind == svn_node_file))
 
691
            break;
 
692
 
 
693
          APR_ARRAY_PUSH(batons, void *) = root_baton;
 
694
          SVN_ERR(editor->add_directory(edit_path,
 
695
                                        root_baton,
 
696
                                        NULL, SVN_INVALID_REVNUM,
 
697
                                        pool, &root_baton));
 
698
 
 
699
          /* Remember that the repository was modified */
 
700
          import_ctx->repos_changed = TRUE;
 
701
        }
 
702
    }
 
703
  else if (dirent->kind == svn_node_file)
 
704
    {
 
705
      return svn_error_create
 
706
        (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
 
707
         _("New entry name required when importing a file"));
 
708
    }
 
709
 
 
710
  /* Note that there is no need to check whether PATH's basename is
 
711
     the same name that we reserve for our administrative
 
712
     subdirectories.  It would be strange -- though not illegal -- to
 
713
     import the contents of a directory of that name, because the
 
714
     directory's own name is not part of those contents.  Of course,
 
715
     if something underneath it also has our reserved name, then we'll
 
716
     error. */
 
717
 
 
718
  if (dirent->kind == svn_node_file)
 
719
    {
 
720
      /* This code path ignores EXCLUDES and FILTER, but they don't make
 
721
         much sense for a single file import anyway. */
 
722
      svn_boolean_t ignores_match = FALSE;
 
723
 
 
724
      if (!no_ignore)
 
725
        ignores_match =
 
726
          (svn_wc_match_ignore_list(local_abspath, global_ignores, pool)
 
727
           || svn_wc_match_ignore_list(local_abspath, local_ignores, pool));
 
728
 
 
729
      if (!ignores_match)
 
730
        SVN_ERR(import_file(editor, root_baton, local_abspath, edit_path,
 
731
                            dirent, import_ctx, ctx, pool));
 
732
    }
 
733
  else if (dirent->kind == svn_node_dir)
 
734
    {
 
735
      apr_hash_t *dirents;
 
736
 
 
737
      /* If we are creating a new repository directory path to import to,
 
738
         then we disregard any svn:ignore property. */
 
739
      if (!no_ignore && new_entries->nelts)
 
740
        local_ignores = NULL;
 
741
 
 
742
      SVN_ERR(get_filtered_children(&dirents, local_abspath, excludes,
 
743
                                    local_ignores, global_ignores,
 
744
                                    filter_callback, filter_baton, ctx,
 
745
                                    pool, pool));
 
746
 
 
747
      SVN_ERR(import_children(local_abspath, edit_path, dirents, editor,
 
748
                              root_baton, depth, excludes, global_ignores,
 
749
                              no_ignore, no_autoprops,
 
750
                              ignore_unknown_node_types, filter_callback,
 
751
                              filter_baton, import_ctx, ctx, pool));
 
752
 
 
753
    }
 
754
  else if (dirent->kind == svn_node_none
 
755
           || dirent->kind == svn_node_unknown)
 
756
    {
 
757
      return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
 
758
                               _("'%s' does not exist"),
 
759
                               svn_dirent_local_style(local_abspath, pool));
 
760
    }
 
761
 
 
762
  /* Close up shop; it's time to go home. */
 
763
  SVN_ERR(editor->close_directory(root_baton, pool));
 
764
  if (batons && batons->nelts)
 
765
    {
 
766
      void **baton;
 
767
      while ((baton = (void **) apr_array_pop(batons)))
 
768
        {
 
769
          SVN_ERR(editor->close_directory(*baton, pool));
 
770
        }
 
771
    }
 
772
 
 
773
  if (import_ctx->repos_changed)
 
774
    return editor->close_edit(edit_baton, pool);
 
775
  else
 
776
    return editor->abort_edit(edit_baton, pool);
 
777
}
 
778
 
 
779
 
 
780
/*** Public Interfaces. ***/
 
781
 
 
782
svn_error_t *
 
783
svn_client_import5(const char *path,
 
784
                   const char *url,
 
785
                   svn_depth_t depth,
 
786
                   svn_boolean_t no_ignore,
 
787
                   svn_boolean_t no_autoprops,
 
788
                   svn_boolean_t ignore_unknown_node_types,
 
789
                   const apr_hash_t *revprop_table,
 
790
                   svn_client_import_filter_func_t filter_callback,
 
791
                   void *filter_baton,
 
792
                   svn_commit_callback2_t commit_callback,
 
793
                   void *commit_baton,
 
794
                   svn_client_ctx_t *ctx,
 
795
                   apr_pool_t *scratch_pool)
 
796
{
 
797
  svn_error_t *err = SVN_NO_ERROR;
 
798
  const char *log_msg = "";
 
799
  const svn_delta_editor_t *editor;
 
800
  void *edit_baton;
 
801
  svn_ra_session_t *ra_session;
 
802
  apr_hash_t *excludes = apr_hash_make(scratch_pool);
 
803
  svn_node_kind_t kind;
 
804
  const char *local_abspath;
 
805
  apr_array_header_t *new_entries = apr_array_make(scratch_pool, 4,
 
806
                                                   sizeof(const char *));
 
807
  apr_hash_t *commit_revprops;
 
808
  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
809
  apr_hash_t *autoprops = NULL;
 
810
  apr_array_header_t *global_ignores;
 
811
  apr_array_header_t *local_ignores_arr;
 
812
 
 
813
  if (svn_path_is_url(path))
 
814
    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
 
815
                             _("'%s' is not a local path"), path);
 
816
 
 
817
  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
 
818
 
 
819
  /* Create a new commit item and add it to the array. */
 
820
  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
 
821
    {
 
822
      /* If there's a log message gatherer, create a temporary commit
 
823
         item array solely to help generate the log message.  The
 
824
         array is not used for the import itself. */
 
825
      svn_client_commit_item3_t *item;
 
826
      const char *tmp_file;
 
827
      apr_array_header_t *commit_items
 
828
        = apr_array_make(scratch_pool, 1, sizeof(item));
 
829
 
 
830
      item = svn_client_commit_item3_create(scratch_pool);
 
831
      item->path = apr_pstrdup(scratch_pool, path);
 
832
      item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
 
833
      APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
 
834
 
 
835
      SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
 
836
                                      ctx, scratch_pool));
 
837
      if (! log_msg)
 
838
        return SVN_NO_ERROR;
 
839
      if (tmp_file)
 
840
        {
 
841
          const char *abs_path;
 
842
          SVN_ERR(svn_dirent_get_absolute(&abs_path, tmp_file, scratch_pool));
 
843
          svn_hash_sets(excludes, abs_path, (void *)1);
 
844
        }
 
845
    }
 
846
 
 
847
  SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
 
848
 
 
849
  SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
 
850
                                      ctx, scratch_pool, iterpool));
 
851
 
 
852
  /* Figure out all the path components we need to create just to have
 
853
     a place to stick our imported tree. */
 
854
  SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
 
855
                            iterpool));
 
856
 
 
857
  /* We can import into directories, but if a file already exists, that's
 
858
     an error. */
 
859
  if (kind == svn_node_file)
 
860
    return svn_error_createf
 
861
      (SVN_ERR_ENTRY_EXISTS, NULL,
 
862
       _("Path '%s' already exists"), url);
 
863
 
 
864
  while (kind == svn_node_none)
 
865
    {
 
866
      const char *dir;
 
867
 
 
868
      svn_pool_clear(iterpool);
 
869
 
 
870
      svn_uri_split(&url, &dir, url, scratch_pool);
 
871
      APR_ARRAY_PUSH(new_entries, const char *) = dir;
 
872
      SVN_ERR(svn_ra_reparent(ra_session, url, iterpool));
 
873
 
 
874
      SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
 
875
                                iterpool));
 
876
    }
 
877
 
 
878
  /* Reverse the order of the components we added to our NEW_ENTRIES array. */
 
879
  svn_sort__array_reverse(new_entries, scratch_pool);
 
880
 
 
881
  /* The repository doesn't know about the reserved administrative
 
882
     directory. */
 
883
  if (new_entries->nelts)
 
884
    {
 
885
      const char *last_component
 
886
        = APR_ARRAY_IDX(new_entries, new_entries->nelts - 1, const char *);
 
887
 
 
888
      if (svn_wc_is_adm_dir(last_component, scratch_pool))
 
889
        return svn_error_createf
 
890
          (SVN_ERR_CL_ADM_DIR_RESERVED, NULL,
 
891
           _("'%s' is a reserved name and cannot be imported"),
 
892
           svn_dirent_local_style(last_component, scratch_pool));
 
893
    }
 
894
 
 
895
  SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
 
896
                                           log_msg, ctx, scratch_pool));
 
897
 
 
898
  /* Fetch RA commit editor. */
 
899
  SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
 
900
                        svn_client__get_shim_callbacks(ctx->wc_ctx,
 
901
                                                       NULL, scratch_pool)));
 
902
  SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
 
903
                                    commit_revprops, commit_callback,
 
904
                                    commit_baton, NULL, TRUE,
 
905
                                    scratch_pool));
 
906
 
 
907
  /* Get inherited svn:auto-props, svn:global-ignores, and
 
908
     svn:ignores for the location we are importing to. */
 
909
  if (!no_autoprops)
 
910
    SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx,
 
911
                                           scratch_pool, iterpool));
 
912
  if (no_ignore)
 
913
    {
 
914
      global_ignores = NULL;
 
915
      local_ignores_arr = NULL;
 
916
    }
 
917
  else
 
918
    {
 
919
      svn_opt_revision_t rev;
 
920
      apr_array_header_t *config_ignores;
 
921
      apr_hash_t *local_ignores_hash;
 
922
 
 
923
      SVN_ERR(svn_client__get_inherited_ignores(&global_ignores, url, ctx,
 
924
                                                scratch_pool, iterpool));
 
925
      SVN_ERR(svn_wc_get_default_ignores(&config_ignores, ctx->config,
 
926
                                         scratch_pool));
 
927
      global_ignores = apr_array_append(scratch_pool, global_ignores,
 
928
                                        config_ignores);
 
929
 
 
930
      rev.kind = svn_opt_revision_head;
 
931
      SVN_ERR(svn_client_propget5(&local_ignores_hash, NULL, SVN_PROP_IGNORE, url,
 
932
                                  &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
 
933
                                  scratch_pool, scratch_pool));
 
934
      local_ignores_arr = apr_array_make(scratch_pool, 1, sizeof(const char *));
 
935
 
 
936
      if (apr_hash_count(local_ignores_hash))
 
937
        {
 
938
          svn_string_t *propval = svn_hash_gets(local_ignores_hash, url);
 
939
          if (propval)
 
940
            {
 
941
              svn_cstring_split_append(local_ignores_arr, propval->data,
 
942
                                       "\n\r\t\v ", FALSE, scratch_pool);
 
943
            }
 
944
        }
 
945
    }
 
946
 
 
947
  /* If an error occurred during the commit, abort the edit and return
 
948
     the error.  We don't even care if the abort itself fails.  */
 
949
  if ((err = import(local_abspath, new_entries, editor, edit_baton,
 
950
                    depth, excludes, autoprops, local_ignores_arr,
 
951
                    global_ignores, no_ignore, no_autoprops,
 
952
                    ignore_unknown_node_types, filter_callback,
 
953
                    filter_baton, ctx, iterpool)))
 
954
    {
 
955
      return svn_error_compose_create(
 
956
                    err,
 
957
                    editor->abort_edit(edit_baton, iterpool));
 
958
    }
 
959
 
 
960
  svn_pool_destroy(iterpool);
 
961
 
 
962
  return SVN_NO_ERROR;
 
963
}
 
964