112657Skvn/* 212657Skvn * crop.c: Cropping the WC 312657Skvn * 412657Skvn * ==================================================================== 512657Skvn * Licensed to the Apache Software Foundation (ASF) under one 612657Skvn * or more contributor license agreements. See the NOTICE file 712657Skvn * distributed with this work for additional information 812657Skvn * regarding copyright ownership. The ASF licenses this file 912657Skvn * to you under the Apache License, Version 2.0 (the 1012657Skvn * "License"); you may not use this file except in compliance 1112657Skvn * with the License. You may obtain a copy of the License at 1212657Skvn * 1312657Skvn * http://www.apache.org/licenses/LICENSE-2.0 1412657Skvn * 1512657Skvn * Unless required by applicable law or agreed to in writing, 1612657Skvn * software distributed under the License is distributed on an 1712657Skvn * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1812657Skvn * KIND, either express or implied. See the License for the 1912657Skvn * specific language governing permissions and limitations 2012657Skvn * under the License. 2112657Skvn * ==================================================================== 2212657Skvn */ 2312657Skvn 2412657Skvn/* ==================================================================== */ 2512657Skvn 2612657Skvn#include "svn_wc.h" 2712657Skvn#include "svn_pools.h" 2812657Skvn#include "svn_error.h" 2912657Skvn#include "svn_error_codes.h" 3012657Skvn#include "svn_dirent_uri.h" 3112657Skvn#include "svn_path.h" 3212657Skvn 3312657Skvn#include "wc.h" 3412657Skvn#include "workqueue.h" 3512657Skvn 3612657Skvn#include "svn_private_config.h" 3712657Skvn 3812657Skvn/* Helper function that crops the children of the LOCAL_ABSPATH, under the 3912657Skvn * constraint of NEW_DEPTH. The DIR_PATH itself will never be cropped. The 4012657Skvn * whole subtree should have been locked. 4112657Skvn * 4212657Skvn * DIR_DEPTH is the current depth of LOCAL_ABSPATH as stored in DB. 4312657Skvn * 4412657Skvn * If NOTIFY_FUNC is not null, each file and ROOT of subtree will be reported 4512657Skvn * upon remove. 4612657Skvn */ 4712657Skvnstatic svn_error_t * 4812657Skvncrop_children(svn_wc__db_t *db, 4912657Skvn const char *local_abspath, 5012657Skvn svn_depth_t dir_depth, 5112657Skvn svn_depth_t new_depth, 5212657Skvn svn_wc_notify_func2_t notify_func, 5312657Skvn void *notify_baton, 5412657Skvn svn_cancel_func_t cancel_func, 5512657Skvn void *cancel_baton, 5612657Skvn apr_pool_t *scratch_pool) 5712657Skvn{ 5812657Skvn const apr_array_header_t *children; 5912657Skvn apr_pool_t *iterpool; 6012657Skvn int i; 6112657Skvn 6212657Skvn SVN_ERR_ASSERT(new_depth >= svn_depth_empty 6312657Skvn && new_depth <= svn_depth_infinity); 6412657Skvn 6512657Skvn if (cancel_func) 6612657Skvn SVN_ERR(cancel_func(cancel_baton)); 6712657Skvn 6812657Skvn iterpool = svn_pool_create(scratch_pool); 6912657Skvn 7012657Skvn if (dir_depth == svn_depth_unknown) 7112657Skvn dir_depth = svn_depth_infinity; 7212657Skvn 7312657Skvn /* Update the depth of target first, if needed. */ 7412657Skvn if (dir_depth > new_depth) 7512657Skvn SVN_ERR(svn_wc__db_op_set_base_depth(db, local_abspath, new_depth, 7612657Skvn iterpool)); 7712657Skvn 7812657Skvn /* Looping over current directory's SVN entries: */ 7912657Skvn SVN_ERR(svn_wc__db_base_get_children(&children, db, local_abspath, 8012657Skvn scratch_pool, iterpool)); 8112657Skvn 8212657Skvn for (i = 0; i < children->nelts; i++) 8312657Skvn { 8412657Skvn const char *child_name = APR_ARRAY_IDX(children, i, const char *); 8512657Skvn const char *child_abspath; 8612657Skvn svn_wc__db_status_t child_status; 8712657Skvn svn_node_kind_t kind; 8812657Skvn svn_depth_t child_depth; 8912657Skvn svn_boolean_t have_work; 9012657Skvn svn_depth_t remove_below; 9112657Skvn 9212657Skvn svn_pool_clear(iterpool); 9312657Skvn 9412657Skvn /* Get the next node */ 9512657Skvn child_abspath = svn_dirent_join(local_abspath, child_name, iterpool); 9612657Skvn 9712657Skvn SVN_ERR(svn_wc__db_read_info(&child_status, &kind, NULL, NULL, NULL, 9812657Skvn NULL,NULL, NULL, NULL, &child_depth, 9912657Skvn NULL, NULL, NULL, NULL, NULL, NULL, 10012657Skvn NULL, NULL, NULL, NULL, NULL, NULL, 10112657Skvn NULL, NULL, NULL, NULL, &have_work, 10212657Skvn db, child_abspath, iterpool, iterpool)); 10312657Skvn 10412657Skvn if (have_work) 10512657Skvn { 10612657Skvn svn_boolean_t modified, all_deletes; 10712657Skvn 10812657Skvn if (child_status != svn_wc__db_status_deleted) 10912657Skvn { 11012657Skvn /* ### TODO: Check for issue #4636 constraints, but not only on 11112657Skvn this node, but also at all its descendants: We don't want 11212657Skvn to remove moved_from information here! */ 11312657Skvn continue; /* Leave local additions alone */ 11412657Skvn } 11512657Skvn 11612657Skvn SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, 11712657Skvn db, child_abspath, FALSE, 11812657Skvn cancel_func, cancel_baton, 11912657Skvn iterpool)); 12012657Skvn 12112657Skvn if (modified && !all_deletes) 12212657Skvn continue; /* Something interesting is still there */ 12312657Skvn } 12412657Skvn 12512657Skvn remove_below = (kind == svn_node_dir) 12612657Skvn ? svn_depth_immediates 12712657Skvn : svn_depth_files; 12812657Skvn 12912657Skvn if ((child_status == svn_wc__db_status_server_excluded || 13012657Skvn child_status == svn_wc__db_status_excluded || 13112657Skvn child_status == svn_wc__db_status_not_present)) 13212657Skvn { 13312657Skvn if (new_depth < remove_below) 13412657Skvn SVN_ERR(svn_wc__db_base_remove(db, child_abspath, 13512657Skvn FALSE /* keep_as_working */, 13612657Skvn FALSE, FALSE, 13712657Skvn SVN_INVALID_REVNUM, 13812657Skvn NULL, NULL, iterpool)); 13912657Skvn 14012657Skvn continue; /* No recurse */ 14112657Skvn } 14212657Skvn 14312657Skvn if (new_depth < remove_below) 14412657Skvn { 14512657Skvn svn_boolean_t modified, all_deletes; 14612657Skvn 14712657Skvn SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, 14812657Skvn db, child_abspath, FALSE, 14912657Skvn cancel_func, cancel_baton, 15012657Skvn iterpool)); 15112657Skvn 15212657Skvn if (!modified || all_deletes) 15312657Skvn { 15412657Skvn SVN_ERR(svn_wc__db_base_remove(db, child_abspath, 15512657Skvn FALSE, FALSE, FALSE, 15612657Skvn SVN_INVALID_REVNUM, 15712657Skvn NULL, NULL, iterpool)); 15812657Skvn if (notify_func) 15912657Skvn { 16012657Skvn svn_wc_notify_t *notify; 16112657Skvn notify = svn_wc_create_notify(child_abspath, 16212657Skvn svn_wc_notify_delete, 16312657Skvn iterpool); 16412657Skvn (*notify_func)(notify_baton, notify, iterpool); 16512657Skvn } 16612657Skvn 16712657Skvn continue; /* No recurse */ 16812657Skvn } 16912657Skvn 17012657Skvn /* Fall through: recurse:*/ 17112657Skvn } 17212657Skvn 17312657Skvn if (kind == svn_node_dir) 17412657Skvn { 17512657Skvn SVN_ERR(crop_children(db, child_abspath, 17612657Skvn child_depth, svn_depth_empty, 17712657Skvn notify_func, notify_baton, 17812657Skvn cancel_func, cancel_baton, 17912657Skvn iterpool)); 18012657Skvn } 18112657Skvn } 18212657Skvn 18312657Skvn svn_pool_destroy(iterpool); 18412657Skvn 18512657Skvn return SVN_NO_ERROR; 18612657Skvn} 18712657Skvn 18812657Skvnsvn_error_t * 18912657Skvnsvn_wc_exclude(svn_wc_context_t *wc_ctx, 19012657Skvn const char *local_abspath, 19112657Skvn svn_cancel_func_t cancel_func, 19212657Skvn void *cancel_baton, 19312657Skvn svn_wc_notify_func2_t notify_func, 19412657Skvn void *notify_baton, 19512657Skvn apr_pool_t *scratch_pool) 19612657Skvn{ 19712657Skvn svn_boolean_t is_root, is_switched; 19812657Skvn svn_wc__db_status_t status; 19912657Skvn svn_node_kind_t kind; 20012657Skvn svn_revnum_t revision; 20112657Skvn svn_depth_t depth; 20212657Skvn svn_boolean_t modified, all_deletes; 20312657Skvn const char *repos_relpath, *repos_root, *repos_uuid; 20412657Skvn 20512657Skvn SVN_ERR(svn_wc__db_is_switched(&is_root, &is_switched, NULL, 20612657Skvn wc_ctx->db, local_abspath, scratch_pool)); 20712657Skvn 20812657Skvn if (is_root) 20912657Skvn { 21012657Skvn return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 21112657Skvn _("Cannot exclude '%s': " 21212657Skvn "it is a working copy root"), 21312657Skvn svn_dirent_local_style(local_abspath, 21412657Skvn scratch_pool)); 21512657Skvn } 21612657Skvn if (is_switched) 21712657Skvn { 21812657Skvn return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 21912657Skvn _("Cannot exclude '%s': " 22012657Skvn "it is a switched path"), 22112657Skvn svn_dirent_local_style(local_abspath, 22212657Skvn scratch_pool)); 22312657Skvn } 22412657Skvn 22512657Skvn SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, &repos_relpath, 22612657Skvn &repos_root, &repos_uuid, NULL, NULL, NULL, 22712657Skvn &depth, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 22812657Skvn NULL, NULL, NULL, NULL, NULL, NULL, NULL, 22912657Skvn NULL, NULL, NULL, 23012657Skvn wc_ctx->db, local_abspath, 23112657Skvn scratch_pool, scratch_pool)); 23212657Skvn 23312657Skvn switch (status) 23412657Skvn { 23512657Skvn case svn_wc__db_status_server_excluded: 23612657Skvn case svn_wc__db_status_excluded: 23712657Skvn case svn_wc__db_status_not_present: 23812657Skvn return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 23912657Skvn _("The node '%s' was not found."), 24012657Skvn svn_dirent_local_style(local_abspath, 24112657Skvn scratch_pool)); 24212657Skvn 24312657Skvn case svn_wc__db_status_added: 24412657Skvn /* Would have to check parents if we want to allow this */ 24512657Skvn return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 24612657Skvn _("Cannot exclude '%s': it is to be added " 24712657Skvn "to the repository. Try commit instead"), 24812657Skvn svn_dirent_local_style(local_abspath, 24912657Skvn scratch_pool)); 25012657Skvn case svn_wc__db_status_deleted: 25112657Skvn /* Would have to check parents if we want to allow this */ 25212657Skvn return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 25312657Skvn _("Cannot exclude '%s': it is to be deleted " 25412657Skvn "from the repository. Try commit instead"), 25512657Skvn svn_dirent_local_style(local_abspath, 25612657Skvn scratch_pool)); 25712657Skvn 25812657Skvn case svn_wc__db_status_normal: 25912657Skvn case svn_wc__db_status_incomplete: 26012657Skvn default: 26112657Skvn break; /* Ok to exclude */ 26212657Skvn } 26312657Skvn 26412657Skvn SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_deletes, 26512657Skvn wc_ctx->db, local_abspath, FALSE, 26612657Skvn cancel_func, cancel_baton, 26712657Skvn scratch_pool)); 26812657Skvn 26912657Skvn if (!modified || all_deletes) 27012657Skvn { 27112657Skvn /* Remove all working copy data below local_abspath */ 27212657Skvn SVN_ERR(svn_wc__db_base_remove(wc_ctx->db, local_abspath, 27312657Skvn FALSE /* keep_working */, 27412657Skvn FALSE, TRUE, 27512657Skvn revision, 27612657Skvn NULL, NULL, 27712657Skvn scratch_pool)); 27812657Skvn 27912657Skvn SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, 28012657Skvn cancel_func, cancel_baton, 28112657Skvn scratch_pool)); 28212657Skvn 28312657Skvn if (notify_func) 28412657Skvn { 28512657Skvn svn_wc_notify_t *notify; 28612657Skvn notify = svn_wc_create_notify(local_abspath, 28712657Skvn svn_wc_notify_exclude, 28812657Skvn scratch_pool); 28912657Skvn notify_func(notify_baton, notify, scratch_pool); 29012657Skvn } 29112657Skvn } 29212657Skvn else 29312657Skvn { 29412657Skvn /* Do the next best thing: retry below this path */ 29512657Skvn SVN_ERR(crop_children(wc_ctx->db, local_abspath, depth, svn_depth_empty, 29612657Skvn notify_func, notify_baton, 29712657Skvn cancel_func, cancel_baton, 29812657Skvn scratch_pool)); 29912657Skvn } 30012657Skvn 30112657Skvn return SVN_NO_ERROR; 30212657Skvn} 30312657Skvn 30412657Skvnsvn_error_t * 30512657Skvnsvn_wc_crop_tree2(svn_wc_context_t *wc_ctx, 30612657Skvn const char *local_abspath, 30712657Skvn svn_depth_t depth, 30812657Skvn svn_cancel_func_t cancel_func, 30912657Skvn void *cancel_baton, 31012657Skvn svn_wc_notify_func2_t notify_func, 31112657Skvn void *notify_baton, 31212657Skvn apr_pool_t *scratch_pool) 31312657Skvn{ 31412657Skvn svn_wc__db_t *db = wc_ctx->db; 31512657Skvn svn_wc__db_status_t status; 31612657Skvn svn_node_kind_t kind; 31712657Skvn svn_depth_t dir_depth; 31812657Skvn 31912657Skvn /* Only makes sense when the depth is restrictive. */ 32012657Skvn if (depth == svn_depth_infinity) 32112657Skvn return SVN_NO_ERROR; /* Nothing to crop */ 32212657Skvn if (!(depth >= svn_depth_empty && depth < svn_depth_infinity)) 32312657Skvn return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 32412657Skvn _("Can only crop a working copy with a restrictive depth")); 32512657Skvn 32612657Skvn SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 32712657Skvn NULL, NULL, &dir_depth, NULL, NULL, NULL, NULL, 32812657Skvn NULL, NULL, NULL, NULL, NULL, NULL, NULL, 32912657Skvn NULL, NULL, NULL, NULL, NULL, NULL, 33012657Skvn db, local_abspath, 33112657Skvn scratch_pool, scratch_pool)); 33212657Skvn 33312657Skvn if (kind != svn_node_dir) 33412657Skvn return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 33512657Skvn _("Can only crop directories")); 33612657Skvn 33712657Skvn switch (status) 33812657Skvn { 33912657Skvn case svn_wc__db_status_not_present: 34012657Skvn case svn_wc__db_status_server_excluded: 34112657Skvn return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 34212657Skvn _("The node '%s' was not found."), 34312657Skvn svn_dirent_local_style(local_abspath, 34412657Skvn scratch_pool)); 34512657Skvn 34612657Skvn case svn_wc__db_status_deleted: 34712657Skvn return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 34812657Skvn _("Cannot crop '%s': it is going to be removed " 34912657Skvn "from repository. Try commit instead"), 35012657Skvn svn_dirent_local_style(local_abspath, 35112657Skvn scratch_pool)); 35212657Skvn 35312657Skvn case svn_wc__db_status_added: 35412657Skvn return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 35512657Skvn _("Cannot crop '%s': it is to be added " 35612657Skvn "to the repository. Try commit instead"), 35712657Skvn svn_dirent_local_style(local_abspath, 35812657Skvn scratch_pool)); 35912657Skvn case svn_wc__db_status_excluded: 36012657Skvn return SVN_NO_ERROR; /* Nothing to do */ 36112657Skvn 36212657Skvn case svn_wc__db_status_normal: 36312657Skvn case svn_wc__db_status_incomplete: 36412657Skvn break; 36512657Skvn 36612657Skvn default: 36712657Skvn SVN_ERR_MALFUNCTION(); 36812657Skvn } 36912657Skvn 37012657Skvn SVN_ERR(crop_children(db, local_abspath, dir_depth, depth, 37112657Skvn notify_func, notify_baton, 37212657Skvn cancel_func, cancel_baton, scratch_pool)); 37312657Skvn 37412657Skvn return svn_error_trace(svn_wc__wq_run(db, local_abspath, 37512657Skvn cancel_func, cancel_baton, 37612657Skvn scratch_pool)); 37712657Skvn} 37812657Skvn