wc_db_update_move.c revision 299742
1/* 2 * wc_db_update_move.c : updating moves during tree-conflict resolution 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* This file implements an editor and an edit driver which are used 25 * to resolve an "incoming edit, local move-away" tree conflict resulting 26 * from an update (or switch). 27 * 28 * Our goal is to be able to resolve this conflict such that the end 29 * result is just the same as if the user had run the update *before* 30 * the local move. 31 * 32 * When an update (or switch) produces incoming changes for a locally 33 * moved-away subtree, it updates the base nodes of the moved-away tree 34 * and flags a tree-conflict on the moved-away root node. 35 * This editor transfers these changes from the moved-away part of the 36 * working copy to the corresponding moved-here part of the working copy. 37 * 38 * Both the driver and receiver components of the editor are implemented 39 * in this file. 40 * 41 * The driver sees two NODES trees: the move source tree and the move 42 * destination tree. When the move is initially made these trees are 43 * equivalent, the destination is a copy of the source. The source is 44 * a single-op-depth, single-revision, deleted layer [1] and the 45 * destination has an equivalent single-op-depth, single-revision 46 * layer. The destination may have additional higher op-depths 47 * representing adds, deletes, moves within the move destination. [2] 48 * 49 * After the initial move an update has modified the NODES in the move 50 * source and may have introduced a tree-conflict since the source and 51 * destination trees are no longer equivalent. The source is a 52 * different revision and may have text, property and tree changes 53 * compared to the destination. The driver will compare the two NODES 54 * trees and drive an editor to change the destination tree so that it 55 * once again matches the source tree. Changes made to the 56 * destination NODES tree to achieve this match will be merged into 57 * the working files/directories. 58 * 59 * The whole drive occurs as one single wc.db transaction. At the end 60 * of the transaction the destination NODES table should have a layer 61 * that is equivalent to the source NODES layer, there should be 62 * workqueue items to make any required changes to working 63 * files/directories in the move destination, and there should be 64 * tree-conflicts in the move destination where it was not possible to 65 * update the working files/directories. 66 * 67 * [1] The move source tree is single-revision because we currently do 68 * not allow a mixed-rev move, and therefore it is single op-depth 69 * regardless whether it is a base layer or a nested move. 70 * 71 * [2] The source tree also may have additional higher op-depths, 72 * representing a replacement, but this editor only reads from the 73 * single-op-depth layer of it, and makes no changes of any kind 74 * within the source tree. 75 */ 76 77#define SVN_WC__I_AM_WC_DB 78 79#include <assert.h> 80 81#include "svn_checksum.h" 82#include "svn_dirent_uri.h" 83#include "svn_error.h" 84#include "svn_hash.h" 85#include "svn_wc.h" 86#include "svn_props.h" 87#include "svn_pools.h" 88#include "svn_sorts.h" 89 90#include "private/svn_skel.h" 91#include "private/svn_sorts_private.h" 92#include "private/svn_sqlite.h" 93#include "private/svn_wc_private.h" 94 95#include "wc.h" 96#include "props.h" 97#include "wc_db_private.h" 98#include "wc-queries.h" 99#include "conflicts.h" 100#include "workqueue.h" 101#include "token-map.h" 102 103/* Helper functions */ 104/* Return the absolute path, in local path style, of LOCAL_RELPATH 105 in WCROOT. */ 106static const char * 107path_for_error_message(const svn_wc__db_wcroot_t *wcroot, 108 const char *local_relpath, 109 apr_pool_t *result_pool) 110{ 111 const char *local_abspath 112 = svn_dirent_join(wcroot->abspath, local_relpath, result_pool); 113 114 return svn_dirent_local_style(local_abspath, result_pool); 115} 116 117/* Ensure that there is a working copy lock for LOCAL_RELPATH in WCROOT */ 118static svn_error_t * 119verify_write_lock(svn_wc__db_wcroot_t *wcroot, 120 const char *local_relpath, 121 apr_pool_t *scratch_pool) 122{ 123 svn_boolean_t locked; 124 125 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, local_relpath, 126 FALSE, scratch_pool)); 127 if (!locked) 128 { 129 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL, 130 _("No write-lock in '%s'"), 131 path_for_error_message(wcroot, local_relpath, 132 scratch_pool)); 133 } 134 135 return SVN_NO_ERROR; 136} 137 138/* In our merge conflicts we record the move_op_src path, which is essentially 139 the depth at which what was moved is marked deleted. The problem is that 140 this depth is not guaranteed to be stable, because somebody might just 141 remove another ancestor, or revert one. 142 143 To work around this problem we locate the layer below this path, and use 144 that to pinpoint whatever is moved. 145 146 For a path SRC_RELPATH that was deleted by an operation rooted at 147 DELETE_OP_DEPTH find the op-depth at which the node was originally added. 148 */ 149static svn_error_t * 150find_src_op_depth(int *src_op_depth, 151 svn_wc__db_wcroot_t *wcroot, 152 const char *src_relpath, 153 int delete_op_depth, 154 apr_pool_t *scratch_pool) 155{ 156 svn_sqlite__stmt_t *stmt; 157 svn_boolean_t have_row; 158 159 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 160 STMT_SELECT_HIGHEST_WORKING_NODE)); 161 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, 162 src_relpath, delete_op_depth)); 163 164 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 165 if (have_row) 166 *src_op_depth = svn_sqlite__column_int(stmt, 0); 167 SVN_ERR(svn_sqlite__reset(stmt)); 168 if (!have_row) 169 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 170 _("'%s' is not deleted"), 171 path_for_error_message(wcroot, src_relpath, 172 scratch_pool)); 173 174 return SVN_NO_ERROR; 175} 176 177/* 178 * Receiver code. 179 * 180 * The receiver is an editor that, when driven with a certain change, will 181 * merge the edits into the working/actual state of the move destination 182 * at MOVE_ROOT_DST_RELPATH (in struct tc_editor_baton), perhaps raising 183 * conflicts if necessary. 184 * 185 * The receiver should not need to refer directly to the move source, as 186 * the driver should provide all relevant information about the change to 187 * be made at the move destination. 188 */ 189 190typedef struct update_move_baton_t { 191 svn_wc__db_t *db; 192 svn_wc__db_wcroot_t *wcroot; 193 194 int src_op_depth; 195 int dst_op_depth; 196 197 svn_wc_operation_t operation; 198 svn_wc_conflict_version_t *old_version; 199 svn_wc_conflict_version_t *new_version; 200 201 svn_cancel_func_t cancel_func; 202 void *cancel_baton; 203} update_move_baton_t; 204 205/* Per node flags for tree conflict collection */ 206typedef struct node_move_baton_t 207{ 208 svn_boolean_t skip; 209 svn_boolean_t shadowed; 210 svn_boolean_t edited; 211 212 const char *src_relpath; 213 const char *dst_relpath; 214 215 update_move_baton_t *umb; 216 struct node_move_baton_t *pb; 217} node_move_baton_t; 218 219/* 220 * Notifications are delayed until the entire update-move transaction 221 * completes. These functions provide the necessary support by storing 222 * notification information in a temporary db table (the "update_move_list") 223 * and spooling notifications out of that table after the transaction. 224 */ 225 226/* Add an entry to the notification list, and at the same time install 227 a conflict and/or work items. */ 228static svn_error_t * 229update_move_list_add(svn_wc__db_wcroot_t *wcroot, 230 const char *local_relpath, 231 svn_wc__db_t *db, 232 svn_wc_notify_action_t action, 233 svn_node_kind_t kind, 234 svn_wc_notify_state_t content_state, 235 svn_wc_notify_state_t prop_state, 236 svn_skel_t *conflict, 237 svn_skel_t *work_item, 238 apr_pool_t *scratch_pool) 239{ 240 svn_sqlite__stmt_t *stmt; 241 242 if (conflict) 243 { 244 svn_boolean_t tree_conflict; 245 246 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL, 247 &tree_conflict, 248 db, wcroot->abspath, conflict, 249 scratch_pool, scratch_pool)); 250 if (tree_conflict) 251 { 252 action = svn_wc_notify_tree_conflict; 253 content_state = svn_wc_notify_state_inapplicable; 254 prop_state = svn_wc_notify_state_inapplicable; 255 } 256 } 257 258 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 259 STMT_INSERT_UPDATE_MOVE_LIST)); 260 SVN_ERR(svn_sqlite__bindf(stmt, "sdtdd", local_relpath, 261 action, kind_map_none, kind, 262 content_state, prop_state)); 263 SVN_ERR(svn_sqlite__step_done(stmt)); 264 265 if (conflict) 266 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, conflict, 267 scratch_pool)); 268 269 if (work_item) 270 SVN_ERR(svn_wc__db_wq_add_internal(wcroot, work_item, scratch_pool)); 271 272 return SVN_NO_ERROR; 273} 274 275/* Send all notifications stored in the notification list, and then 276 * remove the temporary database table. */ 277svn_error_t * 278svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot, 279 svn_revnum_t old_revision, 280 svn_revnum_t new_revision, 281 svn_wc_notify_func2_t notify_func, 282 void *notify_baton, 283 apr_pool_t *scratch_pool) 284{ 285 svn_sqlite__stmt_t *stmt; 286 287 if (notify_func) 288 { 289 apr_pool_t *iterpool; 290 svn_boolean_t have_row; 291 292 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 293 STMT_SELECT_UPDATE_MOVE_LIST)); 294 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 295 296 iterpool = svn_pool_create(scratch_pool); 297 while (have_row) 298 { 299 const char *local_relpath; 300 svn_wc_notify_action_t action; 301 svn_wc_notify_t *notify; 302 303 svn_pool_clear(iterpool); 304 305 local_relpath = svn_sqlite__column_text(stmt, 0, NULL); 306 action = svn_sqlite__column_int(stmt, 1); 307 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath, 308 local_relpath, 309 iterpool), 310 action, iterpool); 311 notify->kind = svn_sqlite__column_token(stmt, 2, kind_map_none); 312 notify->content_state = svn_sqlite__column_int(stmt, 3); 313 notify->prop_state = svn_sqlite__column_int(stmt, 4); 314 notify->old_revision = old_revision; 315 notify->revision = new_revision; 316 notify_func(notify_baton, notify, scratch_pool); 317 318 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 319 } 320 svn_pool_destroy(iterpool); 321 SVN_ERR(svn_sqlite__reset(stmt)); 322 } 323 324 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 325 STMT_FINALIZE_UPDATE_MOVE)); 326 SVN_ERR(svn_sqlite__step_done(stmt)); 327 328 return SVN_NO_ERROR; 329} 330 331/* Create a tree-conflict for recording on LOCAL_RELPATH if such 332 a tree-conflict does not already exist. */ 333static svn_error_t * 334create_tree_conflict(svn_skel_t **conflict_p, 335 svn_wc__db_wcroot_t *wcroot, 336 const char *local_relpath, 337 const char *dst_op_root_relpath, 338 svn_wc__db_t *db, 339 const svn_wc_conflict_version_t *old_version, 340 const svn_wc_conflict_version_t *new_version, 341 svn_wc_operation_t operation, 342 svn_node_kind_t old_kind, 343 svn_node_kind_t new_kind, 344 const char *old_repos_relpath, 345 svn_wc_conflict_reason_t reason, 346 svn_wc_conflict_action_t action, 347 const char *move_src_op_root_relpath, 348 apr_pool_t *result_pool, 349 apr_pool_t *scratch_pool) 350{ 351 svn_error_t *err; 352 svn_skel_t *conflict; 353 svn_wc_conflict_version_t *conflict_old_version, *conflict_new_version; 354 const char *move_src_op_root_abspath 355 = move_src_op_root_relpath 356 ? svn_dirent_join(wcroot->abspath, 357 move_src_op_root_relpath, scratch_pool) 358 : NULL; 359 const char *old_repos_relpath_part 360 = old_repos_relpath 361 ? svn_relpath_skip_ancestor(old_version->path_in_repos, 362 old_repos_relpath) 363 : NULL; 364 const char *new_repos_relpath 365 = old_repos_relpath_part 366 ? svn_relpath_join(new_version->path_in_repos, old_repos_relpath_part, 367 scratch_pool) 368 : NULL; 369 370 if (!new_repos_relpath) 371 { 372 const char *child_relpath = svn_relpath_skip_ancestor( 373 dst_op_root_relpath, 374 local_relpath); 375 SVN_ERR_ASSERT(child_relpath != NULL); 376 new_repos_relpath = svn_relpath_join(new_version->path_in_repos, 377 child_relpath, scratch_pool); 378 } 379 380 err = svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, 381 wcroot, local_relpath, 382 result_pool, scratch_pool); 383 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 384 return svn_error_trace(err); 385 else if (err) 386 { 387 svn_error_clear(err); 388 conflict = NULL; 389 } 390 391 if (conflict) 392 { 393 svn_wc_operation_t conflict_operation; 394 svn_boolean_t tree_conflicted; 395 396 SVN_ERR(svn_wc__conflict_read_info(&conflict_operation, NULL, NULL, NULL, 397 &tree_conflicted, 398 db, wcroot->abspath, conflict, 399 scratch_pool, scratch_pool)); 400 401 if (conflict_operation != svn_wc_operation_update 402 && conflict_operation != svn_wc_operation_switch) 403 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 404 _("'%s' already in conflict"), 405 path_for_error_message(wcroot, local_relpath, 406 scratch_pool)); 407 408 if (tree_conflicted) 409 { 410 svn_wc_conflict_reason_t existing_reason; 411 svn_wc_conflict_action_t existing_action; 412 const char *existing_abspath; 413 414 SVN_ERR(svn_wc__conflict_read_tree_conflict(&existing_reason, 415 &existing_action, 416 &existing_abspath, 417 db, wcroot->abspath, 418 conflict, 419 scratch_pool, 420 scratch_pool)); 421 if (reason != existing_reason 422 || action != existing_action 423 || (reason == svn_wc_conflict_reason_moved_away 424 && strcmp(move_src_op_root_relpath, 425 svn_dirent_skip_ancestor(wcroot->abspath, 426 existing_abspath)))) 427 return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, 428 _("'%s' already in conflict"), 429 path_for_error_message(wcroot, 430 local_relpath, 431 scratch_pool)); 432 433 /* Already a suitable tree-conflict. */ 434 *conflict_p = conflict; 435 return SVN_NO_ERROR; 436 } 437 } 438 else 439 conflict = svn_wc__conflict_skel_create(result_pool); 440 441 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict( 442 conflict, db, 443 svn_dirent_join(wcroot->abspath, local_relpath, 444 scratch_pool), 445 reason, 446 action, 447 move_src_op_root_abspath, 448 result_pool, 449 scratch_pool)); 450 451 conflict_old_version = svn_wc_conflict_version_create2( 452 old_version->repos_url, old_version->repos_uuid, 453 old_repos_relpath, old_version->peg_rev, 454 old_kind, scratch_pool); 455 456 conflict_new_version = svn_wc_conflict_version_create2( 457 new_version->repos_url, new_version->repos_uuid, 458 new_repos_relpath, new_version->peg_rev, 459 new_kind, scratch_pool); 460 461 if (operation == svn_wc_operation_update) 462 { 463 SVN_ERR(svn_wc__conflict_skel_set_op_update( 464 conflict, conflict_old_version, conflict_new_version, 465 result_pool, scratch_pool)); 466 } 467 else 468 { 469 assert(operation == svn_wc_operation_switch); 470 SVN_ERR(svn_wc__conflict_skel_set_op_switch( 471 conflict, conflict_old_version, conflict_new_version, 472 result_pool, scratch_pool)); 473 } 474 475 *conflict_p = conflict; 476 return SVN_NO_ERROR; 477} 478 479static svn_error_t * 480create_node_tree_conflict(svn_skel_t **conflict_p, 481 node_move_baton_t *nmb, 482 const char *dst_local_relpath, 483 svn_node_kind_t old_kind, 484 svn_node_kind_t new_kind, 485 svn_wc_conflict_reason_t reason, 486 svn_wc_conflict_action_t action, 487 const char *move_src_op_root_relpath, 488 apr_pool_t *result_pool, 489 apr_pool_t *scratch_pool) 490{ 491 update_move_baton_t *umb = nmb->umb; 492 const char *dst_repos_relpath; 493 const char *dst_root_relpath = svn_relpath_prefix(nmb->dst_relpath, 494 nmb->umb->dst_op_depth, 495 scratch_pool); 496 497 dst_repos_relpath = 498 svn_relpath_join(nmb->umb->old_version->path_in_repos, 499 svn_relpath_skip_ancestor(dst_root_relpath, 500 nmb->dst_relpath), 501 scratch_pool); 502 503 504 505 return svn_error_trace( 506 create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath, 507 svn_relpath_prefix(dst_local_relpath, 508 umb->dst_op_depth, 509 scratch_pool), 510 umb->db, 511 umb->old_version, umb->new_version, 512 umb->operation, old_kind, new_kind, 513 dst_repos_relpath, 514 reason, action, move_src_op_root_relpath, 515 result_pool, scratch_pool)); 516} 517 518/* Checks if a specific local path is shadowed as seen from the move root. 519 Helper for update_moved_away_node() */ 520static svn_error_t * 521check_node_shadowed(svn_boolean_t *shadowed, 522 svn_wc__db_wcroot_t *wcroot, 523 const char *local_relpath, 524 int move_root_dst_op_depth, 525 apr_pool_t *scratch_pool) 526{ 527 svn_sqlite__stmt_t *stmt; 528 svn_boolean_t have_row; 529 530 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 531 STMT_SELECT_WORKING_NODE)); 532 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); 533 534 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 535 536 if (have_row) 537 { 538 int op_depth = svn_sqlite__column_int(stmt, 0); 539 540 *shadowed = (op_depth > move_root_dst_op_depth); 541 } 542 else 543 *shadowed = FALSE; 544 SVN_ERR(svn_sqlite__reset(stmt)); 545 546 return SVN_NO_ERROR; 547} 548 549/* Set a tree conflict for the shadowed node LOCAL_RELPATH, which is 550 the ROOT OF THE OBSTRUCTION if such a tree-conflict does not 551 already exist. KIND is the kind of the incoming LOCAL_RELPATH. */ 552static svn_error_t * 553mark_tc_on_op_root(node_move_baton_t *nmb, 554 svn_node_kind_t old_kind, 555 svn_node_kind_t new_kind, 556 svn_wc_conflict_action_t action, 557 apr_pool_t *scratch_pool) 558{ 559 update_move_baton_t *b = nmb->umb; 560 const char *move_dst_relpath; 561 svn_skel_t *conflict; 562 563 SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed); 564 565 nmb->skip = TRUE; 566 567 if (old_kind == svn_node_none) 568 move_dst_relpath = NULL; 569 else 570 SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL, 571 b->wcroot, nmb->dst_relpath, 572 b->dst_op_depth, 573 scratch_pool, scratch_pool)); 574 575 SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath, 576 old_kind, new_kind, 577 (move_dst_relpath 578 ? svn_wc_conflict_reason_moved_away 579 : svn_wc_conflict_reason_deleted), 580 action, move_dst_relpath 581 ? nmb->dst_relpath 582 : NULL, 583 scratch_pool, scratch_pool)); 584 585 SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db, 586 svn_wc_notify_tree_conflict, 587 new_kind, 588 svn_wc_notify_state_inapplicable, 589 svn_wc_notify_state_inapplicable, 590 conflict, NULL, scratch_pool)); 591 592 return SVN_NO_ERROR; 593} 594 595static svn_error_t * 596mark_node_edited(node_move_baton_t *nmb, 597 apr_pool_t *scratch_pool) 598{ 599 if (nmb->edited) 600 return SVN_NO_ERROR; 601 602 if (nmb->pb) 603 { 604 SVN_ERR(mark_node_edited(nmb->pb, scratch_pool)); 605 606 if (nmb->pb->skip) 607 nmb->skip = TRUE; 608 } 609 610 nmb->edited = TRUE; 611 612 if (nmb->skip) 613 return SVN_NO_ERROR; 614 615 if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed)) 616 { 617 svn_node_kind_t dst_kind, src_kind; 618 619 SVN_ERR(svn_wc__db_depth_get_info(NULL, &dst_kind, NULL, 620 NULL, NULL, NULL, NULL, 621 NULL, NULL, NULL, NULL, NULL, NULL, 622 nmb->umb->wcroot, nmb->dst_relpath, 623 nmb->umb->dst_op_depth, 624 scratch_pool, scratch_pool)); 625 626 SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, NULL, NULL, 627 NULL, NULL, NULL, 628 NULL, NULL, NULL, NULL, NULL, NULL, 629 nmb->umb->wcroot, nmb->src_relpath, 630 nmb->umb->src_op_depth, 631 scratch_pool, scratch_pool)); 632 633 SVN_ERR(mark_tc_on_op_root(nmb, 634 dst_kind, src_kind, 635 svn_wc_conflict_action_edit, 636 scratch_pool)); 637 } 638 639 return SVN_NO_ERROR; 640} 641 642static svn_error_t * 643mark_parent_edited(node_move_baton_t *nmb, 644 apr_pool_t *scratch_pool) 645{ 646 SVN_ERR_ASSERT(nmb && nmb->pb); 647 648 SVN_ERR(mark_node_edited(nmb->pb, scratch_pool)); 649 650 if (nmb->pb->skip) 651 nmb->skip = TRUE; 652 653 return SVN_NO_ERROR; 654} 655 656static svn_error_t * 657tc_editor_add_directory(node_move_baton_t *nmb, 658 const char *relpath, 659 svn_node_kind_t old_kind, 660 apr_hash_t *props, 661 apr_pool_t *scratch_pool) 662{ 663 update_move_baton_t *b = nmb->umb; 664 const char *local_abspath; 665 svn_node_kind_t wc_kind; 666 svn_skel_t *work_item = NULL; 667 svn_skel_t *conflict = NULL; 668 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned; 669 670 SVN_ERR(mark_parent_edited(nmb, scratch_pool)); 671 if (nmb->skip) 672 return SVN_NO_ERROR; 673 674 if (nmb->shadowed) 675 { 676 svn_wc__db_status_t status; 677 678 SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL, 679 NULL, NULL, NULL, NULL, NULL, NULL, 680 NULL, NULL, NULL, NULL, NULL, NULL, 681 NULL, NULL, NULL, NULL, NULL, NULL, 682 NULL, NULL, NULL, 683 b->wcroot, relpath, 684 scratch_pool, scratch_pool)); 685 686 if (status == svn_wc__db_status_deleted) 687 reason = svn_wc_conflict_reason_deleted; 688 else if (status != svn_wc__db_status_added) 689 wc_kind = svn_node_none; 690 else if (old_kind == svn_node_none) 691 reason = svn_wc_conflict_reason_added; 692 else 693 reason = svn_wc_conflict_reason_replaced; 694 } 695 else 696 wc_kind = svn_node_none; 697 698 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); 699 700 if (wc_kind == svn_node_none) 701 { 702 /* Check for unversioned tree-conflict */ 703 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); 704 } 705 706 if (!nmb->shadowed && wc_kind == old_kind) 707 wc_kind = svn_node_none; /* Node will be gone once we install */ 708 709 if (wc_kind != svn_node_none 710 && (nmb->shadowed || wc_kind != old_kind)) /* replace */ 711 { 712 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, 713 old_kind, svn_node_dir, 714 reason, 715 (old_kind == svn_node_none) 716 ? svn_wc_conflict_action_add 717 : svn_wc_conflict_action_replace, 718 NULL, 719 scratch_pool, scratch_pool)); 720 nmb->skip = TRUE; 721 } 722 else 723 { 724 SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath, 725 scratch_pool, scratch_pool)); 726 } 727 728 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, 729 (old_kind == svn_node_none) 730 ? svn_wc_notify_update_add 731 : svn_wc_notify_update_replace, 732 svn_node_dir, 733 svn_wc_notify_state_inapplicable, 734 svn_wc_notify_state_inapplicable, 735 conflict, work_item, scratch_pool)); 736 return SVN_NO_ERROR; 737} 738 739static svn_error_t * 740tc_editor_add_file(node_move_baton_t *nmb, 741 const char *relpath, 742 svn_node_kind_t old_kind, 743 const svn_checksum_t *checksum, 744 apr_hash_t *props, 745 apr_pool_t *scratch_pool) 746{ 747 update_move_baton_t *b = nmb->umb; 748 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned; 749 svn_node_kind_t wc_kind; 750 const char *local_abspath; 751 svn_skel_t *work_item = NULL; 752 svn_skel_t *conflict = NULL; 753 754 SVN_ERR(mark_parent_edited(nmb, scratch_pool)); 755 if (nmb->skip) 756 return SVN_NO_ERROR; 757 758 if (nmb->shadowed) 759 { 760 svn_wc__db_status_t status; 761 762 SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL, 763 NULL, NULL, NULL, NULL, NULL, NULL, 764 NULL, NULL, NULL, NULL, NULL, NULL, 765 NULL, NULL, NULL, NULL, NULL, NULL, 766 NULL, NULL, NULL, 767 b->wcroot, relpath, 768 scratch_pool, scratch_pool)); 769 770 if (status == svn_wc__db_status_deleted) 771 reason = svn_wc_conflict_reason_deleted; 772 else if (status != svn_wc__db_status_added) 773 wc_kind = svn_node_none; 774 else if (old_kind == svn_node_none) 775 reason = svn_wc_conflict_reason_added; 776 else 777 reason = svn_wc_conflict_reason_replaced; 778 } 779 else 780 wc_kind = svn_node_none; 781 782 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); 783 784 if (wc_kind == svn_node_none) 785 { 786 /* Check for unversioned tree-conflict */ 787 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); 788 } 789 790 if (wc_kind != svn_node_none 791 && (nmb->shadowed || wc_kind != old_kind)) /* replace */ 792 { 793 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, 794 old_kind, svn_node_file, 795 reason, 796 (old_kind == svn_node_none) 797 ? svn_wc_conflict_action_add 798 : svn_wc_conflict_action_replace, 799 NULL, 800 scratch_pool, scratch_pool)); 801 nmb->skip = TRUE; 802 } 803 else 804 { 805 /* Update working file. */ 806 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, 807 svn_dirent_join(b->wcroot->abspath, 808 relpath, 809 scratch_pool), 810 NULL, 811 FALSE /*FIXME: use_commit_times?*/, 812 TRUE /* record_file_info */, 813 scratch_pool, scratch_pool)); 814 } 815 816 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, 817 (old_kind == svn_node_none) 818 ? svn_wc_notify_update_add 819 : svn_wc_notify_update_replace, 820 svn_node_file, 821 svn_wc_notify_state_inapplicable, 822 svn_wc_notify_state_inapplicable, 823 conflict, work_item, scratch_pool)); 824 return SVN_NO_ERROR; 825} 826 827/* All the info we need about one version of a working node. */ 828typedef struct working_node_version_t 829{ 830 svn_wc_conflict_version_t *location_and_kind; 831 apr_hash_t *props; 832 const svn_checksum_t *checksum; /* for files only */ 833} working_node_version_t; 834 835/* Return *WORK_ITEMS to create a conflict on LOCAL_ABSPATH. */ 836static svn_error_t * 837create_conflict_markers(svn_skel_t **work_items, 838 const char *local_abspath, 839 svn_wc__db_t *db, 840 const char *repos_relpath, 841 svn_skel_t *conflict_skel, 842 svn_wc_operation_t operation, 843 const working_node_version_t *old_version, 844 const working_node_version_t *new_version, 845 svn_node_kind_t kind, 846 svn_boolean_t set_operation, 847 apr_pool_t *result_pool, 848 apr_pool_t *scratch_pool) 849{ 850 svn_wc_conflict_version_t *original_version; 851 svn_wc_conflict_version_t *conflicted_version; 852 const char *part; 853 854 original_version = svn_wc_conflict_version_dup( 855 old_version->location_and_kind, scratch_pool); 856 original_version->node_kind = kind; 857 conflicted_version = svn_wc_conflict_version_dup( 858 new_version->location_and_kind, scratch_pool); 859 conflicted_version->node_kind = kind; 860 861 part = svn_relpath_skip_ancestor(original_version->path_in_repos, 862 repos_relpath); 863 conflicted_version->path_in_repos 864 = svn_relpath_join(conflicted_version->path_in_repos, part, scratch_pool); 865 original_version->path_in_repos = repos_relpath; 866 867 if (set_operation) 868 { 869 if (operation == svn_wc_operation_update) 870 { 871 SVN_ERR(svn_wc__conflict_skel_set_op_update( 872 conflict_skel, original_version, 873 conflicted_version, 874 scratch_pool, scratch_pool)); 875 } 876 else 877 { 878 SVN_ERR(svn_wc__conflict_skel_set_op_switch( 879 conflict_skel, original_version, 880 conflicted_version, 881 scratch_pool, scratch_pool)); 882 } 883 } 884 885 /* According to this func's doc string, it is "Currently only used for 886 * property conflicts as text conflict markers are just in-wc files." */ 887 SVN_ERR(svn_wc__conflict_create_markers(work_items, db, 888 local_abspath, 889 conflict_skel, 890 result_pool, 891 scratch_pool)); 892 893 return SVN_NO_ERROR; 894} 895 896static svn_error_t * 897update_working_props(svn_wc_notify_state_t *prop_state, 898 svn_skel_t **conflict_skel, 899 apr_array_header_t **propchanges, 900 apr_hash_t **actual_props, 901 update_move_baton_t *b, 902 const char *local_relpath, 903 const struct working_node_version_t *old_version, 904 const struct working_node_version_t *new_version, 905 apr_pool_t *result_pool, 906 apr_pool_t *scratch_pool) 907{ 908 apr_hash_t *new_actual_props; 909 apr_array_header_t *new_propchanges; 910 911 /* 912 * Run a 3-way prop merge to update the props, using the pre-update 913 * props as the merge base, the post-update props as the 914 * merge-left version, and the current props of the 915 * moved-here working file as the merge-right version. 916 */ 917 SVN_ERR(svn_wc__db_read_props_internal(actual_props, 918 b->wcroot, local_relpath, 919 result_pool, scratch_pool)); 920 SVN_ERR(svn_prop_diffs(propchanges, new_version->props, old_version->props, 921 result_pool)); 922 SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state, 923 &new_actual_props, 924 b->db, svn_dirent_join(b->wcroot->abspath, 925 local_relpath, 926 scratch_pool), 927 old_version->props, old_version->props, 928 *actual_props, *propchanges, 929 result_pool, scratch_pool)); 930 931 /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props_internal 932 relies on NODES row being updated via a different route . 933 934 This extra property diff makes sure we clear the actual row when 935 the final result is unchanged properties. */ 936 SVN_ERR(svn_prop_diffs(&new_propchanges, new_actual_props, new_version->props, 937 scratch_pool)); 938 if (!new_propchanges->nelts) 939 new_actual_props = NULL; 940 941 /* Install the new actual props. */ 942 SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath, 943 new_actual_props, 944 svn_wc__has_magic_property( 945 *propchanges), 946 scratch_pool)); 947 948 return SVN_NO_ERROR; 949} 950 951static svn_error_t * 952tc_editor_alter_directory(node_move_baton_t *nmb, 953 const char *dst_relpath, 954 apr_hash_t *old_props, 955 apr_hash_t *new_props, 956 apr_pool_t *scratch_pool) 957{ 958 update_move_baton_t *b = nmb->umb; 959 working_node_version_t old_version, new_version; 960 svn_skel_t *work_items = NULL; 961 svn_skel_t *conflict_skel = NULL; 962 const char *local_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, 963 scratch_pool); 964 svn_wc_notify_state_t prop_state; 965 apr_hash_t *actual_props; 966 apr_array_header_t *propchanges; 967 svn_node_kind_t wc_kind; 968 svn_boolean_t obstructed = FALSE; 969 970 SVN_ERR(mark_node_edited(nmb, scratch_pool)); 971 if (nmb->skip) 972 return SVN_NO_ERROR; 973 974 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); 975 if (wc_kind != svn_node_none && wc_kind != svn_node_dir) 976 { 977 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, 978 svn_node_dir, svn_node_dir, 979 svn_wc_conflict_reason_obstructed, 980 svn_wc_conflict_action_edit, 981 NULL, 982 scratch_pool, scratch_pool)); 983 obstructed = TRUE; 984 } 985 986 old_version.location_and_kind = b->old_version; 987 new_version.location_and_kind = b->new_version; 988 989 old_version.checksum = NULL; /* not a file */ 990 old_version.props = old_props; 991 new_version.checksum = NULL; /* not a file */ 992 new_version.props = new_props; 993 994 SVN_ERR(update_working_props(&prop_state, &conflict_skel, 995 &propchanges, &actual_props, 996 b, dst_relpath, 997 &old_version, &new_version, 998 scratch_pool, scratch_pool)); 999 1000 if (prop_state == svn_wc_notify_state_conflicted) 1001 { 1002 const char *move_dst_repos_relpath; 1003 1004 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, 1005 &move_dst_repos_relpath, NULL, NULL, 1006 NULL, NULL, NULL, NULL, NULL, NULL, 1007 NULL, 1008 b->wcroot, dst_relpath, 1009 b->dst_op_depth, 1010 scratch_pool, scratch_pool)); 1011 1012 SVN_ERR(create_conflict_markers(&work_items, local_abspath, 1013 b->db, move_dst_repos_relpath, 1014 conflict_skel, b->operation, 1015 &old_version, &new_version, 1016 svn_node_dir, !obstructed, 1017 scratch_pool, scratch_pool)); 1018 } 1019 1020 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, 1021 svn_wc_notify_update_update, 1022 svn_node_dir, 1023 svn_wc_notify_state_inapplicable, 1024 prop_state, 1025 conflict_skel, work_items, scratch_pool)); 1026 1027 return SVN_NO_ERROR; 1028} 1029 1030/* Edit the file found at the move destination, which is initially at 1031 * the old state. Merge the changes into the "working"/"actual" file. 1032 * 1033 * Merge the difference between OLD_VERSION and NEW_VERSION into 1034 * the working file at LOCAL_RELPATH. 1035 * 1036 * The term 'old' refers to the pre-update state, which is the state of 1037 * (some layer of) LOCAL_RELPATH while this function runs; and 'new' 1038 * refers to the post-update state, as found at the (base layer of) the 1039 * move source path while this function runs. 1040 * 1041 * LOCAL_RELPATH is a file in the working copy at WCROOT in DB, and 1042 * REPOS_RELPATH is the repository path it would be committed to. 1043 * 1044 * Use NOTIFY_FUNC and NOTIFY_BATON for notifications. 1045 * Set *WORK_ITEMS to any required work items, allocated in RESULT_POOL. 1046 * Use SCRATCH_POOL for temporary allocations. */ 1047static svn_error_t * 1048tc_editor_alter_file(node_move_baton_t *nmb, 1049 const char *dst_relpath, 1050 const svn_checksum_t *old_checksum, 1051 const svn_checksum_t *new_checksum, 1052 apr_hash_t *old_props, 1053 apr_hash_t *new_props, 1054 apr_pool_t *scratch_pool) 1055{ 1056 update_move_baton_t *b = nmb->umb; 1057 working_node_version_t old_version, new_version; 1058 const char *local_abspath = svn_dirent_join(b->wcroot->abspath, 1059 dst_relpath, 1060 scratch_pool); 1061 const char *old_pristine_abspath; 1062 const char *new_pristine_abspath; 1063 svn_skel_t *conflict_skel = NULL; 1064 apr_hash_t *actual_props; 1065 apr_array_header_t *propchanges; 1066 enum svn_wc_merge_outcome_t merge_outcome; 1067 svn_wc_notify_state_t prop_state, content_state; 1068 svn_skel_t *work_item, *work_items = NULL; 1069 svn_node_kind_t wc_kind; 1070 svn_boolean_t obstructed = FALSE; 1071 1072 SVN_ERR(mark_node_edited(nmb, scratch_pool)); 1073 if (nmb->skip) 1074 return SVN_NO_ERROR; 1075 1076 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool)); 1077 if (wc_kind != svn_node_none && wc_kind != svn_node_file) 1078 { 1079 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath, 1080 svn_node_file, svn_node_file, 1081 svn_wc_conflict_reason_obstructed, 1082 svn_wc_conflict_action_edit, 1083 NULL, 1084 scratch_pool, scratch_pool)); 1085 obstructed = TRUE; 1086 } 1087 1088 old_version.location_and_kind = b->old_version; 1089 new_version.location_and_kind = b->new_version; 1090 1091 old_version.checksum = old_checksum; 1092 old_version.props = old_props; 1093 new_version.checksum = new_checksum; 1094 new_version.props = new_props; 1095 1096 /* ### TODO: Only do this when there is no higher WORKING layer */ 1097 SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges, 1098 &actual_props, b, dst_relpath, 1099 &old_version, &new_version, 1100 scratch_pool, scratch_pool)); 1101 1102 if (!obstructed 1103 && !svn_checksum_match(new_version.checksum, old_version.checksum)) 1104 { 1105 svn_boolean_t is_locally_modified; 1106 1107 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified, 1108 b->db, local_abspath, 1109 FALSE /* exact_comparison */, 1110 scratch_pool)); 1111 if (!is_locally_modified) 1112 { 1113 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, 1114 local_abspath, 1115 NULL, 1116 FALSE /* FIXME: use_commit_times? */, 1117 TRUE /* record_file_info */, 1118 scratch_pool, scratch_pool)); 1119 1120 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 1121 1122 content_state = svn_wc_notify_state_changed; 1123 } 1124 else 1125 { 1126 /* 1127 * Run a 3-way merge to update the file, using the pre-update 1128 * pristine text as the merge base, the post-update pristine 1129 * text as the merge-left version, and the current content of the 1130 * moved-here working file as the merge-right version. 1131 */ 1132 SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath, 1133 b->db, b->wcroot->abspath, 1134 old_version.checksum, 1135 scratch_pool, scratch_pool)); 1136 SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath, 1137 b->db, b->wcroot->abspath, 1138 new_version.checksum, 1139 scratch_pool, scratch_pool)); 1140 SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel, 1141 &merge_outcome, b->db, 1142 old_pristine_abspath, 1143 new_pristine_abspath, 1144 local_abspath, 1145 local_abspath, 1146 NULL, NULL, NULL, /* diff labels */ 1147 actual_props, 1148 FALSE, /* dry-run */ 1149 NULL, /* diff3-cmd */ 1150 NULL, /* merge options */ 1151 propchanges, 1152 b->cancel_func, b->cancel_baton, 1153 scratch_pool, scratch_pool)); 1154 1155 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 1156 1157 if (merge_outcome == svn_wc_merge_conflict) 1158 content_state = svn_wc_notify_state_conflicted; 1159 else 1160 content_state = svn_wc_notify_state_merged; 1161 } 1162 } 1163 else 1164 content_state = svn_wc_notify_state_unchanged; 1165 1166 /* If there are any conflicts to be stored, convert them into work items 1167 * too. */ 1168 if (conflict_skel) 1169 { 1170 const char *move_dst_repos_relpath; 1171 1172 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL, 1173 &move_dst_repos_relpath, NULL, NULL, 1174 NULL, NULL, NULL, NULL, NULL, NULL, 1175 NULL, 1176 b->wcroot, dst_relpath, 1177 b->dst_op_depth, 1178 scratch_pool, scratch_pool)); 1179 1180 SVN_ERR(create_conflict_markers(&work_item, local_abspath, b->db, 1181 move_dst_repos_relpath, conflict_skel, 1182 b->operation, &old_version, &new_version, 1183 svn_node_file, !obstructed, 1184 scratch_pool, scratch_pool)); 1185 1186 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool); 1187 } 1188 1189 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db, 1190 svn_wc_notify_update_update, 1191 svn_node_file, 1192 content_state, 1193 prop_state, 1194 conflict_skel, work_items, scratch_pool)); 1195 1196 return SVN_NO_ERROR; 1197} 1198 1199static svn_error_t * 1200tc_editor_delete(node_move_baton_t *nmb, 1201 const char *relpath, 1202 svn_node_kind_t old_kind, 1203 svn_node_kind_t new_kind, 1204 apr_pool_t *scratch_pool) 1205{ 1206 update_move_baton_t *b = nmb->umb; 1207 svn_sqlite__stmt_t *stmt; 1208 const char *local_abspath; 1209 svn_boolean_t is_modified, is_all_deletes; 1210 svn_skel_t *work_items = NULL; 1211 svn_skel_t *conflict = NULL; 1212 1213 SVN_ERR(mark_parent_edited(nmb, scratch_pool)); 1214 if (nmb->skip) 1215 return SVN_NO_ERROR; 1216 1217 /* Check before retracting delete to catch delete-delete 1218 conflicts. This catches conflicts on the node itself; deleted 1219 children are caught as local modifications below.*/ 1220 if (nmb->shadowed) 1221 { 1222 SVN_ERR(mark_tc_on_op_root(nmb, 1223 old_kind, new_kind, 1224 svn_wc_conflict_action_delete, 1225 scratch_pool)); 1226 return SVN_NO_ERROR; 1227 } 1228 1229 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool); 1230 SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes, 1231 nmb->umb->db, local_abspath, FALSE, 1232 NULL, NULL, scratch_pool)); 1233 if (is_modified) 1234 { 1235 svn_wc_conflict_reason_t reason; 1236 1237 /* No conflict means no NODES rows at the relpath op-depth 1238 so it's easy to convert the modified tree into a copy. 1239 1240 Note the following assumptions for relpath: 1241 * it is not shadowed 1242 * it is not the/an op-root. (or we can't make us a copy) 1243 */ 1244 1245 SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE, 1246 NULL, NULL, scratch_pool)); 1247 1248 reason = svn_wc_conflict_reason_edited; 1249 1250 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath, 1251 old_kind, new_kind, reason, 1252 (new_kind == svn_node_none) 1253 ? svn_wc_conflict_action_delete 1254 : svn_wc_conflict_action_replace, 1255 NULL, 1256 scratch_pool, scratch_pool)); 1257 nmb->skip = TRUE; 1258 } 1259 else 1260 { 1261 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1262 const char *del_abspath; 1263 svn_boolean_t have_row; 1264 1265 /* Get all descendants of the node in reverse order (so children are 1266 handled before their parents, but not strictly depth first) */ 1267 SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb, 1268 STMT_SELECT_DESCENDANTS_OP_DEPTH_RV)); 1269 SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath, 1270 b->dst_op_depth)); 1271 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1272 while (have_row) 1273 { 1274 svn_error_t *err; 1275 svn_skel_t *work_item; 1276 svn_node_kind_t del_kind; 1277 1278 svn_pool_clear(iterpool); 1279 1280 del_kind = svn_sqlite__column_token(stmt, 1, kind_map); 1281 del_abspath = svn_dirent_join(b->wcroot->abspath, 1282 svn_sqlite__column_text(stmt, 0, NULL), 1283 iterpool); 1284 if (del_kind == svn_node_dir) 1285 err = svn_wc__wq_build_dir_remove(&work_item, b->db, 1286 b->wcroot->abspath, del_abspath, 1287 FALSE /* recursive */, 1288 iterpool, iterpool); 1289 else 1290 err = svn_wc__wq_build_file_remove(&work_item, b->db, 1291 b->wcroot->abspath, del_abspath, 1292 iterpool, iterpool); 1293 if (!err) 1294 err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool); 1295 if (err) 1296 return svn_error_compose_create(err, svn_sqlite__reset(stmt)); 1297 1298 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1299 } 1300 SVN_ERR(svn_sqlite__reset(stmt)); 1301 1302 if (old_kind == svn_node_dir) 1303 SVN_ERR(svn_wc__wq_build_dir_remove(&work_items, b->db, 1304 b->wcroot->abspath, local_abspath, 1305 FALSE /* recursive */, 1306 scratch_pool, iterpool)); 1307 else 1308 SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db, 1309 b->wcroot->abspath, local_abspath, 1310 scratch_pool, iterpool)); 1311 1312 svn_pool_destroy(iterpool); 1313 } 1314 1315 /* Only notify if add_file/add_dir is not going to notify */ 1316 if (conflict || (new_kind == svn_node_none)) 1317 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db, 1318 svn_wc_notify_update_delete, 1319 new_kind, 1320 svn_wc_notify_state_inapplicable, 1321 svn_wc_notify_state_inapplicable, 1322 conflict, work_items, scratch_pool)); 1323 else if (work_items) 1324 SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items, 1325 scratch_pool)); 1326 1327 return SVN_NO_ERROR; 1328} 1329 1330/* 1331 * Driver code. 1332 * 1333 * The scenario is that a subtree has been locally moved, and then the base 1334 * layer on the source side of the move has received an update to a new 1335 * state. The destination subtree has not yet been updated, and still 1336 * matches the pre-update state of the source subtree. 1337 * 1338 * The edit driver drives the receiver with the difference between the 1339 * pre-update state (as found now at the move-destination) and the 1340 * post-update state (found now at the move-source). 1341 * 1342 * We currently assume that both the pre-update and post-update states are 1343 * single-revision. 1344 */ 1345 1346/* Return *PROPS, *CHECKSUM, *CHILDREN and *KIND for LOCAL_RELPATH at 1347 OP_DEPTH provided the row exists. Return *KIND of svn_node_none if 1348 the row does not exist, or only describes a delete of a lower op-depth. 1349 *CHILDREN is a sorted array of basenames of type 'const char *', rather 1350 than a hash, to allow the driver to process children in a defined order. */ 1351static svn_error_t * 1352get_info(apr_hash_t **props, 1353 const svn_checksum_t **checksum, 1354 apr_array_header_t **children, 1355 svn_node_kind_t *kind, 1356 const char *local_relpath, 1357 int op_depth, 1358 svn_wc__db_wcroot_t *wcroot, 1359 apr_pool_t *result_pool, 1360 apr_pool_t *scratch_pool) 1361{ 1362 svn_wc__db_status_t status; 1363 const char *repos_relpath; 1364 svn_node_kind_t db_kind; 1365 svn_error_t *err; 1366 1367 err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL, 1368 NULL, NULL, NULL, NULL, checksum, NULL, 1369 NULL, props, 1370 wcroot, local_relpath, op_depth, 1371 result_pool, scratch_pool); 1372 1373 /* If there is no node at this depth, or only a node that describes a delete 1374 of a lower layer we report this node as not existing. */ 1375 if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 1376 || (!err && status != svn_wc__db_status_added 1377 && status != svn_wc__db_status_normal)) 1378 { 1379 svn_error_clear(err); 1380 1381 if (kind) 1382 *kind = svn_node_none; 1383 if (checksum) 1384 *checksum = NULL; 1385 if (props) 1386 *props = NULL; 1387 if (children) 1388 *children = apr_array_make(result_pool, 0, sizeof(const char *)); 1389 1390 return SVN_NO_ERROR; 1391 } 1392 else 1393 SVN_ERR(err); 1394 1395 if (kind) 1396 *kind = db_kind; 1397 1398 if (children && db_kind == svn_node_dir) 1399 { 1400 svn_sqlite__stmt_t *stmt; 1401 svn_boolean_t have_row; 1402 1403 *children = apr_array_make(result_pool, 16, sizeof(const char *)); 1404 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 1405 STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS)); 1406 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 1407 op_depth)); 1408 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1409 while (have_row) 1410 { 1411 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL); 1412 1413 APR_ARRAY_PUSH(*children, const char *) 1414 = svn_relpath_basename(child_relpath, result_pool); 1415 1416 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1417 } 1418 SVN_ERR(svn_sqlite__reset(stmt)); 1419 } 1420 else if (children) 1421 *children = apr_array_make(result_pool, 0, sizeof(const char *)); 1422 1423 return SVN_NO_ERROR; 1424} 1425 1426/* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties, 1427 FALSE otherwise. SRC_PROPS and DST_PROPS are standard property 1428 hashes. */ 1429static svn_error_t * 1430props_match(svn_boolean_t *match, 1431 apr_hash_t *src_props, 1432 apr_hash_t *dst_props, 1433 apr_pool_t *scratch_pool) 1434{ 1435 if (!src_props && !dst_props) 1436 *match = TRUE; 1437 else if (!src_props || ! dst_props) 1438 *match = FALSE; 1439 else 1440 { 1441 apr_array_header_t *propdiffs; 1442 1443 SVN_ERR(svn_prop_diffs(&propdiffs, src_props, dst_props, scratch_pool)); 1444 *match = propdiffs->nelts ? FALSE : TRUE; 1445 } 1446 return SVN_NO_ERROR; 1447} 1448 1449/* ### Drive TC_EDITOR so as to ... 1450 */ 1451static svn_error_t * 1452update_moved_away_node(node_move_baton_t *nmb, 1453 svn_wc__db_wcroot_t *wcroot, 1454 const char *src_relpath, 1455 const char *dst_relpath, 1456 apr_pool_t *scratch_pool) 1457{ 1458 update_move_baton_t *b = nmb->umb; 1459 svn_node_kind_t src_kind, dst_kind; 1460 const svn_checksum_t *src_checksum, *dst_checksum; 1461 apr_hash_t *src_props, *dst_props; 1462 apr_array_header_t *src_children, *dst_children; 1463 1464 if (b->cancel_func) 1465 SVN_ERR(b->cancel_func(b->cancel_baton)); 1466 1467 SVN_ERR(get_info(&src_props, &src_checksum, &src_children, &src_kind, 1468 src_relpath, b->src_op_depth, 1469 wcroot, scratch_pool, scratch_pool)); 1470 1471 SVN_ERR(get_info(&dst_props, &dst_checksum, &dst_children, &dst_kind, 1472 dst_relpath, b->dst_op_depth, 1473 wcroot, scratch_pool, scratch_pool)); 1474 1475 if (src_kind == svn_node_none 1476 || (dst_kind != svn_node_none && src_kind != dst_kind)) 1477 { 1478 SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind, 1479 scratch_pool)); 1480 } 1481 1482 if (nmb->skip) 1483 return SVN_NO_ERROR; 1484 1485 if (src_kind != svn_node_none && src_kind != dst_kind) 1486 { 1487 if (src_kind == svn_node_file || src_kind == svn_node_symlink) 1488 { 1489 SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind, 1490 src_checksum, src_props, scratch_pool)); 1491 } 1492 else if (src_kind == svn_node_dir) 1493 { 1494 SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind, 1495 src_props, scratch_pool)); 1496 } 1497 } 1498 else if (src_kind != svn_node_none) 1499 { 1500 svn_boolean_t props_equal; 1501 1502 SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool)); 1503 1504 if (src_kind == svn_node_file || src_kind == svn_node_symlink) 1505 { 1506 if (!props_equal || !svn_checksum_match(src_checksum, dst_checksum)) 1507 SVN_ERR(tc_editor_alter_file(nmb, dst_relpath, 1508 dst_checksum, src_checksum, 1509 dst_props, src_props, scratch_pool)); 1510 } 1511 else if (src_kind == svn_node_dir) 1512 { 1513 if (!props_equal) 1514 SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath, 1515 dst_props, src_props, 1516 scratch_pool)); 1517 } 1518 } 1519 1520 if (nmb->skip) 1521 return SVN_NO_ERROR; 1522 1523 if (src_kind == svn_node_dir) 1524 { 1525 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1526 int i = 0, j = 0; 1527 1528 while (i < src_children->nelts || j < dst_children->nelts) 1529 { 1530 const char *child_name; 1531 svn_boolean_t src_only = FALSE, dst_only = FALSE; 1532 node_move_baton_t cnmb = { 0 }; 1533 1534 cnmb.pb = nmb; 1535 cnmb.umb = nmb->umb; 1536 cnmb.shadowed = nmb->shadowed; 1537 1538 svn_pool_clear(iterpool); 1539 if (i >= src_children->nelts) 1540 { 1541 dst_only = TRUE; 1542 child_name = APR_ARRAY_IDX(dst_children, j, const char *); 1543 } 1544 else if (j >= dst_children->nelts) 1545 { 1546 src_only = TRUE; 1547 child_name = APR_ARRAY_IDX(src_children, i, const char *); 1548 } 1549 else 1550 { 1551 const char *src_name = APR_ARRAY_IDX(src_children, i, 1552 const char *); 1553 const char *dst_name = APR_ARRAY_IDX(dst_children, j, 1554 const char *); 1555 int cmp = strcmp(src_name, dst_name); 1556 1557 if (cmp > 0) 1558 dst_only = TRUE; 1559 else if (cmp < 0) 1560 src_only = TRUE; 1561 1562 child_name = dst_only ? dst_name : src_name; 1563 } 1564 1565 cnmb.src_relpath = svn_relpath_join(src_relpath, child_name, 1566 iterpool); 1567 cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name, 1568 iterpool); 1569 1570 if (!cnmb.shadowed) 1571 SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot, 1572 cnmb.dst_relpath, b->dst_op_depth, 1573 iterpool)); 1574 1575 SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath, 1576 cnmb.dst_relpath, iterpool)); 1577 1578 if (!dst_only) 1579 ++i; 1580 if (!src_only) 1581 ++j; 1582 1583 if (nmb->skip) /* Does parent now want a skip? */ 1584 break; 1585 } 1586 } 1587 1588 return SVN_NO_ERROR; 1589} 1590 1591static svn_error_t * 1592suitable_for_move(svn_wc__db_wcroot_t *wcroot, 1593 const char *local_relpath, 1594 apr_pool_t *scratch_pool) 1595{ 1596 svn_sqlite__stmt_t *stmt; 1597 svn_boolean_t have_row; 1598 svn_revnum_t revision; 1599 const char *repos_relpath; 1600 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1601 1602 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 1603 STMT_SELECT_BASE_NODE)); 1604 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); 1605 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1606 if (!have_row) 1607 return svn_error_trace(svn_sqlite__reset(stmt)); 1608 1609 revision = svn_sqlite__column_revnum(stmt, 4); 1610 repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); 1611 1612 SVN_ERR(svn_sqlite__reset(stmt)); 1613 1614 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 1615 STMT_SELECT_REPOS_PATH_REVISION)); 1616 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath)); 1617 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1618 while (have_row) 1619 { 1620 svn_revnum_t node_revision = svn_sqlite__column_revnum(stmt, 2); 1621 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL); 1622 1623 svn_pool_clear(iterpool); 1624 1625 relpath = svn_relpath_skip_ancestor(local_relpath, relpath); 1626 relpath = svn_relpath_join(repos_relpath, relpath, iterpool); 1627 1628 if (revision != node_revision) 1629 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 1630 svn_sqlite__reset(stmt), 1631 _("Cannot apply update because move source " 1632 "%s' is a mixed-revision working copy"), 1633 path_for_error_message(wcroot, local_relpath, 1634 scratch_pool)); 1635 1636 if (strcmp(relpath, svn_sqlite__column_text(stmt, 1, NULL))) 1637 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, 1638 svn_sqlite__reset(stmt), 1639 _("Cannot apply update because move source " 1640 "'%s' is a switched subtree"), 1641 path_for_error_message(wcroot, 1642 local_relpath, 1643 scratch_pool)); 1644 1645 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1646 } 1647 SVN_ERR(svn_sqlite__reset(stmt)); 1648 1649 svn_pool_destroy(iterpool); 1650 1651 return SVN_NO_ERROR; 1652} 1653 1654/* The body of svn_wc__db_update_moved_away_conflict_victim(), which see. 1655 */ 1656static svn_error_t * 1657update_moved_away_conflict_victim(svn_revnum_t *old_rev, 1658 svn_revnum_t *new_rev, 1659 svn_wc__db_t *db, 1660 svn_wc__db_wcroot_t *wcroot, 1661 const char *local_relpath, 1662 const char *delete_relpath, 1663 svn_wc_operation_t operation, 1664 svn_wc_conflict_action_t action, 1665 svn_wc_conflict_reason_t reason, 1666 svn_cancel_func_t cancel_func, 1667 void *cancel_baton, 1668 apr_pool_t *scratch_pool) 1669{ 1670 update_move_baton_t umb = { NULL }; 1671 const char *src_relpath, *dst_relpath; 1672 svn_wc_conflict_version_t old_version; 1673 svn_wc_conflict_version_t new_version; 1674 apr_int64_t repos_id; 1675 node_move_baton_t nmb = { 0 }; 1676 1677 SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath)); 1678 1679 /* Construct editor baton. */ 1680 1681 SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot, 1682 local_relpath, relpath_depth(delete_relpath), 1683 scratch_pool)); 1684 1685 SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL, 1686 wcroot, local_relpath, 1687 umb.src_op_depth, 1688 scratch_pool, scratch_pool)); 1689 1690 if (dst_relpath == NULL) 1691 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 1692 _("The node '%s' has not been moved away"), 1693 path_for_error_message(wcroot, local_relpath, 1694 scratch_pool)); 1695 1696 umb.dst_op_depth = relpath_depth(dst_relpath); 1697 1698 SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool)); 1699 SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool)); 1700 1701 1702 SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_version.node_kind, 1703 &new_version.peg_rev, 1704 &new_version.path_in_repos, &repos_id, 1705 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1706 NULL, 1707 wcroot, src_relpath, umb.src_op_depth, 1708 scratch_pool, scratch_pool)); 1709 1710 SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url, 1711 &new_version.repos_uuid, 1712 wcroot, repos_id, 1713 scratch_pool)); 1714 1715 SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind, 1716 &old_version.peg_rev, 1717 &old_version.path_in_repos, &repos_id, 1718 NULL, NULL, NULL, NULL, NULL, NULL, NULL, 1719 NULL, 1720 wcroot, dst_relpath, umb.dst_op_depth, 1721 scratch_pool, scratch_pool)); 1722 1723 SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url, 1724 &old_version.repos_uuid, 1725 wcroot, repos_id, 1726 scratch_pool)); 1727 *old_rev = old_version.peg_rev; 1728 *new_rev = new_version.peg_rev; 1729 1730 umb.operation = operation; 1731 umb.old_version= &old_version; 1732 umb.new_version= &new_version; 1733 umb.db = db; 1734 umb.wcroot = wcroot; 1735 umb.cancel_func = cancel_func; 1736 umb.cancel_baton = cancel_baton; 1737 1738 if (umb.src_op_depth == 0) 1739 SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool)); 1740 1741 /* Create a new, and empty, list for notification information. */ 1742 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, 1743 STMT_CREATE_UPDATE_MOVE_LIST)); 1744 1745 /* Drive the editor... */ 1746 1747 nmb.umb = &umb; 1748 nmb.src_relpath = src_relpath; 1749 nmb.dst_relpath = dst_relpath; 1750 /* nmb.shadowed = FALSE; */ 1751 /* nmb.edited = FALSE; */ 1752 /* nmb.skip_children = FALSE; */ 1753 1754 /* We walk the move source (i.e. the post-update tree), comparing each node 1755 * with the equivalent node at the move destination and applying the update 1756 * to nodes at the move destination. */ 1757 SVN_ERR(update_moved_away_node(&nmb, wcroot, src_relpath, dst_relpath, 1758 scratch_pool)); 1759 1760 SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath, 1761 umb.src_op_depth, 1762 dst_relpath, NULL, NULL, 1763 scratch_pool)); 1764 1765 return SVN_NO_ERROR; 1766} 1767 1768 1769svn_error_t * 1770svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db, 1771 const char *local_abspath, 1772 const char *delete_op_abspath, 1773 svn_wc_operation_t operation, 1774 svn_wc_conflict_action_t action, 1775 svn_wc_conflict_reason_t reason, 1776 svn_cancel_func_t cancel_func, 1777 void *cancel_baton, 1778 svn_wc_notify_func2_t notify_func, 1779 void *notify_baton, 1780 apr_pool_t *scratch_pool) 1781{ 1782 svn_wc__db_wcroot_t *wcroot; 1783 svn_revnum_t old_rev, new_rev; 1784 const char *local_relpath; 1785 const char *delete_relpath; 1786 1787 /* ### Check for mixed-rev src or dst? */ 1788 1789 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, 1790 db, local_abspath, 1791 scratch_pool, scratch_pool)); 1792 VERIFY_USABLE_WCROOT(wcroot); 1793 1794 delete_relpath 1795 = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath); 1796 1797 SVN_WC__DB_WITH_TXN( 1798 update_moved_away_conflict_victim( 1799 &old_rev, &new_rev, 1800 db, wcroot, local_relpath, delete_relpath, 1801 operation, action, reason, 1802 cancel_func, cancel_baton, 1803 scratch_pool), 1804 wcroot); 1805 1806 /* Send all queued up notifications. */ 1807 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev, 1808 notify_func, notify_baton, 1809 scratch_pool)); 1810 if (notify_func) 1811 { 1812 svn_wc_notify_t *notify; 1813 1814 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath, 1815 local_relpath, 1816 scratch_pool), 1817 svn_wc_notify_update_completed, 1818 scratch_pool); 1819 notify->kind = svn_node_none; 1820 notify->content_state = svn_wc_notify_state_inapplicable; 1821 notify->prop_state = svn_wc_notify_state_inapplicable; 1822 notify->revision = new_rev; 1823 notify_func(notify_baton, notify, scratch_pool); 1824 } 1825 1826 1827 return SVN_NO_ERROR; 1828} 1829 1830/* Set *CAN_BUMP to TRUE if DEPTH is sufficient to cover the entire 1831 tree LOCAL_RELPATH at OP_DEPTH, to FALSE otherwise. */ 1832static svn_error_t * 1833depth_sufficient_to_bump(svn_boolean_t *can_bump, 1834 svn_wc__db_wcroot_t *wcroot, 1835 const char *local_relpath, 1836 int op_depth, 1837 svn_depth_t depth, 1838 apr_pool_t *scratch_pool) 1839{ 1840 svn_sqlite__stmt_t *stmt; 1841 svn_boolean_t have_row; 1842 1843 switch (depth) 1844 { 1845 case svn_depth_infinity: 1846 *can_bump = TRUE; 1847 return SVN_NO_ERROR; 1848 1849 case svn_depth_empty: 1850 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 1851 STMT_SELECT_OP_DEPTH_CHILDREN)); 1852 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, 1853 local_relpath, op_depth)); 1854 break; 1855 1856 case svn_depth_files: 1857 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 1858 STMT_SELECT_HAS_NON_FILE_CHILDREN)); 1859 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, 1860 local_relpath, op_depth)); 1861 break; 1862 1863 case svn_depth_immediates: 1864 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 1865 STMT_SELECT_HAS_GRANDCHILDREN)); 1866 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, 1867 local_relpath, op_depth)); 1868 break; 1869 default: 1870 SVN_ERR_MALFUNCTION(); 1871 } 1872 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 1873 SVN_ERR(svn_sqlite__reset(stmt)); 1874 1875 *can_bump = !have_row; 1876 return SVN_NO_ERROR; 1877} 1878 1879/* Mark a move-edit conflict on MOVE_SRC_ROOT_RELPATH. */ 1880static svn_error_t * 1881bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot, 1882 const char *move_src_root_relpath, 1883 int src_op_depth, 1884 const char *move_src_op_root_relpath, 1885 const char *move_dst_op_root_relpath, 1886 svn_wc__db_t *db, 1887 apr_pool_t *scratch_pool) 1888{ 1889 apr_int64_t repos_id; 1890 const char *repos_root_url; 1891 const char *repos_uuid; 1892 const char *old_repos_relpath; 1893 const char *new_repos_relpath; 1894 svn_revnum_t old_rev; 1895 svn_revnum_t new_rev; 1896 svn_node_kind_t old_kind; 1897 svn_node_kind_t new_kind; 1898 svn_wc_conflict_version_t *old_version; 1899 svn_wc_conflict_version_t *new_version; 1900 svn_skel_t *conflict; 1901 1902 /* Verify precondition: We are allowed to set a tree conflict here. */ 1903 SVN_ERR(verify_write_lock(wcroot, move_src_root_relpath, scratch_pool)); 1904 1905 /* Read new (post-update) information from the new move source BASE node. */ 1906 SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_kind, &new_rev, 1907 &new_repos_relpath, &repos_id, 1908 NULL, NULL, NULL, NULL, NULL, 1909 NULL, NULL, NULL, 1910 wcroot, move_src_op_root_relpath, 1911 src_op_depth, scratch_pool, scratch_pool)); 1912 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, 1913 wcroot, repos_id, scratch_pool)); 1914 1915 /* Read old (pre-update) information from the move destination node. 1916 1917 This potentially touches nodes that aren't locked by us, but that is not 1918 a problem because we have a SQLite write lock here, and all sqlite 1919 operations that affect move stability use a sqlite lock as well. 1920 (And affecting the move itself requires a write lock on the node that 1921 we do own the lock for: the move source) 1922 */ 1923 SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_kind, &old_rev, 1924 &old_repos_relpath, NULL, NULL, NULL, 1925 NULL, NULL, NULL, NULL, NULL, NULL, 1926 wcroot, move_dst_op_root_relpath, 1927 relpath_depth(move_dst_op_root_relpath), 1928 scratch_pool, scratch_pool)); 1929 1930 if (strcmp(move_src_root_relpath, move_src_op_root_relpath)) 1931 { 1932 /* We have information for the op-root, but need it for the node that 1933 we are putting the tree conflict on. Luckily we know that we have 1934 a clean BASE */ 1935 1936 const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath, 1937 move_src_root_relpath); 1938 1939 old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath, 1940 scratch_pool); 1941 new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath, 1942 scratch_pool); 1943 } 1944 1945 old_version = svn_wc_conflict_version_create2( 1946 repos_root_url, repos_uuid, old_repos_relpath, old_rev, 1947 old_kind, scratch_pool); 1948 new_version = svn_wc_conflict_version_create2( 1949 repos_root_url, repos_uuid, new_repos_relpath, new_rev, 1950 new_kind, scratch_pool); 1951 1952 SVN_ERR(create_tree_conflict(&conflict, wcroot, move_src_root_relpath, 1953 move_dst_op_root_relpath, 1954 db, old_version, new_version, 1955 svn_wc_operation_update, 1956 old_kind, new_kind, 1957 old_repos_relpath, 1958 svn_wc_conflict_reason_moved_away, 1959 svn_wc_conflict_action_edit, 1960 move_src_op_root_relpath, 1961 scratch_pool, scratch_pool)); 1962 1963 SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db, 1964 svn_wc_notify_tree_conflict, 1965 new_kind, 1966 svn_wc_notify_state_inapplicable, 1967 svn_wc_notify_state_inapplicable, 1968 conflict, NULL, scratch_pool)); 1969 1970 return SVN_NO_ERROR; 1971} 1972 1973/* Checks if SRC_RELPATH is within BUMP_DEPTH from BUMP_ROOT. Sets 1974 * *SKIP to TRUE if the node should be skipped, otherwise to FALSE. 1975 * Sets *SRC_DEPTH to the remaining depth at SRC_RELPATH. 1976 */ 1977static svn_error_t * 1978check_bump_layer(svn_boolean_t *skip, 1979 svn_depth_t *src_depth, 1980 const char *bump_root, 1981 svn_depth_t bump_depth, 1982 const char *src_relpath, 1983 svn_node_kind_t src_kind, 1984 apr_pool_t *scratch_pool) 1985{ 1986 const char *relpath; 1987 1988 *skip = FALSE; 1989 *src_depth = bump_depth; 1990 1991 relpath = svn_relpath_skip_ancestor(bump_root, src_relpath); 1992 1993 if (!relpath) 1994 *skip = TRUE; 1995 1996 if (bump_depth == svn_depth_infinity) 1997 return SVN_NO_ERROR; 1998 1999 if (relpath && *relpath == '\0') 2000 return SVN_NO_ERROR; 2001 2002 switch (bump_depth) 2003 { 2004 case svn_depth_empty: 2005 *skip = TRUE; 2006 break; 2007 2008 case svn_depth_files: 2009 if (src_kind != svn_node_file) 2010 { 2011 *skip = TRUE; 2012 break; 2013 } 2014 /* Fallthrough */ 2015 case svn_depth_immediates: 2016 if (!relpath || relpath_depth(relpath) > 1) 2017 *skip = TRUE; 2018 2019 *src_depth = svn_depth_empty; 2020 break; 2021 default: 2022 SVN_ERR_MALFUNCTION(); 2023 } 2024 2025 return SVN_NO_ERROR; 2026} 2027 2028/* The guts of bump_moved_away: Determines if a move can be bumped to match 2029 * the move origin and if so performs this bump. 2030 */ 2031static svn_error_t * 2032bump_moved_layer(svn_boolean_t *recurse, 2033 svn_wc__db_wcroot_t *wcroot, 2034 const char *local_relpath, 2035 int op_depth, 2036 const char *src_relpath, 2037 int src_del_depth, 2038 svn_depth_t src_depth, 2039 const char *dst_relpath, 2040 svn_wc__db_t *db, 2041 apr_pool_t *scratch_pool) 2042{ 2043 svn_sqlite__stmt_t *stmt; 2044 svn_boolean_t have_row; 2045 svn_skel_t *conflict; 2046 svn_boolean_t can_bump; 2047 const char *src_root_relpath; 2048 2049 SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool)); 2050 2051 *recurse = FALSE; 2052 2053 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 2054 STMT_HAS_LAYER_BETWEEN)); 2055 2056 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath, 2057 op_depth, src_del_depth)); 2058 2059 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2060 SVN_ERR(svn_sqlite__reset(stmt)); 2061 2062 if (have_row) 2063 return SVN_NO_ERROR; 2064 2065 if (op_depth == 0) 2066 SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath, 2067 op_depth, src_depth, scratch_pool)); 2068 else 2069 /* Having chosen to bump an entire BASE tree move we 2070 always have sufficient depth to bump subtree moves. */ 2071 can_bump = TRUE; 2072 2073 /* Are we allowed to bump */ 2074 if (can_bump) 2075 { 2076 svn_boolean_t locked; 2077 2078 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, 2079 dst_relpath, 2080 FALSE, scratch_pool)); 2081 2082 if (!locked) 2083 can_bump = FALSE; 2084 } 2085 2086 src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth, 2087 scratch_pool); 2088 2089 if (!can_bump) 2090 { 2091 SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth, 2092 src_root_relpath, dst_relpath, 2093 db, scratch_pool)); 2094 2095 return SVN_NO_ERROR; 2096 } 2097 2098 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, 2099 wcroot, src_root_relpath, 2100 scratch_pool, scratch_pool)); 2101 2102 /* ### TODO: check this is the right sort of tree-conflict? */ 2103 if (!conflict) 2104 { 2105 /* ### TODO: verify moved_here? */ 2106 2107 SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool)); 2108 2109 SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, 2110 src_relpath, op_depth, 2111 dst_relpath, NULL, NULL, 2112 scratch_pool)); 2113 2114 *recurse = TRUE; 2115 } 2116 2117 return SVN_NO_ERROR; 2118} 2119 2120/* Internal storage for bump_moved_away() */ 2121struct bump_pair_t 2122{ 2123 const char *src_relpath; 2124 const char *dst_relpath; 2125 int src_del_op_depth; 2126 svn_node_kind_t src_kind; 2127}; 2128 2129/* Bump moves of LOCAL_RELPATH and all its descendants that were 2130 originally below LOCAL_RELPATH at op-depth OP_DEPTH. 2131 */ 2132static svn_error_t * 2133bump_moved_away(svn_wc__db_wcroot_t *wcroot, 2134 const char *local_relpath, 2135 int op_depth, 2136 svn_depth_t depth, 2137 svn_wc__db_t *db, 2138 apr_pool_t *scratch_pool) 2139{ 2140 svn_sqlite__stmt_t *stmt; 2141 svn_boolean_t have_row; 2142 apr_pool_t *iterpool; 2143 int i; 2144 apr_array_header_t *pairs = apr_array_make(scratch_pool, 32, 2145 sizeof(struct bump_pair_t*)); 2146 2147 /* Build an array, as we can't execute the same Sqlite query recursively */ 2148 iterpool = svn_pool_create(scratch_pool); 2149 2150 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 2151 STMT_SELECT_MOVED_PAIR3)); 2152 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 2153 op_depth)); 2154 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2155 while(have_row) 2156 { 2157 struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp)); 2158 2159 bp->src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool); 2160 bp->dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool); 2161 bp->src_del_op_depth = svn_sqlite__column_int(stmt, 2); 2162 bp->src_kind = svn_sqlite__column_token(stmt, 3, kind_map); 2163 2164 APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp; 2165 2166 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2167 } 2168 2169 SVN_ERR(svn_sqlite__reset(stmt)); 2170 2171 for (i = 0; i < pairs->nelts; i++) 2172 { 2173 struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *); 2174 svn_boolean_t skip; 2175 svn_depth_t src_wc_depth; 2176 2177 svn_pool_clear(iterpool); 2178 2179 2180 SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth, 2181 bp->src_relpath, bp->src_kind, iterpool)); 2182 2183 if (!skip) 2184 { 2185 svn_boolean_t recurse; 2186 2187 SVN_ERR(bump_moved_layer(&recurse, wcroot, 2188 local_relpath, op_depth, 2189 bp->src_relpath, bp->src_del_op_depth, 2190 src_wc_depth, bp->dst_relpath, 2191 db, iterpool)); 2192 2193 if (recurse) 2194 SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath, 2195 relpath_depth(bp->dst_relpath), 2196 depth, db, iterpool)); 2197 } 2198 } 2199 2200 svn_pool_destroy(iterpool); 2201 2202 return SVN_NO_ERROR; 2203} 2204 2205svn_error_t * 2206svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot, 2207 const char *local_relpath, 2208 svn_depth_t depth, 2209 svn_wc__db_t *db, 2210 apr_pool_t *scratch_pool) 2211{ 2212 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, 2213 STMT_CREATE_UPDATE_MOVE_LIST)); 2214 2215 if (local_relpath[0] != '\0') 2216 { 2217 const char *move_dst_op_root_relpath; 2218 const char *move_src_root_relpath, *delete_relpath; 2219 svn_error_t *err; 2220 2221 /* Is the root of the update moved away? (Impossible for the wcroot) */ 2222 2223 err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath, 2224 &move_dst_op_root_relpath, 2225 &delete_relpath, 2226 wcroot, local_relpath, 2227 0 /* BASE */, 2228 scratch_pool, scratch_pool); 2229 2230 if (err) 2231 { 2232 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2233 return svn_error_trace(err); 2234 2235 svn_error_clear(err); 2236 } 2237 else if (move_src_root_relpath) 2238 { 2239 if (strcmp(move_src_root_relpath, local_relpath)) 2240 { 2241 /* An ancestor of the path that was updated is moved away. 2242 2243 If we have a lock on that ancestor, we can mark a tree 2244 conflict on it, if we don't we ignore this case. A future 2245 update of the ancestor will handle this. */ 2246 svn_boolean_t locked; 2247 2248 SVN_ERR(svn_wc__db_wclock_owns_lock_internal( 2249 &locked, wcroot, 2250 move_src_root_relpath, 2251 FALSE, scratch_pool)); 2252 2253 if (locked) 2254 { 2255 SVN_ERR(bump_mark_tree_conflict(wcroot, 2256 move_src_root_relpath, 0, 2257 delete_relpath, 2258 move_dst_op_root_relpath, 2259 db, scratch_pool)); 2260 } 2261 return SVN_NO_ERROR; 2262 } 2263 } 2264 } 2265 2266 SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool)); 2267 2268 return SVN_NO_ERROR; 2269} 2270 2271/* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION 2272 * to reflect the tree conflict on the victim SRC_ABSPATH in DB. 2273 * 2274 * If SRC_ABSPATH is not a tree-conflict victim, return an error. 2275 */ 2276static svn_error_t * 2277fetch_conflict_details(int *src_op_depth, 2278 svn_wc_operation_t *operation, 2279 svn_wc_conflict_action_t *action, 2280 svn_wc_conflict_version_t **left_version, 2281 svn_wc_conflict_version_t **right_version, 2282 svn_wc__db_wcroot_t *wcroot, 2283 svn_wc__db_t *db, 2284 const char *local_relpath, 2285 const svn_skel_t *conflict_skel, 2286 apr_pool_t *result_pool, 2287 apr_pool_t *scratch_pool) 2288{ 2289 const apr_array_header_t *locations; 2290 svn_boolean_t text_conflicted; 2291 svn_boolean_t prop_conflicted; 2292 svn_boolean_t tree_conflicted; 2293 const char *move_src_op_root_abspath; 2294 svn_wc_conflict_reason_t reason; 2295 const char *local_abspath = svn_dirent_join(wcroot->abspath, local_relpath, 2296 scratch_pool); 2297 2298 if (!conflict_skel) 2299 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 2300 _("'%s' is not in conflict"), 2301 path_for_error_message(wcroot, local_relpath, 2302 scratch_pool)); 2303 2304 SVN_ERR(svn_wc__conflict_read_info(operation, &locations, 2305 &text_conflicted, &prop_conflicted, 2306 &tree_conflicted, 2307 db, local_abspath, 2308 conflict_skel, result_pool, 2309 scratch_pool)); 2310 2311 if (text_conflicted || prop_conflicted || !tree_conflicted) 2312 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL, 2313 _("'%s' is not a valid tree-conflict victim"), 2314 path_for_error_message(wcroot, local_relpath, 2315 scratch_pool)); 2316 2317 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, 2318 action, 2319 &move_src_op_root_abspath, 2320 db, local_abspath, 2321 conflict_skel, result_pool, 2322 scratch_pool)); 2323 2324 if (reason == svn_wc_conflict_reason_moved_away) 2325 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, 2326 _("'%s' is already a moved away tree-conflict"), 2327 path_for_error_message(wcroot, local_relpath, 2328 scratch_pool)); 2329 2330 if (left_version) 2331 { 2332 if (locations && locations->nelts > 0) 2333 *left_version = APR_ARRAY_IDX(locations, 0, 2334 svn_wc_conflict_version_t *); 2335 else 2336 *left_version = NULL; 2337 } 2338 2339 if (right_version) 2340 { 2341 if (locations && locations->nelts > 1) 2342 *right_version = APR_ARRAY_IDX(locations, 1, 2343 svn_wc_conflict_version_t *); 2344 else 2345 *right_version = NULL; 2346 } 2347 2348 { 2349 int del_depth = relpath_depth(local_relpath); 2350 2351 if (move_src_op_root_abspath) 2352 del_depth = relpath_depth( 2353 svn_dirent_skip_ancestor(wcroot->abspath, 2354 move_src_op_root_abspath)); 2355 2356 SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth, 2357 scratch_pool)); 2358 } 2359 2360 return SVN_NO_ERROR; 2361} 2362 2363svn_error_t * 2364svn_wc__db_op_raise_moved_away_internal( 2365 svn_wc__db_wcroot_t *wcroot, 2366 const char *local_relpath, 2367 int src_op_depth, 2368 svn_wc__db_t *db, 2369 svn_wc_operation_t operation, 2370 svn_wc_conflict_action_t action, 2371 const svn_wc_conflict_version_t *old_version, 2372 const svn_wc_conflict_version_t *new_version, 2373 apr_pool_t *scratch_pool) 2374{ 2375 svn_sqlite__stmt_t *stmt; 2376 svn_boolean_t have_row; 2377 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2378 2379 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, 2380 STMT_CREATE_UPDATE_MOVE_LIST)); 2381 2382 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 2383 STMT_SELECT_MOVED_DESCENDANTS_SRC)); 2384 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 2385 src_op_depth)); 2386 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2387 while(have_row) 2388 { 2389 svn_error_t *err; 2390 int delete_op_depth = svn_sqlite__column_int(stmt, 0); 2391 const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL); 2392 svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map); 2393 const char *src_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL); 2394 const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL); 2395 svn_skel_t *conflict; 2396 svn_pool_clear(iterpool); 2397 2398 SVN_ERR_ASSERT(src_repos_relpath != NULL); 2399 2400 err = create_tree_conflict(&conflict, wcroot, src_relpath, dst_relpath, 2401 db, old_version, new_version, operation, 2402 src_kind /* ### old kind */, 2403 src_kind /* ### new kind */, 2404 src_repos_relpath, 2405 svn_wc_conflict_reason_moved_away, 2406 action, 2407 svn_relpath_prefix(src_relpath, 2408 delete_op_depth, 2409 iterpool), 2410 iterpool, iterpool); 2411 2412 if (!err) 2413 err = update_move_list_add(wcroot, src_relpath, db, 2414 svn_wc_notify_tree_conflict, 2415 src_kind, 2416 svn_wc_notify_state_inapplicable, 2417 svn_wc_notify_state_inapplicable, 2418 conflict, NULL, scratch_pool); 2419 2420 if (err) 2421 return svn_error_compose_create(err, svn_sqlite__reset(stmt)); 2422 2423 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2424 } 2425 SVN_ERR(svn_sqlite__reset(stmt)); 2426 2427 svn_pool_destroy(iterpool); 2428 2429 return SVN_NO_ERROR; 2430} 2431 2432svn_error_t * 2433svn_wc__db_op_raise_moved_away(svn_wc__db_t *db, 2434 const char *local_abspath, 2435 svn_wc_notify_func2_t notify_func, 2436 void *notify_baton, 2437 apr_pool_t *scratch_pool) 2438{ 2439 svn_wc__db_wcroot_t *wcroot; 2440 const char *local_relpath; 2441 svn_wc_operation_t operation; 2442 svn_wc_conflict_action_t action; 2443 svn_wc_conflict_version_t *left_version, *right_version; 2444 int move_src_op_depth; 2445 svn_skel_t *conflict; 2446 2447 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, 2448 db, local_abspath, 2449 scratch_pool, scratch_pool)); 2450 VERIFY_USABLE_WCROOT(wcroot); 2451 2452 SVN_WC__DB_WITH_TXN4( 2453 svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, 2454 wcroot, local_relpath, 2455 scratch_pool, scratch_pool), 2456 fetch_conflict_details(&move_src_op_depth, 2457 &operation, &action, 2458 &left_version, &right_version, 2459 wcroot, db, local_relpath, conflict, 2460 scratch_pool, scratch_pool), 2461 svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db, 2462 FALSE, FALSE, TRUE, 2463 NULL, scratch_pool), 2464 svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath, 2465 move_src_op_depth, 2466 db, operation, action, 2467 left_version, right_version, 2468 scratch_pool), 2469 wcroot); 2470 2471 /* These version numbers are valid for update/switch notifications 2472 only! */ 2473 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, 2474 (left_version 2475 ? left_version->peg_rev 2476 : SVN_INVALID_REVNUM), 2477 (right_version 2478 ? right_version->peg_rev 2479 : SVN_INVALID_REVNUM), 2480 notify_func, notify_baton, 2481 scratch_pool)); 2482 2483 return SVN_NO_ERROR; 2484} 2485 2486static svn_error_t * 2487break_moved_away(svn_wc__db_wcroot_t *wcroot, 2488 svn_wc__db_t *db, 2489 const char *local_relpath, 2490 int parent_src_op_depth, 2491 apr_pool_t *scratch_pool) 2492{ 2493 svn_sqlite__stmt_t *stmt; 2494 svn_boolean_t have_row; 2495 apr_pool_t *iterpool; 2496 svn_error_t *err = NULL; 2497 2498 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, 2499 STMT_CREATE_UPDATE_MOVE_LIST)); 2500 2501 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 2502 STMT_SELECT_MOVED_DESCENDANTS_SRC)); 2503 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 2504 parent_src_op_depth)); 2505 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2506 2507 iterpool = svn_pool_create(scratch_pool); 2508 while (have_row) 2509 { 2510 int src_op_depth = svn_sqlite__column_int(stmt, 0); 2511 const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL); 2512 svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map); 2513 const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL); 2514 2515 svn_pool_clear(iterpool); 2516 2517 err = verify_write_lock(wcroot, src_relpath, iterpool); 2518 2519 if (!err) 2520 err = verify_write_lock(wcroot, dst_relpath, iterpool); 2521 2522 if (err) 2523 break; 2524 2525 err = svn_error_trace( 2526 svn_wc__db_op_break_move_internal(wcroot, 2527 src_relpath, src_op_depth, 2528 dst_relpath, NULL, iterpool)); 2529 2530 if (err) 2531 break; 2532 2533 err = svn_error_trace( 2534 update_move_list_add(wcroot, src_relpath, db, 2535 svn_wc_notify_move_broken, 2536 src_kind, 2537 svn_wc_notify_state_inapplicable, 2538 svn_wc_notify_state_inapplicable, 2539 NULL, NULL, scratch_pool)); 2540 2541 if (err) 2542 break; 2543 2544 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2545 } 2546 svn_pool_destroy(iterpool); 2547 2548 return svn_error_compose_create(err, svn_sqlite__reset(stmt)); 2549} 2550 2551svn_error_t * 2552svn_wc__db_op_break_moved_away(svn_wc__db_t *db, 2553 const char *local_abspath, 2554 const char *del_op_root_abspath, 2555 svn_boolean_t mark_tc_resolved, 2556 svn_wc_notify_func2_t notify_func, 2557 void *notify_baton, 2558 apr_pool_t *scratch_pool) 2559{ 2560 svn_wc__db_wcroot_t *wcroot; 2561 const char *local_relpath; 2562 const char *del_relpath; 2563 int src_op_depth; 2564 2565 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, 2566 db, local_abspath, 2567 scratch_pool, scratch_pool)); 2568 VERIFY_USABLE_WCROOT(wcroot); 2569 2570 if (del_op_root_abspath) 2571 del_relpath = svn_dirent_skip_ancestor(wcroot->abspath, 2572 del_op_root_abspath); 2573 else 2574 del_relpath = NULL; 2575 2576 2577 SVN_WC__DB_WITH_TXN4( 2578 find_src_op_depth(&src_op_depth, wcroot, local_relpath, 2579 del_relpath ? relpath_depth(del_relpath) 2580 : relpath_depth(local_relpath), 2581 scratch_pool), 2582 break_moved_away(wcroot, db, local_relpath, src_op_depth, 2583 scratch_pool), 2584 mark_tc_resolved 2585 ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db, 2586 FALSE, FALSE, TRUE, 2587 NULL, scratch_pool) 2588 : SVN_NO_ERROR, 2589 SVN_NO_ERROR, 2590 wcroot); 2591 2592 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, 2593 SVN_INVALID_REVNUM, 2594 SVN_INVALID_REVNUM, 2595 notify_func, notify_baton, 2596 scratch_pool)); 2597 return SVN_NO_ERROR; 2598} 2599 2600static svn_error_t * 2601required_lock_for_resolve(const char **required_relpath, 2602 svn_wc__db_wcroot_t *wcroot, 2603 const char *local_relpath, 2604 apr_pool_t *result_pool, 2605 apr_pool_t *scratch_pool) 2606{ 2607 svn_sqlite__stmt_t *stmt; 2608 svn_boolean_t have_row; 2609 2610 *required_relpath = local_relpath; 2611 2612 /* This simply looks for all moves out of the LOCAL_RELPATH tree. We 2613 could attempt to limit it to only those moves that are going to 2614 be resolved but that would require second guessing the resolver. 2615 This simple algorithm is sufficient although it may give a 2616 strictly larger/deeper lock than necessary. */ 2617 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, 2618 STMT_SELECT_MOVED_OUTSIDE)); 2619 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0)); 2620 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2621 2622 while (have_row) 2623 { 2624 const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1, 2625 NULL); 2626 2627 *required_relpath 2628 = svn_relpath_get_longest_ancestor(*required_relpath, 2629 move_dst_relpath, 2630 scratch_pool); 2631 2632 SVN_ERR(svn_sqlite__step(&have_row, stmt)); 2633 } 2634 SVN_ERR(svn_sqlite__reset(stmt)); 2635 2636 *required_relpath = apr_pstrdup(result_pool, *required_relpath); 2637 2638 return SVN_NO_ERROR; 2639} 2640 2641svn_error_t * 2642svn_wc__required_lock_for_resolve(const char **required_abspath, 2643 svn_wc__db_t *db, 2644 const char *local_abspath, 2645 apr_pool_t *result_pool, 2646 apr_pool_t *scratch_pool) 2647{ 2648 svn_wc__db_wcroot_t *wcroot; 2649 const char *local_relpath; 2650 const char *required_relpath; 2651 2652 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, 2653 db, local_abspath, 2654 scratch_pool, scratch_pool)); 2655 VERIFY_USABLE_WCROOT(wcroot); 2656 2657 SVN_WC__DB_WITH_TXN( 2658 required_lock_for_resolve(&required_relpath, wcroot, local_relpath, 2659 scratch_pool, scratch_pool), 2660 wcroot); 2661 2662 *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath, 2663 result_pool); 2664 2665 return SVN_NO_ERROR; 2666} 2667