]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_wc/adm_ops.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_wc / adm_ops.c
1 /*
2  * adm_ops.c: routines for affecting working copy administrative
3  *            information.  NOTE: this code doesn't know where the adm
4  *            info is actually stored.  Instead, generic handles to
5  *            adm data are requested via a reference to some PATH
6  *            (PATH being a regular, non-administrative directory or
7  *            file in the working copy).
8  *
9  * ====================================================================
10  *    Licensed to the Apache Software Foundation (ASF) under one
11  *    or more contributor license agreements.  See the NOTICE file
12  *    distributed with this work for additional information
13  *    regarding copyright ownership.  The ASF licenses this file
14  *    to you under the Apache License, Version 2.0 (the
15  *    "License"); you may not use this file except in compliance
16  *    with the License.  You may obtain a copy of the License at
17  *
18  *      http://www.apache.org/licenses/LICENSE-2.0
19  *
20  *    Unless required by applicable law or agreed to in writing,
21  *    software distributed under the License is distributed on an
22  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
23  *    KIND, either express or implied.  See the License for the
24  *    specific language governing permissions and limitations
25  *    under the License.
26  * ====================================================================
27  */
28
29
30 \f
31 #include <string.h>
32 #include <stdlib.h>
33
34 #include <apr_pools.h>
35 #include <apr_hash.h>
36 #include <apr_time.h>
37 #include <apr_errno.h>
38
39 #include "svn_types.h"
40 #include "svn_pools.h"
41 #include "svn_string.h"
42 #include "svn_error.h"
43 #include "svn_dirent_uri.h"
44 #include "svn_path.h"
45 #include "svn_hash.h"
46 #include "svn_wc.h"
47 #include "svn_io.h"
48 #include "svn_time.h"
49 #include "svn_sorts.h"
50
51 #include "wc.h"
52 #include "adm_files.h"
53 #include "conflicts.h"
54 #include "workqueue.h"
55
56 #include "svn_private_config.h"
57 #include "private/svn_subr_private.h"
58 #include "private/svn_dep_compat.h"
59
60
61 struct svn_wc_committed_queue_t
62 {
63   /* The pool in which ->queue is allocated. */
64   apr_pool_t *pool;
65   /* Mapping (const char *) local_abspath to (committed_queue_item_t *). */
66   apr_hash_t *queue;
67   /* Is any item in the queue marked as 'recursive'? */
68   svn_boolean_t have_recursive;
69 };
70
71 typedef struct committed_queue_item_t
72 {
73   const char *local_abspath;
74   svn_boolean_t recurse;
75   svn_boolean_t no_unlock;
76   svn_boolean_t keep_changelist;
77
78   /* The pristine text checksum. */
79   const svn_checksum_t *sha1_checksum;
80
81   apr_hash_t *new_dav_cache;
82 } committed_queue_item_t;
83
84
85 apr_pool_t *
86 svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue)
87 {
88   return queue->pool;
89 }
90
91
92 \f
93 /*** Finishing updates and commits. ***/
94
95 /* Queue work items that will finish a commit of the file or directory
96  * LOCAL_ABSPATH in DB:
97  *   - queue the removal of any "revert-base" props and text files;
98  *   - queue an update of the DB entry for this node
99  *
100  * ### The Pristine Store equivalent should be:
101  *   - remember the old BASE_NODE and WORKING_NODE pristine text c'sums;
102  *   - queue an update of the DB entry for this node (incl. updating the
103  *       BASE_NODE c'sum and setting the WORKING_NODE c'sum to NULL);
104  *   - queue deletion of the old pristine texts by the remembered checksums.
105  *
106  * CHECKSUM is the checksum of the new text base for LOCAL_ABSPATH, and must
107  * be provided if there is one, else NULL.
108  *
109  * STATUS, KIND, PROP_MODS and OLD_CHECKSUM are the current in-db values of
110  * the node LOCAL_ABSPATH.
111  */
112 static svn_error_t *
113 process_committed_leaf(svn_wc__db_t *db,
114                        const char *local_abspath,
115                        svn_boolean_t via_recurse,
116                        svn_wc__db_status_t status,
117                        svn_node_kind_t kind,
118                        svn_boolean_t prop_mods,
119                        const svn_checksum_t *old_checksum,
120                        svn_revnum_t new_revnum,
121                        apr_time_t new_changed_date,
122                        const char *new_changed_author,
123                        apr_hash_t *new_dav_cache,
124                        svn_boolean_t no_unlock,
125                        svn_boolean_t keep_changelist,
126                        const svn_checksum_t *checksum,
127                        apr_pool_t *scratch_pool)
128 {
129   svn_revnum_t new_changed_rev = new_revnum;
130   svn_skel_t *work_item = NULL;
131
132   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
133
134   {
135     const char *adm_abspath;
136
137     if (kind == svn_node_dir)
138       adm_abspath = local_abspath;
139     else
140       adm_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
141     SVN_ERR(svn_wc__write_check(db, adm_abspath, scratch_pool));
142   }
143
144   if (status == svn_wc__db_status_deleted)
145     {
146       return svn_error_trace(
147                 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
230 svn_error_t *
231 svn_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
330 apr_hash_t *
331 svn_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
353 svn_wc_committed_queue_t *
354 svn_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
367 svn_error_t *
368 svn_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 */
409 static svn_boolean_t
410 have_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
433 svn_error_t *
434 svn_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  */
537 static svn_error_t *
538 add_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. */
573 static svn_error_t *
574 check_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  */
653 static svn_error_t *
654 check_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. */
790 static svn_error_t *
791 integrate_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
848 svn_error_t *
849 svn_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 */, FALSE, depth,
969                                          NULL /* conflicts */,
970                                          NULL /* work items */,
971                                          scratch_pool));
972         }
973     }
974   else  /* Case 1: Integrating a separate WC into this one, in place */
975     {
976       SVN_ERR(integrate_nested_wc_as_copy(wc_ctx, local_abspath,
977                                           scratch_pool));
978     }
979
980   /* Report the addition to the caller. */
981   if (notify_func != NULL)
982     {
983       svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
984                                                      svn_wc_notify_add,
985                                                      scratch_pool);
986       notify->kind = kind;
987       (*notify_func)(notify_baton, notify, scratch_pool);
988     }
989
990   return SVN_NO_ERROR;
991 }
992
993
994 svn_error_t *
995 svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx,
996                       const char *local_abspath,
997                       const apr_hash_t *props,
998                       svn_wc_notify_func2_t notify_func,
999                       void *notify_baton,
1000                       apr_pool_t *scratch_pool)
1001 {
1002   svn_node_kind_t kind;
1003
1004   SVN_ERR(check_can_add_node(&kind, NULL, NULL, wc_ctx->db, local_abspath,
1005                              NULL, SVN_INVALID_REVNUM, scratch_pool));
1006   SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx->db, local_abspath,
1007                                   scratch_pool, scratch_pool));
1008
1009   /* Canonicalize and check the props */
1010   if (props)
1011     {
1012       apr_hash_t *new_props;
1013
1014       SVN_ERR(svn_wc__canonicalize_props(
1015                 &new_props,
1016                 local_abspath, kind, props, FALSE /* skip_some_checks */,
1017                 scratch_pool, scratch_pool));
1018       props = new_props;
1019     }
1020
1021   /* Add to the DB and maybe update on-disk executable read-only bits */
1022   SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, props,
1023                         scratch_pool));
1024
1025   /* Report the addition to the caller. */
1026   if (notify_func != NULL)
1027     {
1028       svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
1029                                                      svn_wc_notify_add,
1030                                                      scratch_pool);
1031       notify->kind = kind;
1032       notify->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
1033       (*notify_func)(notify_baton, notify, scratch_pool);
1034     }
1035
1036   return SVN_NO_ERROR;
1037 }
1038 \f
1039 /* Return a path where nothing exists on disk, within the admin directory
1040    belonging to the WCROOT_ABSPATH directory.  */
1041 static const char *
1042 nonexistent_path(const char *wcroot_abspath, apr_pool_t *scratch_pool)
1043 {
1044   return svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_NONEXISTENT_PATH,
1045                            scratch_pool);
1046 }
1047
1048
1049 svn_error_t *
1050 svn_wc_get_pristine_copy_path(const char *path,
1051                               const char **pristine_path,
1052                               apr_pool_t *pool)
1053 {
1054   svn_wc__db_t *db;
1055   const char *local_abspath;
1056   svn_error_t *err;
1057
1058   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
1059
1060   SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, TRUE, pool, pool));
1061   /* DB is now open. This is seemingly a "light" function that a caller
1062      may use repeatedly despite error return values. The rest of this
1063      function should aggressively close DB, even in the error case.  */
1064
1065   err = svn_wc__text_base_path_to_read(pristine_path, db, local_abspath,
1066                                        pool, pool);
1067   if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1068     {
1069       /* The node doesn't exist, so return a non-existent path located
1070          in WCROOT/.svn/  */
1071       const char *wcroot_abspath;
1072
1073       svn_error_clear(err);
1074
1075       err = svn_wc__db_get_wcroot(&wcroot_abspath, db, local_abspath,
1076                                   pool, pool);
1077       if (err == NULL)
1078         *pristine_path = nonexistent_path(wcroot_abspath, pool);
1079     }
1080
1081    return svn_error_compose_create(err, svn_wc__db_close(db));
1082 }
1083
1084
1085 svn_error_t *
1086 svn_wc_get_pristine_contents2(svn_stream_t **contents,
1087                               svn_wc_context_t *wc_ctx,
1088                               const char *local_abspath,
1089                               apr_pool_t *result_pool,
1090                               apr_pool_t *scratch_pool)
1091 {
1092   return svn_error_trace(svn_wc__get_pristine_contents(contents, NULL,
1093                                                        wc_ctx->db,
1094                                                        local_abspath,
1095                                                        result_pool,
1096                                                        scratch_pool));
1097 }
1098
1099
1100 typedef struct get_pristine_lazyopen_baton_t
1101 {
1102   svn_wc_context_t *wc_ctx;
1103   const char *wri_abspath;
1104   const svn_checksum_t *checksum;
1105
1106 } get_pristine_lazyopen_baton_t;
1107
1108
1109 /* Implements svn_stream_lazyopen_func_t */
1110 static svn_error_t *
1111 get_pristine_lazyopen_func(svn_stream_t **stream,
1112                            void *baton,
1113                            apr_pool_t *result_pool,
1114                            apr_pool_t *scratch_pool)
1115 {
1116   get_pristine_lazyopen_baton_t *b = baton;
1117   const svn_checksum_t *sha1_checksum;
1118
1119   /* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5,
1120      we'll use it to lookup the SHA1. */
1121   if (b->checksum->kind == svn_checksum_sha1)
1122     sha1_checksum = b->checksum;
1123   else
1124     SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, b->wc_ctx->db,
1125                                          b->wri_abspath, b->checksum,
1126                                          scratch_pool, scratch_pool));
1127
1128   SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db,
1129                                    b->wri_abspath, sha1_checksum,
1130                                    result_pool, scratch_pool));
1131   return SVN_NO_ERROR;
1132 }
1133
1134 svn_error_t *
1135 svn_wc__get_pristine_contents_by_checksum(svn_stream_t **contents,
1136                                           svn_wc_context_t *wc_ctx,
1137                                           const char *wri_abspath,
1138                                           const svn_checksum_t *checksum,
1139                                           apr_pool_t *result_pool,
1140                                           apr_pool_t *scratch_pool)
1141 {
1142   svn_boolean_t present;
1143
1144   *contents = NULL;
1145
1146   SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath,
1147                                     checksum, scratch_pool));
1148
1149   if (present)
1150     {
1151       get_pristine_lazyopen_baton_t *gpl_baton;
1152
1153       gpl_baton = apr_pcalloc(result_pool, sizeof(*gpl_baton));
1154       gpl_baton->wc_ctx = wc_ctx;
1155       gpl_baton->wri_abspath = wri_abspath;
1156       gpl_baton->checksum = checksum;
1157
1158       *contents = svn_stream_lazyopen_create(get_pristine_lazyopen_func,
1159                                              gpl_baton, FALSE, result_pool);
1160     }
1161
1162   return SVN_NO_ERROR;
1163 }
1164
1165
1166
1167 svn_error_t *
1168 svn_wc_add_lock2(svn_wc_context_t *wc_ctx,
1169                  const char *local_abspath,
1170                  const svn_lock_t *lock,
1171                  apr_pool_t *scratch_pool)
1172 {
1173   svn_wc__db_lock_t db_lock;
1174   svn_error_t *err;
1175   const svn_string_t *needs_lock;
1176
1177   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1178
1179   /* ### Enable after fixing callers */
1180   /*SVN_ERR(svn_wc__write_check(wc_ctx->db,
1181                               svn_dirent_dirname(local_abspath, scratch_pool),
1182                               scratch_pool));*/
1183
1184   db_lock.token = lock->token;
1185   db_lock.owner = lock->owner;
1186   db_lock.comment = lock->comment;
1187   db_lock.date = lock->creation_date;
1188   err = svn_wc__db_lock_add(wc_ctx->db, local_abspath, &db_lock, scratch_pool);
1189   if (err)
1190     {
1191       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1192         return svn_error_trace(err);
1193
1194       /* Remap the error.  */
1195       svn_error_clear(err);
1196       return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
1197                                _("'%s' is not under version control"),
1198                                svn_dirent_local_style(local_abspath,
1199                                                       scratch_pool));
1200     }
1201
1202   /* if svn:needs-lock is present, then make the file read-write. */
1203   err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
1204                                  SVN_PROP_NEEDS_LOCK, scratch_pool,
1205                                  scratch_pool);
1206
1207   if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1208     {
1209       /* The node has non wc representation (e.g. deleted), so
1210          we don't want to touch the in-wc file */
1211       svn_error_clear(err);
1212       return SVN_NO_ERROR;
1213     }
1214   SVN_ERR(err);
1215
1216   if (needs_lock)
1217     SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool));
1218
1219   return SVN_NO_ERROR;
1220 }
1221
1222
1223 svn_error_t *
1224 svn_wc_remove_lock2(svn_wc_context_t *wc_ctx,
1225                     const char *local_abspath,
1226                     apr_pool_t *scratch_pool)
1227 {
1228   svn_error_t *err;
1229   const svn_string_t *needs_lock;
1230
1231   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1232
1233   /* ### Enable after fixing callers */
1234   /*SVN_ERR(svn_wc__write_check(wc_ctx->db,
1235                               svn_dirent_dirname(local_abspath, scratch_pool),
1236                               scratch_pool));*/
1237
1238   err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, scratch_pool);
1239   if (err)
1240     {
1241       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1242         return svn_error_trace(err);
1243
1244       /* Remap the error.  */
1245       svn_error_clear(err);
1246       return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
1247                                _("'%s' is not under version control"),
1248                                svn_dirent_local_style(local_abspath,
1249                                                       scratch_pool));
1250     }
1251
1252   /* if svn:needs-lock is present, then make the file read-only. */
1253   err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
1254                                  SVN_PROP_NEEDS_LOCK, scratch_pool,
1255                                  scratch_pool);
1256   if (err)
1257     {
1258       if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1259         return svn_error_trace(err);
1260
1261       svn_error_clear(err);
1262       return SVN_NO_ERROR; /* Node is shadowed and/or deleted,
1263                               so we shouldn't apply its lock */
1264     }
1265
1266   if (needs_lock)
1267     SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
1268
1269   return SVN_NO_ERROR;
1270 }
1271
1272
1273 svn_error_t *
1274 svn_wc_set_changelist2(svn_wc_context_t *wc_ctx,
1275                        const char *local_abspath,
1276                        const char *new_changelist,
1277                        svn_depth_t depth,
1278                        const apr_array_header_t *changelist_filter,
1279                        svn_cancel_func_t cancel_func,
1280                        void *cancel_baton,
1281                        svn_wc_notify_func2_t notify_func,
1282                        void *notify_baton,
1283                        apr_pool_t *scratch_pool)
1284 {
1285   /* Assert that we aren't being asked to set an empty changelist. */
1286   SVN_ERR_ASSERT(! (new_changelist && new_changelist[0] == '\0'));
1287
1288   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1289
1290   SVN_ERR(svn_wc__db_op_set_changelist(wc_ctx->db, local_abspath,
1291                                        new_changelist, changelist_filter,
1292                                        depth, notify_func, notify_baton,
1293                                        cancel_func, cancel_baton,
1294                                        scratch_pool));
1295
1296   return SVN_NO_ERROR;
1297 }
1298
1299 struct get_cl_fn_baton
1300 {
1301   svn_wc__db_t *db;
1302   apr_hash_t *clhash;
1303   svn_changelist_receiver_t callback_func;
1304   void *callback_baton;
1305 };
1306
1307
1308 static svn_error_t *
1309 get_node_changelist(const char *local_abspath,
1310                     svn_node_kind_t kind,
1311                     void *baton,
1312                     apr_pool_t *scratch_pool)
1313 {
1314   struct get_cl_fn_baton *b = baton;
1315   const char *changelist;
1316
1317   SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1318                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1319                                NULL, NULL, NULL, NULL, NULL, &changelist,
1320                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1321                                b->db, local_abspath,
1322                                scratch_pool, scratch_pool));
1323
1324   if (svn_wc__internal_changelist_match(b->db, local_abspath, b->clhash,
1325                                         scratch_pool))
1326     SVN_ERR(b->callback_func(b->callback_baton, local_abspath,
1327                              changelist, scratch_pool));
1328
1329   return SVN_NO_ERROR;
1330 }
1331
1332
1333 svn_error_t *
1334 svn_wc_get_changelists(svn_wc_context_t *wc_ctx,
1335                        const char *local_abspath,
1336                        svn_depth_t depth,
1337                        const apr_array_header_t *changelist_filter,
1338                        svn_changelist_receiver_t callback_func,
1339                        void *callback_baton,
1340                        svn_cancel_func_t cancel_func,
1341                        void *cancel_baton,
1342                        apr_pool_t *scratch_pool)
1343 {
1344   struct get_cl_fn_baton gnb;
1345
1346   gnb.db = wc_ctx->db;
1347   gnb.clhash = NULL;
1348   gnb.callback_func = callback_func;
1349   gnb.callback_baton = callback_baton;
1350
1351   if (changelist_filter)
1352     SVN_ERR(svn_hash_from_cstring_keys(&gnb.clhash, changelist_filter,
1353                                        scratch_pool));
1354
1355   return svn_error_trace(
1356     svn_wc__internal_walk_children(wc_ctx->db, local_abspath, FALSE,
1357                                    changelist_filter, get_node_changelist,
1358                                    &gnb, depth,
1359                                    cancel_func, cancel_baton,
1360                                    scratch_pool));
1361
1362 }
1363
1364
1365 svn_boolean_t
1366 svn_wc__internal_changelist_match(svn_wc__db_t *db,
1367                                   const char *local_abspath,
1368                                   const apr_hash_t *clhash,
1369                                   apr_pool_t *scratch_pool)
1370 {
1371   svn_error_t *err;
1372   const char *changelist;
1373
1374   if (clhash == NULL)
1375     return TRUE;
1376
1377   err = svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1378                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1379                              NULL, NULL, NULL, NULL, NULL, &changelist,
1380                              NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1381                              db, local_abspath, scratch_pool, scratch_pool);
1382   if (err)
1383     {
1384       svn_error_clear(err);
1385       return FALSE;
1386     }
1387
1388   return (changelist
1389             && svn_hash_gets((apr_hash_t *)clhash, changelist) != NULL);
1390 }
1391
1392
1393 svn_boolean_t
1394 svn_wc__changelist_match(svn_wc_context_t *wc_ctx,
1395                          const char *local_abspath,
1396                          const apr_hash_t *clhash,
1397                          apr_pool_t *scratch_pool)
1398 {
1399   return svn_wc__internal_changelist_match(wc_ctx->db, local_abspath, clhash,
1400                                            scratch_pool);
1401 }