/* The main logic of the public svn_client_add4; the only difference is that this function uses an existing access baton. (svn_client_add4 just generates an access baton and calls this func.) */ static svn_error_t * add(const char *path, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, svn_wc_adm_access_t *adm_access, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_node_kind_t kind; svn_error_t *err; SVN_ERR(svn_io_check_path(path, &kind, pool)); if (kind == svn_node_dir && depth >= svn_depth_files) err = add_dir_recursive(path, adm_access, depth, force, no_ignore, ctx, pool); else if (kind == svn_node_file) err = add_file(path, ctx, adm_access, pool); else err = svn_wc_add2(path, adm_access, NULL, SVN_INVALID_REVNUM, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool); /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */ if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) { svn_error_clear(err); err = SVN_NO_ERROR; } return err; }
/* The main logic of the public svn_client_add4. * * EXISTING_PARENT_ABSPATH is the absolute path to the first existing * parent directory of local_abspath. If not NULL, all missing parents * of LOCAL_ABSPATH must be created before LOCAL_ABSPATH can be added. */ static svn_error_t * add(const char *local_abspath, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, const char *existing_parent_abspath, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { svn_node_kind_t kind; svn_error_t *err; svn_magic__cookie_t *magic_cookie; svn_magic__init(&magic_cookie, scratch_pool); if (existing_parent_abspath) { const char *parent_abspath; const char *child_relpath; apr_array_header_t *components; int i; apr_pool_t *iterpool; parent_abspath = existing_parent_abspath; child_relpath = svn_dirent_is_child(existing_parent_abspath, local_abspath, NULL); components = svn_path_decompose(child_relpath, scratch_pool); iterpool = svn_pool_create(scratch_pool); for (i = 0; i < components->nelts - 1; i++) { const char *component; svn_node_kind_t disk_kind; svn_pool_clear(iterpool); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); component = APR_ARRAY_IDX(components, i, const char *); parent_abspath = svn_dirent_join(parent_abspath, component, scratch_pool); SVN_ERR(svn_io_check_path(parent_abspath, &disk_kind, iterpool)); if (disk_kind != svn_node_none && disk_kind != svn_node_dir) return svn_error_createf(SVN_ERR_CLIENT_NO_VERSIONED_PARENT, NULL, _("'%s' prevents creating parent of '%s'"), parent_abspath, local_abspath); SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool)); SVN_ERR(svn_wc_add_from_disk(ctx->wc_ctx, parent_abspath, ctx->notify_func2, ctx->notify_baton2, scratch_pool)); } svn_pool_destroy(iterpool); } SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool)); if (kind == svn_node_dir) { /* We use add_dir_recursive for all directory targets and pass depth along no matter what it is, so that the target's depth will be set correctly. */ err = add_dir_recursive(local_abspath, depth, force, no_ignore, magic_cookie, ctx, scratch_pool); } else if (kind == svn_node_file) err = add_file(local_abspath, magic_cookie, ctx, scratch_pool); else if (kind == svn_node_none) { svn_boolean_t tree_conflicted; /* Provide a meaningful error message if the node does not exist * on disk but is a tree conflict victim. */ err = svn_wc_conflicted_p3(NULL, NULL, &tree_conflicted, ctx->wc_ctx, local_abspath, scratch_pool); if (err) svn_error_clear(err); else if (tree_conflicted) return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, _("'%s' is an existing item in conflict; " "please mark the conflict as resolved " "before adding a new item here"), svn_dirent_local_style(local_abspath, scratch_pool)); return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, _("'%s' not found"), svn_dirent_local_style(local_abspath, scratch_pool)); } else return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, _("Unsupported node kind for path '%s'"), svn_dirent_local_style(local_abspath, scratch_pool)); /* Ignore SVN_ERR_ENTRY_EXISTS when FORCE is set. */ if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) { svn_error_clear(err); err = SVN_NO_ERROR; } return svn_error_trace(err); }
/* Schedule directory DIR_ABSPATH, and some of the tree under it, for * addition. DEPTH is the depth at this * point in the descent (it may be changed for recursive calls). * * If DIR_ABSPATH (or any item below DIR_ABSPATH) is already scheduled for * addition, add will fail and return an error unless FORCE is TRUE. * * Files and directories that match ignore patterns will not be added unless * NO_IGNORE is TRUE. * * Use MAGIC_COOKIE (which may be NULL) to detect the mime-type of files * if necessary. * * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow * the user to cancel the operation */ static svn_error_t * add_dir_recursive(const char *dir_abspath, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, svn_magic__cookie_t *magic_cookie, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { svn_error_t *err; apr_pool_t *iterpool; apr_array_header_t *ignores; apr_hash_t *dirents; apr_hash_index_t *hi; /* Check cancellation; note that this catches recursive calls too. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); iterpool = svn_pool_create(scratch_pool); /* Add this directory to revision control. */ err = svn_wc_add_from_disk(ctx->wc_ctx, dir_abspath, ctx->notify_func2, ctx->notify_baton2, iterpool); if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) svn_error_clear(err); else if (err) return svn_error_trace(err); if (!no_ignore) { SVN_ERR(svn_wc_get_ignores2(&ignores, ctx->wc_ctx, dir_abspath, ctx->config, scratch_pool, iterpool)); } SVN_ERR(svn_io_get_dirents3(&dirents, dir_abspath, TRUE, scratch_pool, iterpool)); /* Read the directory entries one by one and add those things to version control. */ for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) { const char *name = svn__apr_hash_index_key(hi); svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi); const char *abspath; svn_pool_clear(iterpool); /* Check cancellation so you can cancel during an * add of a directory with lots of files. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Skip over SVN admin directories. */ if (svn_wc_is_adm_dir(name, iterpool)) continue; if ((!no_ignore) && svn_wc_match_ignore_list(name, ignores, iterpool)) continue; /* Construct the full path of the entry. */ abspath = svn_dirent_join(dir_abspath, name, iterpool); /* Recurse on directories; add files; ignore the rest. */ if (dirent->kind == svn_node_dir && depth >= svn_depth_immediates) { svn_depth_t depth_below_here = depth; if (depth == svn_depth_immediates) depth_below_here = svn_depth_empty; SVN_ERR(add_dir_recursive(abspath, depth_below_here, force, no_ignore, magic_cookie, ctx, iterpool)); } else if ((dirent->kind == svn_node_file || dirent->special) && depth >= svn_depth_files) { err = add_file(abspath, magic_cookie, ctx, iterpool); if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) svn_error_clear(err); else SVN_ERR(err); } } /* Destroy the per-iteration pool. */ svn_pool_destroy(iterpool); return SVN_NO_ERROR; }
/* Schedule directory DIRNAME, and some of the tree under it, for * addition with access baton ADM_ACCESS. DEPTH is the depth at this * point in the descent (it may be changed for recursive calls). * * If DIRNAME (or any item below directory DIRNAME) is already scheduled for * addition, add will fail and return an error unless FORCE is TRUE. * * Files and directories that match ignore patterns will not be added unless * NO_IGNORE is TRUE. * * If CTX->CANCEL_FUNC is non-null, call it with CTX->CANCEL_BATON to allow * the user to cancel the operation */ static svn_error_t * add_dir_recursive(const char *dirname, svn_wc_adm_access_t *adm_access, svn_depth_t depth, svn_boolean_t force, svn_boolean_t no_ignore, svn_client_ctx_t *ctx, apr_pool_t *pool) { apr_dir_t *dir; apr_finfo_t this_entry; svn_error_t *err; apr_pool_t *subpool; apr_int32_t flags = APR_FINFO_TYPE | APR_FINFO_NAME; svn_wc_adm_access_t *dir_access; apr_array_header_t *ignores; /* Check cancellation; note that this catches recursive calls too. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Add this directory to revision control. */ err = svn_wc_add2(dirname, adm_access, NULL, SVN_INVALID_REVNUM, ctx->cancel_func, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, pool); if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) svn_error_clear(err); else if (err) return err; SVN_ERR(svn_wc_adm_retrieve(&dir_access, adm_access, dirname, pool)); if (!no_ignore) SVN_ERR(svn_wc_get_ignores(&ignores, ctx->config, dir_access, pool)); subpool = svn_pool_create(pool); SVN_ERR(svn_io_dir_open(&dir, dirname, pool)); /* Read the directory entries one by one and add those things to version control. */ while (1) { const char *fullpath; svn_pool_clear(subpool); err = svn_io_dir_read(&this_entry, flags, dir, subpool); if (err) { /* Check if we're done reading the dir's entries. */ if (APR_STATUS_IS_ENOENT(err->apr_err)) { apr_status_t apr_err; svn_error_clear(err); apr_err = apr_dir_close(dir); if (apr_err) return svn_error_wrap_apr (apr_err, _("Can't close directory '%s'"), svn_path_local_style(dirname, subpool)); break; } else { return svn_error_createf (err->apr_err, err, _("Error during add of '%s'"), svn_path_local_style(dirname, subpool)); } } /* Skip entries for this dir and its parent. */ if (this_entry.name[0] == '.' && (this_entry.name[1] == '\0' || (this_entry.name[1] == '.' && this_entry.name[2] == '\0'))) continue; /* Check cancellation so you can cancel during an * add of a directory with lots of files. */ if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); /* Skip over SVN admin directories. */ if (svn_wc_is_adm_dir(this_entry.name, subpool)) continue; if ((!no_ignore) && svn_wc_match_ignore_list(this_entry.name, ignores, subpool)) continue; /* Construct the full path of the entry. */ fullpath = svn_path_join(dirname, this_entry.name, subpool); /* Recurse on directories; add files; ignore the rest. */ if (this_entry.filetype == APR_DIR && depth >= svn_depth_immediates) { svn_depth_t depth_below_here = depth; if (depth == svn_depth_immediates) depth_below_here = svn_depth_empty; SVN_ERR(add_dir_recursive(fullpath, dir_access, depth_below_here, force, no_ignore, ctx, subpool)); } else if (this_entry.filetype != APR_UNKFILE && this_entry.filetype != APR_DIR && depth >= svn_depth_files) { err = add_file(fullpath, ctx, dir_access, subpool); if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force) svn_error_clear(err); else if (err) return err; } } /* Opened by svn_wc_add */ SVN_ERR(svn_wc_adm_close(dir_access)); /* Destroy the per-iteration pool. */ svn_pool_destroy(subpool); return SVN_NO_ERROR; }