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