2 * wc_db_update_move.c : updating moves during tree-conflict resolution
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
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).
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*
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.
38 * Both the driver and receiver components of the editor are implemented
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]
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.
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.
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.
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.
77 #define SVN_WC__I_AM_WC_DB
81 #include "svn_checksum.h"
82 #include "svn_dirent_uri.h"
83 #include "svn_error.h"
86 #include "svn_props.h"
87 #include "svn_pools.h"
88 #include "svn_sorts.h"
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"
97 #include "wc_db_private.h"
98 #include "wc-queries.h"
99 #include "conflicts.h"
100 #include "workqueue.h"
101 #include "token-map.h"
103 /* Helper functions */
104 /* Return the absolute path, in local path style, of LOCAL_RELPATH
107 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
108 const char *local_relpath,
109 apr_pool_t *result_pool)
111 const char *local_abspath
112 = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
114 return svn_dirent_local_style(local_abspath, result_pool);
117 /* Ensure that there is a working copy lock for LOCAL_RELPATH in WCROOT */
119 verify_write_lock(svn_wc__db_wcroot_t *wcroot,
120 const char *local_relpath,
121 apr_pool_t *scratch_pool)
123 svn_boolean_t locked;
125 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, local_relpath,
126 FALSE, scratch_pool));
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,
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.
143 To work around this problem we locate the layer below this path, and use
144 that to pinpoint whatever is moved.
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.
150 find_src_op_depth(int *src_op_depth,
151 svn_wc__db_wcroot_t *wcroot,
152 const char *src_relpath,
154 apr_pool_t *scratch_pool)
156 svn_sqlite__stmt_t *stmt;
157 svn_boolean_t have_row;
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));
164 SVN_ERR(svn_sqlite__step(&have_row, stmt));
166 *src_op_depth = svn_sqlite__column_int(stmt, 0);
167 SVN_ERR(svn_sqlite__reset(stmt));
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,
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.
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.
190 typedef struct update_move_baton_t {
192 svn_wc__db_wcroot_t *wcroot;
197 svn_wc_operation_t operation;
198 svn_wc_conflict_version_t *old_version;
199 svn_wc_conflict_version_t *new_version;
201 svn_cancel_func_t cancel_func;
203 } update_move_baton_t;
205 /* Per node flags for tree conflict collection */
206 typedef struct node_move_baton_t
209 svn_boolean_t shadowed;
210 svn_boolean_t edited;
212 const char *src_relpath;
213 const char *dst_relpath;
215 update_move_baton_t *umb;
216 struct node_move_baton_t *pb;
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.
226 /* Add an entry to the notification list, and at the same time install
227 a conflict and/or work items. */
229 update_move_list_add(svn_wc__db_wcroot_t *wcroot,
230 const char *local_relpath,
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)
240 svn_sqlite__stmt_t *stmt;
244 svn_boolean_t tree_conflict;
246 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
248 db, wcroot->abspath, conflict,
249 scratch_pool, scratch_pool));
252 action = svn_wc_notify_tree_conflict;
253 content_state = svn_wc_notify_state_inapplicable;
254 prop_state = svn_wc_notify_state_inapplicable;
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));
266 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, conflict,
270 SVN_ERR(svn_wc__db_wq_add_internal(wcroot, work_item, scratch_pool));
275 /* Send all notifications stored in the notification list, and then
276 * remove the temporary database table. */
278 svn_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,
283 apr_pool_t *scratch_pool)
285 svn_sqlite__stmt_t *stmt;
289 apr_pool_t *iterpool;
290 svn_boolean_t have_row;
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));
296 iterpool = svn_pool_create(scratch_pool);
299 const char *local_relpath;
300 svn_wc_notify_action_t action;
301 svn_wc_notify_t *notify;
303 svn_pool_clear(iterpool);
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,
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);
318 SVN_ERR(svn_sqlite__step(&have_row, stmt));
320 svn_pool_destroy(iterpool);
321 SVN_ERR(svn_sqlite__reset(stmt));
324 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
325 STMT_FINALIZE_UPDATE_MOVE));
326 SVN_ERR(svn_sqlite__step_done(stmt));
331 /* Create a tree-conflict for recording on LOCAL_RELPATH if such
332 a tree-conflict does not already exist. */
334 create_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,
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)
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)
359 const char *old_repos_relpath_part
361 ? svn_relpath_skip_ancestor(old_version->path_in_repos,
364 const char *new_repos_relpath
365 = old_repos_relpath_part
366 ? svn_relpath_join(new_version->path_in_repos, old_repos_relpath_part,
370 if (!new_repos_relpath)
372 const char *child_relpath = svn_relpath_skip_ancestor(
375 SVN_ERR_ASSERT(child_relpath != NULL);
376 new_repos_relpath = svn_relpath_join(new_version->path_in_repos,
377 child_relpath, scratch_pool);
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);
387 svn_error_clear(err);
393 svn_wc_operation_t conflict_operation;
394 svn_boolean_t tree_conflicted;
396 SVN_ERR(svn_wc__conflict_read_info(&conflict_operation, NULL, NULL, NULL,
398 db, wcroot->abspath, conflict,
399 scratch_pool, scratch_pool));
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,
410 svn_wc_conflict_reason_t existing_reason;
411 svn_wc_conflict_action_t existing_action;
412 const char *existing_abspath;
414 SVN_ERR(svn_wc__conflict_read_tree_conflict(&existing_reason,
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,
427 return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
428 _("'%s' already in conflict"),
429 path_for_error_message(wcroot,
433 /* Already a suitable tree-conflict. */
434 *conflict_p = conflict;
439 conflict = svn_wc__conflict_skel_create(result_pool);
441 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
443 svn_dirent_join(wcroot->abspath, local_relpath,
447 move_src_op_root_abspath,
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);
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);
461 if (operation == svn_wc_operation_update)
463 SVN_ERR(svn_wc__conflict_skel_set_op_update(
464 conflict, conflict_old_version, conflict_new_version,
465 result_pool, scratch_pool));
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));
475 *conflict_p = conflict;
480 create_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)
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,
498 svn_relpath_join(nmb->umb->old_version->path_in_repos,
499 svn_relpath_skip_ancestor(dst_root_relpath,
505 return svn_error_trace(
506 create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath,
507 svn_relpath_prefix(dst_local_relpath,
511 umb->old_version, umb->new_version,
512 umb->operation, old_kind, new_kind,
514 reason, action, move_src_op_root_relpath,
515 result_pool, scratch_pool));
518 /* Checks if a specific local path is shadowed as seen from the move root.
519 Helper for update_moved_away_node() */
521 check_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)
527 svn_sqlite__stmt_t *stmt;
528 svn_boolean_t have_row;
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));
534 SVN_ERR(svn_sqlite__step(&have_row, stmt));
538 int op_depth = svn_sqlite__column_int(stmt, 0);
540 *shadowed = (op_depth > move_root_dst_op_depth);
544 SVN_ERR(svn_sqlite__reset(stmt));
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. */
553 mark_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)
559 update_move_baton_t *b = nmb->umb;
560 const char *move_dst_relpath;
561 svn_skel_t *conflict;
563 SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed);
567 if (old_kind == svn_node_none)
568 move_dst_relpath = NULL;
570 SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL,
571 b->wcroot, nmb->dst_relpath,
573 scratch_pool, scratch_pool));
575 SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath,
578 ? svn_wc_conflict_reason_moved_away
579 : svn_wc_conflict_reason_deleted),
580 action, move_dst_relpath
583 scratch_pool, scratch_pool));
585 SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db,
586 svn_wc_notify_tree_conflict,
588 svn_wc_notify_state_inapplicable,
589 svn_wc_notify_state_inapplicable,
590 conflict, NULL, scratch_pool));
596 mark_node_edited(node_move_baton_t *nmb,
597 apr_pool_t *scratch_pool)
604 SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
615 if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed))
617 svn_node_kind_t dst_kind, src_kind;
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));
626 SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, 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));
633 SVN_ERR(mark_tc_on_op_root(nmb,
635 svn_wc_conflict_action_edit,
643 mark_parent_edited(node_move_baton_t *nmb,
644 apr_pool_t *scratch_pool)
646 SVN_ERR_ASSERT(nmb && nmb->pb);
648 SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
657 tc_editor_add_directory(node_move_baton_t *nmb,
659 svn_node_kind_t old_kind,
661 apr_pool_t *scratch_pool)
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;
670 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
676 svn_wc__db_status_t status;
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,
684 scratch_pool, scratch_pool));
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;
693 reason = svn_wc_conflict_reason_replaced;
696 wc_kind = svn_node_none;
698 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
700 if (wc_kind == svn_node_none)
702 /* Check for unversioned tree-conflict */
703 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
706 if (!nmb->shadowed && wc_kind == old_kind)
707 wc_kind = svn_node_none; /* Node will be gone once we install */
709 if (wc_kind != svn_node_none
710 && (nmb->shadowed || wc_kind != old_kind)) /* replace */
712 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
713 old_kind, svn_node_dir,
715 (old_kind == svn_node_none)
716 ? svn_wc_conflict_action_add
717 : svn_wc_conflict_action_replace,
719 scratch_pool, scratch_pool));
724 SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath,
725 scratch_pool, scratch_pool));
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,
733 svn_wc_notify_state_inapplicable,
734 svn_wc_notify_state_inapplicable,
735 conflict, work_item, scratch_pool));
740 tc_editor_add_file(node_move_baton_t *nmb,
742 svn_node_kind_t old_kind,
743 const svn_checksum_t *checksum,
745 apr_pool_t *scratch_pool)
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;
754 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
760 svn_wc__db_status_t status;
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,
768 scratch_pool, scratch_pool));
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;
777 reason = svn_wc_conflict_reason_replaced;
780 wc_kind = svn_node_none;
782 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
784 if (wc_kind == svn_node_none)
786 /* Check for unversioned tree-conflict */
787 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
790 if (wc_kind != svn_node_none
791 && (nmb->shadowed || wc_kind != old_kind)) /* replace */
793 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
794 old_kind, svn_node_file,
796 (old_kind == svn_node_none)
797 ? svn_wc_conflict_action_add
798 : svn_wc_conflict_action_replace,
800 scratch_pool, scratch_pool));
805 /* Update working file. */
806 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
807 svn_dirent_join(b->wcroot->abspath,
811 FALSE /*FIXME: use_commit_times?*/,
812 TRUE /* record_file_info */,
813 scratch_pool, scratch_pool));
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,
821 svn_wc_notify_state_inapplicable,
822 svn_wc_notify_state_inapplicable,
823 conflict, work_item, scratch_pool));
827 /* All the info we need about one version of a working node. */
828 typedef struct working_node_version_t
830 svn_wc_conflict_version_t *location_and_kind;
832 const svn_checksum_t *checksum; /* for files only */
833 } working_node_version_t;
835 /* Return *WORK_ITEMS to create a conflict on LOCAL_ABSPATH. */
837 create_conflict_markers(svn_skel_t **work_items,
838 const char *local_abspath,
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)
850 svn_wc_conflict_version_t *original_version;
851 svn_wc_conflict_version_t *conflicted_version;
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;
861 part = svn_relpath_skip_ancestor(original_version->path_in_repos,
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;
869 if (operation == svn_wc_operation_update)
871 SVN_ERR(svn_wc__conflict_skel_set_op_update(
872 conflict_skel, original_version,
874 scratch_pool, scratch_pool));
878 SVN_ERR(svn_wc__conflict_skel_set_op_switch(
879 conflict_skel, original_version,
881 scratch_pool, scratch_pool));
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,
897 update_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)
908 apr_hash_t *new_actual_props;
909 apr_array_header_t *new_propchanges;
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.
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,
922 SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state,
924 b->db, svn_dirent_join(b->wcroot->abspath,
927 old_version->props, old_version->props,
928 *actual_props, *propchanges,
929 result_pool, scratch_pool));
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 .
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,
938 if (!new_propchanges->nelts)
939 new_actual_props = NULL;
941 /* Install the new actual props. */
942 SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath,
944 svn_wc__has_magic_property(
952 tc_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)
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,
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;
970 SVN_ERR(mark_node_edited(nmb, scratch_pool));
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)
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,
982 scratch_pool, scratch_pool));
986 old_version.location_and_kind = b->old_version;
987 new_version.location_and_kind = b->new_version;
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;
994 SVN_ERR(update_working_props(&prop_state, &conflict_skel,
995 &propchanges, &actual_props,
997 &old_version, &new_version,
998 scratch_pool, scratch_pool));
1000 if (prop_state == svn_wc_notify_state_conflicted)
1002 const char *move_dst_repos_relpath;
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,
1008 b->wcroot, dst_relpath,
1010 scratch_pool, scratch_pool));
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));
1020 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1021 svn_wc_notify_update_update,
1023 svn_wc_notify_state_inapplicable,
1025 conflict_skel, work_items, scratch_pool));
1027 return SVN_NO_ERROR;
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.
1033 * Merge the difference between OLD_VERSION and NEW_VERSION into
1034 * the working file at LOCAL_RELPATH.
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.
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.
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. */
1047 static svn_error_t *
1048 tc_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)
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,
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;
1072 SVN_ERR(mark_node_edited(nmb, scratch_pool));
1074 return SVN_NO_ERROR;
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)
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,
1084 scratch_pool, scratch_pool));
1088 old_version.location_and_kind = b->old_version;
1089 new_version.location_and_kind = b->new_version;
1091 old_version.checksum = old_checksum;
1092 old_version.props = old_props;
1093 new_version.checksum = new_checksum;
1094 new_version.props = new_props;
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));
1103 && !svn_checksum_match(new_version.checksum, old_version.checksum))
1105 svn_boolean_t is_locally_modified;
1107 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
1108 b->db, local_abspath,
1109 FALSE /* exact_comparison */,
1111 if (!is_locally_modified)
1113 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1116 FALSE /* FIXME: use_commit_times? */,
1117 TRUE /* record_file_info */,
1118 scratch_pool, scratch_pool));
1120 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1122 content_state = svn_wc_notify_state_changed;
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.
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,
1146 NULL, NULL, NULL, /* diff labels */
1148 FALSE, /* dry-run */
1149 NULL, /* diff3-cmd */
1150 NULL, /* merge options */
1152 b->cancel_func, b->cancel_baton,
1153 scratch_pool, scratch_pool));
1155 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1157 if (merge_outcome == svn_wc_merge_conflict)
1158 content_state = svn_wc_notify_state_conflicted;
1160 content_state = svn_wc_notify_state_merged;
1164 content_state = svn_wc_notify_state_unchanged;
1166 /* If there are any conflicts to be stored, convert them into work items
1170 const char *move_dst_repos_relpath;
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,
1176 b->wcroot, dst_relpath,
1178 scratch_pool, scratch_pool));
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));
1186 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1189 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1190 svn_wc_notify_update_update,
1194 conflict_skel, work_items, scratch_pool));
1196 return SVN_NO_ERROR;
1199 static svn_error_t *
1200 tc_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)
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;
1213 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1215 return SVN_NO_ERROR;
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.*/
1222 SVN_ERR(mark_tc_on_op_root(nmb,
1224 svn_wc_conflict_action_delete,
1226 return SVN_NO_ERROR;
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));
1235 svn_wc_conflict_reason_t reason;
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.
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)
1245 SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1246 NULL, NULL, scratch_pool));
1248 reason = svn_wc_conflict_reason_edited;
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,
1256 scratch_pool, scratch_pool));
1261 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1262 const char *del_abspath;
1263 svn_boolean_t have_row;
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,
1271 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1275 svn_skel_t *work_item;
1276 svn_node_kind_t del_kind;
1278 svn_pool_clear(iterpool);
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),
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);
1290 err = svn_wc__wq_build_file_remove(&work_item, b->db,
1291 b->wcroot->abspath, del_abspath,
1292 iterpool, iterpool);
1294 err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool);
1296 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1298 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1300 SVN_ERR(svn_sqlite__reset(stmt));
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));
1308 SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db,
1309 b->wcroot->abspath, local_abspath,
1310 scratch_pool, iterpool));
1312 svn_pool_destroy(iterpool);
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,
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,
1327 return SVN_NO_ERROR;
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.
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).
1342 * We currently assume that both the pre-update and post-update states are
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. */
1351 static svn_error_t *
1352 get_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,
1358 svn_wc__db_wcroot_t *wcroot,
1359 apr_pool_t *result_pool,
1360 apr_pool_t *scratch_pool)
1362 svn_wc__db_status_t status;
1363 const char *repos_relpath;
1364 svn_node_kind_t db_kind;
1367 err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL,
1368 NULL, NULL, NULL, NULL, checksum, NULL,
1370 wcroot, local_relpath, op_depth,
1371 result_pool, scratch_pool);
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))
1379 svn_error_clear(err);
1382 *kind = svn_node_none;
1388 *children = apr_array_make(result_pool, 0, sizeof(const char *));
1390 return SVN_NO_ERROR;
1398 if (children && db_kind == svn_node_dir)
1400 svn_sqlite__stmt_t *stmt;
1401 svn_boolean_t have_row;
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,
1408 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1411 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1413 APR_ARRAY_PUSH(*children, const char *)
1414 = svn_relpath_basename(child_relpath, result_pool);
1416 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1418 SVN_ERR(svn_sqlite__reset(stmt));
1421 *children = apr_array_make(result_pool, 0, sizeof(const char *));
1423 return SVN_NO_ERROR;
1426 /* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties,
1427 FALSE otherwise. SRC_PROPS and DST_PROPS are standard property
1429 static svn_error_t *
1430 props_match(svn_boolean_t *match,
1431 apr_hash_t *src_props,
1432 apr_hash_t *dst_props,
1433 apr_pool_t *scratch_pool)
1435 if (!src_props && !dst_props)
1437 else if (!src_props || ! dst_props)
1441 apr_array_header_t *propdiffs;
1443 SVN_ERR(svn_prop_diffs(&propdiffs, src_props, dst_props, scratch_pool));
1444 *match = propdiffs->nelts ? FALSE : TRUE;
1446 return SVN_NO_ERROR;
1449 /* ### Drive TC_EDITOR so as to ...
1451 static svn_error_t *
1452 update_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)
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;
1465 SVN_ERR(b->cancel_func(b->cancel_baton));
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));
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));
1475 if (src_kind == svn_node_none
1476 || (dst_kind != svn_node_none && src_kind != dst_kind))
1478 SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind,
1483 return SVN_NO_ERROR;
1485 if (src_kind != svn_node_none && src_kind != dst_kind)
1487 if (src_kind == svn_node_file || src_kind == svn_node_symlink)
1489 SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind,
1490 src_checksum, src_props, scratch_pool));
1492 else if (src_kind == svn_node_dir)
1494 SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind,
1495 src_props, scratch_pool));
1498 else if (src_kind != svn_node_none)
1500 svn_boolean_t props_equal;
1502 SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool));
1504 if (src_kind == svn_node_file || src_kind == svn_node_symlink)
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));
1511 else if (src_kind == svn_node_dir)
1514 SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
1515 dst_props, src_props,
1521 return SVN_NO_ERROR;
1523 if (src_kind == svn_node_dir)
1525 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1528 while (i < src_children->nelts || j < dst_children->nelts)
1530 const char *child_name;
1531 svn_boolean_t src_only = FALSE, dst_only = FALSE;
1532 node_move_baton_t cnmb = { 0 };
1535 cnmb.umb = nmb->umb;
1536 cnmb.shadowed = nmb->shadowed;
1538 svn_pool_clear(iterpool);
1539 if (i >= src_children->nelts)
1542 child_name = APR_ARRAY_IDX(dst_children, j, const char *);
1544 else if (j >= dst_children->nelts)
1547 child_name = APR_ARRAY_IDX(src_children, i, const char *);
1551 const char *src_name = APR_ARRAY_IDX(src_children, i,
1553 const char *dst_name = APR_ARRAY_IDX(dst_children, j,
1555 int cmp = strcmp(src_name, dst_name);
1562 child_name = dst_only ? dst_name : src_name;
1565 cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
1567 cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
1571 SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot,
1572 cnmb.dst_relpath, b->dst_op_depth,
1575 SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath,
1576 cnmb.dst_relpath, iterpool));
1583 if (nmb->skip) /* Does parent now want a skip? */
1588 return SVN_NO_ERROR;
1591 static svn_error_t *
1592 suitable_for_move(svn_wc__db_wcroot_t *wcroot,
1593 const char *local_relpath,
1594 apr_pool_t *scratch_pool)
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);
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));
1607 return svn_error_trace(svn_sqlite__reset(stmt));
1609 revision = svn_sqlite__column_revnum(stmt, 4);
1610 repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
1612 SVN_ERR(svn_sqlite__reset(stmt));
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));
1620 svn_revnum_t node_revision = svn_sqlite__column_revnum(stmt, 2);
1621 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
1623 svn_pool_clear(iterpool);
1625 relpath = svn_relpath_skip_ancestor(local_relpath, relpath);
1626 relpath = svn_relpath_join(repos_relpath, relpath, iterpool);
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,
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,
1645 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1647 SVN_ERR(svn_sqlite__reset(stmt));
1649 svn_pool_destroy(iterpool);
1651 return SVN_NO_ERROR;
1654 /* The body of svn_wc__db_update_moved_away_conflict_victim(), which see.
1656 static svn_error_t *
1657 update_moved_away_conflict_victim(svn_revnum_t *old_rev,
1658 svn_revnum_t *new_rev,
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,
1668 apr_pool_t *scratch_pool)
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 };
1677 SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath));
1679 /* Construct editor baton. */
1681 SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot,
1682 local_relpath, relpath_depth(delete_relpath),
1685 SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL,
1686 wcroot, local_relpath,
1688 scratch_pool, scratch_pool));
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,
1696 umb.dst_op_depth = relpath_depth(dst_relpath);
1698 SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
1699 SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
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,
1707 wcroot, src_relpath, umb.src_op_depth,
1708 scratch_pool, scratch_pool));
1710 SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
1711 &new_version.repos_uuid,
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,
1720 wcroot, dst_relpath, umb.dst_op_depth,
1721 scratch_pool, scratch_pool));
1723 SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
1724 &old_version.repos_uuid,
1727 *old_rev = old_version.peg_rev;
1728 *new_rev = new_version.peg_rev;
1730 umb.operation = operation;
1731 umb.old_version= &old_version;
1732 umb.new_version= &new_version;
1734 umb.wcroot = wcroot;
1735 umb.cancel_func = cancel_func;
1736 umb.cancel_baton = cancel_baton;
1738 if (umb.src_op_depth == 0)
1739 SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool));
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));
1745 /* Drive the editor... */
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; */
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,
1760 SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath,
1762 dst_relpath, NULL, NULL,
1765 return SVN_NO_ERROR;
1770 svn_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,
1778 svn_wc_notify_func2_t notify_func,
1780 apr_pool_t *scratch_pool)
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;
1787 /* ### Check for mixed-rev src or dst? */
1789 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1791 scratch_pool, scratch_pool));
1792 VERIFY_USABLE_WCROOT(wcroot);
1795 = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath);
1797 SVN_WC__DB_WITH_TXN(
1798 update_moved_away_conflict_victim(
1800 db, wcroot, local_relpath, delete_relpath,
1801 operation, action, reason,
1802 cancel_func, cancel_baton,
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,
1812 svn_wc_notify_t *notify;
1814 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
1817 svn_wc_notify_update_completed,
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);
1827 return SVN_NO_ERROR;
1830 /* Set *CAN_BUMP to TRUE if DEPTH is sufficient to cover the entire
1831 tree LOCAL_RELPATH at OP_DEPTH, to FALSE otherwise. */
1832 static svn_error_t *
1833 depth_sufficient_to_bump(svn_boolean_t *can_bump,
1834 svn_wc__db_wcroot_t *wcroot,
1835 const char *local_relpath,
1838 apr_pool_t *scratch_pool)
1840 svn_sqlite__stmt_t *stmt;
1841 svn_boolean_t have_row;
1845 case svn_depth_infinity:
1847 return SVN_NO_ERROR;
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));
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));
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));
1870 SVN_ERR_MALFUNCTION();
1872 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1873 SVN_ERR(svn_sqlite__reset(stmt));
1875 *can_bump = !have_row;
1876 return SVN_NO_ERROR;
1879 /* Mark a move-edit conflict on MOVE_SRC_ROOT_RELPATH. */
1880 static svn_error_t *
1881 bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot,
1882 const char *move_src_root_relpath,
1884 const char *move_src_op_root_relpath,
1885 const char *move_dst_op_root_relpath,
1887 apr_pool_t *scratch_pool)
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;
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));
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,
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));
1915 /* Read old (pre-update) information from the move destination node.
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)
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));
1930 if (strcmp(move_src_root_relpath, move_src_op_root_relpath))
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
1936 const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath,
1937 move_src_root_relpath);
1939 old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath,
1941 new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath,
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);
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,
1958 svn_wc_conflict_reason_moved_away,
1959 svn_wc_conflict_action_edit,
1960 move_src_op_root_relpath,
1961 scratch_pool, scratch_pool));
1963 SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db,
1964 svn_wc_notify_tree_conflict,
1966 svn_wc_notify_state_inapplicable,
1967 svn_wc_notify_state_inapplicable,
1968 conflict, NULL, scratch_pool));
1970 return SVN_NO_ERROR;
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.
1977 static svn_error_t *
1978 check_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)
1986 const char *relpath;
1989 *src_depth = bump_depth;
1991 relpath = svn_relpath_skip_ancestor(bump_root, src_relpath);
1996 if (bump_depth == svn_depth_infinity)
1997 return SVN_NO_ERROR;
1999 if (relpath && *relpath == '\0')
2000 return SVN_NO_ERROR;
2004 case svn_depth_empty:
2008 case svn_depth_files:
2009 if (src_kind != svn_node_file)
2015 case svn_depth_immediates:
2016 if (!relpath || relpath_depth(relpath) > 1)
2019 *src_depth = svn_depth_empty;
2022 SVN_ERR_MALFUNCTION();
2025 return SVN_NO_ERROR;
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.
2031 static svn_error_t *
2032 bump_moved_layer(svn_boolean_t *recurse,
2033 svn_wc__db_wcroot_t *wcroot,
2034 const char *local_relpath,
2036 const char *src_relpath,
2038 svn_depth_t src_depth,
2039 const char *dst_relpath,
2041 apr_pool_t *scratch_pool)
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;
2049 SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
2053 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2054 STMT_HAS_LAYER_BETWEEN));
2056 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
2057 op_depth, src_del_depth));
2059 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2060 SVN_ERR(svn_sqlite__reset(stmt));
2063 return SVN_NO_ERROR;
2066 SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath,
2067 op_depth, src_depth, scratch_pool));
2069 /* Having chosen to bump an entire BASE tree move we
2070 always have sufficient depth to bump subtree moves. */
2073 /* Are we allowed to bump */
2076 svn_boolean_t locked;
2078 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
2080 FALSE, scratch_pool));
2086 src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth,
2091 SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth,
2092 src_root_relpath, dst_relpath,
2095 return SVN_NO_ERROR;
2098 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
2099 wcroot, src_root_relpath,
2100 scratch_pool, scratch_pool));
2102 /* ### TODO: check this is the right sort of tree-conflict? */
2105 /* ### TODO: verify moved_here? */
2107 SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
2109 SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot,
2110 src_relpath, op_depth,
2111 dst_relpath, NULL, NULL,
2117 return SVN_NO_ERROR;
2120 /* Internal storage for bump_moved_away() */
2123 const char *src_relpath;
2124 const char *dst_relpath;
2125 int src_del_op_depth;
2126 svn_node_kind_t src_kind;
2129 /* Bump moves of LOCAL_RELPATH and all its descendants that were
2130 originally below LOCAL_RELPATH at op-depth OP_DEPTH.
2132 static svn_error_t *
2133 bump_moved_away(svn_wc__db_wcroot_t *wcroot,
2134 const char *local_relpath,
2138 apr_pool_t *scratch_pool)
2140 svn_sqlite__stmt_t *stmt;
2141 svn_boolean_t have_row;
2142 apr_pool_t *iterpool;
2144 apr_array_header_t *pairs = apr_array_make(scratch_pool, 32,
2145 sizeof(struct bump_pair_t*));
2147 /* Build an array, as we can't execute the same Sqlite query recursively */
2148 iterpool = svn_pool_create(scratch_pool);
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,
2154 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2157 struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp));
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);
2164 APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp;
2166 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2169 SVN_ERR(svn_sqlite__reset(stmt));
2171 for (i = 0; i < pairs->nelts; i++)
2173 struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *);
2175 svn_depth_t src_wc_depth;
2177 svn_pool_clear(iterpool);
2180 SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth,
2181 bp->src_relpath, bp->src_kind, iterpool));
2185 svn_boolean_t recurse;
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,
2194 SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath,
2195 relpath_depth(bp->dst_relpath),
2196 depth, db, iterpool));
2200 svn_pool_destroy(iterpool);
2202 return SVN_NO_ERROR;
2206 svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot,
2207 const char *local_relpath,
2210 apr_pool_t *scratch_pool)
2212 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2213 STMT_CREATE_UPDATE_MOVE_LIST));
2215 if (local_relpath[0] != '\0')
2217 const char *move_dst_op_root_relpath;
2218 const char *move_src_root_relpath, *delete_relpath;
2221 /* Is the root of the update moved away? (Impossible for the wcroot) */
2223 err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath,
2224 &move_dst_op_root_relpath,
2226 wcroot, local_relpath,
2228 scratch_pool, scratch_pool);
2232 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2233 return svn_error_trace(err);
2235 svn_error_clear(err);
2237 else if (move_src_root_relpath)
2239 if (strcmp(move_src_root_relpath, local_relpath))
2241 /* An ancestor of the path that was updated is moved away.
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;
2248 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(
2250 move_src_root_relpath,
2251 FALSE, scratch_pool));
2255 SVN_ERR(bump_mark_tree_conflict(wcroot,
2256 move_src_root_relpath, 0,
2258 move_dst_op_root_relpath,
2261 return SVN_NO_ERROR;
2266 SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool));
2268 return SVN_NO_ERROR;
2271 /* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION
2272 * to reflect the tree conflict on the victim SRC_ABSPATH in DB.
2274 * If SRC_ABSPATH is not a tree-conflict victim, return an error.
2276 static svn_error_t *
2277 fetch_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,
2284 const char *local_relpath,
2285 const svn_skel_t *conflict_skel,
2286 apr_pool_t *result_pool,
2287 apr_pool_t *scratch_pool)
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,
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,
2304 SVN_ERR(svn_wc__conflict_read_info(operation, &locations,
2305 &text_conflicted, &prop_conflicted,
2308 conflict_skel, result_pool,
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,
2317 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason,
2319 &move_src_op_root_abspath,
2321 conflict_skel, result_pool,
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,
2332 if (locations && locations->nelts > 0)
2333 *left_version = APR_ARRAY_IDX(locations, 0,
2334 svn_wc_conflict_version_t *);
2336 *left_version = NULL;
2341 if (locations && locations->nelts > 1)
2342 *right_version = APR_ARRAY_IDX(locations, 1,
2343 svn_wc_conflict_version_t *);
2345 *right_version = NULL;
2349 int del_depth = relpath_depth(local_relpath);
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));
2356 SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth,
2360 return SVN_NO_ERROR;
2364 svn_wc__db_op_raise_moved_away_internal(
2365 svn_wc__db_wcroot_t *wcroot,
2366 const char *local_relpath,
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)
2375 svn_sqlite__stmt_t *stmt;
2376 svn_boolean_t have_row;
2377 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2379 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2380 STMT_CREATE_UPDATE_MOVE_LIST));
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,
2386 SVN_ERR(svn_sqlite__step(&have_row, stmt));
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);
2398 SVN_ERR_ASSERT(src_repos_relpath != NULL);
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 */,
2405 svn_wc_conflict_reason_moved_away,
2407 svn_relpath_prefix(src_relpath,
2410 iterpool, iterpool);
2413 err = update_move_list_add(wcroot, src_relpath, db,
2414 svn_wc_notify_tree_conflict,
2416 svn_wc_notify_state_inapplicable,
2417 svn_wc_notify_state_inapplicable,
2418 conflict, NULL, scratch_pool);
2421 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2423 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2425 SVN_ERR(svn_sqlite__reset(stmt));
2427 svn_pool_destroy(iterpool);
2429 return SVN_NO_ERROR;
2433 svn_wc__db_op_raise_moved_away(svn_wc__db_t *db,
2434 const char *local_abspath,
2435 svn_wc_notify_func2_t notify_func,
2437 apr_pool_t *scratch_pool)
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;
2447 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2449 scratch_pool, scratch_pool));
2450 VERIFY_USABLE_WCROOT(wcroot);
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,
2463 NULL, scratch_pool),
2464 svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath,
2466 db, operation, action,
2467 left_version, right_version,
2471 /* These version numbers are valid for update/switch notifications
2473 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
2475 ? left_version->peg_rev
2476 : SVN_INVALID_REVNUM),
2478 ? right_version->peg_rev
2479 : SVN_INVALID_REVNUM),
2480 notify_func, notify_baton,
2483 return SVN_NO_ERROR;
2486 static svn_error_t *
2487 break_moved_away(svn_wc__db_wcroot_t *wcroot,
2489 const char *local_relpath,
2490 int parent_src_op_depth,
2491 apr_pool_t *scratch_pool)
2493 svn_sqlite__stmt_t *stmt;
2494 svn_boolean_t have_row;
2495 apr_pool_t *iterpool;
2496 svn_error_t *err = NULL;
2498 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2499 STMT_CREATE_UPDATE_MOVE_LIST));
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));
2507 iterpool = svn_pool_create(scratch_pool);
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);
2515 svn_pool_clear(iterpool);
2517 err = verify_write_lock(wcroot, src_relpath, iterpool);
2520 err = verify_write_lock(wcroot, dst_relpath, iterpool);
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));
2533 err = svn_error_trace(
2534 update_move_list_add(wcroot, src_relpath, db,
2535 svn_wc_notify_move_broken,
2537 svn_wc_notify_state_inapplicable,
2538 svn_wc_notify_state_inapplicable,
2539 NULL, NULL, scratch_pool));
2544 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2546 svn_pool_destroy(iterpool);
2548 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2552 svn_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,
2558 apr_pool_t *scratch_pool)
2560 svn_wc__db_wcroot_t *wcroot;
2561 const char *local_relpath;
2562 const char *del_relpath;
2565 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2567 scratch_pool, scratch_pool));
2568 VERIFY_USABLE_WCROOT(wcroot);
2570 if (del_op_root_abspath)
2571 del_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
2572 del_op_root_abspath);
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),
2582 break_moved_away(wcroot, db, local_relpath, src_op_depth,
2585 ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
2592 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
2595 notify_func, notify_baton,
2597 return SVN_NO_ERROR;
2600 static svn_error_t *
2601 required_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)
2607 svn_sqlite__stmt_t *stmt;
2608 svn_boolean_t have_row;
2610 *required_relpath = local_relpath;
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));
2624 const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1,
2628 = svn_relpath_get_longest_ancestor(*required_relpath,
2632 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2634 SVN_ERR(svn_sqlite__reset(stmt));
2636 *required_relpath = apr_pstrdup(result_pool, *required_relpath);
2638 return SVN_NO_ERROR;
2642 svn_wc__required_lock_for_resolve(const char **required_abspath,
2644 const char *local_abspath,
2645 apr_pool_t *result_pool,
2646 apr_pool_t *scratch_pool)
2648 svn_wc__db_wcroot_t *wcroot;
2649 const char *local_relpath;
2650 const char *required_relpath;
2652 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2654 scratch_pool, scratch_pool));
2655 VERIFY_USABLE_WCROOT(wcroot);
2657 SVN_WC__DB_WITH_TXN(
2658 required_lock_for_resolve(&required_relpath, wcroot, local_relpath,
2659 scratch_pool, scratch_pool),
2662 *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath,
2665 return SVN_NO_ERROR;