wc_db_update_move.c revision 262253
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
116struct 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. */
139static svn_error_t *
140update_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. */
161svn_error_t *
162svn_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. */
217static svn_error_t *
218mark_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. */
374static svn_error_t *
375check_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
453static svn_error_t *
454tc_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
544static svn_error_t *
545tc_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
632static svn_error_t *
633tc_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
643static svn_error_t *
644tc_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. */
654typedef 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. */
662static svn_error_t *
663create_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
718static svn_error_t *
719update_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
773static svn_error_t *
774tc_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. */
885static svn_error_t *
886update_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 */
1007static svn_error_t *
1008tc_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
1070static svn_error_t *
1071tc_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
1081static svn_error_t *
1082tc_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
1276static svn_error_t *
1277tc_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
1287static svn_error_t *
1288tc_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
1298static svn_error_t *
1299tc_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
1307static svn_error_t *
1308tc_editor_complete(void *baton,
1309                   apr_pool_t *scratch_pool)
1310{
1311  return SVN_NO_ERROR;
1312}
1313
1314static svn_error_t *
1315tc_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. */
1322static 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 */
1360static svn_error_t *
1361get_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. */
1422static svn_error_t *
1423get_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 *'. */
1472static svn_boolean_t
1473children_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. */
1496static svn_error_t *
1497props_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 */
1518static svn_error_t *
1519update_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. */
1672static svn_error_t *
1673replace_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 */
1731static svn_error_t *
1732drive_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
1776static svn_error_t *
1777suitable_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 */
1847static svn_error_t *
1848update_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
1943svn_error_t *
1944svn_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. */
2021static svn_error_t *
2022depth_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. */
2068static svn_error_t *
2069bump_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. */
2131static svn_error_t *
2132bump_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
2280svn_error_t *
2281svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot,
2282                           const char *local_relpath,
2283                           svn_depth_t depth,
2284                           svn_wc__db_t *db,
2285                           apr_pool_t *scratch_pool)
2286{
2287  apr_hash_t *src_done;
2288
2289  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2290                                      STMT_CREATE_UPDATE_MOVE_LIST));
2291
2292  if (local_relpath[0] != '\0')
2293    {
2294      const char *dummy1, *move_dst_op_root_relpath;
2295      const char *move_src_root_relpath, *move_src_op_root_relpath;
2296
2297      /* Is the root of the update moved away? (Impossible for the wcroot) */
2298      SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath,
2299                                           &move_src_root_relpath,
2300                                           &move_src_op_root_relpath, 0,
2301                                           wcroot, local_relpath,
2302                                           scratch_pool, scratch_pool));
2303
2304      if (move_src_root_relpath)
2305        {
2306          if (strcmp(move_src_root_relpath, local_relpath))
2307            {
2308              SVN_ERR(bump_mark_tree_conflict(wcroot, move_src_root_relpath,
2309                                              move_src_op_root_relpath,
2310                                              move_dst_op_root_relpath,
2311                                              db, scratch_pool));
2312              return SVN_NO_ERROR;
2313            }
2314        }
2315    }
2316
2317  src_done = apr_hash_make(scratch_pool);
2318  SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, src_done, depth, db,
2319                          scratch_pool, scratch_pool));
2320
2321  return SVN_NO_ERROR;
2322}
2323
2324static svn_error_t *
2325resolve_delete_raise_moved_away(svn_wc__db_wcroot_t *wcroot,
2326                                const char *local_relpath,
2327                                svn_wc__db_t *db,
2328                                svn_wc_operation_t operation,
2329                                svn_wc_conflict_action_t action,
2330                                svn_wc_conflict_version_t *old_version,
2331                                svn_wc_conflict_version_t *new_version,
2332                                apr_pool_t *scratch_pool)
2333{
2334  svn_sqlite__stmt_t *stmt;
2335  svn_boolean_t have_row;
2336  int op_depth = relpath_depth(local_relpath);
2337  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2338
2339  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2340                                      STMT_CREATE_UPDATE_MOVE_LIST));
2341
2342  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2343                                    STMT_SELECT_OP_DEPTH_MOVED_PAIR));
2344  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
2345                            op_depth));
2346  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2347  while(have_row)
2348    {
2349      const char *moved_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2350      const char *move_root_dst_relpath = svn_sqlite__column_text(stmt, 1,
2351                                                                  NULL);
2352      const char *moved_dst_repos_relpath = svn_sqlite__column_text(stmt, 2,
2353                                                                    NULL);
2354      svn_pool_clear(iterpool);
2355
2356      SVN_ERR(mark_tree_conflict(moved_relpath,
2357                                 wcroot, db, old_version, new_version,
2358                                 move_root_dst_relpath, operation,
2359                                 svn_node_dir /* ### ? */,
2360                                 svn_node_dir /* ### ? */,
2361                                 moved_dst_repos_relpath,
2362                                 svn_wc_conflict_reason_moved_away,
2363                                 action, local_relpath,
2364                                 iterpool));
2365
2366      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2367    }
2368  SVN_ERR(svn_sqlite__reset(stmt));
2369
2370  svn_pool_destroy(iterpool);
2371
2372  return SVN_NO_ERROR;
2373}
2374
2375svn_error_t *
2376svn_wc__db_resolve_delete_raise_moved_away(svn_wc__db_t *db,
2377                                           const char *local_abspath,
2378                                           svn_wc_notify_func2_t notify_func,
2379                                           void *notify_baton,
2380                                           apr_pool_t *scratch_pool)
2381{
2382  svn_wc__db_wcroot_t *wcroot;
2383  const char *local_relpath;
2384  svn_wc_operation_t operation;
2385  svn_wc_conflict_reason_t reason;
2386  svn_wc_conflict_action_t action;
2387  svn_wc_conflict_version_t *old_version, *new_version;
2388
2389  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2390                                                db, local_abspath,
2391                                                scratch_pool, scratch_pool));
2392  VERIFY_USABLE_WCROOT(wcroot);
2393
2394  SVN_ERR(get_tc_info(&operation, &reason, &action, NULL,
2395                      &old_version, &new_version,
2396                      db, local_abspath, scratch_pool, scratch_pool));
2397
2398  SVN_WC__DB_WITH_TXN(
2399    resolve_delete_raise_moved_away(wcroot, local_relpath,
2400                                    db, operation, action,
2401                                    old_version, new_version,
2402                                    scratch_pool),
2403    wcroot);
2404
2405  SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
2406                                             (old_version
2407                                              ? old_version->peg_rev
2408                                              : SVN_INVALID_REVNUM),
2409                                             (new_version
2410                                              ? new_version->peg_rev
2411                                              : SVN_INVALID_REVNUM),
2412                                             notify_func, notify_baton,
2413                                             scratch_pool));
2414
2415  return SVN_NO_ERROR;
2416}
2417
2418static svn_error_t *
2419break_move(svn_wc__db_wcroot_t *wcroot,
2420           const char *src_relpath,
2421           int src_op_depth,
2422           const char *dst_relpath,
2423           int dst_op_depth,
2424           apr_pool_t *scratch_pool)
2425{
2426  svn_sqlite__stmt_t *stmt;
2427
2428  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2429                                    STMT_CLEAR_MOVED_TO_RELPATH));
2430  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
2431                            src_op_depth));
2432  SVN_ERR(svn_sqlite__step_done(stmt));
2433
2434  /* This statement clears moved_here. */
2435  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2436                                    STMT_UPDATE_OP_DEPTH_RECURSIVE));
2437  SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
2438                            dst_relpath, dst_op_depth, dst_op_depth));
2439  SVN_ERR(svn_sqlite__step_done(stmt));
2440
2441  return SVN_NO_ERROR;
2442}
2443
2444svn_error_t *
2445svn_wc__db_resolve_break_moved_away_internal(svn_wc__db_wcroot_t *wcroot,
2446                                             const char *local_relpath,
2447                                             int op_depth,
2448                                             apr_pool_t *scratch_pool)
2449{
2450  const char *dummy1, *move_dst_op_root_relpath;
2451  const char *dummy2, *move_src_op_root_relpath;
2452
2453  /* We want to include the passed op-depth, but the function does a > check */
2454  SVN_ERR(svn_wc__db_op_depth_moved_to(&dummy1, &move_dst_op_root_relpath,
2455                                       &dummy2,
2456                                       &move_src_op_root_relpath,
2457                                       op_depth - 1,
2458                                       wcroot, local_relpath,
2459                                       scratch_pool, scratch_pool));
2460
2461  SVN_ERR_ASSERT(move_src_op_root_relpath != NULL
2462                 && move_dst_op_root_relpath != NULL);
2463
2464  SVN_ERR(break_move(wcroot, local_relpath,
2465                     relpath_depth(move_src_op_root_relpath),
2466                     move_dst_op_root_relpath,
2467                     relpath_depth(move_dst_op_root_relpath),
2468                     scratch_pool));
2469
2470  return SVN_NO_ERROR;
2471}
2472
2473static svn_error_t *
2474break_moved_away_children_internal(svn_wc__db_wcroot_t *wcroot,
2475                                   const char *local_relpath,
2476                                   apr_pool_t *scratch_pool)
2477{
2478  svn_sqlite__stmt_t *stmt;
2479  svn_boolean_t have_row;
2480  apr_pool_t *iterpool;
2481
2482  SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2483                                      STMT_CREATE_UPDATE_MOVE_LIST));
2484
2485  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2486                                    STMT_SELECT_MOVED_PAIR2));
2487  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2488  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2489
2490  iterpool = svn_pool_create(scratch_pool);
2491  while (have_row)
2492    {
2493      const char *src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2494      const char *dst_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2495      int src_op_depth = svn_sqlite__column_int(stmt, 2);
2496
2497      svn_pool_clear(iterpool);
2498
2499      SVN_ERR(break_move(wcroot, src_relpath, src_op_depth, dst_relpath,
2500                         relpath_depth(dst_relpath), iterpool));
2501      SVN_ERR(update_move_list_add(wcroot, src_relpath,
2502                                   svn_wc_notify_move_broken,
2503                                   svn_node_unknown,
2504                                   svn_wc_notify_state_inapplicable,
2505                                   svn_wc_notify_state_inapplicable));
2506      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2507    }
2508  svn_pool_destroy(iterpool);
2509
2510  SVN_ERR(svn_sqlite__reset(stmt));
2511
2512  return SVN_NO_ERROR;
2513}
2514
2515svn_error_t *
2516svn_wc__db_resolve_break_moved_away(svn_wc__db_t *db,
2517                                    const char *local_abspath,
2518                                    svn_wc_notify_func2_t notify_func,
2519                                    void *notify_baton,
2520                                    apr_pool_t *scratch_pool)
2521{
2522  svn_wc__db_wcroot_t *wcroot;
2523  const char *local_relpath;
2524
2525  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2526                                                db, local_abspath,
2527                                                scratch_pool, scratch_pool));
2528  VERIFY_USABLE_WCROOT(wcroot);
2529
2530  SVN_WC__DB_WITH_TXN(
2531    svn_wc__db_resolve_break_moved_away_internal(wcroot, local_relpath,
2532                                                 relpath_depth(local_relpath),
2533                                                 scratch_pool),
2534    wcroot);
2535
2536  if (notify_func)
2537    {
2538      svn_wc_notify_t *notify;
2539
2540      notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2541                                                    local_relpath,
2542                                                    scratch_pool),
2543                                    svn_wc_notify_move_broken,
2544                                    scratch_pool);
2545      notify->kind = svn_node_unknown;
2546      notify->content_state = svn_wc_notify_state_inapplicable;
2547      notify->prop_state = svn_wc_notify_state_inapplicable;
2548      notify->revision = SVN_INVALID_REVNUM;
2549      notify_func(notify_baton, notify, scratch_pool);
2550    }
2551
2552  return SVN_NO_ERROR;
2553}
2554
2555svn_error_t *
2556svn_wc__db_resolve_break_moved_away_children(svn_wc__db_t *db,
2557                                             const char *local_abspath,
2558                                             svn_wc_notify_func2_t notify_func,
2559                                             void *notify_baton,
2560                                             apr_pool_t *scratch_pool)
2561{
2562  svn_wc__db_wcroot_t *wcroot;
2563  const char *local_relpath;
2564
2565  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2566                                                db, local_abspath,
2567                                                scratch_pool, scratch_pool));
2568  VERIFY_USABLE_WCROOT(wcroot);
2569
2570  SVN_WC__DB_WITH_TXN(
2571    break_moved_away_children_internal(wcroot, local_relpath, scratch_pool),
2572    wcroot);
2573
2574  SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
2575                                             SVN_INVALID_REVNUM,
2576                                             SVN_INVALID_REVNUM,
2577                                             notify_func, notify_baton,
2578                                             scratch_pool));
2579  return SVN_NO_ERROR;
2580}
2581
2582static svn_error_t *
2583required_lock_for_resolve(const char **required_relpath,
2584                          svn_wc__db_wcroot_t *wcroot,
2585                          const char *local_relpath,
2586                          apr_pool_t *result_pool,
2587                          apr_pool_t *scratch_pool)
2588{
2589  svn_sqlite__stmt_t *stmt;
2590  svn_boolean_t have_row;
2591
2592  *required_relpath = local_relpath;
2593
2594  /* This simply looks for all moves out of the LOCAL_RELPATH tree. We
2595     could attempt to limit it to only those moves that are going to
2596     be resolved but that would require second guessing the resolver.
2597     This simple algorithm is sufficient although it may give a
2598     strictly larger/deeper lock than necessary. */
2599  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2600                                    STMT_SELECT_MOVED_OUTSIDE));
2601  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
2602  SVN_ERR(svn_sqlite__step(&have_row, stmt));
2603
2604  while (have_row)
2605    {
2606      const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1,
2607                                                             NULL);
2608
2609      *required_relpath
2610        = svn_relpath_get_longest_ancestor(*required_relpath,
2611                                           move_dst_relpath,
2612                                           scratch_pool);
2613
2614      SVN_ERR(svn_sqlite__step(&have_row, stmt));
2615    }
2616  SVN_ERR(svn_sqlite__reset(stmt));
2617
2618  *required_relpath = apr_pstrdup(result_pool, *required_relpath);
2619
2620  return SVN_NO_ERROR;
2621}
2622
2623svn_error_t *
2624svn_wc__required_lock_for_resolve(const char **required_abspath,
2625                                  svn_wc__db_t *db,
2626                                  const char *local_abspath,
2627                                  apr_pool_t *result_pool,
2628                                  apr_pool_t *scratch_pool)
2629{
2630  svn_wc__db_wcroot_t *wcroot;
2631  const char *local_relpath;
2632  const char *required_relpath;
2633
2634  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2635                                                db, local_abspath,
2636                                                scratch_pool, scratch_pool));
2637  VERIFY_USABLE_WCROOT(wcroot);
2638
2639  SVN_WC__DB_WITH_TXN(
2640    required_lock_for_resolve(&required_relpath, wcroot, local_relpath,
2641                              scratch_pool, scratch_pool),
2642    wcroot);
2643
2644  *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath,
2645                                      result_pool);
2646
2647  return SVN_NO_ERROR;
2648}
2649