svn_error_t * svn_client__wc_node_get_origin(svn_client__pathrev_t **origin_p, const char *wc_abspath, svn_client_ctx_t *ctx, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *relpath; *origin_p = apr_palloc(result_pool, sizeof(**origin_p)); SVN_ERR(svn_wc__node_get_origin(NULL /* is_copy */, &(*origin_p)->rev, &relpath, &(*origin_p)->repos_root_url, &(*origin_p)->repos_uuid, NULL, ctx->wc_ctx, wc_abspath, FALSE /* scan_deleted */, result_pool, scratch_pool)); if ((*origin_p)->repos_root_url && relpath) { (*origin_p)->url = svn_path_url_add_component2( (*origin_p)->repos_root_url, relpath, result_pool); } else { *origin_p = NULL; } return SVN_NO_ERROR; }
static svn_error_t * get_ra_editor(const svn_delta_editor_t **editor, void **edit_baton, svn_ra_session_t *ra_session, svn_client_ctx_t *ctx, const char *log_msg, const apr_array_header_t *commit_items, const apr_hash_t *revprop_table, apr_hash_t *lock_tokens, svn_boolean_t keep_locks, svn_commit_callback2_t commit_callback, void *commit_baton, apr_pool_t *pool) { apr_hash_t *commit_revprops; apr_hash_t *relpath_map = NULL; SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, log_msg, ctx, pool)); #ifdef ENABLE_EV2_SHIMS if (commit_items) { int i; apr_pool_t *iterpool = svn_pool_create(pool); relpath_map = apr_hash_make(pool); for (i = 0; i < commit_items->nelts; i++) { svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item3_t *); const char *relpath; if (!item->path) continue; svn_pool_clear(iterpool); SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL, NULL, ctx->wc_ctx, item->path, FALSE, pool, iterpool)); if (relpath) svn_hash_sets(relpath_map, relpath, item->path); } svn_pool_destroy(iterpool); } #endif /* Fetch RA commit editor. */ SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session, svn_client__get_shim_callbacks(ctx->wc_ctx, relpath_map, pool))); SVN_ERR(svn_ra_get_commit_editor3(ra_session, editor, edit_baton, commit_revprops, commit_callback, commit_baton, lock_tokens, keep_locks, pool)); return SVN_NO_ERROR; }
/* Make an unversioned copy of the versioned file or directory tree at the * source path FROM_ABSPATH. Copy it to the destination path TO_ABSPATH. * * If REVISION is svn_opt_revision_working, copy the working version, * otherwise copy the base version. * * See copy_one_versioned_file() for details of file copying behaviour, * including IGNORE_KEYWORDS and NATIVE_EOL. * * Include externals unless IGNORE_EXTERNALS is true. * * Recurse according to DEPTH. * */ static svn_error_t * copy_versioned_files(const char *from_abspath, const char *to_abspath, const svn_opt_revision_t *revision, svn_boolean_t force, svn_boolean_t ignore_externals, svn_boolean_t ignore_keywords, svn_depth_t depth, const char *native_eol, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_error_t *err; apr_pool_t *iterpool; const apr_array_header_t *children; svn_node_kind_t from_kind; svn_depth_t node_depth; SVN_ERR_ASSERT(svn_dirent_is_absolute(from_abspath)); SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath)); /* Only export 'added' and 'replaced' files when the revision is WORKING; when the revision is BASE (i.e. != WORKING), only export 'added' and 'replaced' files when they are part of a copy-/move-here. Otherwise, skip them, since they don't have an associated text-base. This condition for added/replaced simply is an optimization. Added and replaced files would be handled similarly by svn_wc_get_pristine_contents2(), which would return NULL if they have no base associated. TODO: We may prefer not to duplicate this condition and rather use svn_wc_get_pristine_contents2() or a dedicated new function instead. Don't export 'deleted' files and directories unless it's a revision other than WORKING. These files and directories don't really exist in WORKING. */ if (revision->kind != svn_opt_revision_working) { svn_boolean_t is_added; const char *repos_relpath; SVN_ERR(svn_wc__node_get_origin(&is_added, NULL, &repos_relpath, NULL, NULL, NULL, ctx->wc_ctx, from_abspath, FALSE, pool, pool)); if (is_added && !repos_relpath) return SVN_NO_ERROR; /* Local addition */ } else { svn_boolean_t is_deleted; SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx, from_abspath, pool)); if (is_deleted) return SVN_NO_ERROR; } SVN_ERR(svn_wc_read_kind(&from_kind, ctx->wc_ctx, from_abspath, FALSE, pool)); if (from_kind == svn_node_dir) { apr_fileperms_t perm = APR_OS_DEFAULT; int j; /* Try to make the new directory. If this fails because the directory already exists, check our FORCE flag to see if we care. */ /* Keep the source directory's permissions if applicable. Skip retrieving the umask on windows. Apr does not implement setting filesystem privileges on Windows. Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER is documented to be 'incredibly expensive' */ #ifndef WIN32 if (revision->kind == svn_opt_revision_working) { apr_finfo_t finfo; SVN_ERR(svn_io_stat(&finfo, from_abspath, APR_FINFO_PROT, pool)); perm = finfo.protection; } #endif err = svn_io_dir_make(to_abspath, perm, pool); if (err) { if (! APR_STATUS_IS_EEXIST(err->apr_err)) return svn_error_trace(err); if (! force) SVN_ERR_W(err, _("Destination directory exists, and will not be " "overwritten unless forced")); else svn_error_clear(err); } SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx, from_abspath, FALSE, pool, pool)); iterpool = svn_pool_create(pool); for (j = 0; j < children->nelts; j++) { const char *child_abspath = APR_ARRAY_IDX(children, j, const char *); const char *child_name = svn_dirent_basename(child_abspath, NULL); const char *target_abspath; svn_node_kind_t child_kind; svn_pool_clear(iterpool); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); target_abspath = svn_dirent_join(to_abspath, child_name, iterpool); SVN_ERR(svn_wc_read_kind(&child_kind, ctx->wc_ctx, child_abspath, FALSE, iterpool)); if (child_kind == svn_node_dir) { if (depth == svn_depth_infinity || depth == svn_depth_immediates) { if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(target_abspath, svn_wc_notify_update_add, pool); notify->kind = svn_node_dir; (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); } if (depth == svn_depth_infinity) SVN_ERR(copy_versioned_files(child_abspath, target_abspath, revision, force, ignore_externals, ignore_keywords, depth, native_eol, ctx, iterpool)); else SVN_ERR(svn_io_make_dir_recursively(target_abspath, iterpool)); } } else if (child_kind == svn_node_file && depth >= svn_depth_files) { svn_node_kind_t external_kind; SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL, ctx->wc_ctx, child_abspath, child_abspath, TRUE, pool, pool)); if (external_kind != svn_node_file) SVN_ERR(copy_one_versioned_file(child_abspath, target_abspath, ctx, revision, native_eol, ignore_keywords, iterpool)); } } SVN_ERR(svn_wc__node_get_depth(&node_depth, ctx->wc_ctx, from_abspath, pool)); /* Handle externals. */ if (! ignore_externals && depth == svn_depth_infinity && node_depth == svn_depth_infinity) { apr_array_header_t *ext_items; const svn_string_t *prop_val; SVN_ERR(svn_wc_prop_get2(&prop_val, ctx->wc_ctx, from_abspath, SVN_PROP_EXTERNALS, pool, pool)); if (prop_val != NULL) { int i; SVN_ERR(svn_wc_parse_externals_description3(&ext_items, from_abspath, prop_val->data, FALSE, pool)); for (i = 0; i < ext_items->nelts; ++i) { svn_wc_external_item2_t *ext_item; const char *new_from, *new_to; svn_pool_clear(iterpool); ext_item = APR_ARRAY_IDX(ext_items, i, svn_wc_external_item2_t *); new_from = svn_dirent_join(from_abspath, ext_item->target_dir, iterpool); new_to = svn_dirent_join(to_abspath, ext_item->target_dir, iterpool); /* The target dir might have parents that don't exist. Guarantee the path upto the last component. */ if (!svn_dirent_is_root(ext_item->target_dir, strlen(ext_item->target_dir))) { const char *parent = svn_dirent_dirname(new_to, iterpool); SVN_ERR(svn_io_make_dir_recursively(parent, iterpool)); } SVN_ERR(copy_versioned_files(new_from, new_to, revision, force, FALSE, ignore_keywords, svn_depth_infinity, native_eol, ctx, iterpool)); } }
svn_error_t * svn_client__get_revision_number(svn_revnum_t *revnum, svn_revnum_t *youngest_rev, svn_wc_context_t *wc_ctx, const char *local_abspath, svn_ra_session_t *ra_session, const svn_opt_revision_t *revision, apr_pool_t *scratch_pool) { switch (revision->kind) { case svn_opt_revision_unspecified: *revnum = SVN_INVALID_REVNUM; break; case svn_opt_revision_number: *revnum = revision->value.number; break; case svn_opt_revision_head: /* If our caller provided a value for HEAD that he wants us to use, we'll use it. Otherwise, we have to query the repository (and possible return our fetched value in *YOUNGEST_REV, too). */ if (youngest_rev && SVN_IS_VALID_REVNUM(*youngest_rev)) { *revnum = *youngest_rev; } else { if (! ra_session) return svn_error_create(SVN_ERR_CLIENT_RA_ACCESS_REQUIRED, NULL, NULL); SVN_ERR(svn_ra_get_latest_revnum(ra_session, revnum, scratch_pool)); if (youngest_rev) *youngest_rev = *revnum; } break; case svn_opt_revision_working: case svn_opt_revision_base: { svn_error_t *err; /* Sanity check. */ if (local_abspath == NULL) return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED, NULL, NULL); /* The BASE, COMMITTED, and PREV revision keywords do not apply to URLs. */ if (svn_path_is_url(local_abspath)) goto invalid_rev_arg; err = svn_wc__node_get_origin(NULL, revnum, NULL, NULL, NULL, NULL, wc_ctx, local_abspath, TRUE, scratch_pool, scratch_pool); /* Return the same error as older code did (before and at r935091). At least svn_client_proplist4 promises SVN_ERR_ENTRY_NOT_FOUND. */ if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) { svn_error_clear(err); return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, _("'%s' is not under version control"), svn_dirent_local_style(local_abspath, scratch_pool)); } else SVN_ERR(err); if (! SVN_IS_VALID_REVNUM(*revnum)) return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Path '%s' has no committed " "revision"), svn_dirent_local_style(local_abspath, scratch_pool)); } break; case svn_opt_revision_committed: case svn_opt_revision_previous: { /* Sanity check. */ if (local_abspath == NULL) return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED, NULL, NULL); /* The BASE, COMMITTED, and PREV revision keywords do not apply to URLs. */ if (svn_path_is_url(local_abspath)) goto invalid_rev_arg; SVN_ERR(svn_wc__node_get_changed_info(revnum, NULL, NULL, wc_ctx, local_abspath, scratch_pool, scratch_pool)); if (! SVN_IS_VALID_REVNUM(*revnum)) return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Path '%s' has no committed " "revision"), svn_dirent_local_style(local_abspath, scratch_pool)); if (revision->kind == svn_opt_revision_previous) (*revnum)--; } break; case svn_opt_revision_date: /* ### When revision->kind == svn_opt_revision_date, is there an ### optimization such that we can compare ### revision->value->date with the committed-date in the ### entries file (or rather, with some range of which ### committed-date is one endpoint), and sometimes avoid a ### trip over the RA layer? The only optimizations I can ### think of involve examining other entries to build a ### timespan across which committed-revision is known to be ### the head, but it doesn't seem worth it. -kff */ if (! ra_session) return svn_error_create(SVN_ERR_CLIENT_RA_ACCESS_REQUIRED, NULL, NULL); SVN_ERR(svn_ra_get_dated_revision(ra_session, revnum, revision->value.date, scratch_pool)); break; default: return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, _("Unrecognized revision type requested for " "'%s'"), svn_dirent_local_style(local_abspath, scratch_pool)); } /* Final check -- if our caller provided a youngest revision, and the number we wound up with (after talking to the server) is younger than that revision, we need to stick to our caller's idea of "youngest". */ if (youngest_rev && (revision->kind == svn_opt_revision_head || revision->kind == svn_opt_revision_date) && SVN_IS_VALID_REVNUM(*youngest_rev) && SVN_IS_VALID_REVNUM(*revnum) && (*revnum > *youngest_rev)) *revnum = *youngest_rev; return SVN_NO_ERROR; invalid_rev_arg: return svn_error_create( SVN_ERR_CLIENT_BAD_REVISION, NULL, _("PREV, BASE, or COMMITTED revision keywords are invalid for URL")); }