]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_wc/wc_db_update_move.c
MFV r341618:
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / libsvn_wc / wc_db_update_move.c
1 /*
2  * wc_db_update_move.c :  updating moves during tree-conflict resolution
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* This 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
27  * (or switch).
28  *
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.
32  *
33  * -- Updating local moves --
34  *
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.
40  *
41  * Both the driver and receiver components of the editor are implemented
42  * in this file.
43  *
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]
51  *
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.
61  *
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.
69  *
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.
73  *
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.
78  *
79  * -- Updating incoming moves --
80  *
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.
87  *
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.
93  *
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.
98  * 
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.
106  *
107  * -- Updating local adds --
108  *
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.
115  *
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.
120  *
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
123  * properties merged.
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.
130  */
131
132 #define SVN_WC__I_AM_WC_DB
133
134 #include <assert.h>
135
136 #include "svn_checksum.h"
137 #include "svn_dirent_uri.h"
138 #include "svn_error.h"
139 #include "svn_hash.h"
140 #include "svn_wc.h"
141 #include "svn_props.h"
142 #include "svn_pools.h"
143 #include "svn_sorts.h"
144
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"
149
150 #include "wc.h"
151 #include "props.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"
157
158 /* Helper functions */
159 /* Return the absolute path, in local path style, of LOCAL_RELPATH
160    in WCROOT.  */
161 static const char *
162 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
163                        const char *local_relpath,
164                        apr_pool_t *result_pool)
165 {
166   const char *local_abspath
167     = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
168
169   return svn_dirent_local_style(local_abspath, result_pool);
170 }
171
172 /* Ensure that there is a working copy lock for LOCAL_RELPATH in WCROOT */
173 static svn_error_t *
174 verify_write_lock(svn_wc__db_wcroot_t *wcroot,
175                   const char *local_relpath,
176                   apr_pool_t *scratch_pool)
177 {
178   svn_boolean_t locked;
179
180   SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, local_relpath,
181                                                FALSE, scratch_pool));
182   if (!locked)
183     {
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,
187                                                       scratch_pool));
188     }
189
190   return SVN_NO_ERROR;
191 }
192
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.
197
198    To work around this problem we locate the layer below this path, and use
199    that to pinpoint whatever is moved.
200
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.
203    */
204 static svn_error_t *
205 find_src_op_depth(int *src_op_depth,
206                   svn_wc__db_wcroot_t *wcroot,
207                   const char *src_relpath,
208                   int delete_op_depth,
209                   apr_pool_t *scratch_pool)
210 {
211   svn_sqlite__stmt_t *stmt;
212   svn_boolean_t have_row;
213
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));
218
219   SVN_ERR(svn_sqlite__step(&have_row, stmt));
220   if (have_row)
221     *src_op_depth = svn_sqlite__column_int(stmt, 0);
222   SVN_ERR(svn_sqlite__reset(stmt));
223   if (!have_row)
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,
227                                                     scratch_pool));
228
229   return SVN_NO_ERROR;
230 }
231
232 /*
233  * Receiver code.
234  *
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.
239  *
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.
243  */
244
245 typedef struct update_move_baton_t {
246   svn_wc__db_t *db;
247   svn_wc__db_wcroot_t *wcroot;
248
249   int src_op_depth;
250   int dst_op_depth;
251
252   svn_wc_operation_t operation;
253   svn_wc_conflict_version_t *old_version;
254   svn_wc_conflict_version_t *new_version;
255
256   svn_cancel_func_t cancel_func;
257   void *cancel_baton;
258 } update_move_baton_t;
259
260 /* Per node flags for tree conflict collection */
261 typedef struct node_move_baton_t
262 {
263   svn_boolean_t skip;
264   svn_boolean_t shadowed;
265   svn_boolean_t edited;
266
267   const char *src_relpath;
268   const char *dst_relpath;
269
270   update_move_baton_t *umb;
271   struct node_move_baton_t *pb;
272 } node_move_baton_t;
273
274 /*
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.
279  */
280
281 /* Add an entry to the notification list, and at the same time install
282    a conflict and/or work items. */
283 static svn_error_t *
284 update_move_list_add(svn_wc__db_wcroot_t *wcroot,
285                      const char *local_relpath,
286                      svn_wc__db_t *db,
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)
294 {
295   svn_sqlite__stmt_t *stmt;
296
297   if (conflict)
298     {
299       svn_boolean_t tree_conflict;
300
301       SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
302                                          &tree_conflict,
303                                          db, wcroot->abspath, conflict,
304                                          scratch_pool, scratch_pool));
305       if (tree_conflict)
306         {
307           action = svn_wc_notify_tree_conflict;
308           content_state = svn_wc_notify_state_inapplicable;
309           prop_state = svn_wc_notify_state_inapplicable;
310         }
311     }
312
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));
319
320   if (conflict)
321     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, conflict,
322                                               scratch_pool));
323
324   if (work_item)
325     SVN_ERR(svn_wc__db_wq_add_internal(wcroot, work_item, scratch_pool));
326
327   return SVN_NO_ERROR;
328 }
329
330 /* Send all notifications stored in the notification list, and then
331  * remove the temporary database table. */
332 svn_error_t *
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,
337                                    void *notify_baton,
338                                    apr_pool_t *scratch_pool)
339 {
340   svn_sqlite__stmt_t *stmt;
341
342   if (notify_func)
343     {
344       apr_pool_t *iterpool;
345       svn_boolean_t have_row;
346
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));
350
351       iterpool = svn_pool_create(scratch_pool);
352       while (have_row)
353         {
354           const char *local_relpath;
355           svn_wc_notify_action_t action;
356           svn_wc_notify_t *notify;
357
358           svn_pool_clear(iterpool);
359
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,
363                                                         local_relpath,
364                                                         iterpool),
365                                         action, iterpool);
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);
372
373           SVN_ERR(svn_sqlite__step(&have_row, stmt));
374         }
375       svn_pool_destroy(iterpool);
376       SVN_ERR(svn_sqlite__reset(stmt));
377     }
378
379   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
380                                     STMT_FINALIZE_UPDATE_MOVE));
381   SVN_ERR(svn_sqlite__step_done(stmt));
382
383   return SVN_NO_ERROR;
384 }
385
386 /* Create a tree-conflict for recording on LOCAL_RELPATH if such
387    a tree-conflict does not already exist. */
388 static svn_error_t *
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,
393                      svn_wc__db_t *db,
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)
405 {
406   svn_error_t *err;
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)
413     : NULL;
414   const char *old_repos_relpath_part
415     = old_repos_relpath && old_version
416     ? svn_relpath_skip_ancestor(old_version->path_in_repos,
417                                 old_repos_relpath)
418     : NULL;
419   const char *new_repos_relpath
420     = old_repos_relpath_part
421     ? svn_relpath_join(new_version->path_in_repos, old_repos_relpath_part,
422                        scratch_pool)
423     : NULL;
424
425   if (!new_repos_relpath)
426     {
427       const char *child_relpath = svn_relpath_skip_ancestor(
428                                             dst_op_root_relpath,
429                                             local_relpath);
430       SVN_ERR_ASSERT(child_relpath != NULL);
431       new_repos_relpath = svn_relpath_join(new_version->path_in_repos,
432                                            child_relpath, scratch_pool);
433     }
434
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);
440   else if (err)
441     {
442       svn_error_clear(err);
443       conflict = NULL;
444     }
445
446   if (conflict)
447     {
448       svn_wc_operation_t conflict_operation;
449       svn_boolean_t tree_conflicted;
450
451       SVN_ERR(svn_wc__conflict_read_info(&conflict_operation, NULL, NULL, NULL,
452                                          &tree_conflicted,
453                                          db, wcroot->abspath, conflict,
454                                          scratch_pool, scratch_pool));
455
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,
461                                                         scratch_pool));
462
463       if (tree_conflicted)
464         {
465           svn_wc_conflict_reason_t existing_reason;
466           svn_wc_conflict_action_t existing_action;
467           const char *existing_abspath;
468
469           SVN_ERR(svn_wc__conflict_read_tree_conflict(&existing_reason,
470                                                       &existing_action,
471                                                       &existing_abspath,
472                                                       db, wcroot->abspath,
473                                                       conflict,
474                                                       scratch_pool,
475                                                       scratch_pool));
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,
481                                                      existing_abspath))))
482             return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
483                                      _("'%s' already in conflict"),
484                                      path_for_error_message(wcroot,
485                                                             local_relpath,
486                                                             scratch_pool));
487
488           /* Already a suitable tree-conflict. */
489           *conflict_p = conflict;
490           return SVN_NO_ERROR;
491         }
492     }
493   else
494     conflict = svn_wc__conflict_skel_create(result_pool);
495
496   SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
497                      conflict, db,
498                      svn_dirent_join(wcroot->abspath, local_relpath,
499                                      scratch_pool),
500                      reason,
501                      action,
502                      move_src_op_root_abspath,
503                      result_pool,
504                      scratch_pool));
505
506   if (old_version)
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);
512   else
513     conflict_old_version = NULL;
514
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);
519
520   if (operation == svn_wc_operation_update)
521     {
522       SVN_ERR(svn_wc__conflict_skel_set_op_update(
523                 conflict, conflict_old_version, conflict_new_version,
524                 result_pool, scratch_pool));
525     }
526   else
527     {
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));
532     }
533
534   *conflict_p = conflict;
535   return SVN_NO_ERROR;
536 }
537
538 static svn_error_t *
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)
549 {
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,
553                                                     umb->dst_op_depth,
554                                                     scratch_pool);
555
556   dst_repos_relpath =
557             svn_relpath_join(nmb->umb->old_version->path_in_repos,
558                              svn_relpath_skip_ancestor(dst_root_relpath,
559                                                        nmb->dst_relpath),
560                              scratch_pool);
561
562   return svn_error_trace(
563             create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath,
564                                  svn_relpath_prefix(dst_local_relpath,
565                                                     umb->dst_op_depth,
566                                                     scratch_pool),
567                                  umb->db,
568                                  umb->old_version, umb->new_version,
569                                  umb->operation, old_kind, new_kind,
570                                  dst_repos_relpath,
571                                  reason, action, move_src_op_root_relpath,
572                                  result_pool, scratch_pool));
573 }
574
575 /* Checks if a specific local path is shadowed as seen from the move root.
576    Helper for update_moved_away_node() */
577 static svn_error_t *
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)
583 {
584   svn_sqlite__stmt_t *stmt;
585   svn_boolean_t have_row;
586
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));
590
591   SVN_ERR(svn_sqlite__step(&have_row, stmt));
592
593   if (have_row)
594     {
595       int op_depth = svn_sqlite__column_int(stmt, 0);
596
597       *shadowed = (op_depth > move_root_dst_op_depth);
598     }
599   else
600     *shadowed = FALSE;
601   SVN_ERR(svn_sqlite__reset(stmt));
602
603   return SVN_NO_ERROR;
604 }
605
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. */
609 static svn_error_t *
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)
615 {
616   update_move_baton_t *b = nmb->umb;
617   const char *move_dst_relpath;
618   svn_skel_t *conflict;
619
620   SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed);
621
622   nmb->skip = TRUE;
623
624   if (old_kind == svn_node_none)
625     move_dst_relpath = NULL;
626   else
627     SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL,
628                                               b->wcroot, nmb->dst_relpath,
629                                               b->dst_op_depth,
630                                               scratch_pool, scratch_pool));
631
632   SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath,
633                                     old_kind, new_kind,
634                                     (move_dst_relpath
635                                      ? svn_wc_conflict_reason_moved_away
636                                      : svn_wc_conflict_reason_deleted),
637                                     action, move_dst_relpath
638                                               ? nmb->dst_relpath
639                                               : NULL,
640                                     scratch_pool, scratch_pool));
641
642   SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db,
643                                svn_wc_notify_tree_conflict,
644                                new_kind,
645                                svn_wc_notify_state_inapplicable,
646                                svn_wc_notify_state_inapplicable,
647                                conflict, NULL, scratch_pool));
648
649   return SVN_NO_ERROR;
650 }
651
652 static svn_error_t *
653 mark_node_edited(node_move_baton_t *nmb,
654                  apr_pool_t *scratch_pool)
655 {
656   if (nmb->edited)
657     return SVN_NO_ERROR;
658
659   if (nmb->pb)
660     {
661       SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
662
663       if (nmb->pb->skip)
664         nmb->skip = TRUE;
665     }
666
667   nmb->edited = TRUE;
668
669   if (nmb->skip)
670     return SVN_NO_ERROR;
671
672   if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed))
673     {
674       svn_node_kind_t dst_kind, src_kind;
675
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));
682
683       SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, NULL, NULL,
684                                         NULL, 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));
689
690       SVN_ERR(mark_tc_on_op_root(nmb,
691                                  dst_kind, src_kind,
692                                  svn_wc_conflict_action_edit,
693                                  scratch_pool));
694     }
695
696   return SVN_NO_ERROR;
697 }
698
699 static svn_error_t *
700 mark_parent_edited(node_move_baton_t *nmb,
701                  apr_pool_t *scratch_pool)
702 {
703   SVN_ERR_ASSERT(nmb && nmb->pb);
704
705   SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
706
707   if (nmb->pb->skip)
708     nmb->skip = TRUE;
709
710   return SVN_NO_ERROR;
711 }
712
713 static svn_error_t *
714 tc_editor_add_directory(node_move_baton_t *nmb,
715                         const char *relpath,
716                         svn_node_kind_t old_kind,
717                         apr_hash_t *props,
718                         apr_pool_t *scratch_pool)
719 {
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;
726
727   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
728   if (nmb->skip)
729     return SVN_NO_ERROR;
730
731   if (nmb->shadowed)
732     {
733       svn_wc__db_status_t status;
734
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,
739                                             NULL, NULL, NULL,
740                                             b->wcroot, relpath,
741                                             scratch_pool, scratch_pool));
742
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;
749       else
750         reason = svn_wc_conflict_reason_replaced;
751     }
752   else
753     wc_kind = svn_node_none;
754
755   local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
756
757   if (wc_kind == svn_node_none)
758     {
759       /* Check for unversioned tree-conflict */
760       SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
761     }
762
763   if (!nmb->shadowed && wc_kind == old_kind)
764     wc_kind = svn_node_none; /* Node will be gone once we install */
765
766   if (wc_kind != svn_node_none
767       && (nmb->shadowed || wc_kind != old_kind)) /* replace */
768     {
769       SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
770                                         old_kind, svn_node_dir,
771                                         reason,
772                                         (old_kind == svn_node_none)
773                                           ? svn_wc_conflict_action_add
774                                           : svn_wc_conflict_action_replace,
775                                         NULL,
776                                         scratch_pool, scratch_pool));
777       nmb->skip = TRUE;
778     }
779   else
780     {
781       SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath,
782                                            scratch_pool, scratch_pool));
783     }
784
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,
789                                svn_node_dir,
790                                svn_wc_notify_state_inapplicable,
791                                svn_wc_notify_state_inapplicable,
792                                conflict, work_item, scratch_pool));
793   return SVN_NO_ERROR;
794 }
795
796 static svn_error_t *
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)
801 {
802   svn_sqlite__stmt_t *stmt;
803   svn_boolean_t have_row;
804   const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
805                                                        scratch_pool);
806
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));
815
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));
821   if (have_row)
822     {
823       apr_size_t props_size;
824       const char *properties;
825
826       properties = svn_sqlite__column_blob(stmt, 1, &props_size,
827                                            scratch_pool);
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,
834                                                     scratch_pool),
835                                 properties, props_size, NULL));
836       SVN_ERR(svn_sqlite__step(&have_row, stmt));
837     }
838   SVN_ERR(svn_sqlite__reset(stmt));
839
840   return SVN_NO_ERROR;
841 }
842
843 static svn_error_t *
844 tc_editor_incoming_add_directory(node_move_baton_t *nmb,
845                                  const char *dst_relpath,
846                                  svn_node_kind_t old_kind,
847                                  apr_hash_t *props,
848                                  const char *src_relpath,
849                                  apr_pool_t *scratch_pool)
850 {
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;
857
858   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
859   if (nmb->skip)
860     return SVN_NO_ERROR;
861
862   dst_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool);
863
864   /* Check for unversioned tree-conflict */
865   SVN_ERR(svn_io_check_path(dst_abspath, &wc_kind, scratch_pool));
866
867   if (wc_kind == old_kind)
868     wc_kind = svn_node_none; /* Node will be gone once we install */
869
870   if (wc_kind != svn_node_none && wc_kind != old_kind) /* replace */
871     {
872       SVN_ERR(create_node_tree_conflict(&conflict, nmb, dst_relpath,
873                                         old_kind, svn_node_dir,
874                                         reason,
875                                         (old_kind == svn_node_none)
876                                           ? svn_wc_conflict_action_add
877                                           : svn_wc_conflict_action_replace,
878                                         NULL,
879                                         scratch_pool, scratch_pool));
880       nmb->skip = TRUE;
881     }
882   else
883     {
884       SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
885                                 scratch_pool));
886       SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, dst_abspath,
887                                            scratch_pool, scratch_pool));
888     }
889
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,
894                                svn_node_dir,
895                                svn_wc_notify_state_inapplicable,
896                                svn_wc_notify_state_inapplicable,
897                                conflict, work_item, scratch_pool));
898   return SVN_NO_ERROR;
899 }
900
901 static svn_error_t *
902 tc_editor_add_file(node_move_baton_t *nmb,
903                    const char *relpath,
904                    svn_node_kind_t old_kind,
905                    const svn_checksum_t *checksum,
906                    apr_hash_t *props,
907                    apr_pool_t *scratch_pool)
908 {
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;
915
916   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
917   if (nmb->skip)
918     return SVN_NO_ERROR;
919
920   if (nmb->shadowed)
921     {
922       svn_wc__db_status_t status;
923
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,
928                                             NULL, NULL, NULL,
929                                             b->wcroot, relpath,
930                                             scratch_pool, scratch_pool));
931
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;
938       else
939         reason = svn_wc_conflict_reason_replaced;
940     }
941   else
942     wc_kind = svn_node_none;
943
944   local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
945
946   if (wc_kind == svn_node_none)
947     {
948       /* Check for unversioned tree-conflict */
949       SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
950     }
951
952   if (wc_kind != svn_node_none
953       && (nmb->shadowed || wc_kind != old_kind)) /* replace */
954     {
955       SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
956                                         old_kind, svn_node_file,
957                                         reason,
958                                         (old_kind == svn_node_none)
959                                           ? svn_wc_conflict_action_add
960                                           : svn_wc_conflict_action_replace,
961                                         NULL,
962                                         scratch_pool, scratch_pool));
963       nmb->skip = TRUE;
964     }
965   else
966     {
967       /* Update working file. */
968       SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
969                                             svn_dirent_join(b->wcroot->abspath,
970                                                             relpath,
971                                                             scratch_pool),
972                                             NULL,
973                                             FALSE /*FIXME: use_commit_times?*/,
974                                             TRUE  /* record_file_info */,
975                                             scratch_pool, scratch_pool));
976     }
977
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,
982                                svn_node_file,
983                                svn_wc_notify_state_inapplicable,
984                                svn_wc_notify_state_inapplicable,
985                                conflict, work_item, scratch_pool));
986   return SVN_NO_ERROR;
987 }
988
989 static svn_error_t *
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,
994                             apr_hash_t *props,
995                             const char *src_relpath,
996                             const char *content_abspath,
997                             apr_pool_t *scratch_pool)
998 {
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;
1006
1007   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1008   if (nmb->skip)
1009     {
1010       SVN_ERR(svn_io_remove_file2(content_abspath, TRUE, scratch_pool));
1011       return SVN_NO_ERROR;
1012     }
1013
1014   dst_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool);
1015
1016   /* Check for unversioned tree-conflict */
1017   SVN_ERR(svn_io_check_path(dst_abspath, &wc_kind, scratch_pool));
1018
1019   if (wc_kind != svn_node_none && wc_kind != old_kind) /* replace */
1020     {
1021       SVN_ERR(create_node_tree_conflict(&conflict, nmb, dst_relpath,
1022                                         old_kind, svn_node_file,
1023                                         reason,
1024                                         (old_kind == svn_node_none)
1025                                           ? svn_wc_conflict_action_add
1026                                           : svn_wc_conflict_action_replace,
1027                                         NULL,
1028                                         scratch_pool, scratch_pool));
1029       nmb->skip = TRUE;
1030       SVN_ERR(svn_io_remove_file2(content_abspath, TRUE, scratch_pool));
1031     }
1032   else
1033     {
1034       const char *src_abspath;
1035
1036       SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
1037                                 scratch_pool));
1038
1039       /* Update working file. */
1040       src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1041                                     scratch_pool);
1042       SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, dst_abspath,
1043                                             src_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);
1048
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));
1053     
1054       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1055     }
1056
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,
1061                                svn_node_file,
1062                                svn_wc_notify_state_inapplicable,
1063                                svn_wc_notify_state_inapplicable,
1064                                conflict, work_items, scratch_pool));
1065   return SVN_NO_ERROR;
1066 }
1067
1068 /* All the info we need about one version of a working node. */
1069 typedef struct working_node_version_t
1070 {
1071   svn_wc_conflict_version_t *location_and_kind;
1072   apr_hash_t *props;
1073   const svn_checksum_t *checksum; /* for files only */
1074 } working_node_version_t;
1075
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,
1080                         svn_wc__db_t *db,
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)
1090 {
1091   svn_wc_conflict_version_t *original_version;
1092   svn_wc_conflict_version_t *conflicted_version;
1093   const char *part;
1094
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;
1101
1102   part = svn_relpath_skip_ancestor(original_version->path_in_repos,
1103                                    repos_relpath);
1104   if (part == NULL)
1105     part = svn_relpath_skip_ancestor(conflicted_version->path_in_repos,
1106                                      repos_relpath);
1107   SVN_ERR_ASSERT(part != NULL);
1108
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;
1112
1113   if (set_operation)
1114     {
1115       if (operation == svn_wc_operation_update)
1116         {
1117           SVN_ERR(svn_wc__conflict_skel_set_op_update(
1118                     conflict_skel, original_version,
1119                     conflicted_version,
1120                     scratch_pool, scratch_pool));
1121         }
1122       else if (operation == svn_wc_operation_merge)
1123         {
1124           SVN_ERR(svn_wc__conflict_skel_set_op_merge(
1125                     conflict_skel, original_version,
1126                     conflicted_version,
1127                     scratch_pool, scratch_pool));
1128         }
1129       else
1130         {
1131           SVN_ERR(svn_wc__conflict_skel_set_op_switch(
1132                     conflict_skel, original_version,
1133                     conflicted_version,
1134                     scratch_pool, scratch_pool));
1135         }
1136     }
1137
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,
1141                                           local_abspath,
1142                                           conflict_skel,
1143                                           result_pool,
1144                                           scratch_pool));
1145
1146   return SVN_NO_ERROR;
1147 }
1148
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)
1160 {
1161   apr_hash_t *new_actual_props;
1162   apr_array_header_t *new_propchanges;
1163
1164   /*
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.
1169    */
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,
1174                          result_pool));
1175   SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state,
1176                               &new_actual_props,
1177                               b->db, svn_dirent_join(b->wcroot->abspath,
1178                                                      local_relpath,
1179                                                      scratch_pool),
1180                               old_version->props, old_version->props,
1181                               *actual_props, *propchanges,
1182                               result_pool, scratch_pool));
1183
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 .
1186
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,
1190                          scratch_pool));
1191   if (!new_propchanges->nelts)
1192     new_actual_props = NULL;
1193
1194   /* Install the new actual props. */
1195   SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath,
1196                                            new_actual_props,
1197                                            svn_wc__has_magic_property(
1198                                                     *propchanges),
1199                                            scratch_pool));
1200
1201   return SVN_NO_ERROR;
1202 }
1203
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)
1210 {
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,
1216                                               scratch_pool);
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;
1222
1223   SVN_ERR(mark_node_edited(nmb, scratch_pool));
1224   if (nmb->skip)
1225     return SVN_NO_ERROR;
1226
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)
1229     {
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,
1234                                         NULL,
1235                                         scratch_pool, scratch_pool));
1236       obstructed = TRUE;
1237     }
1238
1239   old_version.location_and_kind = b->old_version;
1240   new_version.location_and_kind = b->new_version;
1241
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;
1246
1247   SVN_ERR(update_working_props(&prop_state, &conflict_skel,
1248                                 &propchanges, &actual_props,
1249                                 b, dst_relpath,
1250                                 &old_version, &new_version,
1251                                 scratch_pool, scratch_pool));
1252
1253   if (prop_state == svn_wc_notify_state_conflicted)
1254     {
1255       const char *move_dst_repos_relpath;
1256
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,
1260                                         NULL,
1261                                         b->wcroot, dst_relpath,
1262                                         b->dst_op_depth,
1263                                         scratch_pool, scratch_pool));
1264
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));
1271     }
1272
1273   SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1274                                svn_wc_notify_update_update,
1275                                svn_node_dir,
1276                                svn_wc_notify_state_inapplicable,
1277                                prop_state,
1278                                conflict_skel, work_items, scratch_pool));
1279
1280   return SVN_NO_ERROR;
1281 }
1282
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.
1285  *
1286  * Merge the difference between OLD_VERSION and NEW_VERSION into
1287  * the working file at LOCAL_RELPATH.
1288  *
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.
1293  *
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.
1296  *
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)
1308 {
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,
1312                                               dst_relpath,
1313                                               scratch_pool);
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;
1324
1325   SVN_ERR(mark_node_edited(nmb, scratch_pool));
1326   if (nmb->skip)
1327     return SVN_NO_ERROR;
1328
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)
1331     {
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,
1336                                         NULL,
1337                                         scratch_pool, scratch_pool));
1338       obstructed = TRUE;
1339     }
1340
1341   old_version.location_and_kind = b->old_version;
1342   new_version.location_and_kind = b->new_version;
1343
1344   old_version.checksum = old_checksum;
1345   old_version.props = old_props;
1346   new_version.checksum = new_checksum;
1347   new_version.props = new_props;
1348
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));
1354
1355   if (!obstructed
1356       && !svn_checksum_match(new_version.checksum, old_version.checksum))
1357     {
1358       svn_boolean_t is_locally_modified;
1359
1360       SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
1361                                                b->db, local_abspath,
1362                                                FALSE /* exact_comparison */,
1363                                                scratch_pool));
1364       if (!is_locally_modified)
1365         {
1366           SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1367                                                 local_abspath,
1368                                                 NULL,
1369                                                 FALSE /* FIXME: use_commit_times? */,
1370                                                 TRUE  /* record_file_info */,
1371                                                 scratch_pool, scratch_pool));
1372
1373           work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1374
1375           content_state = svn_wc_notify_state_changed;
1376         }
1377       else
1378         {
1379           /*
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.
1384            */
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,
1397                                          local_abspath,
1398                                          local_abspath,
1399                                          NULL, NULL, NULL, /* diff labels */
1400                                          actual_props,
1401                                          FALSE, /* dry-run */
1402                                          NULL, /* diff3-cmd */
1403                                          NULL, /* merge options */
1404                                          propchanges,
1405                                          b->cancel_func, b->cancel_baton,
1406                                          scratch_pool, scratch_pool));
1407
1408           work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1409
1410           if (merge_outcome == svn_wc_merge_conflict)
1411             content_state = svn_wc_notify_state_conflicted;
1412           else
1413             content_state = svn_wc_notify_state_merged;
1414         }
1415     }
1416   else
1417     content_state = svn_wc_notify_state_unchanged;
1418
1419   /* If there are any conflicts to be stored, convert them into work items
1420    * too. */
1421   if (conflict_skel)
1422     {
1423       const char *move_dst_repos_relpath;
1424
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,
1428                                         NULL,
1429                                         b->wcroot, dst_relpath,
1430                                         b->dst_op_depth,
1431                                         scratch_pool, scratch_pool));
1432
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));
1438
1439       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1440     }
1441
1442   SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1443                                svn_wc_notify_update_update,
1444                                svn_node_file,
1445                                content_state,
1446                                prop_state,
1447                                conflict_skel, work_items, scratch_pool));
1448
1449   return SVN_NO_ERROR;
1450 }
1451
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)
1462 {
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,
1466                                             dst_relpath,
1467                                             scratch_pool);
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;
1477   svn_error_t *err;
1478
1479   SVN_ERR(mark_node_edited(nmb, scratch_pool));
1480   if (nmb->skip)
1481     return SVN_NO_ERROR;
1482
1483   err = svn_wc__db_base_get_info_internal(NULL, &dst_db_kind, NULL,
1484                                           &dst_repos_relpath,
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)
1490     {
1491       const char *dst_parent_relpath;
1492       const char *dst_parent_repos_relpath;
1493       const char *src_abspath;
1494
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. */
1501
1502       svn_error_clear(err);
1503
1504       /* Create a WORKING node for this file at the move destination. */
1505       SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
1506                                 scratch_pool));
1507
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,
1520                                                 NULL, b->wcroot,
1521                                                 dst_parent_relpath,
1522                                                 scratch_pool, scratch_pool));
1523       dst_repos_relpath = svn_relpath_join(dst_parent_repos_relpath,
1524                                            svn_relpath_basename(dst_relpath,
1525                                                                 scratch_pool),
1526                                            scratch_pool);
1527       tree_conflict = TRUE;
1528
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,
1531                                     scratch_pool);
1532       SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1533                                             dst_abspath,
1534                                             src_abspath,
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);
1539     }
1540   else
1541     SVN_ERR(err);
1542
1543   if ((dst_db_kind == svn_node_none || dst_db_kind != svn_node_file) &&
1544       conflict_skel == NULL)
1545     {
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,
1552                                         NULL,
1553                                         scratch_pool, scratch_pool));
1554       tree_conflict = TRUE;
1555     }
1556
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)
1560     {
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,
1567                                         NULL,
1568                                         scratch_pool, scratch_pool));
1569       tree_conflict = TRUE;
1570     }
1571
1572   old_version.location_and_kind = b->old_version;
1573   new_version.location_and_kind = b->new_version;
1574
1575   old_version.checksum = src_checksum;
1576   old_version.props = src_props;
1577   new_version.checksum = dst_checksum;
1578   new_version.props = dst_props;
1579
1580   /* Merge properties and text content if there is no tree conflict. */
1581   if (conflict_skel == NULL)
1582     {
1583       apr_hash_t *actual_props;
1584       apr_array_header_t *propchanges;
1585
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));
1590       if (do_text_merge)
1591         {
1592           const char *old_pristine_abspath;
1593           const char *src_abspath;
1594           const char *label_left;
1595           const char *label_target;
1596
1597           /*
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.
1603            */
1604           SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath,
1605                                                b->db, b->wcroot->abspath,
1606                                                src_checksum,
1607                                                scratch_pool, scratch_pool));
1608           src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1609                                         scratch_pool);
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,
1617                                          src_abspath,
1618                                          dst_abspath,
1619                                          dst_abspath,
1620                                          label_left,
1621                                          _(".working"),
1622                                          label_target,
1623                                          actual_props,
1624                                          FALSE, /* dry-run */
1625                                          NULL, /* diff3-cmd */
1626                                          NULL, /* merge options */
1627                                          propchanges,
1628                                          b->cancel_func, b->cancel_baton,
1629                                          scratch_pool, scratch_pool));
1630
1631           work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1632
1633           if (merge_outcome == svn_wc_merge_conflict)
1634             content_state = svn_wc_notify_state_conflicted;
1635           else
1636             content_state = svn_wc_notify_state_merged;
1637         }
1638     }
1639
1640   /* If there are any conflicts to be stored, convert them into work items
1641    * too. */
1642   if (conflict_skel)
1643     {
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));
1649
1650       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1651     }
1652
1653   SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1654                                svn_wc_notify_update_update,
1655                                svn_node_file,
1656                                content_state,
1657                                prop_state,
1658                                conflict_skel, work_items, scratch_pool));
1659
1660   return SVN_NO_ERROR;
1661 }
1662
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)
1669 {
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;
1676
1677   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1678   if (nmb->skip)
1679     return SVN_NO_ERROR;
1680
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.*/
1684   if (nmb->shadowed)
1685     {
1686       SVN_ERR(mark_tc_on_op_root(nmb,
1687                                  old_kind, new_kind,
1688                                  svn_wc_conflict_action_delete,
1689                                  scratch_pool));
1690       return SVN_NO_ERROR;
1691     }
1692
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));
1697   if (is_modified)
1698     {
1699       svn_wc_conflict_reason_t reason;
1700
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.
1703
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)
1707        */
1708
1709       SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1710                                                NULL, NULL, scratch_pool));
1711
1712       reason = svn_wc_conflict_reason_edited;
1713
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,
1719                                         NULL,
1720                                         scratch_pool, scratch_pool));
1721       nmb->skip = TRUE;
1722     }
1723   else
1724     {
1725       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1726       const char *del_abspath;
1727       svn_boolean_t have_row;
1728
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,
1734                                 b->dst_op_depth));
1735       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1736       while (have_row)
1737         {
1738           svn_error_t *err;
1739           svn_skel_t *work_item;
1740           svn_node_kind_t del_kind;
1741
1742           svn_pool_clear(iterpool);
1743
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),
1747                                         iterpool);
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);
1753           else
1754             err = svn_wc__wq_build_file_remove(&work_item, b->db,
1755                                                b->wcroot->abspath, del_abspath,
1756                                                iterpool, iterpool);
1757           if (!err)
1758             err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool);
1759           if (err)
1760             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1761
1762           SVN_ERR(svn_sqlite__step(&have_row, stmt));
1763         }
1764       SVN_ERR(svn_sqlite__reset(stmt));
1765
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));
1771       else
1772         SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db,
1773                                              b->wcroot->abspath, local_abspath,
1774                                              scratch_pool, iterpool));
1775
1776       svn_pool_destroy(iterpool);
1777     }
1778
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,
1783                                  new_kind,
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,
1789                                        scratch_pool));
1790
1791   return SVN_NO_ERROR;
1792 }
1793
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)
1801 {
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;
1808
1809   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1810   if (nmb->skip)
1811     return SVN_NO_ERROR;
1812
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.*/
1816   if (nmb->shadowed)
1817     {
1818       SVN_ERR(mark_tc_on_op_root(nmb,
1819                                  old_kind, new_kind,
1820                                  svn_wc_conflict_action_delete,
1821                                  scratch_pool));
1822       return SVN_NO_ERROR;
1823     }
1824
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));
1829   if (is_modified)
1830     {
1831       svn_wc_conflict_reason_t reason;
1832
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.
1835
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)
1839        */
1840
1841       SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1842                                                NULL, NULL, scratch_pool));
1843
1844       reason = svn_wc_conflict_reason_edited;
1845
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,
1851                                         NULL,
1852                                         scratch_pool, scratch_pool));
1853       nmb->skip = TRUE;
1854     }
1855   else
1856     {
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));
1864     }
1865
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,
1870                                  new_kind,
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,
1876                                        scratch_pool));
1877
1878   return SVN_NO_ERROR;
1879 }
1880
1881 /*
1882  * Driver code.
1883  *
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.
1888  *
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).
1892  *
1893  * We currently assume that both the pre-update and post-update states are
1894  * single-revision.
1895  */
1896
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,
1908          int op_depth,
1909          svn_wc__db_wcroot_t *wcroot,
1910          apr_pool_t *result_pool,
1911          apr_pool_t *scratch_pool)
1912 {
1913   svn_wc__db_status_t status;
1914   const char *repos_relpath;
1915   svn_node_kind_t db_kind;
1916   svn_error_t *err;
1917
1918   err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL,
1919                                   NULL, NULL, NULL, NULL, checksum, NULL,
1920                                   NULL, props,
1921                                   wcroot, local_relpath, op_depth,
1922                                   result_pool, scratch_pool);
1923
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))
1929     {
1930       svn_error_clear(err);
1931
1932       if (kind)
1933         *kind = svn_node_none;
1934       if (checksum)
1935         *checksum = NULL;
1936       if (props)
1937         *props = NULL;
1938       if (children)
1939         *children = apr_array_make(result_pool, 0, sizeof(const char *));
1940
1941       return SVN_NO_ERROR;
1942     }
1943   else
1944     SVN_ERR(err);
1945
1946   if (kind)
1947     *kind = db_kind;
1948
1949   if (children && db_kind == svn_node_dir)
1950     {
1951       svn_sqlite__stmt_t *stmt;
1952       svn_boolean_t have_row;
1953
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,
1958                                 op_depth));
1959       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1960       while (have_row)
1961         {
1962           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1963
1964           APR_ARRAY_PUSH(*children, const char *)
1965               = svn_relpath_basename(child_relpath, result_pool);
1966
1967           SVN_ERR(svn_sqlite__step(&have_row, stmt));
1968         }
1969       SVN_ERR(svn_sqlite__reset(stmt));
1970     }
1971   else if (children)
1972     *children = apr_array_make(result_pool, 0, sizeof(const char *));
1973
1974   return SVN_NO_ERROR;
1975 }
1976
1977 /* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties,
1978    FALSE otherwise. SRC_PROPS and DST_PROPS are standard property
1979    hashes. */
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)
1985 {
1986   if (!src_props && !dst_props)
1987     *match = TRUE;
1988   else if (!src_props || ! dst_props)
1989     *match = FALSE;
1990   else
1991     {
1992       apr_array_header_t *propdiffs;
1993
1994       SVN_ERR(svn_prop_diffs(&propdiffs, src_props, dst_props, scratch_pool));
1995       *match = propdiffs->nelts ? FALSE : TRUE;
1996     }
1997   return SVN_NO_ERROR;
1998 }
1999
2000 /* ### Drive TC_EDITOR so as to ...
2001  */
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)
2008 {
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;
2014
2015   if (b->cancel_func)
2016     SVN_ERR(b->cancel_func(b->cancel_baton));
2017
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));
2021
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));
2025
2026   if (src_kind == svn_node_none
2027       || (dst_kind != svn_node_none && src_kind != dst_kind))
2028     {
2029       SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind,
2030                                scratch_pool));
2031     }
2032
2033   if (nmb->skip)
2034     return SVN_NO_ERROR;
2035
2036   if (src_kind != svn_node_none && src_kind != dst_kind)
2037     {
2038       if (src_kind == svn_node_file || src_kind == svn_node_symlink)
2039         {
2040           SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind,
2041                                      src_checksum, src_props, scratch_pool));
2042         }
2043       else if (src_kind == svn_node_dir)
2044         {
2045           SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind,
2046                                           src_props, scratch_pool));
2047         }
2048     }
2049   else if (src_kind != svn_node_none)
2050     {
2051       svn_boolean_t props_equal;
2052
2053       SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool));
2054
2055       if (src_kind == svn_node_file || src_kind == svn_node_symlink)
2056         {
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));
2061         }
2062       else if (src_kind == svn_node_dir)
2063         {
2064           if (!props_equal)
2065             SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
2066                                               dst_props, src_props,
2067                                               scratch_pool));
2068         }
2069     }
2070
2071   if (nmb->skip)
2072     return SVN_NO_ERROR;
2073
2074   if (src_kind == svn_node_dir)
2075     {
2076       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2077       int i = 0, j = 0;
2078
2079       while (i < src_children->nelts || j < dst_children->nelts)
2080         {
2081           const char *child_name;
2082           svn_boolean_t src_only = FALSE, dst_only = FALSE;
2083           node_move_baton_t cnmb = { 0 };
2084
2085           cnmb.pb = nmb;
2086           cnmb.umb = nmb->umb;
2087           cnmb.shadowed = nmb->shadowed;
2088
2089           svn_pool_clear(iterpool);
2090           if (i >= src_children->nelts)
2091             {
2092               dst_only = TRUE;
2093               child_name = APR_ARRAY_IDX(dst_children, j, const char *);
2094             }
2095           else if (j >= dst_children->nelts)
2096             {
2097               src_only = TRUE;
2098               child_name = APR_ARRAY_IDX(src_children, i, const char *);
2099             }
2100           else
2101             {
2102               const char *src_name = APR_ARRAY_IDX(src_children, i,
2103                                                    const char *);
2104               const char *dst_name = APR_ARRAY_IDX(dst_children, j,
2105                                                    const char *);
2106               int cmp = strcmp(src_name, dst_name);
2107
2108               if (cmp > 0)
2109                 dst_only = TRUE;
2110               else if (cmp < 0)
2111                 src_only = TRUE;
2112
2113               child_name = dst_only ? dst_name : src_name;
2114             }
2115
2116           cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
2117                                               iterpool);
2118           cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
2119                                               iterpool);
2120
2121           if (!cnmb.shadowed)
2122             SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot,
2123                                         cnmb.dst_relpath, b->dst_op_depth,
2124                                         iterpool));
2125
2126           SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath,
2127                                          cnmb.dst_relpath, iterpool));
2128
2129           if (!dst_only)
2130             ++i;
2131           if (!src_only)
2132             ++j;
2133
2134           if (nmb->skip) /* Does parent now want a skip? */
2135             break;
2136         }
2137     }
2138
2139   return SVN_NO_ERROR;
2140 }
2141
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)
2146 {
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);
2152
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));
2157   if (!have_row)
2158     return svn_error_trace(svn_sqlite__reset(stmt));
2159
2160   revision = svn_sqlite__column_revnum(stmt, 4);
2161   repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
2162
2163   SVN_ERR(svn_sqlite__reset(stmt));
2164
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));
2169   while (have_row)
2170     {
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;
2174
2175       svn_pool_clear(iterpool);
2176
2177       relpath = svn_relpath_skip_ancestor(local_relpath, child_relpath);
2178       relpath = svn_relpath_join(repos_relpath, relpath, iterpool);
2179
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,
2187                                                         scratch_pool));
2188
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,
2196                                                         scratch_pool));
2197
2198       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2199     }
2200   SVN_ERR(svn_sqlite__reset(stmt));
2201
2202   svn_pool_destroy(iterpool);
2203
2204   return SVN_NO_ERROR;
2205 }
2206
2207 /* The body of svn_wc__db_update_moved_away_conflict_victim(), which see.
2208  */
2209 static svn_error_t *
2210 update_moved_away_conflict_victim(svn_revnum_t *old_rev,
2211                                   svn_revnum_t *new_rev,
2212                                   svn_wc__db_t *db,
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,
2220                                   void *cancel_baton,
2221                                   apr_pool_t *scratch_pool)
2222 {
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 };
2229
2230   SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath));
2231
2232   /* Construct editor baton. */
2233
2234   SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot,
2235                             local_relpath, relpath_depth(delete_relpath),
2236                             scratch_pool));
2237
2238   SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL,
2239                                             wcroot, local_relpath,
2240                                             umb.src_op_depth,
2241                                             scratch_pool, scratch_pool));
2242
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,
2247                                                     scratch_pool));
2248
2249   umb.dst_op_depth = relpath_depth(dst_relpath);
2250
2251   SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
2252   SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
2253
2254
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,
2259                                     NULL,
2260                                     wcroot, src_relpath, umb.src_op_depth,
2261                                     scratch_pool, scratch_pool));
2262
2263   SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
2264                                       &new_version.repos_uuid,
2265                                       wcroot, repos_id,
2266                                       scratch_pool));
2267
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,
2272                                     NULL,
2273                                     wcroot, dst_relpath, umb.dst_op_depth,
2274                                     scratch_pool, scratch_pool));
2275
2276   SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
2277                                       &old_version.repos_uuid,
2278                                       wcroot, repos_id,
2279                                       scratch_pool));
2280   *old_rev = old_version.peg_rev;
2281   *new_rev = new_version.peg_rev;
2282
2283   umb.operation = operation;
2284   umb.old_version= &old_version;
2285   umb.new_version= &new_version;
2286   umb.db = db;
2287   umb.wcroot = wcroot;
2288   umb.cancel_func = cancel_func;
2289   umb.cancel_baton = cancel_baton;
2290
2291   if (umb.src_op_depth == 0)
2292     SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool));
2293
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));
2297
2298   /* Drive the editor... */
2299
2300   nmb.umb = &umb;
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; */
2306
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,
2311                                  scratch_pool));
2312
2313   SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath,
2314                                             umb.src_op_depth,
2315                                             dst_relpath, NULL, NULL,
2316                                             scratch_pool));
2317
2318   return SVN_NO_ERROR;
2319 }
2320
2321
2322 svn_error_t *
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,
2330                                              void *cancel_baton,
2331                                              svn_wc_notify_func2_t notify_func,
2332                                              void *notify_baton,
2333                                              apr_pool_t *scratch_pool)
2334 {
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;
2339
2340   /* ### Check for mixed-rev src or dst? */
2341
2342   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2343                                                 db, local_abspath,
2344                                                 scratch_pool, scratch_pool));
2345   VERIFY_USABLE_WCROOT(wcroot);
2346
2347   delete_relpath
2348     = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath);
2349
2350   SVN_WC__DB_WITH_TXN(
2351     update_moved_away_conflict_victim(
2352       &old_rev, &new_rev,
2353       db, wcroot, local_relpath, delete_relpath,
2354       operation, action, reason,
2355       cancel_func, cancel_baton,
2356       scratch_pool),
2357     wcroot);
2358
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,
2362                                              scratch_pool));
2363   if (notify_func)
2364     {
2365       svn_wc_notify_t *notify;
2366
2367       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2368                                                     local_relpath,
2369                                                     scratch_pool),
2370                                     svn_wc_notify_update_completed,
2371                                     scratch_pool);
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);
2377     }
2378
2379
2380   return SVN_NO_ERROR;
2381 }
2382
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)
2392 {
2393   svn_wc__db_status_t status;
2394   const char *repos_relpath;
2395   svn_node_kind_t db_kind;
2396   svn_error_t *err;
2397
2398   err = svn_wc__db_read_info_internal(&status, &db_kind, NULL, &repos_relpath,
2399                                       NULL, NULL, NULL, NULL, NULL,
2400                                       checksum,
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);
2406
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))
2412     {
2413       svn_error_clear(err);
2414
2415       if (kind)
2416         *kind = svn_node_none;
2417       if (checksum)
2418         *checksum = NULL;
2419       if (props)
2420         *props = NULL;
2421       if (children)
2422         *children = apr_array_make(result_pool, 0, sizeof(const char *));
2423
2424       return SVN_NO_ERROR;
2425     }
2426   else
2427     SVN_ERR(err);
2428
2429   SVN_ERR(svn_wc__db_read_props_internal(props, wcroot, local_relpath,
2430                                          result_pool, scratch_pool));
2431
2432   if (kind)
2433     *kind = db_kind;
2434
2435   if (children && db_kind == svn_node_dir)
2436     {
2437       svn_sqlite__stmt_t *stmt;
2438       svn_boolean_t have_row;
2439
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));
2445       while (have_row)
2446         {
2447           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2448
2449           APR_ARRAY_PUSH(*children, const char *)
2450               = svn_relpath_basename(child_relpath, result_pool);
2451
2452           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2453         }
2454       SVN_ERR(svn_sqlite__reset(stmt));
2455     }
2456   else if (children)
2457     *children = apr_array_make(result_pool, 0, sizeof(const char *));
2458
2459   return SVN_NO_ERROR;
2460 }
2461
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)
2470 {
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;
2477
2478   if (b->cancel_func)
2479     SVN_ERR(b->cancel_func(b->cancel_baton));
2480
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,
2485                    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));
2489
2490   if (working_kind == svn_node_none
2491       || (orig_kind != svn_node_none && orig_kind != working_kind))
2492     {
2493       SVN_ERR(tc_incoming_editor_delete(nmb, dst_relpath, orig_kind,
2494                                         working_kind, scratch_pool));
2495     }
2496
2497   if (nmb->skip)
2498     return SVN_NO_ERROR;
2499
2500   if (working_kind != svn_node_none && orig_kind != working_kind)
2501     {
2502       if (working_kind == svn_node_file || working_kind == svn_node_symlink)
2503         {
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;
2509           svn_error_t *err;
2510
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,
2513                                                  b->wcroot->abspath,
2514                                                  scratch_pool,
2515                                                  scratch_pool));
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,
2525                                  scratch_pool);
2526           if (err && err->apr_err == SVN_ERR_CANCELLED)
2527             {
2528               svn_error_t *err2;
2529
2530               err2 = svn_io_remove_file2(temp_abspath, TRUE, scratch_pool);
2531               return svn_error_compose_create(err, err2);
2532             }
2533           else
2534             SVN_ERR(err);
2535
2536           SVN_ERR(tc_editor_incoming_add_file(nmb, dst_relpath, orig_kind,
2537                                               working_checksum, working_props,
2538                                               victim_relpath, temp_abspath,
2539                                               scratch_pool));
2540         }
2541       else if (working_kind == svn_node_dir)
2542         {
2543           SVN_ERR(tc_editor_incoming_add_directory(nmb, dst_relpath,
2544                                                    orig_kind, working_props,
2545                                                    victim_relpath,
2546                                                    scratch_pool));
2547         }
2548     }
2549   else if (working_kind != svn_node_none)
2550     {
2551       svn_boolean_t props_equal;
2552
2553       SVN_ERR(props_match(&props_equal, orig_props, working_props,
2554                           scratch_pool));
2555
2556       if (working_kind == svn_node_file || working_kind == svn_node_symlink)
2557         {
2558           svn_boolean_t is_modified;
2559
2560           SVN_ERR(svn_wc__internal_file_modified_p(&is_modified, b->db,
2561                                                    svn_dirent_join(
2562                                                      b->wcroot->abspath,
2563                                                      victim_relpath,
2564                                                      scratch_pool),
2565                                                    FALSE /* exact_comparison */,
2566                                                    scratch_pool));
2567           if (!props_equal || is_modified)
2568             SVN_ERR(tc_editor_update_incoming_moved_file(nmb, dst_relpath,
2569                                                          victim_relpath,
2570                                                          working_checksum,
2571                                                          orig_checksum,
2572                                                          orig_props,
2573                                                          working_props,
2574                                                          is_modified,
2575                                                          scratch_pool));
2576         }
2577       else if (working_kind == svn_node_dir)
2578         {
2579           if (!props_equal)
2580             SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
2581                                               orig_props, working_props,
2582                                               scratch_pool));
2583         }
2584     }
2585
2586   if (nmb->skip)
2587     return SVN_NO_ERROR;
2588
2589   if (working_kind == svn_node_dir)
2590     {
2591       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2592       int i = 0, j = 0;
2593
2594       while (i < orig_children->nelts || j < working_children->nelts)
2595         {
2596           const char *child_name;
2597           svn_boolean_t orig_only = FALSE, working_only = FALSE;
2598           node_move_baton_t cnmb = { 0 };
2599
2600           cnmb.pb = nmb;
2601           cnmb.umb = nmb->umb;
2602           cnmb.shadowed = nmb->shadowed;
2603
2604           svn_pool_clear(iterpool);
2605           if (i >= orig_children->nelts)
2606             {
2607               working_only = TRUE;
2608               child_name = APR_ARRAY_IDX(working_children, j, const char *);
2609             }
2610           else if (j >= working_children->nelts)
2611             {
2612               orig_only = TRUE;
2613               child_name = APR_ARRAY_IDX(orig_children, i, const char *);
2614             }
2615           else
2616             {
2617               const char *orig_name = APR_ARRAY_IDX(orig_children, i,
2618                                                     const char *);
2619               const char *working_name = APR_ARRAY_IDX(working_children, j,
2620                                                        const char *);
2621               int cmp = strcmp(orig_name, working_name);
2622
2623               if (cmp > 0)
2624                 working_only = TRUE;
2625               else if (cmp < 0)
2626                 orig_only = TRUE;
2627
2628               child_name = working_only ? working_name : orig_name;
2629             }
2630
2631           cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
2632                                               iterpool);
2633           cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
2634                                               iterpool);
2635
2636           SVN_ERR(update_incoming_moved_node(&cnmb, wcroot, cnmb.src_relpath,
2637                                              cnmb.dst_relpath, iterpool));
2638
2639           if (!working_only)
2640             ++i;
2641           if (!orig_only)
2642             ++j;
2643
2644           if (nmb->skip) /* Does parent now want a skip? */
2645             break;
2646         }
2647     }
2648
2649   return SVN_NO_ERROR;
2650 }
2651
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,
2656                     svn_wc__db_t *db,
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,
2664                     void *cancel_baton,
2665                     apr_pool_t *scratch_pool)
2666 {
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;
2673
2674   SVN_ERR_ASSERT(svn_relpath_skip_ancestor(dst_relpath, local_relpath) == NULL);
2675
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).
2682    *
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.
2688    * 
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.
2693    */
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 */
2696
2697   SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
2698   SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
2699
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,
2703                                                       dst_relpath,
2704                                                       scratch_pool),
2705                                       TRUE, cancel_func, cancel_baton,
2706                                       scratch_pool));
2707   if (is_modified)
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 "
2712                                "and try again)"),
2713                              svn_dirent_local_style(
2714                                svn_dirent_join(wcroot->abspath, local_relpath,
2715                                                scratch_pool),
2716                                scratch_pool),
2717                              svn_dirent_local_style(
2718                                svn_dirent_join(wcroot->abspath, dst_relpath,
2719                                                scratch_pool),
2720                                scratch_pool));
2721
2722   /* Check for switched subtrees and mixed-revision working copy. */
2723   SVN_ERR(suitable_for_move(wcroot, dst_relpath, scratch_pool));
2724
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,
2729                                             &repos_id,
2730                                             NULL, NULL, NULL, NULL, NULL,
2731                                             NULL, NULL, NULL, NULL, NULL,
2732                                             wcroot, dst_relpath,
2733                                             scratch_pool, scratch_pool));
2734
2735   SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
2736                                       &new_version.repos_uuid,
2737                                       wcroot, repos_id,
2738                                       scratch_pool));
2739
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,
2745                                     NULL, wcroot,
2746                                     local_relpath, umb.src_op_depth,
2747                                     scratch_pool, scratch_pool));
2748
2749   SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
2750                                       &old_version.repos_uuid,
2751                                       wcroot, repos_id,
2752                                       scratch_pool));
2753   *old_rev = old_version.peg_rev;
2754   *new_rev = new_version.peg_rev;
2755
2756   umb.operation = operation;
2757   umb.old_version= &old_version;
2758   umb.new_version= &new_version;
2759   umb.db = db;
2760   umb.wcroot = wcroot;
2761   umb.cancel_func = cancel_func;
2762   umb.cancel_baton = cancel_baton;
2763
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));
2767
2768   /* Drive the editor... */
2769
2770   nmb.umb = &umb;
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; */
2776
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
2779    * destination. */
2780   SVN_ERR(update_incoming_moved_node(&nmb, wcroot, local_relpath, dst_relpath,
2781                                      scratch_pool));
2782
2783   return SVN_NO_ERROR;
2784 }
2785
2786 svn_error_t *
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,
2794                                 void *cancel_baton,
2795                                 svn_wc_notify_func2_t notify_func,
2796                                 void *notify_baton,
2797                                 apr_pool_t *scratch_pool)
2798 {
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;
2803
2804   /* ### Check for mixed-rev src or dst? */
2805
2806   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2807                                                 db, local_abspath,
2808                                                 scratch_pool, scratch_pool));
2809   VERIFY_USABLE_WCROOT(wcroot);
2810
2811   dest_relpath
2812     = svn_dirent_skip_ancestor(wcroot->abspath, dest_abspath);
2813
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,
2818                                            scratch_pool),
2819                       wcroot);
2820
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,
2824                                              scratch_pool));
2825   if (notify_func)
2826     {
2827       svn_wc_notify_t *notify;
2828
2829       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2830                                                     local_relpath,
2831                                                     scratch_pool),
2832                                     svn_wc_notify_update_completed,
2833                                     scratch_pool);
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);
2839     }
2840
2841
2842   return SVN_NO_ERROR;
2843 }
2844
2845 typedef struct update_local_add_baton_t {
2846   int add_op_depth;
2847   svn_wc__db_t *db;
2848   svn_wc__db_wcroot_t *wcroot;
2849   svn_cancel_func_t cancel_func;
2850   void *cancel_baton;
2851
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;
2855
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;
2860   svn_boolean_t skip;
2861   svn_boolean_t edited;
2862 } added_node_baton_t;
2863
2864
2865 static svn_error_t *
2866 update_local_add_mark_node_edited(added_node_baton_t *nb,
2867                                   apr_pool_t *scratch_pool)
2868 {
2869   if (nb->edited)
2870     return SVN_NO_ERROR;
2871
2872   if (nb->pb)
2873     {
2874       SVN_ERR(update_local_add_mark_node_edited(nb->pb, scratch_pool));
2875
2876       if (nb->pb->skip)
2877         nb->skip = TRUE;
2878     }
2879
2880   nb->edited = TRUE;
2881
2882   return SVN_NO_ERROR;
2883 }
2884
2885 static svn_error_t *
2886 update_local_add_mark_parent_edited(added_node_baton_t *nb,
2887                                     apr_pool_t *scratch_pool)
2888 {
2889   SVN_ERR_ASSERT(nb && nb->pb);
2890
2891   SVN_ERR(update_local_add_mark_node_edited(nb->pb, scratch_pool));
2892
2893   if (nb->pb->skip)
2894     nb->skip = TRUE;
2895
2896   return SVN_NO_ERROR;
2897 }
2898
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)
2906
2907 {
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;
2912
2913   new_version = svn_wc_conflict_version_dup(nb->b->new_version, result_pool);
2914
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;
2923
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));
2930
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;
2937 }
2938
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)
2944 {
2945   svn_wc_notify_state_t content_state;
2946
2947   if (kind_on_disk == svn_node_none)
2948       content_state = svn_wc_notify_state_missing;
2949   else
2950       content_state = svn_wc_notify_state_obstructed;
2951
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;
2957 }
2958
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)
2968 {
2969   const char *local_abspath;
2970   svn_node_kind_t kind_on_disk;
2971
2972   SVN_ERR(update_local_add_mark_parent_edited(nb, scratch_pool));
2973   if (nb->skip)
2974     return SVN_NO_ERROR;
2975
2976   if (base_kind != svn_node_none)
2977     {
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));
2981       nb->skip = TRUE;
2982       return SVN_NO_ERROR;
2983     }
2984   
2985   /* Check for obstructions. */
2986   local_abspath = svn_dirent_join(nb->b->wcroot->abspath, nb->local_relpath,
2987                                   scratch_pool);
2988   SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
2989   if (kind_on_disk != svn_node_file)
2990     {
2991       SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, working_kind,
2992                                                             kind_on_disk,
2993                                                             scratch_pool));
2994       nb->skip = TRUE;
2995       return SVN_NO_ERROR;
2996     }
2997
2998   /* Nothing else to do. Locally added files are an op-root in NODES. */
2999
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;
3006 }
3007
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)
3014 {
3015   const char *local_abspath;
3016   svn_node_kind_t kind_on_disk;
3017
3018   SVN_ERR(update_local_add_mark_parent_edited(nb, scratch_pool));
3019   if (nb->skip)
3020     return SVN_NO_ERROR;
3021
3022   if (base_kind != svn_node_none)
3023     {
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));
3027       nb->skip = TRUE;
3028       return SVN_NO_ERROR;
3029     }
3030
3031   /* Check for obstructions. */
3032   local_abspath = svn_dirent_join(nb->b->wcroot->abspath, nb->local_relpath,
3033                                   scratch_pool);
3034   SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3035   if (kind_on_disk != svn_node_dir)
3036     {
3037       SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_dir,
3038                                                             kind_on_disk,
3039                                                             scratch_pool));
3040       nb->skip = TRUE;
3041       return SVN_NO_ERROR;
3042     }
3043
3044   /* Nothing else to do. Locally added directories are an op-root in NODES. */
3045
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;
3052 }
3053
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,
3060                                 svn_wc__db_t *db,
3061                                 svn_wc__db_wcroot_t *wcroot,
3062                                 apr_pool_t *result_pool,
3063                                 apr_pool_t *scratch_pool)
3064 {
3065   apr_hash_t *new_actual_props;
3066   apr_array_header_t *propchanges;
3067   const char *local_abspath = svn_dirent_join(wcroot->abspath,
3068                                               local_relpath,
3069                                               scratch_pool);
3070
3071   /*
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.
3075    */
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,
3079                               db, local_abspath,
3080                               apr_hash_make(scratch_pool),
3081                               base_props, working_props, propchanges,
3082                               result_pool, scratch_pool));
3083
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,
3087                                              new_actual_props,
3088                                              svn_wc__has_magic_property(
3089                                                       propchanges),
3090                                              scratch_pool));
3091
3092   return SVN_NO_ERROR;
3093 }
3094
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)
3102 {
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,
3112                                               nb->local_relpath,
3113                                               scratch_pool);
3114
3115   SVN_ERR(update_local_add_mark_node_edited(nb, scratch_pool));
3116   if (nb->skip)
3117     return SVN_NO_ERROR;
3118
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)
3122     {
3123       SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_file,
3124                                                             kind_on_disk,
3125                                                             scratch_pool));
3126       nb->skip = TRUE;
3127       return SVN_NO_ERROR;
3128     }
3129
3130   SVN_ERR(update_incoming_add_merge_props(&prop_state, &conflict_skel,
3131                                           nb->local_relpath,
3132                                           base_props, working_props,
3133                                           b->db, b->wcroot,
3134                                           scratch_pool, scratch_pool));
3135
3136   SVN_ERR(svn_wc__internal_file_modified_p(&is_modified,
3137                                            b->db, local_abspath,
3138                                            FALSE /* exact_comparison */,
3139                                            scratch_pool));
3140   if (!is_modified)
3141     {
3142       svn_skel_t *work_item = NULL;
3143
3144       SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
3145                                             local_abspath, NULL,
3146                                             /* FIXME: use_commit_times? */
3147                                             FALSE,
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;
3152     }
3153   else
3154     {
3155       const char *empty_file_abspath;
3156       const char *pristine_abspath;
3157       svn_skel_t *work_item = NULL;
3158
3159       /*
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
3163        * version.
3164        */
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));
3171
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));
3175
3176       SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
3177                                      &merge_outcome, b->db,
3178                                      empty_file_abspath,
3179                                      pristine_abspath,
3180                                      local_abspath,
3181                                      local_abspath,
3182                                      NULL, NULL, NULL, /* diff labels */
3183                                      apr_hash_make(scratch_pool),
3184                                      FALSE, /* dry-run */
3185                                      NULL, /* diff3-cmd */
3186                                      NULL, /* merge options */
3187                                      propchanges,
3188                                      b->cancel_func, b->cancel_baton,
3189                                      scratch_pool, scratch_pool));
3190
3191       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
3192
3193       if (merge_outcome == svn_wc_merge_conflict)
3194         content_state = svn_wc_notify_state_conflicted;
3195       else
3196         content_state = svn_wc_notify_state_merged;
3197     }
3198
3199   /* If there are any conflicts to be stored, convert them into work items
3200    * too. */
3201   if (conflict_skel)
3202     {
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;
3207
3208       new_version = svn_wc_conflict_version_dup(nb->b->new_version,
3209                                                 scratch_pool);
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;
3220
3221       /* Create conflict markers. */
3222       SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, NULL,
3223                                                   new_version, scratch_pool,
3224                                                   scratch_pool));
3225       if (prop_state == svn_wc_notify_state_conflicted)
3226         SVN_ERR(svn_wc__conflict_create_markers(&work_items, b->db,
3227                                                 local_abspath,
3228                                                 conflict_skel,
3229                                                 scratch_pool,
3230                                                 scratch_pool));
3231     }
3232
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));
3237
3238   return SVN_NO_ERROR;
3239 }
3240
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)
3246 {
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,
3253                                               nb->local_relpath,
3254                                               scratch_pool);
3255
3256   SVN_ERR(update_local_add_mark_node_edited(nb, scratch_pool));
3257   if (nb->skip)
3258     return SVN_NO_ERROR;
3259
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)
3263     {
3264       SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_dir,
3265                                                             kind_on_disk,
3266                                                             scratch_pool));
3267       nb->skip = TRUE;
3268       return SVN_NO_ERROR;
3269     }
3270
3271   SVN_ERR(update_incoming_add_merge_props(&prop_state, &conflict_skel,
3272                                           nb->local_relpath,
3273                                           base_props, working_props,
3274                                           b->db, b->wcroot,
3275                                           scratch_pool, scratch_pool));
3276
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)
3279     {
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;
3284
3285       new_version = svn_wc_conflict_version_dup(nb->b->new_version,
3286                                                 scratch_pool);
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;
3297
3298       /* Create conflict markers. */
3299       SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, NULL,
3300                                                   new_version, scratch_pool,
3301                                                   scratch_pool));
3302       SVN_ERR(svn_wc__conflict_create_markers(&work_items, b->db,
3303                                               local_abspath,
3304                                               conflict_skel,
3305                                               scratch_pool,
3306                                               scratch_pool));
3307     }
3308
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));
3313
3314   return SVN_NO_ERROR;
3315 }
3316
3317 static svn_error_t *
3318 update_locally_added_node(added_node_baton_t *nb,
3319                           apr_pool_t *scratch_pool)
3320 {
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,
3329                                               nb->local_relpath,
3330                                               scratch_pool);
3331
3332   if (b->cancel_func)
3333     SVN_ERR(b->cancel_func(b->cancel_baton));
3334
3335   if (nb->skip)
3336     return SVN_NO_ERROR;
3337
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)
3345     {
3346       svn_node_kind_t kind_on_disk;
3347       svn_skel_t *work_item = NULL;
3348
3349       /* Skip obstructed nodes. */
3350       SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk,
3351                                 scratch_pool));
3352       if (kind_on_disk != base_kind && kind_on_disk != svn_node_none)
3353         {
3354           SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath,
3355                                        nb->b->db,
3356                                        svn_wc_notify_skip,
3357                                        base_kind,
3358                                        svn_wc_notify_state_obstructed,
3359                                        svn_wc_notify_state_inapplicable,
3360                                        NULL, NULL, scratch_pool));
3361           nb->skip = TRUE;
3362           return SVN_NO_ERROR;
3363         }
3364
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,
3370                                               NULL,
3371                                               /* FIXME: use_commit_times? */
3372                                               FALSE,
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));
3378
3379       if (work_item)
3380         SVN_ERR(update_move_list_add(wcroot, nb->local_relpath, db,
3381                                      svn_wc_notify_update_add,
3382                                      base_kind,
3383                                      svn_wc_notify_state_inapplicable,
3384                                      svn_wc_notify_state_inapplicable,
3385                                      NULL, work_item, scratch_pool));
3386       return SVN_NO_ERROR;
3387     }
3388
3389   if (base_kind != working_kind)
3390     {
3391       if (working_kind == svn_node_file || working_kind == svn_node_symlink)
3392         {
3393           svn_checksum_t *working_checksum = NULL;
3394
3395           if (base_checksum)
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,
3401                                                 scratch_pool));
3402         }
3403       else if (working_kind == svn_node_dir)
3404         SVN_ERR(tc_editor_update_add_new_directory(nb, base_kind, base_props,
3405                                                    working_props,
3406                                                    scratch_pool));
3407     }
3408   else
3409     {
3410       svn_boolean_t props_equal;
3411
3412       SVN_ERR(props_match(&props_equal, base_props, working_props,
3413                           scratch_pool));
3414
3415       if (working_kind == svn_node_file || working_kind == svn_node_symlink)
3416         {
3417           svn_checksum_t *working_checksum;
3418
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,
3423                                                   working_checksum))
3424             SVN_ERR(tc_editor_update_add_merge_files(nb, working_checksum,
3425                                                      base_checksum,
3426                                                      working_props, base_props,
3427                                                      scratch_pool));
3428         }
3429       else if (working_kind == svn_node_dir && !props_equal)
3430         SVN_ERR(tc_editor_update_add_merge_dirprops(nb, working_props,
3431                                                     base_props,
3432                                                     scratch_pool));
3433     }
3434
3435   if (nb->skip)
3436     return SVN_NO_ERROR;
3437
3438   if (working_kind == svn_node_dir)
3439     {
3440       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3441       int i = 0, j = 0;
3442
3443       while (i < base_children->nelts || j < working_children->nelts)
3444         {
3445           const char *child_name;
3446           svn_boolean_t base_only = FALSE, working_only = FALSE;
3447           added_node_baton_t cnb = { 0 };
3448
3449           cnb.pb = nb;
3450           cnb.b = nb->b;
3451           cnb.skip = FALSE;
3452
3453           svn_pool_clear(iterpool);
3454           if (i >= base_children->nelts)
3455             {
3456               working_only = TRUE;
3457               child_name = APR_ARRAY_IDX(working_children, j, const char *);
3458             }
3459           else if (j >= working_children->nelts)
3460             {
3461               base_only = TRUE;
3462               child_name = APR_ARRAY_IDX(base_children, i, const char *);
3463             }
3464           else
3465             {
3466               const char *base_name = APR_ARRAY_IDX(base_children, i,
3467                                                     const char *);
3468               const char *working_name = APR_ARRAY_IDX(working_children, j,
3469                                                        const char *);
3470               int cmp = strcmp(base_name, working_name);
3471
3472               if (cmp > 0)
3473                 working_only = TRUE;
3474               else if (cmp < 0)
3475                 base_only = TRUE;
3476
3477               child_name = working_only ? working_name : base_name;
3478             }
3479
3480           cnb.local_relpath = svn_relpath_join(nb->local_relpath, child_name,
3481                                                iterpool);
3482
3483           SVN_ERR(update_locally_added_node(&cnb, iterpool));
3484
3485           if (!working_only)
3486             ++i;
3487           if (!base_only)
3488             ++j;
3489
3490           if (nb->skip) /* Does parent now want a skip? */
3491             break;
3492         }
3493     }
3494
3495   return SVN_NO_ERROR;
3496 }
3497
3498 /* The body of svn_wc__db_update_local_add(). */
3499 static svn_error_t *
3500 update_local_add(svn_revnum_t *new_rev,
3501                 svn_wc__db_t *db,
3502                 svn_wc__db_wcroot_t *wcroot,
3503                 const char *local_relpath,
3504                 svn_cancel_func_t cancel_func,
3505                 void *cancel_baton,
3506                 apr_pool_t *scratch_pool)
3507 {
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;
3516
3517   b.add_op_depth = relpath_depth(local_relpath); /* DST op-root */
3518
3519   SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
3520
3521   b.db = db;
3522   b.wcroot = wcroot;
3523   b.cancel_func = cancel_func;
3524   b.cancel_baton = cancel_baton;
3525
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);
3538
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));
3542
3543   /* Drive the editor... */
3544   nb.b = &b;
3545   nb.local_relpath = local_relpath;
3546   nb.skip = FALSE;
3547   SVN_ERR(update_locally_added_node(&nb, scratch_pool));
3548
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));
3558
3559   /* Remove the tree conflict marker. */
3560   SVN_ERR(svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
3561                                                FALSE, FALSE, TRUE,
3562                                                NULL, scratch_pool));
3563   return SVN_NO_ERROR;
3564 }
3565
3566 svn_error_t *
3567 svn_wc__db_update_local_add(svn_wc__db_t *db,
3568                             const char *local_abspath,
3569                             svn_cancel_func_t cancel_func,
3570                             void *cancel_baton,
3571                             svn_wc_notify_func2_t notify_func,
3572                             void *notify_baton,
3573                             apr_pool_t *scratch_pool)
3574 {
3575   svn_wc__db_wcroot_t *wcroot;
3576   svn_revnum_t new_rev;
3577   const char *local_relpath;
3578
3579   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
3580                                                 db, local_abspath,
3581                                                 scratch_pool, scratch_pool));
3582   VERIFY_USABLE_WCROOT(wcroot);
3583
3584   SVN_WC__DB_WITH_TXN(update_local_add(&new_rev, db, wcroot,
3585                                        local_relpath, 
3586                                        cancel_func, cancel_baton,
3587                                        scratch_pool),
3588                       wcroot);
3589
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,
3593                                              scratch_pool));
3594   if (notify_func)
3595     {
3596       svn_wc_notify_t *notify;
3597
3598       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
3599                                                     local_relpath,
3600                                                     scratch_pool),
3601                                     svn_wc_notify_update_completed,
3602                                     scratch_pool);
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);
3608     }
3609
3610
3611   return SVN_NO_ERROR;
3612 }
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,
3619                          int op_depth,
3620                          svn_depth_t depth,
3621                          apr_pool_t *scratch_pool)
3622 {
3623   svn_sqlite__stmt_t *stmt;
3624   svn_boolean_t have_row;
3625
3626   switch (depth)
3627     {
3628     case svn_depth_infinity:
3629       *can_bump = TRUE;
3630       return SVN_NO_ERROR;
3631
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));
3637       break;
3638
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));
3644       break;
3645
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));
3651       break;
3652     default:
3653       SVN_ERR_MALFUNCTION();
3654     }
3655   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3656   SVN_ERR(svn_sqlite__reset(stmt));
3657
3658   *can_bump = !have_row;
3659   return SVN_NO_ERROR;
3660 }
3661
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,
3666                         int src_op_depth,
3667                         const char *move_src_op_root_relpath,
3668                         const char *move_dst_op_root_relpath,
3669                         svn_wc__db_t *db,
3670                         apr_pool_t *scratch_pool)
3671 {
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;
3684
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));
3687
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,
3692                                     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));
3697
3698   /* Read old (pre-update) information from the move destination node.
3699
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)
3705   */
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));
3712
3713   if (strcmp(move_src_root_relpath, move_src_op_root_relpath))
3714     {
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
3717          a clean BASE */
3718
3719       const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath,
3720                                                     move_src_root_relpath);
3721
3722       old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath,
3723                                            scratch_pool);
3724       new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath,
3725                                            scratch_pool);
3726     }
3727
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);
3734
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,
3739                                old_kind, new_kind,
3740                                old_repos_relpath,
3741                                svn_wc_conflict_reason_moved_away,
3742                                svn_wc_conflict_action_edit,
3743                                move_src_op_root_relpath,
3744                                scratch_pool, scratch_pool));
3745
3746   SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db,
3747                                svn_wc_notify_tree_conflict,
3748                                new_kind,
3749                                svn_wc_notify_state_inapplicable,
3750                                svn_wc_notify_state_inapplicable,
3751                                conflict, NULL, scratch_pool));
3752
3753   return SVN_NO_ERROR;
3754 }
3755
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.
3759  */
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)
3768 {
3769   const char *relpath;
3770
3771   *skip = FALSE;
3772   *src_depth = bump_depth;
3773
3774   relpath = svn_relpath_skip_ancestor(bump_root, src_relpath);
3775
3776   if (!relpath)
3777     *skip = TRUE;
3778
3779   if (bump_depth == svn_depth_infinity)
3780     return SVN_NO_ERROR;
3781
3782   if (relpath && *relpath == '\0')
3783     return SVN_NO_ERROR;
3784
3785   switch (bump_depth)
3786     {
3787       case svn_depth_empty:
3788         *skip = TRUE;
3789         break;
3790
3791       case svn_depth_files:
3792         if (src_kind != svn_node_file)
3793           {
3794             *skip = TRUE;
3795             break;
3796           }
3797         /* Fallthrough */
3798       case svn_depth_immediates:
3799         if (!relpath || relpath_depth(relpath) > 1)
3800           *skip = TRUE;
3801
3802         *src_depth = svn_depth_empty;
3803         break;
3804       default:
3805         SVN_ERR_MALFUNCTION();
3806     }
3807
3808   return SVN_NO_ERROR;
3809 }
3810
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.
3813  */
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,
3818                  int op_depth,
3819                  const char *src_relpath,
3820                  int src_del_depth,
3821                  svn_depth_t src_depth,
3822                  const char *dst_relpath,
3823                  svn_wc__db_t *db,
3824                  apr_pool_t *scratch_pool)
3825 {
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;
3831
3832   SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
3833
3834   *recurse = FALSE;
3835
3836   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3837                                     STMT_HAS_LAYER_BETWEEN));
3838
3839   SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
3840                             op_depth, src_del_depth));
3841
3842   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3843   SVN_ERR(svn_sqlite__reset(stmt));
3844
3845   if (have_row)
3846     return SVN_NO_ERROR;
3847
3848   if (op_depth == 0)
3849     SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath,
3850                                      op_depth, src_depth, scratch_pool));
3851   else
3852     /* Having chosen to bump an entire BASE tree move we
3853        always have sufficient depth to bump subtree moves. */
3854     can_bump = TRUE;
3855
3856   /* Are we allowed to bump */
3857   if (can_bump)
3858     {
3859       svn_boolean_t locked;
3860
3861       SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
3862                                                    dst_relpath,
3863                                                    FALSE, scratch_pool));
3864
3865       if (!locked)
3866         can_bump = FALSE;
3867     }
3868
3869   src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth,
3870                                         scratch_pool);
3871
3872   if (!can_bump)
3873     {
3874       SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth,
3875                                       src_root_relpath, dst_relpath,
3876                                       db, scratch_pool));
3877
3878       return SVN_NO_ERROR;
3879     }
3880
3881   SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
3882                                             wcroot, src_root_relpath,
3883                                             scratch_pool, scratch_pool));
3884
3885   /* ### TODO: check this is the right sort of tree-conflict? */
3886   if (!conflict)
3887     {
3888       /* ### TODO: verify moved_here? */
3889
3890       SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
3891
3892       SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot,
3893                                                 src_relpath, op_depth,
3894                                                 dst_relpath, NULL, NULL,
3895                                                 scratch_pool));
3896
3897       *recurse = TRUE;
3898     }
3899
3900   return SVN_NO_ERROR;
3901 }
3902
3903 /* Internal storage for bump_moved_away() */
3904 struct bump_pair_t
3905 {
3906   const char *src_relpath;
3907   const char *dst_relpath;
3908   int src_del_op_depth;
3909   svn_node_kind_t src_kind;
3910 };
3911
3912 /* Bump moves of LOCAL_RELPATH and all its descendants that were
3913    originally below LOCAL_RELPATH at op-depth OP_DEPTH.
3914  */
3915 static svn_error_t *
3916 bump_moved_away(svn_wc__db_wcroot_t *wcroot,
3917                 const char *local_relpath,
3918                 int op_depth,
3919                 svn_depth_t depth,
3920                 svn_wc__db_t *db,
3921                 apr_pool_t *scratch_pool)
3922 {
3923   svn_sqlite__stmt_t *stmt;
3924   svn_boolean_t have_row;
3925   apr_pool_t *iterpool;
3926   int i;
3927   apr_array_header_t *pairs = apr_array_make(scratch_pool, 32,
3928                                              sizeof(struct bump_pair_t*));
3929
3930   /* Build an array, as we can't execute the same Sqlite query recursively */
3931   iterpool = svn_pool_create(scratch_pool);
3932
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,
3936                             op_depth));
3937   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3938   while(have_row)
3939     {
3940       struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp));
3941
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);
3946
3947       APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp;
3948
3949       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3950     }
3951
3952   SVN_ERR(svn_sqlite__reset(stmt));
3953
3954   for (i = 0; i < pairs->nelts; i++)
3955     {
3956       struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *);
3957       svn_boolean_t skip;
3958       svn_depth_t src_wc_depth;
3959
3960       svn_pool_clear(iterpool);
3961
3962
3963       SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth,
3964                                bp->src_relpath, bp->src_kind, iterpool));
3965
3966       if (!skip)
3967         {
3968           svn_boolean_t recurse;
3969
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,
3974                                    db, iterpool));
3975
3976           if (recurse)
3977             SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath,
3978                                     relpath_depth(bp->dst_relpath),
3979                                     depth, db, iterpool));
3980         }
3981     }
3982
3983   svn_pool_destroy(iterpool);
3984
3985   return SVN_NO_ERROR;
3986 }
3987
3988 svn_error_t *
3989 svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot,
3990                            const char *local_relpath,
3991                            svn_depth_t depth,
3992                            svn_wc__db_t *db,
3993                            apr_pool_t *scratch_pool)
3994 {
3995   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
3996                                       STMT_CREATE_UPDATE_MOVE_LIST));
3997
3998   if (local_relpath[0] != '\0')
3999     {
4000       const char *move_dst_op_root_relpath;
4001       const char *move_src_root_relpath, *delete_relpath;
4002       svn_error_t *err;
4003
4004       /* Is the root of the update moved away? (Impossible for the wcroot) */
4005
4006       err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath,
4007                                               &move_dst_op_root_relpath,
4008                                               &delete_relpath,
4009                                               wcroot, local_relpath,
4010                                               0 /* BASE */,
4011                                               scratch_pool, scratch_pool);
4012
4013       if (err)
4014         {
4015           if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4016             return svn_error_trace(err);
4017
4018           svn_error_clear(err);
4019         }
4020       else if (move_src_root_relpath)
4021         {
4022           if (strcmp(move_src_root_relpath, local_relpath))
4023             {
4024               /* An ancestor of the path that was updated is moved away.
4025
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;
4030
4031               SVN_ERR(svn_wc__db_wclock_owns_lock_internal(
4032                                 &locked, wcroot,
4033                                 move_src_root_relpath,
4034                                 FALSE, scratch_pool));
4035
4036               if (locked)
4037                 {
4038                   SVN_ERR(bump_mark_tree_conflict(wcroot,
4039                                                   move_src_root_relpath, 0,
4040                                                   delete_relpath,
4041                                                   move_dst_op_root_relpath,
4042                                                   db, scratch_pool));
4043                 }
4044               return SVN_NO_ERROR;
4045             }
4046         }
4047     }
4048
4049   SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool));
4050
4051   return SVN_NO_ERROR;
4052 }
4053
4054 /* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION
4055  * to reflect the tree conflict on the victim SRC_ABSPATH in DB.
4056  *
4057  * If SRC_ABSPATH is not a tree-conflict victim, return an error.
4058  */
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,
4066                        svn_wc__db_t *db,
4067                        const char *local_relpath,
4068                        const svn_skel_t *conflict_skel,
4069                        apr_pool_t *result_pool,
4070                        apr_pool_t *scratch_pool)
4071 {
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,
4079                                               scratch_pool);
4080
4081   if (!conflict_skel)
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,
4085                                                     scratch_pool));
4086
4087   SVN_ERR(svn_wc__conflict_read_info(operation, &locations,
4088                                      &text_conflicted, &prop_conflicted,
4089                                      &tree_conflicted,
4090                                      db, local_abspath,
4091                                      conflict_skel, result_pool,
4092                                      scratch_pool));
4093
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,
4098                                                     scratch_pool));
4099
4100   SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason,
4101                                               action,
4102                                               &move_src_op_root_abspath,
4103                                               db, local_abspath,
4104                                               conflict_skel, result_pool,
4105                                               scratch_pool));
4106
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,
4111                                                     scratch_pool));
4112
4113   if (left_version)
4114     {
4115       if (locations && locations->nelts > 0)
4116         *left_version = APR_ARRAY_IDX(locations, 0,
4117                                      svn_wc_conflict_version_t *);
4118       else
4119         *left_version = NULL;
4120     }
4121
4122   if (right_version)
4123     {
4124       if (locations && locations->nelts > 1)
4125         *right_version = APR_ARRAY_IDX(locations, 1,
4126                                      svn_wc_conflict_version_t *);
4127       else
4128         *right_version = NULL;
4129     }
4130
4131   {
4132     int del_depth = relpath_depth(local_relpath);
4133
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));
4138
4139     SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth,
4140                               scratch_pool));
4141   }
4142
4143   return SVN_NO_ERROR;
4144 }
4145
4146 svn_error_t *
4147 svn_wc__db_op_raise_moved_away_internal(
4148                         svn_wc__db_wcroot_t *wcroot,
4149                         const char *local_relpath,
4150                         int src_op_depth,
4151                         svn_wc__db_t *db,
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)
4157 {
4158   svn_sqlite__stmt_t *stmt;
4159   svn_boolean_t have_row;
4160   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4161
4162   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4163                                       STMT_CREATE_UPDATE_MOVE_LIST));
4164
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,
4168                             src_op_depth));
4169   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4170   while(have_row)
4171     {
4172       svn_error_t *err;
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);
4180
4181       SVN_ERR_ASSERT(src_repos_relpath != NULL);
4182
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 */,
4187                                  src_repos_relpath,
4188                                  svn_wc_conflict_reason_moved_away,
4189                                  action,
4190                                  svn_relpath_prefix(src_relpath,
4191                                                     delete_op_depth,
4192                                                     iterpool),
4193                                  iterpool, iterpool);
4194
4195       if (!err)
4196         err = update_move_list_add(wcroot, src_relpath, db,
4197                                    svn_wc_notify_tree_conflict,
4198                                    src_kind,
4199                                    svn_wc_notify_state_inapplicable,
4200                                    svn_wc_notify_state_inapplicable,
4201                                    conflict, NULL, scratch_pool);
4202
4203       if (err)
4204         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
4205
4206       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4207     }
4208   SVN_ERR(svn_sqlite__reset(stmt));
4209
4210   svn_pool_destroy(iterpool);
4211
4212   return SVN_NO_ERROR;
4213 }
4214
4215 svn_error_t *
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,
4219                                void *notify_baton,
4220                                apr_pool_t *scratch_pool)
4221 {
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;
4229
4230   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4231                                                 db, local_abspath,
4232                                                 scratch_pool, scratch_pool));
4233   VERIFY_USABLE_WCROOT(wcroot);
4234
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,
4245                                          FALSE, FALSE, TRUE,
4246                                          NULL, scratch_pool),
4247     svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath,
4248                                             move_src_op_depth,
4249                                             db, operation, action,
4250                                             left_version, right_version,
4251                                             scratch_pool),
4252     wcroot);
4253
4254   /* These version numbers are valid for update/switch notifications 
4255      only! */
4256   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
4257                                              (left_version
4258                                               ? left_version->peg_rev
4259                                               : SVN_INVALID_REVNUM),
4260                                              (right_version
4261                                               ? right_version->peg_rev
4262                                               : SVN_INVALID_REVNUM),
4263                                              notify_func, notify_baton,
4264                                              scratch_pool));
4265
4266   return SVN_NO_ERROR;
4267 }
4268
4269 static svn_error_t *
4270 break_moved_away(svn_wc__db_wcroot_t *wcroot,
4271                  svn_wc__db_t *db,
4272                  const char *local_relpath,
4273                  int parent_src_op_depth,
4274                  apr_pool_t *scratch_pool)
4275 {
4276   svn_sqlite__stmt_t *stmt;
4277   svn_boolean_t have_row;
4278   apr_pool_t *iterpool;
4279   svn_error_t *err = NULL;
4280
4281   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4282                                       STMT_CREATE_UPDATE_MOVE_LIST));
4283
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));
4289
4290   iterpool = svn_pool_create(scratch_pool);
4291   while (have_row)
4292     {
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);
4297
4298       svn_pool_clear(iterpool);
4299
4300       err = verify_write_lock(wcroot, src_relpath, iterpool);
4301
4302       if (!err)
4303         err = verify_write_lock(wcroot, dst_relpath, iterpool);
4304
4305       if (err)
4306         break;
4307
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));
4312
4313       if (err)
4314         break;
4315
4316       err = svn_error_trace(
4317               update_move_list_add(wcroot, src_relpath, db,
4318                                    svn_wc_notify_move_broken,
4319                                    src_kind,
4320                                    svn_wc_notify_state_inapplicable,
4321                                    svn_wc_notify_state_inapplicable,
4322                                    NULL, NULL, scratch_pool));
4323
4324       if (err)
4325         break;
4326
4327       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4328     }
4329   svn_pool_destroy(iterpool);
4330
4331   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
4332 }
4333
4334 svn_error_t *
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,
4340                                void *notify_baton,
4341                                apr_pool_t *scratch_pool)
4342 {
4343   svn_wc__db_wcroot_t *wcroot;
4344   const char *local_relpath;
4345   const char *del_relpath;
4346   int src_op_depth;
4347
4348   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4349                                                 db, local_abspath,
4350                                                 scratch_pool, scratch_pool));
4351   VERIFY_USABLE_WCROOT(wcroot);
4352
4353   if (del_op_root_abspath)
4354     del_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4355                                            del_op_root_abspath);
4356   else
4357     del_relpath = NULL;
4358
4359
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),
4364                       scratch_pool),
4365     break_moved_away(wcroot, db, local_relpath, src_op_depth,
4366                      scratch_pool),
4367     mark_tc_resolved
4368         ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
4369                                                FALSE, FALSE, TRUE,
4370                                                NULL, scratch_pool)
4371         : SVN_NO_ERROR,
4372     SVN_NO_ERROR,
4373     wcroot);
4374
4375   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
4376                                              SVN_INVALID_REVNUM,
4377                                              SVN_INVALID_REVNUM,
4378                                              notify_func, notify_baton,
4379                                              scratch_pool));
4380   return SVN_NO_ERROR;
4381 }
4382
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)
4389 {
4390   svn_sqlite__stmt_t *stmt;
4391   svn_boolean_t have_row;
4392
4393   *required_relpath = local_relpath;
4394
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));
4404
4405   while (have_row)
4406     {
4407       const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1,
4408                                                              NULL);
4409
4410       *required_relpath
4411         = svn_relpath_get_longest_ancestor(*required_relpath,
4412                                            move_dst_relpath,
4413                                            scratch_pool);
4414
4415       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4416     }
4417   SVN_ERR(svn_sqlite__reset(stmt));
4418
4419   *required_relpath = apr_pstrdup(result_pool, *required_relpath);
4420
4421   return SVN_NO_ERROR;
4422 }
4423
4424 svn_error_t *
4425 svn_wc__required_lock_for_resolve(const char **required_abspath,
4426                                   svn_wc__db_t *db,
4427                                   const char *local_abspath,
4428                                   apr_pool_t *result_pool,
4429                                   apr_pool_t *scratch_pool)
4430 {
4431   svn_wc__db_wcroot_t *wcroot;
4432   const char *local_relpath;
4433   const char *required_relpath;
4434
4435   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4436                                                 db, local_abspath,
4437                                                 scratch_pool, scratch_pool));
4438   VERIFY_USABLE_WCROOT(wcroot);
4439
4440   SVN_WC__DB_WITH_TXN(
4441     required_lock_for_resolve(&required_relpath, wcroot, local_relpath,
4442                               scratch_pool, scratch_pool),
4443     wcroot);
4444
4445   *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath,
4446                                       result_pool);
4447
4448   return SVN_NO_ERROR;
4449 }