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