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 /* In case of selecting to resolve the conflict choosing the full
1626 own file, allow the text conflict resolution to just take the
1627 existing local file if no merged file was present (case: binary
1628 file conflicts do not generate a locally merge file).
1630 install_from_abspath = mine_abspath
1635 case svn_wc_conflict_choose_theirs_conflict:
1636 case svn_wc_conflict_choose_mine_conflict:
1638 svn_diff_conflict_display_style_t style
1639 = choice == svn_wc_conflict_choose_theirs_conflict
1640 ? svn_diff_conflict_display_latest
1641 : svn_diff_conflict_display_modified;
1643 if (mine_abspath == NULL)
1644 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1645 _("Conflict on '%s' cannot be resolved to "
1646 "'theirs-conflict' or 'mine-conflict' "
1647 "because a merged version of the file "
1648 "cannot be created."),
1649 svn_dirent_local_style(local_abspath,
1652 SVN_ERR(merge_showing_conflicts(&install_from_abspath,
1654 style, merge_options,
1658 /* ### why not same as other caller? */
1659 svn_io_file_del_none,
1660 cancel_func, cancel_baton,
1661 scratch_pool, scratch_pool));
1662 remove_source = TRUE;
1666 /* For the case of 3-way file merging, we don't
1667 really distinguish between these return values;
1668 if the callback claims to have "generally
1669 resolved" the situation, we still interpret
1670 that as "OK, we'll assume the merged version is
1672 case svn_wc_conflict_choose_merged:
1674 install_from_abspath = merged_file
1679 case svn_wc_conflict_choose_postpone:
1681 /* Assume conflict remains. */
1682 return SVN_NO_ERROR;
1685 SVN_ERR_ASSERT(choice == svn_wc_conflict_choose_postpone);
1688 if (install_from_abspath == NULL)
1689 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1690 _("Conflict on '%s' could not be resolved "
1691 "because the chosen version of the file "
1692 "is not available."),
1693 svn_dirent_local_style(local_abspath,
1696 /* ### It would be nice if we could somehow pass RECORD_FILEINFO
1697 as true in some easy cases. */
1698 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
1700 install_from_abspath,
1701 FALSE /* use_commit_times */,
1702 FALSE /* record_fileinfo */,
1703 result_pool, scratch_pool));
1704 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1708 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
1710 install_from_abspath,
1711 result_pool, scratch_pool));
1712 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1715 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1718 result_pool, scratch_pool));
1719 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1721 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1724 result_pool, scratch_pool));
1725 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1727 SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1730 result_pool, scratch_pool));
1731 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1733 return SVN_NO_ERROR;
1737 /* Set *DESC to a new description of the text conflict in
1738 * CONFLICT_SKEL. If there is no text conflict in CONFLICT_SKEL, return
1741 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1742 * rather than reading them from CONFLICT_SKEL. Use IS_BINARY and
1743 * MIME_TYPE for the corresponding fields of *DESC.
1745 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
1747 static svn_error_t *
1748 read_text_conflict_desc(svn_wc_conflict_description2_t **desc,
1750 const char *local_abspath,
1751 const svn_skel_t *conflict_skel,
1752 const char *mime_type,
1753 svn_wc_operation_t operation,
1754 const svn_wc_conflict_version_t *left_version,
1755 const svn_wc_conflict_version_t *right_version,
1756 apr_pool_t *result_pool,
1757 apr_pool_t *scratch_pool)
1759 *desc = svn_wc_conflict_description_create_text2(local_abspath, result_pool);
1760 (*desc)->mime_type = mime_type;
1761 (*desc)->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
1762 (*desc)->operation = operation;
1763 (*desc)->src_left_version = left_version;
1764 (*desc)->src_right_version = right_version;
1766 SVN_ERR(svn_wc__conflict_read_text_conflict(&(*desc)->my_abspath,
1767 &(*desc)->base_abspath,
1768 &(*desc)->their_abspath,
1771 result_pool, scratch_pool));
1772 (*desc)->merged_file = apr_pstrdup(result_pool, local_abspath);
1774 return SVN_NO_ERROR;
1777 /* Set *CONFLICT_DESC to a new description of the tree conflict in
1778 * CONFLICT_SKEL. If there is no tree conflict in CONFLICT_SKEL, return
1781 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1782 * rather than reading them from CONFLICT_SKEL.
1784 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
1786 static svn_error_t *
1787 read_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
1789 const char *local_abspath,
1790 svn_node_kind_t node_kind,
1791 const svn_skel_t *conflict_skel,
1792 svn_wc_operation_t operation,
1793 const svn_wc_conflict_version_t *left_version,
1794 const svn_wc_conflict_version_t *right_version,
1795 apr_pool_t *result_pool,
1796 apr_pool_t *scratch_pool)
1798 svn_node_kind_t local_kind;
1799 svn_wc_conflict_reason_t reason;
1800 svn_wc_conflict_action_t action;
1802 SVN_ERR(svn_wc__conflict_read_tree_conflict(
1803 &reason, &action, NULL,
1804 db, local_abspath, conflict_skel, scratch_pool, scratch_pool));
1806 if (reason == svn_wc_conflict_reason_missing)
1807 local_kind = svn_node_none;
1808 else if (reason == svn_wc_conflict_reason_unversioned ||
1809 reason == svn_wc_conflict_reason_obstructed)
1810 SVN_ERR(svn_io_check_path(local_abspath, &local_kind, scratch_pool));
1811 else if (action == svn_wc_conflict_action_delete
1813 && (operation == svn_wc_operation_update
1814 ||operation == svn_wc_operation_switch)
1815 && (reason == svn_wc_conflict_reason_deleted
1816 || reason == svn_wc_conflict_reason_moved_away))
1818 /* We have nothing locally to take the kind from */
1819 local_kind = left_version->node_kind;
1822 local_kind = node_kind;
1824 *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind,
1826 left_version, right_version,
1828 (*desc)->reason = reason;
1829 (*desc)->action = action;
1831 return SVN_NO_ERROR;
1834 /* Forward definition */
1835 static svn_error_t *
1836 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
1838 const char *local_abspath,
1839 const svn_skel_t *conflict,
1840 svn_wc_conflict_choice_t conflict_choice,
1841 apr_hash_t *resolve_later,
1842 svn_wc_notify_func2_t notify_func,
1844 svn_cancel_func_t cancel_func,
1846 apr_pool_t *scratch_pool);
1849 svn_wc__conflict_invoke_resolver(svn_wc__db_t *db,
1850 const char *local_abspath,
1851 svn_node_kind_t kind,
1852 const svn_skel_t *conflict_skel,
1853 const apr_array_header_t *merge_options,
1854 svn_wc_conflict_resolver_func2_t resolver_func,
1855 void *resolver_baton,
1856 svn_cancel_func_t cancel_func,
1858 apr_pool_t *scratch_pool)
1860 svn_boolean_t text_conflicted;
1861 svn_boolean_t prop_conflicted;
1862 svn_boolean_t tree_conflicted;
1863 svn_wc_operation_t operation;
1864 const apr_array_header_t *locations;
1865 const svn_wc_conflict_version_t *left_version = NULL;
1866 const svn_wc_conflict_version_t *right_version = NULL;
1868 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
1869 &text_conflicted, &prop_conflicted,
1871 db, local_abspath, conflict_skel,
1872 scratch_pool, scratch_pool));
1874 if (locations && locations->nelts > 0)
1875 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
1877 if (locations && locations->nelts > 1)
1878 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
1880 /* Quick and dirty compatibility wrapper. My guess would be that most resolvers
1881 would want to look at all properties at the same time.
1883 ### svn currently only invokes this from the merge code to collect the list of
1884 ### conflicted paths. Eventually this code will be the base for 'svn resolve'
1885 ### and at that time the test coverage will improve
1887 if (prop_conflicted)
1889 apr_hash_t *old_props;
1890 apr_hash_t *mine_props;
1891 apr_hash_t *their_props;
1892 apr_hash_t *old_their_props;
1893 apr_hash_t *conflicted;
1894 apr_pool_t *iterpool;
1895 apr_hash_index_t *hi;
1896 svn_boolean_t mark_resolved = TRUE;
1898 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
1905 scratch_pool, scratch_pool));
1907 if (operation == svn_wc_operation_merge)
1908 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
1909 scratch_pool, scratch_pool));
1911 old_props = old_their_props;
1913 iterpool = svn_pool_create(scratch_pool);
1915 for (hi = apr_hash_first(scratch_pool, conflicted);
1917 hi = apr_hash_next(hi))
1919 const char *propname = apr_hash_this_key(hi);
1920 svn_boolean_t conflict_remains = TRUE;
1922 svn_pool_clear(iterpool);
1925 SVN_ERR(cancel_func(cancel_baton));
1927 SVN_ERR(generate_propconflict(&conflict_remains,
1928 db, local_abspath, kind,
1934 ? svn_hash_gets(old_props, propname)
1937 ? svn_hash_gets(mine_props, propname)
1940 ? svn_hash_gets(old_their_props, propname)
1943 ? svn_hash_gets(their_props, propname)
1945 resolver_func, resolver_baton,
1946 cancel_func, cancel_baton,
1949 if (conflict_remains)
1950 mark_resolved = FALSE;
1955 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
1958 svn_pool_destroy(iterpool);
1961 if (text_conflicted)
1963 svn_skel_t *work_items;
1964 svn_boolean_t was_resolved;
1965 svn_wc_conflict_description2_t *desc;
1967 svn_wc_conflict_result_t *result;
1969 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
1970 scratch_pool, scratch_pool));
1972 SVN_ERR(read_text_conflict_desc(&desc,
1973 db, local_abspath, conflict_skel,
1974 svn_prop_get_value(props,
1975 SVN_PROP_MIME_TYPE),
1976 operation, left_version, right_version,
1977 scratch_pool, scratch_pool));
1981 was_resolved = FALSE;
1983 /* Give the conflict resolution callback a chance to clean
1984 up the conflicts before we mark the file 'conflicted' */
1986 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
1989 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1990 _("Conflict callback violated API:"
1991 " returned no results"));
1993 SVN_ERR(build_text_conflict_resolve_items(&work_items, &was_resolved,
1995 conflict_skel, result->choice,
1996 result->merged_file,
1997 result->save_merged,
1999 cancel_func, cancel_baton,
2000 scratch_pool, scratch_pool));
2002 if (result->choice != svn_wc_conflict_choose_postpone)
2004 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2006 work_items, scratch_pool));
2007 SVN_ERR(svn_wc__wq_run(db, local_abspath,
2008 cancel_func, cancel_baton,
2013 if (tree_conflicted)
2015 svn_wc_conflict_result_t *result;
2016 svn_wc_conflict_description2_t *desc;
2017 svn_boolean_t resolved;
2018 svn_node_kind_t node_kind;
2020 SVN_ERR(svn_wc__db_read_kind(&node_kind, db, local_abspath, TRUE,
2021 TRUE, FALSE, scratch_pool));
2023 SVN_ERR(read_tree_conflict_desc(&desc,
2024 db, local_abspath, node_kind,
2026 operation, left_version, right_version,
2027 scratch_pool, scratch_pool));
2029 /* Tell the resolver func about this conflict. */
2030 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
2034 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2035 _("Conflict callback violated API:"
2036 " returned no results"));
2038 /* Pass retry hash to avoid erroring out on cases where update
2039 can continue safely. ### Need notify handling */
2040 if (result->choice != svn_wc_conflict_choose_postpone)
2041 SVN_ERR(resolve_tree_conflict_on_node(&resolved,
2042 db, local_abspath, conflict_skel,
2044 apr_hash_make(scratch_pool),
2045 NULL, NULL, /* ### notify */
2046 cancel_func, cancel_baton,
2050 return SVN_NO_ERROR;
2053 /* Read all property conflicts contained in CONFLICT_SKEL into
2054 * individual conflict descriptions, and append those descriptions
2055 * to the CONFLICTS array. If there is no property conflict in
2056 * CONFLICT_SKEL, return an error.
2058 * If NOT create_tempfiles, always create a legacy property conflict
2061 * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
2062 * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
2064 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
2066 static svn_error_t *
2067 read_prop_conflict_descs(apr_array_header_t *conflicts,
2069 const char *local_abspath,
2070 svn_skel_t *conflict_skel,
2071 svn_boolean_t create_tempfiles,
2072 svn_node_kind_t node_kind,
2073 svn_wc_operation_t operation,
2074 const svn_wc_conflict_version_t *left_version,
2075 const svn_wc_conflict_version_t *right_version,
2076 apr_pool_t *result_pool,
2077 apr_pool_t *scratch_pool)
2079 const char *prop_reject_abspath;
2080 apr_hash_t *base_props;
2081 apr_hash_t *my_props;
2082 apr_hash_t *their_old_props;
2083 apr_hash_t *their_props;
2084 apr_hash_t *conflicted_props;
2085 apr_hash_index_t *hi;
2086 apr_pool_t *iterpool;
2087 svn_boolean_t prop_conflicted;
2089 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2090 NULL, db, local_abspath, conflict_skel,
2091 scratch_pool, scratch_pool));
2093 if (!prop_conflicted)
2094 return SVN_NO_ERROR;
2096 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath,
2103 scratch_pool, scratch_pool));
2105 prop_reject_abspath = apr_pstrdup(result_pool, prop_reject_abspath);
2107 if (apr_hash_count(conflicted_props) == 0)
2109 /* Legacy prop conflict with only a .reject file. */
2110 svn_wc_conflict_description2_t *desc;
2112 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2116 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2117 * ### their_abspath, and stores theirs_abspath in merged_file. */
2118 desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2119 desc->their_abspath = desc->prop_reject_abspath;
2121 desc->operation = operation;
2122 desc->src_left_version = left_version;
2123 desc->src_right_version = right_version;
2125 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2127 return SVN_NO_ERROR;
2130 if (operation == svn_wc_operation_merge)
2131 SVN_ERR(svn_wc__db_read_pristine_props(&base_props, db, local_abspath,
2132 result_pool, scratch_pool));
2135 iterpool = svn_pool_create(scratch_pool);
2136 for (hi = apr_hash_first(scratch_pool, conflicted_props);
2138 hi = apr_hash_next(hi))
2140 const char *propname = apr_hash_this_key(hi);
2141 svn_string_t *old_value;
2142 svn_string_t *my_value;
2143 svn_string_t *their_value;
2144 svn_wc_conflict_description2_t *desc;
2146 svn_pool_clear(iterpool);
2148 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2153 desc->operation = operation;
2154 desc->src_left_version = left_version;
2155 desc->src_right_version = right_version;
2157 desc->property_name = apr_pstrdup(result_pool, propname);
2159 my_value = svn_hash_gets(my_props, propname);
2160 their_value = svn_hash_gets(their_props, propname);
2161 old_value = svn_hash_gets(their_old_props, propname);
2163 /* Compute the incoming side of the conflict ('action'). */
2164 if (their_value == NULL)
2165 desc->action = svn_wc_conflict_action_delete;
2166 else if (old_value == NULL)
2167 desc->action = svn_wc_conflict_action_add;
2169 desc->action = svn_wc_conflict_action_edit;
2171 /* Compute the local side of the conflict ('reason'). */
2172 if (my_value == NULL)
2173 desc->reason = svn_wc_conflict_reason_deleted;
2174 else if (old_value == NULL)
2175 desc->reason = svn_wc_conflict_reason_added;
2177 desc->reason = svn_wc_conflict_reason_edited;
2179 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2180 * ### their_abspath, and stores theirs_abspath in merged_file. */
2181 desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2182 desc->their_abspath = desc->prop_reject_abspath;
2184 desc->prop_value_base = base_props ? svn_hash_gets(base_props, propname)
2185 : desc->prop_value_incoming_old;
2192 if (create_tempfiles)
2194 SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL,
2195 svn_io_file_del_on_pool_cleanup,
2196 result_pool, iterpool));
2197 len = my_value->len;
2198 SVN_ERR(svn_stream_write(s, my_value->data, &len));
2199 SVN_ERR(svn_stream_close(s));
2202 desc->prop_value_working = svn_string_dup(my_value, result_pool);
2210 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2211 * ### their_abspath, and stores theirs_abspath in merged_file. */
2212 if (create_tempfiles)
2214 SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL,
2215 svn_io_file_del_on_pool_cleanup,
2216 result_pool, iterpool));
2217 len = their_value->len;
2218 SVN_ERR(svn_stream_write(s, their_value->data, &len));
2219 SVN_ERR(svn_stream_close(s));
2222 desc->prop_value_incoming_new = svn_string_dup(their_value, result_pool);
2230 if (create_tempfiles)
2232 SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL,
2233 svn_io_file_del_on_pool_cleanup,
2234 result_pool, iterpool));
2235 len = old_value->len;
2236 SVN_ERR(svn_stream_write(s, old_value->data, &len));
2237 SVN_ERR(svn_stream_close(s));
2240 desc->prop_value_incoming_old = svn_string_dup(old_value, result_pool);
2243 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2245 svn_pool_destroy(iterpool);
2247 return SVN_NO_ERROR;
2251 svn_wc__read_conflicts(const apr_array_header_t **conflicts,
2252 svn_skel_t **conflict_skel,
2254 const char *local_abspath,
2255 svn_boolean_t create_tempfiles,
2256 svn_boolean_t only_tree_conflict,
2257 apr_pool_t *result_pool,
2258 apr_pool_t *scratch_pool)
2260 svn_skel_t *the_conflict_skel;
2261 apr_array_header_t *cflcts;
2262 svn_boolean_t prop_conflicted;
2263 svn_boolean_t text_conflicted;
2264 svn_boolean_t tree_conflicted;
2265 svn_wc_operation_t operation;
2266 const apr_array_header_t *locations;
2267 const svn_wc_conflict_version_t *left_version = NULL;
2268 const svn_wc_conflict_version_t *right_version = NULL;
2269 svn_node_kind_t node_kind;
2273 conflict_skel = &the_conflict_skel;
2275 SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props,
2277 (conflict_skel == &the_conflict_skel)
2282 if (!*conflict_skel)
2284 /* Some callers expect not NULL */
2285 *conflicts = apr_array_make(result_pool, 0,
2286 sizeof(svn_wc_conflict_description2_t *));
2287 return SVN_NO_ERROR;
2290 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted,
2291 &prop_conflicted, &tree_conflicted,
2292 db, local_abspath, *conflict_skel,
2293 result_pool, scratch_pool));
2295 cflcts = apr_array_make(result_pool, 4,
2296 sizeof(svn_wc_conflict_description2_t *));
2298 if (locations && locations->nelts > 0)
2299 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
2300 if (locations && locations->nelts > 1)
2301 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
2303 if (prop_conflicted && !only_tree_conflict)
2305 SVN_ERR(read_prop_conflict_descs(cflcts,
2306 db, local_abspath, *conflict_skel,
2307 create_tempfiles, node_kind,
2308 operation, left_version, right_version,
2309 result_pool, scratch_pool));
2312 if (text_conflicted && !only_tree_conflict)
2314 svn_wc_conflict_description2_t *desc;
2316 SVN_ERR(read_text_conflict_desc(&desc,
2317 db, local_abspath, *conflict_skel,
2318 svn_prop_get_value(props,
2319 SVN_PROP_MIME_TYPE),
2320 operation, left_version, right_version,
2321 result_pool, scratch_pool));
2322 APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t *) = desc;
2325 if (tree_conflicted)
2327 svn_wc_conflict_description2_t *desc;
2329 SVN_ERR(read_tree_conflict_desc(&desc,
2330 db, local_abspath, node_kind,
2332 operation, left_version, right_version,
2333 result_pool, scratch_pool));
2335 APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc;
2338 *conflicts = cflcts;
2339 return SVN_NO_ERROR;
2343 /*** Resolving a conflict automatically ***/
2346 * Resolve the property conflicts found in DB/LOCAL_ABSPATH according
2347 * to CONFLICT_CHOICE.
2349 * It is not an error if there is no prop conflict. If a prop conflict
2350 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2352 * Note: When there are no conflict markers on-disk to remove there is
2353 * no existing text conflict (unless we are still in the process of
2354 * creating the text conflict and we didn't register a marker file yet).
2355 * In this case the database contains old information, which we should
2356 * remove to avoid checking the next time. Resolving a property conflict
2357 * by just removing the marker file is a fully supported scenario since
2360 * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
2361 * In my opinion, 'mine_full'/'theirs_full' should select
2362 * the entire set of properties from 'mine' or 'theirs' respectively,
2363 * while 'mine_conflict'/'theirs_conflict' should select just the
2364 * properties that are in conflict. Or, '_full' should select the
2365 * entire property whereas '_conflict' should do a text merge within
2366 * each property, selecting hunks. Or all three kinds of behaviour
2367 * should be available (full set of props, full value of conflicting
2368 * props, or conflicting text hunks).
2369 * ### BH: If we make *_full select the full set of properties, we should
2370 * check if we shouldn't make it also select the full text for files.
2372 * ### TODO [JAF] All this complexity should not be down here in libsvn_wc
2373 * but in a layer above.
2375 * ### TODO [JAF] Options for 'base' should be like options for 'mine' and
2376 * for 'theirs' -- choose full set of props, full value of conflicting
2377 * props, or conflicting text hunks.
2380 static svn_error_t *
2381 resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
2383 const char *local_abspath,
2384 svn_skel_t *conflicts,
2385 const char *conflicted_propname,
2386 svn_wc_conflict_choice_t conflict_choice,
2387 const char *merged_file,
2388 const svn_string_t *merged_value,
2389 svn_cancel_func_t cancel_func,
2391 apr_pool_t *scratch_pool)
2393 const char *prop_reject_file;
2394 apr_hash_t *mine_props;
2395 apr_hash_t *their_old_props;
2396 apr_hash_t *their_props;
2397 apr_hash_t *conflicted_props;
2398 apr_hash_t *old_props;
2399 apr_hash_t *resolve_from = NULL;
2400 svn_skel_t *work_items = NULL;
2401 svn_wc_operation_t operation;
2402 svn_boolean_t prop_conflicted;
2403 apr_hash_t *actual_props;
2404 svn_boolean_t resolved_all, resolved_all_prop;
2406 *did_resolve = FALSE;
2408 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2409 NULL, db, local_abspath, conflicts,
2410 scratch_pool, scratch_pool));
2411 if (!prop_conflicted)
2412 return SVN_NO_ERROR;
2414 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2415 &mine_props, &their_old_props,
2416 &their_props, &conflicted_props,
2417 db, local_abspath, conflicts,
2418 scratch_pool, scratch_pool));
2420 if (!conflicted_props)
2422 /* We have a pre 1.8 property conflict. Just mark it resolved */
2424 SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2425 db, local_abspath, prop_reject_file,
2426 scratch_pool, scratch_pool));
2427 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE,
2428 work_items, scratch_pool));
2429 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2431 return SVN_NO_ERROR;
2434 if (conflicted_propname[0] != '\0'
2435 && !svn_hash_gets(conflicted_props, conflicted_propname))
2437 return SVN_NO_ERROR; /* This property is not conflicted! */
2440 if (operation == svn_wc_operation_merge)
2441 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
2442 scratch_pool, scratch_pool));
2444 old_props = their_old_props;
2446 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2447 scratch_pool, scratch_pool));
2449 /* We currently handle *_conflict as *_full as this argument is currently
2450 always applied for all conflicts on a node at the same time. Giving
2451 an error would break some tests that assumed that this would just
2452 resolve property conflicts to working.
2454 An alternative way to handle these conflicts would be to just copy all
2455 property state from mine/theirs on the _full option instead of just the
2456 conflicted properties. In some ways this feels like a sensible option as
2457 that would take both properties and text from mine/theirs, but when not
2458 both properties and text are conflicted we would fail in doing so.
2460 switch (conflict_choice)
2462 case svn_wc_conflict_choose_base:
2463 resolve_from = their_old_props ? their_old_props : old_props;
2465 case svn_wc_conflict_choose_mine_full:
2466 case svn_wc_conflict_choose_mine_conflict:
2467 resolve_from = mine_props;
2469 case svn_wc_conflict_choose_theirs_full:
2470 case svn_wc_conflict_choose_theirs_conflict:
2471 resolve_from = their_props;
2473 case svn_wc_conflict_choose_merged:
2474 if ((merged_file || merged_value) && conflicted_propname[0] != '\0')
2476 resolve_from = apr_hash_copy(scratch_pool, actual_props);
2480 svn_stream_t *stream;
2481 svn_string_t *merged_propval;
2483 SVN_ERR(svn_stream_open_readonly(&stream, merged_file,
2484 scratch_pool, scratch_pool));
2485 SVN_ERR(svn_string_from_stream(&merged_propval, stream,
2486 scratch_pool, scratch_pool));
2488 merged_value = merged_propval;
2490 svn_hash_sets(resolve_from, conflicted_propname, merged_value);
2493 resolve_from = NULL;
2496 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2497 _("Invalid 'conflict_result' argument"));
2503 apr_hash_index_t *hi;
2504 apr_hash_t *apply_on_props;
2506 if (conflicted_propname[0] == '\0')
2508 /* Apply to all conflicted properties */
2509 apply_on_props = conflicted_props;
2513 /* Apply to a single property */
2514 apply_on_props = apr_hash_make(scratch_pool);
2515 svn_hash_sets(apply_on_props, conflicted_propname, "");
2518 /* Apply the selected changes */
2519 for (hi = apr_hash_first(scratch_pool, apply_on_props);
2521 hi = apr_hash_next(hi))
2523 const char *propname = apr_hash_this_key(hi);
2524 svn_string_t *new_value = NULL;
2526 new_value = svn_hash_gets(resolve_from, propname);
2528 svn_hash_sets(actual_props, propname, new_value);
2531 /*else the user accepted the properties as-is */
2533 /* This function handles conflicted_propname "" as resolving
2534 all property conflicts... Just what we need here */
2535 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
2537 FALSE, conflicted_propname,
2539 scratch_pool, scratch_pool));
2543 /* Are there still property conflicts left? (or only...) */
2544 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, &prop_conflicted,
2545 NULL, db, local_abspath, conflicts,
2546 scratch_pool, scratch_pool));
2548 resolved_all_prop = (! prop_conflicted);
2552 resolved_all_prop = TRUE;
2556 if (resolved_all_prop)
2558 /* Legacy behavior: Only report property conflicts as resolved when the
2559 property reject file exists
2561 If not the UI shows the conflict as already resolved
2562 (and in this case we just remove the in-db conflict) */
2563 SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2566 scratch_pool, scratch_pool));
2570 /* Create a new prej file, based on the remaining conflicts */
2571 SVN_ERR(svn_wc__wq_build_prej_install(&work_items,
2573 scratch_pool, scratch_pool));
2574 *did_resolve = TRUE; /* We resolved a property conflict */
2577 /* This installs the updated conflict skel */
2578 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props,
2579 FALSE, conflicts, work_items,
2584 /* Remove the whole conflict. Should probably be integrated
2585 into the op_set_props() call */
2586 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2588 NULL, scratch_pool));
2591 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2594 return SVN_NO_ERROR;
2598 * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
2601 * It is not an error if there is no tree conflict. If a tree conflict
2602 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2604 * It is not an error if there is no tree conflict.
2606 * If the conflict can't be resolved yet because another tree conflict is
2607 * blocking a storage location, store the tree conflict in the RESOLVE_LATER
2610 static svn_error_t *
2611 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
2613 const char *local_abspath,
2614 const svn_skel_t *conflicts,
2615 svn_wc_conflict_choice_t conflict_choice,
2616 apr_hash_t *resolve_later,
2617 svn_wc_notify_func2_t notify_func,
2619 svn_cancel_func_t cancel_func,
2621 apr_pool_t *scratch_pool)
2623 svn_wc_conflict_reason_t reason;
2624 svn_wc_conflict_action_t action;
2625 svn_wc_operation_t operation;
2626 svn_boolean_t tree_conflicted;
2627 const char *src_op_root_abspath;
2629 *did_resolve = FALSE;
2631 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
2632 &tree_conflicted, db, local_abspath,
2633 conflicts, scratch_pool, scratch_pool));
2634 if (!tree_conflicted)
2635 return SVN_NO_ERROR;
2637 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2638 &src_op_root_abspath,
2641 scratch_pool, scratch_pool));
2643 if (operation == svn_wc_operation_update
2644 || operation == svn_wc_operation_switch)
2647 if (reason == svn_wc_conflict_reason_deleted ||
2648 reason == svn_wc_conflict_reason_replaced)
2650 if (conflict_choice == svn_wc_conflict_choose_merged)
2652 /* Break moves for any children moved out of this directory,
2653 * and leave this directory deleted. */
2655 if (action != svn_wc_conflict_action_delete)
2657 SVN_ERR(svn_wc__db_op_break_moved_away(
2658 db, local_abspath, src_op_root_abspath, TRUE,
2659 notify_func, notify_baton,
2661 *did_resolve = TRUE;
2662 return SVN_NO_ERROR; /* Marked resolved by function*/
2664 /* else # The move is/moves are already broken */
2667 *did_resolve = TRUE;
2669 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2671 svn_skel_t *new_conflicts;
2673 /* Raise moved-away conflicts on any children moved out of
2674 * this directory, and leave this directory as-is.
2676 * The newly conflicted moved-away children will be updated
2677 * if they are resolved with 'mine_conflict' as well. */
2678 err = svn_wc__db_op_raise_moved_away(
2679 db, local_abspath, notify_func, notify_baton,
2684 const char *dup_abspath;
2687 || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
2688 return svn_error_trace(err);
2690 svn_error_clear(err);
2691 dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2694 svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2696 return SVN_NO_ERROR; /* Retry after other conflicts */
2699 /* We might now have a moved-away on *this* path, let's
2700 try to resolve that directly if that is the case */
2701 SVN_ERR(svn_wc__db_read_conflict(&new_conflicts, NULL, NULL,
2703 scratch_pool, scratch_pool));
2706 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
2713 if (!new_conflicts || !tree_conflicted)
2715 /* TC is marked resolved by calling
2716 svn_wc__db_resolve_delete_raise_moved_away */
2717 *did_resolve = TRUE;
2718 return SVN_NO_ERROR;
2721 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2722 &src_op_root_abspath,
2728 if (reason != svn_wc_conflict_reason_moved_away)
2730 *did_resolve = TRUE;
2731 return SVN_NO_ERROR; /* We fixed one, but... */
2734 conflicts = new_conflicts;
2735 /* Fall through in moved_away handling */
2738 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2740 _("Tree conflict can only be resolved to "
2741 "'working' or 'mine-conflict' state; "
2742 "'%s' not resolved"),
2743 svn_dirent_local_style(local_abspath,
2747 if (reason == svn_wc_conflict_reason_moved_away
2748 && action == svn_wc_conflict_action_edit)
2750 /* After updates, we can resolve local moved-away
2751 * vs. any incoming change, either by updating the
2752 * moved-away node (mine-conflict) or by breaking the
2753 * move (theirs-conflict). */
2754 if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2756 err = svn_wc__db_update_moved_away_conflict_victim(
2757 db, local_abspath, src_op_root_abspath,
2758 operation, action, reason,
2759 cancel_func, cancel_baton,
2760 notify_func, notify_baton,
2765 const char *dup_abspath;
2768 || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
2769 return svn_error_trace(err);
2771 svn_error_clear(err);
2772 dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2775 svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2777 return SVN_NO_ERROR; /* Retry after other conflicts */
2780 *did_resolve = TRUE;
2782 else if (conflict_choice == svn_wc_conflict_choose_merged)
2784 /* We must break the move if the user accepts the current
2785 * working copy state instead of updating the move.
2786 * Else the move would be left in an invalid state. */
2788 SVN_ERR(svn_wc__db_op_break_moved_away(db, local_abspath,
2789 src_op_root_abspath, TRUE,
2790 notify_func, notify_baton,
2792 *did_resolve = TRUE;
2793 return SVN_NO_ERROR; /* Conflict is marked resolved */
2796 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2798 _("Tree conflict can only be resolved to "
2799 "'working' or 'mine-conflict' state; "
2800 "'%s' not resolved"),
2801 svn_dirent_local_style(local_abspath,
2804 else if (reason == svn_wc_conflict_reason_moved_away
2805 && action != svn_wc_conflict_action_edit)
2807 /* action added is impossible, because that would imply that
2808 something was added, but before that already moved...
2809 (which would imply a replace) */
2810 SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete
2811 || action == svn_wc_conflict_action_replace);
2813 if (conflict_choice == svn_wc_conflict_choose_merged)
2815 /* Whatever was moved is removed at its original location by the
2816 update. That must also remove the recording of the move, so
2817 we don't have to do anything here. */
2819 *did_resolve = TRUE;
2821 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2823 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2825 _("Tree conflict can only be "
2826 "resolved to 'working' state; "
2827 "'%s' is no longer moved"),
2828 svn_dirent_local_style(local_abspath,
2836 if (conflict_choice != svn_wc_conflict_choose_merged)
2838 /* For other tree conflicts, there is no way to pick
2839 * theirs-full or mine-full, etc. Throw an error if the
2840 * user expects us to be smarter than we really are. */
2841 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2843 _("Tree conflict can only be "
2844 "resolved to 'working' state; "
2845 "'%s' not resolved"),
2846 svn_dirent_local_style(local_abspath,
2850 *did_resolve = TRUE;
2853 SVN_ERR_ASSERT(*did_resolve);
2855 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE,
2856 NULL, scratch_pool));
2857 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2859 return SVN_NO_ERROR;
2863 svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
2864 const char *local_abspath,
2865 svn_cancel_func_t cancel_func,
2867 apr_pool_t *scratch_pool)
2869 svn_skel_t *work_items;
2870 svn_skel_t *conflict;
2872 SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
2874 scratch_pool, scratch_pool));
2877 return SVN_NO_ERROR;
2879 SVN_ERR(build_text_conflict_resolve_items(&work_items, NULL,
2880 db, local_abspath, conflict,
2881 svn_wc_conflict_choose_merged,
2883 cancel_func, cancel_baton,
2884 scratch_pool, scratch_pool));
2886 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, TRUE, FALSE, FALSE,
2887 work_items, scratch_pool));
2889 return svn_error_trace(svn_wc__wq_run(db, local_abspath,
2890 cancel_func, cancel_baton,
2895 svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db,
2896 const char *local_abspath,
2897 apr_pool_t *scratch_pool)
2899 svn_boolean_t ignored_result;
2900 svn_skel_t *conflicts;
2902 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
2904 scratch_pool, scratch_pool));
2907 return SVN_NO_ERROR;
2909 return svn_error_trace(resolve_prop_conflict_on_node(
2911 db, local_abspath, conflicts, "",
2912 svn_wc_conflict_choose_merged,
2919 /* Baton for conflict_status_walker */
2920 struct conflict_status_walker_baton
2923 svn_boolean_t resolve_text;
2924 const char *resolve_prop;
2925 svn_boolean_t resolve_tree;
2926 svn_wc_conflict_choice_t conflict_choice;
2927 svn_wc_conflict_resolver_func2_t conflict_func;
2928 void *conflict_baton;
2929 svn_cancel_func_t cancel_func;
2931 svn_wc_notify_func2_t notify_func;
2933 svn_boolean_t resolved_one;
2934 apr_hash_t *resolve_later;
2937 /* Implements svn_wc_notify_func2_t to collect new conflicts caused by
2938 resolving a tree conflict. */
2940 tree_conflict_collector(void *baton,
2941 const svn_wc_notify_t *notify,
2944 struct conflict_status_walker_baton *cswb = baton;
2946 if (cswb->notify_func)
2947 cswb->notify_func(cswb->notify_baton, notify, pool);
2949 if (cswb->resolve_later
2950 && (notify->action == svn_wc_notify_tree_conflict
2951 || notify->prop_state == svn_wc_notify_state_conflicted
2952 || notify->content_state == svn_wc_notify_state_conflicted))
2954 if (!svn_hash_gets(cswb->resolve_later, notify->path))
2956 const char *dup_path;
2958 dup_path = apr_pstrdup(apr_hash_pool_get(cswb->resolve_later),
2961 svn_hash_sets(cswb->resolve_later, dup_path, dup_path);
2966 /* Implements svn_wc_status4_t to walk all conflicts to resolve.
2968 static svn_error_t *
2969 conflict_status_walker(void *baton,
2970 const char *local_abspath,
2971 const svn_wc_status3_t *status,
2972 apr_pool_t *scratch_pool)
2974 struct conflict_status_walker_baton *cswb = baton;
2975 svn_wc__db_t *db = cswb->db;
2977 const apr_array_header_t *conflicts;
2978 apr_pool_t *iterpool;
2980 svn_boolean_t resolved = FALSE;
2981 svn_skel_t *conflict;
2983 if (!status->conflicted)
2984 return SVN_NO_ERROR;
2986 iterpool = svn_pool_create(scratch_pool);
2988 SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict,
2990 (cswb->conflict_func != NULL) /* tmp files */,
2991 FALSE /* only tree conflicts */,
2992 scratch_pool, iterpool));
2994 for (i = 0; i < conflicts->nelts; i++)
2996 const svn_wc_conflict_description2_t *cd;
2997 svn_boolean_t did_resolve;
2998 svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
2999 svn_wc_conflict_result_t *result = NULL;
3000 svn_skel_t *work_items;
3002 cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
3004 if ((cd->kind == svn_wc_conflict_kind_property
3005 && (!cswb->resolve_prop
3006 || (*cswb->resolve_prop != '\0'
3007 && strcmp(cswb->resolve_prop, cd->property_name) != 0)))
3008 || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text)
3009 || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree))
3011 continue; /* Easy out. Don't call resolver func and ignore result */
3014 svn_pool_clear(iterpool);
3016 if (my_choice == svn_wc_conflict_choose_unspecified)
3018 if (!cswb->conflict_func)
3019 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3020 _("No conflict-callback and no "
3021 "pre-defined conflict-choice provided"));
3023 SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton,
3024 iterpool, iterpool));
3026 my_choice = result->choice;
3030 if (my_choice == svn_wc_conflict_choose_postpone)
3035 case svn_wc_conflict_kind_tree:
3036 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
3038 local_abspath, conflict,
3040 cswb->resolve_later,
3041 tree_conflict_collector,
3051 case svn_wc_conflict_kind_text:
3052 SVN_ERR(build_text_conflict_resolve_items(
3055 db, local_abspath, conflict,
3057 result ? result->merged_file
3059 result ? result->save_merged
3061 NULL /* merge_options */,
3064 iterpool, iterpool));
3066 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
3068 work_items, iterpool));
3069 SVN_ERR(svn_wc__wq_run(db, local_abspath,
3070 cswb->cancel_func, cswb->cancel_baton,
3074 case svn_wc_conflict_kind_property:
3075 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
3082 ? result->merged_file
3085 ? result->merged_value
3096 /* We can't resolve other conflict types */
3102 if (cswb->notify_func && resolved)
3103 cswb->notify_func(cswb->notify_baton,
3104 svn_wc_create_notify(local_abspath,
3105 svn_wc_notify_resolved,
3110 cswb->resolved_one = TRUE;
3112 svn_pool_destroy(iterpool);
3114 return SVN_NO_ERROR;
3118 svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
3119 const char *local_abspath,
3121 svn_boolean_t resolve_text,
3122 const char *resolve_prop,
3123 svn_boolean_t resolve_tree,
3124 svn_wc_conflict_choice_t conflict_choice,
3125 svn_wc_conflict_resolver_func2_t conflict_func,
3126 void *conflict_baton,
3127 svn_cancel_func_t cancel_func,
3129 svn_wc_notify_func2_t notify_func,
3131 apr_pool_t *scratch_pool)
3133 svn_node_kind_t kind;
3134 svn_boolean_t conflicted;
3135 struct conflict_status_walker_baton cswb;
3136 apr_pool_t *iterpool = NULL;
3139 /* ### Just a versioned check? */
3140 /* Conflicted is set to allow invoking on actual only nodes */
3141 SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
3142 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3143 NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
3144 NULL, NULL, NULL, NULL, NULL, NULL,
3145 wc_ctx->db, local_abspath,
3146 scratch_pool, scratch_pool));
3148 /* When the implementation still used the entry walker, depth
3149 unknown was translated to infinity. */
3150 if (kind != svn_node_dir)
3151 depth = svn_depth_empty;
3152 else if (depth == svn_depth_unknown)
3153 depth = svn_depth_infinity;
3155 cswb.db = wc_ctx->db;
3156 cswb.resolve_text = resolve_text;
3157 cswb.resolve_prop = resolve_prop;
3158 cswb.resolve_tree = resolve_tree;
3159 cswb.conflict_choice = conflict_choice;
3161 cswb.conflict_func = conflict_func;
3162 cswb.conflict_baton = conflict_baton;
3164 cswb.cancel_func = cancel_func;
3165 cswb.cancel_baton = cancel_baton;
3167 cswb.notify_func = notify_func;
3168 cswb.notify_baton = notify_baton;
3170 cswb.resolved_one = FALSE;
3171 cswb.resolve_later = (depth != svn_depth_empty)
3172 ? apr_hash_make(scratch_pool)
3176 notify_func(notify_baton,
3177 svn_wc_create_notify(local_abspath,
3178 svn_wc_notify_conflict_resolver_starting,
3182 err = svn_wc_walk_status(wc_ctx,
3185 FALSE /* get_all */,
3186 FALSE /* no_ignore */,
3187 TRUE /* ignore_text_mods */,
3188 NULL /* ignore_patterns */,
3189 conflict_status_walker, &cswb,
3190 cancel_func, cancel_baton,
3193 /* If we got new tree conflicts (or delayed conflicts) during the initial
3194 walk, we now walk them one by one as closure. */
3195 while (!err && cswb.resolve_later && apr_hash_count(cswb.resolve_later))
3197 apr_hash_index_t *hi;
3198 svn_wc_status3_t *status = NULL;
3199 const char *tc_abspath = NULL;
3202 svn_pool_clear(iterpool);
3204 iterpool = svn_pool_create(scratch_pool);
3206 hi = apr_hash_first(scratch_pool, cswb.resolve_later);
3207 cswb.resolve_later = apr_hash_make(scratch_pool);
3208 cswb.resolved_one = FALSE;
3210 for (; hi && !err; hi = apr_hash_next(hi))
3212 const char *relpath;
3213 svn_pool_clear(iterpool);
3215 tc_abspath = apr_hash_this_key(hi);
3218 SVN_ERR(cancel_func(cancel_baton));
3220 relpath = svn_dirent_skip_ancestor(local_abspath,
3224 || (depth >= svn_depth_empty
3225 && depth < svn_depth_infinity
3226 && strchr(relpath, '/')))
3231 SVN_ERR(svn_wc_status3(&status, wc_ctx, tc_abspath,
3232 iterpool, iterpool));
3234 if (depth == svn_depth_files
3235 && status->kind == svn_node_dir)
3238 err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3239 status, scratch_pool));
3242 /* None of the remaining conflicts got resolved, and non did provide
3245 We can fix that if we disable the 'resolve_later' option...
3247 if (!cswb.resolved_one && !err && tc_abspath
3248 && apr_hash_count(cswb.resolve_later))
3250 /* Run the last resolve operation again. We still have status
3251 and tc_abspath for that one. */
3253 cswb.resolve_later = NULL; /* Produce proper error! */
3255 /* Recreate the error */
3256 err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3257 status, scratch_pool));
3259 SVN_ERR_ASSERT(err != NULL);
3261 err = svn_error_createf(
3262 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3263 _("Unable to resolve pending conflict on '%s'"),
3264 svn_dirent_local_style(tc_abspath, scratch_pool));
3270 svn_pool_destroy(iterpool);
3272 if (err && err->apr_err != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE)
3273 err = svn_error_createf(
3274 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3275 _("Unable to resolve conflicts on '%s'"),
3276 svn_dirent_local_style(local_abspath, scratch_pool));
3281 notify_func(notify_baton,
3282 svn_wc_create_notify(local_abspath,
3283 svn_wc_notify_conflict_resolver_done,
3287 return SVN_NO_ERROR;
3291 svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
3292 const char *local_abspath,
3294 svn_boolean_t resolve_text,
3295 const char *resolve_prop,
3296 svn_boolean_t resolve_tree,
3297 svn_wc_conflict_choice_t conflict_choice,
3298 svn_cancel_func_t cancel_func,
3300 svn_wc_notify_func2_t notify_func,
3302 apr_pool_t *scratch_pool)
3304 return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath,
3305 depth, resolve_text,
3306 resolve_prop, resolve_tree,
3309 cancel_func, cancel_baton,
3310 notify_func, notify_baton,
3314 /* Constructor for the result-structure returned by conflict callbacks. */
3315 svn_wc_conflict_result_t *
3316 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
3317 const char *merged_file,
3320 svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
3321 result->choice = choice;
3322 result->merged_file = apr_pstrdup(pool, merged_file);
3323 result->save_merged = FALSE;
3325 /* If we add more fields to svn_wc_conflict_result_t, add them here. */