| /* |
| * crop.c: Cropping the WC |
| * |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| */ |
| |
| /* ==================================================================== */ |
| |
| #include "svn_wc.h" |
| #include "svn_pools.h" |
| #include "svn_error.h" |
| #include "svn_error_codes.h" |
| #include "svn_dirent_uri.h" |
| #include "svn_path.h" |
| |
| #include "wc.h" |
| #include "workqueue.h" |
| |
| #include "svn_private_config.h" |
| |
| /* Helper function that crops the children of the LOCAL_ABSPATH, under the |
| * constraint of NEW_DEPTH. The DIR_PATH itself will never be cropped. The |
| * whole subtree should have been locked. |
| * |
| * DIR_DEPTH is the current depth of LOCAL_ABSPATH as stored in DB. |
| * |
| * If NOTIFY_FUNC is not null, each file and ROOT of subtree will be reported |
| * upon remove. |
| */ |
| 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) |
| { |
| const apr_array_header_t *children; |
| apr_pool_t *iterpool; |
| int i; |
| |
| SVN_ERR_ASSERT(new_depth >= svn_depth_empty |
| && new_depth <= svn_depth_infinity); |
| |
| if (cancel_func) |
| SVN_ERR(cancel_func(cancel_baton)); |
| |
| iterpool = svn_pool_create(scratch_pool); |
| |
| if (dir_depth == svn_depth_unknown) |
| dir_depth = svn_depth_infinity; |
| |
| /* Update the depth of target first, if needed. */ |
| if (dir_depth > new_depth) |
| SVN_ERR(svn_wc__db_op_set_base_depth(db, local_abspath, new_depth, |
| iterpool)); |
| |
| /* Looping over current directory's SVN entries: */ |
| SVN_ERR(svn_wc__db_base_get_children(&children, db, local_abspath, |
| scratch_pool, iterpool)); |
| |
| for (i = 0; i < children->nelts; i++) |
| { |
| const char *child_name = APR_ARRAY_IDX(children, i, const char *); |
| const char *child_abspath; |
| svn_wc__db_status_t child_status; |
| svn_node_kind_t kind; |
| svn_depth_t child_depth; |
| svn_boolean_t have_work; |
| svn_depth_t remove_below; |
| |
| svn_pool_clear(iterpool); |
| |
| /* Get the next node */ |
| child_abspath = svn_dirent_join(local_abspath, child_name, iterpool); |
| |
| SVN_ERR(svn_wc__db_read_info(&child_status, &kind, NULL, NULL, NULL, |
| NULL,NULL, NULL, NULL, &child_depth, |
| NULL, NULL, NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL, NULL, &have_work, |
| db, child_abspath, iterpool, iterpool)); |
| |
| if (have_work) |
| { |
| svn_boolean_t modified, all_deletes; |
| |
| if (child_status != svn_wc__db_status_deleted) |
| { |
| /* ### TODO: Check for issue #4636 constraints, but not only on |
| this node, but also at all its descendants: We don't want |
| to remove moved_from information here! */ |
| continue; /* Leave local additions alone */ |
| } |
| |
| SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, |
| db, child_abspath, FALSE, |
| cancel_func, cancel_baton, |
| iterpool)); |
| |
| if (modified && !all_deletes) |
| continue; /* Something interesting is still there */ |
| } |
| |
| remove_below = (kind == svn_node_dir) |
| ? svn_depth_immediates |
| : svn_depth_files; |
| |
| if ((child_status == svn_wc__db_status_server_excluded || |
| child_status == svn_wc__db_status_excluded || |
| child_status == svn_wc__db_status_not_present)) |
| { |
| if (new_depth < remove_below) |
| SVN_ERR(svn_wc__db_base_remove(db, child_abspath, |
| FALSE /* keep_as_working */, |
| FALSE, FALSE, |
| SVN_INVALID_REVNUM, |
| NULL, NULL, iterpool)); |
| |
| continue; /* No recurse */ |
| } |
| |
| if (new_depth < remove_below) |
| { |
| svn_boolean_t modified, all_deletes; |
| |
| SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, |
| db, child_abspath, FALSE, |
| cancel_func, cancel_baton, |
| iterpool)); |
| |
| if (!modified || all_deletes) |
| { |
| SVN_ERR(svn_wc__db_base_remove(db, child_abspath, |
| FALSE, FALSE, FALSE, |
| SVN_INVALID_REVNUM, |
| NULL, NULL, iterpool)); |
| if (notify_func) |
| { |
| svn_wc_notify_t *notify; |
| notify = svn_wc_create_notify(child_abspath, |
| svn_wc_notify_delete, |
| iterpool); |
| (*notify_func)(notify_baton, notify, iterpool); |
| } |
| |
| continue; /* No recurse */ |
| } |
| |
| /* Fall through: recurse:*/ |
| } |
| |
| if (kind == svn_node_dir) |
| { |
| SVN_ERR(crop_children(db, child_abspath, |
| child_depth, svn_depth_empty, |
| notify_func, notify_baton, |
| cancel_func, cancel_baton, |
| iterpool)); |
| } |
| } |
| |
| svn_pool_destroy(iterpool); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| 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) |
| { |
| svn_boolean_t is_root, is_switched; |
| svn_wc__db_status_t status; |
| svn_node_kind_t kind; |
| svn_revnum_t revision; |
| svn_depth_t depth; |
| svn_boolean_t modified, all_deletes; |
| const char *repos_relpath, *repos_root, *repos_uuid; |
| |
| SVN_ERR(svn_wc__db_is_switched(&is_root, &is_switched, NULL, |
| wc_ctx->db, local_abspath, scratch_pool)); |
| |
| if (is_root) |
| { |
| return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, |
| _("Cannot exclude '%s': " |
| "it is a working copy root"), |
| svn_dirent_local_style(local_abspath, |
| scratch_pool)); |
| } |
| if (is_switched) |
| { |
| return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, |
| _("Cannot exclude '%s': " |
| "it is a switched path"), |
| svn_dirent_local_style(local_abspath, |
| scratch_pool)); |
| } |
| |
| SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, &repos_relpath, |
| &repos_root, &repos_uuid, NULL, NULL, NULL, |
| &depth, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL, |
| wc_ctx->db, local_abspath, |
| scratch_pool, scratch_pool)); |
| |
| switch (status) |
| { |
| case svn_wc__db_status_server_excluded: |
| case svn_wc__db_status_excluded: |
| case svn_wc__db_status_not_present: |
| return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, |
| _("The node '%s' was not found."), |
| svn_dirent_local_style(local_abspath, |
| scratch_pool)); |
| |
| case svn_wc__db_status_added: |
| /* Would have to check parents if we want to allow this */ |
| return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, |
| _("Cannot exclude '%s': it is to be added " |
| "to the repository. Try commit instead"), |
| svn_dirent_local_style(local_abspath, |
| scratch_pool)); |
| case svn_wc__db_status_deleted: |
| /* Would have to check parents if we want to allow this */ |
| return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, |
| _("Cannot exclude '%s': it is to be deleted " |
| "from the repository. Try commit instead"), |
| svn_dirent_local_style(local_abspath, |
| scratch_pool)); |
| |
| case svn_wc__db_status_normal: |
| case svn_wc__db_status_incomplete: |
| default: |
| break; /* Ok to exclude */ |
| } |
| |
| SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, |
| wc_ctx->db, local_abspath, FALSE, |
| cancel_func, cancel_baton, |
| scratch_pool)); |
| |
| if (!modified || all_deletes) |
| { |
| /* Remove all working copy data below local_abspath */ |
| SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath, |
| FALSE /* keep_working */, |
| FALSE, TRUE, |
| revision, |
| NULL, NULL, |
| scratch_pool)); |
| |
| SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, |
| cancel_func, cancel_baton, |
| scratch_pool)); |
| |
| if (notify_func) |
| { |
| svn_wc_notify_t *notify; |
| notify = svn_wc_create_notify(local_abspath, |
| svn_wc_notify_exclude, |
| scratch_pool); |
| notify_func(notify_baton, notify, scratch_pool); |
| } |
| } |
| else |
| { |
| /* Do the next best thing: retry below this path */ |
| SVN_ERR(crop_children(wc_ctx->db, local_abspath, depth, svn_depth_empty, |
| notify_func, notify_baton, |
| cancel_func, cancel_baton, |
| scratch_pool)); |
| } |
| |
| return SVN_NO_ERROR; |
| } |
| |
| 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) |
| { |
| svn_wc__db_t *db = wc_ctx->db; |
| svn_wc__db_status_t status; |
| svn_node_kind_t kind; |
| svn_depth_t dir_depth; |
| |
| /* Only makes sense when the depth is restrictive. */ |
| if (depth == svn_depth_infinity) |
| return SVN_NO_ERROR; /* Nothing to crop */ |
| if (!(depth >= svn_depth_empty && depth < svn_depth_infinity)) |
| return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, |
| _("Can only crop a working copy with a restrictive depth")); |
| |
| SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, |
| NULL, NULL, &dir_depth, NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL, NULL, NULL, NULL, |
| db, local_abspath, |
| scratch_pool, scratch_pool)); |
| |
| if (kind != svn_node_dir) |
| return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, |
| _("Can only crop directories")); |
| |
| switch (status) |
| { |
| case svn_wc__db_status_not_present: |
| case svn_wc__db_status_server_excluded: |
| return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, |
| _("The node '%s' was not found."), |
| svn_dirent_local_style(local_abspath, |
| scratch_pool)); |
| |
| case svn_wc__db_status_deleted: |
| return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, |
| _("Cannot crop '%s': it is going to be removed " |
| "from repository. Try commit instead"), |
| svn_dirent_local_style(local_abspath, |
| scratch_pool)); |
| |
| case svn_wc__db_status_added: |
| return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, |
| _("Cannot crop '%s': it is to be added " |
| "to the repository. Try commit instead"), |
| svn_dirent_local_style(local_abspath, |
| scratch_pool)); |
| case svn_wc__db_status_excluded: |
| return SVN_NO_ERROR; /* Nothing to do */ |
| |
| case svn_wc__db_status_normal: |
| case svn_wc__db_status_incomplete: |
| break; |
| |
| default: |
| SVN_ERR_MALFUNCTION(); |
| } |
| |
| SVN_ERR(crop_children(db, local_abspath, dir_depth, depth, |
| notify_func, notify_baton, |
| cancel_func, cancel_baton, scratch_pool)); |
| |
| return svn_error_trace(svn_wc__wq_run(db, local_abspath, |
| cancel_func, cancel_baton, |
| scratch_pool)); |
| } |