2 * conflicts.c: routines for managing conflict data.
3 * NOTE: this code doesn't know where the conflict is
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements. See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership. The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License. You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied. See the License for the
21 * specific language governing permissions and limitations
23 * ====================================================================
30 #include <apr_pools.h>
31 #include <apr_tables.h>
33 #include <apr_errno.h>
36 #include "svn_types.h"
37 #include "svn_pools.h"
38 #include "svn_string.h"
39 #include "svn_error.h"
40 #include "svn_dirent_uri.h"
47 #include "conflicts.h"
48 #include "workqueue.h"
51 #include "private/svn_wc_private.h"
52 #include "private/svn_skel.h"
53 #include "private/svn_sorts_private.h"
54 #include "private/svn_string_private.h"
56 #include "svn_private_config.h"
58 /* --------------------------------------------------------------------
59 * Conflict skel management
63 svn_wc__conflict_skel_create(apr_pool_t *result_pool)
65 svn_skel_t *conflict_skel = svn_skel__make_empty_list(result_pool);
67 /* Add empty CONFLICTS list */
68 svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel);
70 /* Add empty WHY list */
71 svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel);
77 svn_wc__conflict_skel_is_complete(svn_boolean_t *complete,
78 const svn_skel_t *conflict_skel)
82 if (svn_skel__list_length(conflict_skel) < 2)
83 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
84 _("Not a conflict skel"));
86 if (svn_skel__list_length(conflict_skel->children) < 2)
87 return SVN_NO_ERROR; /* WHY is not set */
89 if (svn_skel__list_length(conflict_skel->children->next) == 0)
90 return SVN_NO_ERROR; /* No conflict set */
96 /* Serialize a svn_wc_conflict_version_t before the existing data in skel */
98 conflict__prepend_location(svn_skel_t *skel,
99 const svn_wc_conflict_version_t *location,
100 svn_boolean_t allow_NULL,
101 apr_pool_t *result_pool,
102 apr_pool_t *scratch_pool)
105 SVN_ERR_ASSERT(location || allow_NULL);
109 svn_skel__prepend(svn_skel__make_empty_list(result_pool), skel);
113 /* ("subversion" repos_root_url repos_uuid repos_relpath rev kind) */
114 loc = svn_skel__make_empty_list(result_pool);
116 svn_skel__prepend_str(svn_node_kind_to_word(location->node_kind),
119 svn_skel__prepend_int(location->peg_rev, loc, result_pool);
121 svn_skel__prepend_str(apr_pstrdup(result_pool, location->path_in_repos), loc,
124 if (!location->repos_uuid) /* Can theoretically be NULL */
125 svn_skel__prepend(svn_skel__make_empty_list(result_pool), loc);
127 svn_skel__prepend_str(location->repos_uuid, loc, result_pool);
129 svn_skel__prepend_str(apr_pstrdup(result_pool, location->repos_url), loc,
132 svn_skel__prepend_str(SVN_WC__CONFLICT_SRC_SUBVERSION, loc, result_pool);
134 svn_skel__prepend(loc, skel);
138 /* Deserialize a svn_wc_conflict_version_t from the skel.
139 Set *LOCATION to NULL when the data is not a svn_wc_conflict_version_t. */
141 conflict__read_location(svn_wc_conflict_version_t **location,
142 const svn_skel_t *skel,
143 apr_pool_t *result_pool,
144 apr_pool_t *scratch_pool)
146 const char *repos_root_url;
147 const char *repos_uuid;
148 const char *repos_relpath;
149 svn_revnum_t revision;
151 svn_node_kind_t node_kind; /* note that 'none' is a legitimate value */
152 const char *kind_str;
154 const svn_skel_t *c = skel->children;
156 if (!svn_skel__matches_atom(c, SVN_WC__CONFLICT_SRC_SUBVERSION))
163 repos_root_url = apr_pstrmemdup(result_pool, c->data, c->len);
167 repos_uuid = apr_pstrmemdup(result_pool, c->data, c->len);
172 repos_relpath = apr_pstrmemdup(result_pool, c->data, c->len);
175 SVN_ERR(svn_skel__parse_int(&v, c, scratch_pool));
176 revision = (svn_revnum_t)v;
179 kind_str = apr_pstrmemdup(scratch_pool, c->data, c->len);
180 node_kind = svn_node_kind_from_word(kind_str);
182 *location = svn_wc_conflict_version_create2(repos_root_url,
191 /* Get the operation part of CONFLICT_SKELL or NULL if no operation is set
194 conflict__get_operation(svn_skel_t **why,
195 const svn_skel_t *conflict_skel)
197 SVN_ERR_ASSERT(conflict_skel
198 && conflict_skel->children
199 && conflict_skel->children->next
200 && !conflict_skel->children->next->is_atom);
202 *why = conflict_skel->children;
204 if (!(*why)->children)
205 *why = NULL; /* Operation is not set yet */
212 svn_wc__conflict_skel_set_op_update(svn_skel_t *conflict_skel,
213 const svn_wc_conflict_version_t *original,
214 const svn_wc_conflict_version_t *target,
215 apr_pool_t *result_pool,
216 apr_pool_t *scratch_pool)
221 SVN_ERR_ASSERT(conflict_skel
222 && conflict_skel->children
223 && conflict_skel->children->next
224 && !conflict_skel->children->next->is_atom);
226 SVN_ERR(conflict__get_operation(&why, conflict_skel));
228 SVN_ERR_ASSERT(why == NULL); /* No operation set */
230 why = conflict_skel->children;
232 origins = svn_skel__make_empty_list(result_pool);
234 SVN_ERR(conflict__prepend_location(origins, target, TRUE,
235 result_pool, scratch_pool));
236 SVN_ERR(conflict__prepend_location(origins, original, TRUE,
237 result_pool, scratch_pool));
239 svn_skel__prepend(origins, why);
240 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_UPDATE, why, result_pool);
246 svn_wc__conflict_skel_set_op_switch(svn_skel_t *conflict_skel,
247 const svn_wc_conflict_version_t *original,
248 const svn_wc_conflict_version_t *target,
249 apr_pool_t *result_pool,
250 apr_pool_t *scratch_pool)
255 SVN_ERR_ASSERT(conflict_skel
256 && conflict_skel->children
257 && conflict_skel->children->next
258 && !conflict_skel->children->next->is_atom);
260 SVN_ERR(conflict__get_operation(&why, conflict_skel));
262 SVN_ERR_ASSERT(why == NULL); /* No operation set */
264 why = conflict_skel->children;
266 origins = svn_skel__make_empty_list(result_pool);
268 SVN_ERR(conflict__prepend_location(origins, target, TRUE,
269 result_pool, scratch_pool));
270 SVN_ERR(conflict__prepend_location(origins, original, TRUE,
271 result_pool, scratch_pool));
273 svn_skel__prepend(origins, why);
274 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_SWITCH, why, result_pool);
280 svn_wc__conflict_skel_set_op_merge(svn_skel_t *conflict_skel,
281 const svn_wc_conflict_version_t *left,
282 const svn_wc_conflict_version_t *right,
283 apr_pool_t *result_pool,
284 apr_pool_t *scratch_pool)
289 SVN_ERR_ASSERT(conflict_skel
290 && conflict_skel->children
291 && conflict_skel->children->next
292 && !conflict_skel->children->next->is_atom);
294 SVN_ERR(conflict__get_operation(&why, conflict_skel));
296 SVN_ERR_ASSERT(why == NULL); /* No operation set */
298 why = conflict_skel->children;
300 origins = svn_skel__make_empty_list(result_pool);
302 SVN_ERR(conflict__prepend_location(origins, right, TRUE,
303 result_pool, scratch_pool));
305 SVN_ERR(conflict__prepend_location(origins, left, TRUE,
306 result_pool, scratch_pool));
308 svn_skel__prepend(origins, why);
309 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_MERGE, why, result_pool);
314 /* Gets the conflict data of the specified type CONFLICT_TYPE from
315 CONFLICT_SKEL, or NULL if no such conflict is recorded */
317 conflict__get_conflict(svn_skel_t **conflict,
318 const svn_skel_t *conflict_skel,
319 const char *conflict_type)
323 SVN_ERR_ASSERT(conflict_skel
324 && conflict_skel->children
325 && conflict_skel->children->next
326 && !conflict_skel->children->next->is_atom);
328 for(c = conflict_skel->children->next->children;
332 if (svn_skel__matches_atom(c->children, conflict_type))
345 svn_wc__conflict_skel_add_text_conflict(svn_skel_t *conflict_skel,
347 const char *wri_abspath,
348 const char *mine_abspath,
349 const char *their_old_abspath,
350 const char *their_abspath,
351 apr_pool_t *result_pool,
352 apr_pool_t *scratch_pool)
354 svn_skel_t *text_conflict;
357 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
358 SVN_WC__CONFLICT_KIND_TEXT));
360 SVN_ERR_ASSERT(!text_conflict); /* ### Use proper error? */
362 /* Current skel format
364 (OLD MINE OLD-THEIRS THEIRS)) */
366 text_conflict = svn_skel__make_empty_list(result_pool);
367 markers = svn_skel__make_empty_list(result_pool);
371 const char *their_relpath;
373 SVN_ERR(svn_wc__db_to_relpath(&their_relpath,
374 db, wri_abspath, their_abspath,
375 result_pool, scratch_pool));
376 svn_skel__prepend_str(their_relpath, markers, result_pool);
379 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
383 const char *mine_relpath;
385 SVN_ERR(svn_wc__db_to_relpath(&mine_relpath,
386 db, wri_abspath, mine_abspath,
387 result_pool, scratch_pool));
388 svn_skel__prepend_str(mine_relpath, markers, result_pool);
391 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
393 if (their_old_abspath)
395 const char *original_relpath;
397 SVN_ERR(svn_wc__db_to_relpath(&original_relpath,
398 db, wri_abspath, their_old_abspath,
399 result_pool, scratch_pool));
400 svn_skel__prepend_str(original_relpath, markers, result_pool);
403 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
405 svn_skel__prepend(markers, text_conflict);
406 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TEXT, text_conflict,
409 /* And add it to the conflict skel */
410 svn_skel__prepend(text_conflict, conflict_skel->children->next);
416 svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel,
418 const char *wri_abspath,
419 const char *marker_abspath,
420 const apr_hash_t *mine_props,
421 const apr_hash_t *their_old_props,
422 const apr_hash_t *their_props,
423 const apr_hash_t *conflicted_prop_names,
424 apr_pool_t *result_pool,
425 apr_pool_t *scratch_pool)
427 svn_skel_t *prop_conflict;
429 svn_skel_t *conflict_names;
431 apr_hash_index_t *hi;
433 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
434 SVN_WC__CONFLICT_KIND_PROP));
436 SVN_ERR_ASSERT(!prop_conflict); /* ### Use proper error? */
438 /* This function currently implements:
441 prop-conflicted_prop_names
445 NULL lists are recorded as "" */
446 /* ### Seems that this may not match what we read out. Read-out of
447 * 'theirs-old' comes as NULL. */
449 prop_conflict = svn_skel__make_empty_list(result_pool);
453 SVN_ERR(svn_skel__unparse_proplist(&props, their_props, result_pool));
454 svn_skel__prepend(props, prop_conflict);
457 svn_skel__prepend_str("", prop_conflict, result_pool); /* No their_props */
461 SVN_ERR(svn_skel__unparse_proplist(&props, mine_props, result_pool));
462 svn_skel__prepend(props, prop_conflict);
465 svn_skel__prepend_str("", prop_conflict, result_pool); /* No mine_props */
469 SVN_ERR(svn_skel__unparse_proplist(&props, their_old_props,
471 svn_skel__prepend(props, prop_conflict);
474 svn_skel__prepend_str("", prop_conflict, result_pool); /* No old_props */
476 conflict_names = svn_skel__make_empty_list(result_pool);
477 for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)conflicted_prop_names);
479 hi = apr_hash_next(hi))
481 svn_skel__prepend_str(apr_pstrdup(result_pool, apr_hash_this_key(hi)),
485 svn_skel__prepend(conflict_names, prop_conflict);
487 markers = svn_skel__make_empty_list(result_pool);
491 const char *marker_relpath;
492 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, wri_abspath,
494 result_pool, scratch_pool));
496 svn_skel__prepend_str(marker_relpath, markers, result_pool);
498 /*else // ### set via svn_wc__conflict_create_markers
499 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);*/
501 svn_skel__prepend(markers, prop_conflict);
503 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_conflict, result_pool);
505 /* And add it to the conflict skel */
506 svn_skel__prepend(prop_conflict, conflict_skel->children->next);
511 /* A map for svn_wc_conflict_reason_t values. */
512 static const svn_token_map_t reason_map[] =
514 { "edited", svn_wc_conflict_reason_edited },
515 { "obstructed", svn_wc_conflict_reason_obstructed },
516 { "deleted", svn_wc_conflict_reason_deleted },
517 { "missing", svn_wc_conflict_reason_missing },
518 { "unversioned", svn_wc_conflict_reason_unversioned },
519 { "added", svn_wc_conflict_reason_added },
520 { "replaced", svn_wc_conflict_reason_replaced },
521 { "moved-away", svn_wc_conflict_reason_moved_away },
522 { "moved-here", svn_wc_conflict_reason_moved_here },
526 static const svn_token_map_t action_map[] =
528 { "edited", svn_wc_conflict_action_edit },
529 { "added", svn_wc_conflict_action_add },
530 { "deleted", svn_wc_conflict_action_delete },
531 { "replaced", svn_wc_conflict_action_replace },
536 svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel,
538 const char *wri_abspath,
539 svn_wc_conflict_reason_t reason,
540 svn_wc_conflict_action_t action,
541 const char *move_src_op_root_abspath,
542 apr_pool_t *result_pool,
543 apr_pool_t *scratch_pool)
545 svn_skel_t *tree_conflict;
548 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
549 SVN_WC__CONFLICT_KIND_TREE));
551 SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */
553 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_moved_away
554 || !move_src_op_root_abspath); /* ### Use proper error? */
556 tree_conflict = svn_skel__make_empty_list(result_pool);
558 if (reason == svn_wc_conflict_reason_moved_away
559 && move_src_op_root_abspath)
561 const char *move_src_op_root_relpath;
563 SVN_ERR(svn_wc__db_to_relpath(&move_src_op_root_relpath,
565 move_src_op_root_abspath,
566 result_pool, scratch_pool));
568 svn_skel__prepend_str(move_src_op_root_relpath, tree_conflict,
572 svn_skel__prepend_str(svn_token__to_word(action_map, action),
573 tree_conflict, result_pool);
575 svn_skel__prepend_str(svn_token__to_word(reason_map, reason),
576 tree_conflict, result_pool);
578 /* Tree conflicts have no marker files */
579 markers = svn_skel__make_empty_list(result_pool);
580 svn_skel__prepend(markers, tree_conflict);
582 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TREE, tree_conflict,
585 /* And add it to the conflict skel */
586 svn_skel__prepend(tree_conflict, conflict_skel->children->next);
592 svn_wc__conflict_skel_resolve(svn_boolean_t *completely_resolved,
593 svn_skel_t *conflict_skel,
595 const char *wri_abspath,
596 svn_boolean_t resolve_text,
597 const char *resolve_prop,
598 svn_boolean_t resolve_tree,
599 apr_pool_t *result_pool,
600 apr_pool_t *scratch_pool)
603 svn_skel_t **pconflict;
604 SVN_ERR(conflict__get_operation(&op, conflict_skel));
607 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
608 _("Not a completed conflict skel"));
610 /* We are going to drop items from a linked list. Instead of keeping
611 a pointer to the item we want to drop we store a pointer to the
612 pointer of what we may drop, to allow setting it to the next item. */
614 pconflict = &(conflict_skel->children->next->children);
617 svn_skel_t *c = (*pconflict)->children;
620 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TEXT))
622 /* Remove the text conflict from the linked list */
623 *pconflict = (*pconflict)->next;
626 else if (resolve_prop
627 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_PROP))
629 svn_skel_t **ppropnames = &(c->next->next->children);
631 if (resolve_prop[0] == '\0')
632 *ppropnames = NULL; /* remove all conflicted property names */
636 if (svn_skel__matches_atom(*ppropnames, resolve_prop))
638 *ppropnames = (*ppropnames)->next;
641 ppropnames = &((*ppropnames)->next);
644 /* If no conflicted property names left */
645 if (!c->next->next->children)
647 /* Remove the propery conflict skel from the linked list */
648 *pconflict = (*pconflict)->next;
652 else if (resolve_tree
653 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TREE))
655 /* Remove the tree conflict from the linked list */
656 *pconflict = (*pconflict)->next;
660 pconflict = &((*pconflict)->next);
663 if (completely_resolved)
665 /* Nice, we can just call the complete function */
666 svn_boolean_t complete_conflict;
667 SVN_ERR(svn_wc__conflict_skel_is_complete(&complete_conflict,
670 *completely_resolved = !complete_conflict;
676 /* A map for svn_wc_operation_t values. */
677 static const svn_token_map_t operation_map[] =
679 { "", svn_wc_operation_none },
680 { SVN_WC__CONFLICT_OP_UPDATE, svn_wc_operation_update },
681 { SVN_WC__CONFLICT_OP_SWITCH, svn_wc_operation_switch },
682 { SVN_WC__CONFLICT_OP_MERGE, svn_wc_operation_merge },
687 svn_wc__conflict_read_info(svn_wc_operation_t *operation,
688 const apr_array_header_t **locations,
689 svn_boolean_t *text_conflicted,
690 svn_boolean_t *prop_conflicted,
691 svn_boolean_t *tree_conflicted,
693 const char *wri_abspath,
694 const svn_skel_t *conflict_skel,
695 apr_pool_t *result_pool,
696 apr_pool_t *scratch_pool)
701 SVN_ERR(conflict__get_operation(&op, conflict_skel));
704 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
705 _("Not a completed conflict skel"));
710 int value = svn_token__from_mem(operation_map, c->data, c->len);
712 if (value != SVN_TOKEN_UNKNOWN)
715 *operation = svn_wc_operation_none;
719 if (locations && c->children)
721 const svn_skel_t *loc_skel;
722 svn_wc_conflict_version_t *loc;
723 apr_array_header_t *locs = apr_array_make(result_pool, 2, sizeof(loc));
725 for (loc_skel = c->children; loc_skel; loc_skel = loc_skel->next)
727 SVN_ERR(conflict__read_location(&loc, loc_skel, result_pool,
730 APR_ARRAY_PUSH(locs, svn_wc_conflict_version_t *) = loc;
741 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
742 SVN_WC__CONFLICT_KIND_TEXT));
744 *text_conflicted = (c_skel != NULL);
750 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
751 SVN_WC__CONFLICT_KIND_PROP));
753 *prop_conflicted = (c_skel != NULL);
759 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
760 SVN_WC__CONFLICT_KIND_TREE));
762 *tree_conflicted = (c_skel != NULL);
770 svn_wc__conflict_read_text_conflict(const char **mine_abspath,
771 const char **their_old_abspath,
772 const char **their_abspath,
774 const char *wri_abspath,
775 const svn_skel_t *conflict_skel,
776 apr_pool_t *result_pool,
777 apr_pool_t *scratch_pool)
779 svn_skel_t *text_conflict;
782 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
783 SVN_WC__CONFLICT_KIND_TEXT));
786 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
788 m = text_conflict->children->next->children;
790 if (their_old_abspath)
794 const char *original_relpath;
796 original_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
797 SVN_ERR(svn_wc__db_from_relpath(their_old_abspath,
798 db, wri_abspath, original_relpath,
799 result_pool, scratch_pool));
802 *their_old_abspath = NULL;
810 const char *mine_relpath;
812 mine_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
813 SVN_ERR(svn_wc__db_from_relpath(mine_abspath,
814 db, wri_abspath, mine_relpath,
815 result_pool, scratch_pool));
818 *mine_abspath = NULL;
826 const char *their_relpath;
828 their_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
829 SVN_ERR(svn_wc__db_from_relpath(their_abspath,
830 db, wri_abspath, their_relpath,
831 result_pool, scratch_pool));
834 *their_abspath = NULL;
841 svn_wc__conflict_read_prop_conflict(const char **marker_abspath,
842 apr_hash_t **mine_props,
843 apr_hash_t **their_old_props,
844 apr_hash_t **their_props,
845 apr_hash_t **conflicted_prop_names,
847 const char *wri_abspath,
848 const svn_skel_t *conflict_skel,
849 apr_pool_t *result_pool,
850 apr_pool_t *scratch_pool)
852 svn_skel_t *prop_conflict;
855 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
856 SVN_WC__CONFLICT_KIND_PROP));
859 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
861 c = prop_conflict->children;
863 c = c->next; /* Skip "prop" */
865 /* Get marker file */
868 const char *marker_relpath;
870 if (c->children && c->children->is_atom)
872 marker_relpath = apr_pstrmemdup(result_pool, c->children->data,
875 SVN_ERR(svn_wc__db_from_relpath(marker_abspath, db, wri_abspath,
877 result_pool, scratch_pool));
880 *marker_abspath = NULL;
884 /* Get conflicted properties */
885 if (conflicted_prop_names)
887 const svn_skel_t *name;
888 *conflicted_prop_names = apr_hash_make(result_pool);
890 for (name = c->children; name; name = name->next)
892 svn_hash_sets(*conflicted_prop_names,
893 apr_pstrmemdup(result_pool, name->data, name->len),
899 /* Get original properties */
903 *their_old_props = apr_hash_make(result_pool);
905 SVN_ERR(svn_skel__parse_proplist(their_old_props, c, result_pool));
909 /* Get mine properties */
913 *mine_props = apr_hash_make(result_pool);
915 SVN_ERR(svn_skel__parse_proplist(mine_props, c, result_pool));
919 /* Get their properties */
923 *their_props = apr_hash_make(result_pool);
925 SVN_ERR(svn_skel__parse_proplist(their_props, c, result_pool));
932 svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *reason,
933 svn_wc_conflict_action_t *action,
934 const char **move_src_op_root_abspath,
936 const char *wri_abspath,
937 const svn_skel_t *conflict_skel,
938 apr_pool_t *result_pool,
939 apr_pool_t *scratch_pool)
941 svn_skel_t *tree_conflict;
943 svn_boolean_t is_moved_away = FALSE;
945 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
946 SVN_WC__CONFLICT_KIND_TREE));
949 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
951 c = tree_conflict->children;
953 c = c->next; /* Skip "tree" */
955 c = c->next; /* Skip markers */
958 int value = svn_token__from_mem(reason_map, c->data, c->len);
962 if (value != SVN_TOKEN_UNKNOWN)
965 *reason = svn_wc_conflict_reason_edited;
968 is_moved_away = (value == svn_wc_conflict_reason_moved_away);
974 int value = svn_token__from_mem(action_map, c->data, c->len);
976 if (value != SVN_TOKEN_UNKNOWN)
979 *action = svn_wc_conflict_action_edit;
984 if (move_src_op_root_abspath)
986 /* Only set for update and switch tree conflicts */
987 if (c && is_moved_away)
989 const char *move_src_op_root_relpath
990 = apr_pstrmemdup(scratch_pool, c->data, c->len);
992 SVN_ERR(svn_wc__db_from_relpath(move_src_op_root_abspath,
994 move_src_op_root_relpath,
995 result_pool, scratch_pool));
998 *move_src_op_root_abspath = NULL;
1001 return SVN_NO_ERROR;
1005 svn_wc__conflict_read_markers(const apr_array_header_t **markers,
1007 const char *wri_abspath,
1008 const svn_skel_t *conflict_skel,
1009 apr_pool_t *result_pool,
1010 apr_pool_t *scratch_pool)
1012 const svn_skel_t *conflict;
1013 apr_array_header_t *list = NULL;
1015 SVN_ERR_ASSERT(conflict_skel != NULL);
1017 /* Walk the conflicts */
1018 for (conflict = conflict_skel->children->next->children;
1020 conflict = conflict->next)
1022 const svn_skel_t *marker;
1024 /* Get the list of markers stored per conflict */
1025 for (marker = conflict->children->next->children;
1027 marker = marker->next)
1029 /* Skip placeholders */
1030 if (! marker->is_atom)
1034 list = apr_array_make(result_pool, 4, sizeof(const char *));
1036 SVN_ERR(svn_wc__db_from_relpath(
1037 &APR_ARRAY_PUSH(list, const char*),
1039 apr_pstrmemdup(scratch_pool, marker->data,
1041 result_pool, scratch_pool));
1046 return SVN_NO_ERROR;
1049 /* --------------------------------------------------------------------
1054 svn_wc__conflict_create_markers(svn_skel_t **work_items,
1056 const char *local_abspath,
1057 svn_skel_t *conflict_skel,
1058 apr_pool_t *result_pool,
1059 apr_pool_t *scratch_pool)
1061 svn_boolean_t prop_conflicted;
1062 svn_wc_operation_t operation;
1065 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL,
1066 NULL, &prop_conflicted, NULL,
1069 scratch_pool, scratch_pool));
1071 if (prop_conflicted)
1073 const char *marker_abspath = NULL;
1074 svn_node_kind_t kind;
1075 const char *marker_dir;
1076 const char *marker_name;
1077 const char *marker_relpath;
1079 /* Ok, currently we have to do a few things for property conflicts:
1080 - Create a marker file
1081 - Store the name in the conflict_skel
1082 - Create a WQ item that fills the marker with the expected data */
1084 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
1086 if (kind == svn_node_dir)
1088 marker_dir = local_abspath;
1089 marker_name = SVN_WC__THIS_DIR_PREJ;
1092 svn_dirent_split(&marker_dir, &marker_name, local_abspath,
1095 SVN_ERR(svn_io_open_uniquely_named(NULL, &marker_abspath,
1098 SVN_WC__PROP_REJ_EXT,
1099 svn_io_file_del_none,
1100 scratch_pool, scratch_pool));
1102 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, local_abspath,
1103 marker_abspath, result_pool, result_pool));
1105 /* And store the marker in the skel */
1107 svn_skel_t *prop_conflict;
1108 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
1109 SVN_WC__CONFLICT_KIND_PROP));
1111 svn_skel__prepend_str(marker_relpath, prop_conflict->children->next,
1114 SVN_ERR(svn_wc__wq_build_prej_install(work_items,
1116 scratch_pool, scratch_pool));
1119 return SVN_NO_ERROR;
1122 /* Helper function for the three apply_* functions below, used when
1123 * merging properties together.
1125 * Given property PROPNAME on LOCAL_ABSPATH, and four possible property
1126 * values, generate four tmpfiles and pass them to CONFLICT_FUNC callback.
1127 * This gives the client an opportunity to interactively resolve the
1128 * property conflict.
1130 * BASE_VAL/WORKING_VAL represent the current state of the working
1131 * copy, and INCOMING_OLD_VAL/INCOMING_NEW_VAL represents the incoming
1132 * propchange. Any of these values might be NULL, indicating either
1133 * non-existence or intent-to-delete.
1135 * If the callback isn't available, or if it responds with
1136 * 'choose_postpone', then set *CONFLICT_REMAINS to TRUE and return.
1138 * If the callback responds with a choice of 'base', 'theirs', 'mine',
1139 * or 'merged', then install the proper value into ACTUAL_PROPS and
1140 * set *CONFLICT_REMAINS to FALSE.
1142 static svn_error_t *
1143 generate_propconflict(svn_boolean_t *conflict_remains,
1145 const char *local_abspath,
1146 svn_node_kind_t kind,
1147 svn_wc_operation_t operation,
1148 const svn_wc_conflict_version_t *left_version,
1149 const svn_wc_conflict_version_t *right_version,
1150 const char *propname,
1151 const svn_string_t *base_val,
1152 const svn_string_t *working_val,
1153 const svn_string_t *incoming_old_val,
1154 const svn_string_t *incoming_new_val,
1155 svn_wc_conflict_resolver_func2_t conflict_func,
1156 void *conflict_baton,
1157 svn_cancel_func_t cancel_func,
1159 apr_pool_t *scratch_pool)
1161 svn_wc_conflict_result_t *result = NULL;
1162 svn_wc_conflict_description2_t *cdesc;
1163 const char *dirpath = svn_dirent_dirname(local_abspath, scratch_pool);
1164 const svn_string_t *new_value = NULL;
1166 cdesc = svn_wc_conflict_description_create_prop2(
1169 propname, scratch_pool);
1171 cdesc->operation = operation;
1172 cdesc->src_left_version = left_version;
1173 cdesc->src_right_version = right_version;
1175 /* Create a tmpfile for each of the string_t's we've got. */
1178 const char *file_name;
1180 SVN_ERR(svn_io_write_unique(&file_name, dirpath, working_val->data,
1182 svn_io_file_del_on_pool_cleanup,
1184 cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1185 cdesc->prop_value_working = working_val;
1188 if (incoming_new_val)
1190 const char *file_name;
1192 SVN_ERR(svn_io_write_unique(&file_name, dirpath, incoming_new_val->data,
1193 incoming_new_val->len,
1194 svn_io_file_del_on_pool_cleanup,
1197 /* ### For property conflicts, cd2 stores prop_reject_abspath in
1198 * ### their_abspath, and stores theirs_abspath in merged_file. */
1199 cdesc->merged_file = svn_dirent_join(dirpath, file_name, scratch_pool);
1200 cdesc->prop_value_incoming_new = incoming_new_val;
1203 if (!base_val && !incoming_old_val)
1205 /* If base and old are both NULL, then that's fine, we just let
1206 base_file stay NULL as-is. Both agents are attempting to add a
1209 else if ((base_val && !incoming_old_val)
1210 || (!base_val && incoming_old_val))
1212 /* If only one of base and old are defined, then we've got a
1213 situation where one agent is attempting to add the property
1214 for the first time, and the other agent is changing a
1215 property it thinks already exists. In this case, we return
1216 whichever older-value happens to be defined, so that the
1217 conflict-callback can still attempt a 3-way merge. */
1219 const svn_string_t *conflict_base_val = base_val ? base_val
1221 const char *file_name;
1223 SVN_ERR(svn_io_write_unique(&file_name, dirpath,
1224 conflict_base_val->data,
1225 conflict_base_val->len,
1226 svn_io_file_del_on_pool_cleanup,
1228 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1230 else /* base and old are both non-NULL */
1232 const svn_string_t *conflict_base_val;
1233 const char *file_name;
1235 if (! svn_string_compare(base_val, incoming_old_val))
1237 /* What happens if 'base' and 'old' don't match up? In an
1238 ideal situation, they would. But if they don't, this is
1239 a classic example of a patch 'hunk' failing to apply due
1240 to a lack of context. For example: imagine that the user
1241 is busy changing the property from a value of "cat" to
1242 "dog", but the incoming propchange wants to change the
1243 same property value from "red" to "green". Total context
1246 HOWEVER: we can still pass one of the two base values as
1247 'base_file' to the callback anyway. It's still useful to
1248 present the working and new values to the user to
1251 if (working_val && svn_string_compare(base_val, working_val))
1252 conflict_base_val = incoming_old_val;
1254 conflict_base_val = base_val;
1258 conflict_base_val = base_val;
1261 SVN_ERR(svn_io_write_unique(&file_name, dirpath, conflict_base_val->data,
1262 conflict_base_val->len,
1263 svn_io_file_del_on_pool_cleanup, scratch_pool));
1264 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1266 cdesc->prop_value_base = base_val;
1267 cdesc->prop_value_incoming_old = incoming_old_val;
1269 if (working_val && incoming_new_val)
1271 svn_stream_t *mergestream;
1273 svn_diff_file_options_t *options =
1274 svn_diff_file_options_create(scratch_pool);
1276 SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->prop_reject_abspath,
1277 NULL, svn_io_file_del_on_pool_cleanup,
1278 scratch_pool, scratch_pool));
1279 SVN_ERR(svn_diff_mem_string_diff3(&diff, conflict_base_val,
1281 incoming_new_val, options, scratch_pool));
1282 SVN_ERR(svn_diff_mem_string_output_merge3(mergestream, diff,
1283 conflict_base_val, working_val,
1284 incoming_new_val, NULL, NULL, NULL, NULL,
1285 svn_diff_conflict_display_modified_latest,
1286 cancel_func, cancel_baton, scratch_pool));
1287 SVN_ERR(svn_stream_close(mergestream));
1289 /* ### For property conflicts, cd2 stores prop_reject_abspath in
1290 * ### their_abspath, and stores theirs_abspath in merged_file. */
1291 cdesc->their_abspath = cdesc->prop_reject_abspath;
1295 if (!incoming_old_val && incoming_new_val)
1296 cdesc->action = svn_wc_conflict_action_add;
1297 else if (incoming_old_val && !incoming_new_val)
1298 cdesc->action = svn_wc_conflict_action_delete;
1300 cdesc->action = svn_wc_conflict_action_edit;
1302 if (base_val && !working_val)
1303 cdesc->reason = svn_wc_conflict_reason_deleted;
1304 else if (!base_val && working_val)
1305 cdesc->reason = svn_wc_conflict_reason_obstructed;
1307 cdesc->reason = svn_wc_conflict_reason_edited;
1309 /* Invoke the interactive conflict callback. */
1310 SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
1314 *conflict_remains = TRUE;
1315 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
1316 NULL, _("Conflict callback violated API:"
1317 " returned no results"));
1321 switch (result->choice)
1324 case svn_wc_conflict_choose_postpone:
1326 *conflict_remains = TRUE;
1329 case svn_wc_conflict_choose_mine_full:
1331 /* No need to change actual_props; it already contains working_val */
1332 *conflict_remains = FALSE;
1333 new_value = working_val;
1336 /* I think _mine_full and _theirs_full are appropriate for prop
1337 behavior as well as the text behavior. There should even be
1338 analogous behaviors for _mine and _theirs when those are
1339 ready, namely: fold in all non-conflicting prop changes, and
1340 then choose _mine side or _theirs side for conflicting ones. */
1341 case svn_wc_conflict_choose_theirs_full:
1343 *conflict_remains = FALSE;
1344 new_value = incoming_new_val;
1347 case svn_wc_conflict_choose_base:
1349 *conflict_remains = FALSE;
1350 new_value = base_val;
1353 case svn_wc_conflict_choose_merged:
1355 if (!cdesc->merged_file
1356 && (!result->merged_file && !result->merged_value))
1357 return svn_error_create
1358 (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
1359 NULL, _("Conflict callback violated API:"
1360 " returned no merged file"));
1362 if (result->merged_value)
1363 new_value = result->merged_value;
1366 svn_stringbuf_t *merged_stringbuf;
1368 SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf,
1369 result->merged_file ?
1370 result->merged_file :
1373 new_value = svn_stringbuf__morph_into_string(merged_stringbuf);
1375 *conflict_remains = FALSE;
1380 if (!*conflict_remains)
1384 /* For now, just set the property values. This should really do some of the
1385 more advanced things from svn_wc_prop_set() */
1387 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool,
1390 svn_hash_sets(props, propname, new_value);
1392 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, props,
1397 return SVN_NO_ERROR;
1400 /* Perform a 3-way merge in which conflicts are expected, showing the
1401 * conflicts in the way specified by STYLE, and using MERGE_OPTIONS.
1403 * The three input files are LEFT_ABSPATH (the base), DETRANSLATED_TARGET
1404 * and RIGHT_ABSPATH. The output is stored in a new temporary file,
1405 * whose name is put into *CHOSEN_ABSPATH.
1407 * The output file will be deleted according to DELETE_WHEN. If
1408 * DELETE_WHEN is 'on pool cleanup', it refers to RESULT_POOL.
1410 * DB and WRI_ABSPATH are used to choose a directory for the output file.
1412 * Allocate *CHOSEN_ABSPATH in RESULT_POOL. Use SCRATCH_POOL for temporary
1415 static svn_error_t *
1416 merge_showing_conflicts(const char **chosen_abspath,
1418 const char *wri_abspath,
1419 svn_diff_conflict_display_style_t style,
1420 const apr_array_header_t *merge_options,
1421 const char *left_abspath,
1422 const char *detranslated_target,
1423 const char *right_abspath,
1424 svn_io_file_del_t delete_when,
1425 svn_cancel_func_t cancel_func,
1427 apr_pool_t *result_pool,
1428 apr_pool_t *scratch_pool)
1430 const char *temp_dir;
1431 svn_stream_t *chosen_stream;
1433 svn_diff_file_options_t *diff3_options;
1435 diff3_options = svn_diff_file_options_create(scratch_pool);
1437 SVN_ERR(svn_diff_file_options_parse(diff3_options,
1441 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
1443 scratch_pool, scratch_pool));
1444 /* We need to open the stream in RESULT_POOL because that controls the
1445 * lifetime of the file if DELETE_WHEN is 'on pool cleanup'. (We also
1446 * want to allocate CHOSEN_ABSPATH in RESULT_POOL, but we don't care
1447 * about the stream itself.) */
1448 SVN_ERR(svn_stream_open_unique(&chosen_stream, chosen_abspath,
1449 temp_dir, delete_when,
1450 result_pool, scratch_pool));
1451 SVN_ERR(svn_diff_file_diff3_2(&diff,
1453 detranslated_target, right_abspath,
1454 diff3_options, scratch_pool));
1455 SVN_ERR(svn_diff_file_output_merge3(chosen_stream, diff,
1457 detranslated_target,
1459 NULL, NULL, NULL, NULL, /* markers */
1460 style, cancel_func, cancel_baton,
1462 SVN_ERR(svn_stream_close(chosen_stream));
1464 return SVN_NO_ERROR;
1467 /* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the
1468 * working copy at DB/WRI_ABSPATH.
1470 * Set *WORK_ITEMS to a new work item that, when run, will delete the
1471 * artifact file; or to NULL if there is no file to delete.
1473 * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its
1474 * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND
1475 * may be NULL if not required.
1477 static svn_error_t *
1478 remove_artifact_file_if_exists(svn_skel_t **work_items,
1479 svn_boolean_t *file_found,
1481 const char *wri_abspath,
1482 const char *artifact_file_abspath,
1483 apr_pool_t *result_pool,
1484 apr_pool_t *scratch_pool)
1487 if (artifact_file_abspath)
1489 svn_node_kind_t node_kind;
1491 SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind,
1493 if (node_kind == svn_node_file)
1495 SVN_ERR(svn_wc__wq_build_file_remove(work_items,
1497 artifact_file_abspath,
1498 result_pool, scratch_pool));
1504 return SVN_NO_ERROR;
1507 /* Create a new file in the same directory as LOCAL_ABSPATH, with the
1508 same basename as LOCAL_ABSPATH, with a ".edited" extension, and set
1509 *WORK_ITEM to a new work item that will copy and translate from the file
1510 SOURCE_ABSPATH to that new file. It will be translated from repository-
1511 normal form to working-copy form according to the versioned properties
1512 of LOCAL_ABSPATH that are current when the work item is executed.
1514 DB should have a write lock for the directory containing SOURCE.
1516 Allocate *WORK_ITEM in RESULT_POOL. */
1517 static svn_error_t *
1518 save_merge_result(svn_skel_t **work_item,
1520 const char *local_abspath,
1521 const char *source_abspath,
1522 apr_pool_t *result_pool,
1523 apr_pool_t *scratch_pool)
1525 const char *edited_copy_abspath;
1526 const char *dir_abspath;
1527 const char *filename;
1529 svn_dirent_split(&dir_abspath, &filename, local_abspath, scratch_pool);
1531 /* ### Should use preserved-conflict-file-exts. */
1532 /* Create the .edited file within this file's DIR_ABSPATH */
1533 SVN_ERR(svn_io_open_uniquely_named(NULL,
1534 &edited_copy_abspath,
1538 svn_io_file_del_none,
1539 scratch_pool, scratch_pool));
1540 SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item,
1543 edited_copy_abspath,
1544 result_pool, scratch_pool));
1545 return SVN_NO_ERROR;
1550 /* Resolve the text conflict in CONFLICT, which is currently recorded
1551 * on DB/LOCAL_ABSPATH in the manner specified by CHOICE.
1553 * Set *WORK_ITEMS to new work items that will make the on-disk changes
1554 * needed to complete the resolution (but not to mark it as resolved).
1556 * Set *FOUND_ARTIFACT to true if conflict markers are removed; otherwise
1557 * (which is only if CHOICE is 'postpone') to false.
1559 * CHOICE, MERGED_FILE and SAVE_MERGED are typically values provided by
1560 * the conflict resolver.
1562 * MERGE_OPTIONS allows customizing the diff handling when using
1563 * per hunk conflict resolving.
1565 static svn_error_t *
1566 build_text_conflict_resolve_items(svn_skel_t **work_items,
1567 svn_boolean_t *found_artifact,
1569 const char *local_abspath,
1570 const svn_skel_t *conflict,
1571 svn_wc_conflict_choice_t choice,
1572 const char *merged_file,
1573 svn_boolean_t save_merged,
1574 const apr_array_header_t *merge_options,
1575 svn_cancel_func_t cancel_func,
1577 apr_pool_t *result_pool,
1578 apr_pool_t *scratch_pool)
1580 const char *mine_abspath;
1581 const char *their_old_abspath;
1582 const char *their_abspath;
1583 svn_skel_t *work_item;
1584 const char *install_from_abspath = NULL;
1585 svn_boolean_t remove_source = FALSE;
1590 *found_artifact = FALSE;
1592 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
1597 scratch_pool, scratch_pool));
1600 SVN_ERR(save_merge_result(work_items,
1605 result_pool, scratch_pool));
1607 if (choice == svn_wc_conflict_choose_postpone)
1608 return SVN_NO_ERROR;
1612 /* If the callback wants to use one of the fulltexts
1613 to resolve the conflict, so be it.*/
1614 case svn_wc_conflict_choose_base:
1616 install_from_abspath = their_old_abspath;
1619 case svn_wc_conflict_choose_theirs_full:
1621 install_from_abspath = their_abspath;
1624 case svn_wc_conflict_choose_mine_full:
1626 /* In case of selecting to resolve the conflict choosing the full
1627 own file, allow the text conflict resolution to just take the
1628 existing local file if no merged file was present (case: binary
1629 file conflicts do not generate a locally merge file).
1631 install_from_abspath = mine_abspath
1636 case svn_wc_conflict_choose_theirs_conflict:
1637 case svn_wc_conflict_choose_mine_conflict:
1639 svn_diff_conflict_display_style_t style
1640 = choice == svn_wc_conflict_choose_theirs_conflict
1641 ? svn_diff_conflict_display_latest
1642 : svn_diff_conflict_display_modified;
1644 if (mine_abspath == NULL)
1645 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1646 _("Conflict on '%s' cannot be resolved to "
1647 "'theirs-conflict' or 'mine-conflict' "
1648 "because a merged version of the file "
1649 "cannot be created."),
1650 svn_dirent_local_style(local_abspath,
1653 SVN_ERR(merge_showing_conflicts(&install_from_abspath,
1655 style, merge_options,
1659 /* ### why not same as other caller? */
1660 svn_io_file_del_none,
1661 cancel_func, cancel_baton,
1662 scratch_pool, scratch_pool));
1663 remove_source = TRUE;
1667 /* For the case of 3-way file merging, we don't
1668 really distinguish between these return values;
1669 if the callback claims to have "generally
1670 resolved" the situation, we still interpret
1671 that as "OK, we'll assume the merged version is
1673 case svn_wc_conflict_choose_merged:
1675 install_from_abspath = merged_file
1680 case svn_wc_conflict_choose_postpone:
1682 /* Assume conflict remains. */
1683 return SVN_NO_ERROR;
1686 SVN_ERR_ASSERT(choice == svn_wc_conflict_choose_postpone);
1689 if (install_from_abspath == NULL)
1690 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1691 _("Conflict on '%s' could not be resolved "
1692 "because the chosen version of the file "
1693 "is not available."),
1694 svn_dirent_local_style(local_abspath,
1697 /* ### It would be nice if we could somehow pass RECORD_FILEINFO
1698 as true in some easy cases. */
1699 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
1701 install_from_abspath,
1702 FALSE /* use_commit_times */,
1703 FALSE /* record_fileinfo */,
1704 result_pool, scratch_pool));
1705 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1709 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
1711 install_from_abspath,
1712 result_pool, scratch_pool));
1713 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1716 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1719 result_pool, scratch_pool));
1720 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1722 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1725 result_pool, scratch_pool));
1726 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1728 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1731 result_pool, scratch_pool));
1732 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1734 return SVN_NO_ERROR;
1738 /* Set *DESC to a new description of the text conflict in
1739 * CONFLICT_SKEL. If there is no text conflict in CONFLICT_SKEL, return
1742 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1743 * rather than reading them from CONFLICT_SKEL. Use IS_BINARY and
1744 * MIME_TYPE for the corresponding fields of *DESC.
1746 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
1748 static svn_error_t *
1749 read_text_conflict_desc(svn_wc_conflict_description2_t **desc,
1751 const char *local_abspath,
1752 const svn_skel_t *conflict_skel,
1753 const char *mime_type,
1754 svn_wc_operation_t operation,
1755 const svn_wc_conflict_version_t *left_version,
1756 const svn_wc_conflict_version_t *right_version,
1757 apr_pool_t *result_pool,
1758 apr_pool_t *scratch_pool)
1760 *desc = svn_wc_conflict_description_create_text2(local_abspath, result_pool);
1761 (*desc)->mime_type = mime_type;
1762 (*desc)->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
1763 (*desc)->operation = operation;
1764 (*desc)->src_left_version = left_version;
1765 (*desc)->src_right_version = right_version;
1767 SVN_ERR(svn_wc__conflict_read_text_conflict(&(*desc)->my_abspath,
1768 &(*desc)->base_abspath,
1769 &(*desc)->their_abspath,
1772 result_pool, scratch_pool));
1773 (*desc)->merged_file = apr_pstrdup(result_pool, local_abspath);
1775 return SVN_NO_ERROR;
1778 /* Set *CONFLICT_DESC to a new description of the tree conflict in
1779 * CONFLICT_SKEL. If there is no tree conflict in CONFLICT_SKEL, return
1782 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1783 * rather than reading them from CONFLICT_SKEL.
1785 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
1787 static svn_error_t *
1788 read_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
1790 const char *local_abspath,
1791 svn_node_kind_t node_kind,
1792 const svn_skel_t *conflict_skel,
1793 svn_wc_operation_t operation,
1794 const svn_wc_conflict_version_t *left_version,
1795 const svn_wc_conflict_version_t *right_version,
1796 apr_pool_t *result_pool,
1797 apr_pool_t *scratch_pool)
1799 svn_node_kind_t local_kind;
1800 svn_wc_conflict_reason_t reason;
1801 svn_wc_conflict_action_t action;
1803 SVN_ERR(svn_wc__conflict_read_tree_conflict(
1804 &reason, &action, NULL,
1805 db, local_abspath, conflict_skel, scratch_pool, scratch_pool));
1807 if (reason == svn_wc_conflict_reason_missing)
1808 local_kind = svn_node_none;
1809 else if (reason == svn_wc_conflict_reason_unversioned ||
1810 reason == svn_wc_conflict_reason_obstructed)
1811 SVN_ERR(svn_io_check_path(local_abspath, &local_kind, scratch_pool));
1812 else if (action == svn_wc_conflict_action_delete
1814 && (operation == svn_wc_operation_update
1815 ||operation == svn_wc_operation_switch)
1816 && (reason == svn_wc_conflict_reason_deleted
1817 || reason == svn_wc_conflict_reason_moved_away))
1819 /* We have nothing locally to take the kind from */
1820 local_kind = left_version->node_kind;
1823 local_kind = node_kind;
1825 *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind,
1827 left_version, right_version,
1829 (*desc)->reason = reason;
1830 (*desc)->action = action;
1832 return SVN_NO_ERROR;
1835 /* Forward definition */
1836 static svn_error_t *
1837 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
1839 const char *local_abspath,
1840 const svn_skel_t *conflict,
1841 svn_wc_conflict_choice_t conflict_choice,
1842 apr_hash_t *resolve_later,
1843 svn_wc_notify_func2_t notify_func,
1845 svn_cancel_func_t cancel_func,
1847 apr_pool_t *scratch_pool);
1850 svn_wc__conflict_invoke_resolver(svn_wc__db_t *db,
1851 const char *local_abspath,
1852 svn_node_kind_t kind,
1853 const svn_skel_t *conflict_skel,
1854 const apr_array_header_t *merge_options,
1855 svn_wc_conflict_resolver_func2_t resolver_func,
1856 void *resolver_baton,
1857 svn_cancel_func_t cancel_func,
1859 apr_pool_t *scratch_pool)
1861 svn_boolean_t text_conflicted;
1862 svn_boolean_t prop_conflicted;
1863 svn_boolean_t tree_conflicted;
1864 svn_wc_operation_t operation;
1865 const apr_array_header_t *locations;
1866 const svn_wc_conflict_version_t *left_version = NULL;
1867 const svn_wc_conflict_version_t *right_version = NULL;
1869 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
1870 &text_conflicted, &prop_conflicted,
1872 db, local_abspath, conflict_skel,
1873 scratch_pool, scratch_pool));
1875 if (locations && locations->nelts > 0)
1876 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
1878 if (locations && locations->nelts > 1)
1879 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
1881 /* Quick and dirty compatibility wrapper. My guess would be that most resolvers
1882 would want to look at all properties at the same time.
1884 ### svn currently only invokes this from the merge code to collect the list of
1885 ### conflicted paths. Eventually this code will be the base for 'svn resolve'
1886 ### and at that time the test coverage will improve
1888 if (prop_conflicted)
1890 apr_hash_t *old_props;
1891 apr_hash_t *mine_props;
1892 apr_hash_t *their_props;
1893 apr_hash_t *old_their_props;
1894 apr_hash_t *conflicted;
1895 apr_pool_t *iterpool;
1896 apr_hash_index_t *hi;
1897 svn_boolean_t mark_resolved = TRUE;
1899 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
1906 scratch_pool, scratch_pool));
1908 if (operation == svn_wc_operation_merge)
1909 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
1910 scratch_pool, scratch_pool));
1912 old_props = old_their_props;
1914 iterpool = svn_pool_create(scratch_pool);
1916 for (hi = apr_hash_first(scratch_pool, conflicted);
1918 hi = apr_hash_next(hi))
1920 const char *propname = apr_hash_this_key(hi);
1921 svn_boolean_t conflict_remains = TRUE;
1923 svn_pool_clear(iterpool);
1926 SVN_ERR(cancel_func(cancel_baton));
1928 SVN_ERR(generate_propconflict(&conflict_remains,
1929 db, local_abspath, kind,
1935 ? svn_hash_gets(old_props, propname)
1938 ? svn_hash_gets(mine_props, propname)
1941 ? svn_hash_gets(old_their_props, propname)
1944 ? svn_hash_gets(their_props, propname)
1946 resolver_func, resolver_baton,
1947 cancel_func, cancel_baton,
1950 if (conflict_remains)
1951 mark_resolved = FALSE;
1956 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
1959 svn_pool_destroy(iterpool);
1962 if (text_conflicted)
1964 svn_skel_t *work_items;
1965 svn_boolean_t was_resolved;
1966 svn_wc_conflict_description2_t *desc;
1968 svn_wc_conflict_result_t *result;
1970 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
1971 scratch_pool, scratch_pool));
1973 SVN_ERR(read_text_conflict_desc(&desc,
1974 db, local_abspath, conflict_skel,
1975 svn_prop_get_value(props,
1976 SVN_PROP_MIME_TYPE),
1977 operation, left_version, right_version,
1978 scratch_pool, scratch_pool));
1982 was_resolved = FALSE;
1984 /* Give the conflict resolution callback a chance to clean
1985 up the conflicts before we mark the file 'conflicted' */
1987 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
1990 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1991 _("Conflict callback violated API:"
1992 " returned no results"));
1994 SVN_ERR(build_text_conflict_resolve_items(&work_items, &was_resolved,
1996 conflict_skel, result->choice,
1997 result->merged_file,
1998 result->save_merged,
2000 cancel_func, cancel_baton,
2001 scratch_pool, scratch_pool));
2003 if (result->choice != svn_wc_conflict_choose_postpone)
2005 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2007 work_items, scratch_pool));
2008 SVN_ERR(svn_wc__wq_run(db, local_abspath,
2009 cancel_func, cancel_baton,
2014 if (tree_conflicted)
2016 svn_wc_conflict_result_t *result;
2017 svn_wc_conflict_description2_t *desc;
2018 svn_boolean_t resolved;
2019 svn_node_kind_t node_kind;
2021 SVN_ERR(svn_wc__db_read_kind(&node_kind, db, local_abspath, TRUE,
2022 TRUE, FALSE, scratch_pool));
2024 SVN_ERR(read_tree_conflict_desc(&desc,
2025 db, local_abspath, node_kind,
2027 operation, left_version, right_version,
2028 scratch_pool, scratch_pool));
2030 /* Tell the resolver func about this conflict. */
2031 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
2035 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2036 _("Conflict callback violated API:"
2037 " returned no results"));
2039 /* Pass retry hash to avoid erroring out on cases where update
2040 can continue safely. ### Need notify handling */
2041 if (result->choice != svn_wc_conflict_choose_postpone)
2042 SVN_ERR(resolve_tree_conflict_on_node(&resolved,
2043 db, local_abspath, conflict_skel,
2045 apr_hash_make(scratch_pool),
2046 NULL, NULL, /* ### notify */
2047 cancel_func, cancel_baton,
2051 return SVN_NO_ERROR;
2054 /* Read all property conflicts contained in CONFLICT_SKEL into
2055 * individual conflict descriptions, and append those descriptions
2056 * to the CONFLICTS array. If there is no property conflict in
2057 * CONFLICT_SKEL, return an error.
2059 * If NOT create_tempfiles, always create a legacy property conflict
2062 * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
2063 * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
2065 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
2067 static svn_error_t *
2068 read_prop_conflict_descs(apr_array_header_t *conflicts,
2070 const char *local_abspath,
2071 svn_skel_t *conflict_skel,
2072 svn_boolean_t create_tempfiles,
2073 svn_node_kind_t node_kind,
2074 svn_wc_operation_t operation,
2075 const svn_wc_conflict_version_t *left_version,
2076 const svn_wc_conflict_version_t *right_version,
2077 apr_pool_t *result_pool,
2078 apr_pool_t *scratch_pool)
2080 const char *prop_reject_abspath;
2081 apr_hash_t *base_props;
2082 apr_hash_t *my_props;
2083 apr_hash_t *their_old_props;
2084 apr_hash_t *their_props;
2085 apr_hash_t *conflicted_props;
2086 apr_hash_index_t *hi;
2087 apr_pool_t *iterpool;
2088 svn_boolean_t prop_conflicted;
2090 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2091 NULL, db, local_abspath, conflict_skel,
2092 scratch_pool, scratch_pool));
2094 if (!prop_conflicted)
2095 return SVN_NO_ERROR;
2097 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath,
2104 scratch_pool, scratch_pool));
2106 prop_reject_abspath = apr_pstrdup(result_pool, prop_reject_abspath);
2108 if (apr_hash_count(conflicted_props) == 0)
2110 /* Legacy prop conflict with only a .reject file. */
2111 svn_wc_conflict_description2_t *desc;
2113 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2117 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2118 * ### their_abspath, and stores theirs_abspath in merged_file. */
2119 desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2120 desc->their_abspath = desc->prop_reject_abspath;
2122 desc->operation = operation;
2123 desc->src_left_version = left_version;
2124 desc->src_right_version = right_version;
2126 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2128 return SVN_NO_ERROR;
2131 if (operation == svn_wc_operation_merge)
2132 SVN_ERR(svn_wc__db_read_pristine_props(&base_props, db, local_abspath,
2133 result_pool, scratch_pool));
2136 iterpool = svn_pool_create(scratch_pool);
2137 for (hi = apr_hash_first(scratch_pool, conflicted_props);
2139 hi = apr_hash_next(hi))
2141 const char *propname = apr_hash_this_key(hi);
2142 svn_string_t *old_value;
2143 svn_string_t *my_value;
2144 svn_string_t *their_value;
2145 svn_wc_conflict_description2_t *desc;
2147 svn_pool_clear(iterpool);
2149 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2154 desc->operation = operation;
2155 desc->src_left_version = left_version;
2156 desc->src_right_version = right_version;
2158 desc->property_name = apr_pstrdup(result_pool, propname);
2160 my_value = svn_hash_gets(my_props, propname);
2161 their_value = svn_hash_gets(their_props, propname);
2162 old_value = svn_hash_gets(their_old_props, propname);
2164 /* Compute the incoming side of the conflict ('action'). */
2165 if (their_value == NULL)
2166 desc->action = svn_wc_conflict_action_delete;
2167 else if (old_value == NULL)
2168 desc->action = svn_wc_conflict_action_add;
2170 desc->action = svn_wc_conflict_action_edit;
2172 /* Compute the local side of the conflict ('reason'). */
2173 if (my_value == NULL)
2174 desc->reason = svn_wc_conflict_reason_deleted;
2175 else if (old_value == NULL)
2176 desc->reason = svn_wc_conflict_reason_added;
2178 desc->reason = svn_wc_conflict_reason_edited;
2180 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2181 * ### their_abspath, and stores theirs_abspath in merged_file. */
2182 desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2183 desc->their_abspath = desc->prop_reject_abspath;
2185 desc->prop_value_base = base_props ? svn_hash_gets(base_props, propname)
2186 : desc->prop_value_incoming_old;
2193 if (create_tempfiles)
2195 SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL,
2196 svn_io_file_del_on_pool_cleanup,
2197 result_pool, iterpool));
2198 len = my_value->len;
2199 SVN_ERR(svn_stream_write(s, my_value->data, &len));
2200 SVN_ERR(svn_stream_close(s));
2203 desc->prop_value_working = svn_string_dup(my_value, result_pool);
2211 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2212 * ### their_abspath, and stores theirs_abspath in merged_file. */
2213 if (create_tempfiles)
2215 SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL,
2216 svn_io_file_del_on_pool_cleanup,
2217 result_pool, iterpool));
2218 len = their_value->len;
2219 SVN_ERR(svn_stream_write(s, their_value->data, &len));
2220 SVN_ERR(svn_stream_close(s));
2223 desc->prop_value_incoming_new = svn_string_dup(their_value, result_pool);
2231 if (create_tempfiles)
2233 SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL,
2234 svn_io_file_del_on_pool_cleanup,
2235 result_pool, iterpool));
2236 len = old_value->len;
2237 SVN_ERR(svn_stream_write(s, old_value->data, &len));
2238 SVN_ERR(svn_stream_close(s));
2241 desc->prop_value_incoming_old = svn_string_dup(old_value, result_pool);
2244 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2246 svn_pool_destroy(iterpool);
2248 return SVN_NO_ERROR;
2252 svn_wc__read_conflicts(const apr_array_header_t **conflicts,
2253 svn_skel_t **conflict_skel,
2255 const char *local_abspath,
2256 svn_boolean_t create_tempfiles,
2257 svn_boolean_t only_tree_conflict,
2258 apr_pool_t *result_pool,
2259 apr_pool_t *scratch_pool)
2261 svn_skel_t *the_conflict_skel;
2262 apr_array_header_t *cflcts;
2263 svn_boolean_t prop_conflicted;
2264 svn_boolean_t text_conflicted;
2265 svn_boolean_t tree_conflicted;
2266 svn_wc_operation_t operation;
2267 const apr_array_header_t *locations;
2268 const svn_wc_conflict_version_t *left_version = NULL;
2269 const svn_wc_conflict_version_t *right_version = NULL;
2270 svn_node_kind_t node_kind;
2274 conflict_skel = &the_conflict_skel;
2276 SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props,
2278 (conflict_skel == &the_conflict_skel)
2283 if (!*conflict_skel)
2285 /* Some callers expect not NULL */
2286 *conflicts = apr_array_make(result_pool, 0,
2287 sizeof(svn_wc_conflict_description2_t *));
2288 return SVN_NO_ERROR;
2291 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted,
2292 &prop_conflicted, &tree_conflicted,
2293 db, local_abspath, *conflict_skel,
2294 result_pool, scratch_pool));
2296 cflcts = apr_array_make(result_pool, 4,
2297 sizeof(svn_wc_conflict_description2_t *));
2299 if (locations && locations->nelts > 0)
2300 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
2301 if (locations && locations->nelts > 1)
2302 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
2304 if (prop_conflicted && !only_tree_conflict)
2306 SVN_ERR(read_prop_conflict_descs(cflcts,
2307 db, local_abspath, *conflict_skel,
2308 create_tempfiles, node_kind,
2309 operation, left_version, right_version,
2310 result_pool, scratch_pool));
2313 if (text_conflicted && !only_tree_conflict)
2315 svn_wc_conflict_description2_t *desc;
2317 SVN_ERR(read_text_conflict_desc(&desc,
2318 db, local_abspath, *conflict_skel,
2319 svn_prop_get_value(props,
2320 SVN_PROP_MIME_TYPE),
2321 operation, left_version, right_version,
2322 result_pool, scratch_pool));
2323 APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t *) = desc;
2326 if (tree_conflicted)
2328 svn_wc_conflict_description2_t *desc;
2330 SVN_ERR(read_tree_conflict_desc(&desc,
2331 db, local_abspath, node_kind,
2333 operation, left_version, right_version,
2334 result_pool, scratch_pool));
2336 APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc;
2339 *conflicts = cflcts;
2340 return SVN_NO_ERROR;
2344 svn_wc__read_conflict_descriptions2_t(const apr_array_header_t **conflicts,
2345 svn_wc_context_t *wc_ctx,
2346 const char *local_abspath,
2347 apr_pool_t *result_pool,
2348 apr_pool_t *scratch_pool)
2350 return svn_wc__read_conflicts(conflicts, NULL, wc_ctx->db, local_abspath,
2351 FALSE, FALSE, result_pool, scratch_pool);
2355 /*** Resolving a conflict automatically ***/
2358 * Resolve the property conflicts found in DB/LOCAL_ABSPATH according
2359 * to CONFLICT_CHOICE.
2361 * It is not an error if there is no prop conflict. If a prop conflict
2362 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2364 * Note: When there are no conflict markers on-disk to remove there is
2365 * no existing text conflict (unless we are still in the process of
2366 * creating the text conflict and we didn't register a marker file yet).
2367 * In this case the database contains old information, which we should
2368 * remove to avoid checking the next time. Resolving a property conflict
2369 * by just removing the marker file is a fully supported scenario since
2372 * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
2373 * In my opinion, 'mine_full'/'theirs_full' should select
2374 * the entire set of properties from 'mine' or 'theirs' respectively,
2375 * while 'mine_conflict'/'theirs_conflict' should select just the
2376 * properties that are in conflict. Or, '_full' should select the
2377 * entire property whereas '_conflict' should do a text merge within
2378 * each property, selecting hunks. Or all three kinds of behaviour
2379 * should be available (full set of props, full value of conflicting
2380 * props, or conflicting text hunks).
2381 * ### BH: If we make *_full select the full set of properties, we should
2382 * check if we shouldn't make it also select the full text for files.
2384 * ### TODO [JAF] All this complexity should not be down here in libsvn_wc
2385 * but in a layer above.
2387 * ### TODO [JAF] Options for 'base' should be like options for 'mine' and
2388 * for 'theirs' -- choose full set of props, full value of conflicting
2389 * props, or conflicting text hunks.
2392 static svn_error_t *
2393 resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
2395 const char *local_abspath,
2396 svn_skel_t *conflicts,
2397 const char *conflicted_propname,
2398 svn_wc_conflict_choice_t conflict_choice,
2399 const char *merged_file,
2400 const svn_string_t *merged_value,
2401 svn_cancel_func_t cancel_func,
2403 apr_pool_t *scratch_pool)
2405 const char *prop_reject_file;
2406 apr_hash_t *mine_props;
2407 apr_hash_t *their_old_props;
2408 apr_hash_t *their_props;
2409 apr_hash_t *conflicted_props;
2410 apr_hash_t *old_props;
2411 apr_hash_t *resolve_from = NULL;
2412 svn_skel_t *work_items = NULL;
2413 svn_wc_operation_t operation;
2414 svn_boolean_t prop_conflicted;
2415 apr_hash_t *actual_props;
2416 svn_boolean_t resolved_all, resolved_all_prop;
2418 *did_resolve = FALSE;
2420 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2421 NULL, db, local_abspath, conflicts,
2422 scratch_pool, scratch_pool));
2423 if (!prop_conflicted)
2424 return SVN_NO_ERROR;
2426 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2427 &mine_props, &their_old_props,
2428 &their_props, &conflicted_props,
2429 db, local_abspath, conflicts,
2430 scratch_pool, scratch_pool));
2432 if (!conflicted_props)
2434 /* We have a pre 1.8 property conflict. Just mark it resolved */
2436 SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2437 db, local_abspath, prop_reject_file,
2438 scratch_pool, scratch_pool));
2439 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE,
2440 work_items, scratch_pool));
2441 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2443 return SVN_NO_ERROR;
2446 if (conflicted_propname[0] != '\0'
2447 && !svn_hash_gets(conflicted_props, conflicted_propname))
2449 return SVN_NO_ERROR; /* This property is not conflicted! */
2452 if (operation == svn_wc_operation_merge)
2453 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
2454 scratch_pool, scratch_pool));
2456 old_props = their_old_props;
2458 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2459 scratch_pool, scratch_pool));
2461 /* We currently handle *_conflict as *_full as this argument is currently
2462 always applied for all conflicts on a node at the same time. Giving
2463 an error would break some tests that assumed that this would just
2464 resolve property conflicts to working.
2466 An alternative way to handle these conflicts would be to just copy all
2467 property state from mine/theirs on the _full option instead of just the
2468 conflicted properties. In some ways this feels like a sensible option as
2469 that would take both properties and text from mine/theirs, but when not
2470 both properties and text are conflicted we would fail in doing so.
2472 switch (conflict_choice)
2474 case svn_wc_conflict_choose_base:
2475 resolve_from = their_old_props ? their_old_props : old_props;
2477 case svn_wc_conflict_choose_mine_full:
2478 case svn_wc_conflict_choose_mine_conflict:
2479 resolve_from = mine_props;
2481 case svn_wc_conflict_choose_theirs_full:
2482 case svn_wc_conflict_choose_theirs_conflict:
2483 resolve_from = their_props;
2485 case svn_wc_conflict_choose_merged:
2486 if ((merged_file || merged_value) && conflicted_propname[0] != '\0')
2488 resolve_from = apr_hash_copy(scratch_pool, actual_props);
2492 svn_stringbuf_t *merged_propval;
2494 SVN_ERR(svn_stringbuf_from_file2(&merged_propval, merged_file,
2497 merged_value = svn_stringbuf__morph_into_string(merged_propval);
2499 svn_hash_sets(resolve_from, conflicted_propname, merged_value);
2502 resolve_from = NULL;
2505 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2506 _("Invalid 'conflict_result' argument"));
2512 apr_hash_index_t *hi;
2513 apr_hash_t *apply_on_props;
2515 if (conflicted_propname[0] == '\0')
2517 /* Apply to all conflicted properties */
2518 apply_on_props = conflicted_props;
2522 /* Apply to a single property */
2523 apply_on_props = apr_hash_make(scratch_pool);
2524 svn_hash_sets(apply_on_props, conflicted_propname, "");
2527 /* Apply the selected changes */
2528 for (hi = apr_hash_first(scratch_pool, apply_on_props);
2530 hi = apr_hash_next(hi))
2532 const char *propname = apr_hash_this_key(hi);
2533 svn_string_t *new_value = NULL;
2535 new_value = svn_hash_gets(resolve_from, propname);
2537 svn_hash_sets(actual_props, propname, new_value);
2540 /*else the user accepted the properties as-is */
2542 /* This function handles conflicted_propname "" as resolving
2543 all property conflicts... Just what we need here */
2544 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
2546 FALSE, conflicted_propname,
2548 scratch_pool, scratch_pool));
2552 /* Are there still property conflicts left? (or only...) */
2553 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, &prop_conflicted,
2554 NULL, db, local_abspath, conflicts,
2555 scratch_pool, scratch_pool));
2557 resolved_all_prop = (! prop_conflicted);
2561 resolved_all_prop = TRUE;
2565 if (resolved_all_prop)
2567 /* Legacy behavior: Only report property conflicts as resolved when the
2568 property reject file exists
2570 If not the UI shows the conflict as already resolved
2571 (and in this case we just remove the in-db conflict) */
2572 SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2575 scratch_pool, scratch_pool));
2579 /* Create a new prej file, based on the remaining conflicts */
2580 SVN_ERR(svn_wc__wq_build_prej_install(&work_items,
2582 scratch_pool, scratch_pool));
2583 *did_resolve = TRUE; /* We resolved a property conflict */
2586 /* This installs the updated conflict skel */
2587 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props,
2588 FALSE, conflicts, work_items,
2593 /* Remove the whole conflict. Should probably be integrated
2594 into the op_set_props() call */
2595 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2597 NULL, scratch_pool));
2600 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2603 return SVN_NO_ERROR;
2607 * Record a tree conflict resolution failure due to error condition ERR
2608 * in the RESOLVE_LATER hash table. If the hash table is not available
2609 * (meaning the caller does not wish to retry resolution later), or if
2610 * the error condition does not indicate circumstances where another
2611 * existing tree conflict is blocking the resolution attempt, then
2612 * return the error ERR itself.
2614 static svn_error_t *
2615 handle_tree_conflict_resolution_failure(const char *local_abspath,
2617 apr_hash_t *resolve_later)
2619 const char *dup_abspath;
2622 || (err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE
2623 && err->apr_err != SVN_ERR_WC_FOUND_CONFLICT))
2624 return svn_error_trace(err); /* Give up. Do not retry resolution later. */
2626 svn_error_clear(err);
2627 dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2630 svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2632 return SVN_NO_ERROR; /* Caller may retry after resolving other conflicts. */
2636 * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
2639 * It is not an error if there is no tree conflict. If a tree conflict
2640 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2642 * It is not an error if there is no tree conflict.
2644 * If the conflict can't be resolved yet (e.g. because another tree conflict
2645 * is blocking a storage location), and RESOLVE_LATER is not NULL, store the
2646 * tree conflict in RESOLVE_LATER and do not mark the conflict resolved.
2647 * Else if RESOLVE_LATER is NULL, do not mark the conflict resolved and
2648 * return the error which prevented the conflict from being marked resolved.
2650 static svn_error_t *
2651 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
2653 const char *local_abspath,
2654 const svn_skel_t *conflicts,
2655 svn_wc_conflict_choice_t conflict_choice,
2656 apr_hash_t *resolve_later,
2657 svn_wc_notify_func2_t notify_func,
2659 svn_cancel_func_t cancel_func,
2661 apr_pool_t *scratch_pool)
2663 svn_wc_conflict_reason_t reason;
2664 svn_wc_conflict_action_t action;
2665 svn_wc_operation_t operation;
2666 svn_boolean_t tree_conflicted;
2667 const char *src_op_root_abspath;
2669 *did_resolve = FALSE;
2671 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
2672 &tree_conflicted, db, local_abspath,
2673 conflicts, scratch_pool, scratch_pool));
2674 if (!tree_conflicted)
2675 return SVN_NO_ERROR;
2677 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2678 &src_op_root_abspath,
2681 scratch_pool, scratch_pool));
2683 if (operation == svn_wc_operation_update
2684 || operation == svn_wc_operation_switch)
2687 if (reason == svn_wc_conflict_reason_deleted ||
2688 reason == svn_wc_conflict_reason_replaced)
2690 if (conflict_choice == svn_wc_conflict_choose_merged)
2692 /* Break moves for any children moved out of this directory,
2693 * and leave this directory deleted. */
2695 if (action != svn_wc_conflict_action_delete)
2697 SVN_ERR(svn_wc__db_op_break_moved_away(
2698 db, local_abspath, src_op_root_abspath, TRUE,
2699 notify_func, notify_baton,
2701 *did_resolve = TRUE;
2702 return SVN_NO_ERROR; /* Marked resolved by function*/
2704 /* else # The move is/moves are already broken */
2707 *did_resolve = TRUE;
2709 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2711 svn_skel_t *new_conflicts;
2713 /* Raise local moved-away vs. incoming edit conflicts on
2714 * any children moved out of this directory, and leave
2715 * this directory as-is.
2717 * The newly conflicted moved-away children will be updated
2718 * if they are resolved with 'mine_conflict' as well. */
2719 err = svn_wc__db_op_raise_moved_away(
2720 db, local_abspath, notify_func, notify_baton,
2724 SVN_ERR(handle_tree_conflict_resolution_failure(
2725 local_abspath, err, resolve_later));
2727 /* We might now have a moved-away on *this* path, let's
2728 try to resolve that directly if that is the case */
2729 SVN_ERR(svn_wc__db_read_conflict(&new_conflicts, NULL, NULL,
2731 scratch_pool, scratch_pool));
2734 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
2741 if (!new_conflicts || !tree_conflicted)
2743 /* TC is marked resolved by calling
2744 svn_wc__db_op_raise_moved_away */
2745 *did_resolve = TRUE;
2746 return SVN_NO_ERROR;
2749 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2750 &src_op_root_abspath,
2756 if (reason != svn_wc_conflict_reason_moved_away)
2758 *did_resolve = TRUE;
2759 return SVN_NO_ERROR; /* We fixed one, but... */
2762 conflicts = new_conflicts;
2763 /* Fall through in moved_away handling */
2766 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2768 _("Tree conflict can only be resolved to "
2769 "'working' or 'mine-conflict' state; "
2770 "'%s' not resolved"),
2771 svn_dirent_local_style(local_abspath,
2775 if (reason == svn_wc_conflict_reason_moved_away
2776 && action == svn_wc_conflict_action_edit)
2778 /* After updates, we can resolve local moved-away
2779 * vs. any incoming change, either by updating the
2780 * moved-away node (mine-conflict) or by breaking the
2781 * move (theirs-conflict). */
2782 if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2784 err = svn_wc__db_update_moved_away_conflict_victim(
2785 db, local_abspath, src_op_root_abspath,
2786 operation, action, reason,
2787 cancel_func, cancel_baton,
2788 notify_func, notify_baton,
2792 SVN_ERR(handle_tree_conflict_resolution_failure(
2793 local_abspath, err, resolve_later));
2795 *did_resolve = TRUE;
2797 else if (conflict_choice == svn_wc_conflict_choose_merged)
2799 /* We must break the move if the user accepts the current
2800 * working copy state instead of updating the move.
2801 * Else the move would be left in an invalid state. */
2803 SVN_ERR(svn_wc__db_op_break_moved_away(db, local_abspath,
2804 src_op_root_abspath, TRUE,
2805 notify_func, notify_baton,
2807 *did_resolve = TRUE;
2808 return SVN_NO_ERROR; /* Conflict is marked resolved */
2811 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2813 _("Tree conflict can only be resolved to "
2814 "'working' or 'mine-conflict' state; "
2815 "'%s' not resolved"),
2816 svn_dirent_local_style(local_abspath,
2819 else if (reason == svn_wc_conflict_reason_moved_away
2820 && action != svn_wc_conflict_action_edit)
2822 /* action added is impossible, because that would imply that
2823 something was added, but before that already moved...
2824 (which would imply a replace) */
2825 SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete
2826 || action == svn_wc_conflict_action_replace);
2828 if (conflict_choice == svn_wc_conflict_choose_merged)
2830 /* Whatever was moved is removed at its original location by the
2831 update. That must also remove the recording of the move, so
2832 we don't have to do anything here. */
2834 *did_resolve = TRUE;
2836 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2838 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2840 _("Tree conflict can only be "
2841 "resolved to 'working' state; "
2842 "'%s' is no longer moved"),
2843 svn_dirent_local_style(local_abspath,
2851 if (conflict_choice != svn_wc_conflict_choose_merged)
2853 /* For other tree conflicts, there is no way to pick
2854 * theirs-full or mine-full, etc. Throw an error if the
2855 * user expects us to be smarter than we really are. */
2856 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2858 _("Tree conflict can only be "
2859 "resolved to 'working' state; "
2860 "'%s' not resolved"),
2861 svn_dirent_local_style(local_abspath,
2865 *did_resolve = TRUE;
2868 SVN_ERR_ASSERT(*did_resolve);
2870 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE,
2871 NULL, scratch_pool));
2872 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2874 return SVN_NO_ERROR;
2878 svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
2879 const char *local_abspath,
2880 svn_cancel_func_t cancel_func,
2882 apr_pool_t *scratch_pool)
2884 svn_skel_t *work_items;
2885 svn_skel_t *conflict;
2887 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
2889 scratch_pool, scratch_pool));
2892 return SVN_NO_ERROR;
2894 SVN_ERR(build_text_conflict_resolve_items(&work_items, NULL,
2895 db, local_abspath, conflict,
2896 svn_wc_conflict_choose_merged,
2898 cancel_func, cancel_baton,
2899 scratch_pool, scratch_pool));
2901 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, TRUE, FALSE, FALSE,
2902 work_items, scratch_pool));
2904 return svn_error_trace(svn_wc__wq_run(db, local_abspath,
2905 cancel_func, cancel_baton,
2910 svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db,
2911 const char *local_abspath,
2912 apr_pool_t *scratch_pool)
2914 svn_boolean_t ignored_result;
2915 svn_skel_t *conflicts;
2917 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
2919 scratch_pool, scratch_pool));
2922 return SVN_NO_ERROR;
2924 return svn_error_trace(resolve_prop_conflict_on_node(
2926 db, local_abspath, conflicts, "",
2927 svn_wc_conflict_choose_merged,
2934 /* Baton for conflict_status_walker */
2935 struct conflict_status_walker_baton
2938 svn_boolean_t resolve_text;
2939 const char *resolve_prop;
2940 svn_boolean_t resolve_tree;
2941 svn_wc_conflict_choice_t conflict_choice;
2942 svn_wc_conflict_resolver_func2_t conflict_func;
2943 void *conflict_baton;
2944 svn_cancel_func_t cancel_func;
2946 svn_wc_notify_func2_t notify_func;
2948 svn_boolean_t resolved_one;
2949 apr_hash_t *resolve_later;
2952 /* Implements svn_wc_notify_func2_t to collect new conflicts caused by
2953 resolving a tree conflict. */
2955 tree_conflict_collector(void *baton,
2956 const svn_wc_notify_t *notify,
2959 struct conflict_status_walker_baton *cswb = baton;
2961 if (cswb->notify_func)
2962 cswb->notify_func(cswb->notify_baton, notify, pool);
2964 if (cswb->resolve_later
2965 && (notify->action == svn_wc_notify_tree_conflict
2966 || notify->prop_state == svn_wc_notify_state_conflicted
2967 || notify->content_state == svn_wc_notify_state_conflicted))
2969 if (!svn_hash_gets(cswb->resolve_later, notify->path))
2971 const char *dup_path;
2973 dup_path = apr_pstrdup(apr_hash_pool_get(cswb->resolve_later),
2976 svn_hash_sets(cswb->resolve_later, dup_path, dup_path);
2981 /* Implements svn_wc_status4_t to walk all conflicts to resolve.
2983 static svn_error_t *
2984 conflict_status_walker(void *baton,
2985 const char *local_abspath,
2986 const svn_wc_status3_t *status,
2987 apr_pool_t *scratch_pool)
2989 struct conflict_status_walker_baton *cswb = baton;
2990 svn_wc__db_t *db = cswb->db;
2991 svn_wc_notify_action_t notify_action = svn_wc_notify_resolved;
2992 const apr_array_header_t *conflicts;
2993 apr_pool_t *iterpool;
2995 svn_boolean_t resolved = FALSE;
2996 svn_skel_t *conflict;
2997 const svn_wc_conflict_description2_t *cd;
2999 if (!status->conflicted)
3000 return SVN_NO_ERROR;
3002 iterpool = svn_pool_create(scratch_pool);
3004 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict,
3006 (cswb->conflict_func != NULL) /* tmp files */,
3007 FALSE /* only tree conflicts */,
3008 scratch_pool, iterpool));
3010 for (i = 0; i < conflicts->nelts; i++)
3012 svn_boolean_t did_resolve;
3013 svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
3014 svn_wc_conflict_result_t *result = NULL;
3015 svn_skel_t *work_items;
3017 cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
3019 if ((cd->kind == svn_wc_conflict_kind_property
3020 && (!cswb->resolve_prop
3021 || (*cswb->resolve_prop != '\0'
3022 && strcmp(cswb->resolve_prop, cd->property_name) != 0)))
3023 || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text)
3024 || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree))
3026 continue; /* Easy out. Don't call resolver func and ignore result */
3029 svn_pool_clear(iterpool);
3031 if (my_choice == svn_wc_conflict_choose_unspecified)
3033 if (!cswb->conflict_func)
3034 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3035 _("No conflict-callback and no "
3036 "pre-defined conflict-choice provided"));
3038 SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton,
3039 iterpool, iterpool));
3041 my_choice = result->choice;
3045 if (my_choice == svn_wc_conflict_choose_postpone)
3050 case svn_wc_conflict_kind_tree:
3051 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
3053 local_abspath, conflict,
3055 cswb->resolve_later,
3056 tree_conflict_collector,
3065 notify_action = svn_wc_notify_resolved_tree;
3069 case svn_wc_conflict_kind_text:
3070 SVN_ERR(build_text_conflict_resolve_items(
3073 db, local_abspath, conflict,
3075 result ? result->merged_file
3077 result ? result->save_merged
3079 NULL /* merge_options */,
3082 iterpool, iterpool));
3084 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
3086 work_items, iterpool));
3087 SVN_ERR(svn_wc__wq_run(db, local_abspath,
3088 cswb->cancel_func, cswb->cancel_baton,
3091 notify_action = svn_wc_notify_resolved_text;
3094 case svn_wc_conflict_kind_property:
3095 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
3102 ? result->merged_file
3105 ? result->merged_value
3114 notify_action = svn_wc_notify_resolved_prop;
3119 /* We can't resolve other conflict types */
3125 if (cswb->notify_func && resolved)
3127 svn_wc_notify_t *notify;
3129 /* If our caller asked for all conflicts to be resolved,
3130 * send a general 'resolved' notification. */
3131 if (cswb->resolve_text && cswb->resolve_tree &&
3132 (cswb->resolve_prop == NULL || cswb->resolve_prop[0] == '\0'))
3133 notify_action = svn_wc_notify_resolved;
3135 /* If we resolved a property conflict, but no specific property was
3136 * requested by the caller, send a general 'resolved' notification. */
3137 if (notify_action == svn_wc_notify_resolved_prop &&
3138 (cswb->resolve_prop == NULL || cswb->resolve_prop[0] == '\0'))
3139 notify_action = svn_wc_notify_resolved;
3141 notify = svn_wc_create_notify(local_abspath, notify_action, iterpool);
3143 /* Add the property name for property-specific notifications. */
3144 if (notify_action == svn_wc_notify_resolved_prop)
3146 notify->prop_name = cd->property_name;
3147 SVN_ERR_ASSERT(strlen(notify->prop_name) > 0);
3150 cswb->notify_func(cswb->notify_baton, notify, iterpool);
3154 cswb->resolved_one = TRUE;
3156 svn_pool_destroy(iterpool);
3158 return SVN_NO_ERROR;
3162 svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
3163 const char *local_abspath,
3165 svn_boolean_t resolve_text,
3166 const char *resolve_prop,
3167 svn_boolean_t resolve_tree,
3168 svn_wc_conflict_choice_t conflict_choice,
3169 svn_wc_conflict_resolver_func2_t conflict_func,
3170 void *conflict_baton,
3171 svn_cancel_func_t cancel_func,
3173 svn_wc_notify_func2_t notify_func,
3175 apr_pool_t *scratch_pool)
3177 struct conflict_status_walker_baton cswb;
3178 apr_pool_t *iterpool = NULL;
3181 if (depth == svn_depth_unknown)
3182 depth = svn_depth_infinity;
3184 cswb.db = wc_ctx->db;
3185 cswb.resolve_text = resolve_text;
3186 cswb.resolve_prop = resolve_prop;
3187 cswb.resolve_tree = resolve_tree;
3188 cswb.conflict_choice = conflict_choice;
3190 cswb.conflict_func = conflict_func;
3191 cswb.conflict_baton = conflict_baton;
3193 cswb.cancel_func = cancel_func;
3194 cswb.cancel_baton = cancel_baton;
3196 cswb.notify_func = notify_func;
3197 cswb.notify_baton = notify_baton;
3199 cswb.resolved_one = FALSE;
3200 cswb.resolve_later = (depth != svn_depth_empty)
3201 ? apr_hash_make(scratch_pool)
3205 notify_func(notify_baton,
3206 svn_wc_create_notify(local_abspath,
3207 svn_wc_notify_conflict_resolver_starting,
3211 err = svn_wc_walk_status(wc_ctx,
3214 FALSE /* get_all */,
3215 FALSE /* no_ignore */,
3216 TRUE /* ignore_text_mods */,
3217 NULL /* ignore_patterns */,
3218 conflict_status_walker, &cswb,
3219 cancel_func, cancel_baton,
3222 /* If we got new tree conflicts (or delayed conflicts) during the initial
3223 walk, we now walk them one by one as closure. */
3224 while (!err && cswb.resolve_later && apr_hash_count(cswb.resolve_later))
3226 apr_hash_index_t *hi;
3227 svn_wc_status3_t *status = NULL;
3228 const char *tc_abspath = NULL;
3231 svn_pool_clear(iterpool);
3233 iterpool = svn_pool_create(scratch_pool);
3235 hi = apr_hash_first(scratch_pool, cswb.resolve_later);
3236 cswb.resolve_later = apr_hash_make(scratch_pool);
3237 cswb.resolved_one = FALSE;
3239 for (; hi && !err; hi = apr_hash_next(hi))
3241 const char *relpath;
3242 svn_pool_clear(iterpool);
3244 tc_abspath = apr_hash_this_key(hi);
3247 SVN_ERR(cancel_func(cancel_baton));
3249 relpath = svn_dirent_skip_ancestor(local_abspath,
3253 || (depth >= svn_depth_empty
3254 && depth < svn_depth_infinity
3255 && strchr(relpath, '/')))
3260 SVN_ERR(svn_wc_status3(&status, wc_ctx, tc_abspath,
3261 iterpool, iterpool));
3263 if (depth == svn_depth_files
3264 && status->kind == svn_node_dir)
3267 err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3268 status, scratch_pool));
3271 /* None of the remaining conflicts got resolved, and non did provide
3274 We can fix that if we disable the 'resolve_later' option...
3276 if (!cswb.resolved_one && !err && tc_abspath
3277 && apr_hash_count(cswb.resolve_later))
3279 /* Run the last resolve operation again. We still have status
3280 and tc_abspath for that one. */
3282 cswb.resolve_later = NULL; /* Produce proper error! */
3284 /* Recreate the error */
3285 err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3286 status, scratch_pool));
3288 SVN_ERR_ASSERT(err != NULL);
3290 err = svn_error_createf(
3291 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3292 _("Unable to resolve pending conflict on '%s'"),
3293 svn_dirent_local_style(tc_abspath, scratch_pool));
3299 svn_pool_destroy(iterpool);
3301 if (err && err->apr_err != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE)
3302 err = svn_error_createf(
3303 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3304 _("Unable to resolve conflicts on '%s'"),
3305 svn_dirent_local_style(local_abspath, scratch_pool));
3310 notify_func(notify_baton,
3311 svn_wc_create_notify(local_abspath,
3312 svn_wc_notify_conflict_resolver_done,
3316 return SVN_NO_ERROR;
3320 svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
3321 const char *local_abspath,
3323 svn_boolean_t resolve_text,
3324 const char *resolve_prop,
3325 svn_boolean_t resolve_tree,
3326 svn_wc_conflict_choice_t conflict_choice,
3327 svn_cancel_func_t cancel_func,
3329 svn_wc_notify_func2_t notify_func,
3331 apr_pool_t *scratch_pool)
3333 return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath,
3334 depth, resolve_text,
3335 resolve_prop, resolve_tree,
3338 cancel_func, cancel_baton,
3339 notify_func, notify_baton,
3343 /* Constructor for the result-structure returned by conflict callbacks. */
3344 svn_wc_conflict_result_t *
3345 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
3346 const char *merged_file,
3349 svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
3350 result->choice = choice;
3351 result->merged_file = apr_pstrdup(pool, merged_file);
3352 result->save_merged = FALSE;
3353 result->merged_value = NULL;
3355 /* If we add more fields to svn_wc_conflict_result_t, add them here. */
3361 svn_wc__conflict_text_mark_resolved(svn_wc_context_t *wc_ctx,
3362 const char *local_abspath,
3363 svn_wc_conflict_choice_t choice,
3364 svn_cancel_func_t cancel_func,
3366 svn_wc_notify_func2_t notify_func,
3368 apr_pool_t *scratch_pool)
3370 svn_skel_t *work_items;
3371 svn_skel_t *conflict;
3372 svn_boolean_t did_resolve;
3374 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
3375 wc_ctx->db, local_abspath,
3376 scratch_pool, scratch_pool));
3379 return SVN_NO_ERROR;
3381 SVN_ERR(build_text_conflict_resolve_items(&work_items, &did_resolve,
3382 wc_ctx->db, local_abspath,
3385 cancel_func, cancel_baton,
3386 scratch_pool, scratch_pool));
3388 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath,
3390 work_items, scratch_pool));
3392 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath,
3393 cancel_func, cancel_baton,
3396 if (did_resolve && notify_func)
3397 notify_func(notify_baton,
3398 svn_wc_create_notify(local_abspath,
3399 svn_wc_notify_resolved_text,
3403 return SVN_NO_ERROR;
3407 svn_wc__conflict_prop_mark_resolved(svn_wc_context_t *wc_ctx,
3408 const char *local_abspath,
3409 const char *propname,
3410 svn_wc_conflict_choice_t choice,
3411 const svn_string_t *merged_value,
3412 svn_wc_notify_func2_t notify_func,
3414 apr_pool_t *scratch_pool)
3416 svn_boolean_t did_resolve;
3417 svn_skel_t *conflicts;
3419 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
3420 wc_ctx->db, local_abspath,
3421 scratch_pool, scratch_pool));
3424 return SVN_NO_ERROR;
3426 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve, wc_ctx->db,
3427 local_abspath, conflicts,
3428 propname, choice, NULL, merged_value,
3429 NULL, NULL, scratch_pool));
3431 if (did_resolve && notify_func)
3433 svn_wc_notify_t *notify;
3435 /* Send a general notification if no specific property was requested. */
3436 if (propname == NULL || propname[0] == '\0')
3438 notify = svn_wc_create_notify(local_abspath,
3439 svn_wc_notify_resolved,
3444 notify = svn_wc_create_notify(local_abspath,
3445 svn_wc_notify_resolved_prop,
3447 notify->prop_name = propname;
3450 notify_func(notify_baton, notify, scratch_pool);
3452 return SVN_NO_ERROR;
3456 svn_wc__conflict_tree_update_break_moved_away(svn_wc_context_t *wc_ctx,
3457 const char *local_abspath,
3458 svn_cancel_func_t cancel_func,
3460 svn_wc_notify_func2_t notify_func,
3462 apr_pool_t *scratch_pool)
3464 svn_wc_conflict_reason_t reason;
3465 svn_wc_conflict_action_t action;
3466 svn_wc_operation_t operation;
3467 svn_boolean_t tree_conflicted;
3468 const char *src_op_root_abspath;
3469 const apr_array_header_t *conflicts;
3470 svn_skel_t *conflict_skel;
3472 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
3473 wc_ctx->db, local_abspath,
3474 FALSE, /* no tempfiles */
3475 FALSE, /* only tree conflicts */
3476 scratch_pool, scratch_pool));
3478 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
3479 &tree_conflicted, wc_ctx->db,
3480 local_abspath, conflict_skel,
3481 scratch_pool, scratch_pool));
3482 if (!tree_conflicted)
3483 return SVN_NO_ERROR;
3485 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
3486 &src_op_root_abspath,
3487 wc_ctx->db, local_abspath,
3489 scratch_pool, scratch_pool));
3491 /* Make sure the expected conflict is recorded. */
3492 if (operation != svn_wc_operation_update &&
3493 operation != svn_wc_operation_switch)
3494 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3495 _("Unexpected conflict operation '%s' on '%s'"),
3496 svn_token__to_word(operation_map, operation),
3497 svn_dirent_local_style(local_abspath,
3499 if (reason != svn_wc_conflict_reason_deleted &&
3500 reason != svn_wc_conflict_reason_replaced &&
3501 reason != svn_wc_conflict_reason_moved_away)
3502 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3503 _("Unexpected conflict reason '%s' on '%s'"),
3504 svn_token__to_word(reason_map, reason),
3505 svn_dirent_local_style(local_abspath,
3508 /* Break moves for any children moved out of this directory,
3509 * and leave this directory deleted. */
3510 if (action != svn_wc_conflict_action_delete)
3512 SVN_ERR(svn_wc__db_op_break_moved_away(
3513 wc_ctx->db, local_abspath, src_op_root_abspath, TRUE,
3514 notify_func, notify_baton, scratch_pool));
3515 /* Conflict was marked resolved by db_op_break_moved_away() call .*/
3518 notify_func(notify_baton,
3519 svn_wc_create_notify(local_abspath,
3520 svn_wc_notify_resolved_tree,
3523 return SVN_NO_ERROR;
3525 /* else # The move is/moves are already broken */
3527 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath,
3529 NULL, scratch_pool));
3530 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
3534 notify_func(notify_baton,
3535 svn_wc_create_notify(local_abspath,
3536 svn_wc_notify_resolved_tree,
3540 return SVN_NO_ERROR;
3544 svn_wc__conflict_tree_update_raise_moved_away(svn_wc_context_t *wc_ctx,
3545 const char *local_abspath,
3546 svn_cancel_func_t cancel_func,
3548 svn_wc_notify_func2_t notify_func,
3550 apr_pool_t *scratch_pool)
3552 svn_wc_conflict_reason_t reason;
3553 svn_wc_conflict_action_t action;
3554 svn_wc_operation_t operation;
3555 svn_boolean_t tree_conflicted;
3556 const apr_array_header_t *conflicts;
3557 svn_skel_t *conflict_skel;
3559 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
3560 wc_ctx->db, local_abspath,
3561 FALSE, /* no tempfiles */
3562 FALSE, /* only tree conflicts */
3563 scratch_pool, scratch_pool));
3565 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
3566 &tree_conflicted, wc_ctx->db,
3567 local_abspath, conflict_skel,
3568 scratch_pool, scratch_pool));
3569 if (!tree_conflicted)
3570 return SVN_NO_ERROR;
3572 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL,
3573 wc_ctx->db, local_abspath,
3575 scratch_pool, scratch_pool));
3577 /* Make sure the expected conflict is recorded. */
3578 if (operation != svn_wc_operation_update &&
3579 operation != svn_wc_operation_switch)
3580 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3581 _("Unexpected conflict operation '%s' on '%s'"),
3582 svn_token__to_word(operation_map, operation),
3583 svn_dirent_local_style(local_abspath,
3585 if (reason != svn_wc_conflict_reason_deleted &&
3586 reason != svn_wc_conflict_reason_replaced)
3587 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3588 _("Unexpected conflict reason '%s' on '%s'"),
3589 svn_token__to_word(reason_map, reason),
3590 svn_dirent_local_style(local_abspath,
3592 if (action != svn_wc_conflict_action_edit)
3593 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3594 _("Unexpected conflict action '%s' on '%s'"),
3595 svn_token__to_word(action_map, action),
3596 svn_dirent_local_style(local_abspath,
3599 /* Raise local moved-away vs. incoming edit conflicts on any children
3600 * moved out of this directory, and leave this directory as-is.
3601 * The user may choose to update newly conflicted moved-away children
3602 * when resolving them. If this function raises an error, the conflict
3603 * cannot be resolved yet because other conflicts or obstructions
3604 * prevent us from propagating the conflict to moved-away children. */
3605 SVN_ERR(svn_wc__db_op_raise_moved_away(wc_ctx->db, local_abspath,
3606 notify_func, notify_baton,
3609 /* The conflict was marked resolved by svn_wc__db_op_raise_moved_away(). */
3611 notify_func(notify_baton,
3612 svn_wc_create_notify(local_abspath,
3613 svn_wc_notify_resolved_tree,
3617 return SVN_NO_ERROR;
3621 svn_wc__conflict_tree_update_moved_away_node(svn_wc_context_t *wc_ctx,
3622 const char *local_abspath,
3623 svn_cancel_func_t cancel_func,
3625 svn_wc_notify_func2_t notify_func,
3627 apr_pool_t *scratch_pool)
3629 svn_wc_conflict_reason_t reason;
3630 svn_wc_conflict_action_t action;
3631 svn_wc_operation_t operation;
3632 svn_boolean_t tree_conflicted;
3633 const char *src_op_root_abspath;
3634 const apr_array_header_t *conflicts;
3635 svn_skel_t *conflict_skel;
3637 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
3638 wc_ctx->db, local_abspath,
3639 FALSE, /* no tempfiles */
3640 FALSE, /* only tree conflicts */
3641 scratch_pool, scratch_pool));
3643 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
3644 &tree_conflicted, wc_ctx->db,
3645 local_abspath, conflict_skel,
3646 scratch_pool, scratch_pool));
3647 if (!tree_conflicted)
3648 return SVN_NO_ERROR;
3650 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
3651 &src_op_root_abspath,
3652 wc_ctx->db, local_abspath,
3654 scratch_pool, scratch_pool));
3656 /* Make sure the expected conflict is recorded. */
3657 if (operation != svn_wc_operation_update &&
3658 operation != svn_wc_operation_switch)
3659 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3660 _("Unexpected conflict operation '%s' on '%s'"),
3661 svn_token__to_word(operation_map, operation),
3662 svn_dirent_local_style(local_abspath,
3664 if (reason != svn_wc_conflict_reason_moved_away)
3665 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3666 _("Unexpected conflict reason '%s' on '%s'"),
3667 svn_token__to_word(reason_map, reason),
3668 svn_dirent_local_style(local_abspath,
3670 if (action != svn_wc_conflict_action_edit)
3671 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3672 _("Unexpected conflict action '%s' on '%s'"),
3673 svn_token__to_word(action_map, action),
3674 svn_dirent_local_style(local_abspath,
3677 /* Update the moved-away conflict victim. */
3678 SVN_ERR(svn_wc__db_update_moved_away_conflict_victim(wc_ctx->db,
3680 src_op_root_abspath,
3690 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath,
3692 NULL, scratch_pool));
3693 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
3697 notify_func(notify_baton,
3698 svn_wc_create_notify(local_abspath,
3699 svn_wc_notify_resolved_tree,
3703 return SVN_NO_ERROR;
3707 svn_wc__conflict_tree_update_incoming_move(svn_wc_context_t *wc_ctx,
3708 const char *local_abspath,
3709 const char *dest_abspath,
3710 svn_cancel_func_t cancel_func,
3712 svn_wc_notify_func2_t notify_func,
3714 apr_pool_t *scratch_pool)
3716 svn_wc_conflict_reason_t local_change;
3717 svn_wc_conflict_action_t incoming_change;
3718 svn_wc_operation_t operation;
3719 svn_boolean_t tree_conflicted;
3720 const apr_array_header_t *conflicts;
3721 svn_skel_t *conflict_skel;
3723 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
3724 wc_ctx->db, local_abspath,
3725 FALSE, /* no tempfiles */
3726 FALSE, /* only tree conflicts */
3727 scratch_pool, scratch_pool));
3729 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
3730 &tree_conflicted, wc_ctx->db,
3731 local_abspath, conflict_skel,
3732 scratch_pool, scratch_pool));
3733 if (!tree_conflicted)
3734 return SVN_NO_ERROR;
3736 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, &incoming_change,
3737 NULL, wc_ctx->db, local_abspath,
3739 scratch_pool, scratch_pool));
3741 /* Make sure the expected conflict is recorded. */
3742 if (operation != svn_wc_operation_update &&
3743 operation != svn_wc_operation_switch &&
3744 operation != svn_wc_operation_merge)
3745 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3746 _("Unexpected conflict operation '%s' on '%s'"),
3747 svn_token__to_word(operation_map, operation),
3748 svn_dirent_local_style(local_abspath,
3750 if (local_change != svn_wc_conflict_reason_edited)
3751 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3752 _("Unexpected conflict reason '%s' on '%s'"),
3753 svn_token__to_word(reason_map, local_change),
3754 svn_dirent_local_style(local_abspath,
3756 if (incoming_change != svn_wc_conflict_action_delete)
3757 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3758 _("Unexpected conflict action '%s' on '%s'"),
3759 svn_token__to_word(action_map, incoming_change),
3760 svn_dirent_local_style(local_abspath,
3763 SVN_ERR(svn_wc__db_update_incoming_move(wc_ctx->db, local_abspath,
3764 dest_abspath, operation,
3765 incoming_change, local_change,
3766 cancel_func, cancel_baton,
3767 notify_func, notify_baton,
3770 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
3773 return SVN_NO_ERROR;
3777 svn_wc__conflict_tree_update_local_add(svn_wc_context_t *wc_ctx,
3778 const char *local_abspath,
3779 svn_cancel_func_t cancel_func,
3781 svn_wc_notify_func2_t notify_func,
3783 apr_pool_t *scratch_pool)
3785 svn_wc_conflict_reason_t local_change;
3786 svn_wc_conflict_action_t incoming_change;
3787 svn_wc_operation_t operation;
3788 svn_boolean_t tree_conflicted;
3789 const apr_array_header_t *conflicts;
3790 svn_skel_t *conflict_skel;
3792 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict_skel,
3793 wc_ctx->db, local_abspath,
3794 FALSE, /* no tempfiles */
3795 FALSE, /* only tree conflicts */
3796 scratch_pool, scratch_pool));
3798 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
3799 &tree_conflicted, wc_ctx->db,
3800 local_abspath, conflict_skel,
3801 scratch_pool, scratch_pool));
3802 if (!tree_conflicted)
3803 return SVN_NO_ERROR;
3805 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change, &incoming_change,
3806 NULL, wc_ctx->db, local_abspath,
3808 scratch_pool, scratch_pool));
3810 /* Make sure the expected conflict is recorded. */
3811 if (operation != svn_wc_operation_update)
3812 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3813 _("Unexpected conflict operation '%s' on '%s'"),
3814 svn_token__to_word(operation_map, operation),
3815 svn_dirent_local_style(local_abspath,
3817 if (local_change != svn_wc_conflict_reason_added)
3818 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3819 _("Unexpected conflict reason '%s' on '%s'"),
3820 svn_token__to_word(reason_map, local_change),
3821 svn_dirent_local_style(local_abspath,
3823 if (incoming_change != svn_wc_conflict_action_add)
3824 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3825 _("Unexpected conflict action '%s' on '%s'"),
3826 svn_token__to_word(action_map, incoming_change),
3827 svn_dirent_local_style(local_abspath,
3830 SVN_ERR(svn_wc__db_update_local_add(wc_ctx->db, local_abspath,
3831 cancel_func, cancel_baton,
3832 notify_func, notify_baton,
3835 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
3838 return SVN_NO_ERROR;
3842 svn_wc__guess_incoming_move_target_nodes(apr_array_header_t **possible_targets,
3843 svn_wc_context_t *wc_ctx,
3844 const char *victim_abspath,
3845 svn_node_kind_t victim_node_kind,
3846 const char *moved_to_repos_relpath,
3847 apr_pool_t *result_pool,
3848 apr_pool_t *scratch_pool)
3850 apr_array_header_t *candidates;
3851 apr_pool_t *iterpool;
3853 apr_size_t longest_ancestor_len = 0;
3855 *possible_targets = apr_array_make(result_pool, 1, sizeof(const char *));
3856 SVN_ERR(svn_wc__find_repos_node_in_wc(&candidates, wc_ctx->db, victim_abspath,
3857 moved_to_repos_relpath,
3858 scratch_pool, scratch_pool));
3860 /* Find a "useful move target" node in our set of candidates.
3861 * Since there is no way to be certain, filter out nodes which seem
3862 * unlikely candidates, and return the first node which is "good enough".
3863 * Nodes which are tree conflict victims don't count, and nodes which
3864 * cannot be modified (e.g. replaced or deleted nodes) don't count.
3865 * Nodes which are of a different node kind don't count either.
3866 * Ignore switched nodes as well, since that is an unlikely case during
3867 * update/swtich/merge conflict resolution. And externals shouldn't even
3868 * be on our candidate list in the first place.
3869 * If multiple candidates match these criteria, choose the one which
3870 * shares the longest common ancestor with the victim. */
3871 iterpool = svn_pool_create(scratch_pool);
3872 for (i = 0; i < candidates->nelts; i++)
3874 const char *local_abspath;
3875 const char *ancestor_abspath;
3876 apr_size_t ancestor_len;
3877 svn_boolean_t tree_conflicted;
3878 svn_wc__db_status_t status;
3879 svn_boolean_t is_wcroot;
3880 svn_boolean_t is_switched;
3881 svn_node_kind_t node_kind;
3882 const char *moved_to_abspath;
3885 svn_pool_clear(iterpool);
3887 local_abspath = APR_ARRAY_IDX(candidates, i, const char *);
3889 SVN_ERR(svn_wc__internal_conflicted_p(NULL, NULL, &tree_conflicted,
3890 wc_ctx->db, local_abspath,
3892 if (tree_conflicted)
3895 SVN_ERR(svn_wc__db_read_info(&status, &node_kind,
3896 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3897 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3898 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3899 NULL, NULL, NULL, NULL,
3900 wc_ctx->db, local_abspath, iterpool,
3902 if (status != svn_wc__db_status_normal &&
3903 status != svn_wc__db_status_added)
3906 if (node_kind != victim_node_kind)
3909 SVN_ERR(svn_wc__db_is_switched(&is_wcroot, &is_switched, NULL,
3910 wc_ctx->db, local_abspath, iterpool));
3911 if (is_wcroot || is_switched)
3914 /* This might be a move target. Fingers crossed ;-) */
3915 moved_to_abspath = apr_pstrdup(result_pool, local_abspath);
3917 /* Insert the move target into the list. Targets which are closer
3918 * (path-wise) to the conflict victim are more likely to be a good
3919 * match, so put them at the front of the list. */
3920 ancestor_abspath = svn_dirent_get_longest_ancestor(local_abspath,
3923 ancestor_len = strlen(ancestor_abspath);
3924 if (ancestor_len >= longest_ancestor_len)
3926 longest_ancestor_len = ancestor_len;
3927 insert_index = 0; /* prepend */
3931 insert_index = (*possible_targets)->nelts; /* append */
3933 svn_sort__array_insert(*possible_targets, &moved_to_abspath,
3937 svn_pool_destroy(iterpool);
3939 return SVN_NO_ERROR;