/* Verify that the path can be deleted without losing stuff, i.e. ensure that there are no modified or unversioned resources under PATH. This is similar to checking the output of the status command. CTX is used for the client's config options. POOL is used for all temporary allocations. */ static svn_error_t * can_delete_node(svn_boolean_t *target_missing, const char *local_abspath, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { apr_array_header_t *ignores; struct can_delete_baton_t cdt; /* Use an infinite-depth status check to see if there's anything in or under PATH which would make it unsafe for deletion. The status callback function find_undeletables() makes the determination, returning an error if it finds anything that shouldn't be deleted. */ SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, scratch_pool)); cdt.root_abspath = local_abspath; cdt.target_missing = FALSE; SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, local_abspath, svn_depth_infinity, FALSE /* get_all */, FALSE /* no_ignore */, FALSE /* ignore_text_mod */, ignores, find_undeletables, &cdt, ctx->cancel_func, ctx->cancel_baton, scratch_pool)); if (target_missing) *target_missing = cdt.target_missing; return SVN_NO_ERROR; }
svn_error_t * svn_client_status6(svn_revnum_t *result_rev, svn_client_ctx_t *ctx, const char *path, const svn_opt_revision_t *revision, svn_depth_t depth, svn_boolean_t get_all, svn_boolean_t check_out_of_date, svn_boolean_t check_working_copy, svn_boolean_t no_ignore, svn_boolean_t ignore_externals, svn_boolean_t depth_as_sticky, const apr_array_header_t *changelists, svn_client_status_func_t status_func, void *status_baton, apr_pool_t *pool) /* ### aka scratch_pool */ { struct status_baton sb; const char *dir, *dir_abspath; const char *target_abspath; const char *target_basename; apr_array_header_t *ignores; svn_error_t *err; apr_hash_t *changelist_hash = NULL; /* Override invalid combinations of the check_out_of_date and check_working_copy flags. */ if (!check_out_of_date) check_working_copy = TRUE; if (svn_path_is_url(path)) return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, _("'%s' is not a local path"), path); if (changelists && changelists->nelts) SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool)); if (result_rev) *result_rev = SVN_INVALID_REVNUM; sb.real_status_func = status_func; sb.real_status_baton = status_baton; sb.deleted_in_repos = FALSE; sb.changelist_hash = changelist_hash; sb.wc_ctx = ctx->wc_ctx; SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool)); if (check_out_of_date) { /* The status editor only works on directories, so get the ancestor if necessary */ svn_node_kind_t kind; SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath, TRUE, FALSE, pool)); /* Dir must be a working copy directory or the status editor fails */ if (kind == svn_node_dir) { dir_abspath = target_abspath; target_basename = ""; dir = path; } else { dir_abspath = svn_dirent_dirname(target_abspath, pool); target_basename = svn_dirent_basename(target_abspath, NULL); dir = svn_dirent_dirname(path, pool); if (kind == svn_node_file) { if (depth == svn_depth_empty) depth = svn_depth_files; } else { err = svn_wc_read_kind2(&kind, ctx->wc_ctx, dir_abspath, FALSE, FALSE, pool); svn_error_clear(err); if (err || kind != svn_node_dir) { return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, _("'%s' is not a working copy"), svn_dirent_local_style(path, pool)); } } } } else { dir = path; dir_abspath = target_abspath; } if (svn_dirent_is_absolute(dir)) { sb.anchor_abspath = NULL; sb.anchor_relpath = NULL; } else { sb.anchor_abspath = dir_abspath; sb.anchor_relpath = dir; } /* Get the status edit, and use our wrapping status function/baton as the callback pair. */ SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool)); /* If we want to know about out-of-dateness, we crawl the working copy and let the RA layer drive the editor for real. Otherwise, we just close the edit. :-) */ if (check_out_of_date) { svn_ra_session_t *ra_session; const char *URL; svn_node_kind_t kind; svn_boolean_t server_supports_depth; const svn_delta_editor_t *editor; void *edit_baton, *set_locks_baton; svn_revnum_t edit_revision = SVN_INVALID_REVNUM; /* Get full URL from the ANCHOR. */ SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx, pool, pool)); if (!URL) return svn_error_createf (SVN_ERR_ENTRY_MISSING_URL, NULL, _("Entry '%s' has no URL"), svn_dirent_local_style(dir, pool)); /* Open a repository session to the URL. */ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL, dir_abspath, NULL, FALSE, TRUE, ctx, pool, pool)); SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton, &edit_revision, ctx->wc_ctx, dir_abspath, target_basename, depth, get_all, check_working_copy, no_ignore, depth_as_sticky, server_supports_depth, ignores, tweak_status, &sb, ctx->cancel_func, ctx->cancel_baton, pool, pool)); /* Verify that URL exists in HEAD. If it doesn't, this can save us a whole lot of hassle; if it does, the cost of this request should be minimal compared to the size of getting back the average amount of "out-of-date" information. */ SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind, pool)); if (kind == svn_node_none) { svn_boolean_t added; /* Our status target does not exist in HEAD. If we've got it locally added, that's okay. But if it was previously versioned, then it must have since been deleted from the repository. (Note that "locally replaced" doesn't count as "added" in this case.) */ SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx, dir_abspath, pool)); if (! added) sb.deleted_in_repos = TRUE; /* And now close the edit. */ SVN_ERR(editor->close_edit(edit_baton, pool)); } else { svn_revnum_t revnum; report_baton_t rb; svn_depth_t status_depth; if (revision->kind == svn_opt_revision_head) { /* Cause the revision number to be omitted from the request, which implies HEAD. */ revnum = SVN_INVALID_REVNUM; } else { /* Get a revision number for our status operation. */ SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx, target_abspath, ra_session, revision, pool)); } if (depth_as_sticky || !server_supports_depth) status_depth = depth; else status_depth = svn_depth_unknown; /* Use depth from WC */ /* Do the deed. Let the RA layer drive the status editor. */ SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter, &rb.wrapped_report_baton, target_basename, revnum, status_depth, editor, edit_baton, pool)); /* Init the report baton. */ rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */ rb.set_locks_baton = set_locks_baton; rb.ctx = ctx; rb.pool = pool; if (depth == svn_depth_unknown) rb.depth = svn_depth_infinity; else rb.depth = depth; /* Drive the reporter structure, describing the revisions within PATH. When we call reporter->finish_report, EDITOR will be driven to describe differences between our working copy and HEAD. */ SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, target_abspath, &lock_fetch_reporter, &rb, FALSE /* restore_files */, depth, (! depth_as_sticky), (! server_supports_depth), FALSE /* use_commit_times */, ctx->cancel_func, ctx->cancel_baton, NULL, NULL, pool)); } if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(target_abspath, svn_wc_notify_status_completed, pool); notify->revision = edit_revision; ctx->notify_func2(ctx->notify_baton2, notify, pool); } /* If the caller wants the result revision, give it to them. */ if (result_rev) *result_rev = edit_revision; } else { err = svn_wc_walk_status(ctx->wc_ctx, target_abspath, depth, get_all, no_ignore, FALSE, ignores, tweak_status, &sb, ctx->cancel_func, ctx->cancel_baton, pool); if (err && err->apr_err == SVN_ERR_WC_MISSING) { /* This error code is checked for in svn to continue after this error */ svn_error_clear(err); return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, _("'%s' is not a working copy"), svn_dirent_local_style(path, pool)); } SVN_ERR(err); } /* We only descend into an external if depth is svn_depth_infinity or svn_depth_unknown. However, there are conceivable behaviors that would involve descending under other circumstances; thus, we pass depth anyway, so the code will DTRT if we change the conditional in the future. */ if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals)) { apr_hash_t *external_map; SVN_ERR(svn_wc__externals_defined_below(&external_map, ctx->wc_ctx, target_abspath, pool, pool)); SVN_ERR(do_external_status(ctx, external_map, depth, get_all, check_out_of_date, check_working_copy, no_ignore, changelists, sb.anchor_abspath, sb.anchor_relpath, status_func, status_baton, pool)); } return SVN_NO_ERROR; }