Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / libsvn_client / delete.c
blob4ff3429b6cf90043bb2b943ce2b585adb6a464b4
1 /*
2 * delete.c: wrappers around wc delete functionality.
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes. ***/
25 #include <apr_file_io.h>
26 #include "svn_types.h"
27 #include "svn_pools.h"
28 #include "svn_wc.h"
29 #include "svn_client.h"
30 #include "svn_error.h"
31 #include "svn_path.h"
32 #include "client.h"
34 #include "svn_private_config.h"
37 /*** Code. ***/
39 struct status_baton
41 svn_error_t *err; /* the error generated for an undeletable path. */
42 apr_pool_t *pool; /* for temporary allocations */
46 /* An svn_wc_status_func_t callback function for finding
47 status structures which are not safely deletable. */
48 static void
49 find_undeletables(void *baton,
50 const char *path,
51 svn_wc_status2_t *status)
53 struct status_baton *sb = baton;
55 /* If we already have an error, don't lose that fact. */
56 if (sb->err)
57 return;
59 /* Check for error-ful states. */
60 if (status->text_status == svn_wc_status_obstructed)
61 sb->err = svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
62 _("'%s' is in the way of the resource "
63 "actually under version control"),
64 svn_path_local_style(path, sb->pool));
65 else if (! status->entry)
66 sb->err = svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
67 _("'%s' is not under version control"),
68 svn_path_local_style(path, sb->pool));
70 else if ((status->text_status != svn_wc_status_normal
71 && status->text_status != svn_wc_status_deleted
72 && status->text_status != svn_wc_status_missing)
74 (status->prop_status != svn_wc_status_none
75 && status->prop_status != svn_wc_status_normal))
76 sb->err = svn_error_createf(SVN_ERR_CLIENT_MODIFIED, NULL,
77 _("'%s' has local modifications"),
78 svn_path_local_style(path, sb->pool));
82 svn_error_t *
83 svn_client__can_delete(const char *path,
84 svn_client_ctx_t *ctx,
85 apr_pool_t *pool)
87 struct status_baton sb;
88 svn_opt_revision_t revision;
89 revision.kind = svn_opt_revision_unspecified;
90 sb.err = SVN_NO_ERROR;
91 sb.pool = pool;
93 /* Use an infinite-depth status check to see if there's anything in
94 or under PATH which would make it unsafe for deletion. The
95 status callback function find_undeletables() makes the
96 determination, setting sb.err if it finds anything that shouldn't
97 be deleted. */
98 SVN_ERR(svn_client_status3
99 (NULL, path, &revision, find_undeletables, &sb,
100 svn_depth_infinity, FALSE, FALSE, FALSE, FALSE, NULL, ctx, pool));
101 return sb.err;
105 static svn_error_t *
106 path_driver_cb_func(void **dir_baton,
107 void *parent_baton,
108 void *callback_baton,
109 const char *path,
110 apr_pool_t *pool)
112 const svn_delta_editor_t *editor = callback_baton;
113 *dir_baton = NULL;
114 return editor->delete_entry(path, SVN_INVALID_REVNUM, parent_baton, pool);
118 static svn_error_t *
119 delete_urls(svn_commit_info_t **commit_info_p,
120 const apr_array_header_t *paths,
121 svn_client_ctx_t *ctx,
122 apr_pool_t *pool)
124 svn_ra_session_t *ra_session;
125 const svn_delta_editor_t *editor;
126 void *edit_baton;
127 void *commit_baton;
128 const char *log_msg;
129 apr_hash_t *revprop_table;
130 svn_node_kind_t kind;
131 apr_array_header_t *targets;
132 svn_error_t *err;
133 const char *common;
134 int i;
135 apr_pool_t *subpool = svn_pool_create(pool);
137 /* Condense our list of deletion targets. */
138 SVN_ERR(svn_path_condense_targets(&common, &targets, paths, TRUE, pool));
139 if (! targets->nelts)
141 const char *bname;
142 svn_path_split(common, &common, &bname, pool);
143 APR_ARRAY_PUSH(targets, const char *) = bname;
146 /* Create new commit items and add them to the array. */
147 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
149 svn_client_commit_item3_t *item;
150 const char *tmp_file;
151 apr_array_header_t *commit_items
152 = apr_array_make(pool, targets->nelts, sizeof(item));
154 for (i = 0; i < targets->nelts; i++)
156 const char *path = APR_ARRAY_IDX(targets, i, const char *);
157 SVN_ERR(svn_client_commit_item_create
158 ((const svn_client_commit_item3_t **) &item, pool));
159 item->url = svn_path_join(common, path, pool);
160 item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
161 APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
163 SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
164 ctx, pool));
165 if (! log_msg)
167 svn_pool_destroy(subpool);
168 return SVN_NO_ERROR;
171 else
172 log_msg = "";
174 SVN_ERR(svn_client__get_revprop_table(&revprop_table, log_msg, ctx, pool));
176 /* Open an RA session for the URL. Note that we don't have a local
177 directory, nor a place to put temp files. */
178 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, common, NULL,
179 NULL, NULL, FALSE, TRUE,
180 ctx, pool));
182 /* Verify that each thing to be deleted actually exists (to prevent
183 the creation of a revision that has no changes, since the
184 filesystem allows for no-op deletes). */
185 for (i = 0; i < targets->nelts; i++)
187 const char *path = APR_ARRAY_IDX(targets, i, const char *);
188 svn_pool_clear(subpool);
189 path = svn_path_uri_decode(path, pool);
190 APR_ARRAY_IDX(targets, i, const char *) = path;
191 SVN_ERR(svn_ra_check_path(ra_session, path, SVN_INVALID_REVNUM,
192 &kind, subpool));
193 if (kind == svn_node_none)
194 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
195 "URL '%s' does not exist",
196 svn_path_local_style(path, pool));
198 svn_pool_destroy(subpool);
200 /* Fetch RA commit editor */
201 SVN_ERR(svn_client__commit_get_baton(&commit_baton, commit_info_p, pool));
202 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
203 revprop_table,
204 svn_client__commit_callback,
205 commit_baton,
206 NULL, TRUE, /* No lock tokens */
207 pool));
209 /* Call the path-based editor driver. */
210 err = svn_delta_path_driver(editor, edit_baton, SVN_INVALID_REVNUM,
211 targets, path_driver_cb_func,
212 (void *)editor, pool);
213 if (err)
215 /* At least try to abort the edit (and fs txn) before throwing err. */
216 svn_error_clear(editor->abort_edit(edit_baton, pool));
217 return err;
220 /* Close the edit. */
221 SVN_ERR(editor->close_edit(edit_baton, pool));
223 return SVN_NO_ERROR;
226 svn_error_t *
227 svn_client__wc_delete(const char *path,
228 svn_wc_adm_access_t *adm_access,
229 svn_boolean_t force,
230 svn_boolean_t dry_run,
231 svn_boolean_t keep_local,
232 svn_wc_notify_func2_t notify_func,
233 void *notify_baton,
234 svn_client_ctx_t *ctx,
235 apr_pool_t *pool)
238 if (!force && !keep_local)
239 /* Verify that there are no "awkward" files */
240 SVN_ERR(svn_client__can_delete(path, ctx, pool));
242 if (!dry_run)
243 /* Mark the entry for commit deletion and perform wc deletion */
244 SVN_ERR(svn_wc_delete3(path, adm_access,
245 ctx->cancel_func, ctx->cancel_baton,
246 notify_func, notify_baton, keep_local, pool));
247 return SVN_NO_ERROR;
251 svn_error_t *
252 svn_client_delete3(svn_commit_info_t **commit_info_p,
253 const apr_array_header_t *paths,
254 svn_boolean_t force,
255 svn_boolean_t keep_local,
256 svn_client_ctx_t *ctx,
257 apr_pool_t *pool)
259 if (! paths->nelts)
260 return SVN_NO_ERROR;
262 if (svn_path_is_url(APR_ARRAY_IDX(paths, 0, const char *)))
264 SVN_ERR(delete_urls(commit_info_p, paths, ctx, pool));
266 else
268 apr_pool_t *subpool = svn_pool_create(pool);
269 int i;
271 for (i = 0; i < paths->nelts; i++)
273 svn_wc_adm_access_t *adm_access;
274 const char *path = APR_ARRAY_IDX(paths, i, const char *);
275 const char *parent_path;
277 svn_pool_clear(subpool);
278 parent_path = svn_path_dirname(path, subpool);
280 /* See if the user wants us to stop. */
281 if (ctx->cancel_func)
282 SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
284 /* Let the working copy library handle the PATH. */
285 SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, parent_path,
286 TRUE, 0, ctx->cancel_func,
287 ctx->cancel_baton, subpool));
288 SVN_ERR(svn_client__wc_delete(path, adm_access, force,
289 FALSE, keep_local,
290 ctx->notify_func2,
291 ctx->notify_baton2,
292 ctx, subpool));
293 SVN_ERR(svn_wc_adm_close(adm_access));
295 svn_pool_destroy(subpool);
298 return SVN_NO_ERROR;
301 svn_error_t *
302 svn_client_delete2(svn_commit_info_t **commit_info_p,
303 const apr_array_header_t *paths,
304 svn_boolean_t force,
305 svn_client_ctx_t *ctx,
306 apr_pool_t *pool)
308 return svn_client_delete3(commit_info_p, paths, force, FALSE, ctx, pool);
311 svn_error_t *
312 svn_client_delete(svn_client_commit_info_t **commit_info_p,
313 const apr_array_header_t *paths,
314 svn_boolean_t force,
315 svn_client_ctx_t *ctx,
316 apr_pool_t *pool)
318 svn_commit_info_t *commit_info = NULL;
319 svn_error_t *err = NULL;
321 err = svn_client_delete2(&commit_info, paths, force, ctx, pool);
322 /* These structs have the same layout for the common fields. */
323 *commit_info_p = (svn_client_commit_info_t *) commit_info;
324 return err;