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