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