adm_ops.c revision 289166
112651Skvn/*
212651Skvn * adm_ops.c: routines for affecting working copy administrative
312651Skvn *            information.  NOTE: this code doesn't know where the adm
412651Skvn *            info is actually stored.  Instead, generic handles to
512651Skvn *            adm data are requested via a reference to some PATH
612651Skvn *            (PATH being a regular, non-administrative directory or
712651Skvn *            file in the working copy).
812651Skvn *
912651Skvn * ====================================================================
1012651Skvn *    Licensed to the Apache Software Foundation (ASF) under one
1112651Skvn *    or more contributor license agreements.  See the NOTICE file
1212651Skvn *    distributed with this work for additional information
1312651Skvn *    regarding copyright ownership.  The ASF licenses this file
1412651Skvn *    to you under the Apache License, Version 2.0 (the
1512651Skvn *    "License"); you may not use this file except in compliance
1612651Skvn *    with the License.  You may obtain a copy of the License at
1712651Skvn *
1812651Skvn *      http://www.apache.org/licenses/LICENSE-2.0
1912651Skvn *
2012651Skvn *    Unless required by applicable law or agreed to in writing,
2112651Skvn *    software distributed under the License is distributed on an
2212651Skvn *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
2312651Skvn *    KIND, either express or implied.  See the License for the
2412651Skvn *    specific language governing permissions and limitations
2512651Skvn *    under the License.
2612651Skvn * ====================================================================
2712651Skvn */
2812651Skvn
2912651Skvn
3012651Skvn
3112651Skvn#include <string.h>
3212651Skvn#include <stdlib.h>
3312651Skvn
3412651Skvn#include <apr_pools.h>
3512651Skvn#include <apr_hash.h>
3612651Skvn#include <apr_time.h>
3712651Skvn#include <apr_errno.h>
3812651Skvn
3912651Skvn#include "svn_types.h"
4012651Skvn#include "svn_pools.h"
4112651Skvn#include "svn_string.h"
4212651Skvn#include "svn_error.h"
4312651Skvn#include "svn_dirent_uri.h"
4412651Skvn#include "svn_path.h"
4512651Skvn#include "svn_hash.h"
4612651Skvn#include "svn_wc.h"
4712651Skvn#include "svn_io.h"
4812651Skvn#include "svn_time.h"
4912651Skvn#include "svn_sorts.h"
5012651Skvn
5112651Skvn#include "wc.h"
5212651Skvn#include "adm_files.h"
5312651Skvn#include "conflicts.h"
5412651Skvn#include "workqueue.h"
5512651Skvn
5612651Skvn#include "svn_private_config.h"
5712651Skvn#include "private/svn_subr_private.h"
5812651Skvn#include "private/svn_dep_compat.h"
5912651Skvn
6012651Skvn
6112651Skvnstruct svn_wc_committed_queue_t
6212651Skvn{
6312651Skvn  /* The pool in which ->queue is allocated. */
6412651Skvn  apr_pool_t *pool;
6512651Skvn  /* Mapping (const char *) local_abspath to (committed_queue_item_t *). */
6612651Skvn  apr_hash_t *queue;
6712651Skvn  /* Is any item in the queue marked as 'recursive'? */
6812651Skvn  svn_boolean_t have_recursive;
6912651Skvn};
7012651Skvn
7112651Skvntypedef struct committed_queue_item_t
7212651Skvn{
7312651Skvn  const char *local_abspath;
7412651Skvn  svn_boolean_t recurse;
7512651Skvn  svn_boolean_t no_unlock;
7612651Skvn  svn_boolean_t keep_changelist;
7712651Skvn
7812651Skvn  /* The pristine text checksum. */
7912651Skvn  const svn_checksum_t *sha1_checksum;
8012651Skvn
8112651Skvn  apr_hash_t *new_dav_cache;
8212651Skvn} committed_queue_item_t;
8312651Skvn
8412651Skvn
8512651Skvnapr_pool_t *
8612651Skvnsvn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue)
8712651Skvn{
8812651Skvn  return queue->pool;
8912651Skvn}
9012651Skvn
9112651Skvn
9212651Skvn
9312651Skvn/*** Finishing updates and commits. ***/
9412651Skvn
9512651Skvn/* Queue work items that will finish a commit of the file or directory
9612651Skvn * LOCAL_ABSPATH in DB:
9712651Skvn *   - queue the removal of any "revert-base" props and text files;
9812651Skvn *   - queue an update of the DB entry for this node
9912651Skvn *
10012651Skvn * ### The Pristine Store equivalent should be:
10112651Skvn *   - remember the old BASE_NODE and WORKING_NODE pristine text c'sums;
10212651Skvn *   - queue an update of the DB entry for this node (incl. updating the
10312651Skvn *       BASE_NODE c'sum and setting the WORKING_NODE c'sum to NULL);
10412651Skvn *   - queue deletion of the old pristine texts by the remembered checksums.
10512651Skvn *
10612651Skvn * CHECKSUM is the checksum of the new text base for LOCAL_ABSPATH, and must
10712651Skvn * be provided if there is one, else NULL.
10812651Skvn *
10912651Skvn * STATUS, KIND, PROP_MODS and OLD_CHECKSUM are the current in-db values of
11012651Skvn * the node LOCAL_ABSPATH.
11112651Skvn */
11212651Skvnstatic svn_error_t *
11312651Skvnprocess_committed_leaf(svn_wc__db_t *db,
11412651Skvn                       const char *local_abspath,
11512651Skvn                       svn_boolean_t via_recurse,
11612651Skvn                       svn_wc__db_status_t status,
11712651Skvn                       svn_node_kind_t kind,
11812651Skvn                       svn_boolean_t prop_mods,
11912651Skvn                       const svn_checksum_t *old_checksum,
12012651Skvn                       svn_revnum_t new_revnum,
12112651Skvn                       apr_time_t new_changed_date,
12212651Skvn                       const char *new_changed_author,
12312651Skvn                       apr_hash_t *new_dav_cache,
12412651Skvn                       svn_boolean_t no_unlock,
12512651Skvn                       svn_boolean_t keep_changelist,
12612651Skvn                       const svn_checksum_t *checksum,
12712651Skvn                       apr_pool_t *scratch_pool)
12812651Skvn{
12912651Skvn  svn_revnum_t new_changed_rev = new_revnum;
13012651Skvn  svn_skel_t *work_item = NULL;
13112651Skvn
13212651Skvn  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13312651Skvn
13412651Skvn  {
13512651Skvn    const char *adm_abspath;
13612651Skvn
13712651Skvn    if (kind == svn_node_dir)
13812651Skvn      adm_abspath = local_abspath;
13912651Skvn    else
14012651Skvn      adm_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
14112651Skvn    SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool));
14212651Skvn  }
14312651Skvn
14412651Skvn  if (status == svn_wc__db_status_deleted)
14512651Skvn    {
14612651Skvn      return svn_error_trace(
14712651Skvn                svn_wc__db_base_remove(
148                                db, local_abspath,
149                                FALSE /* keep_as_working */,
150                                FALSE /* queue_deletes */,
151                                TRUE  /* remove_locks */,
152                                (! via_recurse)
153                                    ? new_revnum : SVN_INVALID_REVNUM,
154                                NULL, NULL,
155                                scratch_pool));
156    }
157  else if (status == svn_wc__db_status_not_present)
158    {
159      /* We are committing the leaf of a copy operation.
160         We leave the not-present marker to allow pulling in excluded
161         children of a copy.
162
163         The next update will remove the not-present marker. */
164
165      return SVN_NO_ERROR;
166    }
167
168  SVN_ERR_ASSERT(status == svn_wc__db_status_normal
169                 || status == svn_wc__db_status_incomplete
170                 || status == svn_wc__db_status_added);
171
172  if (kind != svn_node_dir)
173    {
174      /* If we sent a delta (meaning: post-copy modification),
175         then this file will appear in the queue and so we should have
176         its checksum already. */
177      if (checksum == NULL)
178        {
179          /* It was copied and not modified. We must have a text
180             base for it. And the node should have a checksum. */
181          SVN_ERR_ASSERT(old_checksum != NULL);
182
183          checksum = old_checksum;
184
185          /* Is the node completely unmodified and are we recursing? */
186          if (via_recurse && !prop_mods)
187            {
188              /* If a copied node itself is not modified, but the op_root of
189                 the copy is committed we have to make sure that changed_rev,
190                 changed_date and changed_author don't change or the working
191                 copy used for committing will show different last modified
192                 information then a clean checkout of exactly the same
193                 revisions. (Issue #3676) */
194
195              SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL,
196                                           NULL, &new_changed_rev,
197                                           &new_changed_date,
198                                           &new_changed_author, NULL, NULL,
199                                           NULL, NULL, NULL, NULL, NULL,
200                                           NULL, NULL, NULL, NULL,
201                                           NULL, NULL, NULL, NULL,
202                                           NULL, NULL, NULL,
203                                           db, local_abspath,
204                                           scratch_pool, scratch_pool));
205            }
206        }
207
208      SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
209                                           db, local_abspath,
210                                           prop_mods,
211                                           scratch_pool, scratch_pool));
212    }
213
214  /* The new text base will be found in the pristine store by its checksum. */
215  SVN_ERR(svn_wc__db_global_commit(db, local_abspath,
216                                   new_revnum, new_changed_rev,
217                                   new_changed_date, new_changed_author,
218                                   checksum,
219                                   NULL /* new_children */,
220                                   new_dav_cache,
221                                   keep_changelist,
222                                   no_unlock,
223                                   work_item,
224                                   scratch_pool));
225
226  return SVN_NO_ERROR;
227}
228
229
230svn_error_t *
231svn_wc__process_committed_internal(svn_wc__db_t *db,
232                                   const char *local_abspath,
233                                   svn_boolean_t recurse,
234                                   svn_boolean_t top_of_recurse,
235                                   svn_revnum_t new_revnum,
236                                   apr_time_t new_date,
237                                   const char *rev_author,
238                                   apr_hash_t *new_dav_cache,
239                                   svn_boolean_t no_unlock,
240                                   svn_boolean_t keep_changelist,
241                                   const svn_checksum_t *sha1_checksum,
242                                   const svn_wc_committed_queue_t *queue,
243                                   apr_pool_t *scratch_pool)
244{
245  svn_wc__db_status_t status;
246  svn_node_kind_t kind;
247  const svn_checksum_t *old_checksum;
248  svn_boolean_t prop_mods;
249
250  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
251                               NULL, NULL, NULL, &old_checksum, NULL, NULL,
252                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
253                               NULL, NULL, &prop_mods, NULL, NULL, NULL,
254                               db, local_abspath,
255                               scratch_pool, scratch_pool));
256
257  /* NOTE: be wary of making crazy semantic changes in this function, since
258     svn_wc_process_committed4() calls this.  */
259
260  SVN_ERR(process_committed_leaf(db, local_abspath, !top_of_recurse,
261                                 status, kind, prop_mods, old_checksum,
262                                 new_revnum, new_date, rev_author,
263                                 new_dav_cache,
264                                 no_unlock, keep_changelist,
265                                 sha1_checksum,
266                                 scratch_pool));
267
268  /* Only check for recursion on nodes that have children */
269  if (kind != svn_node_file
270      || status == svn_wc__db_status_not_present
271      || status == svn_wc__db_status_excluded
272      || status == svn_wc__db_status_server_excluded
273      /* Node deleted -> then no longer a directory */
274      || status == svn_wc__db_status_deleted)
275    {
276      return SVN_NO_ERROR;
277    }
278
279  if (recurse)
280    {
281      const apr_array_header_t *children;
282      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
283      int i;
284
285      /* Read PATH's entries;  this is the absolute path. */
286      SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
287                                       scratch_pool, iterpool));
288
289      /* Recursively loop over all children. */
290      for (i = 0; i < children->nelts; i++)
291        {
292          const char *name = APR_ARRAY_IDX(children, i, const char *);
293          const char *this_abspath;
294          const committed_queue_item_t *cqi;
295
296          svn_pool_clear(iterpool);
297
298          this_abspath = svn_dirent_join(local_abspath, name, iterpool);
299
300          sha1_checksum = NULL;
301          cqi = svn_hash_gets(queue->queue, this_abspath);
302
303          if (cqi != NULL)
304            sha1_checksum = cqi->sha1_checksum;
305
306          /* Recurse.  Pass NULL for NEW_DAV_CACHE, because the
307             ones present in the current call are only applicable to
308             this one committed item. */
309          SVN_ERR(svn_wc__process_committed_internal(
310                    db, this_abspath,
311                    TRUE /* recurse */,
312                    FALSE /* top_of_recurse */,
313                    new_revnum, new_date,
314                    rev_author,
315                    NULL /* new_dav_cache */,
316                    TRUE /* no_unlock */,
317                    keep_changelist,
318                    sha1_checksum,
319                    queue,
320                    iterpool));
321        }
322
323      svn_pool_destroy(iterpool);
324    }
325
326  return SVN_NO_ERROR;
327}
328
329
330apr_hash_t *
331svn_wc__prop_array_to_hash(const apr_array_header_t *props,
332                           apr_pool_t *result_pool)
333{
334  int i;
335  apr_hash_t *prophash;
336
337  if (props == NULL || props->nelts == 0)
338    return NULL;
339
340  prophash = apr_hash_make(result_pool);
341
342  for (i = 0; i < props->nelts; i++)
343    {
344      const svn_prop_t *prop = APR_ARRAY_IDX(props, i, const svn_prop_t *);
345      if (prop->value != NULL)
346        svn_hash_sets(prophash, prop->name, prop->value);
347    }
348
349  return prophash;
350}
351
352
353svn_wc_committed_queue_t *
354svn_wc_committed_queue_create(apr_pool_t *pool)
355{
356  svn_wc_committed_queue_t *q;
357
358  q = apr_palloc(pool, sizeof(*q));
359  q->pool = pool;
360  q->queue = apr_hash_make(pool);
361  q->have_recursive = FALSE;
362
363  return q;
364}
365
366
367svn_error_t *
368svn_wc_queue_committed3(svn_wc_committed_queue_t *queue,
369                        svn_wc_context_t *wc_ctx,
370                        const char *local_abspath,
371                        svn_boolean_t recurse,
372                        const apr_array_header_t *wcprop_changes,
373                        svn_boolean_t remove_lock,
374                        svn_boolean_t remove_changelist,
375                        const svn_checksum_t *sha1_checksum,
376                        apr_pool_t *scratch_pool)
377{
378  committed_queue_item_t *cqi;
379
380  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
381
382  queue->have_recursive |= recurse;
383
384  /* Use the same pool as the one QUEUE was allocated in,
385     to prevent lifetime issues.  Intermediate operations
386     should use SCRATCH_POOL. */
387
388  /* Add to the array with paths and options */
389  cqi = apr_palloc(queue->pool, sizeof(*cqi));
390  cqi->local_abspath = local_abspath;
391  cqi->recurse = recurse;
392  cqi->no_unlock = !remove_lock;
393  cqi->keep_changelist = !remove_changelist;
394  cqi->sha1_checksum = sha1_checksum;
395  cqi->new_dav_cache = svn_wc__prop_array_to_hash(wcprop_changes, queue->pool);
396
397  svn_hash_sets(queue->queue, local_abspath, cqi);
398
399  return SVN_NO_ERROR;
400}
401
402
403/* Return TRUE if any item of QUEUE is a parent of ITEM and will be
404   processed recursively, return FALSE otherwise.
405
406   The algorithmic complexity of this search implementation is O(queue
407   length), but it's quite quick.
408*/
409static svn_boolean_t
410have_recursive_parent(apr_hash_t *queue,
411                      const committed_queue_item_t *item,
412                      apr_pool_t *scratch_pool)
413{
414  apr_hash_index_t *hi;
415  const char *local_abspath = item->local_abspath;
416
417  for (hi = apr_hash_first(scratch_pool, queue); hi; hi = apr_hash_next(hi))
418    {
419      const committed_queue_item_t *qi = svn__apr_hash_index_val(hi);
420
421      if (qi == item)
422        continue;
423
424      if (qi->recurse && svn_dirent_is_child(qi->local_abspath, local_abspath,
425                                             NULL))
426        return TRUE;
427    }
428
429  return FALSE;
430}
431
432
433svn_error_t *
434svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue,
435                                svn_wc_context_t *wc_ctx,
436                                svn_revnum_t new_revnum,
437                                const char *rev_date,
438                                const char *rev_author,
439                                svn_cancel_func_t cancel_func,
440                                void *cancel_baton,
441                                apr_pool_t *scratch_pool)
442{
443  apr_array_header_t *sorted_queue;
444  int i;
445  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
446  apr_time_t new_date;
447  apr_hash_t *run_wqs = apr_hash_make(scratch_pool);
448  apr_hash_index_t *hi;
449
450  if (rev_date)
451    SVN_ERR(svn_time_from_cstring(&new_date, rev_date, iterpool));
452  else
453    new_date = 0;
454
455  /* Process the queued items in order of their paths.  (The requirement is
456   * probably just that a directory must be processed before its children.) */
457  sorted_queue = svn_sort__hash(queue->queue, svn_sort_compare_items_as_paths,
458                                scratch_pool);
459  for (i = 0; i < sorted_queue->nelts; i++)
460    {
461      const svn_sort__item_t *sort_item
462        = &APR_ARRAY_IDX(sorted_queue, i, svn_sort__item_t);
463      const committed_queue_item_t *cqi = sort_item->value;
464      const char *wcroot_abspath;
465
466      svn_pool_clear(iterpool);
467
468      /* Skip this item if it is a child of a recursive item, because it has
469         been (or will be) accounted for when that recursive item was (or
470         will be) processed. */
471      if (queue->have_recursive && have_recursive_parent(queue->queue, cqi,
472                                                         iterpool))
473        continue;
474
475      SVN_ERR(svn_wc__process_committed_internal(
476                wc_ctx->db, cqi->local_abspath,
477                cqi->recurse,
478                TRUE /* top_of_recurse */,
479                new_revnum, new_date, rev_author,
480                cqi->new_dav_cache,
481                cqi->no_unlock,
482                cqi->keep_changelist,
483                cqi->sha1_checksum, queue,
484                iterpool));
485
486      /* Don't run the wq now, but remember that we must call it for this
487         working copy */
488      SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath,
489                                    wc_ctx->db, cqi->local_abspath,
490                                    iterpool, iterpool));
491
492      if (! svn_hash_gets(run_wqs, wcroot_abspath))
493        {
494          wcroot_abspath = apr_pstrdup(scratch_pool, wcroot_abspath);
495          svn_hash_sets(run_wqs, wcroot_abspath, wcroot_abspath);
496        }
497    }
498
499  /* Make sure nothing happens if this function is called again.  */
500  apr_hash_clear(queue->queue);
501
502  /* Ok; everything is committed now. Now we can start calling callbacks */
503
504  if (cancel_func)
505    SVN_ERR(cancel_func(cancel_baton));
506
507  for (hi = apr_hash_first(scratch_pool, run_wqs);
508       hi;
509       hi = apr_hash_next(hi))
510    {
511      const char *wcroot_abspath = svn__apr_hash_index_key(hi);
512
513      svn_pool_clear(iterpool);
514
515      SVN_ERR(svn_wc__wq_run(wc_ctx->db, wcroot_abspath,
516                             cancel_func, cancel_baton,
517                             iterpool));
518    }
519
520  svn_pool_destroy(iterpool);
521
522  return SVN_NO_ERROR;
523}
524
525/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in
526 * its parent directory in the WC.  It will have the regular properties
527 * provided in PROPS, or none if that is NULL.
528 *
529 * If the node is a file, set its on-disk executable and read-only bits to
530 * match its properties and lock state,
531 * ### only if it has an svn:executable or svn:needs-lock property.
532 * ### This is to match the previous behaviour of setting its props
533 *     afterwards by calling svn_wc_prop_set4(), but is not very clean.
534 *
535 * Sync the on-disk executable and read-only bits accordingly.
536 */
537static svn_error_t *
538add_from_disk(svn_wc__db_t *db,
539              const char *local_abspath,
540              svn_node_kind_t kind,
541              const apr_hash_t *props,
542              apr_pool_t *scratch_pool)
543{
544  if (kind == svn_node_file)
545    {
546      svn_skel_t *work_item = NULL;
547
548      if (props && (svn_prop_get_value(props, SVN_PROP_EXECUTABLE)
549                    || svn_prop_get_value(props, SVN_PROP_NEEDS_LOCK)))
550        SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
551                                                 scratch_pool, scratch_pool));
552
553      SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, props, work_item,
554                                     scratch_pool));
555      if (work_item)
556        SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, scratch_pool));
557    }
558  else
559    {
560      SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, props, NULL,
561                                          scratch_pool));
562    }
563
564  return SVN_NO_ERROR;
565}
566
567
568/* Set *REPOS_ROOT_URL and *REPOS_UUID to the repository of the parent of
569   LOCAL_ABSPATH.  REPOS_ROOT_URL and/or REPOS_UUID may be NULL if not
570   wanted.  Check that the parent of LOCAL_ABSPATH is a versioned directory
571   in a state in which a new child node can be scheduled for addition;
572   return an error if not. */
573static svn_error_t *
574check_can_add_to_parent(const char **repos_root_url,
575                        const char **repos_uuid,
576                        svn_wc__db_t *db,
577                        const char *local_abspath,
578                        apr_pool_t *result_pool,
579                        apr_pool_t *scratch_pool)
580{
581  const char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
582  svn_wc__db_status_t parent_status;
583  svn_node_kind_t parent_kind;
584  svn_error_t *err;
585
586  SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool));
587
588  err = svn_wc__db_read_info(&parent_status, &parent_kind, NULL,
589                             NULL, repos_root_url, repos_uuid, NULL, NULL,
590                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
591                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
592                             NULL, NULL, NULL,
593                             db, parent_abspath, result_pool, scratch_pool);
594
595  if (err
596      || parent_status == svn_wc__db_status_not_present
597      || parent_status == svn_wc__db_status_excluded
598      || parent_status == svn_wc__db_status_server_excluded)
599    {
600      return
601        svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
602                          _("Can't find parent directory's node while"
603                            " trying to add '%s'"),
604                          svn_dirent_local_style(local_abspath,
605                                                 scratch_pool));
606    }
607  else if (parent_status == svn_wc__db_status_deleted)
608    {
609      return
610        svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
611                          _("Can't add '%s' to a parent directory"
612                            " scheduled for deletion"),
613                          svn_dirent_local_style(local_abspath,
614                                                 scratch_pool));
615    }
616  else if (parent_kind != svn_node_dir)
617    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
618                             _("Can't schedule an addition of '%s'"
619                               " below a not-directory node"),
620                             svn_dirent_local_style(local_abspath,
621                                                    scratch_pool));
622
623  /* If we haven't found the repository info yet, find it now. */
624  if ((repos_root_url && ! *repos_root_url)
625      || (repos_uuid && ! *repos_uuid))
626    {
627      if (parent_status == svn_wc__db_status_added)
628        SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
629                                         repos_root_url, repos_uuid, NULL,
630                                         NULL, NULL, NULL,
631                                         db, parent_abspath,
632                                         result_pool, scratch_pool));
633      else
634        SVN_ERR(svn_wc__db_scan_base_repos(NULL,
635                                           repos_root_url, repos_uuid,
636                                           db, parent_abspath,
637                                           result_pool, scratch_pool));
638    }
639
640  return SVN_NO_ERROR;
641}
642
643
644/* Check that the on-disk item at LOCAL_ABSPATH can be scheduled for
645 * addition to its WC parent directory.
646 *
647 * Set *KIND_P to the kind of node to be added, *DB_ROW_EXISTS_P to whether
648 * it is already a versioned path, and if so, *IS_WC_ROOT_P to whether it's
649 * a WC root.
650 *
651 * ### The checks here, and the outputs, are geared towards svn_wc_add4().
652 */
653static svn_error_t *
654check_can_add_node(svn_node_kind_t *kind_p,
655                   svn_boolean_t *db_row_exists_p,
656                   svn_boolean_t *is_wc_root_p,
657                   svn_wc__db_t *db,
658                   const char *local_abspath,
659                   const char *copyfrom_url,
660                   svn_revnum_t copyfrom_rev,
661                   apr_pool_t *scratch_pool)
662{
663  const char *base_name = svn_dirent_basename(local_abspath, scratch_pool);
664  svn_boolean_t is_wc_root;
665  svn_node_kind_t kind;
666  svn_boolean_t is_special;
667
668  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
669  SVN_ERR_ASSERT(!copyfrom_url || (svn_uri_is_canonical(copyfrom_url,
670                                                        scratch_pool)
671                                   && SVN_IS_VALID_REVNUM(copyfrom_rev)));
672
673  /* Check that the proposed node has an acceptable name. */
674  if (svn_wc_is_adm_dir(base_name, scratch_pool))
675    return svn_error_createf
676      (SVN_ERR_ENTRY_FORBIDDEN, NULL,
677       _("Can't create an entry with a reserved name while trying to add '%s'"),
678       svn_dirent_local_style(local_abspath, scratch_pool));
679
680  SVN_ERR(svn_path_check_valid(local_abspath, scratch_pool));
681
682  /* Make sure something's there; set KIND and *KIND_P. */
683  SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
684                                    scratch_pool));
685  if (kind == svn_node_none)
686    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
687                             _("'%s' not found"),
688                             svn_dirent_local_style(local_abspath,
689                                                    scratch_pool));
690  if (kind == svn_node_unknown)
691    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
692                             _("Unsupported node kind for path '%s'"),
693                             svn_dirent_local_style(local_abspath,
694                                                    scratch_pool));
695  if (kind_p)
696    *kind_p = kind;
697
698  /* Determine whether a DB row for this node EXISTS, and whether it
699     IS_WC_ROOT.  If it exists, check that it is in an acceptable state for
700     adding the new node; if not, return an error. */
701  {
702    svn_wc__db_status_t status;
703    svn_boolean_t conflicted;
704    svn_boolean_t exists;
705    svn_error_t *err
706      = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
707                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
708                             NULL, NULL, NULL, NULL, NULL, NULL,
709                             &conflicted,
710                             NULL, NULL, NULL, NULL, NULL, NULL,
711                             db, local_abspath,
712                             scratch_pool, scratch_pool);
713
714    if (err)
715      {
716        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
717          return svn_error_trace(err);
718
719        svn_error_clear(err);
720        exists = FALSE;
721        is_wc_root = FALSE;
722      }
723    else
724      {
725        is_wc_root = FALSE;
726        exists = TRUE;
727
728        /* Note that the node may be in conflict even if it does not
729         * exist on disk (certain tree conflict scenarios). */
730        if (conflicted)
731          return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
732                                   _("'%s' is an existing item in conflict; "
733                                   "please mark the conflict as resolved "
734                                   "before adding a new item here"),
735                                   svn_dirent_local_style(local_abspath,
736                                                          scratch_pool));
737        switch (status)
738          {
739            case svn_wc__db_status_not_present:
740              break;
741            case svn_wc__db_status_deleted:
742              /* A working copy root should never have a WORKING_NODE */
743              SVN_ERR_ASSERT(!is_wc_root);
744              break;
745            case svn_wc__db_status_normal:
746              SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, local_abspath,
747                                           scratch_pool));
748
749              if (is_wc_root && copyfrom_url)
750                {
751                  /* Integrate a sub working copy in a parent working copy
752                     (legacy behavior) */
753                  break;
754                }
755              else if (is_wc_root && is_special)
756                {
757                  /* Adding a symlink to a working copy root.
758                     (special_tests.py 23: externals as symlink targets) */
759                  break;
760                }
761              /* else: Fall through in default error */
762
763            default:
764              return svn_error_createf(
765                               SVN_ERR_ENTRY_EXISTS, NULL,
766                               _("'%s' is already under version control"),
767                               svn_dirent_local_style(local_abspath,
768                                                      scratch_pool));
769          }
770      } /* err */
771
772    if (db_row_exists_p)
773      *db_row_exists_p = exists;
774    if (is_wc_root_p)
775      *is_wc_root_p = is_wc_root;
776  }
777
778  return SVN_NO_ERROR;
779}
780
781
782/* Convert the nested pristine working copy rooted at LOCAL_ABSPATH into
783 * a copied subtree in the outer working copy.
784 *
785 * LOCAL_ABSPATH must be the root of a nested working copy that has no
786 * local modifications.  The parent directory of LOCAL_ABSPATH must be a
787 * versioned directory in the outer WC, and must belong to the same
788 * repository as the nested WC.  The nested WC will be integrated into the
789 * parent's WC, and will no longer be a separate WC. */
790static svn_error_t *
791integrate_nested_wc_as_copy(svn_wc_context_t *wc_ctx,
792                            const char *local_abspath,
793                            apr_pool_t *scratch_pool)
794{
795  svn_wc__db_t *db = wc_ctx->db;
796  const char *moved_abspath;
797
798  /* Drop any references to the wc that is to be rewritten */
799  SVN_ERR(svn_wc__db_drop_root(db, local_abspath, scratch_pool));
800
801  /* Move the admin dir from the wc to a temporary location: MOVED_ABSPATH */
802  {
803    const char *tmpdir_abspath;
804    const char *moved_adm_abspath;
805    const char *adm_abspath;
806
807    SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
808                                           svn_dirent_dirname(local_abspath,
809                                                              scratch_pool),
810                                           scratch_pool, scratch_pool));
811    SVN_ERR(svn_io_open_unique_file3(NULL, &moved_abspath, tmpdir_abspath,
812                                     svn_io_file_del_on_close,
813                                     scratch_pool, scratch_pool));
814    SVN_ERR(svn_io_dir_make(moved_abspath, APR_OS_DEFAULT, scratch_pool));
815
816    adm_abspath = svn_wc__adm_child(local_abspath, "", scratch_pool);
817    moved_adm_abspath = svn_wc__adm_child(moved_abspath, "", scratch_pool);
818    SVN_ERR(svn_io_file_move(adm_abspath, moved_adm_abspath, scratch_pool));
819  }
820
821  /* Copy entries from temporary location into the main db */
822  SVN_ERR(svn_wc_copy3(wc_ctx, moved_abspath, local_abspath,
823                       TRUE /* metadata_only */,
824                       NULL, NULL, NULL, NULL, scratch_pool));
825
826  /* Cleanup the temporary admin dir */
827  SVN_ERR(svn_wc__db_drop_root(db, moved_abspath, scratch_pool));
828  SVN_ERR(svn_io_remove_dir2(moved_abspath, FALSE, NULL, NULL,
829                             scratch_pool));
830
831  /* The subdir is now part of our parent working copy. Our caller assumes
832     that we return the new node locked, so obtain a lock if we didn't
833     receive the lock via our depth infinity lock */
834  {
835    svn_boolean_t owns_lock;
836
837    SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
838                                        FALSE, scratch_pool));
839    if (!owns_lock)
840      SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
841                                       scratch_pool));
842  }
843
844  return SVN_NO_ERROR;
845}
846
847
848svn_error_t *
849svn_wc_add4(svn_wc_context_t *wc_ctx,
850            const char *local_abspath,
851            svn_depth_t depth,
852            const char *copyfrom_url,
853            svn_revnum_t copyfrom_rev,
854            svn_cancel_func_t cancel_func,
855            void *cancel_baton,
856            svn_wc_notify_func2_t notify_func,
857            void *notify_baton,
858            apr_pool_t *scratch_pool)
859{
860  svn_wc__db_t *db = wc_ctx->db;
861  svn_node_kind_t kind;
862  svn_boolean_t db_row_exists;
863  svn_boolean_t is_wc_root;
864  const char *repos_root_url;
865  const char *repos_uuid;
866
867  SVN_ERR(check_can_add_node(&kind, &db_row_exists, &is_wc_root,
868                             db, local_abspath, copyfrom_url, copyfrom_rev,
869                             scratch_pool));
870
871  /* Get REPOS_ROOT_URL and REPOS_UUID.  Check that the
872     parent is a versioned directory in an acceptable state. */
873  SVN_ERR(check_can_add_to_parent(&repos_root_url, &repos_uuid,
874                                  db, local_abspath, scratch_pool,
875                                  scratch_pool));
876
877  /* If we're performing a repos-to-WC copy, check that the copyfrom
878     repository is the same as the parent dir's repository. */
879  if (copyfrom_url && !svn_uri__is_ancestor(repos_root_url, copyfrom_url))
880    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
881                             _("The URL '%s' has a different repository "
882                               "root than its parent"), copyfrom_url);
883
884  /* Verify that we can actually integrate the inner working copy */
885  if (is_wc_root)
886    {
887      const char *repos_relpath, *inner_repos_root_url, *inner_repos_uuid;
888      const char *inner_url;
889
890      SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
891                                         &inner_repos_root_url,
892                                         &inner_repos_uuid,
893                                         db, local_abspath,
894                                         scratch_pool, scratch_pool));
895
896      if (strcmp(inner_repos_uuid, repos_uuid)
897          || strcmp(repos_root_url, inner_repos_root_url))
898        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
899                                 _("Can't schedule the working copy at '%s' "
900                                   "from repository '%s' with uuid '%s' "
901                                   "for addition under a working copy from "
902                                   "repository '%s' with uuid '%s'."),
903                                 svn_dirent_local_style(local_abspath,
904                                                        scratch_pool),
905                                 inner_repos_root_url, inner_repos_uuid,
906                                 repos_root_url, repos_uuid);
907
908      inner_url = svn_path_url_add_component2(repos_root_url, repos_relpath,
909                                              scratch_pool);
910
911      if (strcmp(copyfrom_url, inner_url))
912        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
913                                 _("Can't add '%s' with URL '%s', but with "
914                                   "the data from '%s'"),
915                                 svn_dirent_local_style(local_abspath,
916                                                        scratch_pool),
917                                 copyfrom_url, inner_url);
918    }
919
920  if (!copyfrom_url)  /* Case 2a: It's a simple add */
921    {
922      SVN_ERR(add_from_disk(db, local_abspath, kind, NULL,
923                            scratch_pool));
924      if (kind == svn_node_dir && !db_row_exists)
925        {
926          /* If using the legacy 1.6 interface the parent lock may not
927             be recursive and add is expected to lock the new dir.
928
929             ### Perhaps the lock should be created in the same
930             transaction that adds the node? */
931          svn_boolean_t owns_lock;
932
933          SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
934                                              FALSE, scratch_pool));
935          if (!owns_lock)
936            SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
937                                             scratch_pool));
938        }
939    }
940  else if (!is_wc_root)  /* Case 2b: It's a copy from the repository */
941    {
942      if (kind == svn_node_file)
943        {
944          /* This code should never be used, as it doesn't install proper
945             pristine and/or properties. But it was not an error in the old
946             version of this function.
947
948             ===> Use svn_wc_add_repos_file4() directly! */
949          svn_stream_t *content = svn_stream_empty(scratch_pool);
950
951          SVN_ERR(svn_wc_add_repos_file4(wc_ctx, local_abspath,
952                                         content, NULL, NULL, NULL,
953                                         copyfrom_url, copyfrom_rev,
954                                         cancel_func, cancel_baton,
955                                         scratch_pool));
956        }
957      else
958        {
959          const char *repos_relpath =
960            svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
961
962          SVN_ERR(svn_wc__db_op_copy_dir(db, local_abspath,
963                                         apr_hash_make(scratch_pool),
964                                         copyfrom_rev, 0, NULL,
965                                         repos_relpath,
966                                         repos_root_url, repos_uuid,
967                                         copyfrom_rev,
968                                         NULL /* children */, depth,
969                                         FALSE /* is_move */,
970                                         NULL /* conflicts */,
971                                         NULL /* work items */,
972                                         scratch_pool));
973        }
974    }
975  else  /* Case 1: Integrating a separate WC into this one, in place */
976    {
977      SVN_ERR(integrate_nested_wc_as_copy(wc_ctx, local_abspath,
978                                          scratch_pool));
979    }
980
981  /* Report the addition to the caller. */
982  if (notify_func != NULL)
983    {
984      svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
985                                                     svn_wc_notify_add,
986                                                     scratch_pool);
987      notify->kind = kind;
988      (*notify_func)(notify_baton, notify, scratch_pool);
989    }
990
991  return SVN_NO_ERROR;
992}
993
994
995svn_error_t *
996svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx,
997                      const char *local_abspath,
998                      const apr_hash_t *props,
999                      svn_wc_notify_func2_t notify_func,
1000                      void *notify_baton,
1001                      apr_pool_t *scratch_pool)
1002{
1003  svn_node_kind_t kind;
1004
1005  SVN_ERR(check_can_add_node(&kind, NULL, NULL, wc_ctx->db, local_abspath,
1006                             NULL, SVN_INVALID_REVNUM, scratch_pool));
1007  SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx->db, local_abspath,
1008                                  scratch_pool, scratch_pool));
1009
1010  /* Canonicalize and check the props */
1011  if (props)
1012    {
1013      apr_hash_t *new_props;
1014
1015      SVN_ERR(svn_wc__canonicalize_props(
1016                &new_props,
1017                local_abspath, kind, props, FALSE /* skip_some_checks */,
1018                scratch_pool, scratch_pool));
1019      props = new_props;
1020    }
1021
1022  /* Add to the DB and maybe update on-disk executable read-only bits */
1023  SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, props,
1024                        scratch_pool));
1025
1026  /* Report the addition to the caller. */
1027  if (notify_func != NULL)
1028    {
1029      svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
1030                                                     svn_wc_notify_add,
1031                                                     scratch_pool);
1032      notify->kind = kind;
1033      notify->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
1034      (*notify_func)(notify_baton, notify, scratch_pool);
1035    }
1036
1037  return SVN_NO_ERROR;
1038}
1039
1040/* Return a path where nothing exists on disk, within the admin directory
1041   belonging to the WCROOT_ABSPATH directory.  */
1042static const char *
1043nonexistent_path(const char *wcroot_abspath, apr_pool_t *scratch_pool)
1044{
1045  return svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_NONEXISTENT_PATH,
1046                           scratch_pool);
1047}
1048
1049
1050svn_error_t *
1051svn_wc_get_pristine_copy_path(const char *path,
1052                              const char **pristine_path,
1053                              apr_pool_t *pool)
1054{
1055  svn_wc__db_t *db;
1056  const char *local_abspath;
1057  svn_error_t *err;
1058
1059  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
1060
1061  SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, TRUE, pool, pool));
1062  /* DB is now open. This is seemingly a "light" function that a caller
1063     may use repeatedly despite error return values. The rest of this
1064     function should aggressively close DB, even in the error case.  */
1065
1066  err = svn_wc__text_base_path_to_read(pristine_path, db, local_abspath,
1067                                       pool, pool);
1068  if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1069    {
1070      /* The node doesn't exist, so return a non-existent path located
1071         in WCROOT/.svn/  */
1072      const char *wcroot_abspath;
1073
1074      svn_error_clear(err);
1075
1076      err = svn_wc__db_get_wcroot(&wcroot_abspath, db, local_abspath,
1077                                  pool, pool);
1078      if (err == NULL)
1079        *pristine_path = nonexistent_path(wcroot_abspath, pool);
1080    }
1081
1082   return svn_error_compose_create(err, svn_wc__db_close(db));
1083}
1084
1085
1086svn_error_t *
1087svn_wc_get_pristine_contents2(svn_stream_t **contents,
1088                              svn_wc_context_t *wc_ctx,
1089                              const char *local_abspath,
1090                              apr_pool_t *result_pool,
1091                              apr_pool_t *scratch_pool)
1092{
1093  return svn_error_trace(svn_wc__get_pristine_contents(contents, NULL,
1094                                                       wc_ctx->db,
1095                                                       local_abspath,
1096                                                       result_pool,
1097                                                       scratch_pool));
1098}
1099
1100
1101typedef struct get_pristine_lazyopen_baton_t
1102{
1103  svn_wc_context_t *wc_ctx;
1104  const char *wri_abspath;
1105  const svn_checksum_t *checksum;
1106
1107} get_pristine_lazyopen_baton_t;
1108
1109
1110/* Implements svn_stream_lazyopen_func_t */
1111static svn_error_t *
1112get_pristine_lazyopen_func(svn_stream_t **stream,
1113                           void *baton,
1114                           apr_pool_t *result_pool,
1115                           apr_pool_t *scratch_pool)
1116{
1117  get_pristine_lazyopen_baton_t *b = baton;
1118  const svn_checksum_t *sha1_checksum;
1119
1120  /* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5,
1121     we'll use it to lookup the SHA1. */
1122  if (b->checksum->kind == svn_checksum_sha1)
1123    sha1_checksum = b->checksum;
1124  else
1125    SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, b->wc_ctx->db,
1126                                         b->wri_abspath, b->checksum,
1127                                         scratch_pool, scratch_pool));
1128
1129  SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db,
1130                                   b->wri_abspath, sha1_checksum,
1131                                   result_pool, scratch_pool));
1132  return SVN_NO_ERROR;
1133}
1134
1135svn_error_t *
1136svn_wc__get_pristine_contents_by_checksum(svn_stream_t **contents,
1137                                          svn_wc_context_t *wc_ctx,
1138                                          const char *wri_abspath,
1139                                          const svn_checksum_t *checksum,
1140                                          apr_pool_t *result_pool,
1141                                          apr_pool_t *scratch_pool)
1142{
1143  svn_boolean_t present;
1144
1145  *contents = NULL;
1146
1147  SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath,
1148                                    checksum, scratch_pool));
1149
1150  if (present)
1151    {
1152      get_pristine_lazyopen_baton_t *gpl_baton;
1153
1154      gpl_baton = apr_pcalloc(result_pool, sizeof(*gpl_baton));
1155      gpl_baton->wc_ctx = wc_ctx;
1156      gpl_baton->wri_abspath = wri_abspath;
1157      gpl_baton->checksum = checksum;
1158
1159      *contents = svn_stream_lazyopen_create(get_pristine_lazyopen_func,
1160                                             gpl_baton, FALSE, result_pool);
1161    }
1162
1163  return SVN_NO_ERROR;
1164}
1165
1166
1167
1168svn_error_t *
1169svn_wc_add_lock2(svn_wc_context_t *wc_ctx,
1170                 const char *local_abspath,
1171                 const svn_lock_t *lock,
1172                 apr_pool_t *scratch_pool)
1173{
1174  svn_wc__db_lock_t db_lock;
1175  svn_error_t *err;
1176  const svn_string_t *needs_lock;
1177
1178  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1179
1180  /* ### Enable after fixing callers */
1181  /*SVN_ERR(svn_wc__write_check(wc_ctx->db,
1182                              svn_dirent_dirname(local_abspath, scratch_pool),
1183                              scratch_pool));*/
1184
1185  db_lock.token = lock->token;
1186  db_lock.owner = lock->owner;
1187  db_lock.comment = lock->comment;
1188  db_lock.date = lock->creation_date;
1189  err = svn_wc__db_lock_add(wc_ctx->db, local_abspath, &db_lock, scratch_pool);
1190  if (err)
1191    {
1192      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1193        return svn_error_trace(err);
1194
1195      /* Remap the error.  */
1196      svn_error_clear(err);
1197      return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
1198                               _("'%s' is not under version control"),
1199                               svn_dirent_local_style(local_abspath,
1200                                                      scratch_pool));
1201    }
1202
1203  /* if svn:needs-lock is present, then make the file read-write. */
1204  err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
1205                                 SVN_PROP_NEEDS_LOCK, scratch_pool,
1206                                 scratch_pool);
1207
1208  if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1209    {
1210      /* The node has non wc representation (e.g. deleted), so
1211         we don't want to touch the in-wc file */
1212      svn_error_clear(err);
1213      return SVN_NO_ERROR;
1214    }
1215  SVN_ERR(err);
1216
1217  if (needs_lock)
1218    SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool));
1219
1220  return SVN_NO_ERROR;
1221}
1222
1223
1224svn_error_t *
1225svn_wc_remove_lock2(svn_wc_context_t *wc_ctx,
1226                    const char *local_abspath,
1227                    apr_pool_t *scratch_pool)
1228{
1229  svn_error_t *err;
1230  const svn_string_t *needs_lock;
1231
1232  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1233
1234  /* ### Enable after fixing callers */
1235  /*SVN_ERR(svn_wc__write_check(wc_ctx->db,
1236                              svn_dirent_dirname(local_abspath, scratch_pool),
1237                              scratch_pool));*/
1238
1239  err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, scratch_pool);
1240  if (err)
1241    {
1242      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1243        return svn_error_trace(err);
1244
1245      /* Remap the error.  */
1246      svn_error_clear(err);
1247      return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
1248                               _("'%s' is not under version control"),
1249                               svn_dirent_local_style(local_abspath,
1250                                                      scratch_pool));
1251    }
1252
1253  /* if svn:needs-lock is present, then make the file read-only. */
1254  err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
1255                                 SVN_PROP_NEEDS_LOCK, scratch_pool,
1256                                 scratch_pool);
1257  if (err)
1258    {
1259      if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1260        return svn_error_trace(err);
1261
1262      svn_error_clear(err);
1263      return SVN_NO_ERROR; /* Node is shadowed and/or deleted,
1264                              so we shouldn't apply its lock */
1265    }
1266
1267  if (needs_lock)
1268    SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
1269
1270  return SVN_NO_ERROR;
1271}
1272
1273
1274svn_error_t *
1275svn_wc_set_changelist2(svn_wc_context_t *wc_ctx,
1276                       const char *local_abspath,
1277                       const char *new_changelist,
1278                       svn_depth_t depth,
1279                       const apr_array_header_t *changelist_filter,
1280                       svn_cancel_func_t cancel_func,
1281                       void *cancel_baton,
1282                       svn_wc_notify_func2_t notify_func,
1283                       void *notify_baton,
1284                       apr_pool_t *scratch_pool)
1285{
1286  /* Assert that we aren't being asked to set an empty changelist. */
1287  SVN_ERR_ASSERT(! (new_changelist && new_changelist[0] == '\0'));
1288
1289  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1290
1291  SVN_ERR(svn_wc__db_op_set_changelist(wc_ctx->db, local_abspath,
1292                                       new_changelist, changelist_filter,
1293                                       depth, notify_func, notify_baton,
1294                                       cancel_func, cancel_baton,
1295                                       scratch_pool));
1296
1297  return SVN_NO_ERROR;
1298}
1299
1300struct get_cl_fn_baton
1301{
1302  svn_wc__db_t *db;
1303  apr_hash_t *clhash;
1304  svn_changelist_receiver_t callback_func;
1305  void *callback_baton;
1306};
1307
1308
1309static svn_error_t *
1310get_node_changelist(const char *local_abspath,
1311                    svn_node_kind_t kind,
1312                    void *baton,
1313                    apr_pool_t *scratch_pool)
1314{
1315  struct get_cl_fn_baton *b = baton;
1316  const char *changelist;
1317
1318  SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1319                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1320                               NULL, NULL, NULL, NULL, NULL, &changelist,
1321                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1322                               b->db, local_abspath,
1323                               scratch_pool, scratch_pool));
1324
1325  if (svn_wc__internal_changelist_match(b->db, local_abspath, b->clhash,
1326                                        scratch_pool))
1327    SVN_ERR(b->callback_func(b->callback_baton, local_abspath,
1328                             changelist, scratch_pool));
1329
1330  return SVN_NO_ERROR;
1331}
1332
1333
1334svn_error_t *
1335svn_wc_get_changelists(svn_wc_context_t *wc_ctx,
1336                       const char *local_abspath,
1337                       svn_depth_t depth,
1338                       const apr_array_header_t *changelist_filter,
1339                       svn_changelist_receiver_t callback_func,
1340                       void *callback_baton,
1341                       svn_cancel_func_t cancel_func,
1342                       void *cancel_baton,
1343                       apr_pool_t *scratch_pool)
1344{
1345  struct get_cl_fn_baton gnb;
1346
1347  gnb.db = wc_ctx->db;
1348  gnb.clhash = NULL;
1349  gnb.callback_func = callback_func;
1350  gnb.callback_baton = callback_baton;
1351
1352  if (changelist_filter)
1353    SVN_ERR(svn_hash_from_cstring_keys(&gnb.clhash, changelist_filter,
1354                                       scratch_pool));
1355
1356  return svn_error_trace(
1357    svn_wc__internal_walk_children(wc_ctx->db, local_abspath, FALSE,
1358                                   changelist_filter, get_node_changelist,
1359                                   &gnb, depth,
1360                                   cancel_func, cancel_baton,
1361                                   scratch_pool));
1362
1363}
1364
1365
1366svn_boolean_t
1367svn_wc__internal_changelist_match(svn_wc__db_t *db,
1368                                  const char *local_abspath,
1369                                  const apr_hash_t *clhash,
1370                                  apr_pool_t *scratch_pool)
1371{
1372  svn_error_t *err;
1373  const char *changelist;
1374
1375  if (clhash == NULL)
1376    return TRUE;
1377
1378  err = svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1379                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1380                             NULL, NULL, NULL, NULL, NULL, &changelist,
1381                             NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1382                             db, local_abspath, scratch_pool, scratch_pool);
1383  if (err)
1384    {
1385      svn_error_clear(err);
1386      return FALSE;
1387    }
1388
1389  return (changelist
1390            && svn_hash_gets((apr_hash_t *)clhash, changelist) != NULL);
1391}
1392
1393
1394svn_boolean_t
1395svn_wc__changelist_match(svn_wc_context_t *wc_ctx,
1396                         const char *local_abspath,
1397                         const apr_hash_t *clhash,
1398                         apr_pool_t *scratch_pool)
1399{
1400  return svn_wc__internal_changelist_match(wc_ctx->db, local_abspath, clhash,
1401                                           scratch_pool);
1402}
1403