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 implements editors and edit drivers which are used to resolve
25  * "incoming edit, local move-away", "incoming move, local edit", and
26  * "incoming add, local add" tree conflicts resulting from an update
27  * (or switch).
28  *
29  * Our goal is to be able to resolve conflicts such that the end result
30  * is just the same as if the user had run the update *before* the local
31  * (or incoming) move or local add.
32  *
33  * -- Updating local moves --
34  *
35  * When an update (or switch) produces incoming changes for a locally
36  * moved-away subtree, it updates the base nodes of the moved-away tree
37  * and flags a tree-conflict on the moved-away root node.
38  * This editor transfers these changes from the moved-away part of the
39  * working copy to the corresponding moved-here part of the working copy.
40  *
41  * Both the driver and receiver components of the editor are implemented
42  * in this file.
43  *
44  * The driver sees two NODES trees: the move source tree and the move
45  * destination tree.  When the move is initially made these trees are
46  * equivalent, the destination is a copy of the source.  The source is
47  * a single-op-depth, single-revision, deleted layer [1] and the
48  * destination has an equivalent single-op-depth, single-revision
49  * layer. The destination may have additional higher op-depths
50  * representing adds, deletes, moves within the move destination. [2]
51  *
52  * After the initial move an update has modified the NODES in the move
53  * source and may have introduced a tree-conflict since the source and
54  * destination trees are no longer equivalent.  The source is a
55  * different revision and may have text, property and tree changes
56  * compared to the destination.  The driver will compare the two NODES
57  * trees and drive an editor to change the destination tree so that it
58  * once again matches the source tree.  Changes made to the
59  * destination NODES tree to achieve this match will be merged into
60  * the working files/directories.
61  *
62  * The whole drive occurs as one single wc.db transaction.  At the end
63  * of the transaction the destination NODES table should have a layer
64  * that is equivalent to the source NODES layer, there should be
65  * workqueue items to make any required changes to working
66  * files/directories in the move destination, and there should be
67  * tree-conflicts in the move destination where it was not possible to
68  * update the working files/directories.
69  *
70  * [1] The move source tree is single-revision because we currently do
71  *     not allow a mixed-rev move, and therefore it is single op-depth
72  *     regardless whether it is a base layer or a nested move.
73  *
74  * [2] The source tree also may have additional higher op-depths,
75  *     representing a replacement, but this editor only reads from the
76  *     single-op-depth layer of it, and makes no changes of any kind
77  *     within the source tree.
78  *
79  * -- Updating incoming moves --
80  *
81  * When an update (or switch) produces an incoming move, it deletes the
82  * moved node at the old location from the BASE tree and adds a node at
83  * the new location to the BASE tree. If the old location contains local
84  * changes, a tree conflict is raised, and the former BASE tree which
85  * the local changes were based on (the tree conflict victim) is re-added
86  * as a copy which contains these local changes.
87  *
88  * The driver sees two NODES trees: The op-root of the copy, and the
89  * WORKING layer on top of this copy which represents the local changes.
90  * The driver will compare the two NODES trees and drive an editor to
91  * change the move destination's WORKING tree so that it now contains
92  * the local changes seen in the copy of the victim's tree.
93  *
94  * We require that no local changes exist at the destination, in order
95  * to avoid tree conflicts where the "incoming" and "local" change both
96  * originated in the working copy, because the resolver code cannot handle
97  * such tree conflicts at present.
98  *
99  * The whole drive occurs as one single wc.db transaction.  At the end
100  * of the transaction the destination NODES table should have a WORKING
101  * layer that is equivalent to the WORKING layer found in the copied victim
102  * tree, and there should be workqueue items to make any required changes
103  * to working files/directories in the move destination, and there should be
104  * tree-conflicts in the move destination where it was not possible to
105  * update the working files/directories.
106  *
107  * -- Updating local adds --
108  *
109  * When an update (or switch) adds a directory tree it creates corresponding
110  * nodes in the BASE tree. Any existing locally added nodes are bumped to a
111  * higher layer with the top-most locally added directory as op-root.
112  * In-between, the update inserts a base-deleted layer, i.e. it schedules the
113  * directory in the BASE tree for removal upon the next commit, to be replaced
114  * by the locally added directory.
115  *
116  * The driver sees two NODES trees: The BASE layer, and the WORKING layer
117  * which represents the locally added tree.
118  * The driver will compare the two NODES trees and drive an editor to
119  * merge WORKING tree nodes with the nodes in the BASE tree.
120  *
121  * The whole drive occurs as one single wc.db transaction.
122  * Directories which exist in both trees become part of the BASE tree, with
123  * properties merged.
124  * Files which exist in both trees are merged (there is no common ancestor,
125  * so the common ancestor in this merge is the empty file).
126  * Files and directories which exist only in the WORKING layer become
127  * local-add op-roots of their own.
128  * Mismatching node kinds produce new 'incoming add vs local add upon update'
129  * tree conflicts which must be resolved individually later on.
130  */
131 
132 #define SVN_WC__I_AM_WC_DB
133 
134 #include <assert.h>
135 
136 #include "svn_checksum.h"
137 #include "svn_dirent_uri.h"
138 #include "svn_error.h"
139 #include "svn_hash.h"
140 #include "svn_wc.h"
141 #include "svn_props.h"
142 #include "svn_pools.h"
143 #include "svn_sorts.h"
144 
145 #include "private/svn_skel.h"
146 #include "private/svn_sorts_private.h"
147 #include "private/svn_sqlite.h"
148 #include "private/svn_wc_private.h"
149 
150 #include "wc.h"
151 #include "props.h"
152 #include "wc_db_private.h"
153 #include "wc-queries.h"
154 #include "conflicts.h"
155 #include "workqueue.h"
156 #include "token-map.h"
157 
158 /* Helper functions */
159 /* Return the absolute path, in local path style, of LOCAL_RELPATH
160    in WCROOT.  */
161 static const char *
path_for_error_message(const svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool)162 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
163                        const char *local_relpath,
164                        apr_pool_t *result_pool)
165 {
166   const char *local_abspath
167     = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
168 
169   return svn_dirent_local_style(local_abspath, result_pool);
170 }
171 
172 /* Ensure that there is a working copy lock for LOCAL_RELPATH in WCROOT */
173 static svn_error_t *
verify_write_lock(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)174 verify_write_lock(svn_wc__db_wcroot_t *wcroot,
175                   const char *local_relpath,
176                   apr_pool_t *scratch_pool)
177 {
178   svn_boolean_t locked;
179 
180   SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, local_relpath,
181                                                FALSE, scratch_pool));
182   if (!locked)
183     {
184       return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
185                                _("No write-lock in '%s'"),
186                                path_for_error_message(wcroot, local_relpath,
187                                                       scratch_pool));
188     }
189 
190   return SVN_NO_ERROR;
191 }
192 
193 /* In our merge conflicts we record the move_op_src path, which is essentially
194    the depth at which what was moved is marked deleted. The problem is that
195    this depth is not guaranteed to be stable, because somebody might just
196    remove another ancestor, or revert one.
197 
198    To work around this problem we locate the layer below this path, and use
199    that to pinpoint whatever is moved.
200 
201    For a path SRC_RELPATH that was deleted by an operation rooted at
202    DELETE_OP_DEPTH find the op-depth at which the node was originally added.
203    */
204 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)205 find_src_op_depth(int *src_op_depth,
206                   svn_wc__db_wcroot_t *wcroot,
207                   const char *src_relpath,
208                   int delete_op_depth,
209                   apr_pool_t *scratch_pool)
210 {
211   svn_sqlite__stmt_t *stmt;
212   svn_boolean_t have_row;
213 
214   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
215                                     STMT_SELECT_HIGHEST_WORKING_NODE));
216   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
217                             src_relpath, delete_op_depth));
218 
219   SVN_ERR(svn_sqlite__step(&have_row, stmt));
220   if (have_row)
221     *src_op_depth = svn_sqlite__column_int(stmt, 0);
222   SVN_ERR(svn_sqlite__reset(stmt));
223   if (!have_row)
224     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
225                               _("'%s' is not deleted"),
226                               path_for_error_message(wcroot, src_relpath,
227                                                     scratch_pool));
228 
229   return SVN_NO_ERROR;
230 }
231 
232 /*
233  * Receiver code.
234  *
235  * The receiver is an editor that, when driven with a certain change, will
236  * merge the edits into the working/actual state of the move destination
237  * at MOVE_ROOT_DST_RELPATH (in struct tc_editor_baton), perhaps raising
238  * conflicts if necessary.
239  *
240  * The receiver should not need to refer directly to the move source, as
241  * the driver should provide all relevant information about the change to
242  * be made at the move destination.
243  */
244 
245 typedef struct update_move_baton_t {
246   svn_wc__db_t *db;
247   svn_wc__db_wcroot_t *wcroot;
248 
249   int src_op_depth;
250   int dst_op_depth;
251 
252   svn_wc_operation_t operation;
253   svn_wc_conflict_version_t *old_version;
254   svn_wc_conflict_version_t *new_version;
255 
256   svn_cancel_func_t cancel_func;
257   void *cancel_baton;
258 } update_move_baton_t;
259 
260 /* Per node flags for tree conflict collection */
261 typedef struct node_move_baton_t
262 {
263   svn_boolean_t skip;
264   svn_boolean_t shadowed;
265   svn_boolean_t edited;
266 
267   const char *src_relpath;
268   const char *dst_relpath;
269 
270   update_move_baton_t *umb;
271   struct node_move_baton_t *pb;
272 } node_move_baton_t;
273 
274 /*
275  * Notifications are delayed until the entire update-move transaction
276  * completes. These functions provide the necessary support by storing
277  * notification information in a temporary db table (the "update_move_list")
278  * and spooling notifications out of that table after the transaction.
279  */
280 
281 /* Add an entry to the notification list, and at the same time install
282    a conflict and/or work items. */
283 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)284 update_move_list_add(svn_wc__db_wcroot_t *wcroot,
285                      const char *local_relpath,
286                      svn_wc__db_t *db,
287                      svn_wc_notify_action_t action,
288                      svn_node_kind_t kind,
289                      svn_wc_notify_state_t content_state,
290                      svn_wc_notify_state_t prop_state,
291                      svn_skel_t *conflict,
292                      svn_skel_t *work_item,
293                      apr_pool_t *scratch_pool)
294 {
295   svn_sqlite__stmt_t *stmt;
296 
297   if (conflict)
298     {
299       svn_boolean_t tree_conflict;
300 
301       SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
302                                          &tree_conflict,
303                                          db, wcroot->abspath, conflict,
304                                          scratch_pool, scratch_pool));
305       if (tree_conflict)
306         {
307           action = svn_wc_notify_tree_conflict;
308           content_state = svn_wc_notify_state_inapplicable;
309           prop_state = svn_wc_notify_state_inapplicable;
310         }
311     }
312 
313   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
314                                     STMT_INSERT_UPDATE_MOVE_LIST));
315   SVN_ERR(svn_sqlite__bindf(stmt, "sdtdd", local_relpath,
316                             action, kind_map_none, kind,
317                             content_state, prop_state));
318   SVN_ERR(svn_sqlite__step_done(stmt));
319 
320   if (conflict)
321     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath, conflict,
322                                               scratch_pool));
323 
324   if (work_item)
325     SVN_ERR(svn_wc__db_wq_add_internal(wcroot, work_item, scratch_pool));
326 
327   return SVN_NO_ERROR;
328 }
329 
330 /* Send all notifications stored in the notification list, and then
331  * remove the temporary database table. */
332 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)333 svn_wc__db_update_move_list_notify(svn_wc__db_wcroot_t *wcroot,
334                                    svn_revnum_t old_revision,
335                                    svn_revnum_t new_revision,
336                                    svn_wc_notify_func2_t notify_func,
337                                    void *notify_baton,
338                                    apr_pool_t *scratch_pool)
339 {
340   svn_sqlite__stmt_t *stmt;
341 
342   if (notify_func)
343     {
344       apr_pool_t *iterpool;
345       svn_boolean_t have_row;
346 
347       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
348                                         STMT_SELECT_UPDATE_MOVE_LIST));
349       SVN_ERR(svn_sqlite__step(&have_row, stmt));
350 
351       iterpool = svn_pool_create(scratch_pool);
352       while (have_row)
353         {
354           const char *local_relpath;
355           svn_wc_notify_action_t action;
356           svn_wc_notify_t *notify;
357 
358           svn_pool_clear(iterpool);
359 
360           local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
361           action = svn_sqlite__column_int(stmt, 1);
362           notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
363                                                         local_relpath,
364                                                         iterpool),
365                                         action, iterpool);
366           notify->kind = svn_sqlite__column_token(stmt, 2, kind_map_none);
367           notify->content_state = svn_sqlite__column_int(stmt, 3);
368           notify->prop_state = svn_sqlite__column_int(stmt, 4);
369           notify->old_revision = old_revision;
370           notify->revision = new_revision;
371           notify_func(notify_baton, notify, scratch_pool);
372 
373           SVN_ERR(svn_sqlite__step(&have_row, stmt));
374         }
375       svn_pool_destroy(iterpool);
376       SVN_ERR(svn_sqlite__reset(stmt));
377     }
378 
379   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
380                                     STMT_FINALIZE_UPDATE_MOVE));
381   SVN_ERR(svn_sqlite__step_done(stmt));
382 
383   return SVN_NO_ERROR;
384 }
385 
386 /* Create a tree-conflict for recording on LOCAL_RELPATH if such
387    a tree-conflict does not already exist. */
388 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)389 create_tree_conflict(svn_skel_t **conflict_p,
390                      svn_wc__db_wcroot_t *wcroot,
391                      const char *local_relpath,
392                      const char *dst_op_root_relpath,
393                      svn_wc__db_t *db,
394                      const svn_wc_conflict_version_t *old_version,
395                      const svn_wc_conflict_version_t *new_version,
396                      svn_wc_operation_t operation,
397                      svn_node_kind_t old_kind,
398                      svn_node_kind_t new_kind,
399                      const char *old_repos_relpath,
400                      svn_wc_conflict_reason_t reason,
401                      svn_wc_conflict_action_t action,
402                      const char *move_src_op_root_relpath,
403                      apr_pool_t *result_pool,
404                      apr_pool_t *scratch_pool)
405 {
406   svn_error_t *err;
407   svn_skel_t *conflict;
408   svn_wc_conflict_version_t *conflict_old_version, *conflict_new_version;
409   const char *move_src_op_root_abspath
410     = move_src_op_root_relpath
411     ? svn_dirent_join(wcroot->abspath,
412                       move_src_op_root_relpath, scratch_pool)
413     : NULL;
414   const char *move_dst_op_root_abspath
415     = dst_op_root_relpath
416     ? svn_dirent_join(wcroot->abspath,
417                       dst_op_root_relpath, scratch_pool)
418     : NULL;
419   const char *old_repos_relpath_part
420     = old_repos_relpath && old_version
421     ? svn_relpath_skip_ancestor(old_version->path_in_repos,
422                                 old_repos_relpath)
423     : NULL;
424   const char *new_repos_relpath
425     = old_repos_relpath_part
426     ? svn_relpath_join(new_version->path_in_repos, old_repos_relpath_part,
427                        scratch_pool)
428     : NULL;
429 
430   if (!new_repos_relpath)
431     {
432       const char *child_relpath = svn_relpath_skip_ancestor(
433                                             dst_op_root_relpath,
434                                             local_relpath);
435       SVN_ERR_ASSERT(child_relpath != NULL);
436       new_repos_relpath = svn_relpath_join(new_version->path_in_repos,
437                                            child_relpath, scratch_pool);
438     }
439 
440   err = svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
441                                           wcroot, local_relpath,
442                                           result_pool, scratch_pool);
443   if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
444     return svn_error_trace(err);
445   else if (err)
446     {
447       svn_error_clear(err);
448       conflict = NULL;
449     }
450 
451   if (conflict)
452     {
453       svn_wc_operation_t conflict_operation;
454       svn_boolean_t tree_conflicted;
455 
456       SVN_ERR(svn_wc__conflict_read_info(&conflict_operation, NULL, NULL, NULL,
457                                          &tree_conflicted,
458                                          db, wcroot->abspath, conflict,
459                                          scratch_pool, scratch_pool));
460 
461       if (conflict_operation != svn_wc_operation_update
462           && conflict_operation != svn_wc_operation_switch)
463         return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
464                                  _("'%s' already in conflict"),
465                                  path_for_error_message(wcroot, local_relpath,
466                                                         scratch_pool));
467 
468       if (tree_conflicted)
469         {
470           svn_wc_conflict_reason_t existing_reason;
471           svn_wc_conflict_action_t existing_action;
472           const char *existing_abspath;
473 
474           SVN_ERR(svn_wc__conflict_read_tree_conflict(&existing_reason,
475                                                       &existing_action,
476                                                       &existing_abspath, NULL,
477                                                       db, wcroot->abspath,
478                                                       conflict,
479                                                       scratch_pool,
480                                                       scratch_pool));
481           if (reason != existing_reason
482               || action != existing_action
483               || (reason == svn_wc_conflict_reason_moved_away
484                   && strcmp(move_src_op_root_relpath,
485                             svn_dirent_skip_ancestor(wcroot->abspath,
486                                                      existing_abspath))))
487             return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
488                                      _("'%s' already in conflict"),
489                                      path_for_error_message(wcroot,
490                                                             local_relpath,
491                                                             scratch_pool));
492 
493           /* Already a suitable tree-conflict. */
494           *conflict_p = conflict;
495           return SVN_NO_ERROR;
496         }
497     }
498   else
499     conflict = svn_wc__conflict_skel_create(result_pool);
500 
501   SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
502                      conflict, db,
503                      svn_dirent_join(wcroot->abspath, local_relpath,
504                                      scratch_pool),
505                      reason,
506                      action,
507                      move_src_op_root_abspath,
508                      move_dst_op_root_abspath,
509                      result_pool,
510                      scratch_pool));
511 
512   if (old_version)
513     conflict_old_version = svn_wc_conflict_version_create2(
514                                  old_version->repos_url,
515                                  old_version->repos_uuid,
516                                  old_repos_relpath, old_version->peg_rev,
517                                  old_kind, scratch_pool);
518   else
519     conflict_old_version = NULL;
520 
521   conflict_new_version = svn_wc_conflict_version_create2(
522                            new_version->repos_url, new_version->repos_uuid,
523                            new_repos_relpath, new_version->peg_rev,
524                            new_kind, scratch_pool);
525 
526   if (operation == svn_wc_operation_update)
527     {
528       SVN_ERR(svn_wc__conflict_skel_set_op_update(
529                 conflict, conflict_old_version, conflict_new_version,
530                 result_pool, scratch_pool));
531     }
532   else
533     {
534       assert(operation == svn_wc_operation_switch);
535       SVN_ERR(svn_wc__conflict_skel_set_op_switch(
536                   conflict, conflict_old_version, conflict_new_version,
537                   result_pool, scratch_pool));
538     }
539 
540   *conflict_p = conflict;
541   return SVN_NO_ERROR;
542 }
543 
544 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)545 create_node_tree_conflict(svn_skel_t **conflict_p,
546                           node_move_baton_t *nmb,
547                           const char *dst_local_relpath,
548                           svn_node_kind_t old_kind,
549                           svn_node_kind_t new_kind,
550                           svn_wc_conflict_reason_t reason,
551                           svn_wc_conflict_action_t action,
552                           const char *move_src_op_root_relpath,
553                           apr_pool_t *result_pool,
554                           apr_pool_t *scratch_pool)
555 {
556   update_move_baton_t *umb = nmb->umb;
557   const char *dst_repos_relpath;
558   const char *dst_root_relpath = svn_relpath_prefix(nmb->dst_relpath,
559                                                     umb->dst_op_depth,
560                                                     scratch_pool);
561 
562   dst_repos_relpath =
563             svn_relpath_join(nmb->umb->old_version->path_in_repos,
564                              svn_relpath_skip_ancestor(dst_root_relpath,
565                                                        nmb->dst_relpath),
566                              scratch_pool);
567 
568   return svn_error_trace(
569             create_tree_conflict(conflict_p, umb->wcroot, dst_local_relpath,
570                                  svn_relpath_prefix(dst_local_relpath,
571                                                     umb->dst_op_depth,
572                                                     scratch_pool),
573                                  umb->db,
574                                  umb->old_version, umb->new_version,
575                                  umb->operation, old_kind, new_kind,
576                                  dst_repos_relpath,
577                                  reason, action, move_src_op_root_relpath,
578                                  result_pool, scratch_pool));
579 }
580 
581 /* Checks if a specific local path is shadowed as seen from the move root.
582    Helper for update_moved_away_node() */
583 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)584 check_node_shadowed(svn_boolean_t *shadowed,
585                     svn_wc__db_wcroot_t *wcroot,
586                     const char *local_relpath,
587                     int move_root_dst_op_depth,
588                     apr_pool_t *scratch_pool)
589 {
590   svn_sqlite__stmt_t *stmt;
591   svn_boolean_t have_row;
592 
593   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
594                                     STMT_SELECT_WORKING_NODE));
595   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
596 
597   SVN_ERR(svn_sqlite__step(&have_row, stmt));
598 
599   if (have_row)
600     {
601       int op_depth = svn_sqlite__column_int(stmt, 0);
602 
603       *shadowed = (op_depth > move_root_dst_op_depth);
604     }
605   else
606     *shadowed = FALSE;
607   SVN_ERR(svn_sqlite__reset(stmt));
608 
609   return SVN_NO_ERROR;
610 }
611 
612 /* Set a tree conflict for the shadowed node LOCAL_RELPATH, which is
613    the ROOT OF THE OBSTRUCTION if such a tree-conflict does not
614    already exist.  KIND is the kind of the incoming LOCAL_RELPATH. */
615 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)616 mark_tc_on_op_root(node_move_baton_t *nmb,
617                    svn_node_kind_t old_kind,
618                    svn_node_kind_t new_kind,
619                    svn_wc_conflict_action_t action,
620                    apr_pool_t *scratch_pool)
621 {
622   update_move_baton_t *b = nmb->umb;
623   const char *move_dst_relpath;
624   svn_skel_t *conflict;
625 
626   SVN_ERR_ASSERT(nmb->shadowed && !nmb->pb->shadowed);
627 
628   nmb->skip = TRUE;
629 
630   if (old_kind == svn_node_none)
631     move_dst_relpath = NULL;
632   else
633     SVN_ERR(svn_wc__db_scan_moved_to_internal(NULL, &move_dst_relpath, NULL,
634                                               b->wcroot, nmb->dst_relpath,
635                                               b->dst_op_depth,
636                                               scratch_pool, scratch_pool));
637 
638   SVN_ERR(create_node_tree_conflict(&conflict, nmb, nmb->dst_relpath,
639                                     old_kind, new_kind,
640                                     (move_dst_relpath
641                                      ? svn_wc_conflict_reason_moved_away
642                                      : svn_wc_conflict_reason_deleted),
643                                     action, move_dst_relpath
644                                               ? nmb->dst_relpath
645                                               : NULL,
646                                     scratch_pool, scratch_pool));
647 
648   SVN_ERR(update_move_list_add(b->wcroot, nmb->dst_relpath, b->db,
649                                svn_wc_notify_tree_conflict,
650                                new_kind,
651                                svn_wc_notify_state_inapplicable,
652                                svn_wc_notify_state_inapplicable,
653                                conflict, NULL, scratch_pool));
654 
655   return SVN_NO_ERROR;
656 }
657 
658 static svn_error_t *
mark_node_edited(node_move_baton_t * nmb,apr_pool_t * scratch_pool)659 mark_node_edited(node_move_baton_t *nmb,
660                  apr_pool_t *scratch_pool)
661 {
662   if (nmb->edited)
663     return SVN_NO_ERROR;
664 
665   if (nmb->pb)
666     {
667       SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
668 
669       if (nmb->pb->skip)
670         nmb->skip = TRUE;
671     }
672 
673   nmb->edited = TRUE;
674 
675   if (nmb->skip)
676     return SVN_NO_ERROR;
677 
678   if (nmb->shadowed && !(nmb->pb && nmb->pb->shadowed))
679     {
680       svn_node_kind_t dst_kind, src_kind;
681 
682       SVN_ERR(svn_wc__db_depth_get_info(NULL, &dst_kind, NULL,
683                                         NULL, NULL, NULL, NULL,
684                                         NULL, NULL, NULL, NULL, NULL, NULL,
685                                         nmb->umb->wcroot, nmb->dst_relpath,
686                                         nmb->umb->dst_op_depth,
687                                         scratch_pool, scratch_pool));
688 
689       SVN_ERR(svn_wc__db_depth_get_info(NULL, &src_kind, NULL, NULL,
690                                         NULL, NULL, NULL,
691                                         NULL, NULL, NULL, NULL, NULL, NULL,
692                                         nmb->umb->wcroot, nmb->src_relpath,
693                                         nmb->umb->src_op_depth,
694                                         scratch_pool, scratch_pool));
695 
696       SVN_ERR(mark_tc_on_op_root(nmb,
697                                  dst_kind, src_kind,
698                                  svn_wc_conflict_action_edit,
699                                  scratch_pool));
700     }
701 
702   return SVN_NO_ERROR;
703 }
704 
705 static svn_error_t *
mark_parent_edited(node_move_baton_t * nmb,apr_pool_t * scratch_pool)706 mark_parent_edited(node_move_baton_t *nmb,
707                  apr_pool_t *scratch_pool)
708 {
709   SVN_ERR_ASSERT(nmb && nmb->pb);
710 
711   SVN_ERR(mark_node_edited(nmb->pb, scratch_pool));
712 
713   if (nmb->pb->skip)
714     nmb->skip = TRUE;
715 
716   return SVN_NO_ERROR;
717 }
718 
719 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)720 tc_editor_add_directory(node_move_baton_t *nmb,
721                         const char *relpath,
722                         svn_node_kind_t old_kind,
723                         apr_hash_t *props,
724                         apr_pool_t *scratch_pool)
725 {
726   update_move_baton_t *b = nmb->umb;
727   const char *local_abspath;
728   svn_node_kind_t wc_kind;
729   svn_skel_t *work_item = NULL;
730   svn_skel_t *conflict = NULL;
731   svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
732 
733   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
734   if (nmb->skip)
735     return SVN_NO_ERROR;
736 
737   if (nmb->shadowed)
738     {
739       svn_wc__db_status_t status;
740 
741       SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL,
742                                             NULL, NULL, NULL, NULL, NULL, NULL,
743                                             NULL, NULL, NULL, NULL, NULL, NULL,
744                                             NULL, NULL, NULL, NULL, NULL, NULL,
745                                             NULL, NULL, NULL,
746                                             b->wcroot, relpath,
747                                             scratch_pool, scratch_pool));
748 
749       if (status == svn_wc__db_status_deleted)
750         reason = svn_wc_conflict_reason_deleted;
751       else if (status != svn_wc__db_status_added)
752         wc_kind = svn_node_none;
753       else if (old_kind == svn_node_none)
754         reason = svn_wc_conflict_reason_added;
755       else
756         reason = svn_wc_conflict_reason_replaced;
757     }
758   else
759     wc_kind = svn_node_none;
760 
761   local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
762 
763   if (wc_kind == svn_node_none)
764     {
765       /* Check for unversioned tree-conflict */
766       SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
767     }
768 
769   if (!nmb->shadowed && wc_kind == old_kind)
770     wc_kind = svn_node_none; /* Node will be gone once we install */
771 
772   if (wc_kind != svn_node_none
773       && (nmb->shadowed || wc_kind != old_kind)) /* replace */
774     {
775       SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
776                                         old_kind, svn_node_dir,
777                                         reason,
778                                         (old_kind == svn_node_none)
779                                           ? svn_wc_conflict_action_add
780                                           : svn_wc_conflict_action_replace,
781                                         NULL,
782                                         scratch_pool, scratch_pool));
783       nmb->skip = TRUE;
784     }
785   else
786     {
787       SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, local_abspath,
788                                            scratch_pool, scratch_pool));
789     }
790 
791   SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
792                                (old_kind == svn_node_none)
793                                   ? svn_wc_notify_update_add
794                                   : svn_wc_notify_update_replace,
795                                svn_node_dir,
796                                svn_wc_notify_state_inapplicable,
797                                svn_wc_notify_state_inapplicable,
798                                conflict, work_item, scratch_pool));
799   return SVN_NO_ERROR;
800 }
801 
802 static svn_error_t *
copy_working_node(const char * src_relpath,const char * dst_relpath,svn_wc__db_wcroot_t * wcroot,apr_pool_t * scratch_pool)803 copy_working_node(const char *src_relpath,
804                   const char *dst_relpath,
805                   svn_wc__db_wcroot_t *wcroot,
806                   apr_pool_t *scratch_pool)
807 {
808   svn_sqlite__stmt_t *stmt;
809   svn_boolean_t have_row;
810   const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
811                                                        scratch_pool);
812 
813   /* Add a WORKING row for the new node, based on the source. */
814   SVN_ERR(svn_sqlite__get_statement(&stmt,wcroot->sdb,
815                                     STMT_INSERT_WORKING_NODE_COPY_FROM));
816   SVN_ERR(svn_sqlite__bindf(stmt, "issdst", wcroot->wc_id, src_relpath,
817                             dst_relpath, relpath_depth(dst_relpath),
818                             dst_parent_relpath, presence_map,
819                             svn_wc__db_status_normal));
820   SVN_ERR(svn_sqlite__step_done(stmt));
821 
822   /* Copy properties over.  ### This loses changelist association. */
823   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
824                                     STMT_SELECT_ACTUAL_NODE));
825   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, src_relpath));
826   SVN_ERR(svn_sqlite__step(&have_row, stmt));
827   if (have_row)
828     {
829       apr_size_t props_size;
830       const char *properties;
831 
832       properties = svn_sqlite__column_blob(stmt, 1, &props_size,
833                                            scratch_pool);
834       SVN_ERR(svn_sqlite__reset(stmt));
835       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
836                                         STMT_INSERT_ACTUAL_NODE));
837       SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
838                                 wcroot->wc_id, dst_relpath,
839                                 svn_relpath_dirname(dst_relpath,
840                                                     scratch_pool),
841                                 properties, props_size, NULL));
842       SVN_ERR(svn_sqlite__step(&have_row, stmt));
843     }
844   SVN_ERR(svn_sqlite__reset(stmt));
845 
846   return SVN_NO_ERROR;
847 }
848 
849 static svn_error_t *
tc_editor_incoming_add_directory(node_move_baton_t * nmb,const char * dst_relpath,svn_node_kind_t old_kind,apr_hash_t * props,const char * src_relpath,apr_pool_t * scratch_pool)850 tc_editor_incoming_add_directory(node_move_baton_t *nmb,
851                                  const char *dst_relpath,
852                                  svn_node_kind_t old_kind,
853                                  apr_hash_t *props,
854                                  const char *src_relpath,
855                                  apr_pool_t *scratch_pool)
856 {
857   update_move_baton_t *b = nmb->umb;
858   const char *dst_abspath;
859   svn_node_kind_t wc_kind;
860   svn_skel_t *work_item = NULL;
861   svn_skel_t *conflict = NULL;
862   svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
863 
864   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
865   if (nmb->skip)
866     return SVN_NO_ERROR;
867 
868   dst_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool);
869 
870   /* Check for unversioned tree-conflict */
871   SVN_ERR(svn_io_check_path(dst_abspath, &wc_kind, scratch_pool));
872 
873   if (wc_kind == old_kind)
874     wc_kind = svn_node_none; /* Node will be gone once we install */
875 
876   if (wc_kind != svn_node_none && wc_kind != old_kind) /* replace */
877     {
878       SVN_ERR(create_node_tree_conflict(&conflict, nmb, dst_relpath,
879                                         old_kind, svn_node_dir,
880                                         reason,
881                                         (old_kind == svn_node_none)
882                                           ? svn_wc_conflict_action_add
883                                           : svn_wc_conflict_action_replace,
884                                         NULL,
885                                         scratch_pool, scratch_pool));
886       nmb->skip = TRUE;
887     }
888   else
889     {
890       SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
891                                 scratch_pool));
892       SVN_ERR(svn_wc__wq_build_dir_install(&work_item, b->db, dst_abspath,
893                                            scratch_pool, scratch_pool));
894     }
895 
896   SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
897                                (old_kind == svn_node_none)
898                                   ? svn_wc_notify_update_add
899                                   : svn_wc_notify_update_replace,
900                                svn_node_dir,
901                                svn_wc_notify_state_inapplicable,
902                                svn_wc_notify_state_inapplicable,
903                                conflict, work_item, scratch_pool));
904   return SVN_NO_ERROR;
905 }
906 
907 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)908 tc_editor_add_file(node_move_baton_t *nmb,
909                    const char *relpath,
910                    svn_node_kind_t old_kind,
911                    const svn_checksum_t *checksum,
912                    apr_hash_t *props,
913                    apr_pool_t *scratch_pool)
914 {
915   update_move_baton_t *b = nmb->umb;
916   svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
917   svn_node_kind_t wc_kind;
918   const char *local_abspath;
919   svn_skel_t *work_item = NULL;
920   svn_skel_t *conflict = NULL;
921 
922   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
923   if (nmb->skip)
924     return SVN_NO_ERROR;
925 
926   if (nmb->shadowed)
927     {
928       svn_wc__db_status_t status;
929 
930       SVN_ERR(svn_wc__db_read_info_internal(&status, &wc_kind, NULL, NULL,
931                                             NULL, NULL, NULL, NULL, NULL, NULL,
932                                             NULL, NULL, NULL, NULL, NULL, NULL,
933                                             NULL, NULL, NULL, NULL, NULL, NULL,
934                                             NULL, NULL, NULL,
935                                             b->wcroot, relpath,
936                                             scratch_pool, scratch_pool));
937 
938       if (status == svn_wc__db_status_deleted)
939         reason = svn_wc_conflict_reason_deleted;
940       else if (status != svn_wc__db_status_added)
941         wc_kind = svn_node_none;
942       else if (old_kind == svn_node_none)
943         reason = svn_wc_conflict_reason_added;
944       else
945         reason = svn_wc_conflict_reason_replaced;
946     }
947   else
948     wc_kind = svn_node_none;
949 
950   local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
951 
952   if (wc_kind == svn_node_none)
953     {
954       /* Check for unversioned tree-conflict */
955       SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
956     }
957 
958   if (wc_kind != svn_node_none
959       && (nmb->shadowed || wc_kind != old_kind)) /* replace */
960     {
961       SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
962                                         old_kind, svn_node_file,
963                                         reason,
964                                         (old_kind == svn_node_none)
965                                           ? svn_wc_conflict_action_add
966                                           : svn_wc_conflict_action_replace,
967                                         NULL,
968                                         scratch_pool, scratch_pool));
969       nmb->skip = TRUE;
970     }
971   else
972     {
973       /* Update working file. */
974       SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
975                                             svn_dirent_join(b->wcroot->abspath,
976                                                             relpath,
977                                                             scratch_pool),
978                                             NULL,
979                                             FALSE /*FIXME: use_commit_times?*/,
980                                             TRUE  /* record_file_info */,
981                                             scratch_pool, scratch_pool));
982     }
983 
984   SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
985                                (old_kind == svn_node_none)
986                                   ? svn_wc_notify_update_add
987                                   : svn_wc_notify_update_replace,
988                                svn_node_file,
989                                svn_wc_notify_state_inapplicable,
990                                svn_wc_notify_state_inapplicable,
991                                conflict, work_item, scratch_pool));
992   return SVN_NO_ERROR;
993 }
994 
995 static svn_error_t *
tc_editor_incoming_add_file(node_move_baton_t * nmb,const char * dst_relpath,svn_node_kind_t old_kind,const svn_checksum_t * checksum,apr_hash_t * props,const char * src_relpath,const char * content_abspath,apr_pool_t * scratch_pool)996 tc_editor_incoming_add_file(node_move_baton_t *nmb,
997                             const char *dst_relpath,
998                             svn_node_kind_t old_kind,
999                             const svn_checksum_t *checksum,
1000                             apr_hash_t *props,
1001                             const char *src_relpath,
1002                             const char *content_abspath,
1003                             apr_pool_t *scratch_pool)
1004 {
1005   update_move_baton_t *b = nmb->umb;
1006   svn_wc_conflict_reason_t reason = svn_wc_conflict_reason_unversioned;
1007   svn_node_kind_t wc_kind;
1008   const char *dst_abspath;
1009   svn_skel_t *work_items = NULL;
1010   svn_skel_t *work_item = NULL;
1011   svn_skel_t *conflict = NULL;
1012 
1013   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1014   if (nmb->skip)
1015     {
1016       SVN_ERR(svn_io_remove_file2(content_abspath, TRUE, scratch_pool));
1017       return SVN_NO_ERROR;
1018     }
1019 
1020   dst_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath, scratch_pool);
1021 
1022   /* Check for unversioned tree-conflict */
1023   SVN_ERR(svn_io_check_path(dst_abspath, &wc_kind, scratch_pool));
1024 
1025   if (wc_kind != svn_node_none && wc_kind != old_kind) /* replace */
1026     {
1027       SVN_ERR(create_node_tree_conflict(&conflict, nmb, dst_relpath,
1028                                         old_kind, svn_node_file,
1029                                         reason,
1030                                         (old_kind == svn_node_none)
1031                                           ? svn_wc_conflict_action_add
1032                                           : svn_wc_conflict_action_replace,
1033                                         NULL,
1034                                         scratch_pool, scratch_pool));
1035       nmb->skip = TRUE;
1036       SVN_ERR(svn_io_remove_file2(content_abspath, TRUE, scratch_pool));
1037     }
1038   else
1039     {
1040       const char *src_abspath;
1041 
1042       SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
1043                                 scratch_pool));
1044 
1045       /* Update working file. */
1046       src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1047                                     scratch_pool);
1048       SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db, dst_abspath,
1049                                             src_abspath,
1050                                             FALSE /* FIXME: use_commit_times?*/,
1051                                             TRUE  /* record_file_info */,
1052                                             scratch_pool, scratch_pool));
1053       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1054 
1055       /* Queue removal of temporary content copy. */
1056       SVN_ERR(svn_wc__wq_build_file_remove(&work_item, b->db,
1057                                            b->wcroot->abspath, src_abspath,
1058                                            scratch_pool, scratch_pool));
1059 
1060       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1061     }
1062 
1063   SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1064                                (old_kind == svn_node_none)
1065                                   ? svn_wc_notify_update_add
1066                                   : svn_wc_notify_update_replace,
1067                                svn_node_file,
1068                                svn_wc_notify_state_inapplicable,
1069                                svn_wc_notify_state_inapplicable,
1070                                conflict, work_items, scratch_pool));
1071   return SVN_NO_ERROR;
1072 }
1073 
1074 /* All the info we need about one version of a working node. */
1075 typedef struct working_node_version_t
1076 {
1077   svn_wc_conflict_version_t *location_and_kind;
1078   apr_hash_t *props;
1079   const svn_checksum_t *checksum; /* for files only */
1080 } working_node_version_t;
1081 
1082 /* Return *WORK_ITEMS to create a conflict on LOCAL_ABSPATH. */
1083 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)1084 create_conflict_markers(svn_skel_t **work_items,
1085                         const char *local_abspath,
1086                         svn_wc__db_t *db,
1087                         const char *repos_relpath,
1088                         svn_skel_t *conflict_skel,
1089                         svn_wc_operation_t operation,
1090                         const working_node_version_t *old_version,
1091                         const working_node_version_t *new_version,
1092                         svn_node_kind_t kind,
1093                         svn_boolean_t set_operation,
1094                         apr_pool_t *result_pool,
1095                         apr_pool_t *scratch_pool)
1096 {
1097   svn_wc_conflict_version_t *original_version;
1098   svn_wc_conflict_version_t *conflicted_version;
1099   const char *part;
1100 
1101   original_version = svn_wc_conflict_version_dup(
1102                        old_version->location_and_kind, scratch_pool);
1103   original_version->node_kind = kind;
1104   conflicted_version = svn_wc_conflict_version_dup(
1105                          new_version->location_and_kind, scratch_pool);
1106   conflicted_version->node_kind = kind;
1107 
1108   part = svn_relpath_skip_ancestor(original_version->path_in_repos,
1109                                    repos_relpath);
1110   if (part == NULL)
1111     part = svn_relpath_skip_ancestor(conflicted_version->path_in_repos,
1112                                      repos_relpath);
1113   SVN_ERR_ASSERT(part != NULL);
1114 
1115   conflicted_version->path_in_repos
1116     = svn_relpath_join(conflicted_version->path_in_repos, part, scratch_pool);
1117   original_version->path_in_repos = repos_relpath;
1118 
1119   if (set_operation)
1120     {
1121       if (operation == svn_wc_operation_update)
1122         {
1123           SVN_ERR(svn_wc__conflict_skel_set_op_update(
1124                     conflict_skel, original_version,
1125                     conflicted_version,
1126                     scratch_pool, scratch_pool));
1127         }
1128       else if (operation == svn_wc_operation_merge)
1129         {
1130           SVN_ERR(svn_wc__conflict_skel_set_op_merge(
1131                     conflict_skel, original_version,
1132                     conflicted_version,
1133                     scratch_pool, scratch_pool));
1134         }
1135       else
1136         {
1137           SVN_ERR(svn_wc__conflict_skel_set_op_switch(
1138                     conflict_skel, original_version,
1139                     conflicted_version,
1140                     scratch_pool, scratch_pool));
1141         }
1142     }
1143 
1144   /* According to this func's doc string, it is "Currently only used for
1145    * property conflicts as text conflict markers are just in-wc files." */
1146   SVN_ERR(svn_wc__conflict_create_markers(work_items, db,
1147                                           local_abspath,
1148                                           conflict_skel,
1149                                           result_pool,
1150                                           scratch_pool));
1151 
1152   return SVN_NO_ERROR;
1153 }
1154 
1155 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)1156 update_working_props(svn_wc_notify_state_t *prop_state,
1157                      svn_skel_t **conflict_skel,
1158                      apr_array_header_t **propchanges,
1159                      apr_hash_t **actual_props,
1160                      update_move_baton_t *b,
1161                      const char *local_relpath,
1162                      const struct working_node_version_t *old_version,
1163                      const struct working_node_version_t *new_version,
1164                      apr_pool_t *result_pool,
1165                      apr_pool_t *scratch_pool)
1166 {
1167   apr_hash_t *new_actual_props;
1168   apr_array_header_t *new_propchanges;
1169 
1170   /*
1171    * Run a 3-way prop merge to update the props, using the pre-update
1172    * props as the merge base, the post-update props as the
1173    * merge-left version, and the current props of the
1174    * moved-here working file as the merge-right version.
1175    */
1176   SVN_ERR(svn_wc__db_read_props_internal(actual_props,
1177                                          b->wcroot, local_relpath,
1178                                          result_pool, scratch_pool));
1179   SVN_ERR(svn_prop_diffs(propchanges, new_version->props, old_version->props,
1180                          result_pool));
1181   SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state,
1182                               &new_actual_props,
1183                               b->db, svn_dirent_join(b->wcroot->abspath,
1184                                                      local_relpath,
1185                                                      scratch_pool),
1186                               old_version->props, old_version->props,
1187                               *actual_props, *propchanges,
1188                               result_pool, scratch_pool));
1189 
1190   /* Setting properties in ACTUAL_NODE with svn_wc__db_op_set_props_internal
1191      relies on NODES row being updated via a different route .
1192 
1193      This extra property diff makes sure we clear the actual row when
1194      the final result is unchanged properties. */
1195   SVN_ERR(svn_prop_diffs(&new_propchanges, new_actual_props, new_version->props,
1196                          scratch_pool));
1197   if (!new_propchanges->nelts)
1198     new_actual_props = NULL;
1199 
1200   /* Install the new actual props. */
1201   SVN_ERR(svn_wc__db_op_set_props_internal(b->wcroot, local_relpath,
1202                                            new_actual_props,
1203                                            svn_wc__has_magic_property(
1204                                                     *propchanges),
1205                                            scratch_pool));
1206 
1207   return SVN_NO_ERROR;
1208 }
1209 
1210 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)1211 tc_editor_alter_directory(node_move_baton_t *nmb,
1212                           const char *dst_relpath,
1213                           apr_hash_t *old_props,
1214                           apr_hash_t *new_props,
1215                           apr_pool_t *scratch_pool)
1216 {
1217   update_move_baton_t *b = nmb->umb;
1218   working_node_version_t old_version, new_version;
1219   svn_skel_t *work_items = NULL;
1220   svn_skel_t *conflict_skel = NULL;
1221   const char *local_abspath = svn_dirent_join(b->wcroot->abspath, dst_relpath,
1222                                               scratch_pool);
1223   svn_wc_notify_state_t prop_state;
1224   apr_hash_t *actual_props;
1225   apr_array_header_t *propchanges;
1226   svn_node_kind_t wc_kind;
1227   svn_boolean_t obstructed = FALSE;
1228 
1229   SVN_ERR(mark_node_edited(nmb, scratch_pool));
1230   if (nmb->skip)
1231     return SVN_NO_ERROR;
1232 
1233   SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
1234   if (wc_kind != svn_node_none && wc_kind != svn_node_dir)
1235     {
1236       SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1237                                         svn_node_dir, svn_node_dir,
1238                                         svn_wc_conflict_reason_obstructed,
1239                                         svn_wc_conflict_action_edit,
1240                                         NULL,
1241                                         scratch_pool, scratch_pool));
1242       obstructed = TRUE;
1243     }
1244 
1245   old_version.location_and_kind = b->old_version;
1246   new_version.location_and_kind = b->new_version;
1247 
1248   old_version.checksum = NULL; /* not a file */
1249   old_version.props = old_props;
1250   new_version.checksum = NULL; /* not a file */
1251   new_version.props = new_props;
1252 
1253   SVN_ERR(update_working_props(&prop_state, &conflict_skel,
1254                                 &propchanges, &actual_props,
1255                                 b, dst_relpath,
1256                                 &old_version, &new_version,
1257                                 scratch_pool, scratch_pool));
1258 
1259   if (prop_state == svn_wc_notify_state_conflicted)
1260     {
1261       const char *move_dst_repos_relpath;
1262 
1263       SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL,
1264                                         &move_dst_repos_relpath, NULL, NULL,
1265                                         NULL, NULL, NULL, NULL, NULL, NULL,
1266                                         NULL,
1267                                         b->wcroot, dst_relpath,
1268                                         b->dst_op_depth,
1269                                         scratch_pool, scratch_pool));
1270 
1271       SVN_ERR(create_conflict_markers(&work_items, local_abspath,
1272                                       b->db, move_dst_repos_relpath,
1273                                       conflict_skel, b->operation,
1274                                       &old_version, &new_version,
1275                                       svn_node_dir, !obstructed,
1276                                       scratch_pool, scratch_pool));
1277     }
1278 
1279   SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1280                                svn_wc_notify_update_update,
1281                                svn_node_dir,
1282                                svn_wc_notify_state_inapplicable,
1283                                prop_state,
1284                                conflict_skel, work_items, scratch_pool));
1285 
1286   return SVN_NO_ERROR;
1287 }
1288 
1289 /* Edit the file found at the move destination, which is initially at
1290  * the old state.  Merge the changes into the "working"/"actual" file.
1291  *
1292  * Merge the difference between OLD_VERSION and NEW_VERSION into
1293  * the working file at LOCAL_RELPATH.
1294  *
1295  * The term 'old' refers to the pre-update state, which is the state of
1296  * (some layer of) LOCAL_RELPATH while this function runs; and 'new'
1297  * refers to the post-update state, as found at the (base layer of) the
1298  * move source path while this function runs.
1299  *
1300  * LOCAL_RELPATH is a file in the working copy at WCROOT in DB, and
1301  * REPOS_RELPATH is the repository path it would be committed to.
1302  *
1303  * Use NOTIFY_FUNC and NOTIFY_BATON for notifications.
1304  * Set *WORK_ITEMS to any required work items, allocated in RESULT_POOL.
1305  * Use SCRATCH_POOL for temporary allocations. */
1306 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)1307 tc_editor_alter_file(node_move_baton_t *nmb,
1308                      const char *dst_relpath,
1309                      const svn_checksum_t *old_checksum,
1310                      const svn_checksum_t *new_checksum,
1311                      apr_hash_t *old_props,
1312                      apr_hash_t *new_props,
1313                      apr_pool_t *scratch_pool)
1314 {
1315   update_move_baton_t *b = nmb->umb;
1316   working_node_version_t old_version, new_version;
1317   const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
1318                                               dst_relpath,
1319                                               scratch_pool);
1320   const char *old_pristine_abspath;
1321   const char *new_pristine_abspath;
1322   svn_skel_t *conflict_skel = NULL;
1323   apr_hash_t *actual_props;
1324   apr_array_header_t *propchanges;
1325   enum svn_wc_merge_outcome_t merge_outcome;
1326   svn_wc_notify_state_t prop_state, content_state;
1327   svn_skel_t *work_item, *work_items = NULL;
1328   svn_node_kind_t wc_kind;
1329   svn_boolean_t obstructed = FALSE;
1330 
1331   SVN_ERR(mark_node_edited(nmb, scratch_pool));
1332   if (nmb->skip)
1333     return SVN_NO_ERROR;
1334 
1335   SVN_ERR(svn_io_check_path(local_abspath, &wc_kind, scratch_pool));
1336   if (wc_kind != svn_node_none && wc_kind != svn_node_file)
1337     {
1338       SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1339                                         svn_node_file, svn_node_file,
1340                                         svn_wc_conflict_reason_obstructed,
1341                                         svn_wc_conflict_action_edit,
1342                                         NULL,
1343                                         scratch_pool, scratch_pool));
1344       obstructed = TRUE;
1345     }
1346 
1347   old_version.location_and_kind = b->old_version;
1348   new_version.location_and_kind = b->new_version;
1349 
1350   old_version.checksum = old_checksum;
1351   old_version.props = old_props;
1352   new_version.checksum = new_checksum;
1353   new_version.props = new_props;
1354 
1355   /* ### TODO: Only do this when there is no higher WORKING layer */
1356   SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges,
1357                                &actual_props, b, dst_relpath,
1358                                &old_version, &new_version,
1359                                scratch_pool, scratch_pool));
1360 
1361   if (!obstructed
1362       && !svn_checksum_match(new_version.checksum, old_version.checksum))
1363     {
1364       svn_boolean_t is_locally_modified;
1365 
1366       SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
1367                                                b->db, local_abspath,
1368                                                FALSE /* exact_comparison */,
1369                                                scratch_pool));
1370       if (!is_locally_modified)
1371         {
1372           SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1373                                                 local_abspath,
1374                                                 NULL,
1375                                                 FALSE /* FIXME: use_commit_times? */,
1376                                                 TRUE  /* record_file_info */,
1377                                                 scratch_pool, scratch_pool));
1378 
1379           work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1380 
1381           content_state = svn_wc_notify_state_changed;
1382         }
1383       else
1384         {
1385           /*
1386            * Run a 3-way merge to update the file, using the pre-update
1387            * pristine text as the merge base, the post-update pristine
1388            * text as the merge-left version, and the current content of the
1389            * moved-here working file as the merge-right version.
1390            */
1391           SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath,
1392                                                b->db, b->wcroot->abspath,
1393                                                old_version.checksum,
1394                                                scratch_pool, scratch_pool));
1395           SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
1396                                                b->db, b->wcroot->abspath,
1397                                                new_version.checksum,
1398                                                scratch_pool, scratch_pool));
1399           SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
1400                                          &merge_outcome, b->db,
1401                                          old_pristine_abspath,
1402                                          new_pristine_abspath,
1403                                          local_abspath,
1404                                          local_abspath,
1405                                          NULL, NULL, NULL, /* diff labels */
1406                                          actual_props,
1407                                          FALSE, /* dry-run */
1408                                          NULL, /* diff3-cmd */
1409                                          NULL, /* merge options */
1410                                          propchanges,
1411                                          b->cancel_func, b->cancel_baton,
1412                                          scratch_pool, scratch_pool));
1413 
1414           work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1415 
1416           if (merge_outcome == svn_wc_merge_conflict)
1417             content_state = svn_wc_notify_state_conflicted;
1418           else
1419             content_state = svn_wc_notify_state_merged;
1420         }
1421     }
1422   else
1423     content_state = svn_wc_notify_state_unchanged;
1424 
1425   /* If there are any conflicts to be stored, convert them into work items
1426    * too. */
1427   if (conflict_skel)
1428     {
1429       const char *move_dst_repos_relpath;
1430 
1431       SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, NULL,
1432                                         &move_dst_repos_relpath, NULL, NULL,
1433                                         NULL, NULL, NULL, NULL, NULL, NULL,
1434                                         NULL,
1435                                         b->wcroot, dst_relpath,
1436                                         b->dst_op_depth,
1437                                         scratch_pool, scratch_pool));
1438 
1439       SVN_ERR(create_conflict_markers(&work_item, local_abspath, b->db,
1440                                       move_dst_repos_relpath, conflict_skel,
1441                                       b->operation, &old_version, &new_version,
1442                                       svn_node_file, !obstructed,
1443                                       scratch_pool, scratch_pool));
1444 
1445       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1446     }
1447 
1448   SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1449                                svn_wc_notify_update_update,
1450                                svn_node_file,
1451                                content_state,
1452                                prop_state,
1453                                conflict_skel, work_items, scratch_pool));
1454 
1455   return SVN_NO_ERROR;
1456 }
1457 
1458 static svn_error_t *
tc_editor_update_incoming_moved_file(node_move_baton_t * nmb,const char * dst_relpath,const char * src_relpath,const svn_checksum_t * src_checksum,const svn_checksum_t * dst_checksum,apr_hash_t * dst_props,apr_hash_t * src_props,svn_boolean_t do_text_merge,apr_pool_t * scratch_pool)1459 tc_editor_update_incoming_moved_file(node_move_baton_t *nmb,
1460                                      const char *dst_relpath,
1461                                      const char *src_relpath,
1462                                      const svn_checksum_t *src_checksum,
1463                                      const svn_checksum_t *dst_checksum,
1464                                      apr_hash_t *dst_props,
1465                                      apr_hash_t *src_props,
1466                                      svn_boolean_t do_text_merge,
1467                                      apr_pool_t *scratch_pool)
1468 {
1469   update_move_baton_t *b = nmb->umb;
1470   working_node_version_t old_version, new_version;
1471   const char *dst_abspath = svn_dirent_join(b->wcroot->abspath,
1472                                             dst_relpath,
1473                                             scratch_pool);
1474   svn_skel_t *conflict_skel = NULL;
1475   enum svn_wc_merge_outcome_t merge_outcome;
1476   svn_wc_notify_state_t prop_state = svn_wc_notify_state_unchanged;
1477   svn_wc_notify_state_t content_state = svn_wc_notify_state_unchanged;
1478   svn_skel_t *work_item, *work_items = NULL;
1479   svn_node_kind_t dst_kind_on_disk;
1480   const char *dst_repos_relpath;
1481   svn_boolean_t tree_conflict = FALSE;
1482   svn_node_kind_t dst_db_kind;
1483   svn_error_t *err;
1484 
1485   SVN_ERR(mark_node_edited(nmb, scratch_pool));
1486   if (nmb->skip)
1487     return SVN_NO_ERROR;
1488 
1489   err = svn_wc__db_base_get_info_internal(NULL, &dst_db_kind, NULL,
1490                                           &dst_repos_relpath,
1491                                           NULL, NULL, NULL, NULL, NULL, NULL,
1492                                           NULL, NULL, NULL, NULL, NULL,
1493                                           b->wcroot, dst_relpath,
1494                                           scratch_pool, scratch_pool);
1495   if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1496     {
1497       const char *dst_parent_relpath;
1498       const char *dst_parent_repos_relpath;
1499       const char *src_abspath;
1500 
1501       /* If the file cannot be found, it was either deleted at the
1502        * move destination, or it was moved after its parent was moved.
1503        * We cannot deal with this problem right now. Instead, we will
1504        * raise a new tree conflict at the location where this file should
1505        * have been, and let another run of the resolver deal with the
1506        * new conflict later on. */
1507 
1508       svn_error_clear(err);
1509 
1510       /* Create a WORKING node for this file at the move destination. */
1511       SVN_ERR(copy_working_node(src_relpath, dst_relpath, b->wcroot,
1512                                 scratch_pool));
1513 
1514       /* Raise a tree conflict at the new WORKING node. */
1515       dst_db_kind = svn_node_none;
1516       SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1517                                         svn_node_file, dst_db_kind,
1518                                         svn_wc_conflict_reason_edited,
1519                                         svn_wc_conflict_action_delete,
1520                                         NULL, scratch_pool, scratch_pool));
1521       dst_parent_relpath = svn_relpath_dirname(dst_relpath, scratch_pool);
1522       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
1523                                                 &dst_parent_repos_relpath,
1524                                                 NULL, NULL, NULL, NULL, NULL,
1525                                                 NULL, NULL, NULL, NULL, NULL,
1526                                                 NULL, b->wcroot,
1527                                                 dst_parent_relpath,
1528                                                 scratch_pool, scratch_pool));
1529       dst_repos_relpath = svn_relpath_join(dst_parent_repos_relpath,
1530                                            svn_relpath_basename(dst_relpath,
1531                                                                 scratch_pool),
1532                                            scratch_pool);
1533       tree_conflict = TRUE;
1534 
1535       /* Schedule a copy of the victim's file content to the new node's path. */
1536       src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1537                                     scratch_pool);
1538       SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
1539                                             dst_abspath,
1540                                             src_abspath,
1541                                             FALSE /*FIXME: use_commit_times?*/,
1542                                             TRUE  /* record_file_info */,
1543                                             scratch_pool, scratch_pool));
1544       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1545     }
1546   else
1547     SVN_ERR(err);
1548 
1549   if ((dst_db_kind == svn_node_none || dst_db_kind != svn_node_file) &&
1550       conflict_skel == NULL)
1551     {
1552       SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1553                                         svn_node_file, dst_db_kind,
1554                                         dst_db_kind == svn_node_none
1555                                           ? svn_wc_conflict_reason_missing
1556                                           : svn_wc_conflict_reason_obstructed,
1557                                         svn_wc_conflict_action_edit,
1558                                         NULL,
1559                                         scratch_pool, scratch_pool));
1560       tree_conflict = TRUE;
1561     }
1562 
1563   SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind_on_disk, scratch_pool));
1564   if ((dst_kind_on_disk == svn_node_none || dst_kind_on_disk != svn_node_file)
1565       && conflict_skel == NULL)
1566     {
1567       SVN_ERR(create_node_tree_conflict(&conflict_skel, nmb, dst_relpath,
1568                                         svn_node_file, dst_kind_on_disk,
1569                                         dst_kind_on_disk == svn_node_none
1570                                           ? svn_wc_conflict_reason_missing
1571                                           : svn_wc_conflict_reason_obstructed,
1572                                         svn_wc_conflict_action_edit,
1573                                         NULL,
1574                                         scratch_pool, scratch_pool));
1575       tree_conflict = TRUE;
1576     }
1577 
1578   old_version.location_and_kind = b->old_version;
1579   new_version.location_and_kind = b->new_version;
1580 
1581   old_version.checksum = src_checksum;
1582   old_version.props = src_props;
1583   new_version.checksum = dst_checksum;
1584   new_version.props = dst_props;
1585 
1586   /* Merge properties and text content if there is no tree conflict. */
1587   if (conflict_skel == NULL)
1588     {
1589       apr_hash_t *actual_props;
1590       apr_array_header_t *propchanges;
1591 
1592       SVN_ERR(update_working_props(&prop_state, &conflict_skel, &propchanges,
1593                                    &actual_props, b, dst_relpath,
1594                                    &old_version, &new_version,
1595                                    scratch_pool, scratch_pool));
1596       if (do_text_merge)
1597         {
1598           const char *old_pristine_abspath;
1599           const char *src_abspath;
1600           const char *label_left;
1601           const char *label_target;
1602 
1603           /*
1604            * Run a 3-way merge to update the file at its post-move location,
1605            * using the pre-move file's pristine text as the merge base, the
1606            * post-move content as the merge-right version, and the current
1607            * content of the working file at the pre-move location as the
1608            * merge-left version.
1609            */
1610           SVN_ERR(svn_wc__db_pristine_get_path(&old_pristine_abspath,
1611                                                b->db, b->wcroot->abspath,
1612                                                src_checksum,
1613                                                scratch_pool, scratch_pool));
1614           src_abspath = svn_dirent_join(b->wcroot->abspath, src_relpath,
1615                                         scratch_pool);
1616           label_left = apr_psprintf(scratch_pool, ".r%ld",
1617                                     b->old_version->peg_rev);
1618           label_target = apr_psprintf(scratch_pool, ".r%ld",
1619                                       b->new_version->peg_rev);
1620           SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
1621                                          &merge_outcome, b->db,
1622                                          old_pristine_abspath,
1623                                          src_abspath,
1624                                          dst_abspath,
1625                                          dst_abspath,
1626                                          label_left,
1627                                          _(".working"),
1628                                          label_target,
1629                                          actual_props,
1630                                          FALSE, /* dry-run */
1631                                          NULL, /* diff3-cmd */
1632                                          NULL, /* merge options */
1633                                          propchanges,
1634                                          b->cancel_func, b->cancel_baton,
1635                                          scratch_pool, scratch_pool));
1636 
1637           work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1638 
1639           if (merge_outcome == svn_wc_merge_conflict)
1640             content_state = svn_wc_notify_state_conflicted;
1641           else
1642             content_state = svn_wc_notify_state_merged;
1643         }
1644     }
1645 
1646   /* If there are any conflicts to be stored, convert them into work items
1647    * too. */
1648   if (conflict_skel)
1649     {
1650       SVN_ERR(create_conflict_markers(&work_item, dst_abspath, b->db,
1651                                       dst_repos_relpath, conflict_skel,
1652                                       b->operation, &old_version, &new_version,
1653                                       svn_node_file, !tree_conflict,
1654                                       scratch_pool, scratch_pool));
1655 
1656       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1657     }
1658 
1659   SVN_ERR(update_move_list_add(b->wcroot, dst_relpath, b->db,
1660                                svn_wc_notify_update_update,
1661                                svn_node_file,
1662                                content_state,
1663                                prop_state,
1664                                conflict_skel, work_items, scratch_pool));
1665 
1666   return SVN_NO_ERROR;
1667 }
1668 
1669 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)1670 tc_editor_delete(node_move_baton_t *nmb,
1671                  const char *relpath,
1672                  svn_node_kind_t old_kind,
1673                  svn_node_kind_t new_kind,
1674                  apr_pool_t *scratch_pool)
1675 {
1676   update_move_baton_t *b = nmb->umb;
1677   svn_sqlite__stmt_t *stmt;
1678   const char *local_abspath;
1679   svn_boolean_t is_modified, is_all_deletes;
1680   svn_skel_t *work_items = NULL;
1681   svn_skel_t *conflict = NULL;
1682 
1683   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1684   if (nmb->skip)
1685     return SVN_NO_ERROR;
1686 
1687   /* Check before retracting delete to catch delete-delete
1688      conflicts. This catches conflicts on the node itself; deleted
1689      children are caught as local modifications below.*/
1690   if (nmb->shadowed)
1691     {
1692       SVN_ERR(mark_tc_on_op_root(nmb,
1693                                  old_kind, new_kind,
1694                                  svn_wc_conflict_action_delete,
1695                                  scratch_pool));
1696       return SVN_NO_ERROR;
1697     }
1698 
1699   local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
1700   SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes,
1701                                       nmb->umb->db, local_abspath, FALSE,
1702                                       NULL, NULL, scratch_pool));
1703   if (is_modified)
1704     {
1705       svn_wc_conflict_reason_t reason;
1706 
1707       /* No conflict means no NODES rows at the relpath op-depth
1708          so it's easy to convert the modified tree into a copy.
1709 
1710          Note the following assumptions for relpath:
1711             * it is not shadowed
1712             * it is not the/an op-root. (or we can't make us a copy)
1713        */
1714 
1715       SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1716                                                NULL, NULL, scratch_pool));
1717 
1718       reason = svn_wc_conflict_reason_edited;
1719 
1720       SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
1721                                         old_kind, new_kind, reason,
1722                                         (new_kind == svn_node_none)
1723                                           ? svn_wc_conflict_action_delete
1724                                           : svn_wc_conflict_action_replace,
1725                                         NULL,
1726                                         scratch_pool, scratch_pool));
1727       nmb->skip = TRUE;
1728     }
1729   else
1730     {
1731       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1732       const char *del_abspath;
1733       svn_boolean_t have_row;
1734 
1735       /* Get all descendants of the node in reverse order (so children are
1736          handled before their parents, but not strictly depth first) */
1737       SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb,
1738                                         STMT_SELECT_DESCENDANTS_OP_DEPTH_RV));
1739       SVN_ERR(svn_sqlite__bindf(stmt, "isd", b->wcroot->wc_id, relpath,
1740                                 b->dst_op_depth));
1741       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1742       while (have_row)
1743         {
1744           svn_error_t *err;
1745           svn_skel_t *work_item;
1746           svn_node_kind_t del_kind;
1747 
1748           svn_pool_clear(iterpool);
1749 
1750           del_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1751           del_abspath = svn_dirent_join(b->wcroot->abspath,
1752                                         svn_sqlite__column_text(stmt, 0, NULL),
1753                                         iterpool);
1754           if (del_kind == svn_node_dir)
1755             err = svn_wc__wq_build_dir_remove(&work_item, b->db,
1756                                               b->wcroot->abspath, del_abspath,
1757                                               FALSE /* recursive */,
1758                                               iterpool, iterpool);
1759           else
1760             err = svn_wc__wq_build_file_remove(&work_item, b->db,
1761                                                b->wcroot->abspath, del_abspath,
1762                                                iterpool, iterpool);
1763           if (!err)
1764             err = svn_wc__db_wq_add_internal(b->wcroot, work_item, iterpool);
1765           if (err)
1766             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
1767 
1768           SVN_ERR(svn_sqlite__step(&have_row, stmt));
1769         }
1770       SVN_ERR(svn_sqlite__reset(stmt));
1771 
1772       if (old_kind == svn_node_dir)
1773         SVN_ERR(svn_wc__wq_build_dir_remove(&work_items, b->db,
1774                                             b->wcroot->abspath, local_abspath,
1775                                             FALSE /* recursive */,
1776                                             scratch_pool, iterpool));
1777       else
1778         SVN_ERR(svn_wc__wq_build_file_remove(&work_items, b->db,
1779                                              b->wcroot->abspath, local_abspath,
1780                                              scratch_pool, iterpool));
1781 
1782       svn_pool_destroy(iterpool);
1783     }
1784 
1785   /* Only notify if add_file/add_dir is not going to notify */
1786   if (conflict || (new_kind == svn_node_none))
1787     SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
1788                                  svn_wc_notify_update_delete,
1789                                  new_kind,
1790                                  svn_wc_notify_state_inapplicable,
1791                                  svn_wc_notify_state_inapplicable,
1792                                  conflict, work_items, scratch_pool));
1793   else if (work_items)
1794     SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items,
1795                                        scratch_pool));
1796 
1797   return SVN_NO_ERROR;
1798 }
1799 
1800 /* Handle node deletion for an incoming move. */
1801 static svn_error_t *
tc_incoming_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)1802 tc_incoming_editor_delete(node_move_baton_t *nmb,
1803                           const char *relpath,
1804                           svn_node_kind_t old_kind,
1805                           svn_node_kind_t new_kind,
1806                           apr_pool_t *scratch_pool)
1807 {
1808   update_move_baton_t *b = nmb->umb;
1809   svn_sqlite__stmt_t *stmt;
1810   const char *local_abspath;
1811   svn_boolean_t is_modified, is_all_deletes;
1812   svn_skel_t *work_items = NULL;
1813   svn_skel_t *conflict = NULL;
1814 
1815   SVN_ERR(mark_parent_edited(nmb, scratch_pool));
1816   if (nmb->skip)
1817     return SVN_NO_ERROR;
1818 
1819   /* Check before retracting delete to catch delete-delete
1820      conflicts. This catches conflicts on the node itself; deleted
1821      children are caught as local modifications below.*/
1822   if (nmb->shadowed)
1823     {
1824       SVN_ERR(mark_tc_on_op_root(nmb,
1825                                  old_kind, new_kind,
1826                                  svn_wc_conflict_action_delete,
1827                                  scratch_pool));
1828       return SVN_NO_ERROR;
1829     }
1830 
1831   local_abspath = svn_dirent_join(b->wcroot->abspath, relpath, scratch_pool);
1832   SVN_ERR(svn_wc__node_has_local_mods(&is_modified, &is_all_deletes,
1833                                       nmb->umb->db, local_abspath, FALSE,
1834                                       NULL, NULL, scratch_pool));
1835   if (is_modified)
1836     {
1837       svn_wc_conflict_reason_t reason;
1838 
1839       /* No conflict means no NODES rows at the relpath op-depth
1840          so it's easy to convert the modified tree into a copy.
1841 
1842          Note the following assumptions for relpath:
1843             * it is not shadowed
1844             * it is not the/an op-root. (or we can't make us a copy)
1845        */
1846 
1847       SVN_ERR(svn_wc__db_op_make_copy_internal(b->wcroot, relpath, FALSE,
1848                                                NULL, NULL, scratch_pool));
1849 
1850       reason = svn_wc_conflict_reason_edited;
1851 
1852       SVN_ERR(create_node_tree_conflict(&conflict, nmb, relpath,
1853                                         old_kind, new_kind, reason,
1854                                         (new_kind == svn_node_none)
1855                                           ? svn_wc_conflict_action_delete
1856                                           : svn_wc_conflict_action_replace,
1857                                         NULL,
1858                                         scratch_pool, scratch_pool));
1859       nmb->skip = TRUE;
1860     }
1861   else
1862     {
1863       /* Delete the WORKING node at DST_RELPATH. */
1864       SVN_ERR(svn_sqlite__get_statement(&stmt, b->wcroot->sdb,
1865                                  STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
1866       SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
1867                                 b->wcroot->wc_id, relpath,
1868                                 0, relpath_depth(relpath)));
1869       SVN_ERR(svn_sqlite__step_done(stmt));
1870     }
1871 
1872   /* Only notify if add_file/add_dir is not going to notify */
1873   if (conflict || (new_kind == svn_node_none))
1874     SVN_ERR(update_move_list_add(b->wcroot, relpath, b->db,
1875                                  svn_wc_notify_update_delete,
1876                                  new_kind,
1877                                  svn_wc_notify_state_inapplicable,
1878                                  svn_wc_notify_state_inapplicable,
1879                                  conflict, work_items, scratch_pool));
1880   else if (work_items)
1881     SVN_ERR(svn_wc__db_wq_add_internal(b->wcroot, work_items,
1882                                        scratch_pool));
1883 
1884   return SVN_NO_ERROR;
1885 }
1886 
1887 /*
1888  * Driver code.
1889  *
1890  * The scenario is that a subtree has been locally moved, and then the base
1891  * layer on the source side of the move has received an update to a new
1892  * state.  The destination subtree has not yet been updated, and still
1893  * matches the pre-update state of the source subtree.
1894  *
1895  * The edit driver drives the receiver with the difference between the
1896  * pre-update state (as found now at the move-destination) and the
1897  * post-update state (found now at the move-source).
1898  *
1899  * We currently assume that both the pre-update and post-update states are
1900  * single-revision.
1901  */
1902 
1903 /* Return *PROPS, *CHECKSUM, *CHILDREN and *KIND for LOCAL_RELPATH at
1904    OP_DEPTH provided the row exists.  Return *KIND of svn_node_none if
1905    the row does not exist, or only describes a delete of a lower op-depth.
1906    *CHILDREN is a sorted array of basenames of type 'const char *', rather
1907    than a hash, to allow the driver to process children in a defined order. */
1908 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)1909 get_info(apr_hash_t **props,
1910          const svn_checksum_t **checksum,
1911          apr_array_header_t **children,
1912          svn_node_kind_t *kind,
1913          const char *local_relpath,
1914          int op_depth,
1915          svn_wc__db_wcroot_t *wcroot,
1916          apr_pool_t *result_pool,
1917          apr_pool_t *scratch_pool)
1918 {
1919   svn_wc__db_status_t status;
1920   const char *repos_relpath;
1921   svn_node_kind_t db_kind;
1922   svn_error_t *err;
1923 
1924   err = svn_wc__db_depth_get_info(&status, &db_kind, NULL, &repos_relpath, NULL,
1925                                   NULL, NULL, NULL, NULL, checksum, NULL,
1926                                   NULL, props,
1927                                   wcroot, local_relpath, op_depth,
1928                                   result_pool, scratch_pool);
1929 
1930   /* If there is no node at this depth, or only a node that describes a delete
1931      of a lower layer we report this node as not existing. */
1932   if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
1933       || (!err && status != svn_wc__db_status_added
1934                && status != svn_wc__db_status_normal))
1935     {
1936       svn_error_clear(err);
1937 
1938       if (kind)
1939         *kind = svn_node_none;
1940       if (checksum)
1941         *checksum = NULL;
1942       if (props)
1943         *props = NULL;
1944       if (children)
1945         *children = apr_array_make(result_pool, 0, sizeof(const char *));
1946 
1947       return SVN_NO_ERROR;
1948     }
1949   else
1950     SVN_ERR(err);
1951 
1952   if (kind)
1953     *kind = db_kind;
1954 
1955   if (children && db_kind == svn_node_dir)
1956     {
1957       svn_sqlite__stmt_t *stmt;
1958       svn_boolean_t have_row;
1959 
1960       *children = apr_array_make(result_pool, 16, sizeof(const char *));
1961       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1962                                         STMT_SELECT_OP_DEPTH_CHILDREN_EXISTS));
1963       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1964                                 op_depth));
1965       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1966       while (have_row)
1967         {
1968           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1969 
1970           APR_ARRAY_PUSH(*children, const char *)
1971               = svn_relpath_basename(child_relpath, result_pool);
1972 
1973           SVN_ERR(svn_sqlite__step(&have_row, stmt));
1974         }
1975       SVN_ERR(svn_sqlite__reset(stmt));
1976     }
1977   else if (children)
1978     *children = apr_array_make(result_pool, 0, sizeof(const char *));
1979 
1980   return SVN_NO_ERROR;
1981 }
1982 
1983 /* Return TRUE if SRC_PROPS and DST_PROPS contain the same properties,
1984    FALSE otherwise. SRC_PROPS and DST_PROPS are standard property
1985    hashes. */
1986 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)1987 props_match(svn_boolean_t *match,
1988             apr_hash_t *src_props,
1989             apr_hash_t *dst_props,
1990             apr_pool_t *scratch_pool)
1991 {
1992   if (!src_props && !dst_props)
1993     *match = TRUE;
1994   else if (!src_props || ! dst_props)
1995     *match = FALSE;
1996   else
1997     {
1998       apr_array_header_t *propdiffs;
1999 
2000       SVN_ERR(svn_prop_diffs(&propdiffs, src_props, dst_props, scratch_pool));
2001       *match = propdiffs->nelts ? FALSE : TRUE;
2002     }
2003   return SVN_NO_ERROR;
2004 }
2005 
2006 /* ### Drive TC_EDITOR so as to ...
2007  */
2008 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)2009 update_moved_away_node(node_move_baton_t *nmb,
2010                        svn_wc__db_wcroot_t *wcroot,
2011                        const char *src_relpath,
2012                        const char *dst_relpath,
2013                        apr_pool_t *scratch_pool)
2014 {
2015   update_move_baton_t *b = nmb->umb;
2016   svn_node_kind_t src_kind, dst_kind;
2017   const svn_checksum_t *src_checksum, *dst_checksum;
2018   apr_hash_t *src_props, *dst_props;
2019   apr_array_header_t *src_children, *dst_children;
2020 
2021   if (b->cancel_func)
2022     SVN_ERR(b->cancel_func(b->cancel_baton));
2023 
2024   SVN_ERR(get_info(&src_props, &src_checksum, &src_children, &src_kind,
2025                    src_relpath, b->src_op_depth,
2026                    wcroot, scratch_pool, scratch_pool));
2027 
2028   SVN_ERR(get_info(&dst_props, &dst_checksum, &dst_children, &dst_kind,
2029                    dst_relpath, b->dst_op_depth,
2030                    wcroot, scratch_pool, scratch_pool));
2031 
2032   if (src_kind == svn_node_none
2033       || (dst_kind != svn_node_none && src_kind != dst_kind))
2034     {
2035       SVN_ERR(tc_editor_delete(nmb, dst_relpath, dst_kind, src_kind,
2036                                scratch_pool));
2037     }
2038 
2039   if (nmb->skip)
2040     return SVN_NO_ERROR;
2041 
2042   if (src_kind != svn_node_none && src_kind != dst_kind)
2043     {
2044       if (src_kind == svn_node_file || src_kind == svn_node_symlink)
2045         {
2046           SVN_ERR(tc_editor_add_file(nmb, dst_relpath, dst_kind,
2047                                      src_checksum, src_props, scratch_pool));
2048         }
2049       else if (src_kind == svn_node_dir)
2050         {
2051           SVN_ERR(tc_editor_add_directory(nmb, dst_relpath, dst_kind,
2052                                           src_props, scratch_pool));
2053         }
2054     }
2055   else if (src_kind != svn_node_none)
2056     {
2057       svn_boolean_t props_equal;
2058 
2059       SVN_ERR(props_match(&props_equal, src_props, dst_props, scratch_pool));
2060 
2061       if (src_kind == svn_node_file || src_kind == svn_node_symlink)
2062         {
2063           if (!props_equal || !svn_checksum_match(src_checksum, dst_checksum))
2064             SVN_ERR(tc_editor_alter_file(nmb, dst_relpath,
2065                                          dst_checksum, src_checksum,
2066                                          dst_props, src_props, scratch_pool));
2067         }
2068       else if (src_kind == svn_node_dir)
2069         {
2070           if (!props_equal)
2071             SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
2072                                               dst_props, src_props,
2073                                               scratch_pool));
2074         }
2075     }
2076 
2077   if (nmb->skip)
2078     return SVN_NO_ERROR;
2079 
2080   if (src_kind == svn_node_dir)
2081     {
2082       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2083       int i = 0, j = 0;
2084 
2085       while (i < src_children->nelts || j < dst_children->nelts)
2086         {
2087           const char *child_name;
2088           svn_boolean_t src_only = FALSE, dst_only = FALSE;
2089           node_move_baton_t cnmb = { 0 };
2090 
2091           cnmb.pb = nmb;
2092           cnmb.umb = nmb->umb;
2093           cnmb.shadowed = nmb->shadowed;
2094 
2095           svn_pool_clear(iterpool);
2096           if (i >= src_children->nelts)
2097             {
2098               dst_only = TRUE;
2099               child_name = APR_ARRAY_IDX(dst_children, j, const char *);
2100             }
2101           else if (j >= dst_children->nelts)
2102             {
2103               src_only = TRUE;
2104               child_name = APR_ARRAY_IDX(src_children, i, const char *);
2105             }
2106           else
2107             {
2108               const char *src_name = APR_ARRAY_IDX(src_children, i,
2109                                                    const char *);
2110               const char *dst_name = APR_ARRAY_IDX(dst_children, j,
2111                                                    const char *);
2112               int cmp = strcmp(src_name, dst_name);
2113 
2114               if (cmp > 0)
2115                 dst_only = TRUE;
2116               else if (cmp < 0)
2117                 src_only = TRUE;
2118 
2119               child_name = dst_only ? dst_name : src_name;
2120             }
2121 
2122           cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
2123                                               iterpool);
2124           cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
2125                                               iterpool);
2126 
2127           if (!cnmb.shadowed)
2128             SVN_ERR(check_node_shadowed(&cnmb.shadowed, wcroot,
2129                                         cnmb.dst_relpath, b->dst_op_depth,
2130                                         iterpool));
2131 
2132           SVN_ERR(update_moved_away_node(&cnmb, wcroot, cnmb.src_relpath,
2133                                          cnmb.dst_relpath, iterpool));
2134 
2135           if (!dst_only)
2136             ++i;
2137           if (!src_only)
2138             ++j;
2139 
2140           if (nmb->skip) /* Does parent now want a skip? */
2141             break;
2142         }
2143     }
2144 
2145   return SVN_NO_ERROR;
2146 }
2147 
2148 static svn_error_t *
suitable_for_move(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)2149 suitable_for_move(svn_wc__db_wcroot_t *wcroot,
2150                   const char *local_relpath,
2151                   apr_pool_t *scratch_pool)
2152 {
2153   svn_sqlite__stmt_t *stmt;
2154   svn_boolean_t have_row;
2155   svn_revnum_t revision;
2156   const char *repos_relpath;
2157   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2158 
2159   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2160                                     STMT_SELECT_BASE_NODE));
2161   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2162   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2163   if (!have_row)
2164     return svn_error_trace(svn_sqlite__reset(stmt));
2165 
2166   revision = svn_sqlite__column_revnum(stmt, 4);
2167   repos_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
2168 
2169   SVN_ERR(svn_sqlite__reset(stmt));
2170 
2171   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2172                                     STMT_SELECT_REPOS_PATH_REVISION));
2173   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2174   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2175   while (have_row)
2176     {
2177       svn_revnum_t node_revision = svn_sqlite__column_revnum(stmt, 2);
2178       const char *child_relpath;
2179       const char *relpath;
2180 
2181       svn_pool_clear(iterpool);
2182 
2183       child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2184       relpath = svn_relpath_skip_ancestor(local_relpath, child_relpath);
2185       relpath = svn_relpath_join(repos_relpath, relpath, iterpool);
2186 
2187       if (strcmp(relpath, svn_sqlite__column_text(stmt, 1, NULL)))
2188         return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2189                                  svn_sqlite__reset(stmt),
2190                                  _("Cannot apply update because '%s' is a "
2191                                    "switched path (please switch it back to "
2192                                    "its original URL and try again)"),
2193                                  path_for_error_message(wcroot, child_relpath,
2194                                                         scratch_pool));
2195 
2196       if (revision != node_revision)
2197         return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2198                                  svn_sqlite__reset(stmt),
2199                                  _("Cannot apply update because '%s' is a "
2200                                    "mixed-revision working copy (please "
2201                                    "update and try again)"),
2202                                  path_for_error_message(wcroot, local_relpath,
2203                                                         scratch_pool));
2204 
2205       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2206     }
2207   SVN_ERR(svn_sqlite__reset(stmt));
2208 
2209   svn_pool_destroy(iterpool);
2210 
2211   return SVN_NO_ERROR;
2212 }
2213 
2214 /* The body of svn_wc__db_update_moved_away_conflict_victim(), which see.
2215  */
2216 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)2217 update_moved_away_conflict_victim(svn_revnum_t *old_rev,
2218                                   svn_revnum_t *new_rev,
2219                                   svn_wc__db_t *db,
2220                                   svn_wc__db_wcroot_t *wcroot,
2221                                   const char *local_relpath,
2222                                   const char *delete_relpath,
2223                                   svn_wc_operation_t operation,
2224                                   svn_wc_conflict_action_t action,
2225                                   svn_wc_conflict_reason_t reason,
2226                                   svn_cancel_func_t cancel_func,
2227                                   void *cancel_baton,
2228                                   apr_pool_t *scratch_pool)
2229 {
2230   update_move_baton_t umb = { NULL };
2231   const char *src_relpath, *dst_relpath;
2232   svn_wc_conflict_version_t old_version;
2233   svn_wc_conflict_version_t new_version;
2234   apr_int64_t repos_id;
2235   node_move_baton_t nmb = { 0 };
2236 
2237   SVN_ERR_ASSERT(svn_relpath_skip_ancestor(delete_relpath, local_relpath));
2238 
2239   /* Construct editor baton. */
2240 
2241   SVN_ERR(find_src_op_depth(&umb.src_op_depth, wcroot,
2242                             local_relpath, relpath_depth(delete_relpath),
2243                             scratch_pool));
2244 
2245   SVN_ERR(svn_wc__db_scan_moved_to_internal(&src_relpath, &dst_relpath, NULL,
2246                                             wcroot, local_relpath,
2247                                             umb.src_op_depth,
2248                                             scratch_pool, scratch_pool));
2249 
2250   if (dst_relpath == NULL)
2251     return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2252                              _("The node '%s' has not been moved away"),
2253                              path_for_error_message(wcroot, local_relpath,
2254                                                     scratch_pool));
2255 
2256   umb.dst_op_depth = relpath_depth(dst_relpath);
2257 
2258   SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
2259   SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
2260 
2261 
2262   SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_version.node_kind,
2263                                     &new_version.peg_rev,
2264                                     &new_version.path_in_repos, &repos_id,
2265                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2266                                     NULL,
2267                                     wcroot, src_relpath, umb.src_op_depth,
2268                                     scratch_pool, scratch_pool));
2269 
2270   SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
2271                                       &new_version.repos_uuid,
2272                                       wcroot, repos_id,
2273                                       scratch_pool));
2274 
2275   SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind,
2276                                     &old_version.peg_rev,
2277                                     &old_version.path_in_repos, &repos_id,
2278                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2279                                     NULL,
2280                                     wcroot, dst_relpath, umb.dst_op_depth,
2281                                     scratch_pool, scratch_pool));
2282 
2283   SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
2284                                       &old_version.repos_uuid,
2285                                       wcroot, repos_id,
2286                                       scratch_pool));
2287   *old_rev = old_version.peg_rev;
2288   *new_rev = new_version.peg_rev;
2289 
2290   umb.operation = operation;
2291   umb.old_version= &old_version;
2292   umb.new_version= &new_version;
2293   umb.db = db;
2294   umb.wcroot = wcroot;
2295   umb.cancel_func = cancel_func;
2296   umb.cancel_baton = cancel_baton;
2297 
2298   if (umb.src_op_depth == 0)
2299     SVN_ERR(suitable_for_move(wcroot, src_relpath, scratch_pool));
2300 
2301   /* Create a new, and empty, list for notification information. */
2302   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2303                                       STMT_CREATE_UPDATE_MOVE_LIST));
2304 
2305   /* Drive the editor... */
2306 
2307   nmb.umb = &umb;
2308   nmb.src_relpath = src_relpath;
2309   nmb.dst_relpath = dst_relpath;
2310   /* nmb.shadowed = FALSE; */
2311   /* nmb.edited = FALSE; */
2312   /* nmb.skip_children = FALSE; */
2313 
2314   /* We walk the move source (i.e. the post-update tree), comparing each node
2315     * with the equivalent node at the move destination and applying the update
2316     * to nodes at the move destination. */
2317   SVN_ERR(update_moved_away_node(&nmb, wcroot, src_relpath, dst_relpath,
2318                                  scratch_pool));
2319 
2320   SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot, src_relpath,
2321                                             umb.src_op_depth,
2322                                             dst_relpath, NULL, NULL,
2323                                             scratch_pool));
2324 
2325   return SVN_NO_ERROR;
2326 }
2327 
2328 
2329 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)2330 svn_wc__db_update_moved_away_conflict_victim(svn_wc__db_t *db,
2331                                              const char *local_abspath,
2332                                              const char *delete_op_abspath,
2333                                              svn_wc_operation_t operation,
2334                                              svn_wc_conflict_action_t action,
2335                                              svn_wc_conflict_reason_t reason,
2336                                              svn_cancel_func_t cancel_func,
2337                                              void *cancel_baton,
2338                                              svn_wc_notify_func2_t notify_func,
2339                                              void *notify_baton,
2340                                              apr_pool_t *scratch_pool)
2341 {
2342   svn_wc__db_wcroot_t *wcroot;
2343   svn_revnum_t old_rev, new_rev;
2344   const char *local_relpath;
2345   const char *delete_relpath;
2346 
2347   /* ### Check for mixed-rev src or dst? */
2348 
2349   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2350                                                 db, local_abspath,
2351                                                 scratch_pool, scratch_pool));
2352   VERIFY_USABLE_WCROOT(wcroot);
2353 
2354   delete_relpath
2355     = svn_dirent_skip_ancestor(wcroot->abspath, delete_op_abspath);
2356 
2357   SVN_WC__DB_WITH_TXN(
2358     update_moved_away_conflict_victim(
2359       &old_rev, &new_rev,
2360       db, wcroot, local_relpath, delete_relpath,
2361       operation, action, reason,
2362       cancel_func, cancel_baton,
2363       scratch_pool),
2364     wcroot);
2365 
2366   /* Send all queued up notifications. */
2367   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev,
2368                                              notify_func, notify_baton,
2369                                              scratch_pool));
2370   if (notify_func)
2371     {
2372       svn_wc_notify_t *notify;
2373 
2374       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2375                                                     local_relpath,
2376                                                     scratch_pool),
2377                                     svn_wc_notify_update_completed,
2378                                     scratch_pool);
2379       notify->kind = svn_node_none;
2380       notify->content_state = svn_wc_notify_state_inapplicable;
2381       notify->prop_state = svn_wc_notify_state_inapplicable;
2382       notify->revision = new_rev;
2383       notify_func(notify_baton, notify, scratch_pool);
2384     }
2385 
2386 
2387   return SVN_NO_ERROR;
2388 }
2389 
2390 static svn_error_t *
get_working_info(apr_hash_t ** props,const svn_checksum_t ** checksum,apr_array_header_t ** children,svn_node_kind_t * kind,const char * local_relpath,svn_wc__db_wcroot_t * wcroot,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2391 get_working_info(apr_hash_t **props,
2392                  const svn_checksum_t **checksum,
2393                  apr_array_header_t **children,
2394                  svn_node_kind_t *kind,
2395                  const char *local_relpath,
2396                  svn_wc__db_wcroot_t *wcroot,
2397                  apr_pool_t *result_pool,
2398                  apr_pool_t *scratch_pool)
2399 {
2400   svn_wc__db_status_t status;
2401   const char *repos_relpath;
2402   svn_node_kind_t db_kind;
2403   svn_error_t *err;
2404 
2405   err = svn_wc__db_read_info_internal(&status, &db_kind, NULL, &repos_relpath,
2406                                       NULL, NULL, NULL, NULL, NULL,
2407                                       checksum,
2408                                       NULL, NULL, NULL, NULL, NULL,
2409                                       NULL, NULL, NULL, NULL, NULL,
2410                                       NULL, NULL, NULL, NULL, NULL,
2411                                       wcroot, local_relpath,
2412                                       result_pool, scratch_pool);
2413 
2414   /* If there is no node, or only a node that describes a delete
2415      of a lower layer we report this node as not existing. */
2416   if ((err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
2417       || (!err && status != svn_wc__db_status_added
2418                && status != svn_wc__db_status_normal))
2419     {
2420       svn_error_clear(err);
2421 
2422       if (kind)
2423         *kind = svn_node_none;
2424       if (checksum)
2425         *checksum = NULL;
2426       if (props)
2427         *props = NULL;
2428       if (children)
2429         *children = apr_array_make(result_pool, 0, sizeof(const char *));
2430 
2431       return SVN_NO_ERROR;
2432     }
2433   else
2434     SVN_ERR(err);
2435 
2436   SVN_ERR(svn_wc__db_read_props_internal(props, wcroot, local_relpath,
2437                                          result_pool, scratch_pool));
2438 
2439   if (kind)
2440     *kind = db_kind;
2441 
2442   if (children && db_kind == svn_node_dir)
2443     {
2444       svn_sqlite__stmt_t *stmt;
2445       svn_boolean_t have_row;
2446 
2447       *children = apr_array_make(result_pool, 16, sizeof(const char *));
2448       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2449                                         STMT_SELECT_WORKING_CHILDREN));
2450       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2451       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2452       while (have_row)
2453         {
2454           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2455 
2456           APR_ARRAY_PUSH(*children, const char *)
2457               = svn_relpath_basename(child_relpath, result_pool);
2458 
2459           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2460         }
2461       SVN_ERR(svn_sqlite__reset(stmt));
2462     }
2463   else if (children)
2464     *children = apr_array_make(result_pool, 0, sizeof(const char *));
2465 
2466   return SVN_NO_ERROR;
2467 }
2468 
2469 /* Apply changes found in the victim node at SRC_RELPATH to the incoming
2470  * move at DST_RELPATH. */
2471 static svn_error_t *
update_incoming_moved_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)2472 update_incoming_moved_node(node_move_baton_t *nmb,
2473                            svn_wc__db_wcroot_t *wcroot,
2474                            const char *src_relpath,
2475                            const char *dst_relpath,
2476                            apr_pool_t *scratch_pool)
2477 {
2478   update_move_baton_t *b = nmb->umb;
2479   svn_node_kind_t orig_kind, working_kind;
2480   const char *victim_relpath = src_relpath;
2481   const svn_checksum_t *orig_checksum, *working_checksum;
2482   apr_hash_t *orig_props, *working_props;
2483   apr_array_header_t *orig_children, *working_children;
2484 
2485   if (b->cancel_func)
2486     SVN_ERR(b->cancel_func(b->cancel_baton));
2487 
2488   /* Compare the tree conflict victim's copied layer (the "original") with
2489    * the working layer, i.e. look for changes layered on top of the copy. */
2490   SVN_ERR(get_info(&orig_props, &orig_checksum, &orig_children, &orig_kind,
2491                    victim_relpath, b->src_op_depth, wcroot, scratch_pool,
2492                    scratch_pool));
2493   SVN_ERR(get_working_info(&working_props, &working_checksum,
2494                            &working_children, &working_kind, victim_relpath,
2495                            wcroot, scratch_pool, scratch_pool));
2496 
2497   if (working_kind == svn_node_none
2498       || (orig_kind != svn_node_none && orig_kind != working_kind))
2499     {
2500       SVN_ERR(tc_incoming_editor_delete(nmb, dst_relpath, orig_kind,
2501                                         working_kind, scratch_pool));
2502     }
2503 
2504   if (nmb->skip)
2505     return SVN_NO_ERROR;
2506 
2507   if (working_kind != svn_node_none && orig_kind != working_kind)
2508     {
2509       if (working_kind == svn_node_file || working_kind == svn_node_symlink)
2510         {
2511           const char *victim_abspath;
2512           const char *wctemp_abspath;
2513           svn_stream_t *working_stream;
2514           svn_stream_t *temp_stream;
2515           const char *temp_abspath;
2516           svn_error_t *err;
2517 
2518           /* Copy the victim's content to a safe place and add it from there. */
2519           SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&wctemp_abspath, b->db,
2520                                                  b->wcroot->abspath,
2521                                                  scratch_pool,
2522                                                  scratch_pool));
2523           victim_abspath = svn_dirent_join(b->wcroot->abspath,
2524                                            victim_relpath, scratch_pool);
2525           SVN_ERR(svn_stream_open_readonly(&working_stream, victim_abspath,
2526                                            scratch_pool, scratch_pool));
2527           SVN_ERR(svn_stream_open_unique(&temp_stream, &temp_abspath,
2528                                          wctemp_abspath, svn_io_file_del_none,
2529                                          scratch_pool, scratch_pool));
2530           err = svn_stream_copy3(working_stream, temp_stream,
2531                                  b->cancel_func, b->cancel_baton,
2532                                  scratch_pool);
2533           if (err && err->apr_err == SVN_ERR_CANCELLED)
2534             {
2535               svn_error_t *err2;
2536 
2537               err2 = svn_io_remove_file2(temp_abspath, TRUE, scratch_pool);
2538               return svn_error_compose_create(err, err2);
2539             }
2540           else
2541             SVN_ERR(err);
2542 
2543           SVN_ERR(tc_editor_incoming_add_file(nmb, dst_relpath, orig_kind,
2544                                               working_checksum, working_props,
2545                                               victim_relpath, temp_abspath,
2546                                               scratch_pool));
2547         }
2548       else if (working_kind == svn_node_dir)
2549         {
2550           SVN_ERR(tc_editor_incoming_add_directory(nmb, dst_relpath,
2551                                                    orig_kind, working_props,
2552                                                    victim_relpath,
2553                                                    scratch_pool));
2554         }
2555     }
2556   else if (working_kind != svn_node_none)
2557     {
2558       svn_boolean_t props_equal;
2559 
2560       SVN_ERR(props_match(&props_equal, orig_props, working_props,
2561                           scratch_pool));
2562 
2563       if (working_kind == svn_node_file || working_kind == svn_node_symlink)
2564         {
2565           svn_boolean_t is_modified;
2566 
2567           SVN_ERR(svn_wc__internal_file_modified_p(&is_modified, b->db,
2568                                                    svn_dirent_join(
2569                                                      b->wcroot->abspath,
2570                                                      victim_relpath,
2571                                                      scratch_pool),
2572                                                    FALSE /* exact_comparison */,
2573                                                    scratch_pool));
2574           if (!props_equal || is_modified)
2575             SVN_ERR(tc_editor_update_incoming_moved_file(nmb, dst_relpath,
2576                                                          victim_relpath,
2577                                                          working_checksum,
2578                                                          orig_checksum,
2579                                                          orig_props,
2580                                                          working_props,
2581                                                          is_modified,
2582                                                          scratch_pool));
2583         }
2584       else if (working_kind == svn_node_dir)
2585         {
2586           if (!props_equal)
2587             SVN_ERR(tc_editor_alter_directory(nmb, dst_relpath,
2588                                               orig_props, working_props,
2589                                               scratch_pool));
2590         }
2591     }
2592 
2593   if (nmb->skip)
2594     return SVN_NO_ERROR;
2595 
2596   if (working_kind == svn_node_dir)
2597     {
2598       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2599       int i = 0, j = 0;
2600 
2601       while (i < orig_children->nelts || j < working_children->nelts)
2602         {
2603           const char *child_name;
2604           svn_boolean_t orig_only = FALSE, working_only = FALSE;
2605           node_move_baton_t cnmb = { 0 };
2606 
2607           cnmb.pb = nmb;
2608           cnmb.umb = nmb->umb;
2609           cnmb.shadowed = nmb->shadowed;
2610 
2611           svn_pool_clear(iterpool);
2612           if (i >= orig_children->nelts)
2613             {
2614               working_only = TRUE;
2615               child_name = APR_ARRAY_IDX(working_children, j, const char *);
2616             }
2617           else if (j >= working_children->nelts)
2618             {
2619               orig_only = TRUE;
2620               child_name = APR_ARRAY_IDX(orig_children, i, const char *);
2621             }
2622           else
2623             {
2624               const char *orig_name = APR_ARRAY_IDX(orig_children, i,
2625                                                     const char *);
2626               const char *working_name = APR_ARRAY_IDX(working_children, j,
2627                                                        const char *);
2628               int cmp = strcmp(orig_name, working_name);
2629 
2630               if (cmp > 0)
2631                 working_only = TRUE;
2632               else if (cmp < 0)
2633                 orig_only = TRUE;
2634 
2635               child_name = working_only ? working_name : orig_name;
2636             }
2637 
2638           cnmb.src_relpath = svn_relpath_join(src_relpath, child_name,
2639                                               iterpool);
2640           cnmb.dst_relpath = svn_relpath_join(dst_relpath, child_name,
2641                                               iterpool);
2642 
2643           SVN_ERR(update_incoming_moved_node(&cnmb, wcroot, cnmb.src_relpath,
2644                                              cnmb.dst_relpath, iterpool));
2645 
2646           if (!working_only)
2647             ++i;
2648           if (!orig_only)
2649             ++j;
2650 
2651           if (nmb->skip) /* Does parent now want a skip? */
2652             break;
2653         }
2654     }
2655 
2656   return SVN_NO_ERROR;
2657 }
2658 
2659 /* The body of svn_wc__db_update_incoming_move(). */
2660 static svn_error_t *
update_incoming_move(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 * dst_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)2661 update_incoming_move(svn_revnum_t *old_rev,
2662                     svn_revnum_t *new_rev,
2663                     svn_wc__db_t *db,
2664                     svn_wc__db_wcroot_t *wcroot,
2665                     const char *local_relpath,
2666                     const char *dst_relpath,
2667                     svn_wc_operation_t operation,
2668                     svn_wc_conflict_action_t action,
2669                     svn_wc_conflict_reason_t reason,
2670                     svn_cancel_func_t cancel_func,
2671                     void *cancel_baton,
2672                     apr_pool_t *scratch_pool)
2673 {
2674   update_move_baton_t umb = { NULL };
2675   svn_wc_conflict_version_t old_version;
2676   svn_wc_conflict_version_t new_version;
2677   apr_int64_t repos_id;
2678   node_move_baton_t nmb = { 0 };
2679   svn_boolean_t is_modified;
2680 
2681   SVN_ERR_ASSERT(svn_relpath_skip_ancestor(dst_relpath, local_relpath) == NULL);
2682 
2683   /* For incoming moves during update/switch, the move source is a copied
2684    * tree which was copied from the pre-update BASE revision while raising
2685    * the tree conflict, when the update attempted to delete the move source.
2686    * This copy is our "original" state (SRC of the diff) and the local changes
2687    * on top of this copy at the top-most WORKING layer are used to drive the
2688    * editor (DST of the diff).
2689    *
2690    * The move destination, where changes are applied to, is now in the BASE
2691    * tree at DST_RELPATH. This repository-side move is the "incoming change"
2692    * recorded for any tree conflicts created during the editor drive.
2693    * We assume this path contains no local changes, and create local changes
2694    * in DST_RELPATH corresponding to changes contained in the conflict victim.
2695    *
2696    * DST_OP_DEPTH is used to infer the "op-root" of the incoming move. This
2697    * "op-root" is virtual because all nodes belonging to the incoming move
2698    * live in the BASE tree. It is used for constructing repository paths
2699    * when new tree conflicts need to be raised.
2700    */
2701   umb.src_op_depth = relpath_depth(local_relpath); /* SRC of diff */
2702   umb.dst_op_depth = relpath_depth(dst_relpath); /* virtual DST op-root */
2703 
2704   SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
2705   SVN_ERR(verify_write_lock(wcroot, dst_relpath, scratch_pool));
2706 
2707   /* Make sure there are no local modifications in the move destination. */
2708   SVN_ERR(svn_wc__node_has_local_mods(&is_modified, NULL, db,
2709                                       svn_dirent_join(wcroot->abspath,
2710                                                       dst_relpath,
2711                                                       scratch_pool),
2712                                       TRUE, cancel_func, cancel_baton,
2713                                       scratch_pool));
2714   if (is_modified)
2715     return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2716                              _("Cannot merge local changes from '%s' because "
2717                                "'%s' already contains other local changes "
2718                                "(please commit or revert these other changes "
2719                                "and try again)"),
2720                              svn_dirent_local_style(
2721                                svn_dirent_join(wcroot->abspath, local_relpath,
2722                                                scratch_pool),
2723                                scratch_pool),
2724                              svn_dirent_local_style(
2725                                svn_dirent_join(wcroot->abspath, dst_relpath,
2726                                                scratch_pool),
2727                                scratch_pool));
2728 
2729   /* Check for switched subtrees and mixed-revision working copy. */
2730   SVN_ERR(suitable_for_move(wcroot, dst_relpath, scratch_pool));
2731 
2732   /* Read version info from the updated incoming post-move location. */
2733   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_version.node_kind,
2734                                             &new_version.peg_rev,
2735                                             &new_version.path_in_repos,
2736                                             &repos_id,
2737                                             NULL, NULL, NULL, NULL, NULL,
2738                                             NULL, NULL, NULL, NULL, NULL,
2739                                             wcroot, dst_relpath,
2740                                             scratch_pool, scratch_pool));
2741 
2742   SVN_ERR(svn_wc__db_fetch_repos_info(&new_version.repos_url,
2743                                       &new_version.repos_uuid,
2744                                       wcroot, repos_id,
2745                                       scratch_pool));
2746 
2747   /* Read version info from the victim's location. */
2748   SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_version.node_kind,
2749                                     &old_version.peg_rev,
2750                                     &old_version.path_in_repos, &repos_id,
2751                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2752                                     NULL, wcroot,
2753                                     local_relpath, umb.src_op_depth,
2754                                     scratch_pool, scratch_pool));
2755 
2756   SVN_ERR(svn_wc__db_fetch_repos_info(&old_version.repos_url,
2757                                       &old_version.repos_uuid,
2758                                       wcroot, repos_id,
2759                                       scratch_pool));
2760   *old_rev = old_version.peg_rev;
2761   *new_rev = new_version.peg_rev;
2762 
2763   umb.operation = operation;
2764   umb.old_version= &old_version;
2765   umb.new_version= &new_version;
2766   umb.db = db;
2767   umb.wcroot = wcroot;
2768   umb.cancel_func = cancel_func;
2769   umb.cancel_baton = cancel_baton;
2770 
2771   /* Create a new, and empty, list for notification information. */
2772   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
2773                                       STMT_CREATE_UPDATE_MOVE_LIST));
2774 
2775   /* Drive the editor... */
2776 
2777   nmb.umb = &umb;
2778   nmb.src_relpath = local_relpath;
2779   nmb.dst_relpath = dst_relpath;
2780   /* nmb.shadowed = FALSE; */
2781   /* nmb.edited = FALSE; */
2782   /* nmb.skip_children = FALSE; */
2783 
2784   /* We walk the conflict victim, comparing each node with the equivalent node
2785    * at the WORKING layer, applying any local changes to nodes at the move
2786    * destination. */
2787   SVN_ERR(update_incoming_moved_node(&nmb, wcroot, local_relpath, dst_relpath,
2788                                      scratch_pool));
2789 
2790   return SVN_NO_ERROR;
2791 }
2792 
2793 svn_error_t *
svn_wc__db_update_incoming_move(svn_wc__db_t * db,const char * local_abspath,const char * dest_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)2794 svn_wc__db_update_incoming_move(svn_wc__db_t *db,
2795                                 const char *local_abspath,
2796                                 const char *dest_abspath,
2797                                 svn_wc_operation_t operation,
2798                                 svn_wc_conflict_action_t action,
2799                                 svn_wc_conflict_reason_t reason,
2800                                 svn_cancel_func_t cancel_func,
2801                                 void *cancel_baton,
2802                                 svn_wc_notify_func2_t notify_func,
2803                                 void *notify_baton,
2804                                 apr_pool_t *scratch_pool)
2805 {
2806   svn_wc__db_wcroot_t *wcroot;
2807   svn_revnum_t old_rev, new_rev;
2808   const char *local_relpath;
2809   const char *dest_relpath;
2810 
2811   /* ### Check for mixed-rev src or dst? */
2812 
2813   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2814                                                 db, local_abspath,
2815                                                 scratch_pool, scratch_pool));
2816   VERIFY_USABLE_WCROOT(wcroot);
2817 
2818   dest_relpath
2819     = svn_dirent_skip_ancestor(wcroot->abspath, dest_abspath);
2820 
2821   SVN_WC__DB_WITH_TXN(update_incoming_move(&old_rev, &new_rev, db, wcroot,
2822                                            local_relpath, dest_relpath,
2823                                            operation, action, reason,
2824                                            cancel_func, cancel_baton,
2825                                            scratch_pool),
2826                       wcroot);
2827 
2828   /* Send all queued up notifications. */
2829   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, old_rev, new_rev,
2830                                              notify_func, notify_baton,
2831                                              scratch_pool));
2832   if (notify_func)
2833     {
2834       svn_wc_notify_t *notify;
2835 
2836       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
2837                                                     local_relpath,
2838                                                     scratch_pool),
2839                                     svn_wc_notify_update_completed,
2840                                     scratch_pool);
2841       notify->kind = svn_node_none;
2842       notify->content_state = svn_wc_notify_state_inapplicable;
2843       notify->prop_state = svn_wc_notify_state_inapplicable;
2844       notify->revision = new_rev;
2845       notify_func(notify_baton, notify, scratch_pool);
2846     }
2847 
2848 
2849   return SVN_NO_ERROR;
2850 }
2851 
2852 typedef struct update_local_add_baton_t {
2853   int add_op_depth;
2854   svn_wc__db_t *db;
2855   svn_wc__db_wcroot_t *wcroot;
2856   svn_cancel_func_t cancel_func;
2857   void *cancel_baton;
2858 
2859   /* We refer to these if raising new tree conflicts. */
2860   const svn_wc_conflict_version_t *new_version;
2861 } update_local_add_baton_t;
2862 
2863 typedef struct added_node_baton_t {
2864   struct update_local_add_baton_t *b;
2865   struct added_node_baton_t *pb;
2866   const char *local_relpath;
2867   svn_boolean_t skip;
2868   svn_boolean_t edited;
2869 } added_node_baton_t;
2870 
2871 
2872 static svn_error_t *
update_local_add_mark_node_edited(added_node_baton_t * nb,apr_pool_t * scratch_pool)2873 update_local_add_mark_node_edited(added_node_baton_t *nb,
2874                                   apr_pool_t *scratch_pool)
2875 {
2876   if (nb->edited)
2877     return SVN_NO_ERROR;
2878 
2879   if (nb->pb)
2880     {
2881       SVN_ERR(update_local_add_mark_node_edited(nb->pb, scratch_pool));
2882 
2883       if (nb->pb->skip)
2884         nb->skip = TRUE;
2885     }
2886 
2887   nb->edited = TRUE;
2888 
2889   return SVN_NO_ERROR;
2890 }
2891 
2892 static svn_error_t *
update_local_add_mark_parent_edited(added_node_baton_t * nb,apr_pool_t * scratch_pool)2893 update_local_add_mark_parent_edited(added_node_baton_t *nb,
2894                                     apr_pool_t *scratch_pool)
2895 {
2896   SVN_ERR_ASSERT(nb && nb->pb);
2897 
2898   SVN_ERR(update_local_add_mark_node_edited(nb->pb, scratch_pool));
2899 
2900   if (nb->pb->skip)
2901     nb->skip = TRUE;
2902 
2903   return SVN_NO_ERROR;
2904 }
2905 
2906 static svn_error_t *
mark_update_add_add_tree_conflict(added_node_baton_t * nb,svn_node_kind_t base_kind,svn_node_kind_t working_kind,svn_wc_conflict_reason_t local_change,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2907 mark_update_add_add_tree_conflict(added_node_baton_t *nb,
2908                                   svn_node_kind_t base_kind,
2909                                   svn_node_kind_t working_kind,
2910                                   svn_wc_conflict_reason_t local_change,
2911                                   apr_pool_t *result_pool,
2912                                   apr_pool_t *scratch_pool)
2913 
2914 {
2915   svn_wc__db_t *db = nb->b->db;
2916   svn_wc__db_wcroot_t *wcroot = nb->b->wcroot;
2917   svn_wc_conflict_version_t *new_version;
2918   svn_skel_t *conflict;
2919 
2920   new_version = svn_wc_conflict_version_dup(nb->b->new_version, result_pool);
2921 
2922   /* Fill in conflict info templates with info for this node. */
2923   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, &new_version->peg_rev,
2924                                             &new_version->path_in_repos,
2925                                             NULL, NULL, NULL, NULL, NULL, NULL,
2926                                             NULL, NULL, NULL, NULL, NULL,
2927                                             wcroot, nb->local_relpath,
2928                                             scratch_pool, scratch_pool));
2929   new_version->node_kind = base_kind;
2930 
2931   SVN_ERR(create_tree_conflict(&conflict, wcroot, nb->local_relpath,
2932                                nb->local_relpath, db, NULL, new_version,
2933                                svn_wc_operation_update,
2934                                svn_node_none, base_kind, NULL,
2935                                local_change, svn_wc_conflict_action_add,
2936                                NULL, scratch_pool, scratch_pool));
2937 
2938   SVN_ERR(update_move_list_add(wcroot, nb->local_relpath, db,
2939                                svn_wc_notify_tree_conflict, working_kind,
2940                                svn_wc_notify_state_inapplicable,
2941                                svn_wc_notify_state_inapplicable,
2942                                conflict, NULL, scratch_pool));
2943   return SVN_NO_ERROR;
2944 }
2945 
2946 static svn_error_t *
update_local_add_notify_obstructed_or_missing(added_node_baton_t * nb,svn_node_kind_t working_kind,svn_node_kind_t kind_on_disk,apr_pool_t * scratch_pool)2947 update_local_add_notify_obstructed_or_missing(added_node_baton_t *nb,
2948                                               svn_node_kind_t working_kind,
2949                                               svn_node_kind_t kind_on_disk,
2950                                               apr_pool_t *scratch_pool)
2951 {
2952   svn_wc_notify_state_t content_state;
2953 
2954   if (kind_on_disk == svn_node_none)
2955       content_state = svn_wc_notify_state_missing;
2956   else
2957       content_state = svn_wc_notify_state_obstructed;
2958 
2959   SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
2960                                svn_wc_notify_skip, working_kind,
2961                                content_state, svn_wc_notify_state_inapplicable,
2962                                NULL, NULL, scratch_pool));
2963   return SVN_NO_ERROR;
2964 }
2965 
2966 static svn_error_t *
tc_editor_update_add_new_file(added_node_baton_t * nb,svn_node_kind_t base_kind,const svn_checksum_t * base_checksum,apr_hash_t * base_props,svn_node_kind_t working_kind,const svn_checksum_t * working_checksum,apr_hash_t * working_props,apr_pool_t * scratch_pool)2967 tc_editor_update_add_new_file(added_node_baton_t *nb,
2968                               svn_node_kind_t base_kind,
2969                               const svn_checksum_t *base_checksum,
2970                               apr_hash_t *base_props,
2971                               svn_node_kind_t working_kind,
2972                               const svn_checksum_t *working_checksum,
2973                               apr_hash_t *working_props,
2974                               apr_pool_t *scratch_pool)
2975 {
2976   const char *local_abspath;
2977   svn_node_kind_t kind_on_disk;
2978 
2979   SVN_ERR(update_local_add_mark_parent_edited(nb, scratch_pool));
2980   if (nb->skip)
2981     return SVN_NO_ERROR;
2982 
2983   if (base_kind != svn_node_none)
2984     {
2985       SVN_ERR(mark_update_add_add_tree_conflict(nb, base_kind, svn_node_file,
2986                                                 svn_wc_conflict_reason_added,
2987                                                 scratch_pool, scratch_pool));
2988       nb->skip = TRUE;
2989       return SVN_NO_ERROR;
2990     }
2991 
2992   /* Check for obstructions. */
2993   local_abspath = svn_dirent_join(nb->b->wcroot->abspath, nb->local_relpath,
2994                                   scratch_pool);
2995   SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
2996   if (kind_on_disk != svn_node_file)
2997     {
2998       SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, working_kind,
2999                                                             kind_on_disk,
3000                                                             scratch_pool));
3001       nb->skip = TRUE;
3002       return SVN_NO_ERROR;
3003     }
3004 
3005   /* Nothing else to do. Locally added files are an op-root in NODES. */
3006 
3007   SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
3008                                svn_wc_notify_update_add, svn_node_file,
3009                                svn_wc_notify_state_inapplicable,
3010                                svn_wc_notify_state_inapplicable,
3011                                NULL, NULL, scratch_pool));
3012   return SVN_NO_ERROR;
3013 }
3014 
3015 static svn_error_t *
tc_editor_update_add_new_directory(added_node_baton_t * nb,svn_node_kind_t base_kind,apr_hash_t * base_props,apr_hash_t * working_props,apr_pool_t * scratch_pool)3016 tc_editor_update_add_new_directory(added_node_baton_t *nb,
3017                                    svn_node_kind_t base_kind,
3018                                    apr_hash_t *base_props,
3019                                    apr_hash_t *working_props,
3020                                    apr_pool_t *scratch_pool)
3021 {
3022   const char *local_abspath;
3023   svn_node_kind_t kind_on_disk;
3024 
3025   SVN_ERR(update_local_add_mark_parent_edited(nb, scratch_pool));
3026   if (nb->skip)
3027     return SVN_NO_ERROR;
3028 
3029   if (base_kind != svn_node_none)
3030     {
3031       SVN_ERR(mark_update_add_add_tree_conflict(nb, base_kind, svn_node_dir,
3032                                                 svn_wc_conflict_reason_added,
3033                                                 scratch_pool, scratch_pool));
3034       nb->skip = TRUE;
3035       return SVN_NO_ERROR;
3036     }
3037 
3038   /* Check for obstructions. */
3039   local_abspath = svn_dirent_join(nb->b->wcroot->abspath, nb->local_relpath,
3040                                   scratch_pool);
3041   SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3042   if (kind_on_disk != svn_node_dir)
3043     {
3044       SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_dir,
3045                                                             kind_on_disk,
3046                                                             scratch_pool));
3047       nb->skip = TRUE;
3048       return SVN_NO_ERROR;
3049     }
3050 
3051   /* Nothing else to do. Locally added directories are an op-root in NODES. */
3052 
3053   SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath, nb->b->db,
3054                                svn_wc_notify_update_add, svn_node_dir,
3055                                svn_wc_notify_state_inapplicable,
3056                                svn_wc_notify_state_inapplicable,
3057                                NULL, NULL, scratch_pool));
3058   return SVN_NO_ERROR;
3059 }
3060 
3061 static svn_error_t *
update_incoming_add_merge_props(svn_wc_notify_state_t * prop_state,svn_skel_t ** conflict_skel,const char * local_relpath,apr_hash_t * base_props,apr_hash_t * working_props,svn_wc__db_t * db,svn_wc__db_wcroot_t * wcroot,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3062 update_incoming_add_merge_props(svn_wc_notify_state_t *prop_state,
3063                                 svn_skel_t **conflict_skel,
3064                                 const char *local_relpath,
3065                                 apr_hash_t *base_props,
3066                                 apr_hash_t *working_props,
3067                                 svn_wc__db_t *db,
3068                                 svn_wc__db_wcroot_t *wcroot,
3069                                 apr_pool_t *result_pool,
3070                                 apr_pool_t *scratch_pool)
3071 {
3072   apr_hash_t *new_actual_props;
3073   apr_array_header_t *propchanges;
3074   const char *local_abspath = svn_dirent_join(wcroot->abspath,
3075                                               local_relpath,
3076                                               scratch_pool);
3077 
3078   /*
3079    * Run a 3-way prop merge to update the props, using the empty props
3080    * as the merge base, the post-update props as the merge-left version, and
3081    * the current props of the added working file as the merge-right version.
3082    */
3083   SVN_ERR(svn_prop_diffs(&propchanges, working_props,
3084                          apr_hash_make(scratch_pool), scratch_pool));
3085   SVN_ERR(svn_wc__merge_props(conflict_skel, prop_state, &new_actual_props,
3086                               db, local_abspath,
3087                               apr_hash_make(scratch_pool),
3088                               base_props, working_props, propchanges,
3089                               result_pool, scratch_pool));
3090 
3091   /* Install the new actual props. */
3092   if (apr_hash_count(new_actual_props) > 0)
3093     SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath,
3094                                              new_actual_props,
3095                                              svn_wc__has_magic_property(
3096                                                       propchanges),
3097                                              scratch_pool));
3098 
3099   return SVN_NO_ERROR;
3100 }
3101 
3102 static svn_error_t *
tc_editor_update_add_merge_files(added_node_baton_t * nb,const svn_checksum_t * working_checksum,const svn_checksum_t * base_checksum,apr_hash_t * working_props,apr_hash_t * base_props,apr_pool_t * scratch_pool)3103 tc_editor_update_add_merge_files(added_node_baton_t *nb,
3104                                  const svn_checksum_t *working_checksum,
3105                                  const svn_checksum_t *base_checksum,
3106                                  apr_hash_t *working_props,
3107                                  apr_hash_t *base_props,
3108                                  apr_pool_t *scratch_pool)
3109 {
3110   update_local_add_baton_t *b = nb->b;
3111   apr_array_header_t *propchanges;
3112   svn_boolean_t is_modified;
3113   enum svn_wc_merge_outcome_t merge_outcome;
3114   svn_skel_t *conflict_skel = NULL;
3115   svn_wc_notify_state_t prop_state, content_state;
3116   svn_skel_t *work_items = NULL;
3117   svn_node_kind_t kind_on_disk;
3118   const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
3119                                               nb->local_relpath,
3120                                               scratch_pool);
3121 
3122   SVN_ERR(update_local_add_mark_node_edited(nb, scratch_pool));
3123   if (nb->skip)
3124     return SVN_NO_ERROR;
3125 
3126   /* Check for on-disk obstructions or missing files. */
3127   SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3128   if (kind_on_disk != svn_node_file)
3129     {
3130       SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_file,
3131                                                             kind_on_disk,
3132                                                             scratch_pool));
3133       nb->skip = TRUE;
3134       return SVN_NO_ERROR;
3135     }
3136 
3137   SVN_ERR(update_incoming_add_merge_props(&prop_state, &conflict_skel,
3138                                           nb->local_relpath,
3139                                           base_props, working_props,
3140                                           b->db, b->wcroot,
3141                                           scratch_pool, scratch_pool));
3142 
3143   SVN_ERR(svn_wc__internal_file_modified_p(&is_modified,
3144                                            b->db, local_abspath,
3145                                            FALSE /* exact_comparison */,
3146                                            scratch_pool));
3147   if (!is_modified)
3148     {
3149       svn_skel_t *work_item = NULL;
3150 
3151       SVN_ERR(svn_wc__wq_build_file_install(&work_item, b->db,
3152                                             local_abspath, NULL,
3153                                             /* FIXME: use_commit_times? */
3154                                             FALSE,
3155                                             TRUE,  /* record_file_info */
3156                                             scratch_pool, scratch_pool));
3157       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
3158       content_state = svn_wc_notify_state_changed;
3159     }
3160   else
3161     {
3162       const char *empty_file_abspath;
3163       const char *pristine_abspath;
3164       svn_skel_t *work_item = NULL;
3165 
3166       /*
3167        * Run a 3-way merge to update the file, using the empty file
3168        * merge base, the post-update pristine text as the merge-left version,
3169        * and the locally added content of the working file as the merge-right
3170        * version.
3171        */
3172       SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file_abspath, NULL,
3173                                        svn_io_file_del_on_pool_cleanup,
3174                                        scratch_pool, scratch_pool));
3175       SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, b->db,
3176                                            b->wcroot->abspath, base_checksum,
3177                                            scratch_pool, scratch_pool));
3178 
3179       /* Create a property diff which shows all props as added. */
3180       SVN_ERR(svn_prop_diffs(&propchanges, working_props,
3181                              apr_hash_make(scratch_pool), scratch_pool));
3182 
3183       SVN_ERR(svn_wc__internal_merge(&work_item, &conflict_skel,
3184                                      &merge_outcome, b->db,
3185                                      empty_file_abspath,
3186                                      pristine_abspath,
3187                                      local_abspath,
3188                                      local_abspath,
3189                                      NULL, NULL, NULL, /* diff labels */
3190                                      apr_hash_make(scratch_pool),
3191                                      FALSE, /* dry-run */
3192                                      NULL, /* diff3-cmd */
3193                                      NULL, /* merge options */
3194                                      propchanges,
3195                                      b->cancel_func, b->cancel_baton,
3196                                      scratch_pool, scratch_pool));
3197 
3198       work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
3199 
3200       if (merge_outcome == svn_wc_merge_conflict)
3201         content_state = svn_wc_notify_state_conflicted;
3202       else
3203         content_state = svn_wc_notify_state_merged;
3204     }
3205 
3206   /* If there are any conflicts to be stored, convert them into work items
3207    * too. */
3208   if (conflict_skel)
3209     {
3210       svn_wc_conflict_version_t *new_version;
3211       svn_node_kind_t new_kind;
3212       svn_revnum_t new_rev;
3213       const char *repos_relpath;
3214 
3215       new_version = svn_wc_conflict_version_dup(nb->b->new_version,
3216                                                 scratch_pool);
3217       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev,
3218                                                 &repos_relpath, NULL, NULL,
3219                                                 NULL, NULL, NULL, NULL, NULL,
3220                                                 NULL, NULL, NULL, NULL,
3221                                                 b->wcroot, nb->local_relpath,
3222                                                 scratch_pool, scratch_pool));
3223       /* Fill in conflict info templates with info for this node. */
3224       new_version->path_in_repos = repos_relpath;
3225       new_version->node_kind = new_kind;
3226       new_version->peg_rev = new_rev;
3227 
3228       /* Create conflict markers. */
3229       SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, NULL,
3230                                                   new_version, scratch_pool,
3231                                                   scratch_pool));
3232       if (prop_state == svn_wc_notify_state_conflicted)
3233         SVN_ERR(svn_wc__conflict_create_markers(&work_items, b->db,
3234                                                 local_abspath,
3235                                                 conflict_skel,
3236                                                 scratch_pool,
3237                                                 scratch_pool));
3238     }
3239 
3240   SVN_ERR(update_move_list_add(b->wcroot, nb->local_relpath, b->db,
3241                                svn_wc_notify_update_update,
3242                                svn_node_file, content_state, prop_state,
3243                                conflict_skel, work_items, scratch_pool));
3244 
3245   return SVN_NO_ERROR;
3246 }
3247 
3248 static svn_error_t *
tc_editor_update_add_merge_dirprops(added_node_baton_t * nb,apr_hash_t * working_props,apr_hash_t * base_props,apr_pool_t * scratch_pool)3249 tc_editor_update_add_merge_dirprops(added_node_baton_t *nb,
3250                                     apr_hash_t *working_props,
3251                                     apr_hash_t *base_props,
3252                                     apr_pool_t *scratch_pool)
3253 {
3254   update_local_add_baton_t *b = nb->b;
3255   svn_skel_t *conflict_skel = NULL;
3256   svn_wc_notify_state_t prop_state;
3257   svn_skel_t *work_items = NULL;
3258   svn_node_kind_t kind_on_disk;
3259   const char *local_abspath = svn_dirent_join(b->wcroot->abspath,
3260                                               nb->local_relpath,
3261                                               scratch_pool);
3262 
3263   SVN_ERR(update_local_add_mark_node_edited(nb, scratch_pool));
3264   if (nb->skip)
3265     return SVN_NO_ERROR;
3266 
3267   /* Check for on-disk obstructions or missing files. */
3268   SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
3269   if (kind_on_disk != svn_node_dir)
3270     {
3271       SVN_ERR(update_local_add_notify_obstructed_or_missing(nb, svn_node_dir,
3272                                                             kind_on_disk,
3273                                                             scratch_pool));
3274       nb->skip = TRUE;
3275       return SVN_NO_ERROR;
3276     }
3277 
3278   SVN_ERR(update_incoming_add_merge_props(&prop_state, &conflict_skel,
3279                                           nb->local_relpath,
3280                                           base_props, working_props,
3281                                           b->db, b->wcroot,
3282                                           scratch_pool, scratch_pool));
3283 
3284   /* If there are any conflicts to be stored, convert them into work items. */
3285   if (conflict_skel && prop_state == svn_wc_notify_state_conflicted)
3286     {
3287       svn_wc_conflict_version_t *new_version;
3288       svn_node_kind_t new_kind;
3289       svn_revnum_t new_rev;
3290       const char *repos_relpath;
3291 
3292       new_version = svn_wc_conflict_version_dup(nb->b->new_version,
3293                                                 scratch_pool);
3294       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, &new_rev,
3295                                                 &repos_relpath, NULL, NULL,
3296                                                 NULL, NULL, NULL, NULL, NULL,
3297                                                 NULL, NULL, NULL, NULL,
3298                                                 b->wcroot, nb->local_relpath,
3299                                                 scratch_pool, scratch_pool));
3300       /* Fill in conflict info templates with info for this node. */
3301       new_version->path_in_repos = repos_relpath;
3302       new_version->node_kind = new_kind;
3303       new_version->peg_rev = new_rev;
3304 
3305       /* Create conflict markers. */
3306       SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_skel, NULL,
3307                                                   new_version, scratch_pool,
3308                                                   scratch_pool));
3309       SVN_ERR(svn_wc__conflict_create_markers(&work_items, b->db,
3310                                               local_abspath,
3311                                               conflict_skel,
3312                                               scratch_pool,
3313                                               scratch_pool));
3314     }
3315 
3316   SVN_ERR(update_move_list_add(b->wcroot, nb->local_relpath, b->db,
3317                                svn_wc_notify_update_update, svn_node_dir,
3318                                svn_wc_notify_state_inapplicable, prop_state,
3319                                conflict_skel, work_items, scratch_pool));
3320 
3321   return SVN_NO_ERROR;
3322 }
3323 
3324 static svn_error_t *
update_locally_added_node(added_node_baton_t * nb,apr_pool_t * scratch_pool)3325 update_locally_added_node(added_node_baton_t *nb,
3326                           apr_pool_t *scratch_pool)
3327 {
3328   update_local_add_baton_t *b = nb->b;
3329   svn_wc__db_wcroot_t *wcroot = b->wcroot;
3330   svn_wc__db_t *db = b->db;
3331   svn_node_kind_t base_kind, working_kind;
3332   const svn_checksum_t *base_checksum;
3333   apr_hash_t *base_props, *working_props;
3334   apr_array_header_t *base_children, *working_children;
3335   const char *local_abspath = svn_dirent_join(wcroot->abspath,
3336                                               nb->local_relpath,
3337                                               scratch_pool);
3338 
3339   if (b->cancel_func)
3340     SVN_ERR(b->cancel_func(b->cancel_baton));
3341 
3342   if (nb->skip)
3343     return SVN_NO_ERROR;
3344 
3345   /* Compare the tree conflict victim's BASE layer to the working layer. */
3346   SVN_ERR(get_info(&base_props, &base_checksum, &base_children, &base_kind,
3347                    nb->local_relpath, 0, wcroot, scratch_pool, scratch_pool));
3348   SVN_ERR(get_working_info(&working_props, NULL, &working_children,
3349                            &working_kind, nb->local_relpath, wcroot,
3350                            scratch_pool, scratch_pool));
3351   if (working_kind == svn_node_none)
3352     {
3353       svn_node_kind_t kind_on_disk;
3354       svn_skel_t *work_item = NULL;
3355 
3356       /* Skip obstructed nodes. */
3357       SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk,
3358                                 scratch_pool));
3359       if (kind_on_disk != base_kind && kind_on_disk != svn_node_none)
3360         {
3361           SVN_ERR(update_move_list_add(nb->b->wcroot, nb->local_relpath,
3362                                        nb->b->db,
3363                                        svn_wc_notify_skip,
3364                                        base_kind,
3365                                        svn_wc_notify_state_obstructed,
3366                                        svn_wc_notify_state_inapplicable,
3367                                        NULL, NULL, scratch_pool));
3368           nb->skip = TRUE;
3369           return SVN_NO_ERROR;
3370         }
3371 
3372       /* The working tree has no node here. The working copy of this node
3373        * is currently not installed because the base tree is shadowed.
3374        * Queue an installation of this node into the working copy. */
3375       if (base_kind == svn_node_file || base_kind == svn_node_symlink)
3376         SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, local_abspath,
3377                                               NULL,
3378                                               /* FIXME: use_commit_times? */
3379                                               FALSE,
3380                                               TRUE,  /* record_file_info */
3381                                               scratch_pool, scratch_pool));
3382       else if (base_kind == svn_node_dir)
3383         SVN_ERR(svn_wc__wq_build_dir_install(&work_item, db, local_abspath,
3384                                              scratch_pool, scratch_pool));
3385 
3386       if (work_item)
3387         SVN_ERR(update_move_list_add(wcroot, nb->local_relpath, db,
3388                                      svn_wc_notify_update_add,
3389                                      base_kind,
3390                                      svn_wc_notify_state_inapplicable,
3391                                      svn_wc_notify_state_inapplicable,
3392                                      NULL, work_item, scratch_pool));
3393       return SVN_NO_ERROR;
3394     }
3395 
3396   if (base_kind != working_kind)
3397     {
3398       if (working_kind == svn_node_file || working_kind == svn_node_symlink)
3399         {
3400           svn_checksum_t *working_checksum = NULL;
3401 
3402           if (base_checksum)
3403             SVN_ERR(svn_io_file_checksum2(&working_checksum, local_abspath,
3404                                           base_checksum->kind, scratch_pool));
3405           SVN_ERR(tc_editor_update_add_new_file(nb, base_kind, base_checksum,
3406                                                 base_props, working_kind,
3407                                                 working_checksum, working_props,
3408                                                 scratch_pool));
3409         }
3410       else if (working_kind == svn_node_dir)
3411         SVN_ERR(tc_editor_update_add_new_directory(nb, base_kind, base_props,
3412                                                    working_props,
3413                                                    scratch_pool));
3414     }
3415   else
3416     {
3417       svn_boolean_t props_equal;
3418 
3419       SVN_ERR(props_match(&props_equal, base_props, working_props,
3420                           scratch_pool));
3421 
3422       if (working_kind == svn_node_file || working_kind == svn_node_symlink)
3423         {
3424           svn_checksum_t *working_checksum;
3425 
3426           SVN_ERR_ASSERT(base_checksum);
3427           SVN_ERR(svn_io_file_checksum2(&working_checksum, local_abspath,
3428                                         base_checksum->kind, scratch_pool));
3429           if (!props_equal || !svn_checksum_match(base_checksum,
3430                                                   working_checksum))
3431             SVN_ERR(tc_editor_update_add_merge_files(nb, working_checksum,
3432                                                      base_checksum,
3433                                                      working_props, base_props,
3434                                                      scratch_pool));
3435         }
3436       else if (working_kind == svn_node_dir && !props_equal)
3437         SVN_ERR(tc_editor_update_add_merge_dirprops(nb, working_props,
3438                                                     base_props,
3439                                                     scratch_pool));
3440     }
3441 
3442   if (nb->skip)
3443     return SVN_NO_ERROR;
3444 
3445   if (working_kind == svn_node_dir)
3446     {
3447       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3448       int i = 0, j = 0;
3449 
3450       while (i < base_children->nelts || j < working_children->nelts)
3451         {
3452           const char *child_name;
3453           svn_boolean_t base_only = FALSE, working_only = FALSE;
3454           added_node_baton_t cnb = { 0 };
3455 
3456           cnb.pb = nb;
3457           cnb.b = nb->b;
3458           cnb.skip = FALSE;
3459 
3460           svn_pool_clear(iterpool);
3461           if (i >= base_children->nelts)
3462             {
3463               working_only = TRUE;
3464               child_name = APR_ARRAY_IDX(working_children, j, const char *);
3465             }
3466           else if (j >= working_children->nelts)
3467             {
3468               base_only = TRUE;
3469               child_name = APR_ARRAY_IDX(base_children, i, const char *);
3470             }
3471           else
3472             {
3473               const char *base_name = APR_ARRAY_IDX(base_children, i,
3474                                                     const char *);
3475               const char *working_name = APR_ARRAY_IDX(working_children, j,
3476                                                        const char *);
3477               int cmp = strcmp(base_name, working_name);
3478 
3479               if (cmp > 0)
3480                 working_only = TRUE;
3481               else if (cmp < 0)
3482                 base_only = TRUE;
3483 
3484               child_name = working_only ? working_name : base_name;
3485             }
3486 
3487           cnb.local_relpath = svn_relpath_join(nb->local_relpath, child_name,
3488                                                iterpool);
3489 
3490           SVN_ERR(update_locally_added_node(&cnb, iterpool));
3491 
3492           if (!working_only)
3493             ++i;
3494           if (!base_only)
3495             ++j;
3496 
3497           if (nb->skip) /* Does parent now want a skip? */
3498             break;
3499         }
3500     }
3501 
3502   return SVN_NO_ERROR;
3503 }
3504 
3505 /* The body of svn_wc__db_update_local_add(). */
3506 static svn_error_t *
update_local_add(svn_revnum_t * new_rev,svn_wc__db_t * db,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)3507 update_local_add(svn_revnum_t *new_rev,
3508                 svn_wc__db_t *db,
3509                 svn_wc__db_wcroot_t *wcroot,
3510                 const char *local_relpath,
3511                 svn_cancel_func_t cancel_func,
3512                 void *cancel_baton,
3513                 apr_pool_t *scratch_pool)
3514 {
3515   update_local_add_baton_t b = { 0 };
3516   added_node_baton_t nb = { 0 };
3517   const char *repos_root_url;
3518   const char *repos_uuid;
3519   const char *repos_relpath;
3520   apr_int64_t repos_id;
3521   svn_node_kind_t new_kind;
3522   svn_sqlite__stmt_t *stmt;
3523 
3524   b.add_op_depth = relpath_depth(local_relpath); /* DST op-root */
3525 
3526   SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
3527 
3528   b.db = db;
3529   b.wcroot = wcroot;
3530   b.cancel_func = cancel_func;
3531   b.cancel_baton = cancel_baton;
3532 
3533   /* Read new version info from the updated BASE node. */
3534   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &new_kind, new_rev,
3535                                             &repos_relpath, &repos_id,
3536                                             NULL, NULL, NULL, NULL, NULL,
3537                                             NULL, NULL, NULL, NULL, NULL,
3538                                             wcroot, local_relpath,
3539                                             scratch_pool, scratch_pool));
3540   SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid, wcroot,
3541                                       repos_id, scratch_pool));
3542   b.new_version = svn_wc_conflict_version_create2(repos_root_url, repos_uuid,
3543                                                   repos_relpath, *new_rev,
3544                                                   new_kind, scratch_pool);
3545 
3546   /* Create a new, and empty, list for notification information. */
3547   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
3548                                       STMT_CREATE_UPDATE_MOVE_LIST));
3549 
3550   /* Drive the editor... */
3551   nb.b = &b;
3552   nb.local_relpath = local_relpath;
3553   nb.skip = FALSE;
3554   SVN_ERR(update_locally_added_node(&nb, scratch_pool));
3555 
3556   /* The conflict victim is now part of the base tree.
3557    * Remove the locally added version of the conflict victim and its children.
3558    * Any children we want to retain are at a higher op-depth so they won't
3559    * be deleted by this statement. */
3560   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3561                                     STMT_DELETE_WORKING_OP_DEPTH));
3562   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
3563                             relpath_depth(local_relpath)));
3564   SVN_ERR(svn_sqlite__update(NULL, stmt));
3565 
3566   /* Remove the tree conflict marker. */
3567   SVN_ERR(svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
3568                                                FALSE, FALSE, TRUE,
3569                                                NULL, scratch_pool));
3570   return SVN_NO_ERROR;
3571 }
3572 
3573 svn_error_t *
svn_wc__db_update_local_add(svn_wc__db_t * db,const char * local_abspath,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)3574 svn_wc__db_update_local_add(svn_wc__db_t *db,
3575                             const char *local_abspath,
3576                             svn_cancel_func_t cancel_func,
3577                             void *cancel_baton,
3578                             svn_wc_notify_func2_t notify_func,
3579                             void *notify_baton,
3580                             apr_pool_t *scratch_pool)
3581 {
3582   svn_wc__db_wcroot_t *wcroot;
3583   svn_revnum_t new_rev;
3584   const char *local_relpath;
3585 
3586   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
3587                                                 db, local_abspath,
3588                                                 scratch_pool, scratch_pool));
3589   VERIFY_USABLE_WCROOT(wcroot);
3590 
3591   SVN_WC__DB_WITH_TXN(update_local_add(&new_rev, db, wcroot,
3592                                        local_relpath,
3593                                        cancel_func, cancel_baton,
3594                                        scratch_pool),
3595                       wcroot);
3596 
3597   /* Send all queued up notifications. */
3598   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, new_rev, new_rev,
3599                                              notify_func, notify_baton,
3600                                              scratch_pool));
3601   if (notify_func)
3602     {
3603       svn_wc_notify_t *notify;
3604 
3605       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
3606                                                     local_relpath,
3607                                                     scratch_pool),
3608                                     svn_wc_notify_update_completed,
3609                                     scratch_pool);
3610       notify->kind = svn_node_none;
3611       notify->content_state = svn_wc_notify_state_inapplicable;
3612       notify->prop_state = svn_wc_notify_state_inapplicable;
3613       notify->revision = new_rev;
3614       notify_func(notify_baton, notify, scratch_pool);
3615     }
3616 
3617 
3618   return SVN_NO_ERROR;
3619 }
3620 /* Set *CAN_BUMP to TRUE if DEPTH is sufficient to cover the entire
3621    tree  LOCAL_RELPATH at OP_DEPTH, to FALSE otherwise. */
3622 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)3623 depth_sufficient_to_bump(svn_boolean_t *can_bump,
3624                          svn_wc__db_wcroot_t *wcroot,
3625                          const char *local_relpath,
3626                          int op_depth,
3627                          svn_depth_t depth,
3628                          apr_pool_t *scratch_pool)
3629 {
3630   svn_sqlite__stmt_t *stmt;
3631   svn_boolean_t have_row;
3632 
3633   switch (depth)
3634     {
3635     case svn_depth_infinity:
3636       *can_bump = TRUE;
3637       return SVN_NO_ERROR;
3638 
3639     case svn_depth_empty:
3640       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3641                                         STMT_SELECT_OP_DEPTH_CHILDREN));
3642       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3643                                 local_relpath, op_depth));
3644       break;
3645 
3646     case svn_depth_files:
3647       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3648                                         STMT_SELECT_HAS_NON_FILE_CHILDREN));
3649       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3650                                 local_relpath, op_depth));
3651       break;
3652 
3653     case svn_depth_immediates:
3654       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3655                                         STMT_SELECT_HAS_GRANDCHILDREN));
3656       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
3657                                 local_relpath, op_depth));
3658       break;
3659     default:
3660       SVN_ERR_MALFUNCTION();
3661     }
3662   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3663   SVN_ERR(svn_sqlite__reset(stmt));
3664 
3665   *can_bump = !have_row;
3666   return SVN_NO_ERROR;
3667 }
3668 
3669 /* Mark a move-edit conflict on MOVE_SRC_ROOT_RELPATH. */
3670 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)3671 bump_mark_tree_conflict(svn_wc__db_wcroot_t *wcroot,
3672                         const char *move_src_root_relpath,
3673                         int src_op_depth,
3674                         const char *move_src_op_root_relpath,
3675                         const char *move_dst_op_root_relpath,
3676                         svn_wc__db_t *db,
3677                         apr_pool_t *scratch_pool)
3678 {
3679   apr_int64_t repos_id;
3680   const char *repos_root_url;
3681   const char *repos_uuid;
3682   const char *old_repos_relpath;
3683   const char *new_repos_relpath;
3684   svn_revnum_t old_rev;
3685   svn_revnum_t new_rev;
3686   svn_node_kind_t old_kind;
3687   svn_node_kind_t new_kind;
3688   svn_wc_conflict_version_t *old_version;
3689   svn_wc_conflict_version_t *new_version;
3690   svn_skel_t *conflict;
3691 
3692   /* Verify precondition: We are allowed to set a tree conflict here. */
3693   SVN_ERR(verify_write_lock(wcroot, move_src_root_relpath, scratch_pool));
3694 
3695   /* Read new (post-update) information from the new move source BASE node. */
3696   SVN_ERR(svn_wc__db_depth_get_info(NULL, &new_kind, &new_rev,
3697                                     &new_repos_relpath, &repos_id,
3698                                     NULL, NULL, NULL, NULL, NULL,
3699                                     NULL, NULL, NULL,
3700                                     wcroot, move_src_op_root_relpath,
3701                                     src_op_depth, scratch_pool, scratch_pool));
3702   SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
3703                                       wcroot, repos_id, scratch_pool));
3704 
3705   /* Read old (pre-update) information from the move destination node.
3706 
3707      This potentially touches nodes that aren't locked by us, but that is not
3708      a problem because we have a SQLite write lock here, and all sqlite
3709      operations that affect move stability use a sqlite lock as well.
3710      (And affecting the move itself requires a write lock on the node that
3711       we do own the lock for: the move source)
3712   */
3713   SVN_ERR(svn_wc__db_depth_get_info(NULL, &old_kind, &old_rev,
3714                                     &old_repos_relpath, NULL, NULL, NULL,
3715                                     NULL, NULL, NULL, NULL, NULL, NULL,
3716                                     wcroot, move_dst_op_root_relpath,
3717                                     relpath_depth(move_dst_op_root_relpath),
3718                                     scratch_pool, scratch_pool));
3719 
3720   if (strcmp(move_src_root_relpath, move_src_op_root_relpath))
3721     {
3722       /* We have information for the op-root, but need it for the node that
3723          we are putting the tree conflict on. Luckily we know that we have
3724          a clean BASE */
3725 
3726       const char *rpath = svn_relpath_skip_ancestor(move_src_op_root_relpath,
3727                                                     move_src_root_relpath);
3728 
3729       old_repos_relpath = svn_relpath_join(old_repos_relpath, rpath,
3730                                            scratch_pool);
3731       new_repos_relpath = svn_relpath_join(new_repos_relpath, rpath,
3732                                            scratch_pool);
3733     }
3734 
3735   old_version = svn_wc_conflict_version_create2(
3736                   repos_root_url, repos_uuid, old_repos_relpath, old_rev,
3737                   old_kind, scratch_pool);
3738   new_version = svn_wc_conflict_version_create2(
3739                   repos_root_url, repos_uuid, new_repos_relpath, new_rev,
3740                   new_kind, scratch_pool);
3741 
3742   SVN_ERR(create_tree_conflict(&conflict, wcroot, move_src_root_relpath,
3743                                move_dst_op_root_relpath,
3744                                db, old_version, new_version,
3745                                svn_wc_operation_update,
3746                                old_kind, new_kind,
3747                                old_repos_relpath,
3748                                svn_wc_conflict_reason_moved_away,
3749                                svn_wc_conflict_action_edit,
3750                                move_src_op_root_relpath,
3751                                scratch_pool, scratch_pool));
3752 
3753   SVN_ERR(update_move_list_add(wcroot, move_src_root_relpath, db,
3754                                svn_wc_notify_tree_conflict,
3755                                new_kind,
3756                                svn_wc_notify_state_inapplicable,
3757                                svn_wc_notify_state_inapplicable,
3758                                conflict, NULL, scratch_pool));
3759 
3760   return SVN_NO_ERROR;
3761 }
3762 
3763 /* Checks if SRC_RELPATH is within BUMP_DEPTH from BUMP_ROOT. Sets
3764  * *SKIP to TRUE if the node should be skipped, otherwise to FALSE.
3765  * Sets *SRC_DEPTH to the remaining depth at SRC_RELPATH.
3766  */
3767 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)3768 check_bump_layer(svn_boolean_t *skip,
3769                  svn_depth_t *src_depth,
3770                  const char *bump_root,
3771                  svn_depth_t bump_depth,
3772                  const char *src_relpath,
3773                  svn_node_kind_t src_kind,
3774                  apr_pool_t *scratch_pool)
3775 {
3776   const char *relpath;
3777 
3778   *skip = FALSE;
3779   *src_depth = bump_depth;
3780 
3781   relpath = svn_relpath_skip_ancestor(bump_root, src_relpath);
3782 
3783   if (!relpath)
3784     *skip = TRUE;
3785 
3786   if (bump_depth == svn_depth_infinity)
3787     return SVN_NO_ERROR;
3788 
3789   if (relpath && *relpath == '\0')
3790     return SVN_NO_ERROR;
3791 
3792   switch (bump_depth)
3793     {
3794       case svn_depth_empty:
3795         *skip = TRUE;
3796         break;
3797 
3798       case svn_depth_files:
3799         if (src_kind != svn_node_file)
3800           {
3801             *skip = TRUE;
3802             break;
3803           }
3804         /* Fallthrough */
3805       case svn_depth_immediates:
3806         if (!relpath || relpath_depth(relpath) > 1)
3807           *skip = TRUE;
3808 
3809         *src_depth = svn_depth_empty;
3810         break;
3811       default:
3812         SVN_ERR_MALFUNCTION();
3813     }
3814 
3815   return SVN_NO_ERROR;
3816 }
3817 
3818 /* The guts of bump_moved_away: Determines if a move can be bumped to match
3819  * the move origin and if so performs this bump.
3820  */
3821 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)3822 bump_moved_layer(svn_boolean_t *recurse,
3823                  svn_wc__db_wcroot_t *wcroot,
3824                  const char *local_relpath,
3825                  int op_depth,
3826                  const char *src_relpath,
3827                  int src_del_depth,
3828                  svn_depth_t src_depth,
3829                  const char *dst_relpath,
3830                  svn_wc__db_t *db,
3831                  apr_pool_t *scratch_pool)
3832 {
3833   svn_sqlite__stmt_t *stmt;
3834   svn_boolean_t have_row;
3835   svn_skel_t *conflict;
3836   svn_boolean_t can_bump;
3837   const char *src_root_relpath;
3838 
3839   SVN_ERR(verify_write_lock(wcroot, local_relpath, scratch_pool));
3840 
3841   *recurse = FALSE;
3842 
3843   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3844                                     STMT_HAS_LAYER_BETWEEN));
3845 
3846   SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
3847                             op_depth, src_del_depth));
3848 
3849   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3850   SVN_ERR(svn_sqlite__reset(stmt));
3851 
3852   if (have_row)
3853     return SVN_NO_ERROR;
3854 
3855   if (op_depth == 0)
3856     SVN_ERR(depth_sufficient_to_bump(&can_bump, wcroot, src_relpath,
3857                                      op_depth, src_depth, scratch_pool));
3858   else
3859     /* Having chosen to bump an entire BASE tree move we
3860        always have sufficient depth to bump subtree moves. */
3861     can_bump = TRUE;
3862 
3863   /* Are we allowed to bump */
3864   if (can_bump)
3865     {
3866       svn_boolean_t locked;
3867 
3868       SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
3869                                                    dst_relpath,
3870                                                    FALSE, scratch_pool));
3871 
3872       if (!locked)
3873         can_bump = FALSE;
3874     }
3875 
3876   src_root_relpath = svn_relpath_prefix(src_relpath, src_del_depth,
3877                                         scratch_pool);
3878 
3879   if (!can_bump)
3880     {
3881       SVN_ERR(bump_mark_tree_conflict(wcroot, src_relpath, op_depth,
3882                                       src_root_relpath, dst_relpath,
3883                                       db, scratch_pool));
3884 
3885       return SVN_NO_ERROR;
3886     }
3887 
3888   SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
3889                                             wcroot, src_root_relpath,
3890                                             scratch_pool, scratch_pool));
3891 
3892   /* ### TODO: check this is the right sort of tree-conflict? */
3893   if (!conflict)
3894     {
3895       /* ### TODO: verify moved_here? */
3896 
3897       SVN_ERR(verify_write_lock(wcroot, src_relpath, scratch_pool));
3898 
3899       SVN_ERR(svn_wc__db_op_copy_layer_internal(wcroot,
3900                                                 src_relpath, op_depth,
3901                                                 dst_relpath, NULL, NULL,
3902                                                 scratch_pool));
3903 
3904       *recurse = TRUE;
3905     }
3906 
3907   return SVN_NO_ERROR;
3908 }
3909 
3910 /* Internal storage for bump_moved_away() */
3911 struct bump_pair_t
3912 {
3913   const char *src_relpath;
3914   const char *dst_relpath;
3915   int src_del_op_depth;
3916   svn_node_kind_t src_kind;
3917 };
3918 
3919 /* Bump moves of LOCAL_RELPATH and all its descendants that were
3920    originally below LOCAL_RELPATH at op-depth OP_DEPTH.
3921  */
3922 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)3923 bump_moved_away(svn_wc__db_wcroot_t *wcroot,
3924                 const char *local_relpath,
3925                 int op_depth,
3926                 svn_depth_t depth,
3927                 svn_wc__db_t *db,
3928                 apr_pool_t *scratch_pool)
3929 {
3930   svn_sqlite__stmt_t *stmt;
3931   svn_boolean_t have_row;
3932   apr_pool_t *iterpool;
3933   int i;
3934   apr_array_header_t *pairs = apr_array_make(scratch_pool, 32,
3935                                              sizeof(struct bump_pair_t*));
3936 
3937   /* Build an array, as we can't execute the same Sqlite query recursively */
3938   iterpool = svn_pool_create(scratch_pool);
3939 
3940   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3941                                     STMT_SELECT_MOVED_PAIR3));
3942   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
3943                             op_depth));
3944   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3945   while(have_row)
3946     {
3947       struct bump_pair_t *bp = apr_pcalloc(scratch_pool, sizeof(*bp));
3948 
3949       bp->src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
3950       bp->dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
3951       bp->src_del_op_depth = svn_sqlite__column_int(stmt, 2);
3952       bp->src_kind = svn_sqlite__column_token(stmt, 3, kind_map);
3953 
3954       APR_ARRAY_PUSH(pairs, struct bump_pair_t *) = bp;
3955 
3956       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3957     }
3958 
3959   SVN_ERR(svn_sqlite__reset(stmt));
3960 
3961   for (i = 0; i < pairs->nelts; i++)
3962     {
3963       struct bump_pair_t *bp = APR_ARRAY_IDX(pairs, i, struct bump_pair_t *);
3964       svn_boolean_t skip;
3965       svn_depth_t src_wc_depth;
3966 
3967       svn_pool_clear(iterpool);
3968 
3969 
3970       SVN_ERR(check_bump_layer(&skip, &src_wc_depth, local_relpath, depth,
3971                                bp->src_relpath, bp->src_kind, iterpool));
3972 
3973       if (!skip)
3974         {
3975           svn_boolean_t recurse;
3976 
3977           SVN_ERR(bump_moved_layer(&recurse, wcroot,
3978                                    local_relpath, op_depth,
3979                                    bp->src_relpath, bp->src_del_op_depth,
3980                                    src_wc_depth, bp->dst_relpath,
3981                                    db, iterpool));
3982 
3983           if (recurse)
3984             SVN_ERR(bump_moved_away(wcroot, bp->dst_relpath,
3985                                     relpath_depth(bp->dst_relpath),
3986                                     depth, db, iterpool));
3987         }
3988     }
3989 
3990   svn_pool_destroy(iterpool);
3991 
3992   return SVN_NO_ERROR;
3993 }
3994 
3995 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)3996 svn_wc__db_bump_moved_away(svn_wc__db_wcroot_t *wcroot,
3997                            const char *local_relpath,
3998                            svn_depth_t depth,
3999                            svn_wc__db_t *db,
4000                            apr_pool_t *scratch_pool)
4001 {
4002   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4003                                       STMT_CREATE_UPDATE_MOVE_LIST));
4004 
4005   if (local_relpath[0] != '\0')
4006     {
4007       const char *move_dst_op_root_relpath;
4008       const char *move_src_root_relpath, *delete_relpath;
4009       svn_error_t *err;
4010 
4011       /* Is the root of the update moved away? (Impossible for the wcroot) */
4012 
4013       err = svn_wc__db_scan_moved_to_internal(&move_src_root_relpath,
4014                                               &move_dst_op_root_relpath,
4015                                               &delete_relpath,
4016                                               wcroot, local_relpath,
4017                                               0 /* BASE */,
4018                                               scratch_pool, scratch_pool);
4019 
4020       if (err)
4021         {
4022           if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4023             return svn_error_trace(err);
4024 
4025           svn_error_clear(err);
4026         }
4027       else if (move_src_root_relpath)
4028         {
4029           if (strcmp(move_src_root_relpath, local_relpath))
4030             {
4031               /* An ancestor of the path that was updated is moved away.
4032 
4033                  If we have a lock on that ancestor, we can mark a tree
4034                  conflict on it, if we don't we ignore this case. A future
4035                  update of the ancestor will handle this. */
4036               svn_boolean_t locked;
4037 
4038               SVN_ERR(svn_wc__db_wclock_owns_lock_internal(
4039                                 &locked, wcroot,
4040                                 move_src_root_relpath,
4041                                 FALSE, scratch_pool));
4042 
4043               if (locked)
4044                 {
4045                   SVN_ERR(bump_mark_tree_conflict(wcroot,
4046                                                   move_src_root_relpath, 0,
4047                                                   delete_relpath,
4048                                                   move_dst_op_root_relpath,
4049                                                   db, scratch_pool));
4050                 }
4051               return SVN_NO_ERROR;
4052             }
4053         }
4054     }
4055 
4056   SVN_ERR(bump_moved_away(wcroot, local_relpath, 0, depth, db, scratch_pool));
4057 
4058   return SVN_NO_ERROR;
4059 }
4060 
4061 /* Set *OPERATION, *LOCAL_CHANGE, *INCOMING_CHANGE, *OLD_VERSION, *NEW_VERSION
4062  * to reflect the tree conflict on the victim SRC_ABSPATH in DB.
4063  *
4064  * If SRC_ABSPATH is not a tree-conflict victim, return an error.
4065  */
4066 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)4067 fetch_conflict_details(int *src_op_depth,
4068                        svn_wc_operation_t *operation,
4069                        svn_wc_conflict_action_t *action,
4070                        svn_wc_conflict_version_t **left_version,
4071                        svn_wc_conflict_version_t **right_version,
4072                        svn_wc__db_wcroot_t *wcroot,
4073                        svn_wc__db_t *db,
4074                        const char *local_relpath,
4075                        const svn_skel_t *conflict_skel,
4076                        apr_pool_t *result_pool,
4077                        apr_pool_t *scratch_pool)
4078 {
4079   const apr_array_header_t *locations;
4080   svn_boolean_t text_conflicted;
4081   svn_boolean_t prop_conflicted;
4082   svn_boolean_t tree_conflicted;
4083   const char *move_src_op_root_abspath;
4084   svn_wc_conflict_reason_t reason;
4085   const char *local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
4086                                               scratch_pool);
4087 
4088   if (!conflict_skel)
4089     return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
4090                              _("'%s' is not in conflict"),
4091                              path_for_error_message(wcroot, local_relpath,
4092                                                     scratch_pool));
4093 
4094   SVN_ERR(svn_wc__conflict_read_info(operation, &locations,
4095                                      &text_conflicted, &prop_conflicted,
4096                                      &tree_conflicted,
4097                                      db, local_abspath,
4098                                      conflict_skel, result_pool,
4099                                      scratch_pool));
4100 
4101   if (text_conflicted || prop_conflicted || !tree_conflicted)
4102     return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
4103                              _("'%s' is not a valid tree-conflict victim"),
4104                              path_for_error_message(wcroot, local_relpath,
4105                                                     scratch_pool));
4106 
4107   SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason,
4108                                               action,
4109                                               &move_src_op_root_abspath, NULL,
4110                                               db, local_abspath,
4111                                               conflict_skel, result_pool,
4112                                               scratch_pool));
4113 
4114   if (reason == svn_wc_conflict_reason_moved_away)
4115     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4116                              _("'%s' is already a moved away tree-conflict"),
4117                              path_for_error_message(wcroot, local_relpath,
4118                                                     scratch_pool));
4119 
4120   if (left_version)
4121     {
4122       if (locations && locations->nelts > 0)
4123         *left_version = APR_ARRAY_IDX(locations, 0,
4124                                      svn_wc_conflict_version_t *);
4125       else
4126         *left_version = NULL;
4127     }
4128 
4129   if (right_version)
4130     {
4131       if (locations && locations->nelts > 1)
4132         *right_version = APR_ARRAY_IDX(locations, 1,
4133                                      svn_wc_conflict_version_t *);
4134       else
4135         *right_version = NULL;
4136     }
4137 
4138   {
4139     int del_depth = relpath_depth(local_relpath);
4140 
4141     if (move_src_op_root_abspath)
4142       del_depth = relpath_depth(
4143                       svn_dirent_skip_ancestor(wcroot->abspath,
4144                                                move_src_op_root_abspath));
4145 
4146     SVN_ERR(find_src_op_depth(src_op_depth, wcroot, local_relpath, del_depth,
4147                               scratch_pool));
4148   }
4149 
4150   return SVN_NO_ERROR;
4151 }
4152 
4153 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)4154 svn_wc__db_op_raise_moved_away_internal(
4155                         svn_wc__db_wcroot_t *wcroot,
4156                         const char *local_relpath,
4157                         int src_op_depth,
4158                         svn_wc__db_t *db,
4159                         svn_wc_operation_t operation,
4160                         svn_wc_conflict_action_t action,
4161                         const svn_wc_conflict_version_t *old_version,
4162                         const svn_wc_conflict_version_t *new_version,
4163                         apr_pool_t *scratch_pool)
4164 {
4165   svn_sqlite__stmt_t *stmt;
4166   svn_boolean_t have_row;
4167   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4168 
4169   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4170                                       STMT_CREATE_UPDATE_MOVE_LIST));
4171 
4172   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4173                                     STMT_SELECT_MOVED_DESCENDANTS_SRC));
4174   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
4175                             src_op_depth));
4176   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4177   while(have_row)
4178     {
4179       svn_error_t *err;
4180       int delete_op_depth = svn_sqlite__column_int(stmt, 0);
4181       const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL);
4182       svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map);
4183       const char *src_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4184       const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL);
4185       svn_skel_t *conflict;
4186       svn_pool_clear(iterpool);
4187 
4188       SVN_ERR_ASSERT(src_repos_relpath != NULL);
4189 
4190       err = create_tree_conflict(&conflict, wcroot, src_relpath, dst_relpath,
4191                                  db, old_version, new_version, operation,
4192                                  src_kind /* ### old kind */,
4193                                  src_kind /* ### new kind */,
4194                                  src_repos_relpath,
4195                                  svn_wc_conflict_reason_moved_away,
4196                                  action,
4197                                  svn_relpath_prefix(src_relpath,
4198                                                     delete_op_depth,
4199                                                     iterpool),
4200                                  iterpool, iterpool);
4201 
4202       if (!err)
4203         err = update_move_list_add(wcroot, src_relpath, db,
4204                                    svn_wc_notify_tree_conflict,
4205                                    src_kind,
4206                                    svn_wc_notify_state_inapplicable,
4207                                    svn_wc_notify_state_inapplicable,
4208                                    conflict, NULL, scratch_pool);
4209 
4210       if (err)
4211         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
4212 
4213       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4214     }
4215   SVN_ERR(svn_sqlite__reset(stmt));
4216 
4217   svn_pool_destroy(iterpool);
4218 
4219   return SVN_NO_ERROR;
4220 }
4221 
4222 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)4223 svn_wc__db_op_raise_moved_away(svn_wc__db_t *db,
4224                                const char *local_abspath,
4225                                svn_wc_notify_func2_t notify_func,
4226                                void *notify_baton,
4227                                apr_pool_t *scratch_pool)
4228 {
4229   svn_wc__db_wcroot_t *wcroot;
4230   const char *local_relpath;
4231   svn_wc_operation_t operation;
4232   svn_wc_conflict_action_t action;
4233   svn_wc_conflict_version_t *left_version, *right_version;
4234   int move_src_op_depth;
4235   svn_skel_t *conflict;
4236 
4237   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4238                                                 db, local_abspath,
4239                                                 scratch_pool, scratch_pool));
4240   VERIFY_USABLE_WCROOT(wcroot);
4241 
4242   SVN_WC__DB_WITH_TXN4(
4243     svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
4244                                       wcroot, local_relpath,
4245                                       scratch_pool, scratch_pool),
4246     fetch_conflict_details(&move_src_op_depth,
4247                            &operation, &action,
4248                            &left_version, &right_version,
4249                            wcroot, db, local_relpath, conflict,
4250                            scratch_pool, scratch_pool),
4251     svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
4252                                          FALSE, FALSE, TRUE,
4253                                          NULL, scratch_pool),
4254     svn_wc__db_op_raise_moved_away_internal(wcroot, local_relpath,
4255                                             move_src_op_depth,
4256                                             db, operation, action,
4257                                             left_version, right_version,
4258                                             scratch_pool),
4259     wcroot);
4260 
4261   /* These version numbers are valid for update/switch notifications
4262      only! */
4263   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
4264                                              (left_version
4265                                               ? left_version->peg_rev
4266                                               : SVN_INVALID_REVNUM),
4267                                              (right_version
4268                                               ? right_version->peg_rev
4269                                               : SVN_INVALID_REVNUM),
4270                                              notify_func, notify_baton,
4271                                              scratch_pool));
4272 
4273   return SVN_NO_ERROR;
4274 }
4275 
4276 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)4277 break_moved_away(svn_wc__db_wcroot_t *wcroot,
4278                  svn_wc__db_t *db,
4279                  const char *local_relpath,
4280                  int parent_src_op_depth,
4281                  apr_pool_t *scratch_pool)
4282 {
4283   svn_sqlite__stmt_t *stmt;
4284   svn_boolean_t have_row;
4285   apr_pool_t *iterpool;
4286   svn_error_t *err = NULL;
4287 
4288   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
4289                                       STMT_CREATE_UPDATE_MOVE_LIST));
4290 
4291   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4292                                     STMT_SELECT_MOVED_DESCENDANTS_SRC));
4293   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
4294                             parent_src_op_depth));
4295   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4296 
4297   iterpool = svn_pool_create(scratch_pool);
4298   while (have_row)
4299     {
4300       int src_op_depth = svn_sqlite__column_int(stmt, 0);
4301       const char *src_relpath = svn_sqlite__column_text(stmt, 1, NULL);
4302       svn_node_kind_t src_kind = svn_sqlite__column_token(stmt, 2, kind_map);
4303       const char *dst_relpath = svn_sqlite__column_text(stmt, 4, NULL);
4304 
4305       svn_pool_clear(iterpool);
4306 
4307       err = verify_write_lock(wcroot, src_relpath, iterpool);
4308 
4309       if (!err)
4310         err = verify_write_lock(wcroot, dst_relpath, iterpool);
4311 
4312       if (err)
4313         break;
4314 
4315       err = svn_error_trace(
4316               svn_wc__db_op_break_move_internal(wcroot,
4317                                                 src_relpath, src_op_depth,
4318                                                 dst_relpath, NULL, iterpool));
4319 
4320       if (err)
4321         break;
4322 
4323       err = svn_error_trace(
4324               update_move_list_add(wcroot, src_relpath, db,
4325                                    svn_wc_notify_move_broken,
4326                                    src_kind,
4327                                    svn_wc_notify_state_inapplicable,
4328                                    svn_wc_notify_state_inapplicable,
4329                                    NULL, NULL, scratch_pool));
4330 
4331       if (err)
4332         break;
4333 
4334       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4335     }
4336   svn_pool_destroy(iterpool);
4337 
4338   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
4339 }
4340 
4341 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)4342 svn_wc__db_op_break_moved_away(svn_wc__db_t *db,
4343                                const char *local_abspath,
4344                                const char *del_op_root_abspath,
4345                                svn_boolean_t mark_tc_resolved,
4346                                svn_wc_notify_func2_t notify_func,
4347                                void *notify_baton,
4348                                apr_pool_t *scratch_pool)
4349 {
4350   svn_wc__db_wcroot_t *wcroot;
4351   const char *local_relpath;
4352   const char *del_relpath;
4353   int src_op_depth;
4354 
4355   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4356                                                 db, local_abspath,
4357                                                 scratch_pool, scratch_pool));
4358   VERIFY_USABLE_WCROOT(wcroot);
4359 
4360   if (del_op_root_abspath)
4361     del_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4362                                            del_op_root_abspath);
4363   else
4364     del_relpath = NULL;
4365 
4366 
4367   SVN_WC__DB_WITH_TXN4(
4368     find_src_op_depth(&src_op_depth, wcroot, local_relpath,
4369                       del_relpath ? relpath_depth(del_relpath)
4370                                  : relpath_depth(local_relpath),
4371                       scratch_pool),
4372     break_moved_away(wcroot, db, local_relpath, src_op_depth,
4373                      scratch_pool),
4374     mark_tc_resolved
4375         ? svn_wc__db_op_mark_resolved_internal(wcroot, local_relpath, db,
4376                                                FALSE, FALSE, TRUE,
4377                                                NULL, scratch_pool)
4378         : SVN_NO_ERROR,
4379     SVN_NO_ERROR,
4380     wcroot);
4381 
4382   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot,
4383                                              SVN_INVALID_REVNUM,
4384                                              SVN_INVALID_REVNUM,
4385                                              notify_func, notify_baton,
4386                                              scratch_pool));
4387   return SVN_NO_ERROR;
4388 }
4389 
4390 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)4391 required_lock_for_resolve(const char **required_relpath,
4392                           svn_wc__db_wcroot_t *wcroot,
4393                           const char *local_relpath,
4394                           apr_pool_t *result_pool,
4395                           apr_pool_t *scratch_pool)
4396 {
4397   svn_sqlite__stmt_t *stmt;
4398   svn_boolean_t have_row;
4399 
4400   *required_relpath = local_relpath;
4401 
4402   /* This simply looks for all moves out of the LOCAL_RELPATH tree. We
4403      could attempt to limit it to only those moves that are going to
4404      be resolved but that would require second guessing the resolver.
4405      This simple algorithm is sufficient although it may give a
4406      strictly larger/deeper lock than necessary. */
4407   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4408                                     STMT_SELECT_MOVED_OUTSIDE));
4409   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
4410   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4411 
4412   while (have_row)
4413     {
4414       const char *move_dst_relpath = svn_sqlite__column_text(stmt, 1,
4415                                                              NULL);
4416 
4417       *required_relpath
4418         = svn_relpath_get_longest_ancestor(*required_relpath,
4419                                            move_dst_relpath,
4420                                            scratch_pool);
4421 
4422       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4423     }
4424   SVN_ERR(svn_sqlite__reset(stmt));
4425 
4426   *required_relpath = apr_pstrdup(result_pool, *required_relpath);
4427 
4428   return SVN_NO_ERROR;
4429 }
4430 
4431 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)4432 svn_wc__required_lock_for_resolve(const char **required_abspath,
4433                                   svn_wc__db_t *db,
4434                                   const char *local_abspath,
4435                                   apr_pool_t *result_pool,
4436                                   apr_pool_t *scratch_pool)
4437 {
4438   svn_wc__db_wcroot_t *wcroot;
4439   const char *local_relpath;
4440   const char *required_relpath;
4441 
4442   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
4443                                                 db, local_abspath,
4444                                                 scratch_pool, scratch_pool));
4445   VERIFY_USABLE_WCROOT(wcroot);
4446 
4447   SVN_WC__DB_WITH_TXN(
4448     required_lock_for_resolve(&required_relpath, wcroot, local_relpath,
4449                               scratch_pool, scratch_pool),
4450     wcroot);
4451 
4452   *required_abspath = svn_dirent_join(wcroot->abspath, required_relpath,
4453                                       result_pool);
4454 
4455   return SVN_NO_ERROR;
4456 }
4457