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 if (install_from_abspath == NULL)
1646 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1647 _("Conflict on '%s' could not be resolved "
1648 "because the chosen version of the file "
1649 "is not available."),
1650 svn_dirent_local_style(local_abspath,
1654 svn_skel_t *work_item;
1656 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
1658 install_from_abspath,
1659 FALSE /* use_commit_times */,
1660 FALSE /* record_fileinfo */,
1661 result_pool, scratch_pool));
1662 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1664 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
1665 result_pool, scratch_pool));
1666 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1670 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
1672 install_from_abspath,
1673 result_pool, scratch_pool));
1674 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1678 return SVN_NO_ERROR;
1682 /* Create a new file in the same directory as LOCAL_ABSPATH, with the
1683 same basename as LOCAL_ABSPATH, with a ".edited" extension, and set
1684 *WORK_ITEM to a new work item that will copy and translate from the file
1685 SOURCE_ABSPATH to that new file. It will be translated from repository-
1686 normal form to working-copy form according to the versioned properties
1687 of LOCAL_ABSPATH that are current when the work item is executed.
1689 DB should have a write lock for the directory containing SOURCE.
1691 Allocate *WORK_ITEM in RESULT_POOL. */
1692 static svn_error_t *
1693 save_merge_result(svn_skel_t **work_item,
1695 const char *local_abspath,
1696 const char *source_abspath,
1697 apr_pool_t *result_pool,
1698 apr_pool_t *scratch_pool)
1700 const char *edited_copy_abspath;
1701 const char *dir_abspath;
1702 const char *filename;
1704 svn_dirent_split(&dir_abspath, &filename, local_abspath, scratch_pool);
1706 /* ### Should use preserved-conflict-file-exts. */
1707 /* Create the .edited file within this file's DIR_ABSPATH */
1708 SVN_ERR(svn_io_open_uniquely_named(NULL,
1709 &edited_copy_abspath,
1713 svn_io_file_del_none,
1714 scratch_pool, scratch_pool));
1715 SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item,
1718 edited_copy_abspath,
1719 result_pool, scratch_pool));
1720 return SVN_NO_ERROR;
1724 /* Call the conflict resolver callback for a text conflict, and resolve
1725 * the conflict if it tells us to do so.
1727 * Assume that there is a text conflict on the path DB/LOCAL_ABSPATH.
1729 * Call CONFLICT_FUNC with CONFLICT_BATON to find out whether and how
1730 * it wants to resolve the conflict. Pass it a conflict description
1731 * containing OPERATION, LEFT/RIGHT_ABSPATH, LEFT/RIGHT_VERSION,
1732 * RESULT_TARGET and DETRANSLATED_TARGET.
1734 * If the callback returns a resolution other than 'postpone', then
1735 * perform that requested resolution and prepare to mark the conflict
1738 * Return *WORK_ITEMS that will do the on-disk work required to complete
1739 * the resolution (but not to mark the conflict as resolved), and set
1740 * *WAS_RESOLVED to true, if it was resolved. Set *WORK_ITEMS to NULL
1741 * and *WAS_RESOLVED to FALSE otherwise.
1743 * RESULT_TARGET is the path to the merged file produced by the internal
1744 * or external 3-way merge, which may contain conflict markers, in
1745 * repository normal form. DETRANSLATED_TARGET is the 'mine' version of
1746 * the file, also in RNF.
1748 static svn_error_t *
1749 resolve_text_conflict(svn_skel_t **work_items,
1750 svn_boolean_t *was_resolved,
1752 const char *local_abspath,
1753 const apr_array_header_t *merge_options,
1754 svn_wc_operation_t operation,
1755 const char *left_abspath,
1756 const char *right_abspath,
1757 const svn_wc_conflict_version_t *left_version,
1758 const svn_wc_conflict_version_t *right_version,
1759 const char *result_target,
1760 const char *detranslated_target,
1761 svn_wc_conflict_resolver_func2_t conflict_func,
1762 void *conflict_baton,
1763 apr_pool_t *result_pool,
1764 apr_pool_t *scratch_pool)
1766 svn_wc_conflict_result_t *result;
1767 svn_skel_t *work_item;
1768 svn_wc_conflict_description2_t *cdesc;
1770 const char *mime_type;
1773 *was_resolved = FALSE;
1775 /* Give the conflict resolution callback a chance to clean
1776 up the conflicts before we mark the file 'conflicted' */
1778 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
1779 scratch_pool, scratch_pool));
1781 cdesc = svn_wc_conflict_description_create_text2(local_abspath,
1783 mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
1784 cdesc->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
1785 cdesc->mime_type = mime_type;
1786 cdesc->base_abspath = left_abspath;
1787 cdesc->their_abspath = right_abspath;
1788 cdesc->my_abspath = detranslated_target;
1789 cdesc->merged_file = result_target;
1790 cdesc->operation = operation;
1791 cdesc->src_left_version = left_version;
1792 cdesc->src_right_version = right_version;
1794 SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
1797 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1798 _("Conflict callback violated API:"
1799 " returned no results"));
1801 if (result->save_merged)
1803 SVN_ERR(save_merge_result(work_items,
1805 /* Look for callback's own
1806 merged-file first: */
1808 ? result->merged_file
1810 result_pool, scratch_pool));
1813 if (result->choice != svn_wc_conflict_choose_postpone)
1815 SVN_ERR(eval_text_conflict_func_result(&work_item,
1822 /* ### Sure this is an abspath? */
1824 ? result->merged_file
1826 detranslated_target,
1827 result_pool, scratch_pool));
1828 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1831 *was_resolved = FALSE;
1833 return SVN_NO_ERROR;
1837 static svn_error_t *
1838 setup_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
1840 const char *local_abspath,
1841 svn_wc_operation_t operation,
1842 const svn_wc_conflict_version_t *left_version,
1843 const svn_wc_conflict_version_t *right_version,
1844 svn_wc_conflict_reason_t local_change,
1845 svn_wc_conflict_action_t incoming_change,
1846 apr_pool_t *result_pool,
1847 apr_pool_t *scratch_pool)
1849 svn_node_kind_t tc_kind;
1852 tc_kind = left_version->node_kind;
1853 else if (right_version)
1854 tc_kind = right_version->node_kind;
1856 tc_kind = svn_node_file; /* Avoid assertion */
1858 *desc = svn_wc_conflict_description_create_tree2(local_abspath, tc_kind,
1860 left_version, right_version,
1862 (*desc)->reason = local_change;
1863 (*desc)->action = incoming_change;
1865 return SVN_NO_ERROR;
1870 svn_wc__conflict_invoke_resolver(svn_wc__db_t *db,
1871 const char *local_abspath,
1872 const svn_skel_t *conflict_skel,
1873 const apr_array_header_t *merge_options,
1874 svn_wc_conflict_resolver_func2_t resolver_func,
1875 void *resolver_baton,
1876 svn_cancel_func_t cancel_func,
1878 apr_pool_t *scratch_pool)
1880 svn_boolean_t text_conflicted;
1881 svn_boolean_t prop_conflicted;
1882 svn_boolean_t tree_conflicted;
1883 svn_wc_operation_t operation;
1884 const apr_array_header_t *locations;
1885 const svn_wc_conflict_version_t *left_version = NULL;
1886 const svn_wc_conflict_version_t *right_version = NULL;
1888 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
1889 &text_conflicted, &prop_conflicted,
1891 db, local_abspath, conflict_skel,
1892 scratch_pool, scratch_pool));
1894 if (locations && locations->nelts > 0)
1895 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
1897 if (locations && locations->nelts > 1)
1898 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
1900 /* Quick and dirty compatibility wrapper. My guess would be that most resolvers
1901 would want to look at all properties at the same time.
1903 ### svn currently only invokes this from the merge code to collect the list of
1904 ### conflicted paths. Eventually this code will be the base for 'svn resolve'
1905 ### and at that time the test coverage will improve
1907 if (prop_conflicted)
1909 apr_hash_t *old_props;
1910 apr_hash_t *mine_props;
1911 apr_hash_t *their_props;
1912 apr_hash_t *old_their_props;
1913 apr_hash_t *conflicted;
1914 apr_pool_t *iterpool;
1915 apr_hash_index_t *hi;
1916 svn_boolean_t mark_resolved = TRUE;
1918 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
1925 scratch_pool, scratch_pool));
1927 if (operation == svn_wc_operation_merge)
1928 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
1929 scratch_pool, scratch_pool));
1931 old_props = old_their_props;
1933 iterpool = svn_pool_create(scratch_pool);
1935 for (hi = apr_hash_first(scratch_pool, conflicted);
1937 hi = apr_hash_next(hi))
1939 const char *propname = svn__apr_hash_index_key(hi);
1940 svn_boolean_t conflict_remains = TRUE;
1942 svn_pool_clear(iterpool);
1945 SVN_ERR(cancel_func(cancel_baton));
1947 SVN_ERR(generate_propconflict(&conflict_remains,
1954 ? svn_hash_gets(old_props, propname)
1957 ? svn_hash_gets(mine_props, propname)
1960 ? svn_hash_gets(old_their_props, propname)
1963 ? svn_hash_gets(their_props, propname)
1965 resolver_func, resolver_baton,
1968 if (conflict_remains)
1969 mark_resolved = FALSE;
1974 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
1979 if (text_conflicted)
1981 const char *mine_abspath;
1982 const char *their_original_abspath;
1983 const char *their_abspath;
1984 svn_skel_t *work_items;
1985 svn_boolean_t was_resolved;
1987 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
1988 &their_original_abspath,
1992 scratch_pool, scratch_pool));
1994 SVN_ERR(resolve_text_conflict(&work_items, &was_resolved,
1998 their_original_abspath, their_abspath,
1999 left_version, right_version,
2000 local_abspath, mine_abspath,
2001 resolver_func, resolver_baton,
2002 scratch_pool, scratch_pool));
2008 SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_items,
2010 SVN_ERR(svn_wc__wq_run(db, local_abspath,
2011 cancel_func, cancel_baton,
2014 SVN_ERR(svn_wc__mark_resolved_text_conflict(db, local_abspath,
2019 if (tree_conflicted)
2021 svn_wc_conflict_reason_t local_change;
2022 svn_wc_conflict_action_t incoming_change;
2023 svn_wc_conflict_result_t *result;
2024 svn_wc_conflict_description2_t *desc;
2026 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change,
2031 scratch_pool, scratch_pool));
2033 SVN_ERR(setup_tree_conflict_desc(&desc,
2035 operation, left_version, right_version,
2036 local_change, incoming_change,
2037 scratch_pool, scratch_pool));
2039 /* Tell the resolver func about this conflict. */
2040 SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
2043 /* Ignore the result. We cannot apply it here since this code runs
2044 * during an update or merge operation. Tree conflicts are always
2045 * postponed and resolved after the operation has completed. */
2048 return SVN_NO_ERROR;
2051 /* Read all property conflicts contained in CONFLICT_SKEL into
2052 * individual conflict descriptions, and append those descriptions
2053 * to the CONFLICTS array.
2055 * If NOT create_tempfiles, always create a legacy property conflict
2058 * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
2059 * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
2061 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
2063 static svn_error_t *
2064 read_prop_conflicts(apr_array_header_t *conflicts,
2066 const char *local_abspath,
2067 svn_skel_t *conflict_skel,
2068 svn_boolean_t create_tempfiles,
2069 svn_node_kind_t node_kind,
2070 svn_wc_operation_t operation,
2071 const svn_wc_conflict_version_t *left_version,
2072 const svn_wc_conflict_version_t *right_version,
2073 apr_pool_t *result_pool,
2074 apr_pool_t *scratch_pool)
2076 const char *prop_reject_file;
2077 apr_hash_t *my_props;
2078 apr_hash_t *their_old_props;
2079 apr_hash_t *their_props;
2080 apr_hash_t *conflicted_props;
2081 apr_hash_index_t *hi;
2082 apr_pool_t *iterpool;
2084 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2091 scratch_pool, scratch_pool));
2093 if ((! create_tempfiles) || apr_hash_count(conflicted_props) == 0)
2095 /* Legacy prop conflict with only a .reject file. */
2096 svn_wc_conflict_description2_t *desc;
2098 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2102 /* ### This should be changed. The prej file should be stored
2103 * ### separately from the other files. We need to rev the
2104 * ### conflict description struct for this. */
2105 desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file);
2107 desc->operation = operation;
2108 desc->src_left_version = left_version;
2109 desc->src_right_version = right_version;
2111 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc;
2113 return SVN_NO_ERROR;
2116 iterpool = svn_pool_create(scratch_pool);
2117 for (hi = apr_hash_first(scratch_pool, conflicted_props);
2119 hi = apr_hash_next(hi))
2121 const char *propname = svn__apr_hash_index_key(hi);
2122 svn_string_t *old_value;
2123 svn_string_t *my_value;
2124 svn_string_t *their_value;
2125 svn_wc_conflict_description2_t *desc;
2127 svn_pool_clear(iterpool);
2129 desc = svn_wc_conflict_description_create_prop2(local_abspath,
2134 desc->operation = operation;
2135 desc->src_left_version = left_version;
2136 desc->src_right_version = right_version;
2138 desc->property_name = apr_pstrdup(result_pool, propname);
2140 my_value = svn_hash_gets(my_props, propname);
2141 their_value = svn_hash_gets(their_props, propname);
2142 old_value = svn_hash_gets(their_old_props, propname);
2144 /* Compute the incoming side of the conflict ('action'). */
2145 if (their_value == NULL)
2146 desc->action = svn_wc_conflict_action_delete;
2147 else if (old_value == NULL)
2148 desc->action = svn_wc_conflict_action_add;
2150 desc->action = svn_wc_conflict_action_edit;
2152 /* Compute the local side of the conflict ('reason'). */
2153 if (my_value == NULL)
2154 desc->reason = svn_wc_conflict_reason_deleted;
2155 else if (old_value == NULL)
2156 desc->reason = svn_wc_conflict_reason_added;
2158 desc->reason = svn_wc_conflict_reason_edited;
2160 /* ### This should be changed. The prej file should be stored
2161 * ### separately from the other files. We need to rev the
2162 * ### conflict description struct for this. */
2163 desc->their_abspath = apr_pstrdup(result_pool, prop_reject_file);
2165 /* ### This should be changed. The conflict description for
2166 * ### props should contain these values as svn_string_t,
2167 * ### rather than in temporary files. We need to rev the
2168 * ### conflict description struct for this. */
2174 SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL,
2175 svn_io_file_del_on_pool_cleanup,
2176 result_pool, iterpool));
2177 len = my_value->len;
2178 SVN_ERR(svn_stream_write(s, my_value->data, &len));
2179 SVN_ERR(svn_stream_close(s));
2187 /* ### Currently, their_abspath is used for the prop reject file.
2188 * ### Put their value into merged instead...
2189 * ### We need to rev the conflict description struct to fix this. */
2190 SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL,
2191 svn_io_file_del_on_pool_cleanup,
2192 result_pool, iterpool));
2193 len = their_value->len;
2194 SVN_ERR(svn_stream_write(s, their_value->data, &len));
2195 SVN_ERR(svn_stream_close(s));
2203 SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL,
2204 svn_io_file_del_on_pool_cleanup,
2205 result_pool, iterpool));
2206 len = old_value->len;
2207 SVN_ERR(svn_stream_write(s, old_value->data, &len));
2208 SVN_ERR(svn_stream_close(s));
2211 APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t*) = desc;
2213 svn_pool_destroy(iterpool);
2215 return SVN_NO_ERROR;
2219 svn_wc__read_conflicts(const apr_array_header_t **conflicts,
2221 const char *local_abspath,
2222 svn_boolean_t create_tempfiles,
2223 apr_pool_t *result_pool,
2224 apr_pool_t *scratch_pool)
2226 svn_skel_t *conflict_skel;
2227 apr_array_header_t *cflcts;
2228 svn_boolean_t prop_conflicted;
2229 svn_boolean_t text_conflicted;
2230 svn_boolean_t tree_conflicted;
2231 svn_wc_operation_t operation;
2232 const apr_array_header_t *locations;
2233 const svn_wc_conflict_version_t *left_version = NULL;
2234 const svn_wc_conflict_version_t *right_version = NULL;
2236 SVN_ERR(svn_wc__db_read_conflict(&conflict_skel, db, local_abspath,
2237 scratch_pool, scratch_pool));
2241 /* Some callers expect not NULL */
2242 *conflicts = apr_array_make(result_pool, 0,
2243 sizeof(svn_wc_conflict_description2_t*));;
2244 return SVN_NO_ERROR;
2247 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted,
2248 &prop_conflicted, &tree_conflicted,
2249 db, local_abspath, conflict_skel,
2250 result_pool, scratch_pool));
2252 cflcts = apr_array_make(result_pool, 4,
2253 sizeof(svn_wc_conflict_description2_t*));
2255 if (locations && locations->nelts > 0)
2256 left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
2257 if (locations && locations->nelts > 1)
2258 right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
2260 if (prop_conflicted)
2262 svn_node_kind_t node_kind
2263 = left_version ? left_version->node_kind : svn_node_unknown;
2265 SVN_ERR(read_prop_conflicts(cflcts, db, local_abspath, conflict_skel,
2266 create_tempfiles, node_kind,
2267 operation, left_version, right_version,
2268 result_pool, scratch_pool));
2271 if (text_conflicted)
2274 const char *mime_type;
2275 svn_wc_conflict_description2_t *desc;
2276 desc = svn_wc_conflict_description_create_text2(local_abspath,
2279 desc->operation = operation;
2280 desc->src_left_version = left_version;
2281 desc->src_right_version = right_version;
2283 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
2284 scratch_pool, scratch_pool));
2285 mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
2286 desc->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
2287 desc->mime_type = mime_type;
2289 SVN_ERR(svn_wc__conflict_read_text_conflict(&desc->my_abspath,
2290 &desc->base_abspath,
2291 &desc->their_abspath,
2294 result_pool, scratch_pool));
2296 desc->merged_file = apr_pstrdup(result_pool, local_abspath);
2298 APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t*) = desc;
2301 if (tree_conflicted)
2303 svn_wc_conflict_reason_t local_change;
2304 svn_wc_conflict_action_t incoming_change;
2305 svn_wc_conflict_description2_t *desc;
2307 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change,
2312 scratch_pool, scratch_pool));
2314 SVN_ERR(setup_tree_conflict_desc(&desc,
2316 operation, left_version, right_version,
2317 local_change, incoming_change,
2318 result_pool, scratch_pool));
2320 APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc;
2323 *conflicts = cflcts;
2324 return SVN_NO_ERROR;
2328 /*** Resolving a conflict automatically ***/
2330 /* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the
2331 * working copy at DB/WRI_ABSPATH.
2333 * Set *WORK_ITEMS to a new work item that, when run, will delete the
2334 * artifact file; or to NULL if there is no file to delete.
2336 * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its
2337 * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND
2338 * may be NULL if not required.
2340 static svn_error_t *
2341 remove_artifact_file_if_exists(svn_skel_t **work_items,
2342 svn_boolean_t *file_found,
2344 const char *wri_abspath,
2345 const char *artifact_file_abspath,
2346 apr_pool_t *result_pool,
2347 apr_pool_t *scratch_pool)
2350 if (artifact_file_abspath)
2352 svn_node_kind_t node_kind;
2354 SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind,
2356 if (node_kind == svn_node_file)
2358 SVN_ERR(svn_wc__wq_build_file_remove(work_items,
2360 artifact_file_abspath,
2361 result_pool, scratch_pool));
2367 return SVN_NO_ERROR;
2371 * Resolve the text conflict found in DB/LOCAL_ABSPATH according
2372 * to CONFLICT_CHOICE.
2374 * It is not an error if there is no text conflict. If a text conflict
2375 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2377 * Note: When there are no conflict markers to remove there is no existing
2378 * text conflict; just a database containing old information, which we should
2379 * remove to avoid checking all the time. Resolving a text conflict by
2380 * removing all the marker files is a fully supported scenario since
2383 static svn_error_t *
2384 resolve_text_conflict_on_node(svn_boolean_t *did_resolve,
2386 const char *local_abspath,
2387 svn_wc_conflict_choice_t conflict_choice,
2388 const char *merged_file,
2389 svn_cancel_func_t cancel_func,
2391 apr_pool_t *scratch_pool)
2393 const char *conflict_old = NULL;
2394 const char *conflict_new = NULL;
2395 const char *conflict_working = NULL;
2396 const char *auto_resolve_src;
2397 svn_skel_t *work_item;
2398 svn_skel_t *work_items = NULL;
2399 svn_skel_t *conflicts;
2400 svn_wc_operation_t operation;
2401 svn_boolean_t text_conflicted;
2403 *did_resolve = FALSE;
2405 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
2406 scratch_pool, scratch_pool));
2408 return SVN_NO_ERROR;
2410 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, &text_conflicted,
2411 NULL, NULL, db, local_abspath, conflicts,
2412 scratch_pool, scratch_pool));
2413 if (!text_conflicted)
2414 return SVN_NO_ERROR;
2416 SVN_ERR(svn_wc__conflict_read_text_conflict(&conflict_working,
2419 db, local_abspath, conflicts,
2420 scratch_pool, scratch_pool));
2422 /* Handle automatic conflict resolution before the temporary files are
2423 * deleted, if necessary. */
2424 switch (conflict_choice)
2426 case svn_wc_conflict_choose_base:
2427 auto_resolve_src = conflict_old;
2429 case svn_wc_conflict_choose_mine_full:
2430 auto_resolve_src = conflict_working;
2432 case svn_wc_conflict_choose_theirs_full:
2433 auto_resolve_src = conflict_new;
2435 case svn_wc_conflict_choose_merged:
2436 auto_resolve_src = merged_file;
2438 case svn_wc_conflict_choose_theirs_conflict:
2439 case svn_wc_conflict_choose_mine_conflict:
2441 if (conflict_old && conflict_working && conflict_new)
2443 const char *temp_dir;
2444 svn_stream_t *tmp_stream = NULL;
2446 svn_diff_conflict_display_style_t style =
2447 conflict_choice == svn_wc_conflict_choose_theirs_conflict
2448 ? svn_diff_conflict_display_latest
2449 : svn_diff_conflict_display_modified;
2451 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
2455 SVN_ERR(svn_stream_open_unique(&tmp_stream,
2458 svn_io_file_del_on_pool_cleanup,
2459 scratch_pool, scratch_pool));
2461 SVN_ERR(svn_diff_file_diff3_2(&diff,
2465 svn_diff_file_options_create(
2468 SVN_ERR(svn_diff_file_output_merge2(tmp_stream, diff,
2472 /* markers ignored */
2473 NULL, NULL, NULL, NULL,
2476 SVN_ERR(svn_stream_close(tmp_stream));
2479 auto_resolve_src = NULL;
2483 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2484 _("Invalid 'conflict_result' argument"));
2487 if (auto_resolve_src)
2489 SVN_ERR(svn_wc__wq_build_file_copy_translated(
2490 &work_item, db, local_abspath,
2491 auto_resolve_src, local_abspath, scratch_pool, scratch_pool));
2492 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2494 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db,
2496 scratch_pool, scratch_pool));
2497 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2500 /* Legacy behavior: Only report text conflicts as resolved when at least
2501 one conflict marker file exists.
2503 If not the UI shows the conflict as already resolved
2504 (and in this case we just remove the in-db conflict) */
2506 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
2507 db, local_abspath, conflict_old,
2508 scratch_pool, scratch_pool));
2509 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2511 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
2512 db, local_abspath, conflict_new,
2513 scratch_pool, scratch_pool));
2514 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2516 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
2517 db, local_abspath, conflict_working,
2518 scratch_pool, scratch_pool));
2519 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2521 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2523 work_items, scratch_pool));
2524 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2527 return SVN_NO_ERROR;
2531 * Resolve the property conflicts found in DB/LOCAL_ABSPATH according
2532 * to CONFLICT_CHOICE.
2534 * It is not an error if there is no prop conflict. If a prop conflict
2535 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2537 * Note: When there are no conflict markers on-disk to remove there is
2538 * no existing text conflict (unless we are still in the process of
2539 * creating the text conflict and we didn't register a marker file yet).
2540 * In this case the database contains old information, which we should
2541 * remove to avoid checking the next time. Resolving a property conflict
2542 * by just removing the marker file is a fully supported scenario since
2545 * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
2546 * In my opinion, 'mine_full'/'theirs_full' should select
2547 * the entire set of properties from 'mine' or 'theirs' respectively,
2548 * while 'mine_conflict'/'theirs_conflict' should select just the
2549 * properties that are in conflict. Or, '_full' should select the
2550 * entire property whereas '_conflict' should do a text merge within
2551 * each property, selecting hunks. Or all three kinds of behaviour
2552 * should be available (full set of props, full value of conflicting
2553 * props, or conflicting text hunks).
2554 * ### BH: If we make *_full select the full set of properties, we should
2555 * check if we shouldn't make it also select the full text for files.
2557 * ### TODO [JAF] All this complexity should not be down here in libsvn_wc
2558 * but in a layer above.
2560 * ### TODO [JAF] Options for 'base' should be like options for 'mine' and
2561 * for 'theirs' -- choose full set of props, full value of conflicting
2562 * props, or conflicting text hunks.
2565 static svn_error_t *
2566 resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
2568 const char *local_abspath,
2569 const char *conflicted_propname,
2570 svn_wc_conflict_choice_t conflict_choice,
2571 const char *merged_file,
2572 svn_cancel_func_t cancel_func,
2574 apr_pool_t *scratch_pool)
2576 const char *prop_reject_file;
2577 apr_hash_t *mine_props;
2578 apr_hash_t *their_old_props;
2579 apr_hash_t *their_props;
2580 apr_hash_t *conflicted_props;
2581 apr_hash_t *old_props;
2582 apr_hash_t *resolve_from = NULL;
2583 svn_skel_t *work_items = NULL;
2584 svn_skel_t *conflicts;
2585 svn_wc_operation_t operation;
2586 svn_boolean_t prop_conflicted;
2588 *did_resolve = FALSE;
2590 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
2591 scratch_pool, scratch_pool));
2594 return SVN_NO_ERROR;
2596 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2597 NULL, db, local_abspath, conflicts,
2598 scratch_pool, scratch_pool));
2599 if (!prop_conflicted)
2600 return SVN_NO_ERROR;
2602 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2603 &mine_props, &their_old_props,
2604 &their_props, &conflicted_props,
2605 db, local_abspath, conflicts,
2606 scratch_pool, scratch_pool));
2608 if (operation == svn_wc_operation_merge)
2609 SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
2610 scratch_pool, scratch_pool));
2612 old_props = their_old_props;
2614 /* We currently handle *_conflict as *_full as this argument is currently
2615 always applied for all conflicts on a node at the same time. Giving
2616 an error would break some tests that assumed that this would just
2617 resolve property conflicts to working.
2619 An alternative way to handle these conflicts would be to just copy all
2620 property state from mine/theirs on the _full option instead of just the
2621 conflicted properties. In some ways this feels like a sensible option as
2622 that would take both properties and text from mine/theirs, but when not
2623 both properties and text are conflicted we would fail in doing so.
2625 switch (conflict_choice)
2627 case svn_wc_conflict_choose_base:
2628 resolve_from = their_old_props ? their_old_props : old_props;
2630 case svn_wc_conflict_choose_mine_full:
2631 case svn_wc_conflict_choose_mine_conflict:
2632 resolve_from = mine_props;
2634 case svn_wc_conflict_choose_theirs_full:
2635 case svn_wc_conflict_choose_theirs_conflict:
2636 resolve_from = their_props;
2638 case svn_wc_conflict_choose_merged:
2639 if (merged_file && conflicted_propname[0] != '\0')
2641 apr_hash_t *actual_props;
2642 svn_stream_t *stream;
2643 svn_string_t *merged_propval;
2645 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2646 scratch_pool, scratch_pool));
2647 resolve_from = actual_props;
2649 SVN_ERR(svn_stream_open_readonly(&stream, merged_file,
2650 scratch_pool, scratch_pool));
2651 SVN_ERR(svn_string_from_stream(&merged_propval, stream,
2652 scratch_pool, scratch_pool));
2653 svn_hash_sets(resolve_from, conflicted_propname, merged_propval);
2656 resolve_from = NULL;
2659 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2660 _("Invalid 'conflict_result' argument"));
2663 if (conflicted_props && apr_hash_count(conflicted_props) && resolve_from)
2665 apr_hash_index_t *hi;
2666 apr_hash_t *actual_props;
2668 SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2669 scratch_pool, scratch_pool));
2671 for (hi = apr_hash_first(scratch_pool, conflicted_props);
2673 hi = apr_hash_next(hi))
2675 const char *propname = svn__apr_hash_index_key(hi);
2676 svn_string_t *new_value = NULL;
2678 new_value = svn_hash_gets(resolve_from, propname);
2680 svn_hash_sets(actual_props, propname, new_value);
2682 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props,
2687 /* Legacy behavior: Only report property conflicts as resolved when the
2688 property reject file exists
2690 If not the UI shows the conflict as already resolved
2691 (and in this case we just remove the in-db conflict) */
2694 svn_skel_t *work_item;
2696 SVN_ERR(remove_artifact_file_if_exists(&work_item, did_resolve,
2697 db, local_abspath, prop_reject_file,
2698 scratch_pool, scratch_pool));
2699 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
2702 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE,
2703 work_items, scratch_pool));
2704 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2707 return SVN_NO_ERROR;
2711 * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
2714 * It is not an error if there is no tree conflict. If a tree conflict
2715 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2717 * It is not an error if there is no tree conflict.
2719 static svn_error_t *
2720 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
2722 const char *local_abspath,
2723 svn_wc_conflict_choice_t conflict_choice,
2724 svn_wc_notify_func2_t notify_func,
2726 svn_cancel_func_t cancel_func,
2728 apr_pool_t *scratch_pool)
2730 svn_wc_conflict_reason_t reason;
2731 svn_wc_conflict_action_t action;
2732 svn_skel_t *conflicts;
2733 svn_wc_operation_t operation;
2734 svn_boolean_t tree_conflicted;
2736 *did_resolve = FALSE;
2738 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
2739 scratch_pool, scratch_pool));
2741 return SVN_NO_ERROR;
2743 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
2744 &tree_conflicted, db, local_abspath,
2745 conflicts, scratch_pool, scratch_pool));
2746 if (!tree_conflicted)
2747 return SVN_NO_ERROR;
2749 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL,
2752 scratch_pool, scratch_pool));
2754 if (operation == svn_wc_operation_update
2755 || operation == svn_wc_operation_switch)
2757 if (reason == svn_wc_conflict_reason_deleted ||
2758 reason == svn_wc_conflict_reason_replaced)
2760 if (conflict_choice == svn_wc_conflict_choose_merged)
2762 /* Break moves for any children moved out of this directory,
2763 * and leave this directory deleted. */
2764 SVN_ERR(svn_wc__db_resolve_break_moved_away_children(
2765 db, local_abspath, notify_func, notify_baton,
2767 *did_resolve = TRUE;
2769 else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2771 /* Raised moved-away conflicts on any children moved out of
2772 * this directory, and leave this directory deleted.
2773 * The newly conflicted moved-away children will be updated
2774 * if they are resolved with 'mine_conflict' as well. */
2775 SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
2776 db, local_abspath, notify_func, notify_baton,
2778 *did_resolve = TRUE;
2781 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2783 _("Tree conflict can only be resolved to "
2784 "'working' or 'mine-conflict' state; "
2785 "'%s' not resolved"),
2786 svn_dirent_local_style(local_abspath,
2789 else if (reason == svn_wc_conflict_reason_moved_away
2790 && action == svn_wc_conflict_action_edit)
2792 /* After updates, we can resolve local moved-away
2793 * vs. any incoming change, either by updating the
2794 * moved-away node (mine-conflict) or by breaking the
2795 * move (theirs-conflict). */
2796 if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2798 SVN_ERR(svn_wc__db_update_moved_away_conflict_victim(
2800 notify_func, notify_baton,
2801 cancel_func, cancel_baton,
2803 *did_resolve = TRUE;
2805 else if (conflict_choice == svn_wc_conflict_choose_merged)
2807 /* We must break the move if the user accepts the current
2808 * working copy state instead of updating the move.
2809 * Else the move would be left in an invalid state. */
2811 /* ### This breaks the move but leaves the conflict
2812 ### involving the move until
2813 ### svn_wc__db_op_mark_resolved. */
2814 SVN_ERR(svn_wc__db_resolve_break_moved_away(db, local_abspath,
2818 *did_resolve = TRUE;
2821 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2823 _("Tree conflict can only be resolved to "
2824 "'working' or 'mine-conflict' state; "
2825 "'%s' not resolved"),
2826 svn_dirent_local_style(local_abspath,
2831 if (! *did_resolve && conflict_choice != svn_wc_conflict_choose_merged)
2833 /* For other tree conflicts, there is no way to pick
2834 * theirs-full or mine-full, etc. Throw an error if the
2835 * user expects us to be smarter than we really are. */
2836 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2838 _("Tree conflict can only be "
2839 "resolved to 'working' state; "
2840 "'%s' not resolved"),
2841 svn_dirent_local_style(local_abspath,
2845 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE,
2846 NULL, scratch_pool));
2847 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2849 return SVN_NO_ERROR;
2853 svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
2854 const char *local_abspath,
2855 apr_pool_t *scratch_pool)
2857 svn_boolean_t ignored_result;
2859 return svn_error_trace(resolve_text_conflict_on_node(
2862 svn_wc_conflict_choose_merged, NULL,
2868 svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db,
2869 const char *local_abspath,
2870 apr_pool_t *scratch_pool)
2872 svn_boolean_t ignored_result;
2874 return svn_error_trace(resolve_prop_conflict_on_node(
2876 db, local_abspath, "",
2877 svn_wc_conflict_choose_merged, NULL,
2883 /* Baton for conflict_status_walker */
2884 struct conflict_status_walker_baton
2887 svn_boolean_t resolve_text;
2888 const char *resolve_prop;
2889 svn_boolean_t resolve_tree;
2890 svn_wc_conflict_choice_t conflict_choice;
2891 svn_wc_conflict_resolver_func2_t conflict_func;
2892 void *conflict_baton;
2893 svn_cancel_func_t cancel_func;
2895 svn_wc_notify_func2_t notify_func;
2899 /* Implements svn_wc_status4_t to walk all conflicts to resolve.
2901 static svn_error_t *
2902 conflict_status_walker(void *baton,
2903 const char *local_abspath,
2904 const svn_wc_status3_t *status,
2905 apr_pool_t *scratch_pool)
2907 struct conflict_status_walker_baton *cswb = baton;
2908 svn_wc__db_t *db = cswb->db;
2910 const apr_array_header_t *conflicts;
2911 apr_pool_t *iterpool;
2913 svn_boolean_t resolved = FALSE;
2915 if (!status->conflicted)
2916 return SVN_NO_ERROR;
2918 iterpool = svn_pool_create(scratch_pool);
2920 SVN_ERR(svn_wc__read_conflicts(&conflicts, db, local_abspath, TRUE,
2921 scratch_pool, iterpool));
2923 for (i = 0; i < conflicts->nelts; i++)
2925 const svn_wc_conflict_description2_t *cd;
2926 svn_boolean_t did_resolve;
2927 svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
2928 const char *merged_file = NULL;
2930 cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
2932 if ((cd->kind == svn_wc_conflict_kind_property && !cswb->resolve_prop)
2933 || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text)
2934 || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree))
2936 continue; /* Easy out. Don't call resolver func and ignore result */
2939 svn_pool_clear(iterpool);
2941 if (my_choice == svn_wc_conflict_choose_unspecified)
2943 svn_wc_conflict_result_t *result;
2945 if (!cswb->conflict_func)
2946 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2947 _("No conflict-callback and no "
2948 "pre-defined conflict-choice provided"));
2950 SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton,
2951 iterpool, iterpool));
2953 my_choice = result->choice;
2954 merged_file = result->merged_file;
2955 /* ### Bug: ignores result->save_merged */
2959 if (my_choice == svn_wc_conflict_choose_postpone)
2964 case svn_wc_conflict_kind_tree:
2965 if (!cswb->resolve_tree)
2967 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
2980 case svn_wc_conflict_kind_text:
2981 if (!cswb->resolve_text)
2984 SVN_ERR(resolve_text_conflict_on_node(&did_resolve,
2997 case svn_wc_conflict_kind_property:
2998 if (!cswb->resolve_prop)
3001 if (*cswb->resolve_prop != '\0' &&
3002 strcmp(cswb->resolve_prop, cd->property_name) != 0)
3004 break; /* This is not the property we want to resolve. */
3007 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
3022 /* We can't resolve other conflict types */
3028 if (cswb->notify_func && resolved)
3029 cswb->notify_func(cswb->notify_baton,
3030 svn_wc_create_notify(local_abspath,
3031 svn_wc_notify_resolved,
3035 svn_pool_destroy(iterpool);
3037 return SVN_NO_ERROR;
3041 svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
3042 const char *local_abspath,
3044 svn_boolean_t resolve_text,
3045 const char *resolve_prop,
3046 svn_boolean_t resolve_tree,
3047 svn_wc_conflict_choice_t conflict_choice,
3048 svn_wc_conflict_resolver_func2_t conflict_func,
3049 void *conflict_baton,
3050 svn_cancel_func_t cancel_func,
3052 svn_wc_notify_func2_t notify_func,
3054 apr_pool_t *scratch_pool)
3056 svn_node_kind_t kind;
3057 svn_boolean_t conflicted;
3058 struct conflict_status_walker_baton cswb;
3060 /* ### the underlying code does NOT support resolving individual
3061 ### properties. bail out if the caller tries it. */
3062 if (resolve_prop != NULL && *resolve_prop != '\0')
3063 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
3064 U_("Resolving a single property is not (yet) "
3067 /* ### Just a versioned check? */
3068 /* Conflicted is set to allow invoking on actual only nodes */
3069 SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
3070 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3071 NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
3072 NULL, NULL, NULL, NULL, NULL, NULL,
3073 wc_ctx->db, local_abspath,
3074 scratch_pool, scratch_pool));
3076 /* When the implementation still used the entry walker, depth
3077 unknown was translated to infinity. */
3078 if (kind != svn_node_dir)
3079 depth = svn_depth_empty;
3080 else if (depth == svn_depth_unknown)
3081 depth = svn_depth_infinity;
3083 cswb.db = wc_ctx->db;
3084 cswb.resolve_text = resolve_text;
3085 cswb.resolve_prop = resolve_prop;
3086 cswb.resolve_tree = resolve_tree;
3087 cswb.conflict_choice = conflict_choice;
3089 cswb.conflict_func = conflict_func;
3090 cswb.conflict_baton = conflict_baton;
3092 cswb.cancel_func = cancel_func;
3093 cswb.cancel_baton = cancel_baton;
3095 cswb.notify_func = notify_func;
3096 cswb.notify_baton = notify_baton;
3099 notify_func(notify_baton,
3100 svn_wc_create_notify(local_abspath,
3101 svn_wc_notify_conflict_resolver_starting,
3105 SVN_ERR(svn_wc_walk_status(wc_ctx,
3108 FALSE /* get_all */,
3109 FALSE /* no_ignore */,
3110 TRUE /* ignore_text_mods */,
3111 NULL /* ignore_patterns */,
3112 conflict_status_walker, &cswb,
3113 cancel_func, cancel_baton,
3117 notify_func(notify_baton,
3118 svn_wc_create_notify(local_abspath,
3119 svn_wc_notify_conflict_resolver_done,
3123 return SVN_NO_ERROR;
3127 svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
3128 const char *local_abspath,
3130 svn_boolean_t resolve_text,
3131 const char *resolve_prop,
3132 svn_boolean_t resolve_tree,
3133 svn_wc_conflict_choice_t conflict_choice,
3134 svn_cancel_func_t cancel_func,
3136 svn_wc_notify_func2_t notify_func,
3138 apr_pool_t *scratch_pool)
3140 return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath,
3141 depth, resolve_text,
3142 resolve_prop, resolve_tree,
3145 cancel_func, cancel_baton,
3146 notify_func, notify_baton,
3150 /* Constructor for the result-structure returned by conflict callbacks. */
3151 svn_wc_conflict_result_t *
3152 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
3153 const char *merged_file,
3156 svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
3157 result->choice = choice;
3158 result->merged_file = merged_file;
3159 result->save_merged = FALSE;
3161 /* If we add more fields to svn_wc_conflict_result_t, add them here. */