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 continue; /* Leave local additions alone */ 110 111 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, 112 db, child_abspath, FALSE, 113 cancel_func, cancel_baton, 114 iterpool)); 115 116 if (modified && !all_deletes) 117 continue; /* Something interesting is still there */ 118 } 119 120 remove_below = (kind == svn_node_dir) 121 ? svn_depth_immediates 122 : svn_depth_files; 123 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)) 127 { 128 if (new_depth < remove_below) 129 SVN_ERR(svn_wc__db_base_remove(db, child_abspath, 130 FALSE /* keep_as_working */, 131 FALSE, FALSE, 132 SVN_INVALID_REVNUM, 133 NULL, NULL, iterpool)); 134 135 continue; /* No recurse */ 136 } 137 138 if (new_depth < remove_below) 139 { 140 svn_boolean_t modified, all_deletes; 141 142 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, 143 db, child_abspath, FALSE, 144 cancel_func, cancel_baton, 145 iterpool)); 146 147 if (!modified || all_deletes) 148 { 149 SVN_ERR(svn_wc__db_base_remove(db, child_abspath, 150 FALSE, FALSE, FALSE, 151 SVN_INVALID_REVNUM, 152 NULL, NULL, iterpool)); 153 if (notify_func) 154 { 155 svn_wc_notify_t *notify; 156 notify = svn_wc_create_notify(child_abspath, 157 svn_wc_notify_delete, 158 iterpool); 159 (*notify_func)(notify_baton, notify, iterpool); 160 } 161 162 continue; /* No recurse */ 163 } 164 165 /* Fall through: recurse:*/ 166 } 167 168 if (kind == svn_node_dir) 169 { 170 SVN_ERR(crop_children(db, child_abspath, 171 child_depth, svn_depth_empty, 172 notify_func, notify_baton, 173 cancel_func, cancel_baton, 174 iterpool)); 175 } 176 } 177 178 svn_pool_destroy(iterpool); 179 180 return SVN_NO_ERROR; 181} 182 183svn_error_t * 184svn_wc_exclude(svn_wc_context_t *wc_ctx, 185 const char *local_abspath, 186 svn_cancel_func_t cancel_func, 187 void *cancel_baton, 188 svn_wc_notify_func2_t notify_func, 189 void *notify_baton, 190 apr_pool_t *scratch_pool) 191{ 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; 196 svn_depth_t depth; 197 svn_boolean_t modified, all_deletes; 198 const char *repos_relpath, *repos_root, *repos_uuid; 199 200 SVN_ERR(svn_wc__db_is_switched(&is_root, &is_switched, NULL, 201 wc_ctx->db, local_abspath, scratch_pool)); 202 203 if (is_root) 204 { 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, 209 scratch_pool)); 210 } 211 if (is_switched) 212 { 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, 217 scratch_pool)); 218 } 219 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, 224 NULL, NULL, NULL, 225 wc_ctx->db, local_abspath, 226 scratch_pool, scratch_pool)); 227 228 switch (status) 229 { 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, 236 scratch_pool)); 237 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, 244 scratch_pool)); 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, 251 scratch_pool)); 252 253 case svn_wc__db_status_normal: 254 case svn_wc__db_status_incomplete: 255 default: 256 break; /* Ok to exclude */ 257 } 258 259 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, 260 wc_ctx->db, local_abspath, FALSE, 261 cancel_func, cancel_baton, 262 scratch_pool)); 263 264 if (!modified || all_deletes) 265 { 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 */, 269 FALSE, TRUE, 270 revision, 271 NULL, NULL, 272 scratch_pool)); 273 274 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, 275 cancel_func, cancel_baton, 276 scratch_pool)); 277 278 if (notify_func) 279 { 280 svn_wc_notify_t *notify; 281 notify = svn_wc_create_notify(local_abspath, 282 svn_wc_notify_exclude, 283 scratch_pool); 284 notify_func(notify_baton, notify, scratch_pool); 285 } 286 } 287 else 288 { 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, 293 scratch_pool)); 294 } 295 296 return SVN_NO_ERROR; 297} 298 299svn_error_t * 300svn_wc_crop_tree2(svn_wc_context_t *wc_ctx, 301 const char *local_abspath, 302 svn_depth_t depth, 303 svn_cancel_func_t cancel_func, 304 void *cancel_baton, 305 svn_wc_notify_func2_t notify_func, 306 void *notify_baton, 307 apr_pool_t *scratch_pool) 308{ 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; 313 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")); 320 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, 325 db, local_abspath, 326 scratch_pool, scratch_pool)); 327 328 if (kind != svn_node_dir) 329 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 330 _("Can only crop directories")); 331 332 switch (status) 333 { 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, 339 scratch_pool)); 340 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, 346 scratch_pool)); 347 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, 353 scratch_pool)); 354 case svn_wc__db_status_excluded: 355 return SVN_NO_ERROR; /* Nothing to do */ 356 357 case svn_wc__db_status_normal: 358 case svn_wc__db_status_incomplete: 359 break; 360 361 default: 362 SVN_ERR_MALFUNCTION(); 363 } 364 365 SVN_ERR(crop_children(db, local_abspath, dir_depth, depth, 366 notify_func, notify_baton, 367 cancel_func, cancel_baton, scratch_pool)); 368 369 return svn_error_trace(svn_wc__wq_run(db, local_abspath, 370 cancel_func, cancel_baton, 371 scratch_pool)); 372} 373