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