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