crop.c revision 362181
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 */ 47static svn_error_t * 48crop_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 188svn_error_t * 189svn_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 304svn_error_t * 305svn_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