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 implements editors and edit drivers which are used to resolve
25 * "incoming edit, local move-away", "incoming move, local edit", and
26 * "incoming add, local add" tree conflicts resulting from an update
29 * Our goal is to be able to resolve conflicts such that the end result
30 * is just the same as if the user had run the update *before* the local
31 * (or incoming) move or local add.
33 * -- Updating local moves --
35 * When an update (or switch) produces incoming changes for a locally
36 * moved-away subtree, it updates the base nodes of the moved-away tree
37 * and flags a tree-conflict on the moved-away root node.
38 * This editor transfers these changes from the moved-away part of the
39 * working copy to the corresponding moved-here part of the working copy.
41 * Both the driver and receiver components of the editor are implemented
44 * The driver sees two NODES trees: the move source tree and the move
45 * destination tree. When the move is initially made these trees are
46 * equivalent, the destination is a copy of the source. The source is
47 * a single-op-depth, single-revision, deleted layer [1] and the
48 * destination has an equivalent single-op-depth, single-revision
49 * layer. The destination may have additional higher op-depths
50 * representing adds, deletes, moves within the move destination. [2]
52 * After the initial move an update has modified the NODES in the move
53 * source and may have introduced a tree-conflict since the source and
54 * destination trees are no longer equivalent. The source is a
55 * different revision and may have text, property and tree changes
56 * compared to the destination. The driver will compare the two NODES
57 * trees and drive an editor to change the destination tree so that it
58 * once again matches the source tree. Changes made to the
59 * destination NODES tree to achieve this match will be merged into
60 * the working files/directories.
62 * The whole drive occurs as one single wc.db transaction. At the end
63 * of the transaction the destination NODES table should have a layer
64 * that is equivalent to the source NODES layer, there should be
65 * workqueue items to make any required changes to working
66 * files/directories in the move destination, and there should be
67 * tree-conflicts in the move destination where it was not possible to
68 * update the working files/directories.
70 * [1] The move source tree is single-revision because we currently do
71 * not allow a mixed-rev move, and therefore it is single op-depth
72 * regardless whether it is a base layer or a nested move.
74 * [2] The source tree also may have additional higher op-depths,
75 * representing a replacement, but this editor only reads from the
76 * single-op-depth layer of it, and makes no changes of any kind
77 * within the source tree.
79 * -- Updating incoming moves --
81 * When an update (or switch) produces an incoming move, it deletes the
82 * moved node at the old location from the BASE tree and adds a node at
83 * the new location to the BASE tree. If the old location contains local
84 * changes, a tree conflict is raised, and the former BASE tree which
85 * the local changes were based on (the tree conflict victim) is re-added
86 * as a copy which contains these local changes.
88 * The driver sees two NODES trees: The op-root of the copy, and the
89 * WORKING layer on top of this copy which represents the local changes.
90 * The driver will compare the two NODES trees and drive an editor to
91 * change the move destination's WORKING tree so that it now contains
92 * the local changes seen in the copy of the victim's tree.
94 * We require that no local changes exist at the destination, in order
95 * to avoid tree conflicts where the "incoming" and "local" change both
96 * originated in the working copy, because the resolver code cannot handle
97 * such tree conflicts at present.
99 * The whole drive occurs as one single wc.db transaction. At the end
100 * of the transaction the destination NODES table should have a WORKING
101 * layer that is equivalent to the WORKING layer found in the copied victim
102 * tree, and there should be workqueue items to make any required changes
103 * to working files/directories in the move destination, and there should be
104 * tree-conflicts in the move destination where it was not possible to
105 * update the working files/directories.
107 * -- Updating local adds --
109 * When an update (or switch) adds a directory tree it creates corresponding
110 * nodes in the BASE tree. Any existing locally added nodes are bumped to a
111 * higher layer with the top-most locally added directory as op-root.
112 * In-between, the update inserts a base-deleted layer, i.e. it schedules the
113 * directory in the BASE tree for removal upon the next commit, to be replaced
114 * by the locally added directory.
116 * The driver sees two NODES trees: The BASE layer, and the WORKING layer
117 * which represents the locally added tree.
118 * The driver will compare the two NODES trees and drive an editor to
119 * merge WORKING tree nodes with the nodes in the BASE tree.
121 * The whole drive occurs as one single wc.db transaction.
122 * Directories which exist in both trees become part of the BASE tree, with
124 * Files which exist in both trees are merged (there is no common ancestor,
125 * so the common ancestor in this merge is the empty file).
126 * Files and directories which exist only in the WORKING layer become
127 * local-add op-roots of their own.
128 * Mismatching node kinds produce new 'incoming add vs local add upon update'
129 * tree conflicts which must be resolved individually later on.
132 #define SVN_WC__I_AM_WC_DB
136 #include "svn_checksum.h"
137 #include "svn_dirent_uri.h"
138 #include "svn_error.h"
139 #include "svn_hash.h"
141 #include "svn_props.h"
142 #include "svn_pools.h"
143 #include "svn_sorts.h"
145 #include "private/svn_skel.h"
146 #include "private/svn_sorts_private.h"
147 #include "private/svn_sqlite.h"
148 #include "private/svn_wc_private.h"
152 #include "wc_db_private.h"
153 #include "wc-queries.h"
154 #include "conflicts.h"
155 #include "workqueue.h"
156 #include "token-map.h"
158 /* Helper functions */
159 /* Return the absolute path, in local path style, of LOCAL_RELPATH
162 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
163 const char *local_relpath,
164 apr_pool_t *result_pool)
166 const char *local_abspath
167 = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
169 return svn_dirent_local_style(local_abspath, result_pool);
172 /* Ensure that there is a working copy lock for LOCAL_RELPATH in WCROOT */
174 verify_write_lock(svn_wc__db_wcroot_t *wcroot,
175 const char *local_relpath,
176 apr_pool_t *scratch_pool)
178 svn_boolean_t locked;
180 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, local_relpath,
181 FALSE, scratch_pool));
184 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
185 _("No write-lock in '%s'"),
186 path_for_error_message(wcroot, local_relpath,
193 /* In our merge conflicts we record the move_op_src path, which is essentially
194 the depth at which what was moved is marked deleted. The problem is that
195 this depth is not guaranteed to be stable, because somebody might just
196 remove another ancestor, or revert one.
198 To work around this problem we locate the layer below this path, and use
199 that to pinpoint whatever is moved.
201 For a path SRC_RELPATH that was deleted by an operation rooted at
202 DELETE_OP_DEPTH find the op-depth at which the node was originally added.
205 find_src_op_depth(int *src_op_depth,
206 svn_wc__db_wcroot_t *wcroot,
207 const char *src_relpath,
209 apr_pool_t *scratch_pool)
211 svn_sqlite__stmt_t *stmt;
212 svn_boolean_t have_row;
214 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
215 STMT_SELECT_HIGHEST_WORKING_NODE));
216 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
217 src_relpath, delete_op_depth));
219 SVN_ERR(svn_sqlite__step(&have_row, stmt));
221 *src_op_depth = svn_sqlite__column_int(stmt, 0);
222 SVN_ERR(svn_sqlite__reset(stmt));
224 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
225 _("'%s' is not deleted"),
226 path_for_error_message(wcroot, src_relpath,
235 * The receiver is an editor that, when driven with a certain change, will
236 * merge the edits into the working/actual state of the move destination
237 * at MOVE_ROOT_DST_RELPATH (in struct tc_editor_baton), perhaps raising
238 * conflicts if necessary.
240 * The receiver should not need to refer directly to the move source, as
241 * the driver should provide all relevant information about the change to
242 * be made at the move destination.
245 typedef struct update_move_baton_t {
247 svn_wc__db_wcroot_t *wcroot;
252 svn_wc_operation_t operation;
253 svn_wc_conflict_version_t *old_version;
254 svn_wc_conflict_version_t *new_version;
256 svn_cancel_func_t cancel_func;
258 } update_move_baton_t;
260 /* Per node flags for tree conflict collection */
261 typedef struct node_move_baton_t
264 svn_boolean_t shadowed;
265 svn_boolean_t edited;
267 const char *src_relpath;
268 const char *dst_relpath;
270 update_move_baton_t *umb;
271 struct node_move_baton_t *pb;
275 * Notifications are delayed until the entire update-move transaction
276 * completes. These functions provide the necessary support by storing
277 * notification information in a temporary db table (the "update_move_list")
278 * and spooling notifications out of that table after the transaction.
281 /* Add an entry to the notification list, and at the same time install
282 a conflict and/or work items. */
284 update_move_list_add(svn_wc__db_wcroot_t *wcroot,
285 const char *local_relpath,
287 svn_wc_notify_action_t action,
288 svn_node_kind_t kind,
289 svn_wc_notify_state_t content_state,
290 svn_wc_notify_state_t prop_state,
291 svn_skel_t *conflict,
292 svn_skel_t *work_item,
293 apr_pool_t *scratch_pool)
295 svn_sqlite__stmt_t *stmt;
299 svn_boolean_t tree_conflict;
301 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
303 db, wcroot->abspath, conflict,
304 scratch_pool, scratch_pool));
307 action = svn_wc_notify_tree_conflict;
308 content_state = svn_wc_notify_state_inapplicable;
309 prop_state = svn_wc_notify_state_inapplicable;
313 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
314 STMT_INSERT_UPDATE_MOVE_LIST));
315 SVN_ERR(svn_sqlite__bindf(stmt, "sdtdd", local_relpath,
316 action, kind_map_none, kind,
317 content_state, prop_state));
318 SVN_ERR(svn_sqlite__step_done(stmt));
321 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, conflict,
325 SVN_ERR(svn_wc__db_wq_add_internal(wcroot, work_item, scratch_pool));
330 /* Send all notifications stored in the notification list, and then
331 * remove the temporary database table. */
333 svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot,
334 svn_revnum_t old_revision,
335 svn_revnum_t new_revision,
336 svn_wc_notify_func2_t notify_func,
338 apr_pool_t *scratch_pool)
340 svn_sqlite__stmt_t *stmt;
344 apr_pool_t *iterpool;
345 svn_boolean_t have_row;
347 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
348 STMT_SELECT_UPDATE_MOVE_LIST));
349 SVN_ERR(svn_sqlite__step(&have_row, stmt));
351 iterpool = svn_pool_create(scratch_pool);
354 const char *local_relpath;
355 svn_wc_notify_action_t action;
356 svn_wc_notify_t *notify;
358 svn_pool_clear(iterpool);
360 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
361 action = svn_sqlite__column_int(stmt, 1);
362 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
366 notify->kind = svn_sqlite__column_token(stmt, 2, kind_map_none);
367 notify->content_state = svn_sqlite__column_int(stmt, 3);
368 notify->prop_state = svn_sqlite__column_int(stmt, 4);
369 notify->old_revision = old_revision;
370 notify->revision = new_revision;
371 notify_func(notify_baton, notify, scratch_pool);
373 SVN_ERR(svn_sqlite__step(&have_row, stmt));
375 svn_pool_destroy(iterpool);
376 SVN_ERR(svn_sqlite__reset(stmt));
379 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
380 STMT_FINALIZE_UPDATE_MOVE));
381 SVN_ERR(svn_sqlite__step_done(stmt));
386 /* Create a tree-conflict for recording on LOCAL_RELPATH if such
387 a tree-conflict does not already exist. */
389 create_tree_conflict(svn_skel_t **conflict_p,
390 svn_wc__db_wcroot_t *wcroot,
391 const char *local_relpath,
392 const char *dst_op_root_relpath,
394 const svn_wc_conflict_version_t *old_version,
395 const svn_wc_conflict_version_t *new_version,
396 svn_wc_operation_t operation,
397 svn_node_kind_t old_kind,
398 svn_node_kind_t new_kind,
399 const char *old_repos_relpath,
400 svn_wc_conflict_reason_t reason,
401 svn_wc_conflict_action_t action,
402 const char *move_src_op_root_relpath,
403 apr_pool_t *result_pool,
404 apr_pool_t *scratch_pool)
407 svn_skel_t *conflict;
408 svn_wc_conflict_version_t *conflict_old_version, *conflict_new_version;
409 const char *move_src_op_root_abspath
410 = move_src_op_root_relpath
411 ? svn_dirent_join(wcroot->abspath,
412 move_src_op_root_relpath, scratch_pool)
414 const char *move_dst_op_root_abspath
415 = dst_op_root_relpath
416 ? svn_dirent_join(wcroot->abspath,
417 dst_op_root_relpath, scratch_pool)
419 const char *old_repos_relpath_part
420 = old_repos_relpath && old_version
421 ? svn_relpath_skip_ancestor(old_version->path_in_repos,
424 const char *new_repos_relpath
425 = old_repos_relpath_part
426 ? svn_relpath_join(new_version->path_in_repos, old_repos_relpath_part,
430 if (!new_repos_relpath)
432 const char *child_relpath = svn_relpath_skip_ancestor(
435 SVN_ERR_ASSERT(child_relpath != NULL);
436 new_repos_relpath = svn_relpath_join(new_version->path_in_repos,
437 child_relpath, scratch_pool);
440 err = svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
441 wcroot, local_relpath,
442 result_pool, scratch_pool);
443 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
444 return svn_error_trace(err);
447 svn_error_clear(err);
453 svn_wc_operation_t conflict_operation;
454 svn_boolean_t tree_conflicted;
456 SVN_ERR(svn_wc__conflict_read_info(&conflict_operation, NULL, NULL, NULL,
458 db, wcroot->abspath, conflict,
459 scratch_pool, scratch_pool));
461 if (conflict_operation != svn_wc_operation_update
462 && conflict_operation != svn_wc_operation_switch)
463 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
464 _("'%s' already in conflict"),
465 path_for_error_message(wcroot, local_relpath,
470 svn_wc_conflict_reason_t existing_reason;
471 svn_wc_conflict_action_t existing_action;
472 const char *existing_abspath;
474 SVN_ERR(svn_wc__conflict_read_tree_conflict(&existing_reason,
476 &existing_abspath, NULL,
481 if (reason != existing_reason
482 || action != existing_action
483 || (reason == svn_wc_conflict_reason_moved_away
484 && strcmp(move_src_op_root_relpath,
485 svn_dirent_skip_ancestor(wcroot->abspath,
487 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
488 _("'%s' already in conflict"),
489 path_for_error_message(wcroot,
493 /* Already a suitable tree-conflict. */
494 *conflict_p = conflict;
499 conflict = svn_wc__conflict_skel_create(result_pool);
501 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
503 svn_dirent_join(wcroot->abspath, local_relpath,
507 move_src_op_root_abspath,
508 move_dst_op_root_abspath,
513 conflict_old_version = svn_wc_conflict_version_create2(
514 old_version->repos_url,
515 old_version->repos_uuid,
516 old_repos_relpath, old_version->peg_rev,
517 old_kind, scratch_pool);
519 conflict_old_version = NULL;
521 conflict_new_version = svn_wc_conflict_version_create2(
522 new_version->repos_url, new_version->repos_uuid,
523 new_repos_relpath, new_version->peg_rev,
524 new_kind, scratch_pool);
526 if (operation == svn_wc_operation_update)
528 SVN_ERR(svn_wc__conflict_skel_set_op_update(
529 conflict, conflict_old_version, conflict_new_version,
530 result_pool, scratch_pool));
534 assert(operation == svn_wc_operation_switch);
535 SVN_ERR(svn_wc__conflict_skel_set_op_switch(
536 conflict, conflict_old_version, conflict_new_version,
537 result_pool, scratch_pool));
540 *conflict_p = conflict;
545 create_node_tree_conflict(svn_skel_t **conflict_p,
546 node_move_baton_t *nmb,
547 const char *dst_local_relpath,
548 svn_node_kind_t old_kind,
549 svn_node_kind_t new_kind,
550 svn_wc_conflict_reason_t reason,
551 svn_wc_conflict_action_t action,
552 const char *move_src_op_root_relpath,
553 apr_pool_t *result_pool,
554 apr_pool_t *scratch_pool)
556 update_move_baton_t *umb = nmb->umb;
557 const char *dst_repos_relpath;
558 const char *dst_root_relpath = svn_relpath_prefix(nmb->dst_relpath,
563 svn_relpath_join(nmb->umb->old_version->path_in_repos,
564 svn_relpath_skip_ancestor(dst_root_relpath,
568 return svn_error_trace(
569 create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath,
570 svn_relpath_prefix(dst_local_relpath,
574 umb->old_version, umb->new_version,
575 umb->operation, old_kind, new_kind,
577 reason, action, move_src_op_root_relpath,
578 result_pool, scratch_pool));
581 /* Checks if a specific local path is shadowed as seen from the move root.
582 Helper for update_moved_away_node() */
584 check_node_shadowed(svn_boolean_t *shadowed,
585 svn_wc__db_wcroot_t *wcroot,
586 const char *local_relpath,
587 int move_root_dst_op_depth,
588 apr_pool_t *scratch_pool)
590 svn_sqlite__stmt_t *stmt;
591 svn_boolean_t have_row;
593 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
594 STMT_SELECT_WORKING_NODE));
595 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
597 SVN_ERR(svn_sqlite__step(&have_row, stmt));
601 int op_depth = svn_sqlite__column_int(stmt, 0);
603 *shadowed = (op_depth > move_root_dst_op_depth);
607 SVN_ERR(svn_sqlite__reset(stmt));
612 /* Set a tree conflict for the shadowed node LOCAL_RELPATH, which is
613 the ROOT OF THE OBSTRUCTION if such a tree-conflict does not
614 already exist. KIND is the kind of the incoming LOCAL_RELPATH. */
616 mark_tc_on_op_root(node_move_baton_t *nmb,
617 svn_node_kind_t old_kind,
618 svn_node_kind_t new_kind,
619 svn_wc_conflict_action_t action,
620 apr_pool_t *scratch_pool)
622 update_move_baton_t *b = nmb->umb;
623 const char *move_dst_relpath;
624 svn_skel_t *conflict;
626 SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed);
630 if (old_kind == svn_node_none)
631 move_dst_relpath = NULL;
633 SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL,
634 b->wcroot, nmb->dst_relpath,
636 scratch_pool, scratch_pool));
638 SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath,
641 ? svn_wc_conflict_reason_moved_away
642 : svn_wc_conflict_reason_deleted),
643 action, move_dst_relpath
646 scratch_pool, scratch_pool));
648 SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db,
649 svn_wc_notify_tree_conflict,
651 svn_wc_notify_state_inapplicable,
652 svn_wc_notify_state_inapplicable,
653 conflict, NULL, scratch_pool));
659 mark_node_edited(node_move_baton_t *nmb,
660 apr_pool_t *scratch_pool)
667 SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
678 if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed))
680 svn_node_kind_t dst_kind, src_kind;
682 SVN_ERR(svn_wc__db_depth_get_info(NULL, &dst_kind, NULL,
683 NULL, NULL, NULL, NULL,
684 NULL, NULL, NULL, NULL, NULL, NULL,
685 nmb->umb->wcroot, nmb->dst_relpath,
686 nmb->umb->dst_op_depth,
687 scratch_pool, scratch_pool));
689 SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, NULL, NULL,
691 NULL, NULL, NULL, NULL, NULL, NULL,
692 nmb->umb->wcroot, nmb->src_relpath,
693 nmb->umb->src_op_depth,
694 scratch_pool, scratch_pool));
696 SVN_ERR(mark_tc_on_op_root(nmb,
698 svn_wc_conflict_action_edit,
706 mark_parent_edited(node_move_baton_t *nmb,
707 apr_pool_t *scratch_pool)
709 SVN_ERR_ASSERT(nmb && nmb->pb);
711 SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
720 tc_editor_add_directory(node_move_baton_t *nmb,
722 svn_node_kind_t old_kind,
724 apr_pool_t *scratch_pool)
726 update_move_baton_t *b = nmb->umb;
727 const char *local_abspath;
728 svn_node_kind_t wc_kind;
729 svn_skel_t *work_item = NULL;
730 svn_skel_t *conflict = NULL;
731 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
733 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
739 svn_wc__db_status_t status;
741 SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL,
742 NULL, NULL, NULL, NULL, NULL, NULL,
743 NULL, NULL, NULL, NULL, NULL, NULL,
744 NULL, NULL, NULL, NULL, NULL, NULL,
747 scratch_pool, scratch_pool));
749 if (status == svn_wc__db_status_deleted)
750 reason = svn_wc_conflict_reason_deleted;
751 else if (status != svn_wc__db_status_added)
752 wc_kind = svn_node_none;
753 else if (old_kind == svn_node_none)
754 reason = svn_wc_conflict_reason_added;
756 reason = svn_wc_conflict_reason_replaced;
759 wc_kind = svn_node_none;
761 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
763 if (wc_kind == svn_node_none)
765 /* Check for unversioned tree-conflict */
766 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
769 if (!nmb->shadowed && wc_kind == old_kind)
770 wc_kind = svn_node_none; /* Node will be gone once we install */
772 if (wc_kind != svn_node_none
773 && (nmb->shadowed || wc_kind != old_kind)) /* replace */
775 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
776 old_kind, svn_node_dir,
778 (old_kind == svn_node_none)
779 ? svn_wc_conflict_action_add
780 : svn_wc_conflict_action_replace,
782 scratch_pool, scratch_pool));
787 SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath,
788 scratch_pool, scratch_pool));
791 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
792 (old_kind == svn_node_none)
793 ? svn_wc_notify_update_add
794 : svn_wc_notify_update_replace,
796 svn_wc_notify_state_inapplicable,
797 svn_wc_notify_state_inapplicable,
798 conflict, work_item, scratch_pool));
803 copy_working_node(const char *src_relpath,
804 const char *dst_relpath,
805 svn_wc__db_wcroot_t *wcroot,
806 apr_pool_t *scratch_pool)
808 svn_sqlite__stmt_t *stmt;
809 svn_boolean_t have_row;
810 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
813 /* Add a WORKING row for the new node, based on the source. */
814 SVN_ERR(svn_sqlite__get_statement(&stmt,wcroot->sdb,
815 STMT_INSERT_WORKING_NODE_COPY_FROM));
816 SVN_ERR(svn_sqlite__bindf(stmt, "issdst", wcroot->wc_id, src_relpath,
817 dst_relpath, relpath_depth(dst_relpath),
818 dst_parent_relpath, presence_map,
819 svn_wc__db_status_normal));
820 SVN_ERR(svn_sqlite__step_done(stmt));
822 /* Copy properties over. ### This loses changelist association. */
823 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
824 STMT_SELECT_ACTUAL_NODE));
825 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, src_relpath));
826 SVN_ERR(svn_sqlite__step(&have_row, stmt));
829 apr_size_t props_size;
830 const char *properties;
832 properties = svn_sqlite__column_blob(stmt, 1, &props_size,
834 SVN_ERR(svn_sqlite__reset(stmt));
835 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
836 STMT_INSERT_ACTUAL_NODE));
837 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
838 wcroot->wc_id, dst_relpath,
839 svn_relpath_dirname(dst_relpath,
841 properties, props_size, NULL));
842 SVN_ERR(svn_sqlite__step(&have_row, stmt));
844 SVN_ERR(svn_sqlite__reset(stmt));
850 tc_editor_incoming_add_directory(node_move_baton_t *nmb,
851 const char *dst_relpath,
852 svn_node_kind_t old_kind,
854 const char *src_relpath,
855 apr_pool_t *scratch_pool)
857 update_move_baton_t *b = nmb->umb;
858 const char *dst_abspath;
859 svn_node_kind_t wc_kind;
860 svn_skel_t *work_item = NULL;
861 svn_skel_t *conflict = NULL;
862 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
864 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
868 dst_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool);
870 /* Check for unversioned tree-conflict */
871 SVN_ERR(svn_io_check_path(dst_abspath, &wc_kind, scratch_pool));
873 if (wc_kind == old_kind)
874 wc_kind = svn_node_none; /* Node will be gone once we install */
876 if (wc_kind != svn_node_none && wc_kind != old_kind) /* replace */
878 SVN_ERR(create_node_tree_conflict(&conflict, nmb, dst_relpath,
879 old_kind, svn_node_dir,
881 (old_kind == svn_node_none)
882 ? svn_wc_conflict_action_add
883 : svn_wc_conflict_action_replace,
885 scratch_pool, scratch_pool));
890 SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
892 SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, dst_abspath,
893 scratch_pool, scratch_pool));
896 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
897 (old_kind == svn_node_none)
898 ? svn_wc_notify_update_add
899 : svn_wc_notify_update_replace,
901 svn_wc_notify_state_inapplicable,
902 svn_wc_notify_state_inapplicable,
903 conflict, work_item, scratch_pool));
908 tc_editor_add_file(node_move_baton_t *nmb,
910 svn_node_kind_t old_kind,
911 const svn_checksum_t *checksum,
913 apr_pool_t *scratch_pool)
915 update_move_baton_t *b = nmb->umb;
916 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
917 svn_node_kind_t wc_kind;
918 const char *local_abspath;
919 svn_skel_t *work_item = NULL;
920 svn_skel_t *conflict = NULL;
922 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
928 svn_wc__db_status_t status;
930 SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL,
931 NULL, NULL, NULL, NULL, NULL, NULL,
932 NULL, NULL, NULL, NULL, NULL, NULL,
933 NULL, NULL, NULL, NULL, NULL, NULL,
936 scratch_pool, scratch_pool));
938 if (status == svn_wc__db_status_deleted)
939 reason = svn_wc_conflict_reason_deleted;
940 else if (status != svn_wc__db_status_added)
941 wc_kind = svn_node_none;
942 else if (old_kind == svn_node_none)
943 reason = svn_wc_conflict_reason_added;
945 reason = svn_wc_conflict_reason_replaced;
948 wc_kind = svn_node_none;
950 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
952 if (wc_kind == svn_node_none)
954 /* Check for unversioned tree-conflict */
955 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
958 if (wc_kind != svn_node_none
959 && (nmb->shadowed || wc_kind != old_kind)) /* replace */
961 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
962 old_kind, svn_node_file,
964 (old_kind == svn_node_none)
965 ? svn_wc_conflict_action_add
966 : svn_wc_conflict_action_replace,
968 scratch_pool, scratch_pool));
973 /* Update working file. */
974 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
975 svn_dirent_join(b->wcroot->abspath,
979 FALSE /*FIXME: use_commit_times?*/,
980 TRUE /* record_file_info */,
981 scratch_pool, scratch_pool));
984 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
985 (old_kind == svn_node_none)
986 ? svn_wc_notify_update_add
987 : svn_wc_notify_update_replace,
989 svn_wc_notify_state_inapplicable,
990 svn_wc_notify_state_inapplicable,
991 conflict, work_item, scratch_pool));
996 tc_editor_incoming_add_file(node_move_baton_t *nmb,
997 const char *dst_relpath,
998 svn_node_kind_t old_kind,
999 const svn_checksum_t *checksum,
1001 const char *src_relpath,
1002 const char *content_abspath,
1003 apr_pool_t *scratch_pool)
1005 update_move_baton_t *b = nmb->umb;
1006 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
1007 svn_node_kind_t wc_kind;
1008 const char *dst_abspath;
1009 svn_skel_t *work_items = NULL;
1010 svn_skel_t *work_item = NULL;
1011 svn_skel_t *conflict = NULL;
1013 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1016 SVN_ERR(svn_io_remove_file2(content_abspath, TRUE, scratch_pool));
1017 return SVN_NO_ERROR;
1020 dst_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool);
1022 /* Check for unversioned tree-conflict */
1023 SVN_ERR(svn_io_check_path(dst_abspath, &wc_kind, scratch_pool));
1025 if (wc_kind != svn_node_none && wc_kind != old_kind) /* replace */
1027 SVN_ERR(create_node_tree_conflict(&conflict, nmb, dst_relpath,
1028 old_kind, svn_node_file,
1030 (old_kind == svn_node_none)
1031 ? svn_wc_conflict_action_add
1032 : svn_wc_conflict_action_replace,
1034 scratch_pool, scratch_pool));
1036 SVN_ERR(svn_io_remove_file2(content_abspath, TRUE, scratch_pool));
1040 const char *src_abspath;
1042 SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
1045 /* Update working file. */
1046 src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1048 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, dst_abspath,
1050 FALSE /* FIXME: use_commit_times?*/,
1051 TRUE /* record_file_info */,
1052 scratch_pool, scratch_pool));
1053 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1055 /* Queue removal of temporary content copy. */
1056 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, b->db,
1057 b->wcroot->abspath, src_abspath,
1058 scratch_pool, scratch_pool));
1060 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1063 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1064 (old_kind == svn_node_none)
1065 ? svn_wc_notify_update_add
1066 : svn_wc_notify_update_replace,
1068 svn_wc_notify_state_inapplicable,
1069 svn_wc_notify_state_inapplicable,
1070 conflict, work_items, scratch_pool));
1071 return SVN_NO_ERROR;
1074 /* All the info we need about one version of a working node. */
1075 typedef struct working_node_version_t
1077 svn_wc_conflict_version_t *location_and_kind;
1079 const svn_checksum_t *checksum; /* for files only */
1080 } working_node_version_t;
1082 /* Return *WORK_ITEMS to create a conflict on LOCAL_ABSPATH. */
1083 static svn_error_t *
1084 create_conflict_markers(svn_skel_t **work_items,
1085 const char *local_abspath,
1087 const char *repos_relpath,
1088 svn_skel_t *conflict_skel,
1089 svn_wc_operation_t operation,
1090 const working_node_version_t *old_version,
1091 const working_node_version_t *new_version,
1092 svn_node_kind_t kind,
1093 svn_boolean_t set_operation,
1094 apr_pool_t *result_pool,
1095 apr_pool_t *scratch_pool)
1097 svn_wc_conflict_version_t *original_version;
1098 svn_wc_conflict_version_t *conflicted_version;
1101 original_version = svn_wc_conflict_version_dup(
1102 old_version->location_and_kind, scratch_pool);
1103 original_version->node_kind = kind;
1104 conflicted_version = svn_wc_conflict_version_dup(
1105 new_version->location_and_kind, scratch_pool);
1106 conflicted_version->node_kind = kind;
1108 part = svn_relpath_skip_ancestor(original_version->path_in_repos,
1111 part = svn_relpath_skip_ancestor(conflicted_version->path_in_repos,
1113 SVN_ERR_ASSERT(part != NULL);
1115 conflicted_version->path_in_repos
1116 = svn_relpath_join(conflicted_version->path_in_repos, part, scratch_pool);
1117 original_version->path_in_repos = repos_relpath;
1121 if (operation == svn_wc_operation_update)
1123 SVN_ERR(svn_wc__conflict_skel_set_op_update(
1124 conflict_skel, original_version,
1126 scratch_pool, scratch_pool));
1128 else if (operation == svn_wc_operation_merge)
1130 SVN_ERR(svn_wc__conflict_skel_set_op_merge(
1131 conflict_skel, original_version,
1133 scratch_pool, scratch_pool));
1137 SVN_ERR(svn_wc__conflict_skel_set_op_switch(
1138 conflict_skel, original_version,
1140 scratch_pool, scratch_pool));
1144 /* According to this func's doc string, it is "Currently only used for
1145 * property conflicts as text conflict markers are just in-wc files." */
1146 SVN_ERR(svn_wc__conflict_create_markers(work_items, db,
1152 return SVN_NO_ERROR;
1155 static svn_error_t *
1156 update_working_props(svn_wc_notify_state_t *prop_state,
1157 svn_skel_t **conflict_skel,
1158 apr_array_header_t **propchanges,
1159 apr_hash_t **actual_props,
1160 update_move_baton_t *b,
1161 const char *local_relpath,
1162 const struct working_node_version_t *old_version,
1163 const struct working_node_version_t *new_version,
1164 apr_pool_t *result_pool,
1165 apr_pool_t *scratch_pool)
1167 apr_hash_t *new_actual_props;
1168 apr_array_header_t *new_propchanges;
1171 * Run a 3-way prop merge to update the props, using the pre-update
1172 * props as the merge base, the post-update props as the
1173 * merge-left version, and the current props of the
1174 * moved-here working file as the merge-right version.
1176 SVN_ERR(svn_wc__db_read_props_internal(actual_props,
1177 b->wcroot, local_relpath,
1178 result_pool, scratch_pool));
1179 SVN_ERR(svn_prop_diffs(propchanges, new_version->props, old_version->props,
1181 SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state,
1183 b->db, svn_dirent_join(b->wcroot->abspath,
1186 old_version->props, old_version->props,
1187 *actual_props, *propchanges,
1188 result_pool, scratch_pool));
1190 /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props_internal
1191 relies on NODES row being updated via a different route .
1193 This extra property diff makes sure we clear the actual row when
1194 the final result is unchanged properties. */
1195 SVN_ERR(svn_prop_diffs(&new_propchanges, new_actual_props, new_version->props,
1197 if (!new_propchanges->nelts)
1198 new_actual_props = NULL;
1200 /* Install the new actual props. */
1201 SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath,
1203 svn_wc__has_magic_property(
1207 return SVN_NO_ERROR;
1210 static svn_error_t *
1211 tc_editor_alter_directory(node_move_baton_t *nmb,
1212 const char *dst_relpath,
1213 apr_hash_t *old_props,
1214 apr_hash_t *new_props,
1215 apr_pool_t *scratch_pool)
1217 update_move_baton_t *b = nmb->umb;
1218 working_node_version_t old_version, new_version;
1219 svn_skel_t *work_items = NULL;
1220 svn_skel_t *conflict_skel = NULL;
1221 const char *local_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath,
1223 svn_wc_notify_state_t prop_state;
1224 apr_hash_t *actual_props;
1225 apr_array_header_t *propchanges;
1226 svn_node_kind_t wc_kind;
1227 svn_boolean_t obstructed = FALSE;
1229 SVN_ERR(mark_node_edited(nmb, scratch_pool));
1231 return SVN_NO_ERROR;
1233 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
1234 if (wc_kind != svn_node_none && wc_kind != svn_node_dir)
1236 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1237 svn_node_dir, svn_node_dir,
1238 svn_wc_conflict_reason_obstructed,
1239 svn_wc_conflict_action_edit,
1241 scratch_pool, scratch_pool));
1245 old_version.location_and_kind = b->old_version;
1246 new_version.location_and_kind = b->new_version;
1248 old_version.checksum = NULL; /* not a file */
1249 old_version.props = old_props;
1250 new_version.checksum = NULL; /* not a file */
1251 new_version.props = new_props;
1253 SVN_ERR(update_working_props(&prop_state, &conflict_skel,
1254 &propchanges, &actual_props,
1256 &old_version, &new_version,
1257 scratch_pool, scratch_pool));
1259 if (prop_state == svn_wc_notify_state_conflicted)
1261 const char *move_dst_repos_relpath;
1263 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL,
1264 &move_dst_repos_relpath, NULL, NULL,
1265 NULL, NULL, NULL, NULL, NULL, NULL,
1267 b->wcroot, dst_relpath,
1269 scratch_pool, scratch_pool));
1271 SVN_ERR(create_conflict_markers(&work_items, local_abspath,
1272 b->db, move_dst_repos_relpath,
1273 conflict_skel, b->operation,
1274 &old_version, &new_version,
1275 svn_node_dir, !obstructed,
1276 scratch_pool, scratch_pool));
1279 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1280 svn_wc_notify_update_update,
1282 svn_wc_notify_state_inapplicable,
1284 conflict_skel, work_items, scratch_pool));
1286 return SVN_NO_ERROR;
1289 /* Edit the file found at the move destination, which is initially at
1290 * the old state. Merge the changes into the "working"/"actual" file.
1292 * Merge the difference between OLD_VERSION and NEW_VERSION into
1293 * the working file at LOCAL_RELPATH.
1295 * The term 'old' refers to the pre-update state, which is the state of
1296 * (some layer of) LOCAL_RELPATH while this function runs; and 'new'
1297 * refers to the post-update state, as found at the (base layer of) the
1298 * move source path while this function runs.
1300 * LOCAL_RELPATH is a file in the working copy at WCROOT in DB, and
1301 * REPOS_RELPATH is the repository path it would be committed to.
1303 * Use NOTIFY_FUNC and NOTIFY_BATON for notifications.
1304 * Set *WORK_ITEMS to any required work items, allocated in RESULT_POOL.
1305 * Use SCRATCH_POOL for temporary allocations. */
1306 static svn_error_t *
1307 tc_editor_alter_file(node_move_baton_t *nmb,
1308 const char *dst_relpath,
1309 const svn_checksum_t *old_checksum,
1310 const svn_checksum_t *new_checksum,
1311 apr_hash_t *old_props,
1312 apr_hash_t *new_props,
1313 apr_pool_t *scratch_pool)
1315 update_move_baton_t *b = nmb->umb;
1316 working_node_version_t old_version, new_version;
1317 const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
1320 const char *old_pristine_abspath;
1321 const char *new_pristine_abspath;
1322 svn_skel_t *conflict_skel = NULL;
1323 apr_hash_t *actual_props;
1324 apr_array_header_t *propchanges;
1325 enum svn_wc_merge_outcome_t merge_outcome;
1326 svn_wc_notify_state_t prop_state, content_state;
1327 svn_skel_t *work_item, *work_items = NULL;
1328 svn_node_kind_t wc_kind;
1329 svn_boolean_t obstructed = FALSE;
1331 SVN_ERR(mark_node_edited(nmb, scratch_pool));
1333 return SVN_NO_ERROR;
1335 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
1336 if (wc_kind != svn_node_none && wc_kind != svn_node_file)
1338 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1339 svn_node_file, svn_node_file,
1340 svn_wc_conflict_reason_obstructed,
1341 svn_wc_conflict_action_edit,
1343 scratch_pool, scratch_pool));
1347 old_version.location_and_kind = b->old_version;
1348 new_version.location_and_kind = b->new_version;
1350 old_version.checksum = old_checksum;
1351 old_version.props = old_props;
1352 new_version.checksum = new_checksum;
1353 new_version.props = new_props;
1355 /* ### TODO: Only do this when there is no higher WORKING layer */
1356 SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges,
1357 &actual_props, b, dst_relpath,
1358 &old_version, &new_version,
1359 scratch_pool, scratch_pool));
1362 && !svn_checksum_match(new_version.checksum, old_version.checksum))
1364 svn_boolean_t is_locally_modified;
1366 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
1367 b->db, local_abspath,
1368 FALSE /* exact_comparison */,
1370 if (!is_locally_modified)
1372 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1375 FALSE /* FIXME: use_commit_times? */,
1376 TRUE /* record_file_info */,
1377 scratch_pool, scratch_pool));
1379 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1381 content_state = svn_wc_notify_state_changed;
1386 * Run a 3-way merge to update the file, using the pre-update
1387 * pristine text as the merge base, the post-update pristine
1388 * text as the merge-left version, and the current content of the
1389 * moved-here working file as the merge-right version.
1391 SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath,
1392 b->db, b->wcroot->abspath,
1393 old_version.checksum,
1394 scratch_pool, scratch_pool));
1395 SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
1396 b->db, b->wcroot->abspath,
1397 new_version.checksum,
1398 scratch_pool, scratch_pool));
1399 SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
1400 &merge_outcome, b->db,
1401 old_pristine_abspath,
1402 new_pristine_abspath,
1405 NULL, NULL, NULL, /* diff labels */
1407 FALSE, /* dry-run */
1408 NULL, /* diff3-cmd */
1409 NULL, /* merge options */
1411 b->cancel_func, b->cancel_baton,
1412 scratch_pool, scratch_pool));
1414 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1416 if (merge_outcome == svn_wc_merge_conflict)
1417 content_state = svn_wc_notify_state_conflicted;
1419 content_state = svn_wc_notify_state_merged;
1423 content_state = svn_wc_notify_state_unchanged;
1425 /* If there are any conflicts to be stored, convert them into work items
1429 const char *move_dst_repos_relpath;
1431 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL,
1432 &move_dst_repos_relpath, NULL, NULL,
1433 NULL, NULL, NULL, NULL, NULL, NULL,
1435 b->wcroot, dst_relpath,
1437 scratch_pool, scratch_pool));
1439 SVN_ERR(create_conflict_markers(&work_item, local_abspath, b->db,
1440 move_dst_repos_relpath, conflict_skel,
1441 b->operation, &old_version, &new_version,
1442 svn_node_file, !obstructed,
1443 scratch_pool, scratch_pool));
1445 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1448 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1449 svn_wc_notify_update_update,
1453 conflict_skel, work_items, scratch_pool));
1455 return SVN_NO_ERROR;
1458 static svn_error_t *
1459 tc_editor_update_incoming_moved_file(node_move_baton_t *nmb,
1460 const char *dst_relpath,
1461 const char *src_relpath,
1462 const svn_checksum_t *src_checksum,
1463 const svn_checksum_t *dst_checksum,
1464 apr_hash_t *dst_props,
1465 apr_hash_t *src_props,
1466 svn_boolean_t do_text_merge,
1467 apr_pool_t *scratch_pool)
1469 update_move_baton_t *b = nmb->umb;
1470 working_node_version_t old_version, new_version;
1471 const char *dst_abspath = svn_dirent_join(b->wcroot->abspath,
1474 svn_skel_t *conflict_skel = NULL;
1475 enum svn_wc_merge_outcome_t merge_outcome;
1476 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unchanged;
1477 svn_wc_notify_state_t content_state = svn_wc_notify_state_unchanged;
1478 svn_skel_t *work_item, *work_items = NULL;
1479 svn_node_kind_t dst_kind_on_disk;
1480 const char *dst_repos_relpath;
1481 svn_boolean_t tree_conflict = FALSE;
1482 svn_node_kind_t dst_db_kind;
1485 SVN_ERR(mark_node_edited(nmb, scratch_pool));
1487 return SVN_NO_ERROR;
1489 err = svn_wc__db_base_get_info_internal(NULL, &dst_db_kind, NULL,
1491 NULL, NULL, NULL, NULL, NULL, NULL,
1492 NULL, NULL, NULL, NULL, NULL,
1493 b->wcroot, dst_relpath,
1494 scratch_pool, scratch_pool);
1495 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1497 const char *dst_parent_relpath;
1498 const char *dst_parent_repos_relpath;
1499 const char *src_abspath;
1501 /* If the file cannot be found, it was either deleted at the
1502 * move destination, or it was moved after its parent was moved.
1503 * We cannot deal with this problem right now. Instead, we will
1504 * raise a new tree conflict at the location where this file should
1505 * have been, and let another run of the resolver deal with the
1506 * new conflict later on. */
1508 svn_error_clear(err);
1510 /* Create a WORKING node for this file at the move destination. */
1511 SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
1514 /* Raise a tree conflict at the new WORKING node. */
1515 dst_db_kind = svn_node_none;
1516 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1517 svn_node_file, dst_db_kind,
1518 svn_wc_conflict_reason_edited,
1519 svn_wc_conflict_action_delete,
1520 NULL, scratch_pool, scratch_pool));
1521 dst_parent_relpath = svn_relpath_dirname(dst_relpath, scratch_pool);
1522 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
1523 &dst_parent_repos_relpath,
1524 NULL, NULL, NULL, NULL, NULL,
1525 NULL, NULL, NULL, NULL, NULL,
1528 scratch_pool, scratch_pool));
1529 dst_repos_relpath = svn_relpath_join(dst_parent_repos_relpath,
1530 svn_relpath_basename(dst_relpath,
1533 tree_conflict = TRUE;
1535 /* Schedule a copy of the victim's file content to the new node's path. */
1536 src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1538 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1541 FALSE /*FIXME: use_commit_times?*/,
1542 TRUE /* record_file_info */,
1543 scratch_pool, scratch_pool));
1544 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1549 if ((dst_db_kind == svn_node_none || dst_db_kind != svn_node_file) &&
1550 conflict_skel == NULL)
1552 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1553 svn_node_file, dst_db_kind,
1554 dst_db_kind == svn_node_none
1555 ? svn_wc_conflict_reason_missing
1556 : svn_wc_conflict_reason_obstructed,
1557 svn_wc_conflict_action_edit,
1559 scratch_pool, scratch_pool));
1560 tree_conflict = TRUE;
1563 SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind_on_disk, scratch_pool));
1564 if ((dst_kind_on_disk == svn_node_none || dst_kind_on_disk != svn_node_file)
1565 && conflict_skel == NULL)
1567 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1568 svn_node_file, dst_kind_on_disk,
1569 dst_kind_on_disk == svn_node_none
1570 ? svn_wc_conflict_reason_missing
1571 : svn_wc_conflict_reason_obstructed,
1572 svn_wc_conflict_action_edit,
1574 scratch_pool, scratch_pool));
1575 tree_conflict = TRUE;
1578 old_version.location_and_kind = b->old_version;
1579 new_version.location_and_kind = b->new_version;
1581 old_version.checksum = src_checksum;
1582 old_version.props = src_props;
1583 new_version.checksum = dst_checksum;
1584 new_version.props = dst_props;
1586 /* Merge properties and text content if there is no tree conflict. */
1587 if (conflict_skel == NULL)
1589 apr_hash_t *actual_props;
1590 apr_array_header_t *propchanges;
1592 SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges,
1593 &actual_props, b, dst_relpath,
1594 &old_version, &new_version,
1595 scratch_pool, scratch_pool));
1598 const char *old_pristine_abspath;
1599 const char *src_abspath;
1600 const char *label_left;
1601 const char *label_target;
1604 * Run a 3-way merge to update the file at its post-move location,
1605 * using the pre-move file's pristine text as the merge base, the
1606 * post-move content as the merge-right version, and the current
1607 * content of the working file at the pre-move location as the
1608 * merge-left version.
1610 SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath,
1611 b->db, b->wcroot->abspath,
1613 scratch_pool, scratch_pool));
1614 src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1616 label_left = apr_psprintf(scratch_pool, ".r%ld",
1617 b->old_version->peg_rev);
1618 label_target = apr_psprintf(scratch_pool, ".r%ld",
1619 b->new_version->peg_rev);
1620 SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
1621 &merge_outcome, b->db,
1622 old_pristine_abspath,
1630 FALSE, /* dry-run */
1631 NULL, /* diff3-cmd */
1632 NULL, /* merge options */
1634 b->cancel_func, b->cancel_baton,
1635 scratch_pool, scratch_pool));
1637 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1639 if (merge_outcome == svn_wc_merge_conflict)
1640 content_state = svn_wc_notify_state_conflicted;
1642 content_state = svn_wc_notify_state_merged;
1646 /* If there are any conflicts to be stored, convert them into work items
1650 SVN_ERR(create_conflict_markers(&work_item, dst_abspath, b->db,
1651 dst_repos_relpath, conflict_skel,
1652 b->operation, &old_version, &new_version,
1653 svn_node_file, !tree_conflict,
1654 scratch_pool, scratch_pool));
1656 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1659 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1660 svn_wc_notify_update_update,
1664 conflict_skel, work_items, scratch_pool));
1666 return SVN_NO_ERROR;
1669 static svn_error_t *
1670 tc_editor_delete(node_move_baton_t *nmb,
1671 const char *relpath,
1672 svn_node_kind_t old_kind,
1673 svn_node_kind_t new_kind,
1674 apr_pool_t *scratch_pool)
1676 update_move_baton_t *b = nmb->umb;
1677 svn_sqlite__stmt_t *stmt;
1678 const char *local_abspath;
1679 svn_boolean_t is_modified, is_all_deletes;
1680 svn_skel_t *work_items = NULL;
1681 svn_skel_t *conflict = NULL;
1683 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1685 return SVN_NO_ERROR;
1687 /* Check before retracting delete to catch delete-delete
1688 conflicts. This catches conflicts on the node itself; deleted
1689 children are caught as local modifications below.*/
1692 SVN_ERR(mark_tc_on_op_root(nmb,
1694 svn_wc_conflict_action_delete,
1696 return SVN_NO_ERROR;
1699 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
1700 SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes,
1701 nmb->umb->db, local_abspath, FALSE,
1702 NULL, NULL, scratch_pool));
1705 svn_wc_conflict_reason_t reason;
1707 /* No conflict means no NODES rows at the relpath op-depth
1708 so it's easy to convert the modified tree into a copy.
1710 Note the following assumptions for relpath:
1711 * it is not shadowed
1712 * it is not the/an op-root. (or we can't make us a copy)
1715 SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1716 NULL, NULL, scratch_pool));
1718 reason = svn_wc_conflict_reason_edited;
1720 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
1721 old_kind, new_kind, reason,
1722 (new_kind == svn_node_none)
1723 ? svn_wc_conflict_action_delete
1724 : svn_wc_conflict_action_replace,
1726 scratch_pool, scratch_pool));
1731 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1732 const char *del_abspath;
1733 svn_boolean_t have_row;
1735 /* Get all descendants of the node in reverse order (so children are
1736 handled before their parents, but not strictly depth first) */
1737 SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb,
1738 STMT_SELECT_DESCENDANTS_OP_DEPTH_RV));
1739 SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath,
1741 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1745 svn_skel_t *work_item;
1746 svn_node_kind_t del_kind;
1748 svn_pool_clear(iterpool);
1750 del_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1751 del_abspath = svn_dirent_join(b->wcroot->abspath,
1752 svn_sqlite__column_text(stmt, 0, NULL),
1754 if (del_kind == svn_node_dir)
1755 err = svn_wc__wq_build_dir_remove(&work_item, b->db,
1756 b->wcroot->abspath, del_abspath,
1757 FALSE /* recursive */,
1758 iterpool, iterpool);
1760 err = svn_wc__wq_build_file_remove(&work_item, b->db,
1761 b->wcroot->abspath, del_abspath,
1762 iterpool, iterpool);
1764 err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool);
1766 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1768 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1770 SVN_ERR(svn_sqlite__reset(stmt));
1772 if (old_kind == svn_node_dir)
1773 SVN_ERR(svn_wc__wq_build_dir_remove(&work_items, b->db,
1774 b->wcroot->abspath, local_abspath,
1775 FALSE /* recursive */,
1776 scratch_pool, iterpool));
1778 SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db,
1779 b->wcroot->abspath, local_abspath,
1780 scratch_pool, iterpool));
1782 svn_pool_destroy(iterpool);
1785 /* Only notify if add_file/add_dir is not going to notify */
1786 if (conflict || (new_kind == svn_node_none))
1787 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
1788 svn_wc_notify_update_delete,
1790 svn_wc_notify_state_inapplicable,
1791 svn_wc_notify_state_inapplicable,
1792 conflict, work_items, scratch_pool));
1793 else if (work_items)
1794 SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items,
1797 return SVN_NO_ERROR;
1800 /* Handle node deletion for an incoming move. */
1801 static svn_error_t *
1802 tc_incoming_editor_delete(node_move_baton_t *nmb,
1803 const char *relpath,
1804 svn_node_kind_t old_kind,
1805 svn_node_kind_t new_kind,
1806 apr_pool_t *scratch_pool)
1808 update_move_baton_t *b = nmb->umb;
1809 svn_sqlite__stmt_t *stmt;
1810 const char *local_abspath;
1811 svn_boolean_t is_modified, is_all_deletes;
1812 svn_skel_t *work_items = NULL;
1813 svn_skel_t *conflict = NULL;
1815 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1817 return SVN_NO_ERROR;
1819 /* Check before retracting delete to catch delete-delete
1820 conflicts. This catches conflicts on the node itself; deleted
1821 children are caught as local modifications below.*/
1824 SVN_ERR(mark_tc_on_op_root(nmb,
1826 svn_wc_conflict_action_delete,
1828 return SVN_NO_ERROR;
1831 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
1832 SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes,
1833 nmb->umb->db, local_abspath, FALSE,
1834 NULL, NULL, scratch_pool));
1837 svn_wc_conflict_reason_t reason;
1839 /* No conflict means no NODES rows at the relpath op-depth
1840 so it's easy to convert the modified tree into a copy.
1842 Note the following assumptions for relpath:
1843 * it is not shadowed
1844 * it is not the/an op-root. (or we can't make us a copy)
1847 SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1848 NULL, NULL, scratch_pool));
1850 reason = svn_wc_conflict_reason_edited;
1852 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
1853 old_kind, new_kind, reason,
1854 (new_kind == svn_node_none)
1855 ? svn_wc_conflict_action_delete
1856 : svn_wc_conflict_action_replace,
1858 scratch_pool, scratch_pool));
1863 /* Delete the WORKING node at DST_RELPATH. */
1864 SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb,
1865 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
1866 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
1867 b->wcroot->wc_id, relpath,
1868 0, relpath_depth(relpath)));
1869 SVN_ERR(svn_sqlite__step_done(stmt));
1872 /* Only notify if add_file/add_dir is not going to notify */
1873 if (conflict || (new_kind == svn_node_none))
1874 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
1875 svn_wc_notify_update_delete,
1877 svn_wc_notify_state_inapplicable,
1878 svn_wc_notify_state_inapplicable,
1879 conflict, work_items, scratch_pool));
1880 else if (work_items)
1881 SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items,
1884 return SVN_NO_ERROR;
1890 * The scenario is that a subtree has been locally moved, and then the base
1891 * layer on the source side of the move has received an update to a new
1892 * state. The destination subtree has not yet been updated, and still
1893 * matches the pre-update state of the source subtree.
1895 * The edit driver drives the receiver with the difference between the
1896 * pre-update state (as found now at the move-destination) and the
1897 * post-update state (found now at the move-source).
1899 * We currently assume that both the pre-update and post-update states are
1903 /* Return *PROPS, *CHECKSUM, *CHILDREN and *KIND for LOCAL_RELPATH at
1904 OP_DEPTH provided the row exists. Return *KIND of svn_node_none if
1905 the row does not exist, or only describes a delete of a lower op-depth.
1906 *CHILDREN is a sorted array of basenames of type 'const char *', rather
1907 than a hash, to allow the driver to process children in a defined order. */
1908 static svn_error_t *
1909 get_info(apr_hash_t **props,
1910 const svn_checksum_t **checksum,
1911 apr_array_header_t **children,
1912 svn_node_kind_t *kind,
1913 const char *local_relpath,
1915 svn_wc__db_wcroot_t *wcroot,
1916 apr_pool_t *result_pool,
1917 apr_pool_t *scratch_pool)
1919 svn_wc__db_status_t status;
1920 const char *repos_relpath;
1921 svn_node_kind_t db_kind;
1924 err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL,
1925 NULL, NULL, NULL, NULL, checksum, NULL,
1927 wcroot, local_relpath, op_depth,
1928 result_pool, scratch_pool);
1930 /* If there is no node at this depth, or only a node that describes a delete
1931 of a lower layer we report this node as not existing. */
1932 if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1933 || (!err && status != svn_wc__db_status_added
1934 && status != svn_wc__db_status_normal))
1936 svn_error_clear(err);
1939 *kind = svn_node_none;
1945 *children = apr_array_make(result_pool, 0, sizeof(const char *));
1947 return SVN_NO_ERROR;
1955 if (children && db_kind == svn_node_dir)
1957 svn_sqlite__stmt_t *stmt;
1958 svn_boolean_t have_row;
1960 *children = apr_array_make(result_pool, 16, sizeof(const char *));
1961 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1962 STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS));
1963 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1965 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1968 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1970 APR_ARRAY_PUSH(*children, const char *)
1971 = svn_relpath_basename(child_relpath, result_pool);
1973 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1975 SVN_ERR(svn_sqlite__reset(stmt));
1978 *children = apr_array_make(result_pool, 0, sizeof(const char *));
1980 return SVN_NO_ERROR;
1983 /* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties,
1984 FALSE otherwise. SRC_PROPS and DST_PROPS are standard property
1986 static svn_error_t *
1987 props_match(svn_boolean_t *match,
1988 apr_hash_t *src_props,
1989 apr_hash_t *dst_props,
1990 apr_pool_t *scratch_pool)
1992 if (!src_props && !dst_props)
1994 else if (!src_props || ! dst_props)
1998 apr_array_header_t *propdiffs;
2000 SVN_ERR(svn_prop_diffs(&propdiffs, src_props, dst_props, scratch_pool));
2001 *match = propdiffs->nelts ? FALSE : TRUE;
2003 return SVN_NO_ERROR;
2006 /* ### Drive TC_EDITOR so as to ...
2008 static svn_error_t *
2009 update_moved_away_node(node_move_baton_t *nmb,
2010 svn_wc__db_wcroot_t *wcroot,
2011 const char *src_relpath,
2012 const char *dst_relpath,
2013 apr_pool_t *scratch_pool)
2015 update_move_baton_t *b = nmb->umb;
2016 svn_node_kind_t src_kind, dst_kind;
2017 const svn_checksum_t *src_checksum, *dst_checksum;
2018 apr_hash_t *src_props, *dst_props;
2019 apr_array_header_t *src_children, *dst_children;
2022 SVN_ERR(b->cancel_func(b->cancel_baton));
2024 SVN_ERR(get_info(&src_props, &src_checksum, &src_children, &src_kind,
2025 src_relpath, b->src_op_depth,
2026 wcroot, scratch_pool, scratch_pool));
2028 SVN_ERR(get_info(&dst_props, &dst_checksum, &dst_children, &dst_kind,
2029 dst_relpath, b->dst_op_depth,
2030 wcroot, scratch_pool, scratch_pool));
2032 if (src_kind == svn_node_none
2033 || (dst_kind != svn_node_none && src_kind != dst_kind))
2035 SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind,
2040 return SVN_NO_ERROR;
2042 if (src_kind != svn_node_none && src_kind != dst_kind)
2044 if (src_kind == svn_node_file || src_kind == svn_node_symlink)
2046 SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind,
2047 src_checksum, src_props, scratch_pool));
2049 else if (src_kind == svn_node_dir)
2051 SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind,
2052 src_props, scratch_pool));
2055 else if (src_kind != svn_node_none)
2057 svn_boolean_t props_equal;
2059 SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool));
2061 if (src_kind == svn_node_file || src_kind == svn_node_symlink)
2063 if (!props_equal || !svn_checksum_match(src_checksum, dst_checksum))
2064 SVN_ERR(tc_editor_alter_file(nmb, dst_relpath,
2065 dst_checksum, src_checksum,
2066 dst_props, src_props, scratch_pool));
2068 else if (src_kind == svn_node_dir)
2071 SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
2072 dst_props, src_props,
2078 return SVN_NO_ERROR;
2080 if (src_kind == svn_node_dir)
2082 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2085 while (i < src_children->nelts || j < dst_children->nelts)
2087 const char *child_name;
2088 svn_boolean_t src_only = FALSE, dst_only = FALSE;
2089 node_move_baton_t cnmb = { 0 };
2092 cnmb.umb = nmb->umb;
2093 cnmb.shadowed = nmb->shadowed;
2095 svn_pool_clear(iterpool);
2096 if (i >= src_children->nelts)
2099 child_name = APR_ARRAY_IDX(dst_children, j, const char *);
2101 else if (j >= dst_children->nelts)
2104 child_name = APR_ARRAY_IDX(src_children, i, const char *);
2108 const char *src_name = APR_ARRAY_IDX(src_children, i,
2110 const char *dst_name = APR_ARRAY_IDX(dst_children, j,
2112 int cmp = strcmp(src_name, dst_name);
2119 child_name = dst_only ? dst_name : src_name;
2122 cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
2124 cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
2128 SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot,
2129 cnmb.dst_relpath, b->dst_op_depth,
2132 SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath,
2133 cnmb.dst_relpath, iterpool));
2140 if (nmb->skip) /* Does parent now want a skip? */
2145 return SVN_NO_ERROR;
2148 static svn_error_t *
2149 suitable_for_move(svn_wc__db_wcroot_t *wcroot,
2150 const char *local_relpath,
2151 apr_pool_t *scratch_pool)
2153 svn_sqlite__stmt_t *stmt;
2154 svn_boolean_t have_row;
2155 svn_revnum_t revision;
2156 const char *repos_relpath;
2157 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2159 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2160 STMT_SELECT_BASE_NODE));
2161 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2162 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2164 return svn_error_trace(svn_sqlite__reset(stmt));
2166 revision = svn_sqlite__column_revnum(stmt, 4);
2167 repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
2169 SVN_ERR(svn_sqlite__reset(stmt));
2171 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2172 STMT_SELECT_REPOS_PATH_REVISION));
2173 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2174 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2177 svn_revnum_t node_revision = svn_sqlite__column_revnum(stmt, 2);
2178 const char *child_relpath;
2179 const char *relpath;
2181 svn_pool_clear(iterpool);
2183 child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2184 relpath = svn_relpath_skip_ancestor(local_relpath, child_relpath);
2185 relpath = svn_relpath_join(repos_relpath, relpath, iterpool);
2187 if (strcmp(relpath, svn_sqlite__column_text(stmt, 1, NULL)))
2188 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2189 svn_sqlite__reset(stmt),
2190 _("Cannot apply update because '%s' is a "
2191 "switched path (please switch it back to "
2192 "its original URL and try again)"),
2193 path_for_error_message(wcroot, child_relpath,
2196 if (revision != node_revision)
2197 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2198 svn_sqlite__reset(stmt),
2199 _("Cannot apply update because '%s' is a "
2200 "mixed-revision working copy (please "
2201 "update and try again)"),
2202 path_for_error_message(wcroot, local_relpath,
2205 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2207 SVN_ERR(svn_sqlite__reset(stmt));
2209 svn_pool_destroy(iterpool);
2211 return SVN_NO_ERROR;
2214 /* The body of svn_wc__db_update_moved_away_conflict_victim(), which see.
2216 static svn_error_t *
2217 update_moved_away_conflict_victim(svn_revnum_t *old_rev,
2218 svn_revnum_t *new_rev,
2220 svn_wc__db_wcroot_t *wcroot,
2221 const char *local_relpath,
2222 const char *delete_relpath,
2223 svn_wc_operation_t operation,
2224 svn_wc_conflict_action_t action,
2225 svn_wc_conflict_reason_t reason,
2226 svn_cancel_func_t cancel_func,
2228 apr_pool_t *scratch_pool)
2230 update_move_baton_t umb = { NULL };
2231 const char *src_relpath, *dst_relpath;
2232 svn_wc_conflict_version_t old_version;
2233 svn_wc_conflict_version_t new_version;
2234 apr_int64_t repos_id;
2235 node_move_baton_t nmb = { 0 };
2237 SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath));
2239 /* Construct editor baton. */
2241 SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot,
2242 local_relpath, relpath_depth(delete_relpath),
2245 SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL,
2246 wcroot, local_relpath,
2248 scratch_pool, scratch_pool));
2250 if (dst_relpath == NULL)
2251 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2252 _("The node '%s' has not been moved away"),
2253 path_for_error_message(wcroot, local_relpath,
2256 umb.dst_op_depth = relpath_depth(dst_relpath);
2258 SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
2259 SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
2262 SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_version.node_kind,
2263 &new_version.peg_rev,
2264 &new_version.path_in_repos, &repos_id,
2265 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2267 wcroot, src_relpath, umb.src_op_depth,
2268 scratch_pool, scratch_pool));
2270 SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
2271 &new_version.repos_uuid,
2275 SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind,
2276 &old_version.peg_rev,
2277 &old_version.path_in_repos, &repos_id,
2278 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2280 wcroot, dst_relpath, umb.dst_op_depth,
2281 scratch_pool, scratch_pool));
2283 SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
2284 &old_version.repos_uuid,
2287 *old_rev = old_version.peg_rev;
2288 *new_rev = new_version.peg_rev;
2290 umb.operation = operation;
2291 umb.old_version= &old_version;
2292 umb.new_version= &new_version;
2294 umb.wcroot = wcroot;
2295 umb.cancel_func = cancel_func;
2296 umb.cancel_baton = cancel_baton;
2298 if (umb.src_op_depth == 0)
2299 SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool));
2301 /* Create a new, and empty, list for notification information. */
2302 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2303 STMT_CREATE_UPDATE_MOVE_LIST));
2305 /* Drive the editor... */
2308 nmb.src_relpath = src_relpath;
2309 nmb.dst_relpath = dst_relpath;
2310 /* nmb.shadowed = FALSE; */
2311 /* nmb.edited = FALSE; */
2312 /* nmb.skip_children = FALSE; */
2314 /* We walk the move source (i.e. the post-update tree), comparing each node
2315 * with the equivalent node at the move destination and applying the update
2316 * to nodes at the move destination. */
2317 SVN_ERR(update_moved_away_node(&nmb, wcroot, src_relpath, dst_relpath,
2320 SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath,
2322 dst_relpath, NULL, NULL,
2325 return SVN_NO_ERROR;
2330 svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db,
2331 const char *local_abspath,
2332 const char *delete_op_abspath,
2333 svn_wc_operation_t operation,
2334 svn_wc_conflict_action_t action,
2335 svn_wc_conflict_reason_t reason,
2336 svn_cancel_func_t cancel_func,
2338 svn_wc_notify_func2_t notify_func,
2340 apr_pool_t *scratch_pool)
2342 svn_wc__db_wcroot_t *wcroot;
2343 svn_revnum_t old_rev, new_rev;
2344 const char *local_relpath;
2345 const char *delete_relpath;
2347 /* ### Check for mixed-rev src or dst? */
2349 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2351 scratch_pool, scratch_pool));
2352 VERIFY_USABLE_WCROOT(wcroot);
2355 = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath);
2357 SVN_WC__DB_WITH_TXN(
2358 update_moved_away_conflict_victim(
2360 db, wcroot, local_relpath, delete_relpath,
2361 operation, action, reason,
2362 cancel_func, cancel_baton,
2366 /* Send all queued up notifications. */
2367 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev,
2368 notify_func, notify_baton,
2372 svn_wc_notify_t *notify;
2374 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2377 svn_wc_notify_update_completed,
2379 notify->kind = svn_node_none;
2380 notify->content_state = svn_wc_notify_state_inapplicable;
2381 notify->prop_state = svn_wc_notify_state_inapplicable;
2382 notify->revision = new_rev;
2383 notify_func(notify_baton, notify, scratch_pool);
2387 return SVN_NO_ERROR;
2390 static svn_error_t *
2391 get_working_info(apr_hash_t **props,
2392 const svn_checksum_t **checksum,
2393 apr_array_header_t **children,
2394 svn_node_kind_t *kind,
2395 const char *local_relpath,
2396 svn_wc__db_wcroot_t *wcroot,
2397 apr_pool_t *result_pool,
2398 apr_pool_t *scratch_pool)
2400 svn_wc__db_status_t status;
2401 const char *repos_relpath;
2402 svn_node_kind_t db_kind;
2405 err = svn_wc__db_read_info_internal(&status, &db_kind, NULL, &repos_relpath,
2406 NULL, NULL, NULL, NULL, NULL,
2408 NULL, NULL, NULL, NULL, NULL,
2409 NULL, NULL, NULL, NULL, NULL,
2410 NULL, NULL, NULL, NULL, NULL,
2411 wcroot, local_relpath,
2412 result_pool, scratch_pool);
2414 /* If there is no node, or only a node that describes a delete
2415 of a lower layer we report this node as not existing. */
2416 if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
2417 || (!err && status != svn_wc__db_status_added
2418 && status != svn_wc__db_status_normal))
2420 svn_error_clear(err);
2423 *kind = svn_node_none;
2429 *children = apr_array_make(result_pool, 0, sizeof(const char *));
2431 return SVN_NO_ERROR;
2436 SVN_ERR(svn_wc__db_read_props_internal(props, wcroot, local_relpath,
2437 result_pool, scratch_pool));
2442 if (children && db_kind == svn_node_dir)
2444 svn_sqlite__stmt_t *stmt;
2445 svn_boolean_t have_row;
2447 *children = apr_array_make(result_pool, 16, sizeof(const char *));
2448 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2449 STMT_SELECT_WORKING_CHILDREN));
2450 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2451 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2454 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2456 APR_ARRAY_PUSH(*children, const char *)
2457 = svn_relpath_basename(child_relpath, result_pool);
2459 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2461 SVN_ERR(svn_sqlite__reset(stmt));
2464 *children = apr_array_make(result_pool, 0, sizeof(const char *));
2466 return SVN_NO_ERROR;
2469 /* Apply changes found in the victim node at SRC_RELPATH to the incoming
2470 * move at DST_RELPATH. */
2471 static svn_error_t *
2472 update_incoming_moved_node(node_move_baton_t *nmb,
2473 svn_wc__db_wcroot_t *wcroot,
2474 const char *src_relpath,
2475 const char *dst_relpath,
2476 apr_pool_t *scratch_pool)
2478 update_move_baton_t *b = nmb->umb;
2479 svn_node_kind_t orig_kind, working_kind;
2480 const char *victim_relpath = src_relpath;
2481 const svn_checksum_t *orig_checksum, *working_checksum;
2482 apr_hash_t *orig_props, *working_props;
2483 apr_array_header_t *orig_children, *working_children;
2486 SVN_ERR(b->cancel_func(b->cancel_baton));
2488 /* Compare the tree conflict victim's copied layer (the "original") with
2489 * the working layer, i.e. look for changes layered on top of the copy. */
2490 SVN_ERR(get_info(&orig_props, &orig_checksum, &orig_children, &orig_kind,
2491 victim_relpath, b->src_op_depth, wcroot, scratch_pool,
2493 SVN_ERR(get_working_info(&working_props, &working_checksum,
2494 &working_children, &working_kind, victim_relpath,
2495 wcroot, scratch_pool, scratch_pool));
2497 if (working_kind == svn_node_none
2498 || (orig_kind != svn_node_none && orig_kind != working_kind))
2500 SVN_ERR(tc_incoming_editor_delete(nmb, dst_relpath, orig_kind,
2501 working_kind, scratch_pool));
2505 return SVN_NO_ERROR;
2507 if (working_kind != svn_node_none && orig_kind != working_kind)
2509 if (working_kind == svn_node_file || working_kind == svn_node_symlink)
2511 const char *victim_abspath;
2512 const char *wctemp_abspath;
2513 svn_stream_t *working_stream;
2514 svn_stream_t *temp_stream;
2515 const char *temp_abspath;
2518 /* Copy the victim's content to a safe place and add it from there. */
2519 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&wctemp_abspath, b->db,
2523 victim_abspath = svn_dirent_join(b->wcroot->abspath,
2524 victim_relpath, scratch_pool);
2525 SVN_ERR(svn_stream_open_readonly(&working_stream, victim_abspath,
2526 scratch_pool, scratch_pool));
2527 SVN_ERR(svn_stream_open_unique(&temp_stream, &temp_abspath,
2528 wctemp_abspath, svn_io_file_del_none,
2529 scratch_pool, scratch_pool));
2530 err = svn_stream_copy3(working_stream, temp_stream,
2531 b->cancel_func, b->cancel_baton,
2533 if (err && err->apr_err == SVN_ERR_CANCELLED)
2537 err2 = svn_io_remove_file2(temp_abspath, TRUE, scratch_pool);
2538 return svn_error_compose_create(err, err2);
2543 SVN_ERR(tc_editor_incoming_add_file(nmb, dst_relpath, orig_kind,
2544 working_checksum, working_props,
2545 victim_relpath, temp_abspath,
2548 else if (working_kind == svn_node_dir)
2550 SVN_ERR(tc_editor_incoming_add_directory(nmb, dst_relpath,
2551 orig_kind, working_props,
2556 else if (working_kind != svn_node_none)
2558 svn_boolean_t props_equal;
2560 SVN_ERR(props_match(&props_equal, orig_props, working_props,
2563 if (working_kind == svn_node_file || working_kind == svn_node_symlink)
2565 svn_boolean_t is_modified;
2567 SVN_ERR(svn_wc__internal_file_modified_p(&is_modified, b->db,
2572 FALSE /* exact_comparison */,
2574 if (!props_equal || is_modified)
2575 SVN_ERR(tc_editor_update_incoming_moved_file(nmb, dst_relpath,
2584 else if (working_kind == svn_node_dir)
2587 SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
2588 orig_props, working_props,
2594 return SVN_NO_ERROR;
2596 if (working_kind == svn_node_dir)
2598 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2601 while (i < orig_children->nelts || j < working_children->nelts)
2603 const char *child_name;
2604 svn_boolean_t orig_only = FALSE, working_only = FALSE;
2605 node_move_baton_t cnmb = { 0 };
2608 cnmb.umb = nmb->umb;
2609 cnmb.shadowed = nmb->shadowed;
2611 svn_pool_clear(iterpool);
2612 if (i >= orig_children->nelts)
2614 working_only = TRUE;
2615 child_name = APR_ARRAY_IDX(working_children, j, const char *);
2617 else if (j >= working_children->nelts)
2620 child_name = APR_ARRAY_IDX(orig_children, i, const char *);
2624 const char *orig_name = APR_ARRAY_IDX(orig_children, i,
2626 const char *working_name = APR_ARRAY_IDX(working_children, j,
2628 int cmp = strcmp(orig_name, working_name);
2631 working_only = TRUE;
2635 child_name = working_only ? working_name : orig_name;
2638 cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
2640 cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
2643 SVN_ERR(update_incoming_moved_node(&cnmb, wcroot, cnmb.src_relpath,
2644 cnmb.dst_relpath, iterpool));
2651 if (nmb->skip) /* Does parent now want a skip? */
2656 return SVN_NO_ERROR;
2659 /* The body of svn_wc__db_update_incoming_move(). */
2660 static svn_error_t *
2661 update_incoming_move(svn_revnum_t *old_rev,
2662 svn_revnum_t *new_rev,
2664 svn_wc__db_wcroot_t *wcroot,
2665 const char *local_relpath,
2666 const char *dst_relpath,
2667 svn_wc_operation_t operation,
2668 svn_wc_conflict_action_t action,
2669 svn_wc_conflict_reason_t reason,
2670 svn_cancel_func_t cancel_func,
2672 apr_pool_t *scratch_pool)
2674 update_move_baton_t umb = { NULL };
2675 svn_wc_conflict_version_t old_version;
2676 svn_wc_conflict_version_t new_version;
2677 apr_int64_t repos_id;
2678 node_move_baton_t nmb = { 0 };
2679 svn_boolean_t is_modified;
2681 SVN_ERR_ASSERT(svn_relpath_skip_ancestor(dst_relpath, local_relpath) == NULL);
2683 /* For incoming moves during update/switch, the move source is a copied
2684 * tree which was copied from the pre-update BASE revision while raising
2685 * the tree conflict, when the update attempted to delete the move source.
2686 * This copy is our "original" state (SRC of the diff) and the local changes
2687 * on top of this copy at the top-most WORKING layer are used to drive the
2688 * editor (DST of the diff).
2690 * The move destination, where changes are applied to, is now in the BASE
2691 * tree at DST_RELPATH. This repository-side move is the "incoming change"
2692 * recorded for any tree conflicts created during the editor drive.
2693 * We assume this path contains no local changes, and create local changes
2694 * in DST_RELPATH corresponding to changes contained in the conflict victim.
2696 * DST_OP_DEPTH is used to infer the "op-root" of the incoming move. This
2697 * "op-root" is virtual because all nodes belonging to the incoming move
2698 * live in the BASE tree. It is used for constructing repository paths
2699 * when new tree conflicts need to be raised.
2701 umb.src_op_depth = relpath_depth(local_relpath); /* SRC of diff */
2702 umb.dst_op_depth = relpath_depth(dst_relpath); /* virtual DST op-root */
2704 SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
2705 SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
2707 /* Make sure there are no local modifications in the move destination. */
2708 SVN_ERR(svn_wc__node_has_local_mods(&is_modified, NULL, db,
2709 svn_dirent_join(wcroot->abspath,
2712 TRUE, cancel_func, cancel_baton,
2715 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2716 _("Cannot merge local changes from '%s' because "
2717 "'%s' already contains other local changes "
2718 "(please commit or revert these other changes "
2720 svn_dirent_local_style(
2721 svn_dirent_join(wcroot->abspath, local_relpath,
2724 svn_dirent_local_style(
2725 svn_dirent_join(wcroot->abspath, dst_relpath,
2729 /* Check for switched subtrees and mixed-revision working copy. */
2730 SVN_ERR(suitable_for_move(wcroot, dst_relpath, scratch_pool));
2732 /* Read version info from the updated incoming post-move location. */
2733 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_version.node_kind,
2734 &new_version.peg_rev,
2735 &new_version.path_in_repos,
2737 NULL, NULL, NULL, NULL, NULL,
2738 NULL, NULL, NULL, NULL, NULL,
2739 wcroot, dst_relpath,
2740 scratch_pool, scratch_pool));
2742 SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
2743 &new_version.repos_uuid,
2747 /* Read version info from the victim's location. */
2748 SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind,
2749 &old_version.peg_rev,
2750 &old_version.path_in_repos, &repos_id,
2751 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2753 local_relpath, umb.src_op_depth,
2754 scratch_pool, scratch_pool));
2756 SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
2757 &old_version.repos_uuid,
2760 *old_rev = old_version.peg_rev;
2761 *new_rev = new_version.peg_rev;
2763 umb.operation = operation;
2764 umb.old_version= &old_version;
2765 umb.new_version= &new_version;
2767 umb.wcroot = wcroot;
2768 umb.cancel_func = cancel_func;
2769 umb.cancel_baton = cancel_baton;
2771 /* Create a new, and empty, list for notification information. */
2772 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2773 STMT_CREATE_UPDATE_MOVE_LIST));
2775 /* Drive the editor... */
2778 nmb.src_relpath = local_relpath;
2779 nmb.dst_relpath = dst_relpath;
2780 /* nmb.shadowed = FALSE; */
2781 /* nmb.edited = FALSE; */
2782 /* nmb.skip_children = FALSE; */
2784 /* We walk the conflict victim, comparing each node with the equivalent node
2785 * at the WORKING layer, applying any local changes to nodes at the move
2787 SVN_ERR(update_incoming_moved_node(&nmb, wcroot, local_relpath, dst_relpath,
2790 return SVN_NO_ERROR;
2794 svn_wc__db_update_incoming_move(svn_wc__db_t *db,
2795 const char *local_abspath,
2796 const char *dest_abspath,
2797 svn_wc_operation_t operation,
2798 svn_wc_conflict_action_t action,
2799 svn_wc_conflict_reason_t reason,
2800 svn_cancel_func_t cancel_func,
2802 svn_wc_notify_func2_t notify_func,
2804 apr_pool_t *scratch_pool)
2806 svn_wc__db_wcroot_t *wcroot;
2807 svn_revnum_t old_rev, new_rev;
2808 const char *local_relpath;
2809 const char *dest_relpath;
2811 /* ### Check for mixed-rev src or dst? */
2813 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2815 scratch_pool, scratch_pool));
2816 VERIFY_USABLE_WCROOT(wcroot);
2819 = svn_dirent_skip_ancestor(wcroot->abspath, dest_abspath);
2821 SVN_WC__DB_WITH_TXN(update_incoming_move(&old_rev, &new_rev, db, wcroot,
2822 local_relpath, dest_relpath,
2823 operation, action, reason,
2824 cancel_func, cancel_baton,
2828 /* Send all queued up notifications. */
2829 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev,
2830 notify_func, notify_baton,
2834 svn_wc_notify_t *notify;
2836 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2839 svn_wc_notify_update_completed,
2841 notify->kind = svn_node_none;
2842 notify->content_state = svn_wc_notify_state_inapplicable;
2843 notify->prop_state = svn_wc_notify_state_inapplicable;
2844 notify->revision = new_rev;
2845 notify_func(notify_baton, notify, scratch_pool);
2849 return SVN_NO_ERROR;
2852 typedef struct update_local_add_baton_t {
2855 svn_wc__db_wcroot_t *wcroot;
2856 svn_cancel_func_t cancel_func;
2859 /* We refer to these if raising new tree conflicts. */
2860 const svn_wc_conflict_version_t *new_version;
2861 } update_local_add_baton_t;
2863 typedef struct added_node_baton_t {
2864 struct update_local_add_baton_t *b;
2865 struct added_node_baton_t *pb;
2866 const char *local_relpath;
2868 svn_boolean_t edited;
2869 } added_node_baton_t;
2872 static svn_error_t *
2873 update_local_add_mark_node_edited(added_node_baton_t *nb,
2874 apr_pool_t *scratch_pool)
2877 return SVN_NO_ERROR;
2881 SVN_ERR(update_local_add_mark_node_edited(nb->pb, scratch_pool));
2889 return SVN_NO_ERROR;
2892 static svn_error_t *
2893 update_local_add_mark_parent_edited(added_node_baton_t *nb,
2894 apr_pool_t *scratch_pool)
2896 SVN_ERR_ASSERT(nb && nb->pb);
2898 SVN_ERR(update_local_add_mark_node_edited(nb->pb, scratch_pool));
2903 return SVN_NO_ERROR;
2906 static svn_error_t *
2907 mark_update_add_add_tree_conflict(added_node_baton_t *nb,
2908 svn_node_kind_t base_kind,
2909 svn_node_kind_t working_kind,
2910 svn_wc_conflict_reason_t local_change,
2911 apr_pool_t *result_pool,
2912 apr_pool_t *scratch_pool)
2915 svn_wc__db_t *db = nb->b->db;
2916 svn_wc__db_wcroot_t *wcroot = nb->b->wcroot;
2917 svn_wc_conflict_version_t *new_version;
2918 svn_skel_t *conflict;
2920 new_version = svn_wc_conflict_version_dup(nb->b->new_version, result_pool);
2922 /* Fill in conflict info templates with info for this node. */
2923 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, &new_version->peg_rev,
2924 &new_version->path_in_repos,
2925 NULL, NULL, NULL, NULL, NULL, NULL,
2926 NULL, NULL, NULL, NULL, NULL,
2927 wcroot, nb->local_relpath,
2928 scratch_pool, scratch_pool));
2929 new_version->node_kind = base_kind;
2931 SVN_ERR(create_tree_conflict(&conflict, wcroot, nb->local_relpath,
2932 nb->local_relpath, db, NULL, new_version,
2933 svn_wc_operation_update,
2934 svn_node_none, base_kind, NULL,
2935 local_change, svn_wc_conflict_action_add,
2936 NULL, scratch_pool, scratch_pool));
2938 SVN_ERR(update_move_list_add(wcroot, nb->local_relpath, db,
2939 svn_wc_notify_tree_conflict, working_kind,
2940 svn_wc_notify_state_inapplicable,
2941 svn_wc_notify_state_inapplicable,
2942 conflict, NULL, scratch_pool));
2943 return SVN_NO_ERROR;
2946 static svn_error_t *
2947 update_local_add_notify_obstructed_or_missing(added_node_baton_t *nb,
2948 svn_node_kind_t working_kind,
2949 svn_node_kind_t kind_on_disk,
2950 apr_pool_t *scratch_pool)
2952 svn_wc_notify_state_t content_state;
2954 if (kind_on_disk == svn_node_none)
2955 content_state = svn_wc_notify_state_missing;
2957 content_state = svn_wc_notify_state_obstructed;
2959 SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
2960 svn_wc_notify_skip, working_kind,
2961 content_state, svn_wc_notify_state_inapplicable,
2962 NULL, NULL, scratch_pool));
2963 return SVN_NO_ERROR;
2966 static svn_error_t *
2967 tc_editor_update_add_new_file(added_node_baton_t *nb,
2968 svn_node_kind_t base_kind,
2969 const svn_checksum_t *base_checksum,
2970 apr_hash_t *base_props,
2971 svn_node_kind_t working_kind,
2972 const svn_checksum_t *working_checksum,
2973 apr_hash_t *working_props,
2974 apr_pool_t *scratch_pool)
2976 const char *local_abspath;
2977 svn_node_kind_t kind_on_disk;
2979 SVN_ERR(update_local_add_mark_parent_edited(nb, scratch_pool));
2981 return SVN_NO_ERROR;
2983 if (base_kind != svn_node_none)
2985 SVN_ERR(mark_update_add_add_tree_conflict(nb, base_kind, svn_node_file,
2986 svn_wc_conflict_reason_added,
2987 scratch_pool, scratch_pool));
2989 return SVN_NO_ERROR;
2992 /* Check for obstructions. */
2993 local_abspath = svn_dirent_join(nb->b->wcroot->abspath, nb->local_relpath,
2995 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
2996 if (kind_on_disk != svn_node_file)
2998 SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, working_kind,
3002 return SVN_NO_ERROR;
3005 /* Nothing else to do. Locally added files are an op-root in NODES. */
3007 SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
3008 svn_wc_notify_update_add, svn_node_file,
3009 svn_wc_notify_state_inapplicable,
3010 svn_wc_notify_state_inapplicable,
3011 NULL, NULL, scratch_pool));
3012 return SVN_NO_ERROR;
3015 static svn_error_t *
3016 tc_editor_update_add_new_directory(added_node_baton_t *nb,
3017 svn_node_kind_t base_kind,
3018 apr_hash_t *base_props,
3019 apr_hash_t *working_props,
3020 apr_pool_t *scratch_pool)
3022 const char *local_abspath;
3023 svn_node_kind_t kind_on_disk;
3025 SVN_ERR(update_local_add_mark_parent_edited(nb, scratch_pool));
3027 return SVN_NO_ERROR;
3029 if (base_kind != svn_node_none)
3031 SVN_ERR(mark_update_add_add_tree_conflict(nb, base_kind, svn_node_dir,
3032 svn_wc_conflict_reason_added,
3033 scratch_pool, scratch_pool));
3035 return SVN_NO_ERROR;
3038 /* Check for obstructions. */
3039 local_abspath = svn_dirent_join(nb->b->wcroot->abspath, nb->local_relpath,
3041 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3042 if (kind_on_disk != svn_node_dir)
3044 SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_dir,
3048 return SVN_NO_ERROR;
3051 /* Nothing else to do. Locally added directories are an op-root in NODES. */
3053 SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
3054 svn_wc_notify_update_add, svn_node_dir,
3055 svn_wc_notify_state_inapplicable,
3056 svn_wc_notify_state_inapplicable,
3057 NULL, NULL, scratch_pool));
3058 return SVN_NO_ERROR;
3061 static svn_error_t *
3062 update_incoming_add_merge_props(svn_wc_notify_state_t *prop_state,
3063 svn_skel_t **conflict_skel,
3064 const char *local_relpath,
3065 apr_hash_t *base_props,
3066 apr_hash_t *working_props,
3068 svn_wc__db_wcroot_t *wcroot,
3069 apr_pool_t *result_pool,
3070 apr_pool_t *scratch_pool)
3072 apr_hash_t *new_actual_props;
3073 apr_array_header_t *propchanges;
3074 const char *local_abspath = svn_dirent_join(wcroot->abspath,
3079 * Run a 3-way prop merge to update the props, using the empty props
3080 * as the merge base, the post-update props as the merge-left version, and
3081 * the current props of the added working file as the merge-right version.
3083 SVN_ERR(svn_prop_diffs(&propchanges, working_props,
3084 apr_hash_make(scratch_pool), scratch_pool));
3085 SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state, &new_actual_props,
3087 apr_hash_make(scratch_pool),
3088 base_props, working_props, propchanges,
3089 result_pool, scratch_pool));
3091 /* Install the new actual props. */
3092 if (apr_hash_count(new_actual_props) > 0)
3093 SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath,
3095 svn_wc__has_magic_property(
3099 return SVN_NO_ERROR;
3102 static svn_error_t *
3103 tc_editor_update_add_merge_files(added_node_baton_t *nb,
3104 const svn_checksum_t *working_checksum,
3105 const svn_checksum_t *base_checksum,
3106 apr_hash_t *working_props,
3107 apr_hash_t *base_props,
3108 apr_pool_t *scratch_pool)
3110 update_local_add_baton_t *b = nb->b;
3111 apr_array_header_t *propchanges;
3112 svn_boolean_t is_modified;
3113 enum svn_wc_merge_outcome_t merge_outcome;
3114 svn_skel_t *conflict_skel = NULL;
3115 svn_wc_notify_state_t prop_state, content_state;
3116 svn_skel_t *work_items = NULL;
3117 svn_node_kind_t kind_on_disk;
3118 const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
3122 SVN_ERR(update_local_add_mark_node_edited(nb, scratch_pool));
3124 return SVN_NO_ERROR;
3126 /* Check for on-disk obstructions or missing files. */
3127 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3128 if (kind_on_disk != svn_node_file)
3130 SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_file,
3134 return SVN_NO_ERROR;
3137 SVN_ERR(update_incoming_add_merge_props(&prop_state, &conflict_skel,
3139 base_props, working_props,
3141 scratch_pool, scratch_pool));
3143 SVN_ERR(svn_wc__internal_file_modified_p(&is_modified,
3144 b->db, local_abspath,
3145 FALSE /* exact_comparison */,
3149 svn_skel_t *work_item = NULL;
3151 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
3152 local_abspath, NULL,
3153 /* FIXME: use_commit_times? */
3155 TRUE, /* record_file_info */
3156 scratch_pool, scratch_pool));
3157 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
3158 content_state = svn_wc_notify_state_changed;
3162 const char *empty_file_abspath;
3163 const char *pristine_abspath;
3164 svn_skel_t *work_item = NULL;
3167 * Run a 3-way merge to update the file, using the empty file
3168 * merge base, the post-update pristine text as the merge-left version,
3169 * and the locally added content of the working file as the merge-right
3172 SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file_abspath, NULL,
3173 svn_io_file_del_on_pool_cleanup,
3174 scratch_pool, scratch_pool));
3175 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, b->db,
3176 b->wcroot->abspath, base_checksum,
3177 scratch_pool, scratch_pool));
3179 /* Create a property diff which shows all props as added. */
3180 SVN_ERR(svn_prop_diffs(&propchanges, working_props,
3181 apr_hash_make(scratch_pool), scratch_pool));
3183 SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
3184 &merge_outcome, b->db,
3189 NULL, NULL, NULL, /* diff labels */
3190 apr_hash_make(scratch_pool),
3191 FALSE, /* dry-run */
3192 NULL, /* diff3-cmd */
3193 NULL, /* merge options */
3195 b->cancel_func, b->cancel_baton,
3196 scratch_pool, scratch_pool));
3198 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
3200 if (merge_outcome == svn_wc_merge_conflict)
3201 content_state = svn_wc_notify_state_conflicted;
3203 content_state = svn_wc_notify_state_merged;
3206 /* If there are any conflicts to be stored, convert them into work items
3210 svn_wc_conflict_version_t *new_version;
3211 svn_node_kind_t new_kind;
3212 svn_revnum_t new_rev;
3213 const char *repos_relpath;
3215 new_version = svn_wc_conflict_version_dup(nb->b->new_version,
3217 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev,
3218 &repos_relpath, NULL, NULL,
3219 NULL, NULL, NULL, NULL, NULL,
3220 NULL, NULL, NULL, NULL,
3221 b->wcroot, nb->local_relpath,
3222 scratch_pool, scratch_pool));
3223 /* Fill in conflict info templates with info for this node. */
3224 new_version->path_in_repos = repos_relpath;
3225 new_version->node_kind = new_kind;
3226 new_version->peg_rev = new_rev;
3228 /* Create conflict markers. */
3229 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, NULL,
3230 new_version, scratch_pool,
3232 if (prop_state == svn_wc_notify_state_conflicted)
3233 SVN_ERR(svn_wc__conflict_create_markers(&work_items, b->db,
3240 SVN_ERR(update_move_list_add(b->wcroot, nb->local_relpath, b->db,
3241 svn_wc_notify_update_update,
3242 svn_node_file, content_state, prop_state,
3243 conflict_skel, work_items, scratch_pool));
3245 return SVN_NO_ERROR;
3248 static svn_error_t *
3249 tc_editor_update_add_merge_dirprops(added_node_baton_t *nb,
3250 apr_hash_t *working_props,
3251 apr_hash_t *base_props,
3252 apr_pool_t *scratch_pool)
3254 update_local_add_baton_t *b = nb->b;
3255 svn_skel_t *conflict_skel = NULL;
3256 svn_wc_notify_state_t prop_state;
3257 svn_skel_t *work_items = NULL;
3258 svn_node_kind_t kind_on_disk;
3259 const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
3263 SVN_ERR(update_local_add_mark_node_edited(nb, scratch_pool));
3265 return SVN_NO_ERROR;
3267 /* Check for on-disk obstructions or missing files. */
3268 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3269 if (kind_on_disk != svn_node_dir)
3271 SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_dir,
3275 return SVN_NO_ERROR;
3278 SVN_ERR(update_incoming_add_merge_props(&prop_state, &conflict_skel,
3280 base_props, working_props,
3282 scratch_pool, scratch_pool));
3284 /* If there are any conflicts to be stored, convert them into work items. */
3285 if (conflict_skel && prop_state == svn_wc_notify_state_conflicted)
3287 svn_wc_conflict_version_t *new_version;
3288 svn_node_kind_t new_kind;
3289 svn_revnum_t new_rev;
3290 const char *repos_relpath;
3292 new_version = svn_wc_conflict_version_dup(nb->b->new_version,
3294 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev,
3295 &repos_relpath, NULL, NULL,
3296 NULL, NULL, NULL, NULL, NULL,
3297 NULL, NULL, NULL, NULL,
3298 b->wcroot, nb->local_relpath,
3299 scratch_pool, scratch_pool));
3300 /* Fill in conflict info templates with info for this node. */
3301 new_version->path_in_repos = repos_relpath;
3302 new_version->node_kind = new_kind;
3303 new_version->peg_rev = new_rev;
3305 /* Create conflict markers. */
3306 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, NULL,
3307 new_version, scratch_pool,
3309 SVN_ERR(svn_wc__conflict_create_markers(&work_items, b->db,
3316 SVN_ERR(update_move_list_add(b->wcroot, nb->local_relpath, b->db,
3317 svn_wc_notify_update_update, svn_node_dir,
3318 svn_wc_notify_state_inapplicable, prop_state,
3319 conflict_skel, work_items, scratch_pool));
3321 return SVN_NO_ERROR;
3324 static svn_error_t *
3325 update_locally_added_node(added_node_baton_t *nb,
3326 apr_pool_t *scratch_pool)
3328 update_local_add_baton_t *b = nb->b;
3329 svn_wc__db_wcroot_t *wcroot = b->wcroot;
3330 svn_wc__db_t *db = b->db;
3331 svn_node_kind_t base_kind, working_kind;
3332 const svn_checksum_t *base_checksum;
3333 apr_hash_t *base_props, *working_props;
3334 apr_array_header_t *base_children, *working_children;
3335 const char *local_abspath = svn_dirent_join(wcroot->abspath,
3340 SVN_ERR(b->cancel_func(b->cancel_baton));
3343 return SVN_NO_ERROR;
3345 /* Compare the tree conflict victim's BASE layer to the working layer. */
3346 SVN_ERR(get_info(&base_props, &base_checksum, &base_children, &base_kind,
3347 nb->local_relpath, 0, wcroot, scratch_pool, scratch_pool));
3348 SVN_ERR(get_working_info(&working_props, NULL, &working_children,
3349 &working_kind, nb->local_relpath, wcroot,
3350 scratch_pool, scratch_pool));
3351 if (working_kind == svn_node_none)
3353 svn_node_kind_t kind_on_disk;
3354 svn_skel_t *work_item = NULL;
3356 /* Skip obstructed nodes. */
3357 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk,
3359 if (kind_on_disk != base_kind && kind_on_disk != svn_node_none)
3361 SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath,
3365 svn_wc_notify_state_obstructed,
3366 svn_wc_notify_state_inapplicable,
3367 NULL, NULL, scratch_pool));
3369 return SVN_NO_ERROR;
3372 /* The working tree has no node here. The working copy of this node
3373 * is currently not installed because the base tree is shadowed.
3374 * Queue an installation of this node into the working copy. */
3375 if (base_kind == svn_node_file || base_kind == svn_node_symlink)
3376 SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, local_abspath,
3378 /* FIXME: use_commit_times? */
3380 TRUE, /* record_file_info */
3381 scratch_pool, scratch_pool));
3382 else if (base_kind == svn_node_dir)
3383 SVN_ERR(svn_wc__wq_build_dir_install(&work_item, db, local_abspath,
3384 scratch_pool, scratch_pool));
3387 SVN_ERR(update_move_list_add(wcroot, nb->local_relpath, db,
3388 svn_wc_notify_update_add,
3390 svn_wc_notify_state_inapplicable,
3391 svn_wc_notify_state_inapplicable,
3392 NULL, work_item, scratch_pool));
3393 return SVN_NO_ERROR;
3396 if (base_kind != working_kind)
3398 if (working_kind == svn_node_file || working_kind == svn_node_symlink)
3400 svn_checksum_t *working_checksum = NULL;
3403 SVN_ERR(svn_io_file_checksum2(&working_checksum, local_abspath,
3404 base_checksum->kind, scratch_pool));
3405 SVN_ERR(tc_editor_update_add_new_file(nb, base_kind, base_checksum,
3406 base_props, working_kind,
3407 working_checksum, working_props,
3410 else if (working_kind == svn_node_dir)
3411 SVN_ERR(tc_editor_update_add_new_directory(nb, base_kind, base_props,
3417 svn_boolean_t props_equal;
3419 SVN_ERR(props_match(&props_equal, base_props, working_props,
3422 if (working_kind == svn_node_file || working_kind == svn_node_symlink)
3424 svn_checksum_t *working_checksum;
3426 SVN_ERR_ASSERT(base_checksum);
3427 SVN_ERR(svn_io_file_checksum2(&working_checksum, local_abspath,
3428 base_checksum->kind, scratch_pool));
3429 if (!props_equal || !svn_checksum_match(base_checksum,
3431 SVN_ERR(tc_editor_update_add_merge_files(nb, working_checksum,
3433 working_props, base_props,
3436 else if (working_kind == svn_node_dir && !props_equal)
3437 SVN_ERR(tc_editor_update_add_merge_dirprops(nb, working_props,
3443 return SVN_NO_ERROR;
3445 if (working_kind == svn_node_dir)
3447 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3450 while (i < base_children->nelts || j < working_children->nelts)
3452 const char *child_name;
3453 svn_boolean_t base_only = FALSE, working_only = FALSE;
3454 added_node_baton_t cnb = { 0 };
3460 svn_pool_clear(iterpool);
3461 if (i >= base_children->nelts)
3463 working_only = TRUE;
3464 child_name = APR_ARRAY_IDX(working_children, j, const char *);
3466 else if (j >= working_children->nelts)
3469 child_name = APR_ARRAY_IDX(base_children, i, const char *);
3473 const char *base_name = APR_ARRAY_IDX(base_children, i,
3475 const char *working_name = APR_ARRAY_IDX(working_children, j,
3477 int cmp = strcmp(base_name, working_name);
3480 working_only = TRUE;
3484 child_name = working_only ? working_name : base_name;
3487 cnb.local_relpath = svn_relpath_join(nb->local_relpath, child_name,
3490 SVN_ERR(update_locally_added_node(&cnb, iterpool));
3497 if (nb->skip) /* Does parent now want a skip? */
3502 return SVN_NO_ERROR;
3505 /* The body of svn_wc__db_update_local_add(). */
3506 static svn_error_t *
3507 update_local_add(svn_revnum_t *new_rev,
3509 svn_wc__db_wcroot_t *wcroot,
3510 const char *local_relpath,
3511 svn_cancel_func_t cancel_func,
3513 apr_pool_t *scratch_pool)
3515 update_local_add_baton_t b = { 0 };
3516 added_node_baton_t nb = { 0 };
3517 const char *repos_root_url;
3518 const char *repos_uuid;
3519 const char *repos_relpath;
3520 apr_int64_t repos_id;
3521 svn_node_kind_t new_kind;
3522 svn_sqlite__stmt_t *stmt;
3524 b.add_op_depth = relpath_depth(local_relpath); /* DST op-root */
3526 SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
3530 b.cancel_func = cancel_func;
3531 b.cancel_baton = cancel_baton;
3533 /* Read new version info from the updated BASE node. */
3534 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, new_rev,
3535 &repos_relpath, &repos_id,
3536 NULL, NULL, NULL, NULL, NULL,
3537 NULL, NULL, NULL, NULL, NULL,
3538 wcroot, local_relpath,
3539 scratch_pool, scratch_pool));
3540 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, wcroot,
3541 repos_id, scratch_pool));
3542 b.new_version = svn_wc_conflict_version_create2(repos_root_url, repos_uuid,
3543 repos_relpath, *new_rev,
3544 new_kind, scratch_pool);
3546 /* Create a new, and empty, list for notification information. */
3547 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
3548 STMT_CREATE_UPDATE_MOVE_LIST));
3550 /* Drive the editor... */
3552 nb.local_relpath = local_relpath;
3554 SVN_ERR(update_locally_added_node(&nb, scratch_pool));
3556 /* The conflict victim is now part of the base tree.
3557 * Remove the locally added version of the conflict victim and its children.
3558 * Any children we want to retain are at a higher op-depth so they won't
3559 * be deleted by this statement. */
3560 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3561 STMT_DELETE_WORKING_OP_DEPTH));
3562 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
3563 relpath_depth(local_relpath)));
3564 SVN_ERR(svn_sqlite__update(NULL, stmt));
3566 /* Remove the tree conflict marker. */
3567 SVN_ERR(svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
3569 NULL, scratch_pool));
3570 return SVN_NO_ERROR;
3574 svn_wc__db_update_local_add(svn_wc__db_t *db,
3575 const char *local_abspath,
3576 svn_cancel_func_t cancel_func,
3578 svn_wc_notify_func2_t notify_func,
3580 apr_pool_t *scratch_pool)
3582 svn_wc__db_wcroot_t *wcroot;
3583 svn_revnum_t new_rev;
3584 const char *local_relpath;
3586 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
3588 scratch_pool, scratch_pool));
3589 VERIFY_USABLE_WCROOT(wcroot);
3591 SVN_WC__DB_WITH_TXN(update_local_add(&new_rev, db, wcroot,
3593 cancel_func, cancel_baton,
3597 /* Send all queued up notifications. */
3598 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, new_rev, new_rev,
3599 notify_func, notify_baton,
3603 svn_wc_notify_t *notify;
3605 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
3608 svn_wc_notify_update_completed,
3610 notify->kind = svn_node_none;
3611 notify->content_state = svn_wc_notify_state_inapplicable;
3612 notify->prop_state = svn_wc_notify_state_inapplicable;
3613 notify->revision = new_rev;
3614 notify_func(notify_baton, notify, scratch_pool);
3618 return SVN_NO_ERROR;
3620 /* Set *CAN_BUMP to TRUE if DEPTH is sufficient to cover the entire
3621 tree LOCAL_RELPATH at OP_DEPTH, to FALSE otherwise. */
3622 static svn_error_t *
3623 depth_sufficient_to_bump(svn_boolean_t *can_bump,
3624 svn_wc__db_wcroot_t *wcroot,
3625 const char *local_relpath,
3628 apr_pool_t *scratch_pool)
3630 svn_sqlite__stmt_t *stmt;
3631 svn_boolean_t have_row;
3635 case svn_depth_infinity:
3637 return SVN_NO_ERROR;
3639 case svn_depth_empty:
3640 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3641 STMT_SELECT_OP_DEPTH_CHILDREN));
3642 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3643 local_relpath, op_depth));
3646 case svn_depth_files:
3647 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3648 STMT_SELECT_HAS_NON_FILE_CHILDREN));
3649 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3650 local_relpath, op_depth));
3653 case svn_depth_immediates:
3654 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3655 STMT_SELECT_HAS_GRANDCHILDREN));
3656 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3657 local_relpath, op_depth));
3660 SVN_ERR_MALFUNCTION();
3662 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3663 SVN_ERR(svn_sqlite__reset(stmt));
3665 *can_bump = !have_row;
3666 return SVN_NO_ERROR;
3669 /* Mark a move-edit conflict on MOVE_SRC_ROOT_RELPATH. */
3670 static svn_error_t *
3671 bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot,
3672 const char *move_src_root_relpath,
3674 const char *move_src_op_root_relpath,
3675 const char *move_dst_op_root_relpath,
3677 apr_pool_t *scratch_pool)
3679 apr_int64_t repos_id;
3680 const char *repos_root_url;
3681 const char *repos_uuid;
3682 const char *old_repos_relpath;
3683 const char *new_repos_relpath;
3684 svn_revnum_t old_rev;
3685 svn_revnum_t new_rev;
3686 svn_node_kind_t old_kind;
3687 svn_node_kind_t new_kind;
3688 svn_wc_conflict_version_t *old_version;
3689 svn_wc_conflict_version_t *new_version;
3690 svn_skel_t *conflict;
3692 /* Verify precondition: We are allowed to set a tree conflict here. */
3693 SVN_ERR(verify_write_lock(wcroot, move_src_root_relpath, scratch_pool));
3695 /* Read new (post-update) information from the new move source BASE node. */
3696 SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_kind, &new_rev,
3697 &new_repos_relpath, &repos_id,
3698 NULL, NULL, NULL, NULL, NULL,
3700 wcroot, move_src_op_root_relpath,
3701 src_op_depth, scratch_pool, scratch_pool));
3702 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
3703 wcroot, repos_id, scratch_pool));
3705 /* Read old (pre-update) information from the move destination node.
3707 This potentially touches nodes that aren't locked by us, but that is not
3708 a problem because we have a SQLite write lock here, and all sqlite
3709 operations that affect move stability use a sqlite lock as well.
3710 (And affecting the move itself requires a write lock on the node that
3711 we do own the lock for: the move source)
3713 SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_kind, &old_rev,
3714 &old_repos_relpath, NULL, NULL, NULL,
3715 NULL, NULL, NULL, NULL, NULL, NULL,
3716 wcroot, move_dst_op_root_relpath,
3717 relpath_depth(move_dst_op_root_relpath),
3718 scratch_pool, scratch_pool));
3720 if (strcmp(move_src_root_relpath, move_src_op_root_relpath))
3722 /* We have information for the op-root, but need it for the node that
3723 we are putting the tree conflict on. Luckily we know that we have
3726 const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath,
3727 move_src_root_relpath);
3729 old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath,
3731 new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath,
3735 old_version = svn_wc_conflict_version_create2(
3736 repos_root_url, repos_uuid, old_repos_relpath, old_rev,
3737 old_kind, scratch_pool);
3738 new_version = svn_wc_conflict_version_create2(
3739 repos_root_url, repos_uuid, new_repos_relpath, new_rev,
3740 new_kind, scratch_pool);
3742 SVN_ERR(create_tree_conflict(&conflict, wcroot, move_src_root_relpath,
3743 move_dst_op_root_relpath,
3744 db, old_version, new_version,
3745 svn_wc_operation_update,
3748 svn_wc_conflict_reason_moved_away,
3749 svn_wc_conflict_action_edit,
3750 move_src_op_root_relpath,
3751 scratch_pool, scratch_pool));
3753 SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db,
3754 svn_wc_notify_tree_conflict,
3756 svn_wc_notify_state_inapplicable,
3757 svn_wc_notify_state_inapplicable,
3758 conflict, NULL, scratch_pool));
3760 return SVN_NO_ERROR;
3763 /* Checks if SRC_RELPATH is within BUMP_DEPTH from BUMP_ROOT. Sets
3764 * *SKIP to TRUE if the node should be skipped, otherwise to FALSE.
3765 * Sets *SRC_DEPTH to the remaining depth at SRC_RELPATH.
3767 static svn_error_t *
3768 check_bump_layer(svn_boolean_t *skip,
3769 svn_depth_t *src_depth,
3770 const char *bump_root,
3771 svn_depth_t bump_depth,
3772 const char *src_relpath,
3773 svn_node_kind_t src_kind,
3774 apr_pool_t *scratch_pool)
3776 const char *relpath;
3779 *src_depth = bump_depth;
3781 relpath = svn_relpath_skip_ancestor(bump_root, src_relpath);
3786 if (bump_depth == svn_depth_infinity)
3787 return SVN_NO_ERROR;
3789 if (relpath && *relpath == '\0')
3790 return SVN_NO_ERROR;
3794 case svn_depth_empty:
3798 case svn_depth_files:
3799 if (src_kind != svn_node_file)
3805 case svn_depth_immediates:
3806 if (!relpath || relpath_depth(relpath) > 1)
3809 *src_depth = svn_depth_empty;
3812 SVN_ERR_MALFUNCTION();
3815 return SVN_NO_ERROR;
3818 /* The guts of bump_moved_away: Determines if a move can be bumped to match
3819 * the move origin and if so performs this bump.
3821 static svn_error_t *
3822 bump_moved_layer(svn_boolean_t *recurse,
3823 svn_wc__db_wcroot_t *wcroot,
3824 const char *local_relpath,
3826 const char *src_relpath,
3828 svn_depth_t src_depth,
3829 const char *dst_relpath,
3831 apr_pool_t *scratch_pool)
3833 svn_sqlite__stmt_t *stmt;
3834 svn_boolean_t have_row;
3835 svn_skel_t *conflict;
3836 svn_boolean_t can_bump;
3837 const char *src_root_relpath;
3839 SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
3843 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3844 STMT_HAS_LAYER_BETWEEN));
3846 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
3847 op_depth, src_del_depth));
3849 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3850 SVN_ERR(svn_sqlite__reset(stmt));
3853 return SVN_NO_ERROR;
3856 SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath,
3857 op_depth, src_depth, scratch_pool));
3859 /* Having chosen to bump an entire BASE tree move we
3860 always have sufficient depth to bump subtree moves. */
3863 /* Are we allowed to bump */
3866 svn_boolean_t locked;
3868 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
3870 FALSE, scratch_pool));
3876 src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth,
3881 SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth,
3882 src_root_relpath, dst_relpath,
3885 return SVN_NO_ERROR;
3888 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
3889 wcroot, src_root_relpath,
3890 scratch_pool, scratch_pool));
3892 /* ### TODO: check this is the right sort of tree-conflict? */
3895 /* ### TODO: verify moved_here? */
3897 SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
3899 SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot,
3900 src_relpath, op_depth,
3901 dst_relpath, NULL, NULL,
3907 return SVN_NO_ERROR;
3910 /* Internal storage for bump_moved_away() */
3913 const char *src_relpath;
3914 const char *dst_relpath;
3915 int src_del_op_depth;
3916 svn_node_kind_t src_kind;
3919 /* Bump moves of LOCAL_RELPATH and all its descendants that were
3920 originally below LOCAL_RELPATH at op-depth OP_DEPTH.
3922 static svn_error_t *
3923 bump_moved_away(svn_wc__db_wcroot_t *wcroot,
3924 const char *local_relpath,
3928 apr_pool_t *scratch_pool)
3930 svn_sqlite__stmt_t *stmt;
3931 svn_boolean_t have_row;
3932 apr_pool_t *iterpool;
3934 apr_array_header_t *pairs = apr_array_make(scratch_pool, 32,
3935 sizeof(struct bump_pair_t*));
3937 /* Build an array, as we can't execute the same Sqlite query recursively */
3938 iterpool = svn_pool_create(scratch_pool);
3940 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3941 STMT_SELECT_MOVED_PAIR3));
3942 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
3944 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3947 struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp));
3949 bp->src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
3950 bp->dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
3951 bp->src_del_op_depth = svn_sqlite__column_int(stmt, 2);
3952 bp->src_kind = svn_sqlite__column_token(stmt, 3, kind_map);
3954 APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp;
3956 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3959 SVN_ERR(svn_sqlite__reset(stmt));
3961 for (i = 0; i < pairs->nelts; i++)
3963 struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *);
3965 svn_depth_t src_wc_depth;
3967 svn_pool_clear(iterpool);
3970 SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth,
3971 bp->src_relpath, bp->src_kind, iterpool));
3975 svn_boolean_t recurse;
3977 SVN_ERR(bump_moved_layer(&recurse, wcroot,
3978 local_relpath, op_depth,
3979 bp->src_relpath, bp->src_del_op_depth,
3980 src_wc_depth, bp->dst_relpath,
3984 SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath,
3985 relpath_depth(bp->dst_relpath),
3986 depth, db, iterpool));
3990 svn_pool_destroy(iterpool);
3992 return SVN_NO_ERROR;
3996 svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot,
3997 const char *local_relpath,
4000 apr_pool_t *scratch_pool)
4002 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4003 STMT_CREATE_UPDATE_MOVE_LIST));
4005 if (local_relpath[0] != '\0')
4007 const char *move_dst_op_root_relpath;
4008 const char *move_src_root_relpath, *delete_relpath;
4011 /* Is the root of the update moved away? (Impossible for the wcroot) */
4013 err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath,
4014 &move_dst_op_root_relpath,
4016 wcroot, local_relpath,
4018 scratch_pool, scratch_pool);
4022 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4023 return svn_error_trace(err);
4025 svn_error_clear(err);
4027 else if (move_src_root_relpath)
4029 if (strcmp(move_src_root_relpath, local_relpath))
4031 /* An ancestor of the path that was updated is moved away.
4033 If we have a lock on that ancestor, we can mark a tree
4034 conflict on it, if we don't we ignore this case. A future
4035 update of the ancestor will handle this. */
4036 svn_boolean_t locked;
4038 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(
4040 move_src_root_relpath,
4041 FALSE, scratch_pool));
4045 SVN_ERR(bump_mark_tree_conflict(wcroot,
4046 move_src_root_relpath, 0,
4048 move_dst_op_root_relpath,
4051 return SVN_NO_ERROR;
4056 SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool));
4058 return SVN_NO_ERROR;
4061 /* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION
4062 * to reflect the tree conflict on the victim SRC_ABSPATH in DB.
4064 * If SRC_ABSPATH is not a tree-conflict victim, return an error.
4066 static svn_error_t *
4067 fetch_conflict_details(int *src_op_depth,
4068 svn_wc_operation_t *operation,
4069 svn_wc_conflict_action_t *action,
4070 svn_wc_conflict_version_t **left_version,
4071 svn_wc_conflict_version_t **right_version,
4072 svn_wc__db_wcroot_t *wcroot,
4074 const char *local_relpath,
4075 const svn_skel_t *conflict_skel,
4076 apr_pool_t *result_pool,
4077 apr_pool_t *scratch_pool)
4079 const apr_array_header_t *locations;
4080 svn_boolean_t text_conflicted;
4081 svn_boolean_t prop_conflicted;
4082 svn_boolean_t tree_conflicted;
4083 const char *move_src_op_root_abspath;
4084 svn_wc_conflict_reason_t reason;
4085 const char *local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
4089 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
4090 _("'%s' is not in conflict"),
4091 path_for_error_message(wcroot, local_relpath,
4094 SVN_ERR(svn_wc__conflict_read_info(operation, &locations,
4095 &text_conflicted, &prop_conflicted,
4098 conflict_skel, result_pool,
4101 if (text_conflicted || prop_conflicted || !tree_conflicted)
4102 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
4103 _("'%s' is not a valid tree-conflict victim"),
4104 path_for_error_message(wcroot, local_relpath,
4107 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason,
4109 &move_src_op_root_abspath, NULL,
4111 conflict_skel, result_pool,
4114 if (reason == svn_wc_conflict_reason_moved_away)
4115 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4116 _("'%s' is already a moved away tree-conflict"),
4117 path_for_error_message(wcroot, local_relpath,
4122 if (locations && locations->nelts > 0)
4123 *left_version = APR_ARRAY_IDX(locations, 0,
4124 svn_wc_conflict_version_t *);
4126 *left_version = NULL;
4131 if (locations && locations->nelts > 1)
4132 *right_version = APR_ARRAY_IDX(locations, 1,
4133 svn_wc_conflict_version_t *);
4135 *right_version = NULL;
4139 int del_depth = relpath_depth(local_relpath);
4141 if (move_src_op_root_abspath)
4142 del_depth = relpath_depth(
4143 svn_dirent_skip_ancestor(wcroot->abspath,
4144 move_src_op_root_abspath));
4146 SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth,
4150 return SVN_NO_ERROR;
4154 svn_wc__db_op_raise_moved_away_internal(
4155 svn_wc__db_wcroot_t *wcroot,
4156 const char *local_relpath,
4159 svn_wc_operation_t operation,
4160 svn_wc_conflict_action_t action,
4161 const svn_wc_conflict_version_t *old_version,
4162 const svn_wc_conflict_version_t *new_version,
4163 apr_pool_t *scratch_pool)
4165 svn_sqlite__stmt_t *stmt;
4166 svn_boolean_t have_row;
4167 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4169 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4170 STMT_CREATE_UPDATE_MOVE_LIST));
4172 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4173 STMT_SELECT_MOVED_DESCENDANTS_SRC));
4174 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
4176 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4180 int delete_op_depth = svn_sqlite__column_int(stmt, 0);
4181 const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL);
4182 svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map);
4183 const char *src_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4184 const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL);
4185 svn_skel_t *conflict;
4186 svn_pool_clear(iterpool);
4188 SVN_ERR_ASSERT(src_repos_relpath != NULL);
4190 err = create_tree_conflict(&conflict, wcroot, src_relpath, dst_relpath,
4191 db, old_version, new_version, operation,
4192 src_kind /* ### old kind */,
4193 src_kind /* ### new kind */,
4195 svn_wc_conflict_reason_moved_away,
4197 svn_relpath_prefix(src_relpath,
4200 iterpool, iterpool);
4203 err = update_move_list_add(wcroot, src_relpath, db,
4204 svn_wc_notify_tree_conflict,
4206 svn_wc_notify_state_inapplicable,
4207 svn_wc_notify_state_inapplicable,
4208 conflict, NULL, scratch_pool);
4211 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
4213 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4215 SVN_ERR(svn_sqlite__reset(stmt));
4217 svn_pool_destroy(iterpool);
4219 return SVN_NO_ERROR;
4223 svn_wc__db_op_raise_moved_away(svn_wc__db_t *db,
4224 const char *local_abspath,
4225 svn_wc_notify_func2_t notify_func,
4227 apr_pool_t *scratch_pool)
4229 svn_wc__db_wcroot_t *wcroot;
4230 const char *local_relpath;
4231 svn_wc_operation_t operation;
4232 svn_wc_conflict_action_t action;
4233 svn_wc_conflict_version_t *left_version, *right_version;
4234 int move_src_op_depth;
4235 svn_skel_t *conflict;
4237 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4239 scratch_pool, scratch_pool));
4240 VERIFY_USABLE_WCROOT(wcroot);
4242 SVN_WC__DB_WITH_TXN4(
4243 svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
4244 wcroot, local_relpath,
4245 scratch_pool, scratch_pool),
4246 fetch_conflict_details(&move_src_op_depth,
4247 &operation, &action,
4248 &left_version, &right_version,
4249 wcroot, db, local_relpath, conflict,
4250 scratch_pool, scratch_pool),
4251 svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
4253 NULL, scratch_pool),
4254 svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath,
4256 db, operation, action,
4257 left_version, right_version,
4261 /* These version numbers are valid for update/switch notifications
4263 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
4265 ? left_version->peg_rev
4266 : SVN_INVALID_REVNUM),
4268 ? right_version->peg_rev
4269 : SVN_INVALID_REVNUM),
4270 notify_func, notify_baton,
4273 return SVN_NO_ERROR;
4276 static svn_error_t *
4277 break_moved_away(svn_wc__db_wcroot_t *wcroot,
4279 const char *local_relpath,
4280 int parent_src_op_depth,
4281 apr_pool_t *scratch_pool)
4283 svn_sqlite__stmt_t *stmt;
4284 svn_boolean_t have_row;
4285 apr_pool_t *iterpool;
4286 svn_error_t *err = NULL;
4288 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4289 STMT_CREATE_UPDATE_MOVE_LIST));
4291 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4292 STMT_SELECT_MOVED_DESCENDANTS_SRC));
4293 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
4294 parent_src_op_depth));
4295 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4297 iterpool = svn_pool_create(scratch_pool);
4300 int src_op_depth = svn_sqlite__column_int(stmt, 0);
4301 const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL);
4302 svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map);
4303 const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL);
4305 svn_pool_clear(iterpool);
4307 err = verify_write_lock(wcroot, src_relpath, iterpool);
4310 err = verify_write_lock(wcroot, dst_relpath, iterpool);
4315 err = svn_error_trace(
4316 svn_wc__db_op_break_move_internal(wcroot,
4317 src_relpath, src_op_depth,
4318 dst_relpath, NULL, iterpool));
4323 err = svn_error_trace(
4324 update_move_list_add(wcroot, src_relpath, db,
4325 svn_wc_notify_move_broken,
4327 svn_wc_notify_state_inapplicable,
4328 svn_wc_notify_state_inapplicable,
4329 NULL, NULL, scratch_pool));
4334 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4336 svn_pool_destroy(iterpool);
4338 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
4342 svn_wc__db_op_break_moved_away(svn_wc__db_t *db,
4343 const char *local_abspath,
4344 const char *del_op_root_abspath,
4345 svn_boolean_t mark_tc_resolved,
4346 svn_wc_notify_func2_t notify_func,
4348 apr_pool_t *scratch_pool)
4350 svn_wc__db_wcroot_t *wcroot;
4351 const char *local_relpath;
4352 const char *del_relpath;
4355 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4357 scratch_pool, scratch_pool));
4358 VERIFY_USABLE_WCROOT(wcroot);
4360 if (del_op_root_abspath)
4361 del_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4362 del_op_root_abspath);
4367 SVN_WC__DB_WITH_TXN4(
4368 find_src_op_depth(&src_op_depth, wcroot, local_relpath,
4369 del_relpath ? relpath_depth(del_relpath)
4370 : relpath_depth(local_relpath),
4372 break_moved_away(wcroot, db, local_relpath, src_op_depth,
4375 ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
4382 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
4385 notify_func, notify_baton,
4387 return SVN_NO_ERROR;
4390 static svn_error_t *
4391 required_lock_for_resolve(const char **required_relpath,
4392 svn_wc__db_wcroot_t *wcroot,
4393 const char *local_relpath,
4394 apr_pool_t *result_pool,
4395 apr_pool_t *scratch_pool)
4397 svn_sqlite__stmt_t *stmt;
4398 svn_boolean_t have_row;
4400 *required_relpath = local_relpath;
4402 /* This simply looks for all moves out of the LOCAL_RELPATH tree. We
4403 could attempt to limit it to only those moves that are going to
4404 be resolved but that would require second guessing the resolver.
4405 This simple algorithm is sufficient although it may give a
4406 strictly larger/deeper lock than necessary. */
4407 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4408 STMT_SELECT_MOVED_OUTSIDE));
4409 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
4410 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4414 const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1,
4418 = svn_relpath_get_longest_ancestor(*required_relpath,
4422 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4424 SVN_ERR(svn_sqlite__reset(stmt));
4426 *required_relpath = apr_pstrdup(result_pool, *required_relpath);
4428 return SVN_NO_ERROR;
4432 svn_wc__required_lock_for_resolve(const char **required_abspath,
4434 const char *local_abspath,
4435 apr_pool_t *result_pool,
4436 apr_pool_t *scratch_pool)
4438 svn_wc__db_wcroot_t *wcroot;
4439 const char *local_relpath;
4440 const char *required_relpath;
4442 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4444 scratch_pool, scratch_pool));
4445 VERIFY_USABLE_WCROOT(wcroot);
4447 SVN_WC__DB_WITH_TXN(
4448 required_lock_for_resolve(&required_relpath, wcroot, local_relpath,
4449 scratch_pool, scratch_pool),
4452 *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath,
4455 return SVN_NO_ERROR;