crop.c revision 251881
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 *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(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_read_children(&children, db, local_abspath, pool,
80                                   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
90      svn_pool_clear(iterpool);
91
92      /* Get the next node */
93      child_abspath = svn_dirent_join(local_abspath, child_name, iterpool);
94
95      SVN_ERR(svn_wc__db_read_info(&child_status, &kind, NULL, NULL, NULL,
96                                   NULL,NULL, NULL, NULL, &child_depth,
97                                   NULL, NULL, NULL, NULL, NULL, NULL,
98                                   NULL, NULL, NULL, NULL, NULL, NULL,
99                                   NULL, NULL, NULL, NULL, NULL,
100                                   db, child_abspath, iterpool, iterpool));
101
102      if (child_status == svn_wc__db_status_server_excluded ||
103          child_status == svn_wc__db_status_excluded ||
104          child_status == svn_wc__db_status_not_present)
105        {
106          svn_depth_t remove_below = (kind == svn_node_dir)
107                                            ? svn_depth_immediates
108                                            : svn_depth_files;
109          if (new_depth < remove_below)
110            SVN_ERR(svn_wc__db_base_remove(db, child_abspath,
111                                           FALSE /* keep_as_working */,
112                                           FALSE /* queue_deletes */,
113                                           SVN_INVALID_REVNUM,
114                                           NULL, NULL, iterpool));
115
116          continue;
117        }
118      else if (kind == svn_node_file)
119        {
120          if (new_depth == svn_depth_empty)
121            SVN_ERR(svn_wc__db_op_remove_node(NULL,
122                                              db, child_abspath,
123                                              TRUE /* destroy */,
124                                              FALSE /* destroy_changes */,
125                                              SVN_INVALID_REVNUM,
126                                              svn_wc__db_status_not_present,
127                                              svn_node_none,
128                                              NULL, NULL,
129                                              cancel_func, cancel_baton,
130                                              iterpool));
131          else
132            continue;
133
134        }
135      else if (kind == svn_node_dir)
136        {
137          if (new_depth < svn_depth_immediates)
138            {
139              SVN_ERR(svn_wc__db_op_remove_node(NULL,
140                                                db, child_abspath,
141                                                TRUE /* destroy */,
142                                                FALSE /* destroy_changes */,
143                                                SVN_INVALID_REVNUM,
144                                                svn_wc__db_status_not_present,
145                                                svn_node_none,
146                                                NULL, NULL,
147                                                cancel_func, cancel_baton,
148                                                iterpool));
149            }
150          else
151            {
152              SVN_ERR(crop_children(db,
153                                    child_abspath,
154                                    child_depth,
155                                    svn_depth_empty,
156                                    notify_func,
157                                    notify_baton,
158                                    cancel_func,
159                                    cancel_baton,
160                                    iterpool));
161              continue;
162            }
163        }
164      else
165        {
166          return svn_error_createf
167            (SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("Unknown node kind for '%s'"),
168             svn_dirent_local_style(child_abspath, iterpool));
169        }
170
171      if (notify_func)
172        {
173          svn_wc_notify_t *notify;
174          notify = svn_wc_create_notify(child_abspath,
175                                        svn_wc_notify_delete,
176                                        iterpool);
177          (*notify_func)(notify_baton, notify, iterpool);
178        }
179    }
180
181  svn_pool_destroy(iterpool);
182
183  return SVN_NO_ERROR;
184}
185
186svn_error_t *
187svn_wc_exclude(svn_wc_context_t *wc_ctx,
188               const char *local_abspath,
189               svn_cancel_func_t cancel_func,
190               void *cancel_baton,
191               svn_wc_notify_func2_t notify_func,
192               void *notify_baton,
193               apr_pool_t *scratch_pool)
194{
195  svn_boolean_t is_root, is_switched;
196  svn_wc__db_status_t status;
197  svn_node_kind_t kind;
198  svn_revnum_t revision;
199  const char *repos_relpath, *repos_root, *repos_uuid;
200
201  SVN_ERR(svn_wc__db_is_switched(&is_root, &is_switched, NULL,
202                                 wc_ctx->db, local_abspath, scratch_pool));
203
204  if (is_root)
205    {
206       return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
207                                _("Cannot exclude '%s': "
208                                  "it is a working copy root"),
209                                svn_dirent_local_style(local_abspath,
210                                                       scratch_pool));
211    }
212  if (is_switched)
213    {
214      return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
215                               _("Cannot exclude '%s': "
216                                 "it is a switched path"),
217                               svn_dirent_local_style(local_abspath,
218                                                      scratch_pool));
219    }
220
221  SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, &repos_relpath,
222                               &repos_root, &repos_uuid, NULL, NULL, NULL,
223                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
224                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
225                               NULL, NULL, NULL,
226                               wc_ctx->db, local_abspath,
227                               scratch_pool, scratch_pool));
228
229  switch (status)
230    {
231      case svn_wc__db_status_server_excluded:
232      case svn_wc__db_status_excluded:
233      case svn_wc__db_status_not_present:
234        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
235                                 _("The node '%s' was not found."),
236                                 svn_dirent_local_style(local_abspath,
237                                                        scratch_pool));
238
239      case svn_wc__db_status_added:
240        /* Would have to check parents if we want to allow this */
241        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
242                                 _("Cannot exclude '%s': it is to be added "
243                                   "to the repository. Try commit instead"),
244                                 svn_dirent_local_style(local_abspath,
245                                                        scratch_pool));
246      case svn_wc__db_status_deleted:
247        /* Would have to check parents if we want to allow this */
248        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
249                                 _("Cannot exclude '%s': it is to be deleted "
250                                   "from the repository. Try commit instead"),
251                                 svn_dirent_local_style(local_abspath,
252                                                        scratch_pool));
253
254      case svn_wc__db_status_normal:
255      case svn_wc__db_status_incomplete:
256      default:
257        break; /* Ok to exclude */
258    }
259
260  /* Remove all working copy data below local_abspath */
261  SVN_ERR(svn_wc__db_op_remove_node(NULL,
262                                    wc_ctx->db, local_abspath,
263                                    TRUE /* destroy */,
264                                    FALSE /* destroy_changes */,
265                                    revision,
266                                    svn_wc__db_status_excluded,
267                                    kind,
268                                    NULL, NULL,
269                                    cancel_func, cancel_baton,
270                                    scratch_pool));
271
272  SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath,
273                         cancel_func, cancel_baton,
274                         scratch_pool));
275
276  if (notify_func)
277    {
278      svn_wc_notify_t *notify;
279      notify = svn_wc_create_notify(local_abspath,
280                                    svn_wc_notify_exclude,
281                                    scratch_pool);
282      notify_func(notify_baton, notify, scratch_pool);
283    }
284
285  return SVN_NO_ERROR;
286}
287
288svn_error_t *
289svn_wc_crop_tree2(svn_wc_context_t *wc_ctx,
290                  const char *local_abspath,
291                  svn_depth_t depth,
292                  svn_cancel_func_t cancel_func,
293                  void *cancel_baton,
294                  svn_wc_notify_func2_t notify_func,
295                  void *notify_baton,
296                  apr_pool_t *scratch_pool)
297{
298  svn_wc__db_t *db = wc_ctx->db;
299  svn_wc__db_status_t status;
300  svn_node_kind_t kind;
301  svn_depth_t dir_depth;
302
303  /* Only makes sense when the depth is restrictive. */
304  if (depth == svn_depth_infinity)
305    return SVN_NO_ERROR; /* Nothing to crop */
306  if (!(depth >= svn_depth_empty && depth < svn_depth_infinity))
307    return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
308      _("Can only crop a working copy with a restrictive depth"));
309
310  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
311                               NULL, NULL, &dir_depth, NULL, NULL, NULL, NULL,
312                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
313                               NULL, NULL, NULL, NULL, NULL, NULL,
314                               db, local_abspath,
315                               scratch_pool, scratch_pool));
316
317  if (kind != svn_node_dir)
318    return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
319      _("Can only crop directories"));
320
321  switch (status)
322    {
323      case svn_wc__db_status_not_present:
324      case svn_wc__db_status_server_excluded:
325        return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
326                                 _("The node '%s' was not found."),
327                                 svn_dirent_local_style(local_abspath,
328                                                        scratch_pool));
329
330      case svn_wc__db_status_deleted:
331        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
332                               _("Cannot crop '%s': it is going to be removed "
333                                 "from repository. Try commit instead"),
334                               svn_dirent_local_style(local_abspath,
335                                                      scratch_pool));
336
337      case svn_wc__db_status_added:
338        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
339                                 _("Cannot crop '%s': it is to be added "
340                                   "to the repository. Try commit instead"),
341                                 svn_dirent_local_style(local_abspath,
342                                                        scratch_pool));
343      case svn_wc__db_status_excluded:
344        return SVN_NO_ERROR; /* Nothing to do */
345
346      case svn_wc__db_status_normal:
347      case svn_wc__db_status_incomplete:
348        break;
349
350      default:
351        SVN_ERR_MALFUNCTION();
352    }
353
354  SVN_ERR(crop_children(db, local_abspath, dir_depth, depth,
355                        notify_func, notify_baton,
356                        cancel_func, cancel_baton, scratch_pool));
357
358  return svn_error_trace(svn_wc__wq_run(db, local_abspath,
359                                        cancel_func, cancel_baton,
360                                        scratch_pool));
361}
362