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_string_private.h"
55 #include "svn_private_config.h"
57 /* --------------------------------------------------------------------
58 * Conflict skel management
62 svn_wc__conflict_skel_create(apr_pool_t *result_pool)
64 svn_skel_t *conflict_skel = svn_skel__make_empty_list(result_pool);
66 /* Add empty CONFLICTS list */
67 svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel);
69 /* Add empty WHY list */
70 svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel);
76 svn_wc__conflict_skel_is_complete(svn_boolean_t *complete,
77 const svn_skel_t *conflict_skel)
81 if (svn_skel__list_length(conflict_skel) < 2)
82 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
83 _("Not a conflict skel"));
85 if (svn_skel__list_length(conflict_skel->children) < 2)
86 return SVN_NO_ERROR; /* WHY is not set */
88 if (svn_skel__list_length(conflict_skel->children->next) == 0)
89 return SVN_NO_ERROR; /* No conflict set */
95 /* Serialize a svn_wc_conflict_version_t before the existing data in skel */
97 conflict__prepend_location(svn_skel_t *skel,
98 const svn_wc_conflict_version_t *location,
99 svn_boolean_t allow_NULL,
100 apr_pool_t *result_pool,
101 apr_pool_t *scratch_pool)
104 SVN_ERR_ASSERT(location || allow_NULL);
108 svn_skel__prepend(svn_skel__make_empty_list(result_pool), skel);
112 /* ("subversion" repos_root_url repos_uuid repos_relpath rev kind) */
113 loc = svn_skel__make_empty_list(result_pool);
115 svn_skel__prepend_str(svn_node_kind_to_word(location->node_kind),
118 svn_skel__prepend_int(location->peg_rev, loc, result_pool);
120 svn_skel__prepend_str(apr_pstrdup(result_pool, location->path_in_repos), loc,
123 if (!location->repos_uuid) /* Can theoretically be NULL */
124 svn_skel__prepend(svn_skel__make_empty_list(result_pool), loc);
126 svn_skel__prepend_str(location->repos_uuid, loc, result_pool);
128 svn_skel__prepend_str(apr_pstrdup(result_pool, location->repos_url), loc,
131 svn_skel__prepend_str(SVN_WC__CONFLICT_SRC_SUBVERSION, loc, result_pool);
133 svn_skel__prepend(loc, skel);
137 /* Deserialize a svn_wc_conflict_version_t from the skel.
138 Set *LOCATION to NULL when the data is not a svn_wc_conflict_version_t. */
140 conflict__read_location(svn_wc_conflict_version_t **location,
141 const svn_skel_t *skel,
142 apr_pool_t *result_pool,
143 apr_pool_t *scratch_pool)
145 const char *repos_root_url;
146 const char *repos_uuid;
147 const char *repos_relpath;
148 svn_revnum_t revision;
150 svn_node_kind_t node_kind; /* note that 'none' is a legitimate value */
151 const char *kind_str;
153 const svn_skel_t *c = skel->children;
155 if (!svn_skel__matches_atom(c, SVN_WC__CONFLICT_SRC_SUBVERSION))
162 repos_root_url = apr_pstrmemdup(result_pool, c->data, c->len);
166 repos_uuid = apr_pstrmemdup(result_pool, c->data, c->len);
171 repos_relpath = apr_pstrmemdup(result_pool, c->data, c->len);
174 SVN_ERR(svn_skel__parse_int(&v, c, scratch_pool));
175 revision = (svn_revnum_t)v;
178 kind_str = apr_pstrmemdup(scratch_pool, c->data, c->len);
179 node_kind = svn_node_kind_from_word(kind_str);
181 *location = svn_wc_conflict_version_create2(repos_root_url,
190 /* Get the operation part of CONFLICT_SKELL or NULL if no operation is set
193 conflict__get_operation(svn_skel_t **why,
194 const svn_skel_t *conflict_skel)
196 SVN_ERR_ASSERT(conflict_skel
197 && conflict_skel->children
198 && conflict_skel->children->next
199 && !conflict_skel->children->next->is_atom);
201 *why = conflict_skel->children;
203 if (!(*why)->children)
204 *why = NULL; /* Operation is not set yet */
211 svn_wc__conflict_skel_set_op_update(svn_skel_t *conflict_skel,
212 const svn_wc_conflict_version_t *original,
213 const svn_wc_conflict_version_t *target,
214 apr_pool_t *result_pool,
215 apr_pool_t *scratch_pool)
220 SVN_ERR_ASSERT(conflict_skel
221 && conflict_skel->children
222 && conflict_skel->children->next
223 && !conflict_skel->children->next->is_atom);
225 SVN_ERR(conflict__get_operation(&why, conflict_skel));
227 SVN_ERR_ASSERT(why == NULL); /* No operation set */
229 why = conflict_skel->children;
231 origins = svn_skel__make_empty_list(result_pool);
233 SVN_ERR(conflict__prepend_location(origins, target, TRUE,
234 result_pool, scratch_pool));
235 SVN_ERR(conflict__prepend_location(origins, original, TRUE,
236 result_pool, scratch_pool));
238 svn_skel__prepend(origins, why);
239 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_UPDATE, why, result_pool);
245 svn_wc__conflict_skel_set_op_switch(svn_skel_t *conflict_skel,
246 const svn_wc_conflict_version_t *original,
247 const svn_wc_conflict_version_t *target,
248 apr_pool_t *result_pool,
249 apr_pool_t *scratch_pool)
254 SVN_ERR_ASSERT(conflict_skel
255 && conflict_skel->children
256 && conflict_skel->children->next
257 && !conflict_skel->children->next->is_atom);
259 SVN_ERR(conflict__get_operation(&why, conflict_skel));
261 SVN_ERR_ASSERT(why == NULL); /* No operation set */
263 why = conflict_skel->children;
265 origins = svn_skel__make_empty_list(result_pool);
267 SVN_ERR(conflict__prepend_location(origins, target, TRUE,
268 result_pool, scratch_pool));
269 SVN_ERR(conflict__prepend_location(origins, original, TRUE,
270 result_pool, scratch_pool));
272 svn_skel__prepend(origins, why);
273 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_SWITCH, why, result_pool);
279 svn_wc__conflict_skel_set_op_merge(svn_skel_t *conflict_skel,
280 const svn_wc_conflict_version_t *left,
281 const svn_wc_conflict_version_t *right,
282 apr_pool_t *result_pool,
283 apr_pool_t *scratch_pool)
288 SVN_ERR_ASSERT(conflict_skel
289 && conflict_skel->children
290 && conflict_skel->children->next
291 && !conflict_skel->children->next->is_atom);
293 SVN_ERR(conflict__get_operation(&why, conflict_skel));
295 SVN_ERR_ASSERT(why == NULL); /* No operation set */
297 why = conflict_skel->children;
299 origins = svn_skel__make_empty_list(result_pool);
301 SVN_ERR(conflict__prepend_location(origins, right, TRUE,
302 result_pool, scratch_pool));
304 SVN_ERR(conflict__prepend_location(origins, left, TRUE,
305 result_pool, scratch_pool));
307 svn_skel__prepend(origins, why);
308 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_MERGE, why, result_pool);
313 /* Gets the conflict data of the specified type CONFLICT_TYPE from
314 CONFLICT_SKEL, or NULL if no such conflict is recorded */
316 conflict__get_conflict(svn_skel_t **conflict,
317 const svn_skel_t *conflict_skel,
318 const char *conflict_type)
322 SVN_ERR_ASSERT(conflict_skel
323 && conflict_skel->children
324 && conflict_skel->children->next
325 && !conflict_skel->children->next->is_atom);
327 for(c = conflict_skel->children->next->children;
331 if (svn_skel__matches_atom(c->children, conflict_type))
344 svn_wc__conflict_skel_add_text_conflict(svn_skel_t *conflict_skel,
346 const char *wri_abspath,
347 const char *mine_abspath,
348 const char *their_old_abspath,
349 const char *their_abspath,
350 apr_pool_t *result_pool,
351 apr_pool_t *scratch_pool)
353 svn_skel_t *text_conflict;
356 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
357 SVN_WC__CONFLICT_KIND_TEXT));
359 SVN_ERR_ASSERT(!text_conflict); /* ### Use proper error? */
361 /* Current skel format
363 (OLD MINE OLD-THEIRS THEIRS)) */
365 text_conflict = svn_skel__make_empty_list(result_pool);
366 markers = svn_skel__make_empty_list(result_pool);
370 const char *their_relpath;
372 SVN_ERR(svn_wc__db_to_relpath(&their_relpath,
373 db, wri_abspath, their_abspath,
374 result_pool, scratch_pool));
375 svn_skel__prepend_str(their_relpath, markers, result_pool);
378 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
382 const char *mine_relpath;
384 SVN_ERR(svn_wc__db_to_relpath(&mine_relpath,
385 db, wri_abspath, mine_abspath,
386 result_pool, scratch_pool));
387 svn_skel__prepend_str(mine_relpath, markers, result_pool);
390 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
392 if (their_old_abspath)
394 const char *original_relpath;
396 SVN_ERR(svn_wc__db_to_relpath(&original_relpath,
397 db, wri_abspath, their_old_abspath,
398 result_pool, scratch_pool));
399 svn_skel__prepend_str(original_relpath, markers, result_pool);
402 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
404 svn_skel__prepend(markers, text_conflict);
405 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TEXT, text_conflict,
408 /* And add it to the conflict skel */
409 svn_skel__prepend(text_conflict, conflict_skel->children->next);
415 svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel,
417 const char *wri_abspath,
418 const char *marker_abspath,
419 const apr_hash_t *mine_props,
420 const apr_hash_t *their_old_props,
421 const apr_hash_t *their_props,
422 const apr_hash_t *conflicted_prop_names,
423 apr_pool_t *result_pool,
424 apr_pool_t *scratch_pool)
426 svn_skel_t *prop_conflict;
428 svn_skel_t *conflict_names;
430 apr_hash_index_t *hi;
432 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
433 SVN_WC__CONFLICT_KIND_PROP));
435 SVN_ERR_ASSERT(!prop_conflict); /* ### Use proper error? */
437 /* This function currently implements:
440 prop-conflicted_prop_names
444 NULL lists are recorded as "" */
445 /* ### Seems that this may not match what we read out. Read-out of
446 * 'theirs-old' comes as NULL. */
448 prop_conflict = svn_skel__make_empty_list(result_pool);
452 SVN_ERR(svn_skel__unparse_proplist(&props, their_props, result_pool));
453 svn_skel__prepend(props, prop_conflict);
456 svn_skel__prepend_str("", prop_conflict, result_pool); /* No their_props */
460 SVN_ERR(svn_skel__unparse_proplist(&props, mine_props, result_pool));
461 svn_skel__prepend(props, prop_conflict);
464 svn_skel__prepend_str("", prop_conflict, result_pool); /* No mine_props */
468 SVN_ERR(svn_skel__unparse_proplist(&props, their_old_props,
470 svn_skel__prepend(props, prop_conflict);
473 svn_skel__prepend_str("", prop_conflict, result_pool); /* No old_props */
475 conflict_names = svn_skel__make_empty_list(result_pool);
476 for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)conflicted_prop_names);
478 hi = apr_hash_next(hi))
480 svn_skel__prepend_str(apr_pstrdup(result_pool, apr_hash_this_key(hi)),
484 svn_skel__prepend(conflict_names, prop_conflict);
486 markers = svn_skel__make_empty_list(result_pool);
490 const char *marker_relpath;
491 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, wri_abspath,
493 result_pool, scratch_pool));
495 svn_skel__prepend_str(marker_relpath, markers, result_pool);
497 /*else // ### set via svn_wc__conflict_create_markers
498 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);*/
500 svn_skel__prepend(markers, prop_conflict);
502 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_conflict, result_pool);
504 /* And add it to the conflict skel */
505 svn_skel__prepend(prop_conflict, conflict_skel->children->next);
510 /* A map for svn_wc_conflict_reason_t values. */
511 static const svn_token_map_t reason_map[] =
513 { "edited", svn_wc_conflict_reason_edited },
514 { "obstructed", svn_wc_conflict_reason_obstructed },
515 { "deleted", svn_wc_conflict_reason_deleted },
516 { "missing", svn_wc_conflict_reason_missing },
517 { "unversioned", svn_wc_conflict_reason_unversioned },
518 { "added", svn_wc_conflict_reason_added },
519 { "replaced", svn_wc_conflict_reason_replaced },
520 { "moved-away", svn_wc_conflict_reason_moved_away },
521 { "moved-here", svn_wc_conflict_reason_moved_here },
525 static const svn_token_map_t action_map[] =
527 { "edited", svn_wc_conflict_action_edit },
528 { "added", svn_wc_conflict_action_add },
529 { "deleted", svn_wc_conflict_action_delete },
530 { "replaced", svn_wc_conflict_action_replace },
535 svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel,
537 const char *wri_abspath,
538 svn_wc_conflict_reason_t reason,
539 svn_wc_conflict_action_t action,
540 const char *move_src_op_root_abspath,
541 apr_pool_t *result_pool,
542 apr_pool_t *scratch_pool)
544 svn_skel_t *tree_conflict;
547 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
548 SVN_WC__CONFLICT_KIND_TREE));
550 SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */
552 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_moved_away
553 || !move_src_op_root_abspath); /* ### Use proper error? */
555 tree_conflict = svn_skel__make_empty_list(result_pool);
557 if (reason == svn_wc_conflict_reason_moved_away
558 && move_src_op_root_abspath)
560 const char *move_src_op_root_relpath;
562 SVN_ERR(svn_wc__db_to_relpath(&move_src_op_root_relpath,
564 move_src_op_root_abspath,
565 result_pool, scratch_pool));
567 svn_skel__prepend_str(move_src_op_root_relpath, tree_conflict,
571 svn_skel__prepend_str(svn_token__to_word(action_map, action),
572 tree_conflict, result_pool);
574 svn_skel__prepend_str(svn_token__to_word(reason_map, reason),
575 tree_conflict, result_pool);
577 /* Tree conflicts have no marker files */
578 markers = svn_skel__make_empty_list(result_pool);
579 svn_skel__prepend(markers, tree_conflict);
581 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TREE, tree_conflict,
584 /* And add it to the conflict skel */
585 svn_skel__prepend(tree_conflict, conflict_skel->children->next);
591 svn_wc__conflict_skel_resolve(svn_boolean_t *completely_resolved,
592 svn_skel_t *conflict_skel,
594 const char *wri_abspath,
595 svn_boolean_t resolve_text,
596 const char *resolve_prop,
597 svn_boolean_t resolve_tree,
598 apr_pool_t *result_pool,
599 apr_pool_t *scratch_pool)
602 svn_skel_t **pconflict;
603 SVN_ERR(conflict__get_operation(&op, conflict_skel));
606 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
607 _("Not a completed conflict skel"));
609 /* We are going to drop items from a linked list. Instead of keeping
610 a pointer to the item we want to drop we store a pointer to the
611 pointer of what we may drop, to allow setting it to the next item. */
613 pconflict = &(conflict_skel->children->next->children);
616 svn_skel_t *c = (*pconflict)->children;
619 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TEXT))
621 /* Remove the text conflict from the linked list */
622 *pconflict = (*pconflict)->next;
625 else if (resolve_prop
626 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_PROP))
628 svn_skel_t **ppropnames = &(c->next->next->children);
630 if (resolve_prop[0] == '\0')
631 *ppropnames = NULL; /* remove all conflicted property names */
635 if (svn_skel__matches_atom(*ppropnames, resolve_prop))
637 *ppropnames = (*ppropnames)->next;
640 ppropnames = &((*ppropnames)->next);
643 /* If no conflicted property names left */
644 if (!c->next->next->children)
646 /* Remove the propery conflict skel from the linked list */
647 *pconflict = (*pconflict)->next;
651 else if (resolve_tree
652 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TREE))
654 /* Remove the tree conflict from the linked list */
655 *pconflict = (*pconflict)->next;
659 pconflict = &((*pconflict)->next);
662 if (completely_resolved)
664 /* Nice, we can just call the complete function */
665 svn_boolean_t complete_conflict;
666 SVN_ERR(svn_wc__conflict_skel_is_complete(&complete_conflict,
669 *completely_resolved = !complete_conflict;
675 /* A map for svn_wc_operation_t values. */
676 static const svn_token_map_t operation_map[] =
678 { "", svn_wc_operation_none },
679 { SVN_WC__CONFLICT_OP_UPDATE, svn_wc_operation_update },
680 { SVN_WC__CONFLICT_OP_SWITCH, svn_wc_operation_switch },
681 { SVN_WC__CONFLICT_OP_MERGE, svn_wc_operation_merge },
686 svn_wc__conflict_read_info(svn_wc_operation_t *operation,
687 const apr_array_header_t **locations,
688 svn_boolean_t *text_conflicted,
689 svn_boolean_t *prop_conflicted,
690 svn_boolean_t *tree_conflicted,
692 const char *wri_abspath,
693 const svn_skel_t *conflict_skel,
694 apr_pool_t *result_pool,
695 apr_pool_t *scratch_pool)
700 SVN_ERR(conflict__get_operation(&op, conflict_skel));
703 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
704 _("Not a completed conflict skel"));
709 int value = svn_token__from_mem(operation_map, c->data, c->len);
711 if (value != SVN_TOKEN_UNKNOWN)
714 *operation = svn_wc_operation_none;
718 if (locations && c->children)
720 const svn_skel_t *loc_skel;
721 svn_wc_conflict_version_t *loc;
722 apr_array_header_t *locs = apr_array_make(result_pool, 2, sizeof(loc));
724 for (loc_skel = c->children; loc_skel; loc_skel = loc_skel->next)
726 SVN_ERR(conflict__read_location(&loc, loc_skel, result_pool,
729 APR_ARRAY_PUSH(locs, svn_wc_conflict_version_t *) = loc;
740 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
741 SVN_WC__CONFLICT_KIND_TEXT));
743 *text_conflicted = (c_skel != NULL);
749 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
750 SVN_WC__CONFLICT_KIND_PROP));
752 *prop_conflicted = (c_skel != NULL);
758 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
759 SVN_WC__CONFLICT_KIND_TREE));
761 *tree_conflicted = (c_skel != NULL);
769 svn_wc__conflict_read_text_conflict(const char **mine_abspath,
770 const char **their_old_abspath,
771 const char **their_abspath,
773 const char *wri_abspath,
774 const svn_skel_t *conflict_skel,
775 apr_pool_t *result_pool,
776 apr_pool_t *scratch_pool)
778 svn_skel_t *text_conflict;
781 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
782 SVN_WC__CONFLICT_KIND_TEXT));
785 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
787 m = text_conflict->children->next->children;
789 if (their_old_abspath)
793 const char *original_relpath;
795 original_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
796 SVN_ERR(svn_wc__db_from_relpath(their_old_abspath,
797 db, wri_abspath, original_relpath,
798 result_pool, scratch_pool));
801 *their_old_abspath = NULL;
809 const char *mine_relpath;
811 mine_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
812 SVN_ERR(svn_wc__db_from_relpath(mine_abspath,
813 db, wri_abspath, mine_relpath,
814 result_pool, scratch_pool));
817 *mine_abspath = NULL;
825 const char *their_relpath;
827 their_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
828 SVN_ERR(svn_wc__db_from_relpath(their_abspath,
829 db, wri_abspath, their_relpath,
830 result_pool, scratch_pool));
833 *their_abspath = NULL;
840 svn_wc__conflict_read_prop_conflict(const char **marker_abspath,
841 apr_hash_t **mine_props,
842 apr_hash_t **their_old_props,
843 apr_hash_t **their_props,
844 apr_hash_t **conflicted_prop_names,
846 const char *wri_abspath,
847 const svn_skel_t *conflict_skel,
848 apr_pool_t *result_pool,
849 apr_pool_t *scratch_pool)
851 svn_skel_t *prop_conflict;
854 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
855 SVN_WC__CONFLICT_KIND_PROP));
858 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
860 c = prop_conflict->children;
862 c = c->next; /* Skip "prop" */
864 /* Get marker file */
867 const char *marker_relpath;
869 if (c->children && c->children->is_atom)
871 marker_relpath = apr_pstrmemdup(result_pool, c->children->data,
874 SVN_ERR(svn_wc__db_from_relpath(marker_abspath, db, wri_abspath,
876 result_pool, scratch_pool));
879 *marker_abspath = NULL;
883 /* Get conflicted properties */
884 if (conflicted_prop_names)
886 const svn_skel_t *name;
887 *conflicted_prop_names = apr_hash_make(result_pool);
889 for (name = c->children; name; name = name->next)
891 svn_hash_sets(*conflicted_prop_names,
892 apr_pstrmemdup(result_pool, name->data, name->len),
898 /* Get original properties */
902 *their_old_props = apr_hash_make(result_pool);
904 SVN_ERR(svn_skel__parse_proplist(their_old_props, c, result_pool));
908 /* Get mine properties */
912 *mine_props = apr_hash_make(result_pool);
914 SVN_ERR(svn_skel__parse_proplist(mine_props, c, result_pool));
918 /* Get their properties */
922 *their_props = apr_hash_make(result_pool);
924 SVN_ERR(svn_skel__parse_proplist(their_props, c, result_pool));
931 svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *reason,
932 svn_wc_conflict_action_t *action,
933 const char **move_src_op_root_abspath,
935 const char *wri_abspath,
936 const svn_skel_t *conflict_skel,
937 apr_pool_t *result_pool,
938 apr_pool_t *scratch_pool)
940 svn_skel_t *tree_conflict;
942 svn_boolean_t is_moved_away = FALSE;
944 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
945 SVN_WC__CONFLICT_KIND_TREE));
948 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
950 c = tree_conflict->children;
952 c = c->next; /* Skip "tree" */
954 c = c->next; /* Skip markers */
957 int value = svn_token__from_mem(reason_map, c->data, c->len);
961 if (value != SVN_TOKEN_UNKNOWN)
964 *reason = svn_wc_conflict_reason_edited;
967 is_moved_away = (value == svn_wc_conflict_reason_moved_away);
973 int value = svn_token__from_mem(action_map, c->data, c->len);
975 if (value != SVN_TOKEN_UNKNOWN)
978 *action = svn_wc_conflict_action_edit;
983 if (move_src_op_root_abspath)
985 /* Only set for update and switch tree conflicts */
986 if (c && is_moved_away)
988 const char *move_src_op_root_relpath
989 = apr_pstrmemdup(scratch_pool, c->data, c->len);
991 SVN_ERR(svn_wc__db_from_relpath(move_src_op_root_abspath,
993 move_src_op_root_relpath,
994 result_pool, scratch_pool));
997 *move_src_op_root_abspath = NULL;
1000 return SVN_NO_ERROR;
1004 svn_wc__conflict_read_markers(const apr_array_header_t **markers,
1006 const char *wri_abspath,
1007 const svn_skel_t *conflict_skel,
1008 apr_pool_t *result_pool,
1009 apr_pool_t *scratch_pool)
1011 const svn_skel_t *conflict;
1012 apr_array_header_t *list = NULL;
1014 SVN_ERR_ASSERT(conflict_skel != NULL);
1016 /* Walk the conflicts */
1017 for (conflict = conflict_skel->children->next->children;
1019 conflict = conflict->next)
1021 const svn_skel_t *marker;
1023 /* Get the list of markers stored per conflict */
1024 for (marker = conflict->children->next->children;
1026 marker = marker->next)
1028 /* Skip placeholders */
1029 if (! marker->is_atom)
1033 list = apr_array_make(result_pool, 4, sizeof(const char *));
1035 SVN_ERR(svn_wc__db_from_relpath(
1036 &APR_ARRAY_PUSH(list, const char*),
1038 apr_pstrmemdup(scratch_pool, marker->data,
1040 result_pool, scratch_pool));
1045 return SVN_NO_ERROR;
1048 /* --------------------------------------------------------------------
1053 svn_wc__conflict_create_markers(svn_skel_t **work_items,
1055 const char *local_abspath,
1056 svn_skel_t *conflict_skel,
1057 apr_pool_t *result_pool,
1058 apr_pool_t *scratch_pool)
1060 svn_boolean_t prop_conflicted;
1061 svn_wc_operation_t operation;
1064 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL,
1065 NULL, &prop_conflicted, NULL,
1068 scratch_pool, scratch_pool));
1070 if (prop_conflicted)
1072 const char *marker_abspath = NULL;
1073 svn_node_kind_t kind;
1074 const char *marker_dir;
1075 const char *marker_name;
1076 const char *marker_relpath;
1078 /* Ok, currently we have to do a few things for property conflicts:
1079 - Create a marker file
1080 - Store the name in the conflict_skel
1081 - Create a WQ item that fills the marker with the expected data */
1083 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
1085 if (kind == svn_node_dir)
1087 marker_dir = local_abspath;
1088 marker_name = SVN_WC__THIS_DIR_PREJ;
1091 svn_dirent_split(&marker_dir, &marker_name, local_abspath,
1094 SVN_ERR(svn_io_open_uniquely_named(NULL, &marker_abspath,
1097 SVN_WC__PROP_REJ_EXT,
1098 svn_io_file_del_none,
1099 scratch_pool, scratch_pool));
1101 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, local_abspath,
1102 marker_abspath, result_pool, result_pool));
1104 /* And store the marker in the skel */
1106 svn_skel_t *prop_conflict;
1107 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
1108 SVN_WC__CONFLICT_KIND_PROP));
1110 svn_skel__prepend_str(marker_relpath, prop_conflict->children->next,
1113 SVN_ERR(svn_wc__wq_build_prej_install(work_items,
1115 scratch_pool, scratch_pool));
1118 return SVN_NO_ERROR;
1121 /* Helper function for the three apply_* functions below, used when
1122 * merging properties together.
1124 * Given property PROPNAME on LOCAL_ABSPATH, and four possible property
1125 * values, generate four tmpfiles and pass them to CONFLICT_FUNC callback.
1126 * This gives the client an opportunity to interactively resolve the
1127 * property conflict.
1129 * BASE_VAL/WORKING_VAL represent the current state of the working
1130 * copy, and INCOMING_OLD_VAL/INCOMING_NEW_VAL represents the incoming
1131 * propchange. Any of these values might be NULL, indicating either
1132 * non-existence or intent-to-delete.
1134 * If the callback isn't available, or if it responds with
1135 * 'choose_postpone', then set *CONFLICT_REMAINS to TRUE and return.
1137 * If the callback responds with a choice of 'base', 'theirs', 'mine',
1138 * or 'merged', then install the proper value into ACTUAL_PROPS and
1139 * set *CONFLICT_REMAINS to FALSE.
1141 static svn_error_t *
1142 generate_propconflict(svn_boolean_t *conflict_remains,
1144 const char *local_abspath,
1145 svn_node_kind_t kind,
1146 svn_wc_operation_t operation,
1147 const svn_wc_conflict_version_t *left_version,
1148 const svn_wc_conflict_version_t *right_version,
1149 const char *propname,
1150 const svn_string_t *base_val,
1151 const svn_string_t *working_val,
1152 const svn_string_t *incoming_old_val,
1153 const svn_string_t *incoming_new_val,
1154 svn_wc_conflict_resolver_func2_t conflict_func,
1155 void *conflict_baton,
1156 svn_cancel_func_t cancel_func,
1158 apr_pool_t *scratch_pool)
1160 svn_wc_conflict_result_t *result = NULL;
1161 svn_wc_conflict_description2_t *cdesc;
1162 const char *dirpath = svn_dirent_dirname(local_abspath, scratch_pool);
1163 const svn_string_t *new_value = NULL;
1165 cdesc = svn_wc_conflict_description_create_prop2(
1168 propname, scratch_pool);
1170 cdesc->operation = operation;
1171 cdesc->src_left_version = left_version;
1172 cdesc->src_right_version = right_version;
1174 /* Create a tmpfile for each of the string_t's we've got. */
1177 const char *file_name;
1179 SVN_ERR(svn_io_write_unique(&file_name, dirpath, working_val->data,
1181 svn_io_file_del_on_pool_cleanup,
1183 cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1184 cdesc->prop_value_working = working_val;
1187 if (incoming_new_val)
1189 const char *file_name;
1191 SVN_ERR(svn_io_write_unique(&file_name, dirpath, incoming_new_val->data,
1192 incoming_new_val->len,
1193 svn_io_file_del_on_pool_cleanup,
1196 /* ### For property conflicts, cd2 stores prop_reject_abspath in
1197 * ### their_abspath, and stores theirs_abspath in merged_file. */
1198 cdesc->merged_file = svn_dirent_join(dirpath, file_name, scratch_pool);
1199 cdesc->prop_value_incoming_new = incoming_new_val;
1202 if (!base_val && !incoming_old_val)
1204 /* If base and old are both NULL, then that's fine, we just let
1205 base_file stay NULL as-is. Both agents are attempting to add a
1208 else if ((base_val && !incoming_old_val)
1209 || (!base_val && incoming_old_val))
1211 /* If only one of base and old are defined, then we've got a
1212 situation where one agent is attempting to add the property
1213 for the first time, and the other agent is changing a
1214 property it thinks already exists. In this case, we return
1215 whichever older-value happens to be defined, so that the
1216 conflict-callback can still attempt a 3-way merge. */
1218 const svn_string_t *conflict_base_val = base_val ? base_val
1220 const char *file_name;
1222 SVN_ERR(svn_io_write_unique(&file_name, dirpath,
1223 conflict_base_val->data,
1224 conflict_base_val->len,
1225 svn_io_file_del_on_pool_cleanup,
1227 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1229 else /* base and old are both non-NULL */
1231 const svn_string_t *conflict_base_val;
1232 const char *file_name;
1234 if (! svn_string_compare(base_val, incoming_old_val))
1236 /* What happens if 'base' and 'old' don't match up? In an
1237 ideal situation, they would. But if they don't, this is
1238 a classic example of a patch 'hunk' failing to apply due
1239 to a lack of context. For example: imagine that the user
1240 is busy changing the property from a value of "cat" to
1241 "dog", but the incoming propchange wants to change the
1242 same property value from "red" to "green". Total context
1245 HOWEVER: we can still pass one of the two base values as
1246 'base_file' to the callback anyway. It's still useful to
1247 present the working and new values to the user to
1250 if (working_val && svn_string_compare(base_val, working_val))
1251 conflict_base_val = incoming_old_val;
1253 conflict_base_val = base_val;
1257 conflict_base_val = base_val;
1260 SVN_ERR(svn_io_write_unique(&file_name, dirpath, conflict_base_val->data,
1261 conflict_base_val->len,
1262 svn_io_file_del_on_pool_cleanup, scratch_pool));
1263 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1265 cdesc->prop_value_base = base_val;
1266 cdesc->prop_value_incoming_old = incoming_old_val;
1268 if (working_val && incoming_new_val)
1270 svn_stream_t *mergestream;
1272 svn_diff_file_options_t *options =
1273 svn_diff_file_options_create(scratch_pool);
1275 SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->prop_reject_abspath,
1276 NULL, svn_io_file_del_on_pool_cleanup,
1277 scratch_pool, scratch_pool));
1278 SVN_ERR(svn_diff_mem_string_diff3(&diff, conflict_base_val,
1280 incoming_new_val, options, scratch_pool));
1281 SVN_ERR(svn_diff_mem_string_output_merge3(mergestream, diff,
1282 conflict_base_val, working_val,
1283 incoming_new_val, NULL, NULL, NULL, NULL,
1284 svn_diff_conflict_display_modified_latest,
1285 cancel_func, cancel_baton, scratch_pool));
1286 SVN_ERR(svn_stream_close(mergestream));
1288 /* ### For property conflicts, cd2 stores prop_reject_abspath in
1289 * ### their_abspath, and stores theirs_abspath in merged_file. */
1290 cdesc->their_abspath = cdesc->prop_reject_abspath;
1294 if (!incoming_old_val && incoming_new_val)
1295 cdesc->action = svn_wc_conflict_action_add;
1296 else if (incoming_old_val && !incoming_new_val)
1297 cdesc->action = svn_wc_conflict_action_delete;
1299 cdesc->action = svn_wc_conflict_action_edit;
1301 if (base_val && !working_val)
1302 cdesc->reason = svn_wc_conflict_reason_deleted;
1303 else if (!base_val && working_val)
1304 cdesc->reason = svn_wc_conflict_reason_obstructed;
1306 cdesc->reason = svn_wc_conflict_reason_edited;
1308 /* Invoke the interactive conflict callback. */
1309 SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
1313 *conflict_remains = TRUE;
1314 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
1315 NULL, _("Conflict callback violated API:"
1316 " returned no results"));
1320 switch (result->choice)
1323 case svn_wc_conflict_choose_postpone:
1325 *conflict_remains = TRUE;
1328 case svn_wc_conflict_choose_mine_full:
1330 /* No need to change actual_props; it already contains working_val */
1331 *conflict_remains = FALSE;
1332 new_value = working_val;
1335 /* I think _mine_full and _theirs_full are appropriate for prop
1336 behavior as well as the text behavior. There should even be
1337 analogous behaviors for _mine and _theirs when those are
1338 ready, namely: fold in all non-conflicting prop changes, and
1339 then choose _mine side or _theirs side for conflicting ones. */
1340 case svn_wc_conflict_choose_theirs_full:
1342 *conflict_remains = FALSE;
1343 new_value = incoming_new_val;
1346 case svn_wc_conflict_choose_base:
1348 *conflict_remains = FALSE;
1349 new_value = base_val;
1352 case svn_wc_conflict_choose_merged:
1354 svn_stringbuf_t *merged_stringbuf;
1356 if (!cdesc->merged_file
1357 && (!result->merged_file && !result->merged_value))
1358 return svn_error_create
1359 (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
1360 NULL, _("Conflict callback violated API:"
1361 " returned no merged file"));
1363 if (result->merged_value)
1364 new_value = result->merged_value;
1367 SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf,
1368 result->merged_file ?
1369 result->merged_file :
1372 new_value = svn_stringbuf__morph_into_string(merged_stringbuf);
1374 *conflict_remains = FALSE;
1379 if (!*conflict_remains)
1383 /* For now, just set the property values. This should really do some of the
1384 more advanced things from svn_wc_prop_set() */
1386 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool,
1389 svn_hash_sets(props, propname, new_value);
1391 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, props,
1396 return SVN_NO_ERROR;
1399 /* Perform a 3-way merge in which conflicts are expected, showing the
1400 * conflicts in the way specified by STYLE, and using MERGE_OPTIONS.
1402 * The three input files are LEFT_ABSPATH (the base), DETRANSLATED_TARGET
1403 * and RIGHT_ABSPATH. The output is stored in a new temporary file,
1404 * whose name is put into *CHOSEN_ABSPATH.
1406 * The output file will be deleted according to DELETE_WHEN. If
1407 * DELETE_WHEN is 'on pool cleanup', it refers to RESULT_POOL.
1409 * DB and WRI_ABSPATH are used to choose a directory for the output file.
1411 * Allocate *CHOSEN_ABSPATH in RESULT_POOL. Use SCRATCH_POOL for temporary
1414 static svn_error_t *
1415 merge_showing_conflicts(const char **chosen_abspath,
1417 const char *wri_abspath,
1418 svn_diff_conflict_display_style_t style,
1419 const apr_array_header_t *merge_options,
1420 const char *left_abspath,
1421 const char *detranslated_target,
1422 const char *right_abspath,
1423 svn_io_file_del_t delete_when,
1424 svn_cancel_func_t cancel_func,
1426 apr_pool_t *result_pool,
1427 apr_pool_t *scratch_pool)
1429 const char *temp_dir;
1430 svn_stream_t *chosen_stream;
1432 svn_diff_file_options_t *diff3_options;
1434 diff3_options = svn_diff_file_options_create(scratch_pool);
1436 SVN_ERR(svn_diff_file_options_parse(diff3_options,
1440 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
1442 scratch_pool, scratch_pool));
1443 /* We need to open the stream in RESULT_POOL because that controls the
1444 * lifetime of the file if DELETE_WHEN is 'on pool cleanup'. (We also
1445 * want to allocate CHOSEN_ABSPATH in RESULT_POOL, but we don't care
1446 * about the stream itself.) */
1447 SVN_ERR(svn_stream_open_unique(&chosen_stream, chosen_abspath,
1448 temp_dir, delete_when,
1449 result_pool, scratch_pool));
1450 SVN_ERR(svn_diff_file_diff3_2(&diff,
1452 detranslated_target, right_abspath,
1453 diff3_options, scratch_pool));
1454 SVN_ERR(svn_diff_file_output_merge3(chosen_stream, diff,
1456 detranslated_target,
1458 NULL, NULL, NULL, NULL, /* markers */
1459 style, cancel_func, cancel_baton,
1461 SVN_ERR(svn_stream_close(chosen_stream));
1463 return SVN_NO_ERROR;
1466 /* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the
1467 * working copy at DB/WRI_ABSPATH.
1469 * Set *WORK_ITEMS to a new work item that, when run, will delete the
1470 * artifact file; or to NULL if there is no file to delete.
1472 * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its
1473 * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND
1474 * may be NULL if not required.
1476 static svn_error_t *
1477 remove_artifact_file_if_exists(svn_skel_t **work_items,
1478 svn_boolean_t *file_found,
1480 const char *wri_abspath,
1481 const char *artifact_file_abspath,
1482 apr_pool_t *result_pool,
1483 apr_pool_t *scratch_pool)
1486 if (artifact_file_abspath)
1488 svn_node_kind_t node_kind;
1490 SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind,
1492 if (node_kind == svn_node_file)
1494 SVN_ERR(svn_wc__wq_build_file_remove(work_items,
1496 artifact_file_abspath,
1497 result_pool, scratch_pool));
1503 return SVN_NO_ERROR;
1506 /* Create a new file in the same directory as LOCAL_ABSPATH, with the
1507 same basename as LOCAL_ABSPATH, with a ".edited" extension, and set
1508 *WORK_ITEM to a new work item that will copy and translate from the file
1509 SOURCE_ABSPATH to that new file. It will be translated from repository-
1510 normal form to working-copy form according to the versioned properties
1511 of LOCAL_ABSPATH that are current when the work item is executed.
1513 DB should have a write lock for the directory containing SOURCE.
1515 Allocate *WORK_ITEM in RESULT_POOL. */
1516 static svn_error_t *
1517 save_merge_result(svn_skel_t **work_item,
1519 const char *local_abspath,
1520 const char *source_abspath,
1521 apr_pool_t *result_pool,
1522 apr_pool_t *scratch_pool)
1524 const char *edited_copy_abspath;
1525 const char *dir_abspath;
1526 const char *filename;
1528 svn_dirent_split(&dir_abspath, &filename, local_abspath, scratch_pool);
1530 /* ### Should use preserved-conflict-file-exts. */
1531 /* Create the .edited file within this file's DIR_ABSPATH */
1532 SVN_ERR(svn_io_open_uniquely_named(NULL,
1533 &edited_copy_abspath,
1537 svn_io_file_del_none,
1538 scratch_pool, scratch_pool));
1539 SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item,
1542 edited_copy_abspath,
1543 result_pool, scratch_pool));
1544 return SVN_NO_ERROR;
1549 /* Resolve the text conflict in CONFLICT, which is currently recorded
1550 * on DB/LOCAL_ABSPATH in the manner specified by CHOICE.
1552 * Set *WORK_ITEMS to new work items that will make the on-disk changes
1553 * needed to complete the resolution (but not to mark it as resolved).
1555 * Set *FOUND_ARTIFACT to true if conflict markers are removed; otherwise
1556 * (which is only if CHOICE is 'postpone') to false.
1558 * CHOICE, MERGED_FILE and SAVE_MERGED are typically values provided by
1559 * the conflict resolver.
1561 * MERGE_OPTIONS allows customizing the diff handling when using
1562 * per hunk conflict resolving.
1564 static svn_error_t *
1565 build_text_conflict_resolve_items(svn_skel_t **work_items,
1566 svn_boolean_t *found_artifact,
1568 const char *local_abspath,
1569 const svn_skel_t *conflict,
1570 svn_wc_conflict_choice_t choice,
1571 const char *merged_file,
1572 svn_boolean_t save_merged,
1573 const apr_array_header_t *merge_options,
1574 svn_cancel_func_t cancel_func,
1576 apr_pool_t *result_pool,
1577 apr_pool_t *scratch_pool)
1579 const char *mine_abspath;
1580 const char *their_old_abspath;
1581 const char *their_abspath;
1582 svn_skel_t *work_item;
1583 const char *install_from_abspath = NULL;
1584 svn_boolean_t remove_source = FALSE;
1589 *found_artifact = FALSE;
1591 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
1596 scratch_pool, scratch_pool));
1599 SVN_ERR(save_merge_result(work_items,
1604 result_pool, scratch_pool));
1606 if (choice == svn_wc_conflict_choose_postpone)
1607 return SVN_NO_ERROR;
1611 /* If the callback wants to use one of the fulltexts
1612 to resolve the conflict, so be it.*/
1613 case svn_wc_conflict_choose_base:
1615 install_from_abspath = their_old_abspath;
1618 case svn_wc_conflict_choose_theirs_full:
1620 install_from_abspath = their_abspath;
1623 case svn_wc_conflict_choose_mine_full:
1625 install_from_abspath = mine_abspath;
1628 case svn_wc_conflict_choose_theirs_conflict:
1629 case svn_wc_conflict_choose_mine_conflict:
1631 svn_diff_conflict_display_style_t style
1632 = choice == svn_wc_conflict_choose_theirs_conflict
1633 ? svn_diff_conflict_display_latest
1634 : svn_diff_conflict_display_modified;
1636 SVN_ERR(merge_showing_conflicts(&install_from_abspath,
1638 style, merge_options,
1642 /* ### why not same as other caller? */
1643 svn_io_file_del_none,
1644 cancel_func, cancel_baton,
1645 scratch_pool, scratch_pool));
1646 remove_source = TRUE;
1650 /* For the case of 3-way file merging, we don't
1651 really distinguish between these return values;
1652 if the callback claims to have "generally
1653 resolved" the situation, we still interpret
1654 that as "OK, we'll assume the merged version is
1656 case svn_wc_conflict_choose_merged:
1658 install_from_abspath = merged_file
1663 case svn_wc_conflict_choose_postpone:
1665 /* Assume conflict remains. */
1666 return SVN_NO_ERROR;
1669 SVN_ERR_ASSERT(choice == svn_wc_conflict_choose_postpone);
1672 if (install_from_abspath == NULL)
1673 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1674 _("Conflict on '%s' could not be resolved "
1675 "because the chosen version of the file "
1676 "is not available."),
1677 svn_dirent_local_style(local_abspath,
1680 /* ### It would be nice if we could somehow pass RECORD_FILEINFO
1681 as true in some easy cases. */
1682 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
1684 install_from_abspath,
1685 FALSE /* use_commit_times */,
1686 FALSE /* record_fileinfo */,
1687 result_pool, scratch_pool));
1688 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1692 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
1694 install_from_abspath,
1695 result_pool, scratch_pool));
1696 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1699 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1702 result_pool, scratch_pool));
1703 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1705 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1708 result_pool, scratch_pool));
1709 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1711 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1714 result_pool, scratch_pool));
1715 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1717 return SVN_NO_ERROR;
1721 /* Set *DESC to a new description of the text conflict in
1722 * CONFLICT_SKEL. If there is no text conflict in CONFLICT_SKEL, return
1725 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1726 * rather than reading them from CONFLICT_SKEL. Use IS_BINARY and
1727 * MIME_TYPE for the corresponding fields of *DESC.
1729 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
1731 static svn_error_t *
1732 read_text_conflict_desc(svn_wc_conflict_description2_t **desc,
1734 const char *local_abspath,
1735 const svn_skel_t *conflict_skel,
1736 const char *mime_type,
1737 svn_wc_operation_t operation,
1738 const svn_wc_conflict_version_t *left_version,
1739 const svn_wc_conflict_version_t *right_version,
1740 apr_pool_t *result_pool,
1741 apr_pool_t *scratch_pool)
1743 *desc = svn_wc_conflict_description_create_text2(local_abspath, result_pool);
1744 (*desc)->mime_type = mime_type;
1745 (*desc)->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
1746 (*desc)->operation = operation;
1747 (*desc)->src_left_version = left_version;
1748 (*desc)->src_right_version = right_version;
1750 SVN_ERR(svn_wc__conflict_read_text_conflict(&(*desc)->my_abspath,
1751 &(*desc)->base_abspath,
1752 &(*desc)->their_abspath,
1755 result_pool, scratch_pool));
1756 (*desc)->merged_file = apr_pstrdup(result_pool, local_abspath);
1758 return SVN_NO_ERROR;
1761 /* Set *CONFLICT_DESC to a new description of the tree conflict in
1762 * CONFLICT_SKEL. If there is no tree conflict in CONFLICT_SKEL, return
1765 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1766 * rather than reading them from CONFLICT_SKEL.
1768 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
1770 static svn_error_t *
1771 read_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
1773 const char *local_abspath,
1774 svn_node_kind_t node_kind,
1775 const svn_skel_t *conflict_skel,
1776 svn_wc_operation_t operation,
1777 const svn_wc_conflict_version_t *left_version,
1778 const svn_wc_conflict_version_t *right_version,
1779 apr_pool_t *result_pool,
1780 apr_pool_t *scratch_pool)
1782 svn_node_kind_t local_kind;
1783 svn_wc_conflict_reason_t reason;
1784 svn_wc_conflict_action_t action;
1786 SVN_ERR(svn_wc__conflict_read_tree_conflict(
1787 &reason, &action, NULL,
1788 db, local_abspath, conflict_skel, scratch_pool, scratch_pool));
1790 if (reason == svn_wc_conflict_reason_missing)
1791 local_kind = svn_node_none;
1792 else if (reason == svn_wc_conflict_reason_unversioned ||
1793 reason == svn_wc_conflict_reason_obstructed)
1794 SVN_ERR(svn_io_check_path(local_abspath, &local_kind, scratch_pool));
1795 else if (action == svn_wc_conflict_action_delete
1797 && (operation == svn_wc_operation_update
1798 ||operation == svn_wc_operation_switch)
1799 && (reason == svn_wc_conflict_reason_deleted
1800 || reason == svn_wc_conflict_reason_moved_away))
1802 /* We have nothing locally to take the kind from */
1803 local_kind = left_version->node_kind;
1806 local_kind = node_kind;
1808 *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind,
1810 left_version, right_version,
1812 (*desc)->reason = reason;
1813 (*desc)->action = action;
1815 return SVN_NO_ERROR;
1818 /* Forward definition */
1819 static svn_error_t *
1820 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
1822 const char *local_abspath,
1823 const svn_skel_t *conflict,
1824 svn_wc_conflict_choice_t conflict_choice,
1825 apr_hash_t *resolve_later,
1826 svn_wc_notify_func2_t notify_func,
1828 svn_cancel_func_t cancel_func,
1830 apr_pool_t *scratch_pool);
1833 svn_wc__conflict_invoke_resolver(svn_wc__db_t *db,
1834 const char *local_abspath,
1835 svn_node_kind_t kind,
1836 const svn_skel_t *conflict_skel,
1837 const apr_array_header_t *merge_options,
1838 svn_wc_conflict_resolver_func2_t resolver_func,
1839 void *resolver_baton,
1840 svn_cancel_func_t cancel_func,
1842 apr_pool_t *scratch_pool)
1844 svn_boolean_t text_conflicted;
1845 svn_boolean_t prop_conflicted;
1846 svn_boolean_t tree_conflicted;
1847 svn_wc_operation_t operation;
1848 const apr_array_header_t *locations;
1849 const svn_wc_conflict_version_t *left_version = NULL;
1850 const svn_wc_conflict_version_t *right_version = NULL;
1852 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
1853 &text_conflicted, &prop_conflicted,
1855 db, local_abspath, conflict_skel,
1856 scratch_pool, scratch_pool));
1858 if (locations && locations->nelts > 0)
1859 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
1861 if (locations && locations->nelts > 1)
1862 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
1864 /* Quick and dirty compatibility wrapper. My guess would be that most resolvers
1865 would want to look at all properties at the same time.
1867 ### svn currently only invokes this from the merge code to collect the list of
1868 ### conflicted paths. Eventually this code will be the base for 'svn resolve'
1869 ### and at that time the test coverage will improve
1871 if (prop_conflicted)
1873 apr_hash_t *old_props;
1874 apr_hash_t *mine_props;
1875 apr_hash_t *their_props;
1876 apr_hash_t *old_their_props;
1877 apr_hash_t *conflicted;
1878 apr_pool_t *iterpool;
1879 apr_hash_index_t *hi;
1880 svn_boolean_t mark_resolved = TRUE;
1882 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
1889 scratch_pool, scratch_pool));
1891 if (operation == svn_wc_operation_merge)
1892 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
1893 scratch_pool, scratch_pool));
1895 old_props = old_their_props;
1897 iterpool = svn_pool_create(scratch_pool);
1899 for (hi = apr_hash_first(scratch_pool, conflicted);
1901 hi = apr_hash_next(hi))
1903 const char *propname = apr_hash_this_key(hi);
1904 svn_boolean_t conflict_remains = TRUE;
1906 svn_pool_clear(iterpool);
1909 SVN_ERR(cancel_func(cancel_baton));
1911 SVN_ERR(generate_propconflict(&conflict_remains,
1912 db, local_abspath, kind,
1918 ? svn_hash_gets(old_props, propname)
1921 ? svn_hash_gets(mine_props, propname)
1924 ? svn_hash_gets(old_their_props, propname)
1927 ? svn_hash_gets(their_props, propname)
1929 resolver_func, resolver_baton,
1930 cancel_func, cancel_baton,
1933 if (conflict_remains)
1934 mark_resolved = FALSE;
1939 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
1942 svn_pool_destroy(iterpool);
1945 if (text_conflicted)
1947 svn_skel_t *work_items;
1948 svn_boolean_t was_resolved;
1949 svn_wc_conflict_description2_t *desc;
1951 svn_wc_conflict_result_t *result;
1953 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
1954 scratch_pool, scratch_pool));
1956 SVN_ERR(read_text_conflict_desc(&desc,
1957 db, local_abspath, conflict_skel,
1958 svn_prop_get_value(props,
1959 SVN_PROP_MIME_TYPE),
1960 operation, left_version, right_version,
1961 scratch_pool, scratch_pool));
1965 was_resolved = FALSE;
1967 /* Give the conflict resolution callback a chance to clean
1968 up the conflicts before we mark the file 'conflicted' */
1970 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
1973 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1974 _("Conflict callback violated API:"
1975 " returned no results"));
1977 SVN_ERR(build_text_conflict_resolve_items(&work_items, &was_resolved,
1979 conflict_skel, result->choice,
1980 result->merged_file,
1981 result->save_merged,
1983 cancel_func, cancel_baton,
1984 scratch_pool, scratch_pool));
1986 if (result->choice != svn_wc_conflict_choose_postpone)
1988 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
1990 work_items, scratch_pool));
1991 SVN_ERR(svn_wc__wq_run(db, local_abspath,
1992 cancel_func, cancel_baton,
1997 if (tree_conflicted)
1999 svn_wc_conflict_result_t *result;
2000 svn_wc_conflict_description2_t *desc;
2001 svn_boolean_t resolved;
2002 svn_node_kind_t node_kind;
2004 SVN_ERR(svn_wc__db_read_kind(&node_kind, db, local_abspath, TRUE,
2005 TRUE, FALSE, scratch_pool));
2007 SVN_ERR(read_tree_conflict_desc(&desc,
2008 db, local_abspath, node_kind,
2010 operation, left_version, right_version,
2011 scratch_pool, scratch_pool));
2013 /* Tell the resolver func about this conflict. */
2014 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
2018 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2019 _("Conflict callback violated API:"
2020 " returned no results"));
2022 /* Pass retry hash to avoid erroring out on cases where update
2023 can continue safely. ### Need notify handling */
2024 if (result->choice != svn_wc_conflict_choose_postpone)
2025 SVN_ERR(resolve_tree_conflict_on_node(&resolved,
2026 db, local_abspath, conflict_skel,
2028 apr_hash_make(scratch_pool),
2029 NULL, NULL, /* ### notify */
2030 cancel_func, cancel_baton,
2034 return SVN_NO_ERROR;
2037 /* Read all property conflicts contained in CONFLICT_SKEL into
2038 * individual conflict descriptions, and append those descriptions
2039 * to the CONFLICTS array. If there is no property conflict in
2040 * CONFLICT_SKEL, return an error.
2042 * If NOT create_tempfiles, always create a legacy property conflict
2045 * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
2046 * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
2048 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
2050 static svn_error_t *
2051 read_prop_conflict_descs(apr_array_header_t *conflicts,
2053 const char *local_abspath,
2054 svn_skel_t *conflict_skel,
2055 svn_boolean_t create_tempfiles,
2056 svn_node_kind_t node_kind,
2057 svn_wc_operation_t operation,
2058 const svn_wc_conflict_version_t *left_version,
2059 const svn_wc_conflict_version_t *right_version,
2060 apr_pool_t *result_pool,
2061 apr_pool_t *scratch_pool)
2063 const char *prop_reject_abspath;
2064 apr_hash_t *base_props;
2065 apr_hash_t *my_props;
2066 apr_hash_t *their_old_props;
2067 apr_hash_t *their_props;
2068 apr_hash_t *conflicted_props;
2069 apr_hash_index_t *hi;
2070 apr_pool_t *iterpool;
2071 svn_boolean_t prop_conflicted;
2073 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2074 NULL, db, local_abspath, conflict_skel,
2075 scratch_pool, scratch_pool));
2077 if (!prop_conflicted)
2078 return SVN_NO_ERROR;
2080 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath,
2087 scratch_pool, scratch_pool));
2089 prop_reject_abspath = apr_pstrdup(result_pool, prop_reject_abspath);
2091 if (apr_hash_count(conflicted_props) == 0)
2093 /* Legacy prop conflict with only a .reject file. */
2094 svn_wc_conflict_description2_t *desc;
2096 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2100 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2101 * ### their_abspath, and stores theirs_abspath in merged_file. */
2102 desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2103 desc->their_abspath = desc->prop_reject_abspath;
2105 desc->operation = operation;
2106 desc->src_left_version = left_version;
2107 desc->src_right_version = right_version;
2109 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2111 return SVN_NO_ERROR;
2114 if (operation == svn_wc_operation_merge)
2115 SVN_ERR(svn_wc__db_read_pristine_props(&base_props, db, local_abspath,
2116 result_pool, scratch_pool));
2119 iterpool = svn_pool_create(scratch_pool);
2120 for (hi = apr_hash_first(scratch_pool, conflicted_props);
2122 hi = apr_hash_next(hi))
2124 const char *propname = apr_hash_this_key(hi);
2125 svn_string_t *old_value;
2126 svn_string_t *my_value;
2127 svn_string_t *their_value;
2128 svn_wc_conflict_description2_t *desc;
2130 svn_pool_clear(iterpool);
2132 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2137 desc->operation = operation;
2138 desc->src_left_version = left_version;
2139 desc->src_right_version = right_version;
2141 desc->property_name = apr_pstrdup(result_pool, propname);
2143 my_value = svn_hash_gets(my_props, propname);
2144 their_value = svn_hash_gets(their_props, propname);
2145 old_value = svn_hash_gets(their_old_props, propname);
2147 /* Compute the incoming side of the conflict ('action'). */
2148 if (their_value == NULL)
2149 desc->action = svn_wc_conflict_action_delete;
2150 else if (old_value == NULL)
2151 desc->action = svn_wc_conflict_action_add;
2153 desc->action = svn_wc_conflict_action_edit;
2155 /* Compute the local side of the conflict ('reason'). */
2156 if (my_value == NULL)
2157 desc->reason = svn_wc_conflict_reason_deleted;
2158 else if (old_value == NULL)
2159 desc->reason = svn_wc_conflict_reason_added;
2161 desc->reason = svn_wc_conflict_reason_edited;
2163 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2164 * ### their_abspath, and stores theirs_abspath in merged_file. */
2165 desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2166 desc->their_abspath = desc->prop_reject_abspath;
2168 desc->prop_value_base = base_props ? svn_hash_gets(base_props, propname)
2169 : desc->prop_value_incoming_old;
2176 if (create_tempfiles)
2178 SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL,
2179 svn_io_file_del_on_pool_cleanup,
2180 result_pool, iterpool));
2181 len = my_value->len;
2182 SVN_ERR(svn_stream_write(s, my_value->data, &len));
2183 SVN_ERR(svn_stream_close(s));
2186 desc->prop_value_working = svn_string_dup(my_value, result_pool);
2194 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2195 * ### their_abspath, and stores theirs_abspath in merged_file. */
2196 if (create_tempfiles)
2198 SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL,
2199 svn_io_file_del_on_pool_cleanup,
2200 result_pool, iterpool));
2201 len = their_value->len;
2202 SVN_ERR(svn_stream_write(s, their_value->data, &len));
2203 SVN_ERR(svn_stream_close(s));
2206 desc->prop_value_incoming_new = svn_string_dup(their_value, result_pool);
2214 if (create_tempfiles)
2216 SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL,
2217 svn_io_file_del_on_pool_cleanup,
2218 result_pool, iterpool));
2219 len = old_value->len;
2220 SVN_ERR(svn_stream_write(s, old_value->data, &len));
2221 SVN_ERR(svn_stream_close(s));
2224 desc->prop_value_incoming_old = svn_string_dup(old_value, result_pool);
2227 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2229 svn_pool_destroy(iterpool);
2231 return SVN_NO_ERROR;
2235 svn_wc__read_conflicts(const apr_array_header_t **conflicts,
2236 svn_skel_t **conflict_skel,
2238 const char *local_abspath,
2239 svn_boolean_t create_tempfiles,
2240 svn_boolean_t only_tree_conflict,
2241 apr_pool_t *result_pool,
2242 apr_pool_t *scratch_pool)
2244 svn_skel_t *the_conflict_skel;
2245 apr_array_header_t *cflcts;
2246 svn_boolean_t prop_conflicted;
2247 svn_boolean_t text_conflicted;
2248 svn_boolean_t tree_conflicted;
2249 svn_wc_operation_t operation;
2250 const apr_array_header_t *locations;
2251 const svn_wc_conflict_version_t *left_version = NULL;
2252 const svn_wc_conflict_version_t *right_version = NULL;
2253 svn_node_kind_t node_kind;
2257 conflict_skel = &the_conflict_skel;
2259 SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props,
2261 (conflict_skel == &the_conflict_skel)
2266 if (!*conflict_skel)
2268 /* Some callers expect not NULL */
2269 *conflicts = apr_array_make(result_pool, 0,
2270 sizeof(svn_wc_conflict_description2_t *));
2271 return SVN_NO_ERROR;
2274 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted,
2275 &prop_conflicted, &tree_conflicted,
2276 db, local_abspath, *conflict_skel,
2277 result_pool, scratch_pool));
2279 cflcts = apr_array_make(result_pool, 4,
2280 sizeof(svn_wc_conflict_description2_t *));
2282 if (locations && locations->nelts > 0)
2283 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
2284 if (locations && locations->nelts > 1)
2285 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
2287 if (prop_conflicted && !only_tree_conflict)
2289 SVN_ERR(read_prop_conflict_descs(cflcts,
2290 db, local_abspath, *conflict_skel,
2291 create_tempfiles, node_kind,
2292 operation, left_version, right_version,
2293 result_pool, scratch_pool));
2296 if (text_conflicted && !only_tree_conflict)
2298 svn_wc_conflict_description2_t *desc;
2300 SVN_ERR(read_text_conflict_desc(&desc,
2301 db, local_abspath, *conflict_skel,
2302 svn_prop_get_value(props,
2303 SVN_PROP_MIME_TYPE),
2304 operation, left_version, right_version,
2305 result_pool, scratch_pool));
2306 APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t *) = desc;
2309 if (tree_conflicted)
2311 svn_wc_conflict_description2_t *desc;
2313 SVN_ERR(read_tree_conflict_desc(&desc,
2314 db, local_abspath, node_kind,
2316 operation, left_version, right_version,
2317 result_pool, scratch_pool));
2319 APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc;
2322 *conflicts = cflcts;
2323 return SVN_NO_ERROR;
2327 /*** Resolving a conflict automatically ***/
2330 * Resolve the property conflicts found in DB/LOCAL_ABSPATH according
2331 * to CONFLICT_CHOICE.
2333 * It is not an error if there is no prop conflict. If a prop conflict
2334 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2336 * Note: When there are no conflict markers on-disk to remove there is
2337 * no existing text conflict (unless we are still in the process of
2338 * creating the text conflict and we didn't register a marker file yet).
2339 * In this case the database contains old information, which we should
2340 * remove to avoid checking the next time. Resolving a property conflict
2341 * by just removing the marker file is a fully supported scenario since
2344 * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
2345 * In my opinion, 'mine_full'/'theirs_full' should select
2346 * the entire set of properties from 'mine' or 'theirs' respectively,
2347 * while 'mine_conflict'/'theirs_conflict' should select just the
2348 * properties that are in conflict. Or, '_full' should select the
2349 * entire property whereas '_conflict' should do a text merge within
2350 * each property, selecting hunks. Or all three kinds of behaviour
2351 * should be available (full set of props, full value of conflicting
2352 * props, or conflicting text hunks).
2353 * ### BH: If we make *_full select the full set of properties, we should
2354 * check if we shouldn't make it also select the full text for files.
2356 * ### TODO [JAF] All this complexity should not be down here in libsvn_wc
2357 * but in a layer above.
2359 * ### TODO [JAF] Options for 'base' should be like options for 'mine' and
2360 * for 'theirs' -- choose full set of props, full value of conflicting
2361 * props, or conflicting text hunks.
2364 static svn_error_t *
2365 resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
2367 const char *local_abspath,
2368 svn_skel_t *conflicts,
2369 const char *conflicted_propname,
2370 svn_wc_conflict_choice_t conflict_choice,
2371 const char *merged_file,
2372 const svn_string_t *merged_value,
2373 svn_cancel_func_t cancel_func,
2375 apr_pool_t *scratch_pool)
2377 const char *prop_reject_file;
2378 apr_hash_t *mine_props;
2379 apr_hash_t *their_old_props;
2380 apr_hash_t *their_props;
2381 apr_hash_t *conflicted_props;
2382 apr_hash_t *old_props;
2383 apr_hash_t *resolve_from = NULL;
2384 svn_skel_t *work_items = NULL;
2385 svn_wc_operation_t operation;
2386 svn_boolean_t prop_conflicted;
2387 apr_hash_t *actual_props;
2388 svn_boolean_t resolved_all, resolved_all_prop;
2390 *did_resolve = FALSE;
2392 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2393 NULL, db, local_abspath, conflicts,
2394 scratch_pool, scratch_pool));
2395 if (!prop_conflicted)
2396 return SVN_NO_ERROR;
2398 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2399 &mine_props, &their_old_props,
2400 &their_props, &conflicted_props,
2401 db, local_abspath, conflicts,
2402 scratch_pool, scratch_pool));
2404 if (!conflicted_props)
2406 /* We have a pre 1.8 property conflict. Just mark it resolved */
2408 SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2409 db, local_abspath, prop_reject_file,
2410 scratch_pool, scratch_pool));
2411 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE,
2412 work_items, scratch_pool));
2413 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2415 return SVN_NO_ERROR;
2418 if (conflicted_propname[0] != '\0'
2419 && !svn_hash_gets(conflicted_props, conflicted_propname))
2421 return SVN_NO_ERROR; /* This property is not conflicted! */
2424 if (operation == svn_wc_operation_merge)
2425 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
2426 scratch_pool, scratch_pool));
2428 old_props = their_old_props;
2430 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2431 scratch_pool, scratch_pool));
2433 /* We currently handle *_conflict as *_full as this argument is currently
2434 always applied for all conflicts on a node at the same time. Giving
2435 an error would break some tests that assumed that this would just
2436 resolve property conflicts to working.
2438 An alternative way to handle these conflicts would be to just copy all
2439 property state from mine/theirs on the _full option instead of just the
2440 conflicted properties. In some ways this feels like a sensible option as
2441 that would take both properties and text from mine/theirs, but when not
2442 both properties and text are conflicted we would fail in doing so.
2444 switch (conflict_choice)
2446 case svn_wc_conflict_choose_base:
2447 resolve_from = their_old_props ? their_old_props : old_props;
2449 case svn_wc_conflict_choose_mine_full:
2450 case svn_wc_conflict_choose_mine_conflict:
2451 resolve_from = mine_props;
2453 case svn_wc_conflict_choose_theirs_full:
2454 case svn_wc_conflict_choose_theirs_conflict:
2455 resolve_from = their_props;
2457 case svn_wc_conflict_choose_merged:
2458 if ((merged_file || merged_value) && conflicted_propname[0] != '\0')
2460 resolve_from = apr_hash_copy(scratch_pool, actual_props);
2464 svn_stream_t *stream;
2465 svn_string_t *merged_propval;
2467 SVN_ERR(svn_stream_open_readonly(&stream, merged_file,
2468 scratch_pool, scratch_pool));
2469 SVN_ERR(svn_string_from_stream(&merged_propval, stream,
2470 scratch_pool, scratch_pool));
2472 merged_value = merged_propval;
2474 svn_hash_sets(resolve_from, conflicted_propname, merged_value);
2477 resolve_from = NULL;
2480 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2481 _("Invalid 'conflict_result' argument"));
2487 apr_hash_index_t *hi;
2488 apr_hash_t *apply_on_props;
2490 if (conflicted_propname[0] == '\0')
2492 /* Apply to all conflicted properties */
2493 apply_on_props = conflicted_props;
2497 /* Apply to a single property */
2498 apply_on_props = apr_hash_make(scratch_pool);
2499 svn_hash_sets(apply_on_props, conflicted_propname, "");
2502 /* Apply the selected changes */
2503 for (hi = apr_hash_first(scratch_pool, apply_on_props);
2505 hi = apr_hash_next(hi))
2507 const char *propname = apr_hash_this_key(hi);
2508 svn_string_t *new_value = NULL;
2510 new_value = svn_hash_gets(resolve_from, propname);
2512 svn_hash_sets(actual_props, propname, new_value);
2515 /*else the user accepted the properties as-is */
2517 /* This function handles conflicted_propname "" as resolving
2518 all property conflicts... Just what we need here */
2519 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
2521 FALSE, conflicted_propname,
2523 scratch_pool, scratch_pool));
2527 /* Are there still property conflicts left? (or only...) */
2528 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, &prop_conflicted,
2529 NULL, db, local_abspath, conflicts,
2530 scratch_pool, scratch_pool));
2532 resolved_all_prop = (! prop_conflicted);
2536 resolved_all_prop = TRUE;
2540 if (resolved_all_prop)
2542 /* Legacy behavior: Only report property conflicts as resolved when the
2543 property reject file exists
2545 If not the UI shows the conflict as already resolved
2546 (and in this case we just remove the in-db conflict) */
2547 SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2550 scratch_pool, scratch_pool));
2554 /* Create a new prej file, based on the remaining conflicts */
2555 SVN_ERR(svn_wc__wq_build_prej_install(&work_items,
2557 scratch_pool, scratch_pool));
2558 *did_resolve = TRUE; /* We resolved a property conflict */
2561 /* This installs the updated conflict skel */
2562 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props,
2563 FALSE, conflicts, work_items,
2568 /* Remove the whole conflict. Should probably be integrated
2569 into the op_set_props() call */
2570 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2572 NULL, scratch_pool));
2575 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2578 return SVN_NO_ERROR;
2582 * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
2585 * It is not an error if there is no tree conflict. If a tree conflict
2586 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2588 * It is not an error if there is no tree conflict.
2590 * If the conflict can't be resolved yet because another tree conflict is
2591 * blocking a storage location, store the tree conflict in the RESOLVE_LATER
2594 static svn_error_t *
2595 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
2597 const char *local_abspath,
2598 const svn_skel_t *conflicts,
2599 svn_wc_conflict_choice_t conflict_choice,
2600 apr_hash_t *resolve_later,
2601 svn_wc_notify_func2_t notify_func,
2603 svn_cancel_func_t cancel_func,
2605 apr_pool_t *scratch_pool)
2607 svn_wc_conflict_reason_t reason;
2608 svn_wc_conflict_action_t action;
2609 svn_wc_operation_t operation;
2610 svn_boolean_t tree_conflicted;
2611 const char *src_op_root_abspath;
2613 *did_resolve = FALSE;
2615 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
2616 &tree_conflicted, db, local_abspath,
2617 conflicts, scratch_pool, scratch_pool));
2618 if (!tree_conflicted)
2619 return SVN_NO_ERROR;
2621 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2622 &src_op_root_abspath,
2625 scratch_pool, scratch_pool));
2627 if (operation == svn_wc_operation_update
2628 || operation == svn_wc_operation_switch)
2631 if (reason == svn_wc_conflict_reason_deleted ||
2632 reason == svn_wc_conflict_reason_replaced)
2634 if (conflict_choice == svn_wc_conflict_choose_merged)
2636 /* Break moves for any children moved out of this directory,
2637 * and leave this directory deleted. */
2639 if (action != svn_wc_conflict_action_delete)
2641 SVN_ERR(svn_wc__db_op_break_moved_away(
2642 db, local_abspath, src_op_root_abspath, TRUE,
2643 notify_func, notify_baton,
2645 *did_resolve = TRUE;
2646 return SVN_NO_ERROR; /* Marked resolved by function*/
2648 /* else # The move is/moves are already broken */
2651 *did_resolve = TRUE;
2653 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2655 svn_skel_t *new_conflicts;
2657 /* Raise moved-away conflicts on any children moved out of
2658 * this directory, and leave this directory as-is.
2660 * The newly conflicted moved-away children will be updated
2661 * if they are resolved with 'mine_conflict' as well. */
2662 err = svn_wc__db_op_raise_moved_away(
2663 db, local_abspath, notify_func, notify_baton,
2668 const char *dup_abspath;
2671 || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
2672 return svn_error_trace(err);
2674 svn_error_clear(err);
2675 dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2678 svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2680 return SVN_NO_ERROR; /* Retry after other conflicts */
2683 /* We might now have a moved-away on *this* path, let's
2684 try to resolve that directly if that is the case */
2685 SVN_ERR(svn_wc__db_read_conflict(&new_conflicts, NULL, NULL,
2687 scratch_pool, scratch_pool));
2690 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
2697 if (!new_conflicts || !tree_conflicted)
2699 /* TC is marked resolved by calling
2700 svn_wc__db_resolve_delete_raise_moved_away */
2701 *did_resolve = TRUE;
2702 return SVN_NO_ERROR;
2705 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2706 &src_op_root_abspath,
2712 if (reason != svn_wc_conflict_reason_moved_away)
2714 *did_resolve = TRUE;
2715 return SVN_NO_ERROR; /* We fixed one, but... */
2718 conflicts = new_conflicts;
2719 /* Fall through in moved_away handling */
2722 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2724 _("Tree conflict can only be resolved to "
2725 "'working' or 'mine-conflict' state; "
2726 "'%s' not resolved"),
2727 svn_dirent_local_style(local_abspath,
2731 if (reason == svn_wc_conflict_reason_moved_away
2732 && action == svn_wc_conflict_action_edit)
2734 /* After updates, we can resolve local moved-away
2735 * vs. any incoming change, either by updating the
2736 * moved-away node (mine-conflict) or by breaking the
2737 * move (theirs-conflict). */
2738 if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2740 err = svn_wc__db_update_moved_away_conflict_victim(
2741 db, local_abspath, src_op_root_abspath,
2742 operation, action, reason,
2743 cancel_func, cancel_baton,
2744 notify_func, notify_baton,
2749 const char *dup_abspath;
2752 || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
2753 return svn_error_trace(err);
2755 svn_error_clear(err);
2756 dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2759 svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2761 return SVN_NO_ERROR; /* Retry after other conflicts */
2764 *did_resolve = TRUE;
2766 else if (conflict_choice == svn_wc_conflict_choose_merged)
2768 /* We must break the move if the user accepts the current
2769 * working copy state instead of updating the move.
2770 * Else the move would be left in an invalid state. */
2772 SVN_ERR(svn_wc__db_op_break_moved_away(db, local_abspath,
2773 src_op_root_abspath, TRUE,
2774 notify_func, notify_baton,
2776 *did_resolve = TRUE;
2777 return SVN_NO_ERROR; /* Conflict is marked resolved */
2780 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2782 _("Tree conflict can only be resolved to "
2783 "'working' or 'mine-conflict' state; "
2784 "'%s' not resolved"),
2785 svn_dirent_local_style(local_abspath,
2788 else if (reason == svn_wc_conflict_reason_moved_away
2789 && action != svn_wc_conflict_action_edit)
2791 /* action added is impossible, because that would imply that
2792 something was added, but before that already moved...
2793 (which would imply a replace) */
2794 SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete
2795 || action == svn_wc_conflict_action_replace);
2797 if (conflict_choice == svn_wc_conflict_choose_merged)
2799 /* Whatever was moved is removed at its original location by the
2800 update. That must also remove the recording of the move, so
2801 we don't have to do anything here. */
2803 *did_resolve = TRUE;
2805 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2807 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2809 _("Tree conflict can only be "
2810 "resolved to 'working' state; "
2811 "'%s' is no longer moved"),
2812 svn_dirent_local_style(local_abspath,
2820 if (conflict_choice != svn_wc_conflict_choose_merged)
2822 /* For other tree conflicts, there is no way to pick
2823 * theirs-full or mine-full, etc. Throw an error if the
2824 * user expects us to be smarter than we really are. */
2825 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2827 _("Tree conflict can only be "
2828 "resolved to 'working' state; "
2829 "'%s' not resolved"),
2830 svn_dirent_local_style(local_abspath,
2834 *did_resolve = TRUE;
2837 SVN_ERR_ASSERT(*did_resolve);
2839 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE,
2840 NULL, scratch_pool));
2841 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2843 return SVN_NO_ERROR;
2847 svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
2848 const char *local_abspath,
2849 svn_cancel_func_t cancel_func,
2851 apr_pool_t *scratch_pool)
2853 svn_skel_t *work_items;
2854 svn_skel_t *conflict;
2856 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
2858 scratch_pool, scratch_pool));
2861 return SVN_NO_ERROR;
2863 SVN_ERR(build_text_conflict_resolve_items(&work_items, NULL,
2864 db, local_abspath, conflict,
2865 svn_wc_conflict_choose_merged,
2867 cancel_func, cancel_baton,
2868 scratch_pool, scratch_pool));
2870 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, TRUE, FALSE, FALSE,
2871 work_items, scratch_pool));
2873 return svn_error_trace(svn_wc__wq_run(db, local_abspath,
2874 cancel_func, cancel_baton,
2879 svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db,
2880 const char *local_abspath,
2881 apr_pool_t *scratch_pool)
2883 svn_boolean_t ignored_result;
2884 svn_skel_t *conflicts;
2886 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
2888 scratch_pool, scratch_pool));
2891 return SVN_NO_ERROR;
2893 return svn_error_trace(resolve_prop_conflict_on_node(
2895 db, local_abspath, conflicts, "",
2896 svn_wc_conflict_choose_merged,
2903 /* Baton for conflict_status_walker */
2904 struct conflict_status_walker_baton
2907 svn_boolean_t resolve_text;
2908 const char *resolve_prop;
2909 svn_boolean_t resolve_tree;
2910 svn_wc_conflict_choice_t conflict_choice;
2911 svn_wc_conflict_resolver_func2_t conflict_func;
2912 void *conflict_baton;
2913 svn_cancel_func_t cancel_func;
2915 svn_wc_notify_func2_t notify_func;
2917 svn_boolean_t resolved_one;
2918 apr_hash_t *resolve_later;
2921 /* Implements svn_wc_notify_func2_t to collect new conflicts caused by
2922 resolving a tree conflict. */
2924 tree_conflict_collector(void *baton,
2925 const svn_wc_notify_t *notify,
2928 struct conflict_status_walker_baton *cswb = baton;
2930 if (cswb->notify_func)
2931 cswb->notify_func(cswb->notify_baton, notify, pool);
2933 if (cswb->resolve_later
2934 && (notify->action == svn_wc_notify_tree_conflict
2935 || notify->prop_state == svn_wc_notify_state_conflicted
2936 || notify->content_state == svn_wc_notify_state_conflicted))
2938 if (!svn_hash_gets(cswb->resolve_later, notify->path))
2940 const char *dup_path;
2942 dup_path = apr_pstrdup(apr_hash_pool_get(cswb->resolve_later),
2945 svn_hash_sets(cswb->resolve_later, dup_path, dup_path);
2950 /* Implements svn_wc_status4_t to walk all conflicts to resolve.
2952 static svn_error_t *
2953 conflict_status_walker(void *baton,
2954 const char *local_abspath,
2955 const svn_wc_status3_t *status,
2956 apr_pool_t *scratch_pool)
2958 struct conflict_status_walker_baton *cswb = baton;
2959 svn_wc__db_t *db = cswb->db;
2961 const apr_array_header_t *conflicts;
2962 apr_pool_t *iterpool;
2964 svn_boolean_t resolved = FALSE;
2965 svn_skel_t *conflict;
2967 if (!status->conflicted)
2968 return SVN_NO_ERROR;
2970 iterpool = svn_pool_create(scratch_pool);
2972 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict,
2974 (cswb->conflict_func != NULL) /* tmp files */,
2975 FALSE /* only tree conflicts */,
2976 scratch_pool, iterpool));
2978 for (i = 0; i < conflicts->nelts; i++)
2980 const svn_wc_conflict_description2_t *cd;
2981 svn_boolean_t did_resolve;
2982 svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
2983 svn_wc_conflict_result_t *result = NULL;
2984 svn_skel_t *work_items;
2986 cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
2988 if ((cd->kind == svn_wc_conflict_kind_property
2989 && (!cswb->resolve_prop
2990 || (*cswb->resolve_prop != '\0'
2991 && strcmp(cswb->resolve_prop, cd->property_name) != 0)))
2992 || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text)
2993 || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree))
2995 continue; /* Easy out. Don't call resolver func and ignore result */
2998 svn_pool_clear(iterpool);
3000 if (my_choice == svn_wc_conflict_choose_unspecified)
3002 if (!cswb->conflict_func)
3003 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3004 _("No conflict-callback and no "
3005 "pre-defined conflict-choice provided"));
3007 SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton,
3008 iterpool, iterpool));
3010 my_choice = result->choice;
3014 if (my_choice == svn_wc_conflict_choose_postpone)
3019 case svn_wc_conflict_kind_tree:
3020 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
3022 local_abspath, conflict,
3024 cswb->resolve_later,
3025 tree_conflict_collector,
3035 case svn_wc_conflict_kind_text:
3036 SVN_ERR(build_text_conflict_resolve_items(
3039 db, local_abspath, conflict,
3041 result ? result->merged_file
3043 result ? result->save_merged
3045 NULL /* merge_options */,
3048 iterpool, iterpool));
3050 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
3052 work_items, iterpool));
3053 SVN_ERR(svn_wc__wq_run(db, local_abspath,
3054 cswb->cancel_func, cswb->cancel_baton,
3058 case svn_wc_conflict_kind_property:
3059 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
3066 ? result->merged_file
3069 ? result->merged_value
3080 /* We can't resolve other conflict types */
3086 if (cswb->notify_func && resolved)
3087 cswb->notify_func(cswb->notify_baton,
3088 svn_wc_create_notify(local_abspath,
3089 svn_wc_notify_resolved,
3094 cswb->resolved_one = TRUE;
3096 svn_pool_destroy(iterpool);
3098 return SVN_NO_ERROR;
3102 svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
3103 const char *local_abspath,
3105 svn_boolean_t resolve_text,
3106 const char *resolve_prop,
3107 svn_boolean_t resolve_tree,
3108 svn_wc_conflict_choice_t conflict_choice,
3109 svn_wc_conflict_resolver_func2_t conflict_func,
3110 void *conflict_baton,
3111 svn_cancel_func_t cancel_func,
3113 svn_wc_notify_func2_t notify_func,
3115 apr_pool_t *scratch_pool)
3117 svn_node_kind_t kind;
3118 svn_boolean_t conflicted;
3119 struct conflict_status_walker_baton cswb;
3120 apr_pool_t *iterpool = NULL;
3123 /* ### Just a versioned check? */
3124 /* Conflicted is set to allow invoking on actual only nodes */
3125 SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
3126 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3127 NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
3128 NULL, NULL, NULL, NULL, NULL, NULL,
3129 wc_ctx->db, local_abspath,
3130 scratch_pool, scratch_pool));
3132 /* When the implementation still used the entry walker, depth
3133 unknown was translated to infinity. */
3134 if (kind != svn_node_dir)
3135 depth = svn_depth_empty;
3136 else if (depth == svn_depth_unknown)
3137 depth = svn_depth_infinity;
3139 cswb.db = wc_ctx->db;
3140 cswb.resolve_text = resolve_text;
3141 cswb.resolve_prop = resolve_prop;
3142 cswb.resolve_tree = resolve_tree;
3143 cswb.conflict_choice = conflict_choice;
3145 cswb.conflict_func = conflict_func;
3146 cswb.conflict_baton = conflict_baton;
3148 cswb.cancel_func = cancel_func;
3149 cswb.cancel_baton = cancel_baton;
3151 cswb.notify_func = notify_func;
3152 cswb.notify_baton = notify_baton;
3154 cswb.resolved_one = FALSE;
3155 cswb.resolve_later = (depth != svn_depth_empty)
3156 ? apr_hash_make(scratch_pool)
3160 notify_func(notify_baton,
3161 svn_wc_create_notify(local_abspath,
3162 svn_wc_notify_conflict_resolver_starting,
3166 err = svn_wc_walk_status(wc_ctx,
3169 FALSE /* get_all */,
3170 FALSE /* no_ignore */,
3171 TRUE /* ignore_text_mods */,
3172 NULL /* ignore_patterns */,
3173 conflict_status_walker, &cswb,
3174 cancel_func, cancel_baton,
3177 /* If we got new tree conflicts (or delayed conflicts) during the initial
3178 walk, we now walk them one by one as closure. */
3179 while (!err && cswb.resolve_later && apr_hash_count(cswb.resolve_later))
3181 apr_hash_index_t *hi;
3182 svn_wc_status3_t *status = NULL;
3183 const char *tc_abspath = NULL;
3186 svn_pool_clear(iterpool);
3188 iterpool = svn_pool_create(scratch_pool);
3190 hi = apr_hash_first(scratch_pool, cswb.resolve_later);
3191 cswb.resolve_later = apr_hash_make(scratch_pool);
3192 cswb.resolved_one = FALSE;
3194 for (; hi && !err; hi = apr_hash_next(hi))
3196 const char *relpath;
3197 svn_pool_clear(iterpool);
3199 tc_abspath = apr_hash_this_key(hi);
3202 SVN_ERR(cancel_func(cancel_baton));
3204 relpath = svn_dirent_skip_ancestor(local_abspath,
3208 || (depth >= svn_depth_empty
3209 && depth < svn_depth_infinity
3210 && strchr(relpath, '/')))
3215 SVN_ERR(svn_wc_status3(&status, wc_ctx, tc_abspath,
3216 iterpool, iterpool));
3218 if (depth == svn_depth_files
3219 && status->kind == svn_node_dir)
3222 err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3223 status, scratch_pool));
3226 /* None of the remaining conflicts got resolved, and non did provide
3229 We can fix that if we disable the 'resolve_later' option...
3231 if (!cswb.resolved_one && !err && tc_abspath
3232 && apr_hash_count(cswb.resolve_later))
3234 /* Run the last resolve operation again. We still have status
3235 and tc_abspath for that one. */
3237 cswb.resolve_later = NULL; /* Produce proper error! */
3239 /* Recreate the error */
3240 err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3241 status, scratch_pool));
3243 SVN_ERR_ASSERT(err != NULL);
3245 err = svn_error_createf(
3246 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3247 _("Unable to resolve pending conflict on '%s'"),
3248 svn_dirent_local_style(tc_abspath, scratch_pool));
3254 svn_pool_destroy(iterpool);
3256 if (err && err->apr_err != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE)
3257 err = svn_error_createf(
3258 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3259 _("Unable to resolve conflicts on '%s'"),
3260 svn_dirent_local_style(local_abspath, scratch_pool));
3265 notify_func(notify_baton,
3266 svn_wc_create_notify(local_abspath,
3267 svn_wc_notify_conflict_resolver_done,
3271 return SVN_NO_ERROR;
3275 svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
3276 const char *local_abspath,
3278 svn_boolean_t resolve_text,
3279 const char *resolve_prop,
3280 svn_boolean_t resolve_tree,
3281 svn_wc_conflict_choice_t conflict_choice,
3282 svn_cancel_func_t cancel_func,
3284 svn_wc_notify_func2_t notify_func,
3286 apr_pool_t *scratch_pool)
3288 return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath,
3289 depth, resolve_text,
3290 resolve_prop, resolve_tree,
3293 cancel_func, cancel_baton,
3294 notify_func, notify_baton,
3298 /* Constructor for the result-structure returned by conflict callbacks. */
3299 svn_wc_conflict_result_t *
3300 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
3301 const char *merged_file,
3304 svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
3305 result->choice = choice;
3306 result->merged_file = apr_pstrdup(pool, merged_file);
3307 result->save_merged = FALSE;
3309 /* If we add more fields to svn_wc_conflict_result_t, add them here. */