1 /*
2 * crop.c: Cropping the WC
3 *
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
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
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
20 * under the License.
21 * ====================================================================
22 */
23
24 /* ==================================================================== */
25
26 #include "svn_wc.h"
27 #include "svn_pools.h"
28 #include "svn_error.h"
29 #include "svn_error_codes.h"
30 #include "svn_dirent_uri.h"
31 #include "svn_path.h"
32
33 #include "wc.h"
34 #include "workqueue.h"
35
36 #include "svn_private_config.h"
37
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.
41 *
42 * DIR_DEPTH is the current depth of LOCAL_ABSPATH as stored in DB.
43 *
44 * If NOTIFY_FUNC is not null, each file and ROOT of subtree will be reported
45 * upon remove.
46 */
47 static svn_error_t *
crop_children(svn_wc__db_t * db,const char * local_abspath,svn_depth_t dir_depth,svn_depth_t new_depth,svn_wc_notify_func2_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)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,
53 void *notify_baton,
54 svn_cancel_func_t cancel_func,
55 void *cancel_baton,
56 apr_pool_t *scratch_pool)
57 {
58 const apr_array_header_t *children;
59 apr_pool_t *iterpool;
60 int i;
61
62 SVN_ERR_ASSERT(new_depth >= svn_depth_empty
63 && new_depth <= svn_depth_infinity);
64
65 if (cancel_func)
66 SVN_ERR(cancel_func(cancel_baton));
67
68 iterpool = svn_pool_create(scratch_pool);
69
70 if (dir_depth == svn_depth_unknown)
71 dir_depth = svn_depth_infinity;
72
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,
76 iterpool));
77
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));
81
82 for (i = 0; i < children->nelts; i++)
83 {
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;
87 svn_node_kind_t kind;
88 svn_depth_t child_depth;
89 svn_boolean_t have_work;
90 svn_depth_t remove_below;
91
92 svn_pool_clear(iterpool);
93
94 /* Get the next node */
95 child_abspath = svn_dirent_join(local_abspath, child_name, iterpool);
96
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));
103
104 if (have_work)
105 {
106 svn_boolean_t modified, all_deletes;
107
108 if (child_status != svn_wc__db_status_deleted)
109 {
110 /* ### TODO: Check for issue #4636 constraints, but not only on
111 this node, but also at all its descendants: We don't want
112 to remove moved_from information here! */
113 continue; /* Leave local additions alone */
114 }
115
116 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes,
117 db, child_abspath, FALSE,
118 cancel_func, cancel_baton,
119 iterpool));
120
121 if (modified && !all_deletes)
122 continue; /* Something interesting is still there */
123 }
124
125 remove_below = (kind == svn_node_dir)
126 ? svn_depth_immediates
127 : svn_depth_files;
128
129 if ((child_status == svn_wc__db_status_server_excluded ||
130 child_status == svn_wc__db_status_excluded ||
131 child_status == svn_wc__db_status_not_present))
132 {
133 if (new_depth < remove_below)
134 SVN_ERR(svn_wc__db_base_remove(db, child_abspath,
135 FALSE /* keep_as_working */,
136 FALSE, FALSE,
137 SVN_INVALID_REVNUM,
138 NULL, NULL, iterpool));
139
140 continue; /* No recurse */
141 }
142
143 if (new_depth < remove_below)
144 {
145 svn_boolean_t modified, all_deletes;
146
147 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes,
148 db, child_abspath, FALSE,
149 cancel_func, cancel_baton,
150 iterpool));
151
152 if (!modified || all_deletes)
153 {
154 SVN_ERR(svn_wc__db_base_remove(db, child_abspath,
155 FALSE, FALSE, FALSE,
156 SVN_INVALID_REVNUM,
157 NULL, NULL, iterpool));
158 if (notify_func)
159 {
160 svn_wc_notify_t *notify;
161 notify = svn_wc_create_notify(child_abspath,
162 svn_wc_notify_delete,
163 iterpool);
164 (*notify_func)(notify_baton, notify, iterpool);
165 }
166
167 continue; /* No recurse */
168 }
169
170 /* Fall through: recurse:*/
171 }
172
173 if (kind == svn_node_dir)
174 {
175 SVN_ERR(crop_children(db, child_abspath,
176 child_depth, svn_depth_empty,
177 notify_func, notify_baton,
178 cancel_func, cancel_baton,
179 iterpool));
180 }
181 }
182
183 svn_pool_destroy(iterpool);
184
185 return SVN_NO_ERROR;
186 }
187
188 svn_error_t *
svn_wc_exclude(svn_wc_context_t * wc_ctx,const char * local_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)189 svn_wc_exclude(svn_wc_context_t *wc_ctx,
190 const char *local_abspath,
191 svn_cancel_func_t cancel_func,
192 void *cancel_baton,
193 svn_wc_notify_func2_t notify_func,
194 void *notify_baton,
195 apr_pool_t *scratch_pool)
196 {
197 svn_boolean_t is_root, is_switched;
198 svn_wc__db_status_t status;
199 svn_node_kind_t kind;
200 svn_revnum_t revision;
201 svn_depth_t depth;
202 svn_boolean_t modified, all_deletes;
203 const char *repos_relpath, *repos_root, *repos_uuid;
204
205 SVN_ERR(svn_wc__db_is_switched(&is_root, &is_switched, NULL,
206 wc_ctx->db, local_abspath, scratch_pool));
207
208 if (is_root)
209 {
210 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
211 _("Cannot exclude '%s': "
212 "it is a working copy root"),
213 svn_dirent_local_style(local_abspath,
214 scratch_pool));
215 }
216 if (is_switched)
217 {
218 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
219 _("Cannot exclude '%s': "
220 "it is a switched path"),
221 svn_dirent_local_style(local_abspath,
222 scratch_pool));
223 }
224
225 SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, &repos_relpath,
226 &repos_root, &repos_uuid, NULL, NULL, NULL,
227 &depth, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
228 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
229 NULL, NULL, NULL,
230 wc_ctx->db, local_abspath,
231 scratch_pool, scratch_pool));
232
233 switch (status)
234 {
235 case svn_wc__db_status_server_excluded:
236 case svn_wc__db_status_excluded:
237 case svn_wc__db_status_not_present:
238 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
239 _("The node '%s' was not found."),
240 svn_dirent_local_style(local_abspath,
241 scratch_pool));
242
243 case svn_wc__db_status_added:
244 /* Would have to check parents if we want to allow this */
245 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
246 _("Cannot exclude '%s': it is to be added "
247 "to the repository. Try commit instead"),
248 svn_dirent_local_style(local_abspath,
249 scratch_pool));
250 case svn_wc__db_status_deleted:
251 /* Would have to check parents if we want to allow this */
252 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
253 _("Cannot exclude '%s': it is to be deleted "
254 "from the repository. Try commit instead"),
255 svn_dirent_local_style(local_abspath,
256 scratch_pool));
257
258 case svn_wc__db_status_normal:
259 case svn_wc__db_status_incomplete:
260 default:
261 break; /* Ok to exclude */
262 }
263
264 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes,
265 wc_ctx->db, local_abspath, FALSE,
266 cancel_func, cancel_baton,
267 scratch_pool));
268
269 if (!modified || all_deletes)
270 {
271 /* Remove all working copy data below local_abspath */
272 SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath,
273 FALSE /* keep_working */,
274 FALSE, TRUE,
275 revision,
276 NULL, NULL,
277 scratch_pool));
278
279 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath,
280 cancel_func, cancel_baton,
281 scratch_pool));
282
283 if (notify_func)
284 {
285 svn_wc_notify_t *notify;
286 notify = svn_wc_create_notify(local_abspath,
287 svn_wc_notify_exclude,
288 scratch_pool);
289 notify_func(notify_baton, notify, scratch_pool);
290 }
291 }
292 else
293 {
294 /* Do the next best thing: retry below this path */
295 SVN_ERR(crop_children(wc_ctx->db, local_abspath, depth, svn_depth_empty,
296 notify_func, notify_baton,
297 cancel_func, cancel_baton,
298 scratch_pool));
299 }
300
301 return SVN_NO_ERROR;
302 }
303
304 svn_error_t *
svn_wc_crop_tree2(svn_wc_context_t * wc_ctx,const char * local_abspath,svn_depth_t depth,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)305 svn_wc_crop_tree2(svn_wc_context_t *wc_ctx,
306 const char *local_abspath,
307 svn_depth_t depth,
308 svn_cancel_func_t cancel_func,
309 void *cancel_baton,
310 svn_wc_notify_func2_t notify_func,
311 void *notify_baton,
312 apr_pool_t *scratch_pool)
313 {
314 svn_wc__db_t *db = wc_ctx->db;
315 svn_wc__db_status_t status;
316 svn_node_kind_t kind;
317 svn_depth_t dir_depth;
318
319 /* Only makes sense when the depth is restrictive. */
320 if (depth == svn_depth_infinity)
321 return SVN_NO_ERROR; /* Nothing to crop */
322 if (!(depth >= svn_depth_empty && depth < svn_depth_infinity))
323 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
324 _("Can only crop a working copy with a restrictive depth"));
325
326 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
327 NULL, NULL, &dir_depth, NULL, NULL, NULL, NULL,
328 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
329 NULL, NULL, NULL, NULL, NULL, NULL,
330 db, local_abspath,
331 scratch_pool, scratch_pool));
332
333 if (kind != svn_node_dir)
334 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
335 _("Can only crop directories"));
336
337 switch (status)
338 {
339 case svn_wc__db_status_not_present:
340 case svn_wc__db_status_server_excluded:
341 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
342 _("The node '%s' was not found."),
343 svn_dirent_local_style(local_abspath,
344 scratch_pool));
345
346 case svn_wc__db_status_deleted:
347 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
348 _("Cannot crop '%s': it is going to be removed "
349 "from repository. Try commit instead"),
350 svn_dirent_local_style(local_abspath,
351 scratch_pool));
352
353 case svn_wc__db_status_added:
354 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
355 _("Cannot crop '%s': it is to be added "
356 "to the repository. Try commit instead"),
357 svn_dirent_local_style(local_abspath,
358 scratch_pool));
359 case svn_wc__db_status_excluded:
360 return SVN_NO_ERROR; /* Nothing to do */
361
362 case svn_wc__db_status_normal:
363 case svn_wc__db_status_incomplete:
364 break;
365
366 default:
367 SVN_ERR_MALFUNCTION();
368 }
369
370 SVN_ERR(crop_children(db, local_abspath, dir_depth, depth,
371 notify_func, notify_baton,
372 cancel_func, cancel_baton, scratch_pool));
373
374 return svn_error_trace(svn_wc__wq_run(db, local_abspath,
375 cancel_func, cancel_baton,
376 scratch_pool));
377 }
378