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,
481 svn__apr_hash_index_key(hi)),
485 svn_skel__prepend(conflict_names, prop_conflict);
487 markers = svn_skel__make_empty_list(result_pool);
491 const char *marker_relpath;
492 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, wri_abspath,
494 result_pool, scratch_pool));
496 svn_skel__prepend_str(marker_relpath, markers, result_pool);
498 /*else // ### set via svn_wc__conflict_create_markers
499 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);*/
501 svn_skel__prepend(markers, prop_conflict);
503 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_conflict, result_pool);
505 /* And add it to the conflict skel */
506 svn_skel__prepend(prop_conflict, conflict_skel->children->next);
511 /* A map for svn_wc_conflict_reason_t values. */
512 static const svn_token_map_t local_change_map[] =
514 { "edited", svn_wc_conflict_reason_edited },
515 { "obstructed", svn_wc_conflict_reason_obstructed },
516 { "deleted", svn_wc_conflict_reason_deleted },
517 { "missing", svn_wc_conflict_reason_missing },
518 { "unversioned", svn_wc_conflict_reason_unversioned },
519 { "added", svn_wc_conflict_reason_added },
520 { "replaced", svn_wc_conflict_reason_replaced },
521 { "moved-away", svn_wc_conflict_reason_moved_away },
522 { "moved-here", svn_wc_conflict_reason_moved_here },
526 static const svn_token_map_t incoming_change_map[] =
528 { "edited", svn_wc_conflict_action_edit },
529 { "added", svn_wc_conflict_action_add },
530 { "deleted", svn_wc_conflict_action_delete },
531 { "replaced", svn_wc_conflict_action_replace },
536 svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel,
538 const char *wri_abspath,
539 svn_wc_conflict_reason_t local_change,
540 svn_wc_conflict_action_t incoming_change,
541 const char *move_src_op_root_abspath,
542 apr_pool_t *result_pool,
543 apr_pool_t *scratch_pool)
545 svn_skel_t *tree_conflict;
548 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
549 SVN_WC__CONFLICT_KIND_TREE));
551 SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */
553 SVN_ERR_ASSERT(local_change == svn_wc_conflict_reason_moved_away
554 || !move_src_op_root_abspath); /* ### Use proper error? */
556 tree_conflict = svn_skel__make_empty_list(result_pool);
558 if (local_change == svn_wc_conflict_reason_moved_away
559 && move_src_op_root_abspath)
561 const char *move_src_op_root_relpath;
563 SVN_ERR(svn_wc__db_to_relpath(&move_src_op_root_relpath,
565 move_src_op_root_abspath,
566 result_pool, scratch_pool));
568 svn_skel__prepend_str(move_src_op_root_relpath, tree_conflict,
572 svn_skel__prepend_str(
573 svn_token__to_word(incoming_change_map, incoming_change),
574 tree_conflict, result_pool);
576 svn_skel__prepend_str(
577 svn_token__to_word(local_change_map, local_change),
578 tree_conflict, result_pool);
580 /* Tree conflicts have no marker files */
581 markers = svn_skel__make_empty_list(result_pool);
582 svn_skel__prepend(markers, tree_conflict);
584 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TREE, tree_conflict,
587 /* And add it to the conflict skel */
588 svn_skel__prepend(tree_conflict, conflict_skel->children->next);
594 svn_wc__conflict_skel_resolve(svn_boolean_t *completely_resolved,
595 svn_skel_t *conflict_skel,
597 const char *wri_abspath,
598 svn_boolean_t resolve_text,
599 const char *resolve_prop,
600 svn_boolean_t resolve_tree,
601 apr_pool_t *result_pool,
602 apr_pool_t *scratch_pool)
605 svn_skel_t **pconflict;
606 SVN_ERR(conflict__get_operation(&op, conflict_skel));
609 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
610 _("Not a completed conflict skel"));
612 /* We are going to drop items from a linked list. Instead of keeping
613 a pointer to the item we want to drop we store a pointer to the
614 pointer of what we may drop, to allow setting it to the next item. */
616 pconflict = &(conflict_skel->children->next->children);
619 svn_skel_t *c = (*pconflict)->children;
622 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TEXT))
624 /* Remove the text conflict from the linked list */
625 *pconflict = (*pconflict)->next;
628 else if (resolve_prop
629 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_PROP))
631 svn_skel_t **ppropnames = &(c->next->next->children);
633 if (resolve_prop[0] == '\0')
634 *ppropnames = NULL; /* remove all conflicted property names */
638 if (svn_skel__matches_atom(*ppropnames, resolve_prop))
640 *ppropnames = (*ppropnames)->next;
643 ppropnames = &((*ppropnames)->next);
646 /* If no conflicted property names left */
647 if (!c->next->next->children)
649 /* Remove the propery conflict skel from the linked list */
650 *pconflict = (*pconflict)->next;
654 else if (resolve_tree
655 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TREE))
657 /* Remove the tree conflict from the linked list */
658 *pconflict = (*pconflict)->next;
662 pconflict = &((*pconflict)->next);
665 if (completely_resolved)
667 /* Nice, we can just call the complete function */
668 svn_boolean_t complete_conflict;
669 SVN_ERR(svn_wc__conflict_skel_is_complete(&complete_conflict,
672 *completely_resolved = !complete_conflict;
678 /* A map for svn_wc_operation_t values. */
679 static const svn_token_map_t operation_map[] =
681 { "", svn_wc_operation_none },
682 { SVN_WC__CONFLICT_OP_UPDATE, svn_wc_operation_update },
683 { SVN_WC__CONFLICT_OP_SWITCH, svn_wc_operation_switch },
684 { SVN_WC__CONFLICT_OP_MERGE, svn_wc_operation_merge },
689 svn_wc__conflict_read_info(svn_wc_operation_t *operation,
690 const apr_array_header_t **locations,
691 svn_boolean_t *text_conflicted,
692 svn_boolean_t *prop_conflicted,
693 svn_boolean_t *tree_conflicted,
695 const char *wri_abspath,
696 const svn_skel_t *conflict_skel,
697 apr_pool_t *result_pool,
698 apr_pool_t *scratch_pool)
703 SVN_ERR(conflict__get_operation(&op, conflict_skel));
706 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
707 _("Not a completed conflict skel"));
712 int value = svn_token__from_mem(operation_map, c->data, c->len);
714 if (value != SVN_TOKEN_UNKNOWN)
717 *operation = svn_wc_operation_none;
721 if (locations && c->children)
723 const svn_skel_t *loc_skel;
724 svn_wc_conflict_version_t *loc;
725 apr_array_header_t *locs = apr_array_make(result_pool, 2, sizeof(loc));
727 for (loc_skel = c->children; loc_skel; loc_skel = loc_skel->next)
729 SVN_ERR(conflict__read_location(&loc, loc_skel, result_pool,
732 APR_ARRAY_PUSH(locs, svn_wc_conflict_version_t *) = loc;
743 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
744 SVN_WC__CONFLICT_KIND_TEXT));
746 *text_conflicted = (c_skel != NULL);
752 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
753 SVN_WC__CONFLICT_KIND_PROP));
755 *prop_conflicted = (c_skel != NULL);
761 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
762 SVN_WC__CONFLICT_KIND_TREE));
764 *tree_conflicted = (c_skel != NULL);
772 svn_wc__conflict_read_text_conflict(const char **mine_abspath,
773 const char **their_old_abspath,
774 const char **their_abspath,
776 const char *wri_abspath,
777 const svn_skel_t *conflict_skel,
778 apr_pool_t *result_pool,
779 apr_pool_t *scratch_pool)
781 svn_skel_t *text_conflict;
784 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
785 SVN_WC__CONFLICT_KIND_TEXT));
788 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
790 m = text_conflict->children->next->children;
792 if (their_old_abspath)
796 const char *original_relpath;
798 original_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
799 SVN_ERR(svn_wc__db_from_relpath(their_old_abspath,
800 db, wri_abspath, original_relpath,
801 result_pool, scratch_pool));
804 *their_old_abspath = NULL;
812 const char *mine_relpath;
814 mine_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
815 SVN_ERR(svn_wc__db_from_relpath(mine_abspath,
816 db, wri_abspath, mine_relpath,
817 result_pool, scratch_pool));
820 *mine_abspath = NULL;
828 const char *their_relpath;
830 their_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
831 SVN_ERR(svn_wc__db_from_relpath(their_abspath,
832 db, wri_abspath, their_relpath,
833 result_pool, scratch_pool));
836 *their_abspath = NULL;
843 svn_wc__conflict_read_prop_conflict(const char **marker_abspath,
844 apr_hash_t **mine_props,
845 apr_hash_t **their_old_props,
846 apr_hash_t **their_props,
847 apr_hash_t **conflicted_prop_names,
849 const char *wri_abspath,
850 const svn_skel_t *conflict_skel,
851 apr_pool_t *result_pool,
852 apr_pool_t *scratch_pool)
854 svn_skel_t *prop_conflict;
857 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
858 SVN_WC__CONFLICT_KIND_PROP));
861 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
863 c = prop_conflict->children;
865 c = c->next; /* Skip "prop" */
867 /* Get marker file */
870 const char *marker_relpath;
872 if (c->children && c->children->is_atom)
874 marker_relpath = apr_pstrmemdup(result_pool, c->children->data,
877 SVN_ERR(svn_wc__db_from_relpath(marker_abspath, db, wri_abspath,
879 result_pool, scratch_pool));
882 *marker_abspath = NULL;
886 /* Get conflicted properties */
887 if (conflicted_prop_names)
889 const svn_skel_t *name;
890 *conflicted_prop_names = apr_hash_make(result_pool);
892 for (name = c->children; name; name = name->next)
894 svn_hash_sets(*conflicted_prop_names,
895 apr_pstrmemdup(result_pool, name->data, name->len),
901 /* Get original properties */
905 *their_old_props = apr_hash_make(result_pool);
907 SVN_ERR(svn_skel__parse_proplist(their_old_props, c, result_pool));
911 /* Get mine properties */
915 *mine_props = apr_hash_make(result_pool);
917 SVN_ERR(svn_skel__parse_proplist(mine_props, c, result_pool));
921 /* Get their properties */
925 *their_props = apr_hash_make(result_pool);
927 SVN_ERR(svn_skel__parse_proplist(their_props, c, result_pool));
934 svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *local_change,
935 svn_wc_conflict_action_t *incoming_change,
936 const char **move_src_op_root_abspath,
938 const char *wri_abspath,
939 const svn_skel_t *conflict_skel,
940 apr_pool_t *result_pool,
941 apr_pool_t *scratch_pool)
943 svn_skel_t *tree_conflict;
945 svn_boolean_t is_moved_away = FALSE;
947 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
948 SVN_WC__CONFLICT_KIND_TREE));
951 return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
953 c = tree_conflict->children;
955 c = c->next; /* Skip "tree" */
957 c = c->next; /* Skip markers */
960 int value = svn_token__from_mem(local_change_map, c->data, c->len);
964 if (value != SVN_TOKEN_UNKNOWN)
965 *local_change = value;
967 *local_change = svn_wc_conflict_reason_edited;
970 is_moved_away = (value == svn_wc_conflict_reason_moved_away);
976 int value = svn_token__from_mem(incoming_change_map, c->data, c->len);
978 if (value != SVN_TOKEN_UNKNOWN)
979 *incoming_change = value;
981 *incoming_change = svn_wc_conflict_action_edit;
986 if (move_src_op_root_abspath)
988 /* Only set for update and switch tree conflicts */
989 if (c && is_moved_away)
991 const char *move_src_op_root_relpath
992 = apr_pstrmemdup(scratch_pool, c->data, c->len);
994 SVN_ERR(svn_wc__db_from_relpath(move_src_op_root_abspath,
996 move_src_op_root_relpath,
997 result_pool, scratch_pool));
1000 *move_src_op_root_abspath = NULL;
1003 return SVN_NO_ERROR;
1007 svn_wc__conflict_read_markers(const apr_array_header_t **markers,
1009 const char *wri_abspath,
1010 const svn_skel_t *conflict_skel,
1011 apr_pool_t *result_pool,
1012 apr_pool_t *scratch_pool)
1014 const svn_skel_t *conflict;
1015 apr_array_header_t *list = NULL;
1017 SVN_ERR_ASSERT(conflict_skel != NULL);
1019 /* Walk the conflicts */
1020 for (conflict = conflict_skel->children->next->children;
1022 conflict = conflict->next)
1024 const svn_skel_t *marker;
1026 /* Get the list of markers stored per conflict */
1027 for (marker = conflict->children->next->children;
1029 marker = marker->next)
1031 /* Skip placeholders */
1032 if (! marker->is_atom)
1036 list = apr_array_make(result_pool, 4, sizeof(const char *));
1038 SVN_ERR(svn_wc__db_from_relpath(
1039 &APR_ARRAY_PUSH(list, const char*),
1041 apr_pstrmemdup(scratch_pool, marker->data,
1043 result_pool, scratch_pool));
1048 return SVN_NO_ERROR;
1051 /* --------------------------------------------------------------------
1053 /* Helper for svn_wc__conflict_create_markers */
1055 prop_conflict_skel_new(apr_pool_t *result_pool)
1057 svn_skel_t *operation = svn_skel__make_empty_list(result_pool);
1058 svn_skel_t *result = svn_skel__make_empty_list(result_pool);
1060 svn_skel__prepend(operation, result);
1065 /* Helper for prop_conflict_skel_add */
1067 prepend_prop_value(const svn_string_t *value,
1069 apr_pool_t *result_pool)
1071 svn_skel_t *value_skel = svn_skel__make_empty_list(result_pool);
1075 const void *dup = apr_pmemdup(result_pool, value->data, value->len);
1077 svn_skel__prepend(svn_skel__mem_atom(dup, value->len, result_pool),
1081 svn_skel__prepend(value_skel, skel);
1085 /* Helper for svn_wc__conflict_create_markers */
1086 static svn_error_t *
1087 prop_conflict_skel_add(
1089 const char *prop_name,
1090 const svn_string_t *original_value,
1091 const svn_string_t *mine_value,
1092 const svn_string_t *incoming_value,
1093 const svn_string_t *incoming_base_value,
1094 apr_pool_t *result_pool,
1095 apr_pool_t *scratch_pool)
1097 svn_skel_t *prop_skel = svn_skel__make_empty_list(result_pool);
1099 /* ### check that OPERATION has been filled in. */
1101 /* See notes/wc-ng/conflict-storage */
1102 prepend_prop_value(incoming_base_value, prop_skel, result_pool);
1103 prepend_prop_value(incoming_value, prop_skel, result_pool);
1104 prepend_prop_value(mine_value, prop_skel, result_pool);
1105 prepend_prop_value(original_value, prop_skel, result_pool);
1106 svn_skel__prepend_str(apr_pstrdup(result_pool, prop_name), prop_skel,
1108 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_skel, result_pool);
1110 /* Now we append PROP_SKEL to the end of the provided conflict SKEL. */
1111 svn_skel__append(skel, prop_skel);
1113 return SVN_NO_ERROR;
1117 svn_wc__conflict_create_markers(svn_skel_t **work_items,
1119 const char *local_abspath,
1120 svn_skel_t *conflict_skel,
1121 apr_pool_t *result_pool,
1122 apr_pool_t *scratch_pool)
1124 svn_boolean_t prop_conflicted;
1125 svn_wc_operation_t operation;
1128 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL,
1129 NULL, &prop_conflicted, NULL,
1132 scratch_pool, scratch_pool));
1134 if (prop_conflicted)
1136 const char *marker_abspath = NULL;
1137 svn_node_kind_t kind;
1138 const char *marker_dir;
1139 const char *marker_name;
1140 const char *marker_relpath;
1142 /* Ok, currently we have to do a few things for property conflicts:
1143 - Create a marker file
1144 - Create a WQ item that sets the marker name
1145 - Create a WQ item that fills the marker with the expected data
1147 This can be simplified once we really store conflict_skel in wc.db */
1149 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
1151 if (kind == svn_node_dir)
1153 marker_dir = local_abspath;
1154 marker_name = SVN_WC__THIS_DIR_PREJ;
1157 svn_dirent_split(&marker_dir, &marker_name, local_abspath,
1160 SVN_ERR(svn_io_open_uniquely_named(NULL, &marker_abspath,
1163 SVN_WC__PROP_REJ_EXT,
1164 svn_io_file_del_none,
1165 scratch_pool, scratch_pool));
1167 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, local_abspath,
1168 marker_abspath, result_pool, result_pool));
1170 /* And store the marker in the skel */
1172 svn_skel_t *prop_conflict;
1173 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
1174 SVN_WC__CONFLICT_KIND_PROP));
1176 svn_skel__prepend_str(marker_relpath, prop_conflict->children->next,
1180 /* Store the data in the WQ item in the same format used as 1.7.
1181 Once we store the data in DB it is easier to just read it back
1182 from the workqueue */
1184 svn_skel_t *prop_data;
1185 apr_hash_index_t *hi;
1186 apr_hash_t *old_props;
1187 apr_hash_t *mine_props;
1188 apr_hash_t *their_original_props;
1189 apr_hash_t *their_props;
1190 apr_hash_t *conflicted_props;
1192 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
1194 &their_original_props,
1202 if (operation == svn_wc_operation_merge)
1203 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
1204 scratch_pool, scratch_pool));
1206 old_props = their_original_props;
1208 prop_data = prop_conflict_skel_new(result_pool);
1210 for (hi = apr_hash_first(scratch_pool, conflicted_props);
1212 hi = apr_hash_next(hi))
1214 const char *propname = svn__apr_hash_index_key(hi);
1216 SVN_ERR(prop_conflict_skel_add(
1217 prop_data, propname,
1219 ? svn_hash_gets(old_props, propname)
1222 ? svn_hash_gets(mine_props, propname)
1225 ? svn_hash_gets(their_props, propname)
1227 their_original_props
1228 ? svn_hash_gets(their_original_props, propname)
1230 result_pool, scratch_pool));
1233 SVN_ERR(svn_wc__wq_build_prej_install(work_items,
1236 scratch_pool, scratch_pool));
1240 return SVN_NO_ERROR;
1243 /* Helper function for the three apply_* functions below, used when
1244 * merging properties together.
1246 * Given property PROPNAME on LOCAL_ABSPATH, and four possible property
1247 * values, generate four tmpfiles and pass them to CONFLICT_FUNC callback.
1248 * This gives the client an opportunity to interactively resolve the
1249 * property conflict.
1251 * BASE_VAL/WORKING_VAL represent the current state of the working
1252 * copy, and INCOMING_OLD_VAL/INCOMING_NEW_VAL represents the incoming
1253 * propchange. Any of these values might be NULL, indicating either
1254 * non-existence or intent-to-delete.
1256 * If the callback isn't available, or if it responds with
1257 * 'choose_postpone', then set *CONFLICT_REMAINS to TRUE and return.
1259 * If the callback responds with a choice of 'base', 'theirs', 'mine',
1260 * or 'merged', then install the proper value into ACTUAL_PROPS and
1261 * set *CONFLICT_REMAINS to FALSE.
1263 static svn_error_t *
1264 generate_propconflict(svn_boolean_t *conflict_remains,
1266 const char *local_abspath,
1267 svn_wc_operation_t operation,
1268 const svn_wc_conflict_version_t *left_version,
1269 const svn_wc_conflict_version_t *right_version,
1270 const char *propname,
1271 const svn_string_t *base_val,
1272 const svn_string_t *working_val,
1273 const svn_string_t *incoming_old_val,
1274 const svn_string_t *incoming_new_val,
1275 svn_wc_conflict_resolver_func2_t conflict_func,
1276 void *conflict_baton,
1277 apr_pool_t *scratch_pool)
1279 svn_wc_conflict_result_t *result = NULL;
1280 svn_wc_conflict_description2_t *cdesc;
1281 const char *dirpath = svn_dirent_dirname(local_abspath, scratch_pool);
1282 svn_node_kind_t kind;
1283 const svn_string_t *new_value = NULL;
1285 SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath,
1286 FALSE /* allow_missing */,
1287 FALSE /* show_deleted */,
1288 FALSE /* show_hidden */,
1291 if (kind == svn_node_none)
1292 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1293 _("The node '%s' was not found."),
1294 svn_dirent_local_style(local_abspath,
1297 cdesc = svn_wc_conflict_description_create_prop2(
1299 (kind == svn_node_dir) ? svn_node_dir : svn_node_file,
1300 propname, scratch_pool);
1302 cdesc->operation = operation;
1303 cdesc->src_left_version = left_version;
1304 cdesc->src_right_version = right_version;
1306 /* Create a tmpfile for each of the string_t's we've got. */
1309 const char *file_name;
1311 SVN_ERR(svn_io_write_unique(&file_name, dirpath, working_val->data,
1313 svn_io_file_del_on_pool_cleanup,
1315 cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1318 if (incoming_new_val)
1320 const char *file_name;
1322 SVN_ERR(svn_io_write_unique(&file_name, dirpath, incoming_new_val->data,
1323 incoming_new_val->len,
1324 svn_io_file_del_on_pool_cleanup,
1326 cdesc->their_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1329 if (!base_val && !incoming_old_val)
1331 /* If base and old are both NULL, then that's fine, we just let
1332 base_file stay NULL as-is. Both agents are attempting to add a
1336 else if ((base_val && !incoming_old_val)
1337 || (!base_val && incoming_old_val))
1339 /* If only one of base and old are defined, then we've got a
1340 situation where one agent is attempting to add the property
1341 for the first time, and the other agent is changing a
1342 property it thinks already exists. In this case, we return
1343 whichever older-value happens to be defined, so that the
1344 conflict-callback can still attempt a 3-way merge. */
1346 const svn_string_t *conflict_base_val = base_val ? base_val
1348 const char *file_name;
1350 SVN_ERR(svn_io_write_unique(&file_name, dirpath,
1351 conflict_base_val->data,
1352 conflict_base_val->len,
1353 svn_io_file_del_on_pool_cleanup,
1355 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1358 else /* base and old are both non-NULL */
1360 const svn_string_t *conflict_base_val;
1361 const char *file_name;
1363 if (! svn_string_compare(base_val, incoming_old_val))
1365 /* What happens if 'base' and 'old' don't match up? In an
1366 ideal situation, they would. But if they don't, this is
1367 a classic example of a patch 'hunk' failing to apply due
1368 to a lack of context. For example: imagine that the user
1369 is busy changing the property from a value of "cat" to
1370 "dog", but the incoming propchange wants to change the
1371 same property value from "red" to "green". Total context
1374 HOWEVER: we can still pass one of the two base values as
1375 'base_file' to the callback anyway. It's still useful to
1376 present the working and new values to the user to
1379 if (working_val && svn_string_compare(base_val, working_val))
1380 conflict_base_val = incoming_old_val;
1382 conflict_base_val = base_val;
1386 conflict_base_val = base_val;
1389 SVN_ERR(svn_io_write_unique(&file_name, dirpath, conflict_base_val->data,
1390 conflict_base_val->len,
1391 svn_io_file_del_on_pool_cleanup, scratch_pool));
1392 cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1394 if (working_val && incoming_new_val)
1396 svn_stream_t *mergestream;
1398 svn_diff_file_options_t *options =
1399 svn_diff_file_options_create(scratch_pool);
1401 SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->merged_file,
1402 NULL, svn_io_file_del_on_pool_cleanup,
1403 scratch_pool, scratch_pool));
1404 SVN_ERR(svn_diff_mem_string_diff3(&diff, conflict_base_val,
1406 incoming_new_val, options, scratch_pool));
1407 SVN_ERR(svn_diff_mem_string_output_merge2
1408 (mergestream, diff, conflict_base_val, working_val,
1409 incoming_new_val, NULL, NULL, NULL, NULL,
1410 svn_diff_conflict_display_modified_latest, scratch_pool));
1411 SVN_ERR(svn_stream_close(mergestream));
1415 if (!incoming_old_val && incoming_new_val)
1416 cdesc->action = svn_wc_conflict_action_add;
1417 else if (incoming_old_val && !incoming_new_val)
1418 cdesc->action = svn_wc_conflict_action_delete;
1420 cdesc->action = svn_wc_conflict_action_edit;
1422 if (base_val && !working_val)
1423 cdesc->reason = svn_wc_conflict_reason_deleted;
1424 else if (!base_val && working_val)
1425 cdesc->reason = svn_wc_conflict_reason_obstructed;
1427 cdesc->reason = svn_wc_conflict_reason_edited;
1429 /* Invoke the interactive conflict callback. */
1431 SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
1436 *conflict_remains = TRUE;
1437 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
1438 NULL, _("Conflict callback violated API:"
1439 " returned no results"));
1443 switch (result->choice)
1446 case svn_wc_conflict_choose_postpone:
1448 *conflict_remains = TRUE;
1451 case svn_wc_conflict_choose_mine_full:
1453 /* No need to change actual_props; it already contains working_val */
1454 *conflict_remains = FALSE;
1455 new_value = working_val;
1458 /* I think _mine_full and _theirs_full are appropriate for prop
1459 behavior as well as the text behavior. There should even be
1460 analogous behaviors for _mine and _theirs when those are
1461 ready, namely: fold in all non-conflicting prop changes, and
1462 then choose _mine side or _theirs side for conflicting ones. */
1463 case svn_wc_conflict_choose_theirs_full:
1465 *conflict_remains = FALSE;
1466 new_value = incoming_new_val;
1469 case svn_wc_conflict_choose_base:
1471 *conflict_remains = FALSE;
1472 new_value = base_val;
1475 case svn_wc_conflict_choose_merged:
1477 svn_stringbuf_t *merged_stringbuf;
1479 if (!cdesc->merged_file && !result->merged_file)
1480 return svn_error_create
1481 (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
1482 NULL, _("Conflict callback violated API:"
1483 " returned no merged file"));
1485 SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf,
1486 result->merged_file ?
1487 result->merged_file :
1490 new_value = svn_stringbuf__morph_into_string(merged_stringbuf);
1491 *conflict_remains = FALSE;
1496 if (!*conflict_remains)
1500 /* For now, just set the property values. This should really do some of the
1501 more advanced things from svn_wc_prop_set() */
1503 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool,
1506 svn_hash_sets(props, propname, new_value);
1508 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, props,
1513 return SVN_NO_ERROR;
1516 /* Resolve the text conflict on DB/LOCAL_ABSPATH in the manner specified
1519 * Set *WORK_ITEMS to new work items that will make the on-disk changes
1520 * needed to complete the resolution (but not to mark it as resolved).
1521 * Set *IS_RESOLVED to true if the conflicts are resolved; otherwise
1522 * (which is only if CHOICE is 'postpone') to false.
1524 * LEFT_ABSPATH, RIGHT_ABSPATH, and DETRANSLATED_TARGET are the
1525 * input files to the 3-way merge that will be performed if CHOICE is
1526 * 'theirs-conflict' or 'mine-conflict'. LEFT_ABSPATH is also the file
1527 * that will be used if CHOICE is 'base', and RIGHT_ABSPATH if CHOICE is
1528 * 'theirs-full'. MERGED_ABSPATH will be used if CHOICE is 'merged'.
1530 * DETRANSLATED_TARGET is the detranslated version of 'mine' (see
1531 * detranslate_wc_file() above). MERGE_OPTIONS are passed to the
1532 * diff3 implementation in case a 3-way merge has to be carried out.
1534 static svn_error_t *
1535 eval_text_conflict_func_result(svn_skel_t **work_items,
1536 svn_boolean_t *is_resolved,
1538 const char *local_abspath,
1539 svn_wc_conflict_choice_t choice,
1540 const apr_array_header_t *merge_options,
1541 const char *left_abspath,
1542 const char *right_abspath,
1543 const char *merged_abspath,
1544 const char *detranslated_target,
1545 apr_pool_t *result_pool,
1546 apr_pool_t *scratch_pool)
1548 const char *install_from_abspath = NULL;
1549 svn_boolean_t remove_source = FALSE;
1555 /* If the callback wants to use one of the fulltexts
1556 to resolve the conflict, so be it.*/
1557 case svn_wc_conflict_choose_base:
1559 install_from_abspath = left_abspath;
1560 *is_resolved = TRUE;
1563 case svn_wc_conflict_choose_theirs_full:
1565 install_from_abspath = right_abspath;
1566 *is_resolved = TRUE;
1569 case svn_wc_conflict_choose_mine_full:
1571 install_from_abspath = detranslated_target;
1572 *is_resolved = TRUE;
1575 case svn_wc_conflict_choose_theirs_conflict:
1576 case svn_wc_conflict_choose_mine_conflict:
1578 const char *chosen_abspath;
1579 const char *temp_dir;
1580 svn_stream_t *chosen_stream;
1582 svn_diff_conflict_display_style_t style;
1583 svn_diff_file_options_t *diff3_options;
1585 diff3_options = svn_diff_file_options_create(scratch_pool);
1588 SVN_ERR(svn_diff_file_options_parse(diff3_options,
1592 style = choice == svn_wc_conflict_choose_theirs_conflict
1593 ? svn_diff_conflict_display_latest
1594 : svn_diff_conflict_display_modified;
1596 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
1598 scratch_pool, scratch_pool));
1599 SVN_ERR(svn_stream_open_unique(&chosen_stream, &chosen_abspath,
1600 temp_dir, svn_io_file_del_none,
1601 scratch_pool, scratch_pool));
1603 SVN_ERR(svn_diff_file_diff3_2(&diff,
1605 detranslated_target, right_abspath,
1606 diff3_options, scratch_pool));
1607 SVN_ERR(svn_diff_file_output_merge2(chosen_stream, diff,
1609 detranslated_target,
1611 /* markers ignored */
1616 SVN_ERR(svn_stream_close(chosen_stream));
1618 install_from_abspath = chosen_abspath;
1619 remove_source = TRUE;
1620 *is_resolved = TRUE;
1624 /* For the case of 3-way file merging, we don't
1625 really distinguish between these return values;
1626 if the callback claims to have "generally
1627 resolved" the situation, we still interpret
1628 that as "OK, we'll assume the merged version is
1630 case svn_wc_conflict_choose_merged:
1632 install_from_abspath = merged_abspath;
1633 *is_resolved = TRUE;
1636 case svn_wc_conflict_choose_postpone:
1639 /* Assume conflict remains. */
1640 *is_resolved = FALSE;
1641 return SVN_NO_ERROR;
1645 SVN_ERR_ASSERT(install_from_abspath != NULL);
1648 svn_skel_t *work_item;
1650 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
1652 install_from_abspath,
1653 FALSE /* use_commit_times */,
1654 FALSE /* record_fileinfo */,
1655 result_pool, scratch_pool));
1656 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1658 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
1659 result_pool, scratch_pool));
1660 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1664 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
1666 install_from_abspath,
1667 result_pool, scratch_pool));
1668 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1672 return SVN_NO_ERROR;
1676 /* Create a new file in the same directory as LOCAL_ABSPATH, with the
1677 same basename as LOCAL_ABSPATH, with a ".edited" extension, and set
1678 *WORK_ITEM to a new work item that will copy and translate from the file
1679 SOURCE_ABSPATH to that new file. It will be translated from repository-
1680 normal form to working-copy form according to the versioned properties
1681 of LOCAL_ABSPATH that are current when the work item is executed.
1683 DB should have a write lock for the directory containing SOURCE.
1685 Allocate *WORK_ITEM in RESULT_POOL. */
1686 static svn_error_t *
1687 save_merge_result(svn_skel_t **work_item,
1689 const char *local_abspath,
1690 const char *source_abspath,
1691 apr_pool_t *result_pool,
1692 apr_pool_t *scratch_pool)
1694 const char *edited_copy_abspath;
1695 const char *dir_abspath;
1696 const char *filename;
1698 svn_dirent_split(&dir_abspath, &filename, local_abspath, scratch_pool);
1700 /* ### Should use preserved-conflict-file-exts. */
1701 /* Create the .edited file within this file's DIR_ABSPATH */
1702 SVN_ERR(svn_io_open_uniquely_named(NULL,
1703 &edited_copy_abspath,
1707 svn_io_file_del_none,
1708 scratch_pool, scratch_pool));
1709 SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item,
1712 edited_copy_abspath,
1713 result_pool, scratch_pool));
1714 return SVN_NO_ERROR;
1718 /* Call the conflict resolver callback for a text conflict, and resolve
1719 * the conflict if it tells us to do so.
1721 * Assume that there is a text conflict on the path DB/LOCAL_ABSPATH.
1723 * Call CONFLICT_FUNC with CONFLICT_BATON to find out whether and how
1724 * it wants to resolve the conflict. Pass it a conflict description
1725 * containing OPERATION, LEFT/RIGHT_ABSPATH, LEFT/RIGHT_VERSION,
1726 * RESULT_TARGET and DETRANSLATED_TARGET.
1728 * If the callback returns a resolution other than 'postpone', then
1729 * perform that requested resolution and prepare to mark the conflict
1732 * Return *WORK_ITEMS that will do the on-disk work required to complete
1733 * the resolution (but not to mark the conflict as resolved), and set
1734 * *WAS_RESOLVED to true, if it was resolved. Set *WORK_ITEMS to NULL
1735 * and *WAS_RESOLVED to FALSE otherwise.
1737 * RESULT_TARGET is the path to the merged file produced by the internal
1738 * or external 3-way merge, which may contain conflict markers, in
1739 * repository normal form. DETRANSLATED_TARGET is the 'mine' version of
1740 * the file, also in RNF.
1742 static svn_error_t *
1743 resolve_text_conflict(svn_skel_t **work_items,
1744 svn_boolean_t *was_resolved,
1746 const char *local_abspath,
1747 const apr_array_header_t *merge_options,
1748 svn_wc_operation_t operation,
1749 const char *left_abspath,
1750 const char *right_abspath,
1751 const svn_wc_conflict_version_t *left_version,
1752 const svn_wc_conflict_version_t *right_version,
1753 const char *result_target,
1754 const char *detranslated_target,
1755 svn_wc_conflict_resolver_func2_t conflict_func,
1756 void *conflict_baton,
1757 apr_pool_t *result_pool,
1758 apr_pool_t *scratch_pool)
1760 svn_wc_conflict_result_t *result;
1761 svn_skel_t *work_item;
1762 svn_wc_conflict_description2_t *cdesc;
1766 *was_resolved = FALSE;
1768 /* Give the conflict resolution callback a chance to clean
1769 up the conflicts before we mark the file 'conflicted' */
1771 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
1772 scratch_pool, scratch_pool));
1774 cdesc = svn_wc_conflict_description_create_text2(local_abspath,
1776 cdesc->is_binary = FALSE;
1777 cdesc->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
1778 cdesc->base_abspath = left_abspath;
1779 cdesc->their_abspath = right_abspath;
1780 cdesc->my_abspath = detranslated_target;
1781 cdesc->merged_file = result_target;
1782 cdesc->operation = operation;
1783 cdesc->src_left_version = left_version;
1784 cdesc->src_right_version = right_version;
1786 SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
1789 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1790 _("Conflict callback violated API:"
1791 " returned no results"));
1793 if (result->save_merged)
1795 SVN_ERR(save_merge_result(work_items,
1797 /* Look for callback's own
1798 merged-file first: */
1800 ? result->merged_file
1802 result_pool, scratch_pool));
1805 if (result->choice != svn_wc_conflict_choose_postpone)
1807 SVN_ERR(eval_text_conflict_func_result(&work_item,
1814 /* ### Sure this is an abspath? */
1816 ? result->merged_file
1818 detranslated_target,
1819 result_pool, scratch_pool));
1820 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1823 *was_resolved = FALSE;
1825 return SVN_NO_ERROR;
1829 static svn_error_t *
1830 setup_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
1832 const char *local_abspath,
1833 svn_wc_operation_t operation,
1834 const svn_wc_conflict_version_t *left_version,
1835 const svn_wc_conflict_version_t *right_version,
1836 svn_wc_conflict_reason_t local_change,
1837 svn_wc_conflict_action_t incoming_change,
1838 apr_pool_t *result_pool,
1839 apr_pool_t *scratch_pool)
1841 svn_node_kind_t tc_kind;
1844 tc_kind = left_version->node_kind;
1845 else if (right_version)
1846 tc_kind = right_version->node_kind;
1848 tc_kind = svn_node_file; /* Avoid assertion */
1850 *desc = svn_wc_conflict_description_create_tree2(local_abspath, tc_kind,
1852 left_version, right_version,
1854 (*desc)->reason = local_change;
1855 (*desc)->action = incoming_change;
1857 return SVN_NO_ERROR;
1862 svn_wc__conflict_invoke_resolver(svn_wc__db_t *db,
1863 const char *local_abspath,
1864 const svn_skel_t *conflict_skel,
1865 const apr_array_header_t *merge_options,
1866 svn_wc_conflict_resolver_func2_t resolver_func,
1867 void *resolver_baton,
1868 svn_cancel_func_t cancel_func,
1870 apr_pool_t *scratch_pool)
1872 svn_boolean_t text_conflicted;
1873 svn_boolean_t prop_conflicted;
1874 svn_boolean_t tree_conflicted;
1875 svn_wc_operation_t operation;
1876 const apr_array_header_t *locations;
1877 const svn_wc_conflict_version_t *left_version = NULL;
1878 const svn_wc_conflict_version_t *right_version = NULL;
1880 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
1881 &text_conflicted, &prop_conflicted,
1883 db, local_abspath, conflict_skel,
1884 scratch_pool, scratch_pool));
1886 if (locations && locations->nelts > 0)
1887 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
1889 if (locations && locations->nelts > 1)
1890 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
1892 /* Quick and dirty compatibility wrapper. My guess would be that most resolvers
1893 would want to look at all properties at the same time.
1895 ### svn currently only invokes this from the merge code to collect the list of
1896 ### conflicted paths. Eventually this code will be the base for 'svn resolve'
1897 ### and at that time the test coverage will improve
1899 if (prop_conflicted)
1901 apr_hash_t *old_props;
1902 apr_hash_t *mine_props;
1903 apr_hash_t *their_props;
1904 apr_hash_t *old_their_props;
1905 apr_hash_t *conflicted;
1906 apr_pool_t *iterpool;
1907 apr_hash_index_t *hi;
1908 svn_boolean_t mark_resolved = TRUE;
1910 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
1917 scratch_pool, scratch_pool));
1919 if (operation == svn_wc_operation_merge)
1920 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
1921 scratch_pool, scratch_pool));
1923 old_props = old_their_props;
1925 iterpool = svn_pool_create(scratch_pool);
1927 for (hi = apr_hash_first(scratch_pool, conflicted);
1929 hi = apr_hash_next(hi))
1931 const char *propname = svn__apr_hash_index_key(hi);
1932 svn_boolean_t conflict_remains = TRUE;
1934 svn_pool_clear(iterpool);
1937 SVN_ERR(cancel_func(cancel_baton));
1939 SVN_ERR(generate_propconflict(&conflict_remains,
1946 ? svn_hash_gets(old_props, propname)
1949 ? svn_hash_gets(mine_props, propname)
1952 ? svn_hash_gets(old_their_props, propname)
1955 ? svn_hash_gets(their_props, propname)
1957 resolver_func, resolver_baton,
1960 if (conflict_remains)
1961 mark_resolved = FALSE;
1966 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
1971 if (text_conflicted)
1973 const char *mine_abspath;
1974 const char *their_original_abspath;
1975 const char *their_abspath;
1976 svn_skel_t *work_items;
1977 svn_boolean_t was_resolved;
1979 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
1980 &their_original_abspath,
1984 scratch_pool, scratch_pool));
1986 SVN_ERR(resolve_text_conflict(&work_items, &was_resolved,
1990 their_original_abspath, their_abspath,
1991 left_version, right_version,
1992 local_abspath, mine_abspath,
1993 resolver_func, resolver_baton,
1994 scratch_pool, scratch_pool));
2000 SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_items,
2002 SVN_ERR(svn_wc__wq_run(db, local_abspath,
2003 cancel_func, cancel_baton,
2006 SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath,
2011 if (tree_conflicted)
2013 svn_wc_conflict_reason_t local_change;
2014 svn_wc_conflict_action_t incoming_change;
2015 svn_wc_conflict_result_t *result;
2016 svn_wc_conflict_description2_t *desc;
2018 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change,
2023 scratch_pool, scratch_pool));
2025 SVN_ERR(setup_tree_conflict_desc(&desc,
2027 operation, left_version, right_version,
2028 local_change, incoming_change,
2029 scratch_pool, scratch_pool));
2031 /* Tell the resolver func about this conflict. */
2032 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
2035 /* Ignore the result. We cannot apply it here since this code runs
2036 * during an update or merge operation. Tree conflicts are always
2037 * postponed and resolved after the operation has completed. */
2040 return SVN_NO_ERROR;
2043 /* Read all property conflicts contained in CONFLICT_SKEL into
2044 * individual conflict descriptions, and append those descriptions
2045 * to the CONFLICTS array.
2047 * If NOT create_tempfiles, always create a legacy property conflict
2050 * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
2051 * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
2053 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
2055 static svn_error_t *
2056 read_prop_conflicts(apr_array_header_t *conflicts,
2058 const char *local_abspath,
2059 svn_skel_t *conflict_skel,
2060 svn_boolean_t create_tempfiles,
2061 svn_node_kind_t node_kind,
2062 svn_wc_operation_t operation,
2063 const svn_wc_conflict_version_t *left_version,
2064 const svn_wc_conflict_version_t *right_version,
2065 apr_pool_t *result_pool,
2066 apr_pool_t *scratch_pool)
2068 const char *prop_reject_file;
2069 apr_hash_t *my_props;
2070 apr_hash_t *their_old_props;
2071 apr_hash_t *their_props;
2072 apr_hash_t *conflicted_props;
2073 apr_hash_index_t *hi;
2074 apr_pool_t *iterpool;
2076 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2083 scratch_pool, scratch_pool));
2085 if ((! create_tempfiles) || apr_hash_count(conflicted_props) == 0)
2087 /* Legacy prop conflict with only a .reject file. */
2088 svn_wc_conflict_description2_t *desc;
2090 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2094 /* ### This should be changed. The prej file should be stored
2095 * ### separately from the other files. We need to rev the
2096 * ### conflict description struct for this. */
2097 desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file);
2099 desc->operation = operation;
2100 desc->src_left_version = left_version;
2101 desc->src_right_version = right_version;
2103 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc;
2105 return SVN_NO_ERROR;
2108 iterpool = svn_pool_create(scratch_pool);
2109 for (hi = apr_hash_first(scratch_pool, conflicted_props);
2111 hi = apr_hash_next(hi))
2113 const char *propname = svn__apr_hash_index_key(hi);
2114 svn_string_t *old_value;
2115 svn_string_t *my_value;
2116 svn_string_t *their_value;
2117 svn_wc_conflict_description2_t *desc;
2119 svn_pool_clear(iterpool);
2121 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2126 desc->operation = operation;
2127 desc->src_left_version = left_version;
2128 desc->src_right_version = right_version;
2130 desc->property_name = apr_pstrdup(result_pool, propname);
2132 my_value = svn_hash_gets(my_props, propname);
2133 their_value = svn_hash_gets(their_props, propname);
2134 old_value = svn_hash_gets(their_old_props, propname);
2136 /* Compute the incoming side of the conflict ('action'). */
2137 if (their_value == NULL)
2138 desc->action = svn_wc_conflict_action_delete;
2139 else if (old_value == NULL)
2140 desc->action = svn_wc_conflict_action_add;
2142 desc->action = svn_wc_conflict_action_edit;
2144 /* Compute the local side of the conflict ('reason'). */
2145 if (my_value == NULL)
2146 desc->reason = svn_wc_conflict_reason_deleted;
2147 else if (old_value == NULL)
2148 desc->reason = svn_wc_conflict_reason_added;
2150 desc->reason = svn_wc_conflict_reason_edited;
2152 /* ### This should be changed. The prej file should be stored
2153 * ### separately from the other files. We need to rev the
2154 * ### conflict description struct for this. */
2155 desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file);
2157 /* ### This should be changed. The conflict description for
2158 * ### props should contain these values as svn_string_t,
2159 * ### rather than in temporary files. We need to rev the
2160 * ### conflict description struct for this. */
2166 SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL,
2167 svn_io_file_del_on_pool_cleanup,
2168 result_pool, iterpool));
2169 len = my_value->len;
2170 SVN_ERR(svn_stream_write(s, my_value->data, &len));
2171 SVN_ERR(svn_stream_close(s));
2179 /* ### Currently, their_abspath is used for the prop reject file.
2180 * ### Put their value into merged instead...
2181 * ### We need to rev the conflict description struct to fix this. */
2182 SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL,
2183 svn_io_file_del_on_pool_cleanup,
2184 result_pool, iterpool));
2185 len = their_value->len;
2186 SVN_ERR(svn_stream_write(s, their_value->data, &len));
2187 SVN_ERR(svn_stream_close(s));
2195 SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL,
2196 svn_io_file_del_on_pool_cleanup,
2197 result_pool, iterpool));
2198 len = old_value->len;
2199 SVN_ERR(svn_stream_write(s, old_value->data, &len));
2200 SVN_ERR(svn_stream_close(s));
2203 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc;
2205 svn_pool_destroy(iterpool);
2207 return SVN_NO_ERROR;
2211 svn_wc__read_conflicts(const apr_array_header_t **conflicts,
2213 const char *local_abspath,
2214 svn_boolean_t create_tempfiles,
2215 apr_pool_t *result_pool,
2216 apr_pool_t *scratch_pool)
2218 svn_skel_t *conflict_skel;
2219 apr_array_header_t *cflcts;
2220 svn_boolean_t prop_conflicted;
2221 svn_boolean_t text_conflicted;
2222 svn_boolean_t tree_conflicted;
2223 svn_wc_operation_t operation;
2224 const apr_array_header_t *locations;
2225 const svn_wc_conflict_version_t *left_version = NULL;
2226 const svn_wc_conflict_version_t *right_version = NULL;
2228 SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db, local_abspath,
2229 scratch_pool, scratch_pool));
2233 /* Some callers expect not NULL */
2234 *conflicts = apr_array_make(result_pool, 0,
2235 sizeof(svn_wc_conflict_description2_t*));;
2236 return SVN_NO_ERROR;
2239 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted,
2240 &prop_conflicted, &tree_conflicted,
2241 db, local_abspath, conflict_skel,
2242 result_pool, scratch_pool));
2244 cflcts = apr_array_make(result_pool, 4,
2245 sizeof(svn_wc_conflict_description2_t*));
2247 if (locations && locations->nelts > 0)
2248 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
2249 if (locations && locations->nelts > 1)
2250 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
2252 if (prop_conflicted)
2254 svn_node_kind_t node_kind
2255 = left_version ? left_version->node_kind : svn_node_unknown;
2257 SVN_ERR(read_prop_conflicts(cflcts, db, local_abspath, conflict_skel,
2258 create_tempfiles, node_kind,
2259 operation, left_version, right_version,
2260 result_pool, scratch_pool));
2263 if (text_conflicted)
2265 svn_wc_conflict_description2_t *desc;
2266 desc = svn_wc_conflict_description_create_text2(local_abspath,
2269 desc->operation = operation;
2270 desc->src_left_version = left_version;
2271 desc->src_right_version = right_version;
2273 SVN_ERR(svn_wc__conflict_read_text_conflict(&desc->my_abspath,
2274 &desc->base_abspath,
2275 &desc->their_abspath,
2278 result_pool, scratch_pool));
2280 desc->merged_file = apr_pstrdup(result_pool, local_abspath);
2282 APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t*) = desc;
2285 if (tree_conflicted)
2287 svn_wc_conflict_reason_t local_change;
2288 svn_wc_conflict_action_t incoming_change;
2289 svn_wc_conflict_description2_t *desc;
2291 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change,
2296 scratch_pool, scratch_pool));
2298 SVN_ERR(setup_tree_conflict_desc(&desc,
2300 operation, left_version, right_version,
2301 local_change, incoming_change,
2302 result_pool, scratch_pool));
2304 APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc;
2307 *conflicts = cflcts;
2308 return SVN_NO_ERROR;
2312 /*** Resolving a conflict automatically ***/
2314 /* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the
2315 * working copy at DB/WRI_ABSPATH.
2317 * Set *WORK_ITEMS to a new work item that, when run, will delete the
2318 * artifact file; or to NULL if there is no file to delete.
2320 * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its
2321 * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND
2322 * may be NULL if not required.
2324 static svn_error_t *
2325 remove_artifact_file_if_exists(svn_skel_t **work_items,
2326 svn_boolean_t *file_found,
2328 const char *wri_abspath,
2329 const char *artifact_file_abspath,
2330 apr_pool_t *result_pool,
2331 apr_pool_t *scratch_pool)
2334 if (artifact_file_abspath)
2336 svn_node_kind_t node_kind;
2338 SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind,
2340 if (node_kind == svn_node_file)
2342 SVN_ERR(svn_wc__wq_build_file_remove(work_items,
2344 artifact_file_abspath,
2345 result_pool, scratch_pool));
2351 return SVN_NO_ERROR;
2355 * Resolve the text conflict found in DB/LOCAL_ABSPATH according
2356 * to CONFLICT_CHOICE.
2358 * It is not an error if there is no text conflict. If a text conflict
2359 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2361 * Note: When there are no conflict markers to remove there is no existing
2362 * text conflict; just a database containing old information, which we should
2363 * remove to avoid checking all the time. Resolving a text conflict by
2364 * removing all the marker files is a fully supported scenario since
2367 static svn_error_t *
2368 resolve_text_conflict_on_node(svn_boolean_t *did_resolve,
2370 const char *local_abspath,
2371 svn_wc_conflict_choice_t conflict_choice,
2372 const char *merged_file,
2373 svn_cancel_func_t cancel_func,
2375 apr_pool_t *scratch_pool)
2377 const char *conflict_old = NULL;
2378 const char *conflict_new = NULL;
2379 const char *conflict_working = NULL;
2380 const char *auto_resolve_src;
2381 svn_skel_t *work_item;
2382 svn_skel_t *work_items = NULL;
2383 svn_skel_t *conflicts;
2384 svn_wc_operation_t operation;
2385 svn_boolean_t text_conflicted;
2387 *did_resolve = FALSE;
2389 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
2390 scratch_pool, scratch_pool));
2392 return SVN_NO_ERROR;
2394 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, &text_conflicted,
2395 NULL, NULL, db, local_abspath, conflicts,
2396 scratch_pool, scratch_pool));
2397 if (!text_conflicted)
2398 return SVN_NO_ERROR;
2400 SVN_ERR(svn_wc__conflict_read_text_conflict(&conflict_working,
2403 db, local_abspath, conflicts,
2404 scratch_pool, scratch_pool));
2406 /* Handle automatic conflict resolution before the temporary files are
2407 * deleted, if necessary. */
2408 switch (conflict_choice)
2410 case svn_wc_conflict_choose_base:
2411 auto_resolve_src = conflict_old;
2413 case svn_wc_conflict_choose_mine_full:
2414 auto_resolve_src = conflict_working;
2416 case svn_wc_conflict_choose_theirs_full:
2417 auto_resolve_src = conflict_new;
2419 case svn_wc_conflict_choose_merged:
2420 auto_resolve_src = merged_file;
2422 case svn_wc_conflict_choose_theirs_conflict:
2423 case svn_wc_conflict_choose_mine_conflict:
2425 if (conflict_old && conflict_working && conflict_new)
2427 const char *temp_dir;
2428 svn_stream_t *tmp_stream = NULL;
2430 svn_diff_conflict_display_style_t style =
2431 conflict_choice == svn_wc_conflict_choose_theirs_conflict
2432 ? svn_diff_conflict_display_latest
2433 : svn_diff_conflict_display_modified;
2435 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
2439 SVN_ERR(svn_stream_open_unique(&tmp_stream,
2442 svn_io_file_del_on_pool_cleanup,
2443 scratch_pool, scratch_pool));
2445 SVN_ERR(svn_diff_file_diff3_2(&diff,
2449 svn_diff_file_options_create(
2452 SVN_ERR(svn_diff_file_output_merge2(tmp_stream, diff,
2456 /* markers ignored */
2457 NULL, NULL, NULL, NULL,
2460 SVN_ERR(svn_stream_close(tmp_stream));
2463 auto_resolve_src = NULL;
2467 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2468 _("Invalid 'conflict_result' argument"));
2471 if (auto_resolve_src)
2473 SVN_ERR(svn_wc__wq_build_file_copy_translated(
2474 &work_item, db, local_abspath,
2475 auto_resolve_src, local_abspath, scratch_pool, scratch_pool));
2476 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2478 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db,
2480 scratch_pool, scratch_pool));
2481 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2484 /* Legacy behavior: Only report text conflicts as resolved when at least
2485 one conflict marker file exists.
2487 If not the UI shows the conflict as already resolved
2488 (and in this case we just remove the in-db conflict) */
2490 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
2491 db, local_abspath, conflict_old,
2492 scratch_pool, scratch_pool));
2493 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2495 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
2496 db, local_abspath, conflict_new,
2497 scratch_pool, scratch_pool));
2498 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2500 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
2501 db, local_abspath, conflict_working,
2502 scratch_pool, scratch_pool));
2503 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2505 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2507 work_items, scratch_pool));
2508 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2511 return SVN_NO_ERROR;
2515 * Resolve the property conflicts found in DB/LOCAL_ABSPATH according
2516 * to CONFLICT_CHOICE.
2518 * It is not an error if there is no prop conflict. If a prop conflict
2519 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2521 * Note: When there are no conflict markers on-disk to remove there is
2522 * no existing text conflict (unless we are still in the process of
2523 * creating the text conflict and we didn't register a marker file yet).
2524 * In this case the database contains old information, which we should
2525 * remove to avoid checking the next time. Resolving a property conflict
2526 * by just removing the marker file is a fully supported scenario since
2529 * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
2530 * In my opinion, 'mine_full'/'theirs_full' should select
2531 * the entire set of properties from 'mine' or 'theirs' respectively,
2532 * while 'mine_conflict'/'theirs_conflict' should select just the
2533 * properties that are in conflict. Or, '_full' should select the
2534 * entire property whereas '_conflict' should do a text merge within
2535 * each property, selecting hunks. Or all three kinds of behaviour
2536 * should be available (full set of props, full value of conflicting
2537 * props, or conflicting text hunks).
2538 * ### BH: If we make *_full select the full set of properties, we should
2539 * check if we shouldn't make it also select the full text for files.
2541 * ### TODO [JAF] All this complexity should not be down here in libsvn_wc
2542 * but in a layer above.
2544 * ### TODO [JAF] Options for 'base' should be like options for 'mine' and
2545 * for 'theirs' -- choose full set of props, full value of conflicting
2546 * props, or conflicting text hunks.
2549 static svn_error_t *
2550 resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
2552 const char *local_abspath,
2553 const char *conflicted_propname,
2554 svn_wc_conflict_choice_t conflict_choice,
2555 const char *merged_file,
2556 svn_cancel_func_t cancel_func,
2558 apr_pool_t *scratch_pool)
2560 const char *prop_reject_file;
2561 apr_hash_t *mine_props;
2562 apr_hash_t *their_old_props;
2563 apr_hash_t *their_props;
2564 apr_hash_t *conflicted_props;
2565 apr_hash_t *old_props;
2566 apr_hash_t *resolve_from = NULL;
2567 svn_skel_t *work_items = NULL;
2568 svn_skel_t *conflicts;
2569 svn_wc_operation_t operation;
2570 svn_boolean_t prop_conflicted;
2572 *did_resolve = FALSE;
2574 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
2575 scratch_pool, scratch_pool));
2578 return SVN_NO_ERROR;
2580 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2581 NULL, db, local_abspath, conflicts,
2582 scratch_pool, scratch_pool));
2583 if (!prop_conflicted)
2584 return SVN_NO_ERROR;
2586 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2587 &mine_props, &their_old_props,
2588 &their_props, &conflicted_props,
2589 db, local_abspath, conflicts,
2590 scratch_pool, scratch_pool));
2592 if (operation == svn_wc_operation_merge)
2593 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
2594 scratch_pool, scratch_pool));
2596 old_props = their_old_props;
2598 /* We currently handle *_conflict as *_full as this argument is currently
2599 always applied for all conflicts on a node at the same time. Giving
2600 an error would break some tests that assumed that this would just
2601 resolve property conflicts to working.
2603 An alternative way to handle these conflicts would be to just copy all
2604 property state from mine/theirs on the _full option instead of just the
2605 conflicted properties. In some ways this feels like a sensible option as
2606 that would take both properties and text from mine/theirs, but when not
2607 both properties and text are conflicted we would fail in doing so.
2609 switch (conflict_choice)
2611 case svn_wc_conflict_choose_base:
2612 resolve_from = their_old_props ? their_old_props : old_props;
2614 case svn_wc_conflict_choose_mine_full:
2615 case svn_wc_conflict_choose_mine_conflict:
2616 resolve_from = mine_props;
2618 case svn_wc_conflict_choose_theirs_full:
2619 case svn_wc_conflict_choose_theirs_conflict:
2620 resolve_from = their_props;
2622 case svn_wc_conflict_choose_merged:
2623 if (merged_file && conflicted_propname[0] != '\0')
2625 apr_hash_t *actual_props;
2626 svn_stream_t *stream;
2627 svn_string_t *merged_propval;
2629 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2630 scratch_pool, scratch_pool));
2631 resolve_from = actual_props;
2633 SVN_ERR(svn_stream_open_readonly(&stream, merged_file,
2634 scratch_pool, scratch_pool));
2635 SVN_ERR(svn_string_from_stream(&merged_propval, stream,
2636 scratch_pool, scratch_pool));
2637 svn_hash_sets(resolve_from, conflicted_propname, merged_propval);
2640 resolve_from = NULL;
2643 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2644 _("Invalid 'conflict_result' argument"));
2647 if (conflicted_props && apr_hash_count(conflicted_props) && resolve_from)
2649 apr_hash_index_t *hi;
2650 apr_hash_t *actual_props;
2652 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2653 scratch_pool, scratch_pool));
2655 for (hi = apr_hash_first(scratch_pool, conflicted_props);
2657 hi = apr_hash_next(hi))
2659 const char *propname = svn__apr_hash_index_key(hi);
2660 svn_string_t *new_value = NULL;
2662 new_value = svn_hash_gets(resolve_from, propname);
2664 svn_hash_sets(actual_props, propname, new_value);
2666 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props,
2671 /* Legacy behavior: Only report property conflicts as resolved when the
2672 property reject file exists
2674 If not the UI shows the conflict as already resolved
2675 (and in this case we just remove the in-db conflict) */
2678 svn_skel_t *work_item;
2680 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
2681 db, local_abspath, prop_reject_file,
2682 scratch_pool, scratch_pool));
2683 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2686 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE,
2687 work_items, scratch_pool));
2688 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2691 return SVN_NO_ERROR;
2695 * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
2698 * It is not an error if there is no tree conflict. If a tree conflict
2699 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2701 * It is not an error if there is no tree conflict.
2703 static svn_error_t *
2704 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
2706 const char *local_abspath,
2707 svn_wc_conflict_choice_t conflict_choice,
2708 svn_wc_notify_func2_t notify_func,
2710 svn_cancel_func_t cancel_func,
2712 apr_pool_t *scratch_pool)
2714 svn_wc_conflict_reason_t reason;
2715 svn_wc_conflict_action_t action;
2716 svn_skel_t *conflicts;
2717 svn_wc_operation_t operation;
2718 svn_boolean_t tree_conflicted;
2720 *did_resolve = FALSE;
2722 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
2723 scratch_pool, scratch_pool));
2725 return SVN_NO_ERROR;
2727 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
2728 &tree_conflicted, db, local_abspath,
2729 conflicts, scratch_pool, scratch_pool));
2730 if (!tree_conflicted)
2731 return SVN_NO_ERROR;
2733 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL,
2736 scratch_pool, scratch_pool));
2738 if (operation == svn_wc_operation_update
2739 || operation == svn_wc_operation_switch)
2741 if (reason == svn_wc_conflict_reason_deleted ||
2742 reason == svn_wc_conflict_reason_replaced)
2744 if (conflict_choice == svn_wc_conflict_choose_merged)
2746 /* Break moves for any children moved out of this directory,
2747 * and leave this directory deleted. */
2748 SVN_ERR(svn_wc__db_resolve_break_moved_away_children(
2749 db, local_abspath, notify_func, notify_baton,
2751 *did_resolve = TRUE;
2753 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2755 /* Raised moved-away conflicts on any children moved out of
2756 * this directory, and leave this directory deleted.
2757 * The newly conflicted moved-away children will be updated
2758 * if they are resolved with 'mine_conflict' as well. */
2759 SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
2760 db, local_abspath, notify_func, notify_baton,
2762 *did_resolve = TRUE;
2765 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2767 _("Tree conflict can only be resolved to "
2768 "'working' or 'mine-conflict' state; "
2769 "'%s' not resolved"),
2770 svn_dirent_local_style(local_abspath,
2773 else if (reason == svn_wc_conflict_reason_moved_away
2774 && action == svn_wc_conflict_action_edit)
2776 /* After updates, we can resolve local moved-away
2777 * vs. any incoming change, either by updating the
2778 * moved-away node (mine-conflict) or by breaking the
2779 * move (theirs-conflict). */
2780 if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2782 SVN_ERR(svn_wc__db_update_moved_away_conflict_victim(
2784 notify_func, notify_baton,
2785 cancel_func, cancel_baton,
2787 *did_resolve = TRUE;
2789 else if (conflict_choice == svn_wc_conflict_choose_merged)
2791 /* We must break the move if the user accepts the current
2792 * working copy state instead of updating the move.
2793 * Else the move would be left in an invalid state. */
2795 /* ### This breaks the move but leaves the conflict
2796 ### involving the move until
2797 ### svn_wc__db_op_mark_resolved. */
2798 SVN_ERR(svn_wc__db_resolve_break_moved_away(db, local_abspath,
2802 *did_resolve = TRUE;
2805 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2807 _("Tree conflict can only be resolved to "
2808 "'working' or 'mine-conflict' state; "
2809 "'%s' not resolved"),
2810 svn_dirent_local_style(local_abspath,
2815 if (! *did_resolve && conflict_choice != svn_wc_conflict_choose_merged)
2817 /* For other tree conflicts, there is no way to pick
2818 * theirs-full or mine-full, etc. Throw an error if the
2819 * user expects us to be smarter than we really are. */
2820 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2822 _("Tree conflict can only be "
2823 "resolved to 'working' state; "
2824 "'%s' not resolved"),
2825 svn_dirent_local_style(local_abspath,
2829 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE,
2830 NULL, scratch_pool));
2831 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2833 return SVN_NO_ERROR;
2837 svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
2838 const char *local_abspath,
2839 apr_pool_t *scratch_pool)
2841 svn_boolean_t ignored_result;
2843 return svn_error_trace(resolve_text_conflict_on_node(
2846 svn_wc_conflict_choose_merged, NULL,
2852 svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db,
2853 const char *local_abspath,
2854 apr_pool_t *scratch_pool)
2856 svn_boolean_t ignored_result;
2858 return svn_error_trace(resolve_prop_conflict_on_node(
2860 db, local_abspath, "",
2861 svn_wc_conflict_choose_merged, NULL,
2867 /* Baton for conflict_status_walker */
2868 struct conflict_status_walker_baton
2871 svn_boolean_t resolve_text;
2872 const char *resolve_prop;
2873 svn_boolean_t resolve_tree;
2874 svn_wc_conflict_choice_t conflict_choice;
2875 svn_wc_conflict_resolver_func2_t conflict_func;
2876 void *conflict_baton;
2877 svn_cancel_func_t cancel_func;
2879 svn_wc_notify_func2_t notify_func;
2883 /* Implements svn_wc_status4_t to walk all conflicts to resolve.
2885 static svn_error_t *
2886 conflict_status_walker(void *baton,
2887 const char *local_abspath,
2888 const svn_wc_status3_t *status,
2889 apr_pool_t *scratch_pool)
2891 struct conflict_status_walker_baton *cswb = baton;
2892 svn_wc__db_t *db = cswb->db;
2894 const apr_array_header_t *conflicts;
2895 apr_pool_t *iterpool;
2897 svn_boolean_t resolved = FALSE;
2899 if (!status->conflicted)
2900 return SVN_NO_ERROR;
2902 iterpool = svn_pool_create(scratch_pool);
2904 SVN_ERR(svn_wc__read_conflicts(&conflicts, db, local_abspath, TRUE,
2905 scratch_pool, iterpool));
2907 for (i = 0; i < conflicts->nelts; i++)
2909 const svn_wc_conflict_description2_t *cd;
2910 svn_boolean_t did_resolve;
2911 svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
2912 const char *merged_file = NULL;
2914 cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
2916 svn_pool_clear(iterpool);
2918 if (my_choice == svn_wc_conflict_choose_unspecified)
2920 svn_wc_conflict_result_t *result;
2922 if (!cswb->conflict_func)
2923 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2924 _("No conflict-callback and no "
2925 "pre-defined conflict-choice provided"));
2927 SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton,
2928 iterpool, iterpool));
2930 my_choice = result->choice;
2931 merged_file = result->merged_file;
2932 /* ### Bug: ignores result->save_merged */
2936 if (my_choice == svn_wc_conflict_choose_postpone)
2941 case svn_wc_conflict_kind_tree:
2942 if (!cswb->resolve_tree)
2944 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
2957 case svn_wc_conflict_kind_text:
2958 if (!cswb->resolve_text)
2961 SVN_ERR(resolve_text_conflict_on_node(&did_resolve,
2974 case svn_wc_conflict_kind_property:
2975 if (!cswb->resolve_prop)
2978 if (*cswb->resolve_prop != '\0' &&
2979 strcmp(cswb->resolve_prop, cd->property_name) != 0)
2981 break; /* This is not the property we want to resolve. */
2984 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
2999 /* We can't resolve other conflict types */
3005 if (cswb->notify_func && resolved)
3006 cswb->notify_func(cswb->notify_baton,
3007 svn_wc_create_notify(local_abspath,
3008 svn_wc_notify_resolved,
3012 svn_pool_destroy(iterpool);
3014 return SVN_NO_ERROR;
3018 svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
3019 const char *local_abspath,
3021 svn_boolean_t resolve_text,
3022 const char *resolve_prop,
3023 svn_boolean_t resolve_tree,
3024 svn_wc_conflict_choice_t conflict_choice,
3025 svn_wc_conflict_resolver_func2_t conflict_func,
3026 void *conflict_baton,
3027 svn_cancel_func_t cancel_func,
3029 svn_wc_notify_func2_t notify_func,
3031 apr_pool_t *scratch_pool)
3033 svn_node_kind_t kind;
3034 svn_boolean_t conflicted;
3035 struct conflict_status_walker_baton cswb;
3037 /* ### the underlying code does NOT support resolving individual
3038 ### properties. bail out if the caller tries it. */
3039 if (resolve_prop != NULL && *resolve_prop != '\0')
3040 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
3041 U_("Resolving a single property is not (yet) "
3044 /* ### Just a versioned check? */
3045 /* Conflicted is set to allow invoking on actual only nodes */
3046 SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
3047 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3048 NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
3049 NULL, NULL, NULL, NULL, NULL, NULL,
3050 wc_ctx->db, local_abspath,
3051 scratch_pool, scratch_pool));
3053 /* When the implementation still used the entry walker, depth
3054 unknown was translated to infinity. */
3055 if (kind != svn_node_dir)
3056 depth = svn_depth_empty;
3057 else if (depth == svn_depth_unknown)
3058 depth = svn_depth_infinity;
3060 cswb.db = wc_ctx->db;
3061 cswb.resolve_text = resolve_text;
3062 cswb.resolve_prop = resolve_prop;
3063 cswb.resolve_tree = resolve_tree;
3064 cswb.conflict_choice = conflict_choice;
3066 cswb.conflict_func = conflict_func;
3067 cswb.conflict_baton = conflict_baton;
3069 cswb.cancel_func = cancel_func;
3070 cswb.cancel_baton = cancel_baton;
3072 cswb.notify_func = notify_func;
3073 cswb.notify_baton = notify_baton;
3076 notify_func(notify_baton,
3077 svn_wc_create_notify(local_abspath,
3078 svn_wc_notify_conflict_resolver_starting,
3082 SVN_ERR(svn_wc_walk_status(wc_ctx,
3085 FALSE /* get_all */,
3086 FALSE /* no_ignore */,
3087 TRUE /* ignore_text_mods */,
3088 NULL /* ignore_patterns */,
3089 conflict_status_walker, &cswb,
3090 cancel_func, cancel_baton,
3094 notify_func(notify_baton,
3095 svn_wc_create_notify(local_abspath,
3096 svn_wc_notify_conflict_resolver_done,
3100 return SVN_NO_ERROR;
3104 svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
3105 const char *local_abspath,
3107 svn_boolean_t resolve_text,
3108 const char *resolve_prop,
3109 svn_boolean_t resolve_tree,
3110 svn_wc_conflict_choice_t conflict_choice,
3111 svn_cancel_func_t cancel_func,
3113 svn_wc_notify_func2_t notify_func,
3115 apr_pool_t *scratch_pool)
3117 return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath,
3118 depth, resolve_text,
3119 resolve_prop, resolve_tree,
3122 cancel_func, cancel_baton,
3123 notify_func, notify_baton,
3127 /* Constructor for the result-structure returned by conflict callbacks. */
3128 svn_wc_conflict_result_t *
3129 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
3130 const char *merged_file,
3133 svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
3134 result->choice = choice;
3135 result->merged_file = merged_file;
3136 result->save_merged = FALSE;
3138 /* If we add more fields to svn_wc_conflict_result_t, add them here. */