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