adm_ops.c revision 289166
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 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 61struct 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 71typedef 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 85apr_pool_t * 86svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue) 87{ 88 return queue->pool; 89} 90 91 92 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 */ 112static svn_error_t * 113process_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 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