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 *old_repos_relpath_part
415 = old_repos_relpath && old_version
416 ? svn_relpath_skip_ancestor(old_version->path_in_repos,
419 const char *new_repos_relpath
420 = old_repos_relpath_part
421 ? svn_relpath_join(new_version->path_in_repos, old_repos_relpath_part,
425 if (!new_repos_relpath)
427 const char *child_relpath = svn_relpath_skip_ancestor(
430 SVN_ERR_ASSERT(child_relpath != NULL);
431 new_repos_relpath = svn_relpath_join(new_version->path_in_repos,
432 child_relpath, scratch_pool);
435 err = svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
436 wcroot, local_relpath,
437 result_pool, scratch_pool);
438 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
439 return svn_error_trace(err);
442 svn_error_clear(err);
448 svn_wc_operation_t conflict_operation;
449 svn_boolean_t tree_conflicted;
451 SVN_ERR(svn_wc__conflict_read_info(&conflict_operation, NULL, NULL, NULL,
453 db, wcroot->abspath, conflict,
454 scratch_pool, scratch_pool));
456 if (conflict_operation != svn_wc_operation_update
457 && conflict_operation != svn_wc_operation_switch)
458 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
459 _("'%s' already in conflict"),
460 path_for_error_message(wcroot, local_relpath,
465 svn_wc_conflict_reason_t existing_reason;
466 svn_wc_conflict_action_t existing_action;
467 const char *existing_abspath;
469 SVN_ERR(svn_wc__conflict_read_tree_conflict(&existing_reason,
476 if (reason != existing_reason
477 || action != existing_action
478 || (reason == svn_wc_conflict_reason_moved_away
479 && strcmp(move_src_op_root_relpath,
480 svn_dirent_skip_ancestor(wcroot->abspath,
482 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
483 _("'%s' already in conflict"),
484 path_for_error_message(wcroot,
488 /* Already a suitable tree-conflict. */
489 *conflict_p = conflict;
494 conflict = svn_wc__conflict_skel_create(result_pool);
496 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
498 svn_dirent_join(wcroot->abspath, local_relpath,
502 move_src_op_root_abspath,
507 conflict_old_version = svn_wc_conflict_version_create2(
508 old_version->repos_url,
509 old_version->repos_uuid,
510 old_repos_relpath, old_version->peg_rev,
511 old_kind, scratch_pool);
513 conflict_old_version = NULL;
515 conflict_new_version = svn_wc_conflict_version_create2(
516 new_version->repos_url, new_version->repos_uuid,
517 new_repos_relpath, new_version->peg_rev,
518 new_kind, scratch_pool);
520 if (operation == svn_wc_operation_update)
522 SVN_ERR(svn_wc__conflict_skel_set_op_update(
523 conflict, conflict_old_version, conflict_new_version,
524 result_pool, scratch_pool));
528 assert(operation == svn_wc_operation_switch);
529 SVN_ERR(svn_wc__conflict_skel_set_op_switch(
530 conflict, conflict_old_version, conflict_new_version,
531 result_pool, scratch_pool));
534 *conflict_p = conflict;
539 create_node_tree_conflict(svn_skel_t **conflict_p,
540 node_move_baton_t *nmb,
541 const char *dst_local_relpath,
542 svn_node_kind_t old_kind,
543 svn_node_kind_t new_kind,
544 svn_wc_conflict_reason_t reason,
545 svn_wc_conflict_action_t action,
546 const char *move_src_op_root_relpath,
547 apr_pool_t *result_pool,
548 apr_pool_t *scratch_pool)
550 update_move_baton_t *umb = nmb->umb;
551 const char *dst_repos_relpath;
552 const char *dst_root_relpath = svn_relpath_prefix(nmb->dst_relpath,
557 svn_relpath_join(nmb->umb->old_version->path_in_repos,
558 svn_relpath_skip_ancestor(dst_root_relpath,
562 return svn_error_trace(
563 create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath,
564 svn_relpath_prefix(dst_local_relpath,
568 umb->old_version, umb->new_version,
569 umb->operation, old_kind, new_kind,
571 reason, action, move_src_op_root_relpath,
572 result_pool, scratch_pool));
575 /* Checks if a specific local path is shadowed as seen from the move root.
576 Helper for update_moved_away_node() */
578 check_node_shadowed(svn_boolean_t *shadowed,
579 svn_wc__db_wcroot_t *wcroot,
580 const char *local_relpath,
581 int move_root_dst_op_depth,
582 apr_pool_t *scratch_pool)
584 svn_sqlite__stmt_t *stmt;
585 svn_boolean_t have_row;
587 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
588 STMT_SELECT_WORKING_NODE));
589 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
591 SVN_ERR(svn_sqlite__step(&have_row, stmt));
595 int op_depth = svn_sqlite__column_int(stmt, 0);
597 *shadowed = (op_depth > move_root_dst_op_depth);
601 SVN_ERR(svn_sqlite__reset(stmt));
606 /* Set a tree conflict for the shadowed node LOCAL_RELPATH, which is
607 the ROOT OF THE OBSTRUCTION if such a tree-conflict does not
608 already exist. KIND is the kind of the incoming LOCAL_RELPATH. */
610 mark_tc_on_op_root(node_move_baton_t *nmb,
611 svn_node_kind_t old_kind,
612 svn_node_kind_t new_kind,
613 svn_wc_conflict_action_t action,
614 apr_pool_t *scratch_pool)
616 update_move_baton_t *b = nmb->umb;
617 const char *move_dst_relpath;
618 svn_skel_t *conflict;
620 SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed);
624 if (old_kind == svn_node_none)
625 move_dst_relpath = NULL;
627 SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL,
628 b->wcroot, nmb->dst_relpath,
630 scratch_pool, scratch_pool));
632 SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath,
635 ? svn_wc_conflict_reason_moved_away
636 : svn_wc_conflict_reason_deleted),
637 action, move_dst_relpath
640 scratch_pool, scratch_pool));
642 SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db,
643 svn_wc_notify_tree_conflict,
645 svn_wc_notify_state_inapplicable,
646 svn_wc_notify_state_inapplicable,
647 conflict, NULL, scratch_pool));
653 mark_node_edited(node_move_baton_t *nmb,
654 apr_pool_t *scratch_pool)
661 SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
672 if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed))
674 svn_node_kind_t dst_kind, src_kind;
676 SVN_ERR(svn_wc__db_depth_get_info(NULL, &dst_kind, NULL,
677 NULL, NULL, NULL, NULL,
678 NULL, NULL, NULL, NULL, NULL, NULL,
679 nmb->umb->wcroot, nmb->dst_relpath,
680 nmb->umb->dst_op_depth,
681 scratch_pool, scratch_pool));
683 SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, NULL, NULL,
685 NULL, NULL, NULL, NULL, NULL, NULL,
686 nmb->umb->wcroot, nmb->src_relpath,
687 nmb->umb->src_op_depth,
688 scratch_pool, scratch_pool));
690 SVN_ERR(mark_tc_on_op_root(nmb,
692 svn_wc_conflict_action_edit,
700 mark_parent_edited(node_move_baton_t *nmb,
701 apr_pool_t *scratch_pool)
703 SVN_ERR_ASSERT(nmb && nmb->pb);
705 SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
714 tc_editor_add_directory(node_move_baton_t *nmb,
716 svn_node_kind_t old_kind,
718 apr_pool_t *scratch_pool)
720 update_move_baton_t *b = nmb->umb;
721 const char *local_abspath;
722 svn_node_kind_t wc_kind;
723 svn_skel_t *work_item = NULL;
724 svn_skel_t *conflict = NULL;
725 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
727 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
733 svn_wc__db_status_t status;
735 SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL,
736 NULL, NULL, NULL, NULL, NULL, NULL,
737 NULL, NULL, NULL, NULL, NULL, NULL,
738 NULL, NULL, NULL, NULL, NULL, NULL,
741 scratch_pool, scratch_pool));
743 if (status == svn_wc__db_status_deleted)
744 reason = svn_wc_conflict_reason_deleted;
745 else if (status != svn_wc__db_status_added)
746 wc_kind = svn_node_none;
747 else if (old_kind == svn_node_none)
748 reason = svn_wc_conflict_reason_added;
750 reason = svn_wc_conflict_reason_replaced;
753 wc_kind = svn_node_none;
755 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
757 if (wc_kind == svn_node_none)
759 /* Check for unversioned tree-conflict */
760 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
763 if (!nmb->shadowed && wc_kind == old_kind)
764 wc_kind = svn_node_none; /* Node will be gone once we install */
766 if (wc_kind != svn_node_none
767 && (nmb->shadowed || wc_kind != old_kind)) /* replace */
769 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
770 old_kind, svn_node_dir,
772 (old_kind == svn_node_none)
773 ? svn_wc_conflict_action_add
774 : svn_wc_conflict_action_replace,
776 scratch_pool, scratch_pool));
781 SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath,
782 scratch_pool, scratch_pool));
785 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
786 (old_kind == svn_node_none)
787 ? svn_wc_notify_update_add
788 : svn_wc_notify_update_replace,
790 svn_wc_notify_state_inapplicable,
791 svn_wc_notify_state_inapplicable,
792 conflict, work_item, scratch_pool));
797 copy_working_node(const char *src_relpath,
798 const char *dst_relpath,
799 svn_wc__db_wcroot_t *wcroot,
800 apr_pool_t *scratch_pool)
802 svn_sqlite__stmt_t *stmt;
803 svn_boolean_t have_row;
804 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
807 /* Add a WORKING row for the new node, based on the source. */
808 SVN_ERR(svn_sqlite__get_statement(&stmt,wcroot->sdb,
809 STMT_INSERT_WORKING_NODE_COPY_FROM));
810 SVN_ERR(svn_sqlite__bindf(stmt, "issdst", wcroot->wc_id, src_relpath,
811 dst_relpath, relpath_depth(dst_relpath),
812 dst_parent_relpath, presence_map,
813 svn_wc__db_status_normal));
814 SVN_ERR(svn_sqlite__step_done(stmt));
816 /* Copy properties over. ### This loses changelist association. */
817 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
818 STMT_SELECT_ACTUAL_NODE));
819 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, src_relpath));
820 SVN_ERR(svn_sqlite__step(&have_row, stmt));
823 apr_size_t props_size;
824 const char *properties;
826 properties = svn_sqlite__column_blob(stmt, 1, &props_size,
828 SVN_ERR(svn_sqlite__reset(stmt));
829 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
830 STMT_INSERT_ACTUAL_NODE));
831 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
832 wcroot->wc_id, dst_relpath,
833 svn_relpath_dirname(dst_relpath,
835 properties, props_size, NULL));
836 SVN_ERR(svn_sqlite__step(&have_row, stmt));
838 SVN_ERR(svn_sqlite__reset(stmt));
844 tc_editor_incoming_add_directory(node_move_baton_t *nmb,
845 const char *dst_relpath,
846 svn_node_kind_t old_kind,
848 const char *src_relpath,
849 apr_pool_t *scratch_pool)
851 update_move_baton_t *b = nmb->umb;
852 const char *dst_abspath;
853 svn_node_kind_t wc_kind;
854 svn_skel_t *work_item = NULL;
855 svn_skel_t *conflict = NULL;
856 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
858 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
862 dst_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool);
864 /* Check for unversioned tree-conflict */
865 SVN_ERR(svn_io_check_path(dst_abspath, &wc_kind, scratch_pool));
867 if (wc_kind == old_kind)
868 wc_kind = svn_node_none; /* Node will be gone once we install */
870 if (wc_kind != svn_node_none && wc_kind != old_kind) /* replace */
872 SVN_ERR(create_node_tree_conflict(&conflict, nmb, dst_relpath,
873 old_kind, svn_node_dir,
875 (old_kind == svn_node_none)
876 ? svn_wc_conflict_action_add
877 : svn_wc_conflict_action_replace,
879 scratch_pool, scratch_pool));
884 SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
886 SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, dst_abspath,
887 scratch_pool, scratch_pool));
890 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
891 (old_kind == svn_node_none)
892 ? svn_wc_notify_update_add
893 : svn_wc_notify_update_replace,
895 svn_wc_notify_state_inapplicable,
896 svn_wc_notify_state_inapplicable,
897 conflict, work_item, scratch_pool));
902 tc_editor_add_file(node_move_baton_t *nmb,
904 svn_node_kind_t old_kind,
905 const svn_checksum_t *checksum,
907 apr_pool_t *scratch_pool)
909 update_move_baton_t *b = nmb->umb;
910 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
911 svn_node_kind_t wc_kind;
912 const char *local_abspath;
913 svn_skel_t *work_item = NULL;
914 svn_skel_t *conflict = NULL;
916 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
922 svn_wc__db_status_t status;
924 SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL,
925 NULL, NULL, NULL, NULL, NULL, NULL,
926 NULL, NULL, NULL, NULL, NULL, NULL,
927 NULL, NULL, NULL, NULL, NULL, NULL,
930 scratch_pool, scratch_pool));
932 if (status == svn_wc__db_status_deleted)
933 reason = svn_wc_conflict_reason_deleted;
934 else if (status != svn_wc__db_status_added)
935 wc_kind = svn_node_none;
936 else if (old_kind == svn_node_none)
937 reason = svn_wc_conflict_reason_added;
939 reason = svn_wc_conflict_reason_replaced;
942 wc_kind = svn_node_none;
944 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
946 if (wc_kind == svn_node_none)
948 /* Check for unversioned tree-conflict */
949 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
952 if (wc_kind != svn_node_none
953 && (nmb->shadowed || wc_kind != old_kind)) /* replace */
955 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
956 old_kind, svn_node_file,
958 (old_kind == svn_node_none)
959 ? svn_wc_conflict_action_add
960 : svn_wc_conflict_action_replace,
962 scratch_pool, scratch_pool));
967 /* Update working file. */
968 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
969 svn_dirent_join(b->wcroot->abspath,
973 FALSE /*FIXME: use_commit_times?*/,
974 TRUE /* record_file_info */,
975 scratch_pool, scratch_pool));
978 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
979 (old_kind == svn_node_none)
980 ? svn_wc_notify_update_add
981 : svn_wc_notify_update_replace,
983 svn_wc_notify_state_inapplicable,
984 svn_wc_notify_state_inapplicable,
985 conflict, work_item, scratch_pool));
990 tc_editor_incoming_add_file(node_move_baton_t *nmb,
991 const char *dst_relpath,
992 svn_node_kind_t old_kind,
993 const svn_checksum_t *checksum,
995 const char *src_relpath,
996 const char *content_abspath,
997 apr_pool_t *scratch_pool)
999 update_move_baton_t *b = nmb->umb;
1000 svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
1001 svn_node_kind_t wc_kind;
1002 const char *dst_abspath;
1003 svn_skel_t *work_items = NULL;
1004 svn_skel_t *work_item = NULL;
1005 svn_skel_t *conflict = NULL;
1007 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1010 SVN_ERR(svn_io_remove_file2(content_abspath, TRUE, scratch_pool));
1011 return SVN_NO_ERROR;
1014 dst_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool);
1016 /* Check for unversioned tree-conflict */
1017 SVN_ERR(svn_io_check_path(dst_abspath, &wc_kind, scratch_pool));
1019 if (wc_kind != svn_node_none && wc_kind != old_kind) /* replace */
1021 SVN_ERR(create_node_tree_conflict(&conflict, nmb, dst_relpath,
1022 old_kind, svn_node_file,
1024 (old_kind == svn_node_none)
1025 ? svn_wc_conflict_action_add
1026 : svn_wc_conflict_action_replace,
1028 scratch_pool, scratch_pool));
1030 SVN_ERR(svn_io_remove_file2(content_abspath, TRUE, scratch_pool));
1034 const char *src_abspath;
1036 SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
1039 /* Update working file. */
1040 src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1042 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, dst_abspath,
1044 FALSE /* FIXME: use_commit_times?*/,
1045 TRUE /* record_file_info */,
1046 scratch_pool, scratch_pool));
1047 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1049 /* Queue removal of temporary content copy. */
1050 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, b->db,
1051 b->wcroot->abspath, src_abspath,
1052 scratch_pool, scratch_pool));
1054 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1057 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1058 (old_kind == svn_node_none)
1059 ? svn_wc_notify_update_add
1060 : svn_wc_notify_update_replace,
1062 svn_wc_notify_state_inapplicable,
1063 svn_wc_notify_state_inapplicable,
1064 conflict, work_items, scratch_pool));
1065 return SVN_NO_ERROR;
1068 /* All the info we need about one version of a working node. */
1069 typedef struct working_node_version_t
1071 svn_wc_conflict_version_t *location_and_kind;
1073 const svn_checksum_t *checksum; /* for files only */
1074 } working_node_version_t;
1076 /* Return *WORK_ITEMS to create a conflict on LOCAL_ABSPATH. */
1077 static svn_error_t *
1078 create_conflict_markers(svn_skel_t **work_items,
1079 const char *local_abspath,
1081 const char *repos_relpath,
1082 svn_skel_t *conflict_skel,
1083 svn_wc_operation_t operation,
1084 const working_node_version_t *old_version,
1085 const working_node_version_t *new_version,
1086 svn_node_kind_t kind,
1087 svn_boolean_t set_operation,
1088 apr_pool_t *result_pool,
1089 apr_pool_t *scratch_pool)
1091 svn_wc_conflict_version_t *original_version;
1092 svn_wc_conflict_version_t *conflicted_version;
1095 original_version = svn_wc_conflict_version_dup(
1096 old_version->location_and_kind, scratch_pool);
1097 original_version->node_kind = kind;
1098 conflicted_version = svn_wc_conflict_version_dup(
1099 new_version->location_and_kind, scratch_pool);
1100 conflicted_version->node_kind = kind;
1102 part = svn_relpath_skip_ancestor(original_version->path_in_repos,
1105 part = svn_relpath_skip_ancestor(conflicted_version->path_in_repos,
1107 SVN_ERR_ASSERT(part != NULL);
1109 conflicted_version->path_in_repos
1110 = svn_relpath_join(conflicted_version->path_in_repos, part, scratch_pool);
1111 original_version->path_in_repos = repos_relpath;
1115 if (operation == svn_wc_operation_update)
1117 SVN_ERR(svn_wc__conflict_skel_set_op_update(
1118 conflict_skel, original_version,
1120 scratch_pool, scratch_pool));
1122 else if (operation == svn_wc_operation_merge)
1124 SVN_ERR(svn_wc__conflict_skel_set_op_merge(
1125 conflict_skel, original_version,
1127 scratch_pool, scratch_pool));
1131 SVN_ERR(svn_wc__conflict_skel_set_op_switch(
1132 conflict_skel, original_version,
1134 scratch_pool, scratch_pool));
1138 /* According to this func's doc string, it is "Currently only used for
1139 * property conflicts as text conflict markers are just in-wc files." */
1140 SVN_ERR(svn_wc__conflict_create_markers(work_items, db,
1146 return SVN_NO_ERROR;
1149 static svn_error_t *
1150 update_working_props(svn_wc_notify_state_t *prop_state,
1151 svn_skel_t **conflict_skel,
1152 apr_array_header_t **propchanges,
1153 apr_hash_t **actual_props,
1154 update_move_baton_t *b,
1155 const char *local_relpath,
1156 const struct working_node_version_t *old_version,
1157 const struct working_node_version_t *new_version,
1158 apr_pool_t *result_pool,
1159 apr_pool_t *scratch_pool)
1161 apr_hash_t *new_actual_props;
1162 apr_array_header_t *new_propchanges;
1165 * Run a 3-way prop merge to update the props, using the pre-update
1166 * props as the merge base, the post-update props as the
1167 * merge-left version, and the current props of the
1168 * moved-here working file as the merge-right version.
1170 SVN_ERR(svn_wc__db_read_props_internal(actual_props,
1171 b->wcroot, local_relpath,
1172 result_pool, scratch_pool));
1173 SVN_ERR(svn_prop_diffs(propchanges, new_version->props, old_version->props,
1175 SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state,
1177 b->db, svn_dirent_join(b->wcroot->abspath,
1180 old_version->props, old_version->props,
1181 *actual_props, *propchanges,
1182 result_pool, scratch_pool));
1184 /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props_internal
1185 relies on NODES row being updated via a different route .
1187 This extra property diff makes sure we clear the actual row when
1188 the final result is unchanged properties. */
1189 SVN_ERR(svn_prop_diffs(&new_propchanges, new_actual_props, new_version->props,
1191 if (!new_propchanges->nelts)
1192 new_actual_props = NULL;
1194 /* Install the new actual props. */
1195 SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath,
1197 svn_wc__has_magic_property(
1201 return SVN_NO_ERROR;
1204 static svn_error_t *
1205 tc_editor_alter_directory(node_move_baton_t *nmb,
1206 const char *dst_relpath,
1207 apr_hash_t *old_props,
1208 apr_hash_t *new_props,
1209 apr_pool_t *scratch_pool)
1211 update_move_baton_t *b = nmb->umb;
1212 working_node_version_t old_version, new_version;
1213 svn_skel_t *work_items = NULL;
1214 svn_skel_t *conflict_skel = NULL;
1215 const char *local_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath,
1217 svn_wc_notify_state_t prop_state;
1218 apr_hash_t *actual_props;
1219 apr_array_header_t *propchanges;
1220 svn_node_kind_t wc_kind;
1221 svn_boolean_t obstructed = FALSE;
1223 SVN_ERR(mark_node_edited(nmb, scratch_pool));
1225 return SVN_NO_ERROR;
1227 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
1228 if (wc_kind != svn_node_none && wc_kind != svn_node_dir)
1230 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1231 svn_node_dir, svn_node_dir,
1232 svn_wc_conflict_reason_obstructed,
1233 svn_wc_conflict_action_edit,
1235 scratch_pool, scratch_pool));
1239 old_version.location_and_kind = b->old_version;
1240 new_version.location_and_kind = b->new_version;
1242 old_version.checksum = NULL; /* not a file */
1243 old_version.props = old_props;
1244 new_version.checksum = NULL; /* not a file */
1245 new_version.props = new_props;
1247 SVN_ERR(update_working_props(&prop_state, &conflict_skel,
1248 &propchanges, &actual_props,
1250 &old_version, &new_version,
1251 scratch_pool, scratch_pool));
1253 if (prop_state == svn_wc_notify_state_conflicted)
1255 const char *move_dst_repos_relpath;
1257 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL,
1258 &move_dst_repos_relpath, NULL, NULL,
1259 NULL, NULL, NULL, NULL, NULL, NULL,
1261 b->wcroot, dst_relpath,
1263 scratch_pool, scratch_pool));
1265 SVN_ERR(create_conflict_markers(&work_items, local_abspath,
1266 b->db, move_dst_repos_relpath,
1267 conflict_skel, b->operation,
1268 &old_version, &new_version,
1269 svn_node_dir, !obstructed,
1270 scratch_pool, scratch_pool));
1273 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1274 svn_wc_notify_update_update,
1276 svn_wc_notify_state_inapplicable,
1278 conflict_skel, work_items, scratch_pool));
1280 return SVN_NO_ERROR;
1283 /* Edit the file found at the move destination, which is initially at
1284 * the old state. Merge the changes into the "working"/"actual" file.
1286 * Merge the difference between OLD_VERSION and NEW_VERSION into
1287 * the working file at LOCAL_RELPATH.
1289 * The term 'old' refers to the pre-update state, which is the state of
1290 * (some layer of) LOCAL_RELPATH while this function runs; and 'new'
1291 * refers to the post-update state, as found at the (base layer of) the
1292 * move source path while this function runs.
1294 * LOCAL_RELPATH is a file in the working copy at WCROOT in DB, and
1295 * REPOS_RELPATH is the repository path it would be committed to.
1297 * Use NOTIFY_FUNC and NOTIFY_BATON for notifications.
1298 * Set *WORK_ITEMS to any required work items, allocated in RESULT_POOL.
1299 * Use SCRATCH_POOL for temporary allocations. */
1300 static svn_error_t *
1301 tc_editor_alter_file(node_move_baton_t *nmb,
1302 const char *dst_relpath,
1303 const svn_checksum_t *old_checksum,
1304 const svn_checksum_t *new_checksum,
1305 apr_hash_t *old_props,
1306 apr_hash_t *new_props,
1307 apr_pool_t *scratch_pool)
1309 update_move_baton_t *b = nmb->umb;
1310 working_node_version_t old_version, new_version;
1311 const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
1314 const char *old_pristine_abspath;
1315 const char *new_pristine_abspath;
1316 svn_skel_t *conflict_skel = NULL;
1317 apr_hash_t *actual_props;
1318 apr_array_header_t *propchanges;
1319 enum svn_wc_merge_outcome_t merge_outcome;
1320 svn_wc_notify_state_t prop_state, content_state;
1321 svn_skel_t *work_item, *work_items = NULL;
1322 svn_node_kind_t wc_kind;
1323 svn_boolean_t obstructed = FALSE;
1325 SVN_ERR(mark_node_edited(nmb, scratch_pool));
1327 return SVN_NO_ERROR;
1329 SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
1330 if (wc_kind != svn_node_none && wc_kind != svn_node_file)
1332 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1333 svn_node_file, svn_node_file,
1334 svn_wc_conflict_reason_obstructed,
1335 svn_wc_conflict_action_edit,
1337 scratch_pool, scratch_pool));
1341 old_version.location_and_kind = b->old_version;
1342 new_version.location_and_kind = b->new_version;
1344 old_version.checksum = old_checksum;
1345 old_version.props = old_props;
1346 new_version.checksum = new_checksum;
1347 new_version.props = new_props;
1349 /* ### TODO: Only do this when there is no higher WORKING layer */
1350 SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges,
1351 &actual_props, b, dst_relpath,
1352 &old_version, &new_version,
1353 scratch_pool, scratch_pool));
1356 && !svn_checksum_match(new_version.checksum, old_version.checksum))
1358 svn_boolean_t is_locally_modified;
1360 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
1361 b->db, local_abspath,
1362 FALSE /* exact_comparison */,
1364 if (!is_locally_modified)
1366 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1369 FALSE /* FIXME: use_commit_times? */,
1370 TRUE /* record_file_info */,
1371 scratch_pool, scratch_pool));
1373 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1375 content_state = svn_wc_notify_state_changed;
1380 * Run a 3-way merge to update the file, using the pre-update
1381 * pristine text as the merge base, the post-update pristine
1382 * text as the merge-left version, and the current content of the
1383 * moved-here working file as the merge-right version.
1385 SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath,
1386 b->db, b->wcroot->abspath,
1387 old_version.checksum,
1388 scratch_pool, scratch_pool));
1389 SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
1390 b->db, b->wcroot->abspath,
1391 new_version.checksum,
1392 scratch_pool, scratch_pool));
1393 SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
1394 &merge_outcome, b->db,
1395 old_pristine_abspath,
1396 new_pristine_abspath,
1399 NULL, NULL, NULL, /* diff labels */
1401 FALSE, /* dry-run */
1402 NULL, /* diff3-cmd */
1403 NULL, /* merge options */
1405 b->cancel_func, b->cancel_baton,
1406 scratch_pool, scratch_pool));
1408 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1410 if (merge_outcome == svn_wc_merge_conflict)
1411 content_state = svn_wc_notify_state_conflicted;
1413 content_state = svn_wc_notify_state_merged;
1417 content_state = svn_wc_notify_state_unchanged;
1419 /* If there are any conflicts to be stored, convert them into work items
1423 const char *move_dst_repos_relpath;
1425 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL,
1426 &move_dst_repos_relpath, NULL, NULL,
1427 NULL, NULL, NULL, NULL, NULL, NULL,
1429 b->wcroot, dst_relpath,
1431 scratch_pool, scratch_pool));
1433 SVN_ERR(create_conflict_markers(&work_item, local_abspath, b->db,
1434 move_dst_repos_relpath, conflict_skel,
1435 b->operation, &old_version, &new_version,
1436 svn_node_file, !obstructed,
1437 scratch_pool, scratch_pool));
1439 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1442 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1443 svn_wc_notify_update_update,
1447 conflict_skel, work_items, scratch_pool));
1449 return SVN_NO_ERROR;
1452 static svn_error_t *
1453 tc_editor_update_incoming_moved_file(node_move_baton_t *nmb,
1454 const char *dst_relpath,
1455 const char *src_relpath,
1456 const svn_checksum_t *src_checksum,
1457 const svn_checksum_t *dst_checksum,
1458 apr_hash_t *dst_props,
1459 apr_hash_t *src_props,
1460 svn_boolean_t do_text_merge,
1461 apr_pool_t *scratch_pool)
1463 update_move_baton_t *b = nmb->umb;
1464 working_node_version_t old_version, new_version;
1465 const char *dst_abspath = svn_dirent_join(b->wcroot->abspath,
1468 svn_skel_t *conflict_skel = NULL;
1469 enum svn_wc_merge_outcome_t merge_outcome;
1470 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unchanged;
1471 svn_wc_notify_state_t content_state = svn_wc_notify_state_unchanged;
1472 svn_skel_t *work_item, *work_items = NULL;
1473 svn_node_kind_t dst_kind_on_disk;
1474 const char *dst_repos_relpath;
1475 svn_boolean_t tree_conflict = FALSE;
1476 svn_node_kind_t dst_db_kind;
1479 SVN_ERR(mark_node_edited(nmb, scratch_pool));
1481 return SVN_NO_ERROR;
1483 err = svn_wc__db_base_get_info_internal(NULL, &dst_db_kind, NULL,
1485 NULL, NULL, NULL, NULL, NULL, NULL,
1486 NULL, NULL, NULL, NULL, NULL,
1487 b->wcroot, dst_relpath,
1488 scratch_pool, scratch_pool);
1489 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1491 const char *dst_parent_relpath;
1492 const char *dst_parent_repos_relpath;
1493 const char *src_abspath;
1495 /* If the file cannot be found, it was either deleted at the
1496 * move destination, or it was moved after its parent was moved.
1497 * We cannot deal with this problem right now. Instead, we will
1498 * raise a new tree conflict at the location where this file should
1499 * have been, and let another run of the resolver deal with the
1500 * new conflict later on. */
1502 svn_error_clear(err);
1504 /* Create a WORKING node for this file at the move destination. */
1505 SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
1508 /* Raise a tree conflict at the new WORKING node. */
1509 dst_db_kind = svn_node_none;
1510 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1511 svn_node_file, dst_db_kind,
1512 svn_wc_conflict_reason_edited,
1513 svn_wc_conflict_action_delete,
1514 NULL, scratch_pool, scratch_pool));
1515 dst_parent_relpath = svn_relpath_dirname(dst_relpath, scratch_pool);
1516 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
1517 &dst_parent_repos_relpath,
1518 NULL, NULL, NULL, NULL, NULL,
1519 NULL, NULL, NULL, NULL, NULL,
1522 scratch_pool, scratch_pool));
1523 dst_repos_relpath = svn_relpath_join(dst_parent_repos_relpath,
1524 svn_relpath_basename(dst_relpath,
1527 tree_conflict = TRUE;
1529 /* Schedule a copy of the victim's file content to the new node's path. */
1530 src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1532 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1535 FALSE /*FIXME: use_commit_times?*/,
1536 TRUE /* record_file_info */,
1537 scratch_pool, scratch_pool));
1538 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1543 if ((dst_db_kind == svn_node_none || dst_db_kind != svn_node_file) &&
1544 conflict_skel == NULL)
1546 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1547 svn_node_file, dst_db_kind,
1548 dst_db_kind == svn_node_none
1549 ? svn_wc_conflict_reason_missing
1550 : svn_wc_conflict_reason_obstructed,
1551 svn_wc_conflict_action_edit,
1553 scratch_pool, scratch_pool));
1554 tree_conflict = TRUE;
1557 SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind_on_disk, scratch_pool));
1558 if ((dst_kind_on_disk == svn_node_none || dst_kind_on_disk != svn_node_file)
1559 && conflict_skel == NULL)
1561 SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1562 svn_node_file, dst_kind_on_disk,
1563 dst_kind_on_disk == svn_node_none
1564 ? svn_wc_conflict_reason_missing
1565 : svn_wc_conflict_reason_obstructed,
1566 svn_wc_conflict_action_edit,
1568 scratch_pool, scratch_pool));
1569 tree_conflict = TRUE;
1572 old_version.location_and_kind = b->old_version;
1573 new_version.location_and_kind = b->new_version;
1575 old_version.checksum = src_checksum;
1576 old_version.props = src_props;
1577 new_version.checksum = dst_checksum;
1578 new_version.props = dst_props;
1580 /* Merge properties and text content if there is no tree conflict. */
1581 if (conflict_skel == NULL)
1583 apr_hash_t *actual_props;
1584 apr_array_header_t *propchanges;
1586 SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges,
1587 &actual_props, b, dst_relpath,
1588 &old_version, &new_version,
1589 scratch_pool, scratch_pool));
1592 const char *old_pristine_abspath;
1593 const char *src_abspath;
1594 const char *label_left;
1595 const char *label_target;
1598 * Run a 3-way merge to update the file at its post-move location,
1599 * using the pre-move file's pristine text as the merge base, the
1600 * post-move content as the merge-right version, and the current
1601 * content of the working file at the pre-move location as the
1602 * merge-left version.
1604 SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath,
1605 b->db, b->wcroot->abspath,
1607 scratch_pool, scratch_pool));
1608 src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1610 label_left = apr_psprintf(scratch_pool, ".r%ld",
1611 b->old_version->peg_rev);
1612 label_target = apr_psprintf(scratch_pool, ".r%ld",
1613 b->new_version->peg_rev);
1614 SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
1615 &merge_outcome, b->db,
1616 old_pristine_abspath,
1624 FALSE, /* dry-run */
1625 NULL, /* diff3-cmd */
1626 NULL, /* merge options */
1628 b->cancel_func, b->cancel_baton,
1629 scratch_pool, scratch_pool));
1631 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1633 if (merge_outcome == svn_wc_merge_conflict)
1634 content_state = svn_wc_notify_state_conflicted;
1636 content_state = svn_wc_notify_state_merged;
1640 /* If there are any conflicts to be stored, convert them into work items
1644 SVN_ERR(create_conflict_markers(&work_item, dst_abspath, b->db,
1645 dst_repos_relpath, conflict_skel,
1646 b->operation, &old_version, &new_version,
1647 svn_node_file, !tree_conflict,
1648 scratch_pool, scratch_pool));
1650 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1653 SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1654 svn_wc_notify_update_update,
1658 conflict_skel, work_items, scratch_pool));
1660 return SVN_NO_ERROR;
1663 static svn_error_t *
1664 tc_editor_delete(node_move_baton_t *nmb,
1665 const char *relpath,
1666 svn_node_kind_t old_kind,
1667 svn_node_kind_t new_kind,
1668 apr_pool_t *scratch_pool)
1670 update_move_baton_t *b = nmb->umb;
1671 svn_sqlite__stmt_t *stmt;
1672 const char *local_abspath;
1673 svn_boolean_t is_modified, is_all_deletes;
1674 svn_skel_t *work_items = NULL;
1675 svn_skel_t *conflict = NULL;
1677 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1679 return SVN_NO_ERROR;
1681 /* Check before retracting delete to catch delete-delete
1682 conflicts. This catches conflicts on the node itself; deleted
1683 children are caught as local modifications below.*/
1686 SVN_ERR(mark_tc_on_op_root(nmb,
1688 svn_wc_conflict_action_delete,
1690 return SVN_NO_ERROR;
1693 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
1694 SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes,
1695 nmb->umb->db, local_abspath, FALSE,
1696 NULL, NULL, scratch_pool));
1699 svn_wc_conflict_reason_t reason;
1701 /* No conflict means no NODES rows at the relpath op-depth
1702 so it's easy to convert the modified tree into a copy.
1704 Note the following assumptions for relpath:
1705 * it is not shadowed
1706 * it is not the/an op-root. (or we can't make us a copy)
1709 SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1710 NULL, NULL, scratch_pool));
1712 reason = svn_wc_conflict_reason_edited;
1714 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
1715 old_kind, new_kind, reason,
1716 (new_kind == svn_node_none)
1717 ? svn_wc_conflict_action_delete
1718 : svn_wc_conflict_action_replace,
1720 scratch_pool, scratch_pool));
1725 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1726 const char *del_abspath;
1727 svn_boolean_t have_row;
1729 /* Get all descendants of the node in reverse order (so children are
1730 handled before their parents, but not strictly depth first) */
1731 SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb,
1732 STMT_SELECT_DESCENDANTS_OP_DEPTH_RV));
1733 SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath,
1735 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1739 svn_skel_t *work_item;
1740 svn_node_kind_t del_kind;
1742 svn_pool_clear(iterpool);
1744 del_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1745 del_abspath = svn_dirent_join(b->wcroot->abspath,
1746 svn_sqlite__column_text(stmt, 0, NULL),
1748 if (del_kind == svn_node_dir)
1749 err = svn_wc__wq_build_dir_remove(&work_item, b->db,
1750 b->wcroot->abspath, del_abspath,
1751 FALSE /* recursive */,
1752 iterpool, iterpool);
1754 err = svn_wc__wq_build_file_remove(&work_item, b->db,
1755 b->wcroot->abspath, del_abspath,
1756 iterpool, iterpool);
1758 err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool);
1760 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1762 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1764 SVN_ERR(svn_sqlite__reset(stmt));
1766 if (old_kind == svn_node_dir)
1767 SVN_ERR(svn_wc__wq_build_dir_remove(&work_items, b->db,
1768 b->wcroot->abspath, local_abspath,
1769 FALSE /* recursive */,
1770 scratch_pool, iterpool));
1772 SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db,
1773 b->wcroot->abspath, local_abspath,
1774 scratch_pool, iterpool));
1776 svn_pool_destroy(iterpool);
1779 /* Only notify if add_file/add_dir is not going to notify */
1780 if (conflict || (new_kind == svn_node_none))
1781 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
1782 svn_wc_notify_update_delete,
1784 svn_wc_notify_state_inapplicable,
1785 svn_wc_notify_state_inapplicable,
1786 conflict, work_items, scratch_pool));
1787 else if (work_items)
1788 SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items,
1791 return SVN_NO_ERROR;
1794 /* Handle node deletion for an incoming move. */
1795 static svn_error_t *
1796 tc_incoming_editor_delete(node_move_baton_t *nmb,
1797 const char *relpath,
1798 svn_node_kind_t old_kind,
1799 svn_node_kind_t new_kind,
1800 apr_pool_t *scratch_pool)
1802 update_move_baton_t *b = nmb->umb;
1803 svn_sqlite__stmt_t *stmt;
1804 const char *local_abspath;
1805 svn_boolean_t is_modified, is_all_deletes;
1806 svn_skel_t *work_items = NULL;
1807 svn_skel_t *conflict = NULL;
1809 SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1811 return SVN_NO_ERROR;
1813 /* Check before retracting delete to catch delete-delete
1814 conflicts. This catches conflicts on the node itself; deleted
1815 children are caught as local modifications below.*/
1818 SVN_ERR(mark_tc_on_op_root(nmb,
1820 svn_wc_conflict_action_delete,
1822 return SVN_NO_ERROR;
1825 local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
1826 SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes,
1827 nmb->umb->db, local_abspath, FALSE,
1828 NULL, NULL, scratch_pool));
1831 svn_wc_conflict_reason_t reason;
1833 /* No conflict means no NODES rows at the relpath op-depth
1834 so it's easy to convert the modified tree into a copy.
1836 Note the following assumptions for relpath:
1837 * it is not shadowed
1838 * it is not the/an op-root. (or we can't make us a copy)
1841 SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1842 NULL, NULL, scratch_pool));
1844 reason = svn_wc_conflict_reason_edited;
1846 SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
1847 old_kind, new_kind, reason,
1848 (new_kind == svn_node_none)
1849 ? svn_wc_conflict_action_delete
1850 : svn_wc_conflict_action_replace,
1852 scratch_pool, scratch_pool));
1857 /* Delete the WORKING node at DST_RELPATH. */
1858 SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb,
1859 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
1860 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
1861 b->wcroot->wc_id, relpath,
1862 0, relpath_depth(relpath)));
1863 SVN_ERR(svn_sqlite__step_done(stmt));
1866 /* Only notify if add_file/add_dir is not going to notify */
1867 if (conflict || (new_kind == svn_node_none))
1868 SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
1869 svn_wc_notify_update_delete,
1871 svn_wc_notify_state_inapplicable,
1872 svn_wc_notify_state_inapplicable,
1873 conflict, work_items, scratch_pool));
1874 else if (work_items)
1875 SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items,
1878 return SVN_NO_ERROR;
1884 * The scenario is that a subtree has been locally moved, and then the base
1885 * layer on the source side of the move has received an update to a new
1886 * state. The destination subtree has not yet been updated, and still
1887 * matches the pre-update state of the source subtree.
1889 * The edit driver drives the receiver with the difference between the
1890 * pre-update state (as found now at the move-destination) and the
1891 * post-update state (found now at the move-source).
1893 * We currently assume that both the pre-update and post-update states are
1897 /* Return *PROPS, *CHECKSUM, *CHILDREN and *KIND for LOCAL_RELPATH at
1898 OP_DEPTH provided the row exists. Return *KIND of svn_node_none if
1899 the row does not exist, or only describes a delete of a lower op-depth.
1900 *CHILDREN is a sorted array of basenames of type 'const char *', rather
1901 than a hash, to allow the driver to process children in a defined order. */
1902 static svn_error_t *
1903 get_info(apr_hash_t **props,
1904 const svn_checksum_t **checksum,
1905 apr_array_header_t **children,
1906 svn_node_kind_t *kind,
1907 const char *local_relpath,
1909 svn_wc__db_wcroot_t *wcroot,
1910 apr_pool_t *result_pool,
1911 apr_pool_t *scratch_pool)
1913 svn_wc__db_status_t status;
1914 const char *repos_relpath;
1915 svn_node_kind_t db_kind;
1918 err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL,
1919 NULL, NULL, NULL, NULL, checksum, NULL,
1921 wcroot, local_relpath, op_depth,
1922 result_pool, scratch_pool);
1924 /* If there is no node at this depth, or only a node that describes a delete
1925 of a lower layer we report this node as not existing. */
1926 if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1927 || (!err && status != svn_wc__db_status_added
1928 && status != svn_wc__db_status_normal))
1930 svn_error_clear(err);
1933 *kind = svn_node_none;
1939 *children = apr_array_make(result_pool, 0, sizeof(const char *));
1941 return SVN_NO_ERROR;
1949 if (children && db_kind == svn_node_dir)
1951 svn_sqlite__stmt_t *stmt;
1952 svn_boolean_t have_row;
1954 *children = apr_array_make(result_pool, 16, sizeof(const char *));
1955 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1956 STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS));
1957 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1959 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1962 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1964 APR_ARRAY_PUSH(*children, const char *)
1965 = svn_relpath_basename(child_relpath, result_pool);
1967 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1969 SVN_ERR(svn_sqlite__reset(stmt));
1972 *children = apr_array_make(result_pool, 0, sizeof(const char *));
1974 return SVN_NO_ERROR;
1977 /* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties,
1978 FALSE otherwise. SRC_PROPS and DST_PROPS are standard property
1980 static svn_error_t *
1981 props_match(svn_boolean_t *match,
1982 apr_hash_t *src_props,
1983 apr_hash_t *dst_props,
1984 apr_pool_t *scratch_pool)
1986 if (!src_props && !dst_props)
1988 else if (!src_props || ! dst_props)
1992 apr_array_header_t *propdiffs;
1994 SVN_ERR(svn_prop_diffs(&propdiffs, src_props, dst_props, scratch_pool));
1995 *match = propdiffs->nelts ? FALSE : TRUE;
1997 return SVN_NO_ERROR;
2000 /* ### Drive TC_EDITOR so as to ...
2002 static svn_error_t *
2003 update_moved_away_node(node_move_baton_t *nmb,
2004 svn_wc__db_wcroot_t *wcroot,
2005 const char *src_relpath,
2006 const char *dst_relpath,
2007 apr_pool_t *scratch_pool)
2009 update_move_baton_t *b = nmb->umb;
2010 svn_node_kind_t src_kind, dst_kind;
2011 const svn_checksum_t *src_checksum, *dst_checksum;
2012 apr_hash_t *src_props, *dst_props;
2013 apr_array_header_t *src_children, *dst_children;
2016 SVN_ERR(b->cancel_func(b->cancel_baton));
2018 SVN_ERR(get_info(&src_props, &src_checksum, &src_children, &src_kind,
2019 src_relpath, b->src_op_depth,
2020 wcroot, scratch_pool, scratch_pool));
2022 SVN_ERR(get_info(&dst_props, &dst_checksum, &dst_children, &dst_kind,
2023 dst_relpath, b->dst_op_depth,
2024 wcroot, scratch_pool, scratch_pool));
2026 if (src_kind == svn_node_none
2027 || (dst_kind != svn_node_none && src_kind != dst_kind))
2029 SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind,
2034 return SVN_NO_ERROR;
2036 if (src_kind != svn_node_none && src_kind != dst_kind)
2038 if (src_kind == svn_node_file || src_kind == svn_node_symlink)
2040 SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind,
2041 src_checksum, src_props, scratch_pool));
2043 else if (src_kind == svn_node_dir)
2045 SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind,
2046 src_props, scratch_pool));
2049 else if (src_kind != svn_node_none)
2051 svn_boolean_t props_equal;
2053 SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool));
2055 if (src_kind == svn_node_file || src_kind == svn_node_symlink)
2057 if (!props_equal || !svn_checksum_match(src_checksum, dst_checksum))
2058 SVN_ERR(tc_editor_alter_file(nmb, dst_relpath,
2059 dst_checksum, src_checksum,
2060 dst_props, src_props, scratch_pool));
2062 else if (src_kind == svn_node_dir)
2065 SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
2066 dst_props, src_props,
2072 return SVN_NO_ERROR;
2074 if (src_kind == svn_node_dir)
2076 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2079 while (i < src_children->nelts || j < dst_children->nelts)
2081 const char *child_name;
2082 svn_boolean_t src_only = FALSE, dst_only = FALSE;
2083 node_move_baton_t cnmb = { 0 };
2086 cnmb.umb = nmb->umb;
2087 cnmb.shadowed = nmb->shadowed;
2089 svn_pool_clear(iterpool);
2090 if (i >= src_children->nelts)
2093 child_name = APR_ARRAY_IDX(dst_children, j, const char *);
2095 else if (j >= dst_children->nelts)
2098 child_name = APR_ARRAY_IDX(src_children, i, const char *);
2102 const char *src_name = APR_ARRAY_IDX(src_children, i,
2104 const char *dst_name = APR_ARRAY_IDX(dst_children, j,
2106 int cmp = strcmp(src_name, dst_name);
2113 child_name = dst_only ? dst_name : src_name;
2116 cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
2118 cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
2122 SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot,
2123 cnmb.dst_relpath, b->dst_op_depth,
2126 SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath,
2127 cnmb.dst_relpath, iterpool));
2134 if (nmb->skip) /* Does parent now want a skip? */
2139 return SVN_NO_ERROR;
2142 static svn_error_t *
2143 suitable_for_move(svn_wc__db_wcroot_t *wcroot,
2144 const char *local_relpath,
2145 apr_pool_t *scratch_pool)
2147 svn_sqlite__stmt_t *stmt;
2148 svn_boolean_t have_row;
2149 svn_revnum_t revision;
2150 const char *repos_relpath;
2151 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2153 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2154 STMT_SELECT_BASE_NODE));
2155 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2156 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2158 return svn_error_trace(svn_sqlite__reset(stmt));
2160 revision = svn_sqlite__column_revnum(stmt, 4);
2161 repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
2163 SVN_ERR(svn_sqlite__reset(stmt));
2165 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2166 STMT_SELECT_REPOS_PATH_REVISION));
2167 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2168 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2171 svn_revnum_t node_revision = svn_sqlite__column_revnum(stmt, 2);
2172 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2173 const char *relpath;
2175 svn_pool_clear(iterpool);
2177 relpath = svn_relpath_skip_ancestor(local_relpath, child_relpath);
2178 relpath = svn_relpath_join(repos_relpath, relpath, iterpool);
2180 if (strcmp(relpath, svn_sqlite__column_text(stmt, 1, NULL)))
2181 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2182 svn_sqlite__reset(stmt),
2183 _("Cannot apply update because '%s' is a "
2184 "switched path (please switch it back to "
2185 "its original URL and try again)"),
2186 path_for_error_message(wcroot, child_relpath,
2189 if (revision != node_revision)
2190 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2191 svn_sqlite__reset(stmt),
2192 _("Cannot apply update because '%s' is a "
2193 "mixed-revision working copy (please "
2194 "update and try again)"),
2195 path_for_error_message(wcroot, local_relpath,
2198 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2200 SVN_ERR(svn_sqlite__reset(stmt));
2202 svn_pool_destroy(iterpool);
2204 return SVN_NO_ERROR;
2207 /* The body of svn_wc__db_update_moved_away_conflict_victim(), which see.
2209 static svn_error_t *
2210 update_moved_away_conflict_victim(svn_revnum_t *old_rev,
2211 svn_revnum_t *new_rev,
2213 svn_wc__db_wcroot_t *wcroot,
2214 const char *local_relpath,
2215 const char *delete_relpath,
2216 svn_wc_operation_t operation,
2217 svn_wc_conflict_action_t action,
2218 svn_wc_conflict_reason_t reason,
2219 svn_cancel_func_t cancel_func,
2221 apr_pool_t *scratch_pool)
2223 update_move_baton_t umb = { NULL };
2224 const char *src_relpath, *dst_relpath;
2225 svn_wc_conflict_version_t old_version;
2226 svn_wc_conflict_version_t new_version;
2227 apr_int64_t repos_id;
2228 node_move_baton_t nmb = { 0 };
2230 SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath));
2232 /* Construct editor baton. */
2234 SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot,
2235 local_relpath, relpath_depth(delete_relpath),
2238 SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL,
2239 wcroot, local_relpath,
2241 scratch_pool, scratch_pool));
2243 if (dst_relpath == NULL)
2244 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2245 _("The node '%s' has not been moved away"),
2246 path_for_error_message(wcroot, local_relpath,
2249 umb.dst_op_depth = relpath_depth(dst_relpath);
2251 SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
2252 SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
2255 SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_version.node_kind,
2256 &new_version.peg_rev,
2257 &new_version.path_in_repos, &repos_id,
2258 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2260 wcroot, src_relpath, umb.src_op_depth,
2261 scratch_pool, scratch_pool));
2263 SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
2264 &new_version.repos_uuid,
2268 SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind,
2269 &old_version.peg_rev,
2270 &old_version.path_in_repos, &repos_id,
2271 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2273 wcroot, dst_relpath, umb.dst_op_depth,
2274 scratch_pool, scratch_pool));
2276 SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
2277 &old_version.repos_uuid,
2280 *old_rev = old_version.peg_rev;
2281 *new_rev = new_version.peg_rev;
2283 umb.operation = operation;
2284 umb.old_version= &old_version;
2285 umb.new_version= &new_version;
2287 umb.wcroot = wcroot;
2288 umb.cancel_func = cancel_func;
2289 umb.cancel_baton = cancel_baton;
2291 if (umb.src_op_depth == 0)
2292 SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool));
2294 /* Create a new, and empty, list for notification information. */
2295 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2296 STMT_CREATE_UPDATE_MOVE_LIST));
2298 /* Drive the editor... */
2301 nmb.src_relpath = src_relpath;
2302 nmb.dst_relpath = dst_relpath;
2303 /* nmb.shadowed = FALSE; */
2304 /* nmb.edited = FALSE; */
2305 /* nmb.skip_children = FALSE; */
2307 /* We walk the move source (i.e. the post-update tree), comparing each node
2308 * with the equivalent node at the move destination and applying the update
2309 * to nodes at the move destination. */
2310 SVN_ERR(update_moved_away_node(&nmb, wcroot, src_relpath, dst_relpath,
2313 SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath,
2315 dst_relpath, NULL, NULL,
2318 return SVN_NO_ERROR;
2323 svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db,
2324 const char *local_abspath,
2325 const char *delete_op_abspath,
2326 svn_wc_operation_t operation,
2327 svn_wc_conflict_action_t action,
2328 svn_wc_conflict_reason_t reason,
2329 svn_cancel_func_t cancel_func,
2331 svn_wc_notify_func2_t notify_func,
2333 apr_pool_t *scratch_pool)
2335 svn_wc__db_wcroot_t *wcroot;
2336 svn_revnum_t old_rev, new_rev;
2337 const char *local_relpath;
2338 const char *delete_relpath;
2340 /* ### Check for mixed-rev src or dst? */
2342 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2344 scratch_pool, scratch_pool));
2345 VERIFY_USABLE_WCROOT(wcroot);
2348 = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath);
2350 SVN_WC__DB_WITH_TXN(
2351 update_moved_away_conflict_victim(
2353 db, wcroot, local_relpath, delete_relpath,
2354 operation, action, reason,
2355 cancel_func, cancel_baton,
2359 /* Send all queued up notifications. */
2360 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev,
2361 notify_func, notify_baton,
2365 svn_wc_notify_t *notify;
2367 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2370 svn_wc_notify_update_completed,
2372 notify->kind = svn_node_none;
2373 notify->content_state = svn_wc_notify_state_inapplicable;
2374 notify->prop_state = svn_wc_notify_state_inapplicable;
2375 notify->revision = new_rev;
2376 notify_func(notify_baton, notify, scratch_pool);
2380 return SVN_NO_ERROR;
2383 static svn_error_t *
2384 get_working_info(apr_hash_t **props,
2385 const svn_checksum_t **checksum,
2386 apr_array_header_t **children,
2387 svn_node_kind_t *kind,
2388 const char *local_relpath,
2389 svn_wc__db_wcroot_t *wcroot,
2390 apr_pool_t *result_pool,
2391 apr_pool_t *scratch_pool)
2393 svn_wc__db_status_t status;
2394 const char *repos_relpath;
2395 svn_node_kind_t db_kind;
2398 err = svn_wc__db_read_info_internal(&status, &db_kind, NULL, &repos_relpath,
2399 NULL, NULL, NULL, NULL, NULL,
2401 NULL, NULL, NULL, NULL, NULL,
2402 NULL, NULL, NULL, NULL, NULL,
2403 NULL, NULL, NULL, NULL, NULL,
2404 wcroot, local_relpath,
2405 result_pool, scratch_pool);
2407 /* If there is no node, or only a node that describes a delete
2408 of a lower layer we report this node as not existing. */
2409 if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
2410 || (!err && status != svn_wc__db_status_added
2411 && status != svn_wc__db_status_normal))
2413 svn_error_clear(err);
2416 *kind = svn_node_none;
2422 *children = apr_array_make(result_pool, 0, sizeof(const char *));
2424 return SVN_NO_ERROR;
2429 SVN_ERR(svn_wc__db_read_props_internal(props, wcroot, local_relpath,
2430 result_pool, scratch_pool));
2435 if (children && db_kind == svn_node_dir)
2437 svn_sqlite__stmt_t *stmt;
2438 svn_boolean_t have_row;
2440 *children = apr_array_make(result_pool, 16, sizeof(const char *));
2441 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2442 STMT_SELECT_WORKING_CHILDREN));
2443 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2444 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2447 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2449 APR_ARRAY_PUSH(*children, const char *)
2450 = svn_relpath_basename(child_relpath, result_pool);
2452 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2454 SVN_ERR(svn_sqlite__reset(stmt));
2457 *children = apr_array_make(result_pool, 0, sizeof(const char *));
2459 return SVN_NO_ERROR;
2462 /* Apply changes found in the victim node at SRC_RELPATH to the incoming
2463 * move at DST_RELPATH. */
2464 static svn_error_t *
2465 update_incoming_moved_node(node_move_baton_t *nmb,
2466 svn_wc__db_wcroot_t *wcroot,
2467 const char *src_relpath,
2468 const char *dst_relpath,
2469 apr_pool_t *scratch_pool)
2471 update_move_baton_t *b = nmb->umb;
2472 svn_node_kind_t orig_kind, working_kind;
2473 const char *victim_relpath = src_relpath;
2474 const svn_checksum_t *orig_checksum, *working_checksum;
2475 apr_hash_t *orig_props, *working_props;
2476 apr_array_header_t *orig_children, *working_children;
2479 SVN_ERR(b->cancel_func(b->cancel_baton));
2481 /* Compare the tree conflict victim's copied layer (the "original") with
2482 * the working layer, i.e. look for changes layered on top of the copy. */
2483 SVN_ERR(get_info(&orig_props, &orig_checksum, &orig_children, &orig_kind,
2484 victim_relpath, b->src_op_depth, wcroot, scratch_pool,
2486 SVN_ERR(get_working_info(&working_props, &working_checksum,
2487 &working_children, &working_kind, victim_relpath,
2488 wcroot, scratch_pool, scratch_pool));
2490 if (working_kind == svn_node_none
2491 || (orig_kind != svn_node_none && orig_kind != working_kind))
2493 SVN_ERR(tc_incoming_editor_delete(nmb, dst_relpath, orig_kind,
2494 working_kind, scratch_pool));
2498 return SVN_NO_ERROR;
2500 if (working_kind != svn_node_none && orig_kind != working_kind)
2502 if (working_kind == svn_node_file || working_kind == svn_node_symlink)
2504 const char *victim_abspath;
2505 const char *wctemp_abspath;
2506 svn_stream_t *working_stream;
2507 svn_stream_t *temp_stream;
2508 const char *temp_abspath;
2511 /* Copy the victim's content to a safe place and add it from there. */
2512 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&wctemp_abspath, b->db,
2516 victim_abspath = svn_dirent_join(b->wcroot->abspath,
2517 victim_relpath, scratch_pool);
2518 SVN_ERR(svn_stream_open_readonly(&working_stream, victim_abspath,
2519 scratch_pool, scratch_pool));
2520 SVN_ERR(svn_stream_open_unique(&temp_stream, &temp_abspath,
2521 wctemp_abspath, svn_io_file_del_none,
2522 scratch_pool, scratch_pool));
2523 err = svn_stream_copy3(working_stream, temp_stream,
2524 b->cancel_func, b->cancel_baton,
2526 if (err && err->apr_err == SVN_ERR_CANCELLED)
2530 err2 = svn_io_remove_file2(temp_abspath, TRUE, scratch_pool);
2531 return svn_error_compose_create(err, err2);
2536 SVN_ERR(tc_editor_incoming_add_file(nmb, dst_relpath, orig_kind,
2537 working_checksum, working_props,
2538 victim_relpath, temp_abspath,
2541 else if (working_kind == svn_node_dir)
2543 SVN_ERR(tc_editor_incoming_add_directory(nmb, dst_relpath,
2544 orig_kind, working_props,
2549 else if (working_kind != svn_node_none)
2551 svn_boolean_t props_equal;
2553 SVN_ERR(props_match(&props_equal, orig_props, working_props,
2556 if (working_kind == svn_node_file || working_kind == svn_node_symlink)
2558 svn_boolean_t is_modified;
2560 SVN_ERR(svn_wc__internal_file_modified_p(&is_modified, b->db,
2565 FALSE /* exact_comparison */,
2567 if (!props_equal || is_modified)
2568 SVN_ERR(tc_editor_update_incoming_moved_file(nmb, dst_relpath,
2577 else if (working_kind == svn_node_dir)
2580 SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
2581 orig_props, working_props,
2587 return SVN_NO_ERROR;
2589 if (working_kind == svn_node_dir)
2591 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2594 while (i < orig_children->nelts || j < working_children->nelts)
2596 const char *child_name;
2597 svn_boolean_t orig_only = FALSE, working_only = FALSE;
2598 node_move_baton_t cnmb = { 0 };
2601 cnmb.umb = nmb->umb;
2602 cnmb.shadowed = nmb->shadowed;
2604 svn_pool_clear(iterpool);
2605 if (i >= orig_children->nelts)
2607 working_only = TRUE;
2608 child_name = APR_ARRAY_IDX(working_children, j, const char *);
2610 else if (j >= working_children->nelts)
2613 child_name = APR_ARRAY_IDX(orig_children, i, const char *);
2617 const char *orig_name = APR_ARRAY_IDX(orig_children, i,
2619 const char *working_name = APR_ARRAY_IDX(working_children, j,
2621 int cmp = strcmp(orig_name, working_name);
2624 working_only = TRUE;
2628 child_name = working_only ? working_name : orig_name;
2631 cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
2633 cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
2636 SVN_ERR(update_incoming_moved_node(&cnmb, wcroot, cnmb.src_relpath,
2637 cnmb.dst_relpath, iterpool));
2644 if (nmb->skip) /* Does parent now want a skip? */
2649 return SVN_NO_ERROR;
2652 /* The body of svn_wc__db_update_incoming_move(). */
2653 static svn_error_t *
2654 update_incoming_move(svn_revnum_t *old_rev,
2655 svn_revnum_t *new_rev,
2657 svn_wc__db_wcroot_t *wcroot,
2658 const char *local_relpath,
2659 const char *dst_relpath,
2660 svn_wc_operation_t operation,
2661 svn_wc_conflict_action_t action,
2662 svn_wc_conflict_reason_t reason,
2663 svn_cancel_func_t cancel_func,
2665 apr_pool_t *scratch_pool)
2667 update_move_baton_t umb = { NULL };
2668 svn_wc_conflict_version_t old_version;
2669 svn_wc_conflict_version_t new_version;
2670 apr_int64_t repos_id;
2671 node_move_baton_t nmb = { 0 };
2672 svn_boolean_t is_modified;
2674 SVN_ERR_ASSERT(svn_relpath_skip_ancestor(dst_relpath, local_relpath) == NULL);
2676 /* For incoming moves during update/switch, the move source is a copied
2677 * tree which was copied from the pre-update BASE revision while raising
2678 * the tree conflict, when the update attempted to delete the move source.
2679 * This copy is our "original" state (SRC of the diff) and the local changes
2680 * on top of this copy at the top-most WORKING layer are used to drive the
2681 * editor (DST of the diff).
2683 * The move destination, where changes are applied to, is now in the BASE
2684 * tree at DST_RELPATH. This repository-side move is the "incoming change"
2685 * recorded for any tree conflicts created during the editor drive.
2686 * We assume this path contains no local changes, and create local changes
2687 * in DST_RELPATH corresponding to changes contained in the conflict victim.
2689 * DST_OP_DEPTH is used to infer the "op-root" of the incoming move. This
2690 * "op-root" is virtual because all nodes belonging to the incoming move
2691 * live in the BASE tree. It is used for constructing repository paths
2692 * when new tree conflicts need to be raised.
2694 umb.src_op_depth = relpath_depth(local_relpath); /* SRC of diff */
2695 umb.dst_op_depth = relpath_depth(dst_relpath); /* virtual DST op-root */
2697 SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
2698 SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
2700 /* Make sure there are no local modifications in the move destination. */
2701 SVN_ERR(svn_wc__node_has_local_mods(&is_modified, NULL, db,
2702 svn_dirent_join(wcroot->abspath,
2705 TRUE, cancel_func, cancel_baton,
2708 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2709 _("Cannot merge local changes from '%s' because "
2710 "'%s' already contains other local changes "
2711 "(please commit or revert these other changes "
2713 svn_dirent_local_style(
2714 svn_dirent_join(wcroot->abspath, local_relpath,
2717 svn_dirent_local_style(
2718 svn_dirent_join(wcroot->abspath, dst_relpath,
2722 /* Check for switched subtrees and mixed-revision working copy. */
2723 SVN_ERR(suitable_for_move(wcroot, dst_relpath, scratch_pool));
2725 /* Read version info from the updated incoming post-move location. */
2726 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_version.node_kind,
2727 &new_version.peg_rev,
2728 &new_version.path_in_repos,
2730 NULL, NULL, NULL, NULL, NULL,
2731 NULL, NULL, NULL, NULL, NULL,
2732 wcroot, dst_relpath,
2733 scratch_pool, scratch_pool));
2735 SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
2736 &new_version.repos_uuid,
2740 /* Read version info from the victim's location. */
2741 SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind,
2742 &old_version.peg_rev,
2743 &old_version.path_in_repos, &repos_id,
2744 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2746 local_relpath, umb.src_op_depth,
2747 scratch_pool, scratch_pool));
2749 SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
2750 &old_version.repos_uuid,
2753 *old_rev = old_version.peg_rev;
2754 *new_rev = new_version.peg_rev;
2756 umb.operation = operation;
2757 umb.old_version= &old_version;
2758 umb.new_version= &new_version;
2760 umb.wcroot = wcroot;
2761 umb.cancel_func = cancel_func;
2762 umb.cancel_baton = cancel_baton;
2764 /* Create a new, and empty, list for notification information. */
2765 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2766 STMT_CREATE_UPDATE_MOVE_LIST));
2768 /* Drive the editor... */
2771 nmb.src_relpath = local_relpath;
2772 nmb.dst_relpath = dst_relpath;
2773 /* nmb.shadowed = FALSE; */
2774 /* nmb.edited = FALSE; */
2775 /* nmb.skip_children = FALSE; */
2777 /* We walk the conflict victim, comparing each node with the equivalent node
2778 * at the WORKING layer, applying any local changes to nodes at the move
2780 SVN_ERR(update_incoming_moved_node(&nmb, wcroot, local_relpath, dst_relpath,
2783 return SVN_NO_ERROR;
2787 svn_wc__db_update_incoming_move(svn_wc__db_t *db,
2788 const char *local_abspath,
2789 const char *dest_abspath,
2790 svn_wc_operation_t operation,
2791 svn_wc_conflict_action_t action,
2792 svn_wc_conflict_reason_t reason,
2793 svn_cancel_func_t cancel_func,
2795 svn_wc_notify_func2_t notify_func,
2797 apr_pool_t *scratch_pool)
2799 svn_wc__db_wcroot_t *wcroot;
2800 svn_revnum_t old_rev, new_rev;
2801 const char *local_relpath;
2802 const char *dest_relpath;
2804 /* ### Check for mixed-rev src or dst? */
2806 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2808 scratch_pool, scratch_pool));
2809 VERIFY_USABLE_WCROOT(wcroot);
2812 = svn_dirent_skip_ancestor(wcroot->abspath, dest_abspath);
2814 SVN_WC__DB_WITH_TXN(update_incoming_move(&old_rev, &new_rev, db, wcroot,
2815 local_relpath, dest_relpath,
2816 operation, action, reason,
2817 cancel_func, cancel_baton,
2821 /* Send all queued up notifications. */
2822 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev,
2823 notify_func, notify_baton,
2827 svn_wc_notify_t *notify;
2829 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2832 svn_wc_notify_update_completed,
2834 notify->kind = svn_node_none;
2835 notify->content_state = svn_wc_notify_state_inapplicable;
2836 notify->prop_state = svn_wc_notify_state_inapplicable;
2837 notify->revision = new_rev;
2838 notify_func(notify_baton, notify, scratch_pool);
2842 return SVN_NO_ERROR;
2845 typedef struct update_local_add_baton_t {
2848 svn_wc__db_wcroot_t *wcroot;
2849 svn_cancel_func_t cancel_func;
2852 /* We refer to these if raising new tree conflicts. */
2853 const svn_wc_conflict_version_t *new_version;
2854 } update_local_add_baton_t;
2856 typedef struct added_node_baton_t {
2857 struct update_local_add_baton_t *b;
2858 struct added_node_baton_t *pb;
2859 const char *local_relpath;
2861 svn_boolean_t edited;
2862 } added_node_baton_t;
2865 static svn_error_t *
2866 update_local_add_mark_node_edited(added_node_baton_t *nb,
2867 apr_pool_t *scratch_pool)
2870 return SVN_NO_ERROR;
2874 SVN_ERR(update_local_add_mark_node_edited(nb->pb, scratch_pool));
2882 return SVN_NO_ERROR;
2885 static svn_error_t *
2886 update_local_add_mark_parent_edited(added_node_baton_t *nb,
2887 apr_pool_t *scratch_pool)
2889 SVN_ERR_ASSERT(nb && nb->pb);
2891 SVN_ERR(update_local_add_mark_node_edited(nb->pb, scratch_pool));
2896 return SVN_NO_ERROR;
2899 static svn_error_t *
2900 mark_update_add_add_tree_conflict(added_node_baton_t *nb,
2901 svn_node_kind_t base_kind,
2902 svn_node_kind_t working_kind,
2903 svn_wc_conflict_reason_t local_change,
2904 apr_pool_t *result_pool,
2905 apr_pool_t *scratch_pool)
2908 svn_wc__db_t *db = nb->b->db;
2909 svn_wc__db_wcroot_t *wcroot = nb->b->wcroot;
2910 svn_wc_conflict_version_t *new_version;
2911 svn_skel_t *conflict;
2913 new_version = svn_wc_conflict_version_dup(nb->b->new_version, result_pool);
2915 /* Fill in conflict info templates with info for this node. */
2916 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, &new_version->peg_rev,
2917 &new_version->path_in_repos,
2918 NULL, NULL, NULL, NULL, NULL, NULL,
2919 NULL, NULL, NULL, NULL, NULL,
2920 wcroot, nb->local_relpath,
2921 scratch_pool, scratch_pool));
2922 new_version->node_kind = base_kind;
2924 SVN_ERR(create_tree_conflict(&conflict, wcroot, nb->local_relpath,
2925 nb->local_relpath, db, NULL, new_version,
2926 svn_wc_operation_update,
2927 svn_node_none, base_kind, NULL,
2928 local_change, svn_wc_conflict_action_add,
2929 NULL, scratch_pool, scratch_pool));
2931 SVN_ERR(update_move_list_add(wcroot, nb->local_relpath, db,
2932 svn_wc_notify_tree_conflict, working_kind,
2933 svn_wc_notify_state_inapplicable,
2934 svn_wc_notify_state_inapplicable,
2935 conflict, NULL, scratch_pool));
2936 return SVN_NO_ERROR;
2939 static svn_error_t *
2940 update_local_add_notify_obstructed_or_missing(added_node_baton_t *nb,
2941 svn_node_kind_t working_kind,
2942 svn_node_kind_t kind_on_disk,
2943 apr_pool_t *scratch_pool)
2945 svn_wc_notify_state_t content_state;
2947 if (kind_on_disk == svn_node_none)
2948 content_state = svn_wc_notify_state_missing;
2950 content_state = svn_wc_notify_state_obstructed;
2952 SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
2953 svn_wc_notify_skip, working_kind,
2954 content_state, svn_wc_notify_state_inapplicable,
2955 NULL, NULL, scratch_pool));
2956 return SVN_NO_ERROR;
2959 static svn_error_t *
2960 tc_editor_update_add_new_file(added_node_baton_t *nb,
2961 svn_node_kind_t base_kind,
2962 const svn_checksum_t *base_checksum,
2963 apr_hash_t *base_props,
2964 svn_node_kind_t working_kind,
2965 const svn_checksum_t *working_checksum,
2966 apr_hash_t *working_props,
2967 apr_pool_t *scratch_pool)
2969 const char *local_abspath;
2970 svn_node_kind_t kind_on_disk;
2972 SVN_ERR(update_local_add_mark_parent_edited(nb, scratch_pool));
2974 return SVN_NO_ERROR;
2976 if (base_kind != svn_node_none)
2978 SVN_ERR(mark_update_add_add_tree_conflict(nb, base_kind, svn_node_file,
2979 svn_wc_conflict_reason_added,
2980 scratch_pool, scratch_pool));
2982 return SVN_NO_ERROR;
2985 /* Check for obstructions. */
2986 local_abspath = svn_dirent_join(nb->b->wcroot->abspath, nb->local_relpath,
2988 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
2989 if (kind_on_disk != svn_node_file)
2991 SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, working_kind,
2995 return SVN_NO_ERROR;
2998 /* Nothing else to do. Locally added files are an op-root in NODES. */
3000 SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
3001 svn_wc_notify_update_add, svn_node_file,
3002 svn_wc_notify_state_inapplicable,
3003 svn_wc_notify_state_inapplicable,
3004 NULL, NULL, scratch_pool));
3005 return SVN_NO_ERROR;
3008 static svn_error_t *
3009 tc_editor_update_add_new_directory(added_node_baton_t *nb,
3010 svn_node_kind_t base_kind,
3011 apr_hash_t *base_props,
3012 apr_hash_t *working_props,
3013 apr_pool_t *scratch_pool)
3015 const char *local_abspath;
3016 svn_node_kind_t kind_on_disk;
3018 SVN_ERR(update_local_add_mark_parent_edited(nb, scratch_pool));
3020 return SVN_NO_ERROR;
3022 if (base_kind != svn_node_none)
3024 SVN_ERR(mark_update_add_add_tree_conflict(nb, base_kind, svn_node_dir,
3025 svn_wc_conflict_reason_added,
3026 scratch_pool, scratch_pool));
3028 return SVN_NO_ERROR;
3031 /* Check for obstructions. */
3032 local_abspath = svn_dirent_join(nb->b->wcroot->abspath, nb->local_relpath,
3034 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3035 if (kind_on_disk != svn_node_dir)
3037 SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_dir,
3041 return SVN_NO_ERROR;
3044 /* Nothing else to do. Locally added directories are an op-root in NODES. */
3046 SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
3047 svn_wc_notify_update_add, svn_node_dir,
3048 svn_wc_notify_state_inapplicable,
3049 svn_wc_notify_state_inapplicable,
3050 NULL, NULL, scratch_pool));
3051 return SVN_NO_ERROR;
3054 static svn_error_t *
3055 update_incoming_add_merge_props(svn_wc_notify_state_t *prop_state,
3056 svn_skel_t **conflict_skel,
3057 const char *local_relpath,
3058 apr_hash_t *base_props,
3059 apr_hash_t *working_props,
3061 svn_wc__db_wcroot_t *wcroot,
3062 apr_pool_t *result_pool,
3063 apr_pool_t *scratch_pool)
3065 apr_hash_t *new_actual_props;
3066 apr_array_header_t *propchanges;
3067 const char *local_abspath = svn_dirent_join(wcroot->abspath,
3072 * Run a 3-way prop merge to update the props, using the empty props
3073 * as the merge base, the post-update props as the merge-left version, and
3074 * the current props of the added working file as the merge-right version.
3076 SVN_ERR(svn_prop_diffs(&propchanges, working_props,
3077 apr_hash_make(scratch_pool), scratch_pool));
3078 SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state, &new_actual_props,
3080 apr_hash_make(scratch_pool),
3081 base_props, working_props, propchanges,
3082 result_pool, scratch_pool));
3084 /* Install the new actual props. */
3085 if (apr_hash_count(new_actual_props) > 0)
3086 SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath,
3088 svn_wc__has_magic_property(
3092 return SVN_NO_ERROR;
3095 static svn_error_t *
3096 tc_editor_update_add_merge_files(added_node_baton_t *nb,
3097 const svn_checksum_t *working_checksum,
3098 const svn_checksum_t *base_checksum,
3099 apr_hash_t *working_props,
3100 apr_hash_t *base_props,
3101 apr_pool_t *scratch_pool)
3103 update_local_add_baton_t *b = nb->b;
3104 apr_array_header_t *propchanges;
3105 svn_boolean_t is_modified;
3106 enum svn_wc_merge_outcome_t merge_outcome;
3107 svn_skel_t *conflict_skel = NULL;
3108 svn_wc_notify_state_t prop_state, content_state;
3109 svn_skel_t *work_items = NULL;
3110 svn_node_kind_t kind_on_disk;
3111 const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
3115 SVN_ERR(update_local_add_mark_node_edited(nb, scratch_pool));
3117 return SVN_NO_ERROR;
3119 /* Check for on-disk obstructions or missing files. */
3120 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3121 if (kind_on_disk != svn_node_file)
3123 SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_file,
3127 return SVN_NO_ERROR;
3130 SVN_ERR(update_incoming_add_merge_props(&prop_state, &conflict_skel,
3132 base_props, working_props,
3134 scratch_pool, scratch_pool));
3136 SVN_ERR(svn_wc__internal_file_modified_p(&is_modified,
3137 b->db, local_abspath,
3138 FALSE /* exact_comparison */,
3142 svn_skel_t *work_item = NULL;
3144 SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
3145 local_abspath, NULL,
3146 /* FIXME: use_commit_times? */
3148 TRUE, /* record_file_info */
3149 scratch_pool, scratch_pool));
3150 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
3151 content_state = svn_wc_notify_state_changed;
3155 const char *empty_file_abspath;
3156 const char *pristine_abspath;
3157 svn_skel_t *work_item = NULL;
3160 * Run a 3-way merge to update the file, using the empty file
3161 * merge base, the post-update pristine text as the merge-left version,
3162 * and the locally added content of the working file as the merge-right
3165 SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file_abspath, NULL,
3166 svn_io_file_del_on_pool_cleanup,
3167 scratch_pool, scratch_pool));
3168 SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, b->db,
3169 b->wcroot->abspath, base_checksum,
3170 scratch_pool, scratch_pool));
3172 /* Create a property diff which shows all props as added. */
3173 SVN_ERR(svn_prop_diffs(&propchanges, working_props,
3174 apr_hash_make(scratch_pool), scratch_pool));
3176 SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
3177 &merge_outcome, b->db,
3182 NULL, NULL, NULL, /* diff labels */
3183 apr_hash_make(scratch_pool),
3184 FALSE, /* dry-run */
3185 NULL, /* diff3-cmd */
3186 NULL, /* merge options */
3188 b->cancel_func, b->cancel_baton,
3189 scratch_pool, scratch_pool));
3191 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
3193 if (merge_outcome == svn_wc_merge_conflict)
3194 content_state = svn_wc_notify_state_conflicted;
3196 content_state = svn_wc_notify_state_merged;
3199 /* If there are any conflicts to be stored, convert them into work items
3203 svn_wc_conflict_version_t *new_version;
3204 svn_node_kind_t new_kind;
3205 svn_revnum_t new_rev;
3206 const char *repos_relpath;
3208 new_version = svn_wc_conflict_version_dup(nb->b->new_version,
3210 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev,
3211 &repos_relpath, NULL, NULL,
3212 NULL, NULL, NULL, NULL, NULL,
3213 NULL, NULL, NULL, NULL,
3214 b->wcroot, nb->local_relpath,
3215 scratch_pool, scratch_pool));
3216 /* Fill in conflict info templates with info for this node. */
3217 new_version->path_in_repos = repos_relpath;
3218 new_version->node_kind = new_kind;
3219 new_version->peg_rev = new_rev;
3221 /* Create conflict markers. */
3222 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, NULL,
3223 new_version, scratch_pool,
3225 if (prop_state == svn_wc_notify_state_conflicted)
3226 SVN_ERR(svn_wc__conflict_create_markers(&work_items, b->db,
3233 SVN_ERR(update_move_list_add(b->wcroot, nb->local_relpath, b->db,
3234 svn_wc_notify_update_update,
3235 svn_node_file, content_state, prop_state,
3236 conflict_skel, work_items, scratch_pool));
3238 return SVN_NO_ERROR;
3241 static svn_error_t *
3242 tc_editor_update_add_merge_dirprops(added_node_baton_t *nb,
3243 apr_hash_t *working_props,
3244 apr_hash_t *base_props,
3245 apr_pool_t *scratch_pool)
3247 update_local_add_baton_t *b = nb->b;
3248 svn_skel_t *conflict_skel = NULL;
3249 svn_wc_notify_state_t prop_state;
3250 svn_skel_t *work_items = NULL;
3251 svn_node_kind_t kind_on_disk;
3252 const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
3256 SVN_ERR(update_local_add_mark_node_edited(nb, scratch_pool));
3258 return SVN_NO_ERROR;
3260 /* Check for on-disk obstructions or missing files. */
3261 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3262 if (kind_on_disk != svn_node_dir)
3264 SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_dir,
3268 return SVN_NO_ERROR;
3271 SVN_ERR(update_incoming_add_merge_props(&prop_state, &conflict_skel,
3273 base_props, working_props,
3275 scratch_pool, scratch_pool));
3277 /* If there are any conflicts to be stored, convert them into work items. */
3278 if (conflict_skel && prop_state == svn_wc_notify_state_conflicted)
3280 svn_wc_conflict_version_t *new_version;
3281 svn_node_kind_t new_kind;
3282 svn_revnum_t new_rev;
3283 const char *repos_relpath;
3285 new_version = svn_wc_conflict_version_dup(nb->b->new_version,
3287 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev,
3288 &repos_relpath, NULL, NULL,
3289 NULL, NULL, NULL, NULL, NULL,
3290 NULL, NULL, NULL, NULL,
3291 b->wcroot, nb->local_relpath,
3292 scratch_pool, scratch_pool));
3293 /* Fill in conflict info templates with info for this node. */
3294 new_version->path_in_repos = repos_relpath;
3295 new_version->node_kind = new_kind;
3296 new_version->peg_rev = new_rev;
3298 /* Create conflict markers. */
3299 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, NULL,
3300 new_version, scratch_pool,
3302 SVN_ERR(svn_wc__conflict_create_markers(&work_items, b->db,
3309 SVN_ERR(update_move_list_add(b->wcroot, nb->local_relpath, b->db,
3310 svn_wc_notify_update_update, svn_node_dir,
3311 svn_wc_notify_state_inapplicable, prop_state,
3312 conflict_skel, work_items, scratch_pool));
3314 return SVN_NO_ERROR;
3317 static svn_error_t *
3318 update_locally_added_node(added_node_baton_t *nb,
3319 apr_pool_t *scratch_pool)
3321 update_local_add_baton_t *b = nb->b;
3322 svn_wc__db_wcroot_t *wcroot = b->wcroot;
3323 svn_wc__db_t *db = b->db;
3324 svn_node_kind_t base_kind, working_kind;
3325 const svn_checksum_t *base_checksum;
3326 apr_hash_t *base_props, *working_props;
3327 apr_array_header_t *base_children, *working_children;
3328 const char *local_abspath = svn_dirent_join(wcroot->abspath,
3333 SVN_ERR(b->cancel_func(b->cancel_baton));
3336 return SVN_NO_ERROR;
3338 /* Compare the tree conflict victim's BASE layer to the working layer. */
3339 SVN_ERR(get_info(&base_props, &base_checksum, &base_children, &base_kind,
3340 nb->local_relpath, 0, wcroot, scratch_pool, scratch_pool));
3341 SVN_ERR(get_working_info(&working_props, NULL, &working_children,
3342 &working_kind, nb->local_relpath, wcroot,
3343 scratch_pool, scratch_pool));
3344 if (working_kind == svn_node_none)
3346 svn_node_kind_t kind_on_disk;
3347 svn_skel_t *work_item = NULL;
3349 /* Skip obstructed nodes. */
3350 SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk,
3352 if (kind_on_disk != base_kind && kind_on_disk != svn_node_none)
3354 SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath,
3358 svn_wc_notify_state_obstructed,
3359 svn_wc_notify_state_inapplicable,
3360 NULL, NULL, scratch_pool));
3362 return SVN_NO_ERROR;
3365 /* The working tree has no node here. The working copy of this node
3366 * is currently not installed because the base tree is shadowed.
3367 * Queue an installation of this node into the working copy. */
3368 if (base_kind == svn_node_file || base_kind == svn_node_symlink)
3369 SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, local_abspath,
3371 /* FIXME: use_commit_times? */
3373 TRUE, /* record_file_info */
3374 scratch_pool, scratch_pool));
3375 else if (base_kind == svn_node_dir)
3376 SVN_ERR(svn_wc__wq_build_dir_install(&work_item, db, local_abspath,
3377 scratch_pool, scratch_pool));
3380 SVN_ERR(update_move_list_add(wcroot, nb->local_relpath, db,
3381 svn_wc_notify_update_add,
3383 svn_wc_notify_state_inapplicable,
3384 svn_wc_notify_state_inapplicable,
3385 NULL, work_item, scratch_pool));
3386 return SVN_NO_ERROR;
3389 if (base_kind != working_kind)
3391 if (working_kind == svn_node_file || working_kind == svn_node_symlink)
3393 svn_checksum_t *working_checksum = NULL;
3396 SVN_ERR(svn_io_file_checksum2(&working_checksum, local_abspath,
3397 base_checksum->kind, scratch_pool));
3398 SVN_ERR(tc_editor_update_add_new_file(nb, base_kind, base_checksum,
3399 base_props, working_kind,
3400 working_checksum, working_props,
3403 else if (working_kind == svn_node_dir)
3404 SVN_ERR(tc_editor_update_add_new_directory(nb, base_kind, base_props,
3410 svn_boolean_t props_equal;
3412 SVN_ERR(props_match(&props_equal, base_props, working_props,
3415 if (working_kind == svn_node_file || working_kind == svn_node_symlink)
3417 svn_checksum_t *working_checksum;
3419 SVN_ERR_ASSERT(base_checksum);
3420 SVN_ERR(svn_io_file_checksum2(&working_checksum, local_abspath,
3421 base_checksum->kind, scratch_pool));
3422 if (!props_equal || !svn_checksum_match(base_checksum,
3424 SVN_ERR(tc_editor_update_add_merge_files(nb, working_checksum,
3426 working_props, base_props,
3429 else if (working_kind == svn_node_dir && !props_equal)
3430 SVN_ERR(tc_editor_update_add_merge_dirprops(nb, working_props,
3436 return SVN_NO_ERROR;
3438 if (working_kind == svn_node_dir)
3440 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3443 while (i < base_children->nelts || j < working_children->nelts)
3445 const char *child_name;
3446 svn_boolean_t base_only = FALSE, working_only = FALSE;
3447 added_node_baton_t cnb = { 0 };
3453 svn_pool_clear(iterpool);
3454 if (i >= base_children->nelts)
3456 working_only = TRUE;
3457 child_name = APR_ARRAY_IDX(working_children, j, const char *);
3459 else if (j >= working_children->nelts)
3462 child_name = APR_ARRAY_IDX(base_children, i, const char *);
3466 const char *base_name = APR_ARRAY_IDX(base_children, i,
3468 const char *working_name = APR_ARRAY_IDX(working_children, j,
3470 int cmp = strcmp(base_name, working_name);
3473 working_only = TRUE;
3477 child_name = working_only ? working_name : base_name;
3480 cnb.local_relpath = svn_relpath_join(nb->local_relpath, child_name,
3483 SVN_ERR(update_locally_added_node(&cnb, iterpool));
3490 if (nb->skip) /* Does parent now want a skip? */
3495 return SVN_NO_ERROR;
3498 /* The body of svn_wc__db_update_local_add(). */
3499 static svn_error_t *
3500 update_local_add(svn_revnum_t *new_rev,
3502 svn_wc__db_wcroot_t *wcroot,
3503 const char *local_relpath,
3504 svn_cancel_func_t cancel_func,
3506 apr_pool_t *scratch_pool)
3508 update_local_add_baton_t b = { 0 };
3509 added_node_baton_t nb = { 0 };
3510 const char *repos_root_url;
3511 const char *repos_uuid;
3512 const char *repos_relpath;
3513 apr_int64_t repos_id;
3514 svn_node_kind_t new_kind;
3515 svn_sqlite__stmt_t *stmt;
3517 b.add_op_depth = relpath_depth(local_relpath); /* DST op-root */
3519 SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
3523 b.cancel_func = cancel_func;
3524 b.cancel_baton = cancel_baton;
3526 /* Read new version info from the updated BASE node. */
3527 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, new_rev,
3528 &repos_relpath, &repos_id,
3529 NULL, NULL, NULL, NULL, NULL,
3530 NULL, NULL, NULL, NULL, NULL,
3531 wcroot, local_relpath,
3532 scratch_pool, scratch_pool));
3533 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, wcroot,
3534 repos_id, scratch_pool));
3535 b.new_version = svn_wc_conflict_version_create2(repos_root_url, repos_uuid,
3536 repos_relpath, *new_rev,
3537 new_kind, scratch_pool);
3539 /* Create a new, and empty, list for notification information. */
3540 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
3541 STMT_CREATE_UPDATE_MOVE_LIST));
3543 /* Drive the editor... */
3545 nb.local_relpath = local_relpath;
3547 SVN_ERR(update_locally_added_node(&nb, scratch_pool));
3549 /* The conflict victim is now part of the base tree.
3550 * Remove the locally added version of the conflict victim and its children.
3551 * Any children we want to retain are at a higher op-depth so they won't
3552 * be deleted by this statement. */
3553 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3554 STMT_DELETE_WORKING_OP_DEPTH));
3555 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
3556 relpath_depth(local_relpath)));
3557 SVN_ERR(svn_sqlite__update(NULL, stmt));
3559 /* Remove the tree conflict marker. */
3560 SVN_ERR(svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
3562 NULL, scratch_pool));
3563 return SVN_NO_ERROR;
3567 svn_wc__db_update_local_add(svn_wc__db_t *db,
3568 const char *local_abspath,
3569 svn_cancel_func_t cancel_func,
3571 svn_wc_notify_func2_t notify_func,
3573 apr_pool_t *scratch_pool)
3575 svn_wc__db_wcroot_t *wcroot;
3576 svn_revnum_t new_rev;
3577 const char *local_relpath;
3579 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
3581 scratch_pool, scratch_pool));
3582 VERIFY_USABLE_WCROOT(wcroot);
3584 SVN_WC__DB_WITH_TXN(update_local_add(&new_rev, db, wcroot,
3586 cancel_func, cancel_baton,
3590 /* Send all queued up notifications. */
3591 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, new_rev, new_rev,
3592 notify_func, notify_baton,
3596 svn_wc_notify_t *notify;
3598 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
3601 svn_wc_notify_update_completed,
3603 notify->kind = svn_node_none;
3604 notify->content_state = svn_wc_notify_state_inapplicable;
3605 notify->prop_state = svn_wc_notify_state_inapplicable;
3606 notify->revision = new_rev;
3607 notify_func(notify_baton, notify, scratch_pool);
3611 return SVN_NO_ERROR;
3613 /* Set *CAN_BUMP to TRUE if DEPTH is sufficient to cover the entire
3614 tree LOCAL_RELPATH at OP_DEPTH, to FALSE otherwise. */
3615 static svn_error_t *
3616 depth_sufficient_to_bump(svn_boolean_t *can_bump,
3617 svn_wc__db_wcroot_t *wcroot,
3618 const char *local_relpath,
3621 apr_pool_t *scratch_pool)
3623 svn_sqlite__stmt_t *stmt;
3624 svn_boolean_t have_row;
3628 case svn_depth_infinity:
3630 return SVN_NO_ERROR;
3632 case svn_depth_empty:
3633 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3634 STMT_SELECT_OP_DEPTH_CHILDREN));
3635 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3636 local_relpath, op_depth));
3639 case svn_depth_files:
3640 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3641 STMT_SELECT_HAS_NON_FILE_CHILDREN));
3642 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3643 local_relpath, op_depth));
3646 case svn_depth_immediates:
3647 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3648 STMT_SELECT_HAS_GRANDCHILDREN));
3649 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3650 local_relpath, op_depth));
3653 SVN_ERR_MALFUNCTION();
3655 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3656 SVN_ERR(svn_sqlite__reset(stmt));
3658 *can_bump = !have_row;
3659 return SVN_NO_ERROR;
3662 /* Mark a move-edit conflict on MOVE_SRC_ROOT_RELPATH. */
3663 static svn_error_t *
3664 bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot,
3665 const char *move_src_root_relpath,
3667 const char *move_src_op_root_relpath,
3668 const char *move_dst_op_root_relpath,
3670 apr_pool_t *scratch_pool)
3672 apr_int64_t repos_id;
3673 const char *repos_root_url;
3674 const char *repos_uuid;
3675 const char *old_repos_relpath;
3676 const char *new_repos_relpath;
3677 svn_revnum_t old_rev;
3678 svn_revnum_t new_rev;
3679 svn_node_kind_t old_kind;
3680 svn_node_kind_t new_kind;
3681 svn_wc_conflict_version_t *old_version;
3682 svn_wc_conflict_version_t *new_version;
3683 svn_skel_t *conflict;
3685 /* Verify precondition: We are allowed to set a tree conflict here. */
3686 SVN_ERR(verify_write_lock(wcroot, move_src_root_relpath, scratch_pool));
3688 /* Read new (post-update) information from the new move source BASE node. */
3689 SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_kind, &new_rev,
3690 &new_repos_relpath, &repos_id,
3691 NULL, NULL, NULL, NULL, NULL,
3693 wcroot, move_src_op_root_relpath,
3694 src_op_depth, scratch_pool, scratch_pool));
3695 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
3696 wcroot, repos_id, scratch_pool));
3698 /* Read old (pre-update) information from the move destination node.
3700 This potentially touches nodes that aren't locked by us, but that is not
3701 a problem because we have a SQLite write lock here, and all sqlite
3702 operations that affect move stability use a sqlite lock as well.
3703 (And affecting the move itself requires a write lock on the node that
3704 we do own the lock for: the move source)
3706 SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_kind, &old_rev,
3707 &old_repos_relpath, NULL, NULL, NULL,
3708 NULL, NULL, NULL, NULL, NULL, NULL,
3709 wcroot, move_dst_op_root_relpath,
3710 relpath_depth(move_dst_op_root_relpath),
3711 scratch_pool, scratch_pool));
3713 if (strcmp(move_src_root_relpath, move_src_op_root_relpath))
3715 /* We have information for the op-root, but need it for the node that
3716 we are putting the tree conflict on. Luckily we know that we have
3719 const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath,
3720 move_src_root_relpath);
3722 old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath,
3724 new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath,
3728 old_version = svn_wc_conflict_version_create2(
3729 repos_root_url, repos_uuid, old_repos_relpath, old_rev,
3730 old_kind, scratch_pool);
3731 new_version = svn_wc_conflict_version_create2(
3732 repos_root_url, repos_uuid, new_repos_relpath, new_rev,
3733 new_kind, scratch_pool);
3735 SVN_ERR(create_tree_conflict(&conflict, wcroot, move_src_root_relpath,
3736 move_dst_op_root_relpath,
3737 db, old_version, new_version,
3738 svn_wc_operation_update,
3741 svn_wc_conflict_reason_moved_away,
3742 svn_wc_conflict_action_edit,
3743 move_src_op_root_relpath,
3744 scratch_pool, scratch_pool));
3746 SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db,
3747 svn_wc_notify_tree_conflict,
3749 svn_wc_notify_state_inapplicable,
3750 svn_wc_notify_state_inapplicable,
3751 conflict, NULL, scratch_pool));
3753 return SVN_NO_ERROR;
3756 /* Checks if SRC_RELPATH is within BUMP_DEPTH from BUMP_ROOT. Sets
3757 * *SKIP to TRUE if the node should be skipped, otherwise to FALSE.
3758 * Sets *SRC_DEPTH to the remaining depth at SRC_RELPATH.
3760 static svn_error_t *
3761 check_bump_layer(svn_boolean_t *skip,
3762 svn_depth_t *src_depth,
3763 const char *bump_root,
3764 svn_depth_t bump_depth,
3765 const char *src_relpath,
3766 svn_node_kind_t src_kind,
3767 apr_pool_t *scratch_pool)
3769 const char *relpath;
3772 *src_depth = bump_depth;
3774 relpath = svn_relpath_skip_ancestor(bump_root, src_relpath);
3779 if (bump_depth == svn_depth_infinity)
3780 return SVN_NO_ERROR;
3782 if (relpath && *relpath == '\0')
3783 return SVN_NO_ERROR;
3787 case svn_depth_empty:
3791 case svn_depth_files:
3792 if (src_kind != svn_node_file)
3798 case svn_depth_immediates:
3799 if (!relpath || relpath_depth(relpath) > 1)
3802 *src_depth = svn_depth_empty;
3805 SVN_ERR_MALFUNCTION();
3808 return SVN_NO_ERROR;
3811 /* The guts of bump_moved_away: Determines if a move can be bumped to match
3812 * the move origin and if so performs this bump.
3814 static svn_error_t *
3815 bump_moved_layer(svn_boolean_t *recurse,
3816 svn_wc__db_wcroot_t *wcroot,
3817 const char *local_relpath,
3819 const char *src_relpath,
3821 svn_depth_t src_depth,
3822 const char *dst_relpath,
3824 apr_pool_t *scratch_pool)
3826 svn_sqlite__stmt_t *stmt;
3827 svn_boolean_t have_row;
3828 svn_skel_t *conflict;
3829 svn_boolean_t can_bump;
3830 const char *src_root_relpath;
3832 SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
3836 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3837 STMT_HAS_LAYER_BETWEEN));
3839 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
3840 op_depth, src_del_depth));
3842 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3843 SVN_ERR(svn_sqlite__reset(stmt));
3846 return SVN_NO_ERROR;
3849 SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath,
3850 op_depth, src_depth, scratch_pool));
3852 /* Having chosen to bump an entire BASE tree move we
3853 always have sufficient depth to bump subtree moves. */
3856 /* Are we allowed to bump */
3859 svn_boolean_t locked;
3861 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
3863 FALSE, scratch_pool));
3869 src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth,
3874 SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth,
3875 src_root_relpath, dst_relpath,
3878 return SVN_NO_ERROR;
3881 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
3882 wcroot, src_root_relpath,
3883 scratch_pool, scratch_pool));
3885 /* ### TODO: check this is the right sort of tree-conflict? */
3888 /* ### TODO: verify moved_here? */
3890 SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
3892 SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot,
3893 src_relpath, op_depth,
3894 dst_relpath, NULL, NULL,
3900 return SVN_NO_ERROR;
3903 /* Internal storage for bump_moved_away() */
3906 const char *src_relpath;
3907 const char *dst_relpath;
3908 int src_del_op_depth;
3909 svn_node_kind_t src_kind;
3912 /* Bump moves of LOCAL_RELPATH and all its descendants that were
3913 originally below LOCAL_RELPATH at op-depth OP_DEPTH.
3915 static svn_error_t *
3916 bump_moved_away(svn_wc__db_wcroot_t *wcroot,
3917 const char *local_relpath,
3921 apr_pool_t *scratch_pool)
3923 svn_sqlite__stmt_t *stmt;
3924 svn_boolean_t have_row;
3925 apr_pool_t *iterpool;
3927 apr_array_header_t *pairs = apr_array_make(scratch_pool, 32,
3928 sizeof(struct bump_pair_t*));
3930 /* Build an array, as we can't execute the same Sqlite query recursively */
3931 iterpool = svn_pool_create(scratch_pool);
3933 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3934 STMT_SELECT_MOVED_PAIR3));
3935 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
3937 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3940 struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp));
3942 bp->src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
3943 bp->dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
3944 bp->src_del_op_depth = svn_sqlite__column_int(stmt, 2);
3945 bp->src_kind = svn_sqlite__column_token(stmt, 3, kind_map);
3947 APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp;
3949 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3952 SVN_ERR(svn_sqlite__reset(stmt));
3954 for (i = 0; i < pairs->nelts; i++)
3956 struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *);
3958 svn_depth_t src_wc_depth;
3960 svn_pool_clear(iterpool);
3963 SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth,
3964 bp->src_relpath, bp->src_kind, iterpool));
3968 svn_boolean_t recurse;
3970 SVN_ERR(bump_moved_layer(&recurse, wcroot,
3971 local_relpath, op_depth,
3972 bp->src_relpath, bp->src_del_op_depth,
3973 src_wc_depth, bp->dst_relpath,
3977 SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath,
3978 relpath_depth(bp->dst_relpath),
3979 depth, db, iterpool));
3983 svn_pool_destroy(iterpool);
3985 return SVN_NO_ERROR;
3989 svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot,
3990 const char *local_relpath,
3993 apr_pool_t *scratch_pool)
3995 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
3996 STMT_CREATE_UPDATE_MOVE_LIST));
3998 if (local_relpath[0] != '\0')
4000 const char *move_dst_op_root_relpath;
4001 const char *move_src_root_relpath, *delete_relpath;
4004 /* Is the root of the update moved away? (Impossible for the wcroot) */
4006 err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath,
4007 &move_dst_op_root_relpath,
4009 wcroot, local_relpath,
4011 scratch_pool, scratch_pool);
4015 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4016 return svn_error_trace(err);
4018 svn_error_clear(err);
4020 else if (move_src_root_relpath)
4022 if (strcmp(move_src_root_relpath, local_relpath))
4024 /* An ancestor of the path that was updated is moved away.
4026 If we have a lock on that ancestor, we can mark a tree
4027 conflict on it, if we don't we ignore this case. A future
4028 update of the ancestor will handle this. */
4029 svn_boolean_t locked;
4031 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(
4033 move_src_root_relpath,
4034 FALSE, scratch_pool));
4038 SVN_ERR(bump_mark_tree_conflict(wcroot,
4039 move_src_root_relpath, 0,
4041 move_dst_op_root_relpath,
4044 return SVN_NO_ERROR;
4049 SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool));
4051 return SVN_NO_ERROR;
4054 /* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION
4055 * to reflect the tree conflict on the victim SRC_ABSPATH in DB.
4057 * If SRC_ABSPATH is not a tree-conflict victim, return an error.
4059 static svn_error_t *
4060 fetch_conflict_details(int *src_op_depth,
4061 svn_wc_operation_t *operation,
4062 svn_wc_conflict_action_t *action,
4063 svn_wc_conflict_version_t **left_version,
4064 svn_wc_conflict_version_t **right_version,
4065 svn_wc__db_wcroot_t *wcroot,
4067 const char *local_relpath,
4068 const svn_skel_t *conflict_skel,
4069 apr_pool_t *result_pool,
4070 apr_pool_t *scratch_pool)
4072 const apr_array_header_t *locations;
4073 svn_boolean_t text_conflicted;
4074 svn_boolean_t prop_conflicted;
4075 svn_boolean_t tree_conflicted;
4076 const char *move_src_op_root_abspath;
4077 svn_wc_conflict_reason_t reason;
4078 const char *local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
4082 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
4083 _("'%s' is not in conflict"),
4084 path_for_error_message(wcroot, local_relpath,
4087 SVN_ERR(svn_wc__conflict_read_info(operation, &locations,
4088 &text_conflicted, &prop_conflicted,
4091 conflict_skel, result_pool,
4094 if (text_conflicted || prop_conflicted || !tree_conflicted)
4095 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
4096 _("'%s' is not a valid tree-conflict victim"),
4097 path_for_error_message(wcroot, local_relpath,
4100 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason,
4102 &move_src_op_root_abspath,
4104 conflict_skel, result_pool,
4107 if (reason == svn_wc_conflict_reason_moved_away)
4108 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4109 _("'%s' is already a moved away tree-conflict"),
4110 path_for_error_message(wcroot, local_relpath,
4115 if (locations && locations->nelts > 0)
4116 *left_version = APR_ARRAY_IDX(locations, 0,
4117 svn_wc_conflict_version_t *);
4119 *left_version = NULL;
4124 if (locations && locations->nelts > 1)
4125 *right_version = APR_ARRAY_IDX(locations, 1,
4126 svn_wc_conflict_version_t *);
4128 *right_version = NULL;
4132 int del_depth = relpath_depth(local_relpath);
4134 if (move_src_op_root_abspath)
4135 del_depth = relpath_depth(
4136 svn_dirent_skip_ancestor(wcroot->abspath,
4137 move_src_op_root_abspath));
4139 SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth,
4143 return SVN_NO_ERROR;
4147 svn_wc__db_op_raise_moved_away_internal(
4148 svn_wc__db_wcroot_t *wcroot,
4149 const char *local_relpath,
4152 svn_wc_operation_t operation,
4153 svn_wc_conflict_action_t action,
4154 const svn_wc_conflict_version_t *old_version,
4155 const svn_wc_conflict_version_t *new_version,
4156 apr_pool_t *scratch_pool)
4158 svn_sqlite__stmt_t *stmt;
4159 svn_boolean_t have_row;
4160 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4162 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4163 STMT_CREATE_UPDATE_MOVE_LIST));
4165 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4166 STMT_SELECT_MOVED_DESCENDANTS_SRC));
4167 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
4169 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4173 int delete_op_depth = svn_sqlite__column_int(stmt, 0);
4174 const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL);
4175 svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map);
4176 const char *src_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4177 const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL);
4178 svn_skel_t *conflict;
4179 svn_pool_clear(iterpool);
4181 SVN_ERR_ASSERT(src_repos_relpath != NULL);
4183 err = create_tree_conflict(&conflict, wcroot, src_relpath, dst_relpath,
4184 db, old_version, new_version, operation,
4185 src_kind /* ### old kind */,
4186 src_kind /* ### new kind */,
4188 svn_wc_conflict_reason_moved_away,
4190 svn_relpath_prefix(src_relpath,
4193 iterpool, iterpool);
4196 err = update_move_list_add(wcroot, src_relpath, db,
4197 svn_wc_notify_tree_conflict,
4199 svn_wc_notify_state_inapplicable,
4200 svn_wc_notify_state_inapplicable,
4201 conflict, NULL, scratch_pool);
4204 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
4206 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4208 SVN_ERR(svn_sqlite__reset(stmt));
4210 svn_pool_destroy(iterpool);
4212 return SVN_NO_ERROR;
4216 svn_wc__db_op_raise_moved_away(svn_wc__db_t *db,
4217 const char *local_abspath,
4218 svn_wc_notify_func2_t notify_func,
4220 apr_pool_t *scratch_pool)
4222 svn_wc__db_wcroot_t *wcroot;
4223 const char *local_relpath;
4224 svn_wc_operation_t operation;
4225 svn_wc_conflict_action_t action;
4226 svn_wc_conflict_version_t *left_version, *right_version;
4227 int move_src_op_depth;
4228 svn_skel_t *conflict;
4230 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4232 scratch_pool, scratch_pool));
4233 VERIFY_USABLE_WCROOT(wcroot);
4235 SVN_WC__DB_WITH_TXN4(
4236 svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
4237 wcroot, local_relpath,
4238 scratch_pool, scratch_pool),
4239 fetch_conflict_details(&move_src_op_depth,
4240 &operation, &action,
4241 &left_version, &right_version,
4242 wcroot, db, local_relpath, conflict,
4243 scratch_pool, scratch_pool),
4244 svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
4246 NULL, scratch_pool),
4247 svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath,
4249 db, operation, action,
4250 left_version, right_version,
4254 /* These version numbers are valid for update/switch notifications
4256 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
4258 ? left_version->peg_rev
4259 : SVN_INVALID_REVNUM),
4261 ? right_version->peg_rev
4262 : SVN_INVALID_REVNUM),
4263 notify_func, notify_baton,
4266 return SVN_NO_ERROR;
4269 static svn_error_t *
4270 break_moved_away(svn_wc__db_wcroot_t *wcroot,
4272 const char *local_relpath,
4273 int parent_src_op_depth,
4274 apr_pool_t *scratch_pool)
4276 svn_sqlite__stmt_t *stmt;
4277 svn_boolean_t have_row;
4278 apr_pool_t *iterpool;
4279 svn_error_t *err = NULL;
4281 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4282 STMT_CREATE_UPDATE_MOVE_LIST));
4284 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4285 STMT_SELECT_MOVED_DESCENDANTS_SRC));
4286 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
4287 parent_src_op_depth));
4288 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4290 iterpool = svn_pool_create(scratch_pool);
4293 int src_op_depth = svn_sqlite__column_int(stmt, 0);
4294 const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL);
4295 svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map);
4296 const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL);
4298 svn_pool_clear(iterpool);
4300 err = verify_write_lock(wcroot, src_relpath, iterpool);
4303 err = verify_write_lock(wcroot, dst_relpath, iterpool);
4308 err = svn_error_trace(
4309 svn_wc__db_op_break_move_internal(wcroot,
4310 src_relpath, src_op_depth,
4311 dst_relpath, NULL, iterpool));
4316 err = svn_error_trace(
4317 update_move_list_add(wcroot, src_relpath, db,
4318 svn_wc_notify_move_broken,
4320 svn_wc_notify_state_inapplicable,
4321 svn_wc_notify_state_inapplicable,
4322 NULL, NULL, scratch_pool));
4327 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4329 svn_pool_destroy(iterpool);
4331 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
4335 svn_wc__db_op_break_moved_away(svn_wc__db_t *db,
4336 const char *local_abspath,
4337 const char *del_op_root_abspath,
4338 svn_boolean_t mark_tc_resolved,
4339 svn_wc_notify_func2_t notify_func,
4341 apr_pool_t *scratch_pool)
4343 svn_wc__db_wcroot_t *wcroot;
4344 const char *local_relpath;
4345 const char *del_relpath;
4348 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4350 scratch_pool, scratch_pool));
4351 VERIFY_USABLE_WCROOT(wcroot);
4353 if (del_op_root_abspath)
4354 del_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4355 del_op_root_abspath);
4360 SVN_WC__DB_WITH_TXN4(
4361 find_src_op_depth(&src_op_depth, wcroot, local_relpath,
4362 del_relpath ? relpath_depth(del_relpath)
4363 : relpath_depth(local_relpath),
4365 break_moved_away(wcroot, db, local_relpath, src_op_depth,
4368 ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
4375 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
4378 notify_func, notify_baton,
4380 return SVN_NO_ERROR;
4383 static svn_error_t *
4384 required_lock_for_resolve(const char **required_relpath,
4385 svn_wc__db_wcroot_t *wcroot,
4386 const char *local_relpath,
4387 apr_pool_t *result_pool,
4388 apr_pool_t *scratch_pool)
4390 svn_sqlite__stmt_t *stmt;
4391 svn_boolean_t have_row;
4393 *required_relpath = local_relpath;
4395 /* This simply looks for all moves out of the LOCAL_RELPATH tree. We
4396 could attempt to limit it to only those moves that are going to
4397 be resolved but that would require second guessing the resolver.
4398 This simple algorithm is sufficient although it may give a
4399 strictly larger/deeper lock than necessary. */
4400 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4401 STMT_SELECT_MOVED_OUTSIDE));
4402 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
4403 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4407 const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1,
4411 = svn_relpath_get_longest_ancestor(*required_relpath,
4415 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4417 SVN_ERR(svn_sqlite__reset(stmt));
4419 *required_relpath = apr_pstrdup(result_pool, *required_relpath);
4421 return SVN_NO_ERROR;
4425 svn_wc__required_lock_for_resolve(const char **required_abspath,
4427 const char *local_abspath,
4428 apr_pool_t *result_pool,
4429 apr_pool_t *scratch_pool)
4431 svn_wc__db_wcroot_t *wcroot;
4432 const char *local_relpath;
4433 const char *required_relpath;
4435 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4437 scratch_pool, scratch_pool));
4438 VERIFY_USABLE_WCROOT(wcroot);
4440 SVN_WC__DB_WITH_TXN(
4441 required_lock_for_resolve(&required_relpath, wcroot, local_relpath,
4442 scratch_pool, scratch_pool),
4445 *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath,
4448 return SVN_NO_ERROR;