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