2 * crop.c: Cropping the WC
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 /* ==================================================================== */
27 #include "svn_pools.h"
28 #include "svn_error.h"
29 #include "svn_error_codes.h"
30 #include "svn_dirent_uri.h"
34 #include "workqueue.h"
36 #include "svn_private_config.h"
38 /* Helper function that crops the children of the LOCAL_ABSPATH, under the
39 * constraint of NEW_DEPTH. The DIR_PATH itself will never be cropped. The
40 * whole subtree should have been locked.
42 * DIR_DEPTH is the current depth of LOCAL_ABSPATH as stored in DB.
44 * If NOTIFY_FUNC is not null, each file and ROOT of subtree will be reported
48 crop_children(svn_wc__db_t *db,
49 const char *local_abspath,
50 svn_depth_t dir_depth,
51 svn_depth_t new_depth,
52 svn_wc_notify_func2_t notify_func,
54 svn_cancel_func_t cancel_func,
56 apr_pool_t *scratch_pool)
58 const apr_array_header_t *children;
62 SVN_ERR_ASSERT(new_depth >= svn_depth_empty
63 && new_depth <= svn_depth_infinity);
66 SVN_ERR(cancel_func(cancel_baton));
68 iterpool = svn_pool_create(scratch_pool);
70 if (dir_depth == svn_depth_unknown)
71 dir_depth = svn_depth_infinity;
73 /* Update the depth of target first, if needed. */
74 if (dir_depth > new_depth)
75 SVN_ERR(svn_wc__db_op_set_base_depth(db, local_abspath, new_depth,
78 /* Looping over current directory's SVN entries: */
79 SVN_ERR(svn_wc__db_base_get_children(&children, db, local_abspath,
80 scratch_pool, iterpool));
82 for (i = 0; i < children->nelts; i++)
84 const char *child_name = APR_ARRAY_IDX(children, i, const char *);
85 const char *child_abspath;
86 svn_wc__db_status_t child_status;
88 svn_depth_t child_depth;
89 svn_boolean_t have_work;
90 svn_depth_t remove_below;
92 svn_pool_clear(iterpool);
94 /* Get the next node */
95 child_abspath = svn_dirent_join(local_abspath, child_name, iterpool);
97 SVN_ERR(svn_wc__db_read_info(&child_status, &kind, NULL, NULL, NULL,
98 NULL,NULL, NULL, NULL, &child_depth,
99 NULL, NULL, NULL, NULL, NULL, NULL,
100 NULL, NULL, NULL, NULL, NULL, NULL,
101 NULL, NULL, NULL, NULL, &have_work,
102 db, child_abspath, iterpool, iterpool));
106 svn_boolean_t modified, all_deletes;
108 if (child_status != svn_wc__db_status_deleted)
109 continue; /* Leave local additions alone */
111 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes,
112 db, child_abspath, FALSE,
113 cancel_func, cancel_baton,
116 if (modified && !all_deletes)
117 continue; /* Something interesting is still there */
120 remove_below = (kind == svn_node_dir)
121 ? svn_depth_immediates
124 if ((child_status == svn_wc__db_status_server_excluded ||
125 child_status == svn_wc__db_status_excluded ||
126 child_status == svn_wc__db_status_not_present))
128 if (new_depth < remove_below)
129 SVN_ERR(svn_wc__db_base_remove(db, child_abspath,
130 FALSE /* keep_as_working */,
133 NULL, NULL, iterpool));
135 continue; /* No recurse */
138 if (new_depth < remove_below)
140 svn_boolean_t modified, all_deletes;
142 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes,
143 db, child_abspath, FALSE,
144 cancel_func, cancel_baton,
147 if (!modified || all_deletes)
149 SVN_ERR(svn_wc__db_base_remove(db, child_abspath,
152 NULL, NULL, iterpool));
155 svn_wc_notify_t *notify;
156 notify = svn_wc_create_notify(child_abspath,
157 svn_wc_notify_delete,
159 (*notify_func)(notify_baton, notify, iterpool);
162 continue; /* No recurse */
165 /* Fall through: recurse:*/
168 if (kind == svn_node_dir)
170 SVN_ERR(crop_children(db, child_abspath,
171 child_depth, svn_depth_empty,
172 notify_func, notify_baton,
173 cancel_func, cancel_baton,
178 svn_pool_destroy(iterpool);
184 svn_wc_exclude(svn_wc_context_t *wc_ctx,
185 const char *local_abspath,
186 svn_cancel_func_t cancel_func,
188 svn_wc_notify_func2_t notify_func,
190 apr_pool_t *scratch_pool)
192 svn_boolean_t is_root, is_switched;
193 svn_wc__db_status_t status;
194 svn_node_kind_t kind;
195 svn_revnum_t revision;
197 svn_boolean_t modified, all_deletes;
198 const char *repos_relpath, *repos_root, *repos_uuid;
200 SVN_ERR(svn_wc__db_is_switched(&is_root, &is_switched, NULL,
201 wc_ctx->db, local_abspath, scratch_pool));
205 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
206 _("Cannot exclude '%s': "
207 "it is a working copy root"),
208 svn_dirent_local_style(local_abspath,
213 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
214 _("Cannot exclude '%s': "
215 "it is a switched path"),
216 svn_dirent_local_style(local_abspath,
220 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, &repos_relpath,
221 &repos_root, &repos_uuid, NULL, NULL, NULL,
222 &depth, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
223 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
225 wc_ctx->db, local_abspath,
226 scratch_pool, scratch_pool));
230 case svn_wc__db_status_server_excluded:
231 case svn_wc__db_status_excluded:
232 case svn_wc__db_status_not_present:
233 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
234 _("The node '%s' was not found."),
235 svn_dirent_local_style(local_abspath,
238 case svn_wc__db_status_added:
239 /* Would have to check parents if we want to allow this */
240 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
241 _("Cannot exclude '%s': it is to be added "
242 "to the repository. Try commit instead"),
243 svn_dirent_local_style(local_abspath,
245 case svn_wc__db_status_deleted:
246 /* Would have to check parents if we want to allow this */
247 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
248 _("Cannot exclude '%s': it is to be deleted "
249 "from the repository. Try commit instead"),
250 svn_dirent_local_style(local_abspath,
253 case svn_wc__db_status_normal:
254 case svn_wc__db_status_incomplete:
256 break; /* Ok to exclude */
259 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes,
260 wc_ctx->db, local_abspath, FALSE,
261 cancel_func, cancel_baton,
264 if (!modified || all_deletes)
266 /* Remove all working copy data below local_abspath */
267 SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath,
268 FALSE /* keep_working */,
274 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath,
275 cancel_func, cancel_baton,
280 svn_wc_notify_t *notify;
281 notify = svn_wc_create_notify(local_abspath,
282 svn_wc_notify_exclude,
284 notify_func(notify_baton, notify, scratch_pool);
289 /* Do the next best thing: retry below this path */
290 SVN_ERR(crop_children(wc_ctx->db, local_abspath, depth, svn_depth_empty,
291 notify_func, notify_baton,
292 cancel_func, cancel_baton,
300 svn_wc_crop_tree2(svn_wc_context_t *wc_ctx,
301 const char *local_abspath,
303 svn_cancel_func_t cancel_func,
305 svn_wc_notify_func2_t notify_func,
307 apr_pool_t *scratch_pool)
309 svn_wc__db_t *db = wc_ctx->db;
310 svn_wc__db_status_t status;
311 svn_node_kind_t kind;
312 svn_depth_t dir_depth;
314 /* Only makes sense when the depth is restrictive. */
315 if (depth == svn_depth_infinity)
316 return SVN_NO_ERROR; /* Nothing to crop */
317 if (!(depth >= svn_depth_empty && depth < svn_depth_infinity))
318 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
319 _("Can only crop a working copy with a restrictive depth"));
321 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
322 NULL, NULL, &dir_depth, NULL, NULL, NULL, NULL,
323 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
324 NULL, NULL, NULL, NULL, NULL, NULL,
326 scratch_pool, scratch_pool));
328 if (kind != svn_node_dir)
329 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
330 _("Can only crop directories"));
334 case svn_wc__db_status_not_present:
335 case svn_wc__db_status_server_excluded:
336 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
337 _("The node '%s' was not found."),
338 svn_dirent_local_style(local_abspath,
341 case svn_wc__db_status_deleted:
342 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
343 _("Cannot crop '%s': it is going to be removed "
344 "from repository. Try commit instead"),
345 svn_dirent_local_style(local_abspath,
348 case svn_wc__db_status_added:
349 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
350 _("Cannot crop '%s': it is to be added "
351 "to the repository. Try commit instead"),
352 svn_dirent_local_style(local_abspath,
354 case svn_wc__db_status_excluded:
355 return SVN_NO_ERROR; /* Nothing to do */
357 case svn_wc__db_status_normal:
358 case svn_wc__db_status_incomplete:
362 SVN_ERR_MALFUNCTION();
365 SVN_ERR(crop_children(db, local_abspath, dir_depth, depth,
366 notify_func, notify_baton,
367 cancel_func, cancel_baton, scratch_pool));
369 return svn_error_trace(svn_wc__wq_run(db, local_abspath,
370 cancel_func, cancel_baton,