2 * add.c: wrappers around wc add/mkdir functionality.
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 /* ==================================================================== */
32 #include <apr_fnmatch.h>
34 #include "svn_client.h"
35 #include "svn_string.h"
36 #include "svn_pools.h"
37 #include "svn_error.h"
38 #include "svn_dirent_uri.h"
41 #include "svn_config.h"
42 #include "svn_props.h"
44 #include "svn_sorts.h"
46 #include "svn_ctype.h"
48 #include "private/svn_client_private.h"
49 #include "private/svn_wc_private.h"
50 #include "private/svn_ra_private.h"
51 #include "private/svn_sorts_private.h"
52 #include "private/svn_magic.h"
54 #include "svn_private_config.h"
60 /* Remove leading and trailing white space from a C string, in place. */
62 trim_string(char **pstr)
67 while (svn_ctype_isspace(*str))
71 while ((i > 0) && svn_ctype_isspace(str[i-1]))
76 /* Remove leading and trailing single- or double quotes from a C string,
79 unquote_string(char **pstr)
82 size_t i = strlen(str);
84 if (i > 0 && ((*str == '"' && str[i - 1] == '"') ||
85 (*str == '\'' && str[i - 1] == '\'')))
93 /* Split PROPERTY and store each individual value in PROPS.
94 Allocates from POOL. */
96 split_props(apr_array_header_t **props,
100 apr_array_header_t *temp_props;
105 temp_props = apr_array_make(pool, 4, sizeof(char *));
106 new_prop = apr_palloc(pool, strlen(property)+1);
108 for (i = 0; property[i] != '\0'; i++)
110 if (property[i] != ';')
112 new_prop[j] = property[i];
115 else if (property[i] == ';')
117 /* ";;" becomes ";" */
118 if (property[i+1] == ';')
127 APR_ARRAY_PUSH(temp_props, char *) = new_prop;
134 APR_ARRAY_PUSH(temp_props, char *) = new_prop;
138 /* PROPVALS is a hash mapping char * property names to const char * property
139 values. PROPERTIES can be empty but not NULL.
141 If FILENAME doesn't match the filename pattern PATTERN case insensitively,
142 the do nothing. Otherwise for each 'name':'value' pair in PROPVALS, add
143 a new entry mappying 'name' to a svn_string_t * wrapping the 'value' in
144 PROPERTIES. The svn_string_t is allocated in the pool used to allocate
145 PROPERTIES, but the char *'s from PROPVALS are re-used in PROPERTIES.
146 If PROPVALS contains a 'svn:mime-type' mapping, then set *MIMETYPE to
147 the mapped value. Likewise if PROPVALS contains a mapping for
148 svn:executable, then set *HAVE_EXECUTABLE to TRUE.
150 Use SCRATCH_POOL for temporary allocations.
153 get_auto_props_for_pattern(apr_hash_t *properties,
154 const char **mimetype,
155 svn_boolean_t *have_executable,
156 const char *filename,
158 apr_hash_t *propvals,
159 apr_pool_t *scratch_pool)
161 apr_hash_index_t *hi;
163 /* check if filename matches and return if it doesn't */
164 if (apr_fnmatch(pattern, filename,
165 APR_FNM_CASE_BLIND) == APR_FNM_NOMATCH)
168 for (hi = apr_hash_first(scratch_pool, propvals);
170 hi = apr_hash_next(hi))
172 const char *propname = apr_hash_this_key(hi);
173 const char *propval = apr_hash_this_val(hi);
174 svn_string_t *propval_str =
175 svn_string_create_empty(apr_hash_pool_get(properties));
177 propval_str->data = propval;
178 propval_str->len = strlen(propval);
180 svn_hash_sets(properties, propname, propval_str);
181 if (strcmp(propname, SVN_PROP_MIME_TYPE) == 0)
183 else if (strcmp(propname, SVN_PROP_EXECUTABLE) == 0)
184 *have_executable = TRUE;
189 svn_client__get_paths_auto_props(apr_hash_t **properties,
190 const char **mimetype,
192 svn_magic__cookie_t *magic_cookie,
193 apr_hash_t *autoprops,
194 svn_client_ctx_t *ctx,
195 apr_pool_t *result_pool,
196 apr_pool_t *scratch_pool)
198 apr_hash_index_t *hi;
199 svn_boolean_t have_executable = FALSE;
201 *properties = apr_hash_make(result_pool);
206 for (hi = apr_hash_first(scratch_pool, autoprops);
208 hi = apr_hash_next(hi))
210 const char *pattern = apr_hash_this_key(hi);
211 apr_hash_t *propvals = apr_hash_this_val(hi);
213 get_auto_props_for_pattern(*properties, mimetype, &have_executable,
214 svn_dirent_basename(path, scratch_pool),
215 pattern, propvals, scratch_pool);
219 /* if mimetype has not been set check the file */
222 SVN_ERR(svn_io_detect_mimetype2(mimetype, path, ctx->mimetypes_map,
225 /* If we got no mime-type, or if it is "application/octet-stream",
226 * try to get the mime-type from libmagic. */
229 strcmp(*mimetype, "application/octet-stream") == 0))
231 const char *magic_mimetype;
233 /* Since libmagic usually treats UTF-16 files as "text/plain",
234 * svn_magic__detect_binary_mimetype() will return NULL for such
235 * files. This is fine for now since we currently don't support
236 * UTF-16-encoded text files (issue #2194).
237 * Once we do support UTF-16 this code path will fail to detect
238 * them as text unless the svn_io_detect_mimetype2() call above
239 * returns "text/plain" for them. */
240 SVN_ERR(svn_magic__detect_binary_mimetype(&magic_mimetype,
245 *mimetype = magic_mimetype;
249 apr_hash_set(*properties, SVN_PROP_MIME_TYPE,
250 strlen(SVN_PROP_MIME_TYPE),
251 svn_string_create(*mimetype, result_pool));
254 /* if executable has not been set check the file */
255 if (! have_executable)
257 svn_boolean_t executable = FALSE;
258 SVN_ERR(svn_io_is_file_executable(&executable, path, scratch_pool));
260 apr_hash_set(*properties, SVN_PROP_EXECUTABLE,
261 strlen(SVN_PROP_EXECUTABLE),
262 svn_string_create_empty(result_pool));
268 /* Only call this if the on-disk node kind is a file. */
270 add_file(const char *local_abspath,
271 svn_magic__cookie_t *magic_cookie,
272 apr_hash_t *autoprops,
273 svn_boolean_t no_autoprops,
274 svn_client_ctx_t *ctx,
277 apr_hash_t *properties;
278 const char *mimetype;
279 svn_node_kind_t kind;
280 svn_boolean_t is_special;
282 /* Check to see if this is a special file. */
283 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special, pool));
285 /* Determine the properties that the file should have */
289 properties = apr_hash_make(pool);
290 svn_hash_sets(properties, SVN_PROP_SPECIAL,
291 svn_string_create(SVN_PROP_BOOLEAN_TRUE, pool));
295 apr_hash_t *file_autoprops = NULL;
297 /* Get automatic properties */
298 /* If we are setting autoprops grab the inherited svn:auto-props and
299 config file auto-props for this file if we haven't already got them
300 when iterating over the file's unversioned parents. */
303 if (autoprops == NULL)
304 SVN_ERR(svn_client__get_all_auto_props(
305 &file_autoprops, svn_dirent_dirname(local_abspath,pool),
308 file_autoprops = autoprops;
311 /* This may fail on write-only files:
312 we open them to estimate file type. */
313 SVN_ERR(svn_client__get_paths_auto_props(&properties, &mimetype,
314 local_abspath, magic_cookie,
315 file_autoprops, ctx, pool,
320 SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath, properties,
321 FALSE /* skip checks */,
322 ctx->notify_func2, ctx->notify_baton2, pool));
327 /* Schedule directory DIR_ABSPATH, and some of the tree under it, for
328 * addition. DEPTH is the depth at this point in the descent (it may
329 * be changed for recursive calls).
331 * If DIR_ABSPATH (or any item below DIR_ABSPATH) is already scheduled for
332 * addition, add will fail and return an error unless FORCE is TRUE.
334 * Use MAGIC_COOKIE (which may be NULL) to detect the mime-type of files
337 * If not NULL, CONFIG_AUTOPROPS is a hash representing the config file and
338 * svn:auto-props autoprops which apply to DIR_ABSPATH. It maps
339 * const char * file patterns to another hash which maps const char *
340 * property names to const char *property values. If CONFIG_AUTOPROPS is
341 * NULL and the config file and svn:auto-props autoprops are required by this
342 * function, then such will be obtained.
344 * If IGNORES is not NULL, then it is an array of const char * ignore patterns
345 * that apply to any children of DIR_ABSPATH. If REFRESH_IGNORES is TRUE, then
346 * the passed in value of IGNORES (if any) is itself ignored and this function
347 * will gather all ignore patterns applicable to DIR_ABSPATH itself (allocated in
348 * RESULT_POOL). Any recursive calls to this function get the refreshed ignore
349 * patterns. If IGNORES is NULL and REFRESH_IGNORES is FALSE, then all children of DIR_ABSPATH
350 * are unconditionally added.
352 * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow
353 * the user to cancel the operation.
355 * Use SCRATCH_POOL for temporary allocations.
358 add_dir_recursive(const char *dir_abspath,
361 svn_boolean_t no_autoprops,
362 svn_magic__cookie_t *magic_cookie,
363 apr_hash_t *config_autoprops,
364 svn_boolean_t refresh_ignores,
365 apr_array_header_t *ignores,
366 svn_client_ctx_t *ctx,
367 apr_pool_t *result_pool,
368 apr_pool_t *scratch_pool)
371 apr_pool_t *iterpool;
373 apr_hash_index_t *hi;
374 svn_boolean_t entry_exists = FALSE;
376 /* Check cancellation; note that this catches recursive calls too. */
377 if (ctx->cancel_func)
378 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
380 iterpool = svn_pool_create(scratch_pool);
382 /* Add this directory to revision control. */
383 err = svn_wc_add_from_disk3(ctx->wc_ctx, dir_abspath, NULL /*props*/,
384 FALSE /* skip checks */,
385 ctx->notify_func2, ctx->notify_baton2,
389 if (err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
391 svn_error_clear(err);
396 return svn_error_trace(err);
400 /* Fetch ignores after adding to handle ignores on the directory itself
401 and ancestors via the single db optimization in libsvn_wc */
403 SVN_ERR(svn_wc_get_ignores2(&ignores, ctx->wc_ctx, dir_abspath,
404 ctx->config, result_pool, iterpool));
406 /* If DIR_ABSPATH is the root of an unversioned subtree then get the
407 following "autoprops":
409 1) Explicit and inherited svn:auto-props properties on
411 2) auto-props from the CTX->CONFIG hash
413 Since this set of autoprops applies to all unversioned children of
414 DIR_ABSPATH, we will pass these along to any recursive calls to
415 add_dir_recursive() and calls to add_file() below. Thus sparing
416 these callees from looking up the same information. */
417 if (!entry_exists && config_autoprops == NULL)
419 SVN_ERR(svn_client__get_all_auto_props(&config_autoprops, dir_abspath,
420 ctx, scratch_pool, iterpool));
423 SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, scratch_pool,
426 /* Read the directory entries one by one and add those things to
428 for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
430 const char *name = apr_hash_this_key(hi);
431 svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
434 svn_pool_clear(iterpool);
436 /* Check cancellation so you can cancel during an
437 * add of a directory with lots of files. */
438 if (ctx->cancel_func)
439 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
441 /* Skip over SVN admin directories. */
442 if (svn_wc_is_adm_dir(name, iterpool))
446 && svn_wc_match_ignore_list(name, ignores, iterpool))
449 /* Construct the full path of the entry. */
450 abspath = svn_dirent_join(dir_abspath, name, iterpool);
452 /* Recurse on directories; add files; ignore the rest. */
453 if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates)
455 svn_depth_t depth_below_here = depth;
456 if (depth == svn_depth_immediates)
457 depth_below_here = svn_depth_empty;
459 /* When DIR_ABSPATH is the root of an unversioned subtree then
460 it and all of its children have the same set of ignores. So
461 save any recursive calls the extra work of finding the same
463 if (refresh_ignores && !entry_exists)
464 refresh_ignores = FALSE;
466 SVN_ERR(add_dir_recursive(abspath, depth_below_here,
468 magic_cookie, config_autoprops,
469 refresh_ignores, ignores, ctx,
470 result_pool, iterpool));
472 else if ((dirent->kind == svn_node_file || dirent->special)
473 && depth >= svn_depth_files)
475 err = add_file(abspath, magic_cookie, config_autoprops,
476 no_autoprops, ctx, iterpool);
477 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
478 svn_error_clear(err);
484 /* Destroy the per-iteration pool. */
485 svn_pool_destroy(iterpool);
490 /* This structure is used as baton for collecting the config entries
491 in the auto-props section and any inherited svn:auto-props
494 typedef struct collect_auto_props_baton_t
496 /* the hash table for storing the property name/value pairs */
497 apr_hash_t *autoprops;
499 /* a pool used for allocating memory */
500 apr_pool_t *result_pool;
501 } collect_auto_props_baton_t;
503 /* Implements svn_config_enumerator2_t callback.
505 For one auto-props config entry (NAME, VALUE), stash a copy of
506 NAME and VALUE, allocated in BATON->POOL, in BATON->AUTOPROP.
507 BATON must point to an collect_auto_props_baton_t.
510 all_auto_props_collector(const char *name,
515 collect_auto_props_baton_t *autoprops_baton = baton;
516 apr_array_header_t *autoprops;
519 /* nothing to do here without a value */
523 split_props(&autoprops, value, pool);
525 for (i = 0; i < autoprops->nelts; i ++)
528 const char *this_value;
529 char *property = APR_ARRAY_IDX(autoprops, i, char *);
530 char *equal_sign = strchr(property, '=');
536 trim_string(&equal_sign);
537 unquote_string(&equal_sign);
538 this_value = equal_sign;
544 trim_string(&property);
545 len = strlen(property);
549 apr_hash_t *pattern_hash = svn_hash_gets(autoprops_baton->autoprops,
551 svn_string_t *propval;
553 /* Force reserved boolean property values to '*'. */
554 if (svn_prop_is_boolean(property))
556 /* SVN_PROP_EXECUTABLE, SVN_PROP_NEEDS_LOCK, SVN_PROP_SPECIAL */
557 propval = svn_string_create("*", autoprops_baton->result_pool);
561 propval = svn_string_create(this_value,
562 autoprops_baton->result_pool);
567 pattern_hash = apr_hash_make(autoprops_baton->result_pool);
568 svn_hash_sets(autoprops_baton->autoprops,
569 apr_pstrdup(autoprops_baton->result_pool, name),
572 svn_hash_sets(pattern_hash,
573 apr_pstrdup(autoprops_baton->result_pool, property),
580 /* Go up the directory tree from LOCAL_ABSPATH, looking for a versioned
581 * directory. If found, return its path in *EXISTING_PARENT_ABSPATH.
582 * Otherwise, return SVN_ERR_CLIENT_NO_VERSIONED_PARENT. */
584 find_existing_parent(const char **existing_parent_abspath,
585 svn_client_ctx_t *ctx,
586 const char *local_abspath,
587 apr_pool_t *result_pool,
588 apr_pool_t *scratch_pool)
590 svn_node_kind_t kind;
591 const char *parent_abspath;
592 svn_wc_context_t *wc_ctx = ctx->wc_ctx;
594 SVN_ERR(svn_wc_read_kind2(&kind, wc_ctx, local_abspath,
595 FALSE, FALSE, scratch_pool));
597 if (kind == svn_node_dir)
599 *existing_parent_abspath = apr_pstrdup(result_pool, local_abspath);
603 if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
604 return svn_error_create(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, NULL);
606 if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, scratch_pool),
608 return svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED, NULL,
609 _("'%s' ends in a reserved name"),
610 svn_dirent_local_style(local_abspath,
613 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
615 if (ctx->cancel_func)
616 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
618 SVN_ERR(find_existing_parent(existing_parent_abspath, ctx, parent_abspath,
619 result_pool, scratch_pool));
625 svn_client__get_all_auto_props(apr_hash_t **autoprops,
626 const char *path_or_url,
627 svn_client_ctx_t *ctx,
628 apr_pool_t *result_pool,
629 apr_pool_t *scratch_pool)
632 apr_array_header_t *inherited_config_auto_props;
634 svn_opt_revision_t rev;
635 svn_string_t *config_auto_prop;
636 svn_boolean_t use_autoprops;
637 collect_auto_props_baton_t autoprops_baton;
638 svn_error_t *err = NULL;
639 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
640 svn_boolean_t target_is_url = svn_path_is_url(path_or_url);
641 svn_config_t *cfg = ctx->config ? svn_hash_gets(ctx->config,
642 SVN_CONFIG_CATEGORY_CONFIG)
644 *autoprops = apr_hash_make(result_pool);
645 autoprops_baton.result_pool = result_pool;
646 autoprops_baton.autoprops = *autoprops;
649 /* Are "traditional" auto-props enabled? If so grab them from the
650 config. This is our starting set auto-props, which may be overriden
651 by svn:auto-props. */
652 SVN_ERR(svn_config_get_bool(cfg, &use_autoprops,
653 SVN_CONFIG_SECTION_MISCELLANY,
654 SVN_CONFIG_OPTION_ENABLE_AUTO_PROPS, FALSE));
656 svn_config_enumerate2(cfg, SVN_CONFIG_SECTION_AUTO_PROPS,
657 all_auto_props_collector, &autoprops_baton,
660 /* Convert the config file setting (if any) into a hash mapping file
661 patterns to as hash of prop-->val mappings. */
662 if (svn_path_is_url(path_or_url))
663 rev.kind = svn_opt_revision_head;
665 rev.kind = svn_opt_revision_working;
667 /* If PATH_OR_URL is a WC path, then it might be unversioned, in which case
668 we find it's nearest versioned parent. */
671 err = svn_client_propget5(&props, &inherited_config_auto_props,
672 SVN_PROP_INHERITABLE_AUTO_PROPS, path_or_url,
673 &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
674 scratch_pool, iterpool);
677 if (target_is_url || err->apr_err != SVN_ERR_UNVERSIONED_RESOURCE)
678 return svn_error_trace(err);
680 svn_error_clear(err);
682 SVN_ERR(find_existing_parent(&path_or_url, ctx, path_or_url,
683 scratch_pool, iterpool));
692 /* Stash any explicit PROPS for PARENT_PATH into the inherited props array,
693 since these are actually inherited props for LOCAL_ABSPATH. */
694 config_auto_prop = svn_hash_gets(props, path_or_url);
696 if (config_auto_prop)
698 svn_prop_inherited_item_t *new_iprop =
699 apr_palloc(scratch_pool, sizeof(*new_iprop));
700 new_iprop->path_or_url = path_or_url;
701 new_iprop->prop_hash = apr_hash_make(scratch_pool);
702 svn_hash_sets(new_iprop->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS,
704 APR_ARRAY_PUSH(inherited_config_auto_props,
705 svn_prop_inherited_item_t *) = new_iprop;
708 for (i = 0; i < inherited_config_auto_props->nelts; i++)
710 svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
711 inherited_config_auto_props, i, svn_prop_inherited_item_t *);
712 const svn_string_t *propval =
713 svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS);
716 const char *ch = propval->data;
717 svn_stringbuf_t *config_auto_prop_pattern;
718 svn_stringbuf_t *config_auto_prop_val;
720 svn_pool_clear(iterpool);
722 config_auto_prop_pattern = svn_stringbuf_create_empty(iterpool);
723 config_auto_prop_val = svn_stringbuf_create_empty(iterpool);
725 /* Parse svn:auto-props value. */
728 svn_stringbuf_setempty(config_auto_prop_pattern);
729 svn_stringbuf_setempty(config_auto_prop_val);
731 /* Parse the file pattern. */
732 while (*ch != '\0' && *ch != '=' && *ch != '\n')
734 svn_stringbuf_appendbyte(config_auto_prop_pattern, *ch);
738 svn_stringbuf_strip_whitespace(config_auto_prop_pattern);
740 /* Parse the auto-prop group. */
741 while (*ch != '\0' && *ch != '\n')
743 svn_stringbuf_appendbyte(config_auto_prop_val, *ch);
747 /* Strip leading '=' and whitespace from auto-prop group. */
748 if (config_auto_prop_val->data[0] == '=')
749 svn_stringbuf_remove(config_auto_prop_val, 0, 1);
750 svn_stringbuf_strip_whitespace(config_auto_prop_val);
752 all_auto_props_collector(config_auto_prop_pattern->data,
753 config_auto_prop_val->data,
757 /* Skip to next line if any. */
758 while (*ch != '\0' && *ch != '\n')
766 svn_pool_destroy(iterpool);
771 /* The main logic of the public svn_client_add5.
773 * EXISTING_PARENT_ABSPATH is the absolute path to the first existing
774 * parent directory of local_abspath. If not NULL, all missing parents
775 * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */
777 add(const char *local_abspath,
780 svn_boolean_t no_ignore,
781 svn_boolean_t no_autoprops,
782 const char *existing_parent_abspath,
783 svn_client_ctx_t *ctx,
784 apr_pool_t *scratch_pool)
786 svn_node_kind_t kind;
788 svn_magic__cookie_t *magic_cookie;
789 apr_array_header_t *ignores = NULL;
791 SVN_ERR(svn_magic__init(&magic_cookie, ctx->config, scratch_pool));
793 if (existing_parent_abspath)
795 const char *parent_abspath;
796 const char *child_relpath;
797 apr_array_header_t *components;
799 apr_pool_t *iterpool;
801 parent_abspath = existing_parent_abspath;
802 child_relpath = svn_dirent_is_child(existing_parent_abspath,
803 local_abspath, NULL);
804 components = svn_path_decompose(child_relpath, scratch_pool);
805 iterpool = svn_pool_create(scratch_pool);
806 for (i = 0; i < components->nelts - 1; i++)
808 const char *component;
809 svn_node_kind_t disk_kind;
811 svn_pool_clear(iterpool);
813 if (ctx->cancel_func)
814 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
816 component = APR_ARRAY_IDX(components, i, const char *);
817 parent_abspath = svn_dirent_join(parent_abspath, component,
819 SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool));
820 if (disk_kind != svn_node_none && disk_kind != svn_node_dir)
821 return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL,
822 _("'%s' prevents creating parent of '%s'"),
823 parent_abspath, local_abspath);
825 SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool));
826 SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, parent_abspath,
828 FALSE /* skip checks */,
829 ctx->notify_func2, ctx->notify_baton2,
832 svn_pool_destroy(iterpool);
835 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
836 if (kind == svn_node_dir)
838 /* We use add_dir_recursive for all directory targets
839 and pass depth along no matter what it is, so that the
840 target's depth will be set correctly. */
841 err = add_dir_recursive(local_abspath, depth, force,
842 no_autoprops, magic_cookie, NULL,
843 !no_ignore, ignores, ctx,
844 scratch_pool, scratch_pool);
846 else if (kind == svn_node_file)
847 err = add_file(local_abspath, magic_cookie, NULL,
848 no_autoprops, ctx, scratch_pool);
849 else if (kind == svn_node_none)
851 svn_boolean_t tree_conflicted;
853 /* Provide a meaningful error message if the node does not exist
854 * on disk but is a tree conflict victim. */
855 err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted,
856 ctx->wc_ctx, local_abspath,
859 svn_error_clear(err);
860 else if (tree_conflicted)
861 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
862 _("'%s' is an existing item in conflict; "
863 "please mark the conflict as resolved "
864 "before adding a new item here"),
865 svn_dirent_local_style(local_abspath,
868 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
870 svn_dirent_local_style(local_abspath,
874 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
875 _("Unsupported node kind for path '%s'"),
876 svn_dirent_local_style(local_abspath,
879 /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */
880 if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
882 svn_error_clear(err);
885 return svn_error_trace(err);
891 svn_client_add5(const char *path,
894 svn_boolean_t no_ignore,
895 svn_boolean_t no_autoprops,
896 svn_boolean_t add_parents,
897 svn_client_ctx_t *ctx,
898 apr_pool_t *scratch_pool)
900 const char *parent_abspath;
901 const char *local_abspath;
902 const char *existing_parent_abspath;
903 svn_boolean_t is_wc_root;
906 if (svn_path_is_url(path))
907 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
908 _("'%s' is not a local path"), path);
910 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
912 /* See if we're being asked to add a wc-root. That's typically not
913 okay, unless we're in "force" mode. svn_wc__is_wcroot()
914 will return TRUE even if LOCAL_ABSPATH is a *symlink* to a working
915 copy root, which is a scenario we want to treat differently. */
916 err = svn_wc__is_wcroot(&is_wc_root, ctx->wc_ctx, local_abspath,
920 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
921 && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
923 return svn_error_trace(err);
926 svn_error_clear(err);
927 err = NULL; /* SVN_NO_ERROR */
933 svn_node_kind_t disk_kind;
934 svn_boolean_t is_special;
936 SVN_ERR(svn_io_check_special_path(local_abspath, &disk_kind, &is_special,
939 /* A symlink can be an unversioned target and a wcroot. Lets try to add
940 the symlink, which can't be a wcroot. */
947 return svn_error_createf(
948 SVN_ERR_ENTRY_EXISTS, NULL,
949 _("'%s' is already under version control"),
950 svn_dirent_local_style(local_abspath,
956 parent_abspath = local_abspath; /* We will only add children */
958 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
960 existing_parent_abspath = NULL;
961 if (add_parents && !is_wc_root)
964 const char *existing_parent_abspath2;
966 subpool = svn_pool_create(scratch_pool);
967 SVN_ERR(find_existing_parent(&existing_parent_abspath2, ctx,
968 parent_abspath, scratch_pool, subpool));
969 if (strcmp(existing_parent_abspath2, parent_abspath) != 0)
970 existing_parent_abspath = existing_parent_abspath2;
971 svn_pool_destroy(subpool);
974 SVN_WC__CALL_WITH_WRITE_LOCK(
975 add(local_abspath, depth, force, no_ignore, no_autoprops,
976 existing_parent_abspath, ctx, scratch_pool),
977 ctx->wc_ctx, (existing_parent_abspath ? existing_parent_abspath
979 FALSE /* lock_anchor */, scratch_pool);
985 path_driver_cb_func(void **dir_baton,
987 void *callback_baton,
991 const svn_delta_editor_t *editor = callback_baton;
992 SVN_ERR(svn_path_check_valid(path, pool));
993 return editor->add_directory(path, parent_baton, NULL,
994 SVN_INVALID_REVNUM, pool, dir_baton);
997 /* Append URL, and all it's non-existent parent directories, to TARGETS.
998 Use TEMPPOOL for temporary allocations and POOL for any additions to
1000 static svn_error_t *
1001 add_url_parents(svn_ra_session_t *ra_session,
1003 apr_array_header_t *targets,
1004 apr_pool_t *temppool,
1007 svn_node_kind_t kind;
1008 const char *parent_url = svn_uri_dirname(url, pool);
1010 SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool));
1011 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
1014 if (kind == svn_node_none)
1015 SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool));
1017 APR_ARRAY_PUSH(targets, const char *) = url;
1019 return SVN_NO_ERROR;
1022 static svn_error_t *
1023 mkdir_urls(const apr_array_header_t *urls,
1024 svn_boolean_t make_parents,
1025 const apr_hash_t *revprop_table,
1026 svn_commit_callback2_t commit_callback,
1028 svn_client_ctx_t *ctx,
1031 svn_ra_session_t *ra_session = NULL;
1032 const svn_delta_editor_t *editor;
1034 const char *log_msg;
1035 apr_array_header_t *targets;
1036 apr_hash_t *targets_hash;
1037 apr_hash_t *commit_revprops;
1042 /* Find any non-existent parent directories */
1045 apr_array_header_t *all_urls = apr_array_make(pool, urls->nelts,
1046 sizeof(const char *));
1047 const char *first_url = APR_ARRAY_IDX(urls, 0, const char *);
1048 apr_pool_t *iterpool = svn_pool_create(pool);
1050 SVN_ERR(svn_client_open_ra_session2(&ra_session, first_url, NULL,
1051 ctx, pool, iterpool));
1053 for (i = 0; i < urls->nelts; i++)
1055 const char *url = APR_ARRAY_IDX(urls, i, const char *);
1057 svn_pool_clear(iterpool);
1058 SVN_ERR(add_url_parents(ra_session, url, all_urls, iterpool, pool));
1061 svn_pool_destroy(iterpool);
1066 /* Condense our list of mkdir targets. */
1067 SVN_ERR(svn_uri_condense_targets(&common, &targets, urls, FALSE,
1070 /*Remove duplicate targets introduced by make_parents with more targets. */
1071 SVN_ERR(svn_hash_from_cstring_keys(&targets_hash, targets, pool));
1072 SVN_ERR(svn_hash_keys(&targets, targets_hash, pool));
1074 if (! targets->nelts)
1077 svn_uri_split(&common, &bname, common, pool);
1078 APR_ARRAY_PUSH(targets, const char *) = bname;
1081 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1082 _("There is no valid URI above '%s'"),
1087 svn_boolean_t resplit = FALSE;
1089 /* We can't "mkdir" the root of an editor drive, so if one of
1090 our targets is the empty string, we need to back everything
1091 up by a path component. */
1092 for (i = 0; i < targets->nelts; i++)
1094 const char *path = APR_ARRAY_IDX(targets, i, const char *);
1105 svn_uri_split(&common, &bname, common, pool);
1108 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
1109 _("There is no valid URI above '%s'"),
1112 for (i = 0; i < targets->nelts; i++)
1114 const char *path = APR_ARRAY_IDX(targets, i, const char *);
1115 path = svn_relpath_join(bname, path, pool);
1116 APR_ARRAY_IDX(targets, i, const char *) = path;
1121 svn_sort__array(targets, svn_sort_compare_paths);
1123 /* ### This reparent may be problematic in limited-authz-to-common-parent
1124 ### scenarios (compare issue #3242). See also issue #3649. */
1126 SVN_ERR(svn_ra_reparent(ra_session, common, pool));
1128 /* Create new commit items and add them to the array. */
1129 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
1131 svn_client_commit_item3_t *item;
1132 const char *tmp_file;
1133 apr_array_header_t *commit_items
1134 = apr_array_make(pool, targets->nelts, sizeof(item));
1136 for (i = 0; i < targets->nelts; i++)
1138 const char *path = APR_ARRAY_IDX(targets, i, const char *);
1140 item = svn_client_commit_item3_create(pool);
1141 item->url = svn_path_url_add_component2(common, path, pool);
1142 item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
1143 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
1146 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
1150 return SVN_NO_ERROR;
1155 SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
1156 log_msg, ctx, pool));
1158 /* Open an RA session for the URL. Note that we don't have a local
1159 directory, nor a place to put temp files. */
1161 SVN_ERR(svn_client_open_ra_session2(&ra_session, common, NULL, ctx,
1164 SVN_ERR(svn_ra_reparent(ra_session, common, pool));
1167 /* Fetch RA commit editor */
1168 SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
1169 svn_client__get_shim_callbacks(ctx->wc_ctx, NULL,
1171 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1175 NULL, TRUE, /* No lock tokens */
1178 /* Call the path-based editor driver. */
1179 err = svn_error_trace(
1180 svn_delta_path_driver2(editor, edit_baton, targets, TRUE,
1181 path_driver_cb_func, (void *)editor, pool));
1185 /* At least try to abort the edit (and fs txn) before throwing err. */
1186 return svn_error_compose_create(
1188 svn_error_trace(editor->abort_edit(edit_baton, pool)));
1191 if (ctx->notify_func2)
1193 svn_wc_notify_t *notify;
1194 notify = svn_wc_create_notify_url(common,
1195 svn_wc_notify_commit_finalizing,
1197 ctx->notify_func2(ctx->notify_baton2, notify, pool);
1200 /* Close the edit. */
1201 return svn_error_trace(editor->close_edit(edit_baton, pool));
1207 svn_client__make_local_parents(const char *local_abspath,
1208 svn_boolean_t make_parents,
1209 svn_client_ctx_t *ctx,
1210 apr_pool_t *scratch_pool)
1213 svn_node_kind_t orig_kind;
1214 SVN_ERR(svn_io_check_path(local_abspath, &orig_kind, scratch_pool));
1216 SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool));
1218 SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool));
1220 err = svn_client_add5(local_abspath, svn_depth_empty, FALSE, FALSE, FALSE,
1221 make_parents, ctx, scratch_pool);
1223 /* If we created a new directory, but couldn't add it to version
1224 control, then delete it. */
1225 if (err && (orig_kind == svn_node_none))
1227 err = svn_error_compose_create(err,
1228 svn_io_remove_dir2(local_abspath, FALSE,
1233 return svn_error_trace(err);
1238 svn_client_mkdir4(const apr_array_header_t *paths,
1239 svn_boolean_t make_parents,
1240 const apr_hash_t *revprop_table,
1241 svn_commit_callback2_t commit_callback,
1243 svn_client_ctx_t *ctx,
1247 return SVN_NO_ERROR;
1249 SVN_ERR(svn_client__assert_homogeneous_target_type(paths));
1251 if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *)))
1253 SVN_ERR(mkdir_urls(paths, make_parents, revprop_table, commit_callback,
1254 commit_baton, ctx, pool));
1258 /* This is a regular "mkdir" + "svn add" */
1259 apr_pool_t *iterpool = svn_pool_create(pool);
1262 for (i = 0; i < paths->nelts; i++)
1264 const char *path = APR_ARRAY_IDX(paths, i, const char *);
1266 svn_pool_clear(iterpool);
1268 /* See if the user wants us to stop. */
1269 if (ctx->cancel_func)
1270 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
1272 SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool));
1274 SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx,
1277 svn_pool_destroy(iterpool);
1280 return SVN_NO_ERROR;