]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_wc/wc_db_update_move.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_wc / wc_db_update_move.c
1 /*
2  * wc_db_update_move.c :  updating moves during tree-conflict resolution
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* This file implements an editor and an edit driver which are used
25  * to resolve an "incoming edit, local move-away" tree conflict resulting
26  * from an update (or switch).
27  *
28  * Our goal is to be able to resolve this conflict such that the end
29  * result is just the same as if the user had run the update *before*
30  * the local move.
31  *
32  * When an update (or switch) produces incoming changes for a locally
33  * moved-away subtree, it updates the base nodes of the moved-away tree
34  * and flags a tree-conflict on the moved-away root node.
35  * This editor transfers these changes from the moved-away part of the
36  * working copy to the corresponding moved-here part of the working copy.
37  *
38  * Both the driver and receiver components of the editor are implemented
39  * in this file.
40  *
41  * The driver sees two NODES trees: the move source tree and the move
42  * destination tree.  When the move is initially made these trees are
43  * equivalent, the destination is a copy of the source.  The source is
44  * a single-op-depth, single-revision, deleted layer [1] and the
45  * destination has an equivalent single-op-depth, single-revision
46  * layer. The destination may have additional higher op-depths
47  * representing adds, deletes, moves within the move destination. [2]
48  *
49  * After the 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   const char *dummy1, *move_dst_op_root_relpath;
2288   const char *move_src_root_relpath, *move_src_op_root_relpath;
2289   apr_hash_t *src_done;
2290
2291   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2292                                       STMT_CREATE_UPDATE_MOVE_LIST));
2293
2294   SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath,
2295                                        &move_src_root_relpath,
2296                                        &move_src_op_root_relpath, 0,
2297                                        wcroot, local_relpath,
2298                                        scratch_pool, scratch_pool));
2299
2300   if (move_src_root_relpath)
2301     {
2302       if (strcmp(move_src_root_relpath, local_relpath))
2303         {
2304           SVN_ERR(bump_mark_tree_conflict(wcroot, move_src_root_relpath,
2305                                           move_src_op_root_relpath,
2306                                           move_dst_op_root_relpath,
2307                                           db, scratch_pool));
2308           return SVN_NO_ERROR;
2309         }
2310
2311     }
2312
2313   src_done = apr_hash_make(scratch_pool);
2314   SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, src_done, depth, db,
2315                           scratch_pool, scratch_pool));
2316
2317   return SVN_NO_ERROR;
2318 }
2319
2320 static svn_error_t *
2321 resolve_delete_raise_moved_away(svn_wc__db_wcroot_t *wcroot,
2322                                 const char *local_relpath,
2323                                 svn_wc__db_t *db,
2324                                 svn_wc_operation_t operation,
2325                                 svn_wc_conflict_action_t action,
2326                                 svn_wc_conflict_version_t *old_version,
2327                                 svn_wc_conflict_version_t *new_version,
2328                                 apr_pool_t *scratch_pool)
2329 {
2330   svn_sqlite__stmt_t *stmt;
2331   svn_boolean_t have_row;
2332   int op_depth = relpath_depth(local_relpath);
2333   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2334
2335   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2336                                       STMT_CREATE_UPDATE_MOVE_LIST));
2337
2338   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2339                                     STMT_SELECT_OP_DEPTH_MOVED_PAIR));
2340   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
2341                             op_depth));
2342   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2343   while(have_row)
2344     {
2345       const char *moved_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2346       const char *move_root_dst_relpath = svn_sqlite__column_text(stmt, 1,
2347                                                                   NULL);
2348       const char *moved_dst_repos_relpath = svn_sqlite__column_text(stmt, 2,
2349                                                                     NULL);
2350       svn_pool_clear(iterpool);
2351
2352       SVN_ERR(mark_tree_conflict(moved_relpath,
2353                                  wcroot, db, old_version, new_version,
2354                                  move_root_dst_relpath, operation,
2355                                  svn_node_dir /* ### ? */,
2356                                  svn_node_dir /* ### ? */,
2357                                  moved_dst_repos_relpath,
2358                                  svn_wc_conflict_reason_moved_away,
2359                                  action, local_relpath,
2360                                  iterpool));
2361
2362       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2363     }
2364   SVN_ERR(svn_sqlite__reset(stmt));
2365
2366   svn_pool_destroy(iterpool);
2367
2368   return SVN_NO_ERROR;
2369 }
2370
2371 svn_error_t *
2372 svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db,
2373                                            const char *local_abspath,
2374                                            svn_wc_notify_func2_t notify_func,
2375                                            void *notify_baton,
2376                                            apr_pool_t *scratch_pool)
2377 {
2378   svn_wc__db_wcroot_t *wcroot;
2379   const char *local_relpath;
2380   svn_wc_operation_t operation;
2381   svn_wc_conflict_reason_t reason;
2382   svn_wc_conflict_action_t action;
2383   svn_wc_conflict_version_t *old_version, *new_version;
2384
2385   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2386                                                 db, local_abspath,
2387                                                 scratch_pool, scratch_pool));
2388   VERIFY_USABLE_WCROOT(wcroot);
2389
2390   SVN_ERR(get_tc_info(&operation, &reason, &action, NULL,
2391                       &old_version, &new_version,
2392                       db, local_abspath, scratch_pool, scratch_pool));
2393
2394   SVN_WC__DB_WITH_TXN(
2395     resolve_delete_raise_moved_away(wcroot, local_relpath,
2396                                     db, operation, action,
2397                                     old_version, new_version,
2398                                     scratch_pool),
2399     wcroot);
2400
2401   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
2402                                              (old_version
2403                                               ? old_version->peg_rev
2404                                               : SVN_INVALID_REVNUM),
2405                                              (new_version
2406                                               ? new_version->peg_rev
2407                                               : SVN_INVALID_REVNUM),
2408                                              notify_func, notify_baton,
2409                                              scratch_pool));
2410
2411   return SVN_NO_ERROR;
2412 }
2413
2414 static svn_error_t *
2415 break_move(svn_wc__db_wcroot_t *wcroot,
2416            const char *src_relpath,
2417            int src_op_depth,
2418            const char *dst_relpath,
2419            int dst_op_depth,
2420            apr_pool_t *scratch_pool)
2421 {
2422   svn_sqlite__stmt_t *stmt;
2423
2424   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2425                                     STMT_CLEAR_MOVED_TO_RELPATH));
2426   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
2427                             src_op_depth));
2428   SVN_ERR(svn_sqlite__step_done(stmt));
2429
2430   /* This statement clears moved_here. */
2431   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2432                                     STMT_UPDATE_OP_DEPTH_RECURSIVE));
2433   SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
2434                             dst_relpath, dst_op_depth, dst_op_depth));
2435   SVN_ERR(svn_sqlite__step_done(stmt));
2436
2437   return SVN_NO_ERROR;
2438 }
2439
2440 svn_error_t *
2441 svn_wc__db_resolve_break_moved_away_internal(svn_wc__db_wcroot_t *wcroot,
2442                                              const char *local_relpath,
2443                                              apr_pool_t *scratch_pool)
2444 {
2445   const char *dummy1, *move_dst_op_root_relpath;
2446   const char *dummy2, *move_src_op_root_relpath;
2447
2448   SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath,
2449                                        &dummy2,
2450                                        &move_src_op_root_relpath,
2451                                        relpath_depth(local_relpath) - 1,
2452                                        wcroot, local_relpath,
2453                                        scratch_pool, scratch_pool));
2454   SVN_ERR(break_move(wcroot, local_relpath,
2455                      relpath_depth(move_src_op_root_relpath),
2456                      move_dst_op_root_relpath,
2457                      relpath_depth(move_dst_op_root_relpath),
2458                      scratch_pool));
2459
2460   return SVN_NO_ERROR;
2461 }
2462
2463 static svn_error_t *
2464 break_moved_away_children_internal(svn_wc__db_wcroot_t *wcroot,
2465                                    const char *local_relpath,
2466                                    apr_pool_t *scratch_pool)
2467 {
2468   svn_sqlite__stmt_t *stmt;
2469   svn_boolean_t have_row;
2470   apr_pool_t *iterpool;
2471
2472   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2473                                       STMT_CREATE_UPDATE_MOVE_LIST));
2474
2475   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2476                                     STMT_SELECT_MOVED_PAIR2));
2477   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2478   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2479
2480   iterpool = svn_pool_create(scratch_pool);
2481   while (have_row)
2482     {
2483       const char *src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2484       const char *dst_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2485       int src_op_depth = svn_sqlite__column_int(stmt, 2);
2486
2487       svn_pool_clear(iterpool);
2488
2489       SVN_ERR(break_move(wcroot, src_relpath, src_op_depth, dst_relpath,
2490                          relpath_depth(dst_relpath), iterpool));
2491       SVN_ERR(update_move_list_add(wcroot, src_relpath,
2492                                    svn_wc_notify_move_broken,
2493                                    svn_node_unknown,
2494                                    svn_wc_notify_state_inapplicable,
2495                                    svn_wc_notify_state_inapplicable));
2496       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2497     }
2498   svn_pool_destroy(iterpool);
2499
2500   SVN_ERR(svn_sqlite__reset(stmt));
2501
2502   return SVN_NO_ERROR;
2503 }
2504
2505 svn_error_t *
2506 svn_wc__db_resolve_break_moved_away(svn_wc__db_t *db,
2507                                     const char *local_abspath,
2508                                     svn_wc_notify_func2_t notify_func,
2509                                     void *notify_baton,
2510                                     apr_pool_t *scratch_pool)
2511 {
2512   svn_wc__db_wcroot_t *wcroot;
2513   const char *local_relpath;
2514
2515   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2516                                                 db, local_abspath,
2517                                                 scratch_pool, scratch_pool));
2518   VERIFY_USABLE_WCROOT(wcroot);
2519
2520   SVN_WC__DB_WITH_TXN(
2521     svn_wc__db_resolve_break_moved_away_internal(wcroot, local_relpath,
2522                                                  scratch_pool),
2523     wcroot);
2524
2525   if (notify_func)
2526     {
2527       svn_wc_notify_t *notify;
2528
2529       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2530                                                     local_relpath,
2531                                                     scratch_pool),
2532                                     svn_wc_notify_move_broken,
2533                                     scratch_pool);
2534       notify->kind = svn_node_unknown;
2535       notify->content_state = svn_wc_notify_state_inapplicable;
2536       notify->prop_state = svn_wc_notify_state_inapplicable;
2537       notify->revision = SVN_INVALID_REVNUM;
2538       notify_func(notify_baton, notify, scratch_pool);
2539     }
2540
2541   return SVN_NO_ERROR;
2542 }
2543
2544 svn_error_t *
2545 svn_wc__db_resolve_break_moved_away_children(svn_wc__db_t *db,
2546                                              const char *local_abspath,
2547                                              svn_wc_notify_func2_t notify_func,
2548                                              void *notify_baton,
2549                                              apr_pool_t *scratch_pool)
2550 {
2551   svn_wc__db_wcroot_t *wcroot;
2552   const char *local_relpath;
2553
2554   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2555                                                 db, local_abspath,
2556                                                 scratch_pool, scratch_pool));
2557   VERIFY_USABLE_WCROOT(wcroot);
2558
2559   SVN_WC__DB_WITH_TXN(
2560     break_moved_away_children_internal(wcroot, local_relpath, scratch_pool),
2561     wcroot);
2562
2563   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
2564                                              SVN_INVALID_REVNUM,
2565                                              SVN_INVALID_REVNUM,
2566                                              notify_func, notify_baton,
2567                                              scratch_pool));
2568   return SVN_NO_ERROR;
2569 }
2570
2571 static svn_error_t *
2572 required_lock_for_resolve(const char **required_relpath,
2573                           svn_wc__db_wcroot_t *wcroot,
2574                           const char *local_relpath,
2575                           apr_pool_t *result_pool,
2576                           apr_pool_t *scratch_pool)
2577 {
2578   svn_sqlite__stmt_t *stmt;
2579   svn_boolean_t have_row;
2580
2581   *required_relpath = local_relpath;
2582
2583   /* This simply looks for all moves out of the LOCAL_RELPATH tree. We
2584      could attempt to limit it to only those moves that are going to
2585      be resolved but that would require second guessing the resolver.
2586      This simple algorithm is sufficient although it may give a
2587      strictly larger/deeper lock than necessary. */
2588   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2589                                     STMT_SELECT_MOVED_OUTSIDE));
2590   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
2591   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2592
2593   while (have_row)
2594     {
2595       const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1,
2596                                                              NULL);
2597
2598       *required_relpath
2599         = svn_relpath_get_longest_ancestor(*required_relpath,
2600                                            move_dst_relpath,
2601                                            scratch_pool);
2602
2603       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2604     }
2605   SVN_ERR(svn_sqlite__reset(stmt));
2606
2607   *required_relpath = apr_pstrdup(result_pool, *required_relpath);
2608
2609   return SVN_NO_ERROR;
2610 }
2611
2612 svn_error_t *
2613 svn_wc__required_lock_for_resolve(const char **required_abspath,
2614                                   svn_wc__db_t *db,
2615                                   const char *local_abspath,
2616                                   apr_pool_t *result_pool,
2617                                   apr_pool_t *scratch_pool)
2618 {
2619   svn_wc__db_wcroot_t *wcroot;
2620   const char *local_relpath;
2621   const char *required_relpath;
2622
2623   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2624                                                 db, local_abspath,
2625                                                 scratch_pool, scratch_pool));
2626   VERIFY_USABLE_WCROOT(wcroot);
2627
2628   SVN_WC__DB_WITH_TXN(
2629     required_lock_for_resolve(&required_relpath, wcroot, local_relpath,
2630                               scratch_pool, scratch_pool),
2631     wcroot);
2632
2633   *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath,
2634                                       result_pool);
2635
2636   return SVN_NO_ERROR;
2637 }