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