2 * conflicts.c: routines for managing conflict data.
3 * NOTE: this code doesn't know where the conflict is
6 * ====================================================================
7 * Licensed to the Apache Software Foundation (ASF) under one
8 * or more contributor license agreements. See the NOTICE file
9 * distributed with this work for additional information
10 * regarding copyright ownership. The ASF licenses this file
11 * to you under the Apache License, Version 2.0 (the
12 * "License"); you may not use this file except in compliance
13 * with the License. You may obtain a copy of the License at
15 * http://www.apache.org/licenses/LICENSE-2.0
17 * Unless required by applicable law or agreed to in writing,
18 * software distributed under the License is distributed on an
19 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20 * KIND, either express or implied. See the License for the
21 * specific language governing permissions and limitations
23 * ====================================================================
30 #include <apr_pools.h>
31 #include <apr_tables.h>
33 #include <apr_errno.h>
36 #include "svn_types.h"
37 #include "svn_pools.h"
38 #include "svn_string.h"
39 #include "svn_error.h"
40 #include "svn_dirent_uri.h"
47 #include "conflicts.h"
48 #include "workqueue.h"
51 #include "private/svn_wc_private.h"
52 #include "private/svn_skel.h"
53 #include "private/svn_sorts_private.h"
54 #include "private/svn_string_private.h"
56 #include "svn_private_config.h"
58 /* --------------------------------------------------------------------
59 * Conflict skel management
63 svn_wc__conflict_skel_create(apr_pool_t
*result_pool
)
65 svn_skel_t
*conflict_skel
= svn_skel__make_empty_list(result_pool
);
67 /* Add empty CONFLICTS list */
68 svn_skel__prepend(svn_skel__make_empty_list(result_pool
), conflict_skel
);
70 /* Add empty WHY list */
71 svn_skel__prepend(svn_skel__make_empty_list(result_pool
), conflict_skel
);
77 svn_wc__conflict_skel_is_complete(svn_boolean_t
*complete
,
78 const svn_skel_t
*conflict_skel
)
82 if (svn_skel__list_length(conflict_skel
) < 2)
83 return svn_error_create(SVN_ERR_INCOMPLETE_DATA
, NULL
,
84 _("Not a conflict skel"));
86 if (svn_skel__list_length(conflict_skel
->children
) < 2)
87 return SVN_NO_ERROR
; /* WHY is not set */
89 if (svn_skel__list_length(conflict_skel
->children
->next
) == 0)
90 return SVN_NO_ERROR
; /* No conflict set */
96 /* Serialize a svn_wc_conflict_version_t before the existing data in skel */
98 conflict__prepend_location(svn_skel_t
*skel
,
99 const svn_wc_conflict_version_t
*location
,
100 svn_boolean_t allow_NULL
,
101 apr_pool_t
*result_pool
,
102 apr_pool_t
*scratch_pool
)
105 SVN_ERR_ASSERT(location
|| allow_NULL
);
109 svn_skel__prepend(svn_skel__make_empty_list(result_pool
), skel
);
113 /* ("subversion" repos_root_url repos_uuid repos_relpath rev kind) */
114 loc
= svn_skel__make_empty_list(result_pool
);
116 svn_skel__prepend_str(svn_node_kind_to_word(location
->node_kind
),
119 svn_skel__prepend_int(location
->peg_rev
, loc
, result_pool
);
121 svn_skel__prepend_str(apr_pstrdup(result_pool
, location
->path_in_repos
), loc
,
124 if (!location
->repos_uuid
) /* Can theoretically be NULL */
125 svn_skel__prepend(svn_skel__make_empty_list(result_pool
), loc
);
127 svn_skel__prepend_str(location
->repos_uuid
, loc
, result_pool
);
129 svn_skel__prepend_str(apr_pstrdup(result_pool
, location
->repos_url
), loc
,
132 svn_skel__prepend_str(SVN_WC__CONFLICT_SRC_SUBVERSION
, loc
, result_pool
);
134 svn_skel__prepend(loc
, skel
);
138 /* Deserialize a svn_wc_conflict_version_t from the skel.
139 Set *LOCATION to NULL when the data is not a svn_wc_conflict_version_t. */
141 conflict__read_location(svn_wc_conflict_version_t
**location
,
142 const svn_skel_t
*skel
,
143 apr_pool_t
*result_pool
,
144 apr_pool_t
*scratch_pool
)
146 const char *repos_root_url
;
147 const char *repos_uuid
;
148 const char *repos_relpath
;
149 svn_revnum_t revision
;
151 svn_node_kind_t node_kind
; /* note that 'none' is a legitimate value */
152 const char *kind_str
;
154 const svn_skel_t
*c
= skel
->children
;
156 if (!svn_skel__matches_atom(c
, SVN_WC__CONFLICT_SRC_SUBVERSION
))
163 repos_root_url
= apr_pstrmemdup(result_pool
, c
->data
, c
->len
);
167 repos_uuid
= apr_pstrmemdup(result_pool
, c
->data
, c
->len
);
172 repos_relpath
= apr_pstrmemdup(result_pool
, c
->data
, c
->len
);
175 SVN_ERR(svn_skel__parse_int(&v
, c
, scratch_pool
));
176 revision
= (svn_revnum_t
)v
;
179 kind_str
= apr_pstrmemdup(scratch_pool
, c
->data
, c
->len
);
180 node_kind
= svn_node_kind_from_word(kind_str
);
182 *location
= svn_wc_conflict_version_create2(repos_root_url
,
191 /* Get the operation part of CONFLICT_SKELL or NULL if no operation is set
194 conflict__get_operation(svn_skel_t
**why
,
195 const svn_skel_t
*conflict_skel
)
197 SVN_ERR_ASSERT(conflict_skel
198 && conflict_skel
->children
199 && conflict_skel
->children
->next
200 && !conflict_skel
->children
->next
->is_atom
);
202 *why
= conflict_skel
->children
;
204 if (!(*why
)->children
)
205 *why
= NULL
; /* Operation is not set yet */
212 svn_wc__conflict_skel_set_op_update(svn_skel_t
*conflict_skel
,
213 const svn_wc_conflict_version_t
*original
,
214 const svn_wc_conflict_version_t
*target
,
215 apr_pool_t
*result_pool
,
216 apr_pool_t
*scratch_pool
)
221 SVN_ERR_ASSERT(conflict_skel
222 && conflict_skel
->children
223 && conflict_skel
->children
->next
224 && !conflict_skel
->children
->next
->is_atom
);
226 SVN_ERR(conflict__get_operation(&why
, conflict_skel
));
228 SVN_ERR_ASSERT(why
== NULL
); /* No operation set */
230 why
= conflict_skel
->children
;
232 origins
= svn_skel__make_empty_list(result_pool
);
234 SVN_ERR(conflict__prepend_location(origins
, target
, TRUE
,
235 result_pool
, scratch_pool
));
236 SVN_ERR(conflict__prepend_location(origins
, original
, TRUE
,
237 result_pool
, scratch_pool
));
239 svn_skel__prepend(origins
, why
);
240 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_UPDATE
, why
, result_pool
);
246 svn_wc__conflict_skel_set_op_switch(svn_skel_t
*conflict_skel
,
247 const svn_wc_conflict_version_t
*original
,
248 const svn_wc_conflict_version_t
*target
,
249 apr_pool_t
*result_pool
,
250 apr_pool_t
*scratch_pool
)
255 SVN_ERR_ASSERT(conflict_skel
256 && conflict_skel
->children
257 && conflict_skel
->children
->next
258 && !conflict_skel
->children
->next
->is_atom
);
260 SVN_ERR(conflict__get_operation(&why
, conflict_skel
));
262 SVN_ERR_ASSERT(why
== NULL
); /* No operation set */
264 why
= conflict_skel
->children
;
266 origins
= svn_skel__make_empty_list(result_pool
);
268 SVN_ERR(conflict__prepend_location(origins
, target
, TRUE
,
269 result_pool
, scratch_pool
));
270 SVN_ERR(conflict__prepend_location(origins
, original
, TRUE
,
271 result_pool
, scratch_pool
));
273 svn_skel__prepend(origins
, why
);
274 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_SWITCH
, why
, result_pool
);
280 svn_wc__conflict_skel_set_op_merge(svn_skel_t
*conflict_skel
,
281 const svn_wc_conflict_version_t
*left
,
282 const svn_wc_conflict_version_t
*right
,
283 apr_pool_t
*result_pool
,
284 apr_pool_t
*scratch_pool
)
289 SVN_ERR_ASSERT(conflict_skel
290 && conflict_skel
->children
291 && conflict_skel
->children
->next
292 && !conflict_skel
->children
->next
->is_atom
);
294 SVN_ERR(conflict__get_operation(&why
, conflict_skel
));
296 SVN_ERR_ASSERT(why
== NULL
); /* No operation set */
298 why
= conflict_skel
->children
;
300 origins
= svn_skel__make_empty_list(result_pool
);
302 SVN_ERR(conflict__prepend_location(origins
, right
, TRUE
,
303 result_pool
, scratch_pool
));
305 SVN_ERR(conflict__prepend_location(origins
, left
, TRUE
,
306 result_pool
, scratch_pool
));
308 svn_skel__prepend(origins
, why
);
309 svn_skel__prepend_str(SVN_WC__CONFLICT_OP_MERGE
, why
, result_pool
);
314 /* Gets the conflict data of the specified type CONFLICT_TYPE from
315 CONFLICT_SKEL, or NULL if no such conflict is recorded */
317 conflict__get_conflict(svn_skel_t
**conflict
,
318 const svn_skel_t
*conflict_skel
,
319 const char *conflict_type
)
323 SVN_ERR_ASSERT(conflict_skel
324 && conflict_skel
->children
325 && conflict_skel
->children
->next
326 && !conflict_skel
->children
->next
->is_atom
);
328 for(c
= conflict_skel
->children
->next
->children
;
332 if (svn_skel__matches_atom(c
->children
, conflict_type
))
345 svn_wc__conflict_skel_add_text_conflict(svn_skel_t
*conflict_skel
,
347 const char *wri_abspath
,
348 const char *mine_abspath
,
349 const char *their_old_abspath
,
350 const char *their_abspath
,
351 apr_pool_t
*result_pool
,
352 apr_pool_t
*scratch_pool
)
354 svn_skel_t
*text_conflict
;
357 SVN_ERR(conflict__get_conflict(&text_conflict
, conflict_skel
,
358 SVN_WC__CONFLICT_KIND_TEXT
));
360 SVN_ERR_ASSERT(!text_conflict
); /* ### Use proper error? */
362 /* Current skel format
364 (OLD MINE OLD-THEIRS THEIRS)) */
366 text_conflict
= svn_skel__make_empty_list(result_pool
);
367 markers
= svn_skel__make_empty_list(result_pool
);
371 const char *their_relpath
;
373 SVN_ERR(svn_wc__db_to_relpath(&their_relpath
,
374 db
, wri_abspath
, their_abspath
,
375 result_pool
, scratch_pool
));
376 svn_skel__prepend_str(their_relpath
, markers
, result_pool
);
379 svn_skel__prepend(svn_skel__make_empty_list(result_pool
), markers
);
383 const char *mine_relpath
;
385 SVN_ERR(svn_wc__db_to_relpath(&mine_relpath
,
386 db
, wri_abspath
, mine_abspath
,
387 result_pool
, scratch_pool
));
388 svn_skel__prepend_str(mine_relpath
, markers
, result_pool
);
391 svn_skel__prepend(svn_skel__make_empty_list(result_pool
), markers
);
393 if (their_old_abspath
)
395 const char *original_relpath
;
397 SVN_ERR(svn_wc__db_to_relpath(&original_relpath
,
398 db
, wri_abspath
, their_old_abspath
,
399 result_pool
, scratch_pool
));
400 svn_skel__prepend_str(original_relpath
, markers
, result_pool
);
403 svn_skel__prepend(svn_skel__make_empty_list(result_pool
), markers
);
405 svn_skel__prepend(markers
, text_conflict
);
406 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TEXT
, text_conflict
,
409 /* And add it to the conflict skel */
410 svn_skel__prepend(text_conflict
, conflict_skel
->children
->next
);
416 svn_wc__conflict_skel_add_prop_conflict(svn_skel_t
*conflict_skel
,
418 const char *wri_abspath
,
419 const char *marker_abspath
,
420 const apr_hash_t
*mine_props
,
421 const apr_hash_t
*their_old_props
,
422 const apr_hash_t
*their_props
,
423 const apr_hash_t
*conflicted_prop_names
,
424 apr_pool_t
*result_pool
,
425 apr_pool_t
*scratch_pool
)
427 svn_skel_t
*prop_conflict
;
429 svn_skel_t
*conflict_names
;
431 apr_hash_index_t
*hi
;
433 SVN_ERR(conflict__get_conflict(&prop_conflict
, conflict_skel
,
434 SVN_WC__CONFLICT_KIND_PROP
));
436 SVN_ERR_ASSERT(!prop_conflict
); /* ### Use proper error? */
438 /* This function currently implements:
441 prop-conflicted_prop_names
445 NULL lists are recorded as "" */
446 /* ### Seems that this may not match what we read out. Read-out of
447 * 'theirs-old' comes as NULL. */
449 prop_conflict
= svn_skel__make_empty_list(result_pool
);
453 SVN_ERR(svn_skel__unparse_proplist(&props
, their_props
, result_pool
));
454 svn_skel__prepend(props
, prop_conflict
);
457 svn_skel__prepend_str("", prop_conflict
, result_pool
); /* No their_props */
461 SVN_ERR(svn_skel__unparse_proplist(&props
, mine_props
, result_pool
));
462 svn_skel__prepend(props
, prop_conflict
);
465 svn_skel__prepend_str("", prop_conflict
, result_pool
); /* No mine_props */
469 SVN_ERR(svn_skel__unparse_proplist(&props
, their_old_props
,
471 svn_skel__prepend(props
, prop_conflict
);
474 svn_skel__prepend_str("", prop_conflict
, result_pool
); /* No old_props */
476 conflict_names
= svn_skel__make_empty_list(result_pool
);
477 for (hi
= apr_hash_first(scratch_pool
, (apr_hash_t
*)conflicted_prop_names
);
479 hi
= apr_hash_next(hi
))
481 svn_skel__prepend_str(apr_pstrdup(result_pool
, apr_hash_this_key(hi
)),
485 svn_skel__prepend(conflict_names
, prop_conflict
);
487 markers
= svn_skel__make_empty_list(result_pool
);
491 const char *marker_relpath
;
492 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath
, db
, wri_abspath
,
494 result_pool
, scratch_pool
));
496 svn_skel__prepend_str(marker_relpath
, markers
, result_pool
);
498 /*else // ### set via svn_wc__conflict_create_markers
499 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);*/
501 svn_skel__prepend(markers
, prop_conflict
);
503 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP
, prop_conflict
, result_pool
);
505 /* And add it to the conflict skel */
506 svn_skel__prepend(prop_conflict
, conflict_skel
->children
->next
);
511 /* A map for svn_wc_conflict_reason_t values. */
512 static const svn_token_map_t reason_map
[] =
514 { "edited", svn_wc_conflict_reason_edited
},
515 { "obstructed", svn_wc_conflict_reason_obstructed
},
516 { "deleted", svn_wc_conflict_reason_deleted
},
517 { "missing", svn_wc_conflict_reason_missing
},
518 { "unversioned", svn_wc_conflict_reason_unversioned
},
519 { "added", svn_wc_conflict_reason_added
},
520 { "replaced", svn_wc_conflict_reason_replaced
},
521 { "moved-away", svn_wc_conflict_reason_moved_away
},
522 { "moved-here", svn_wc_conflict_reason_moved_here
},
526 static const svn_token_map_t action_map
[] =
528 { "edited", svn_wc_conflict_action_edit
},
529 { "added", svn_wc_conflict_action_add
},
530 { "deleted", svn_wc_conflict_action_delete
},
531 { "replaced", svn_wc_conflict_action_replace
},
536 svn_wc__conflict_skel_add_tree_conflict(svn_skel_t
*conflict_skel
,
538 const char *wri_abspath
,
539 svn_wc_conflict_reason_t reason
,
540 svn_wc_conflict_action_t action
,
541 const char *move_src_op_root_abspath
,
542 const char *move_dst_op_root_abspath
,
543 apr_pool_t
*result_pool
,
544 apr_pool_t
*scratch_pool
)
546 svn_skel_t
*tree_conflict
;
549 SVN_ERR(conflict__get_conflict(&tree_conflict
, conflict_skel
,
550 SVN_WC__CONFLICT_KIND_TREE
));
552 SVN_ERR_ASSERT(!tree_conflict
); /* ### Use proper error? */
554 SVN_ERR_ASSERT(reason
== svn_wc_conflict_reason_moved_away
555 || !move_src_op_root_abspath
); /* ### Use proper error? */
557 tree_conflict
= svn_skel__make_empty_list(result_pool
);
559 if (reason
== svn_wc_conflict_reason_moved_away
)
561 if (move_dst_op_root_abspath
)
563 const char *move_dst_op_root_relpath
;
565 SVN_ERR(svn_wc__db_to_relpath(&move_dst_op_root_relpath
,
567 move_dst_op_root_abspath
,
568 result_pool
, scratch_pool
));
570 svn_skel__prepend_str(move_dst_op_root_relpath
, tree_conflict
,
574 if (move_src_op_root_abspath
)
576 const char *move_src_op_root_relpath
;
578 SVN_ERR(svn_wc__db_to_relpath(&move_src_op_root_relpath
,
580 move_src_op_root_abspath
,
581 result_pool
, scratch_pool
));
583 svn_skel__prepend_str(move_src_op_root_relpath
, tree_conflict
,
588 svn_skel__prepend_str(svn_token__to_word(action_map
, action
),
589 tree_conflict
, result_pool
);
591 svn_skel__prepend_str(svn_token__to_word(reason_map
, reason
),
592 tree_conflict
, result_pool
);
594 /* Tree conflicts have no marker files */
595 markers
= svn_skel__make_empty_list(result_pool
);
596 svn_skel__prepend(markers
, tree_conflict
);
598 svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TREE
, tree_conflict
,
601 /* And add it to the conflict skel */
602 svn_skel__prepend(tree_conflict
, conflict_skel
->children
->next
);
608 svn_wc__conflict_skel_resolve(svn_boolean_t
*completely_resolved
,
609 svn_skel_t
*conflict_skel
,
611 const char *wri_abspath
,
612 svn_boolean_t resolve_text
,
613 const char *resolve_prop
,
614 svn_boolean_t resolve_tree
,
615 apr_pool_t
*result_pool
,
616 apr_pool_t
*scratch_pool
)
619 svn_skel_t
**pconflict
;
620 SVN_ERR(conflict__get_operation(&op
, conflict_skel
));
623 return svn_error_create(SVN_ERR_INCOMPLETE_DATA
, NULL
,
624 _("Not a completed conflict skel"));
626 /* We are going to drop items from a linked list. Instead of keeping
627 a pointer to the item we want to drop we store a pointer to the
628 pointer of what we may drop, to allow setting it to the next item. */
630 pconflict
= &(conflict_skel
->children
->next
->children
);
633 svn_skel_t
*c
= (*pconflict
)->children
;
636 && svn_skel__matches_atom(c
, SVN_WC__CONFLICT_KIND_TEXT
))
638 /* Remove the text conflict from the linked list */
639 *pconflict
= (*pconflict
)->next
;
642 else if (resolve_prop
643 && svn_skel__matches_atom(c
, SVN_WC__CONFLICT_KIND_PROP
))
645 svn_skel_t
**ppropnames
= &(c
->next
->next
->children
);
647 if (resolve_prop
[0] == '\0')
648 *ppropnames
= NULL
; /* remove all conflicted property names */
652 if (svn_skel__matches_atom(*ppropnames
, resolve_prop
))
654 *ppropnames
= (*ppropnames
)->next
;
657 ppropnames
= &((*ppropnames
)->next
);
660 /* If no conflicted property names left */
661 if (!c
->next
->next
->children
)
663 /* Remove the propery conflict skel from the linked list */
664 *pconflict
= (*pconflict
)->next
;
668 else if (resolve_tree
669 && svn_skel__matches_atom(c
, SVN_WC__CONFLICT_KIND_TREE
))
671 /* Remove the tree conflict from the linked list */
672 *pconflict
= (*pconflict
)->next
;
676 pconflict
= &((*pconflict
)->next
);
679 if (completely_resolved
)
681 /* Nice, we can just call the complete function */
682 svn_boolean_t complete_conflict
;
683 SVN_ERR(svn_wc__conflict_skel_is_complete(&complete_conflict
,
686 *completely_resolved
= !complete_conflict
;
692 /* A map for svn_wc_operation_t values. */
693 static const svn_token_map_t operation_map
[] =
695 { "", svn_wc_operation_none
},
696 { SVN_WC__CONFLICT_OP_UPDATE
, svn_wc_operation_update
},
697 { SVN_WC__CONFLICT_OP_SWITCH
, svn_wc_operation_switch
},
698 { SVN_WC__CONFLICT_OP_MERGE
, svn_wc_operation_merge
},
703 svn_wc__conflict_read_info(svn_wc_operation_t
*operation
,
704 const apr_array_header_t
**locations
,
705 svn_boolean_t
*text_conflicted
,
706 svn_boolean_t
*prop_conflicted
,
707 svn_boolean_t
*tree_conflicted
,
709 const char *wri_abspath
,
710 const svn_skel_t
*conflict_skel
,
711 apr_pool_t
*result_pool
,
712 apr_pool_t
*scratch_pool
)
717 SVN_ERR(conflict__get_operation(&op
, conflict_skel
));
720 return svn_error_create(SVN_ERR_INCOMPLETE_DATA
, NULL
,
721 _("Not a completed conflict skel"));
726 int value
= svn_token__from_mem(operation_map
, c
->data
, c
->len
);
728 if (value
!= SVN_TOKEN_UNKNOWN
)
731 *operation
= svn_wc_operation_none
;
735 if (locations
&& c
->children
)
737 const svn_skel_t
*loc_skel
;
738 svn_wc_conflict_version_t
*loc
;
739 apr_array_header_t
*locs
= apr_array_make(result_pool
, 2, sizeof(loc
));
741 for (loc_skel
= c
->children
; loc_skel
; loc_skel
= loc_skel
->next
)
743 SVN_ERR(conflict__read_location(&loc
, loc_skel
, result_pool
,
746 APR_ARRAY_PUSH(locs
, svn_wc_conflict_version_t
*) = loc
;
757 SVN_ERR(conflict__get_conflict(&c_skel
, conflict_skel
,
758 SVN_WC__CONFLICT_KIND_TEXT
));
760 *text_conflicted
= (c_skel
!= NULL
);
766 SVN_ERR(conflict__get_conflict(&c_skel
, conflict_skel
,
767 SVN_WC__CONFLICT_KIND_PROP
));
769 *prop_conflicted
= (c_skel
!= NULL
);
775 SVN_ERR(conflict__get_conflict(&c_skel
, conflict_skel
,
776 SVN_WC__CONFLICT_KIND_TREE
));
778 *tree_conflicted
= (c_skel
!= NULL
);
786 svn_wc__conflict_read_text_conflict(const char **mine_abspath
,
787 const char **their_old_abspath
,
788 const char **their_abspath
,
790 const char *wri_abspath
,
791 const svn_skel_t
*conflict_skel
,
792 apr_pool_t
*result_pool
,
793 apr_pool_t
*scratch_pool
)
795 svn_skel_t
*text_conflict
;
798 SVN_ERR(conflict__get_conflict(&text_conflict
, conflict_skel
,
799 SVN_WC__CONFLICT_KIND_TEXT
));
802 return svn_error_create(SVN_ERR_WC_MISSING
, NULL
, _("Conflict not set"));
804 m
= text_conflict
->children
->next
->children
;
806 if (their_old_abspath
)
810 const char *original_relpath
;
812 original_relpath
= apr_pstrmemdup(scratch_pool
, m
->data
, m
->len
);
813 SVN_ERR(svn_wc__db_from_relpath(their_old_abspath
,
814 db
, wri_abspath
, original_relpath
,
815 result_pool
, scratch_pool
));
818 *their_old_abspath
= NULL
;
826 const char *mine_relpath
;
828 mine_relpath
= apr_pstrmemdup(scratch_pool
, m
->data
, m
->len
);
829 SVN_ERR(svn_wc__db_from_relpath(mine_abspath
,
830 db
, wri_abspath
, mine_relpath
,
831 result_pool
, scratch_pool
));
834 *mine_abspath
= NULL
;
842 const char *their_relpath
;
844 their_relpath
= apr_pstrmemdup(scratch_pool
, m
->data
, m
->len
);
845 SVN_ERR(svn_wc__db_from_relpath(their_abspath
,
846 db
, wri_abspath
, their_relpath
,
847 result_pool
, scratch_pool
));
850 *their_abspath
= NULL
;
857 svn_wc__conflict_read_prop_conflict(const char **marker_abspath
,
858 apr_hash_t
**mine_props
,
859 apr_hash_t
**their_old_props
,
860 apr_hash_t
**their_props
,
861 apr_hash_t
**conflicted_prop_names
,
863 const char *wri_abspath
,
864 const svn_skel_t
*conflict_skel
,
865 apr_pool_t
*result_pool
,
866 apr_pool_t
*scratch_pool
)
868 svn_skel_t
*prop_conflict
;
871 SVN_ERR(conflict__get_conflict(&prop_conflict
, conflict_skel
,
872 SVN_WC__CONFLICT_KIND_PROP
));
875 return svn_error_create(SVN_ERR_WC_MISSING
, NULL
, _("Conflict not set"));
877 c
= prop_conflict
->children
;
879 c
= c
->next
; /* Skip "prop" */
881 /* Get marker file */
884 const char *marker_relpath
;
886 if (c
->children
&& c
->children
->is_atom
)
888 marker_relpath
= apr_pstrmemdup(result_pool
, c
->children
->data
,
891 SVN_ERR(svn_wc__db_from_relpath(marker_abspath
, db
, wri_abspath
,
893 result_pool
, scratch_pool
));
896 *marker_abspath
= NULL
;
900 /* Get conflicted properties */
901 if (conflicted_prop_names
)
903 const svn_skel_t
*name
;
904 *conflicted_prop_names
= apr_hash_make(result_pool
);
906 for (name
= c
->children
; name
; name
= name
->next
)
908 svn_hash_sets(*conflicted_prop_names
,
909 apr_pstrmemdup(result_pool
, name
->data
, name
->len
),
915 /* Get original properties */
919 *their_old_props
= apr_hash_make(result_pool
);
921 SVN_ERR(svn_skel__parse_proplist(their_old_props
, c
, result_pool
));
925 /* Get mine properties */
929 *mine_props
= apr_hash_make(result_pool
);
931 SVN_ERR(svn_skel__parse_proplist(mine_props
, c
, result_pool
));
935 /* Get their properties */
939 *their_props
= apr_hash_make(result_pool
);
941 SVN_ERR(svn_skel__parse_proplist(their_props
, c
, result_pool
));
948 svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t
*reason
,
949 svn_wc_conflict_action_t
*action
,
950 const char **move_src_op_root_abspath
,
951 const char **move_dst_op_root_abspath
,
953 const char *wri_abspath
,
954 const svn_skel_t
*conflict_skel
,
955 apr_pool_t
*result_pool
,
956 apr_pool_t
*scratch_pool
)
958 svn_skel_t
*tree_conflict
;
960 svn_boolean_t is_moved_away
= FALSE
;
962 SVN_ERR(conflict__get_conflict(&tree_conflict
, conflict_skel
,
963 SVN_WC__CONFLICT_KIND_TREE
));
966 return svn_error_create(SVN_ERR_WC_MISSING
, NULL
, _("Conflict not set"));
968 c
= tree_conflict
->children
;
970 c
= c
->next
; /* Skip "tree" */
972 c
= c
->next
; /* Skip markers */
975 int value
= svn_token__from_mem(reason_map
, c
->data
, c
->len
);
979 if (value
!= SVN_TOKEN_UNKNOWN
)
982 *reason
= svn_wc_conflict_reason_edited
;
985 is_moved_away
= (value
== svn_wc_conflict_reason_moved_away
);
991 int value
= svn_token__from_mem(action_map
, c
->data
, c
->len
);
993 if (value
!= SVN_TOKEN_UNKNOWN
)
996 *action
= svn_wc_conflict_action_edit
;
1001 if (move_src_op_root_abspath
|| move_dst_op_root_abspath
)
1003 /* Only set for update and switch tree conflicts */
1004 if (c
&& is_moved_away
&& move_src_op_root_abspath
)
1006 const char *move_src_op_root_relpath
1007 = apr_pstrmemdup(scratch_pool
, c
->data
, c
->len
);
1009 SVN_ERR(svn_wc__db_from_relpath(move_src_op_root_abspath
,
1011 move_src_op_root_relpath
,
1012 result_pool
, scratch_pool
));
1014 else if (move_src_op_root_abspath
)
1015 *move_src_op_root_abspath
= NULL
;
1020 if (c
&& is_moved_away
&& move_dst_op_root_abspath
)
1022 const char *move_dst_op_root_relpath
1023 = apr_pstrmemdup(scratch_pool
, c
->data
, c
->len
);
1025 SVN_ERR(svn_wc__db_from_relpath(move_dst_op_root_abspath
,
1027 move_dst_op_root_relpath
,
1028 result_pool
, scratch_pool
));
1030 else if (move_dst_op_root_abspath
)
1031 *move_dst_op_root_abspath
= NULL
;
1035 return SVN_NO_ERROR
;
1039 svn_wc__conflict_read_markers(const apr_array_header_t
**markers
,
1041 const char *wri_abspath
,
1042 const svn_skel_t
*conflict_skel
,
1043 apr_pool_t
*result_pool
,
1044 apr_pool_t
*scratch_pool
)
1046 const svn_skel_t
*conflict
;
1047 apr_array_header_t
*list
= NULL
;
1049 SVN_ERR_ASSERT(conflict_skel
!= NULL
);
1051 /* Walk the conflicts */
1052 for (conflict
= conflict_skel
->children
->next
->children
;
1054 conflict
= conflict
->next
)
1056 const svn_skel_t
*marker
;
1058 /* Get the list of markers stored per conflict */
1059 for (marker
= conflict
->children
->next
->children
;
1061 marker
= marker
->next
)
1063 /* Skip placeholders */
1064 if (! marker
->is_atom
)
1068 list
= apr_array_make(result_pool
, 4, sizeof(const char *));
1070 SVN_ERR(svn_wc__db_from_relpath(
1071 &APR_ARRAY_PUSH(list
, const char*),
1073 apr_pstrmemdup(scratch_pool
, marker
->data
,
1075 result_pool
, scratch_pool
));
1080 return SVN_NO_ERROR
;
1083 /* --------------------------------------------------------------------
1088 svn_wc__conflict_create_markers(svn_skel_t
**work_items
,
1090 const char *local_abspath
,
1091 svn_skel_t
*conflict_skel
,
1092 apr_pool_t
*result_pool
,
1093 apr_pool_t
*scratch_pool
)
1095 svn_boolean_t prop_conflicted
;
1096 svn_wc_operation_t operation
;
1099 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
,
1100 NULL
, &prop_conflicted
, NULL
,
1103 scratch_pool
, scratch_pool
));
1105 if (prop_conflicted
)
1107 const char *marker_abspath
= NULL
;
1108 svn_node_kind_t kind
;
1109 const char *marker_dir
;
1110 const char *marker_name
;
1111 const char *marker_relpath
;
1113 /* Ok, currently we have to do a few things for property conflicts:
1114 - Create a marker file
1115 - Store the name in the conflict_skel
1116 - Create a WQ item that fills the marker with the expected data */
1118 SVN_ERR(svn_io_check_path(local_abspath
, &kind
, scratch_pool
));
1120 if (kind
== svn_node_dir
)
1122 marker_dir
= local_abspath
;
1123 marker_name
= SVN_WC__THIS_DIR_PREJ
;
1126 svn_dirent_split(&marker_dir
, &marker_name
, local_abspath
,
1129 SVN_ERR(svn_io_open_uniquely_named(NULL
, &marker_abspath
,
1132 SVN_WC__PROP_REJ_EXT
,
1133 svn_io_file_del_none
,
1134 scratch_pool
, scratch_pool
));
1136 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath
, db
, local_abspath
,
1137 marker_abspath
, result_pool
, result_pool
));
1139 /* And store the marker in the skel */
1141 svn_skel_t
*prop_conflict
;
1142 SVN_ERR(conflict__get_conflict(&prop_conflict
, conflict_skel
,
1143 SVN_WC__CONFLICT_KIND_PROP
));
1145 svn_skel__prepend_str(marker_relpath
, prop_conflict
->children
->next
,
1148 SVN_ERR(svn_wc__wq_build_prej_install(work_items
,
1150 scratch_pool
, scratch_pool
));
1153 return SVN_NO_ERROR
;
1156 /* Helper function for the three apply_* functions below, used when
1157 * merging properties together.
1159 * Given property PROPNAME on LOCAL_ABSPATH, and four possible property
1160 * values, generate four tmpfiles and pass them to CONFLICT_FUNC callback.
1161 * This gives the client an opportunity to interactively resolve the
1162 * property conflict.
1164 * BASE_VAL/WORKING_VAL represent the current state of the working
1165 * copy, and INCOMING_OLD_VAL/INCOMING_NEW_VAL represents the incoming
1166 * propchange. Any of these values might be NULL, indicating either
1167 * non-existence or intent-to-delete.
1169 * If the callback isn't available, or if it responds with
1170 * 'choose_postpone', then set *CONFLICT_REMAINS to TRUE and return.
1172 * If the callback responds with a choice of 'base', 'theirs', 'mine',
1173 * or 'merged', then install the proper value into ACTUAL_PROPS and
1174 * set *CONFLICT_REMAINS to FALSE.
1176 static svn_error_t
*
1177 generate_propconflict(svn_boolean_t
*conflict_remains
,
1179 const char *local_abspath
,
1180 svn_node_kind_t kind
,
1181 svn_wc_operation_t operation
,
1182 const svn_wc_conflict_version_t
*left_version
,
1183 const svn_wc_conflict_version_t
*right_version
,
1184 const char *propname
,
1185 const svn_string_t
*base_val
,
1186 const svn_string_t
*working_val
,
1187 const svn_string_t
*incoming_old_val
,
1188 const svn_string_t
*incoming_new_val
,
1189 svn_wc_conflict_resolver_func2_t conflict_func
,
1190 void *conflict_baton
,
1191 svn_cancel_func_t cancel_func
,
1193 apr_pool_t
*scratch_pool
)
1195 svn_wc_conflict_result_t
*result
= NULL
;
1196 svn_wc_conflict_description2_t
*cdesc
;
1197 const char *dirpath
= svn_dirent_dirname(local_abspath
, scratch_pool
);
1198 const svn_string_t
*new_value
= NULL
;
1200 cdesc
= svn_wc_conflict_description_create_prop2(
1203 propname
, scratch_pool
);
1205 cdesc
->operation
= operation
;
1206 cdesc
->src_left_version
= left_version
;
1207 cdesc
->src_right_version
= right_version
;
1209 /* Create a tmpfile for each of the string_t's we've got. */
1212 const char *file_name
;
1214 SVN_ERR(svn_io_write_unique(&file_name
, dirpath
, working_val
->data
,
1216 svn_io_file_del_on_pool_cleanup
,
1218 cdesc
->my_abspath
= svn_dirent_join(dirpath
, file_name
, scratch_pool
);
1219 cdesc
->prop_value_working
= working_val
;
1222 if (incoming_new_val
)
1224 const char *file_name
;
1226 SVN_ERR(svn_io_write_unique(&file_name
, dirpath
, incoming_new_val
->data
,
1227 incoming_new_val
->len
,
1228 svn_io_file_del_on_pool_cleanup
,
1231 /* ### For property conflicts, cd2 stores prop_reject_abspath in
1232 * ### their_abspath, and stores theirs_abspath in merged_file. */
1233 cdesc
->merged_file
= svn_dirent_join(dirpath
, file_name
, scratch_pool
);
1234 cdesc
->prop_value_incoming_new
= incoming_new_val
;
1237 if (!base_val
&& !incoming_old_val
)
1239 /* If base and old are both NULL, then that's fine, we just let
1240 base_file stay NULL as-is. Both agents are attempting to add a
1243 else if ((base_val
&& !incoming_old_val
)
1244 || (!base_val
&& incoming_old_val
))
1246 /* If only one of base and old are defined, then we've got a
1247 situation where one agent is attempting to add the property
1248 for the first time, and the other agent is changing a
1249 property it thinks already exists. In this case, we return
1250 whichever older-value happens to be defined, so that the
1251 conflict-callback can still attempt a 3-way merge. */
1253 const svn_string_t
*conflict_base_val
= base_val
? base_val
1255 const char *file_name
;
1257 SVN_ERR(svn_io_write_unique(&file_name
, dirpath
,
1258 conflict_base_val
->data
,
1259 conflict_base_val
->len
,
1260 svn_io_file_del_on_pool_cleanup
,
1262 cdesc
->base_abspath
= svn_dirent_join(dirpath
, file_name
, scratch_pool
);
1264 else /* base and old are both non-NULL */
1266 const svn_string_t
*conflict_base_val
;
1267 const char *file_name
;
1269 if (! svn_string_compare(base_val
, incoming_old_val
))
1271 /* What happens if 'base' and 'old' don't match up? In an
1272 ideal situation, they would. But if they don't, this is
1273 a classic example of a patch 'hunk' failing to apply due
1274 to a lack of context. For example: imagine that the user
1275 is busy changing the property from a value of "cat" to
1276 "dog", but the incoming propchange wants to change the
1277 same property value from "red" to "green". Total context
1280 HOWEVER: we can still pass one of the two base values as
1281 'base_file' to the callback anyway. It's still useful to
1282 present the working and new values to the user to
1285 if (working_val
&& svn_string_compare(base_val
, working_val
))
1286 conflict_base_val
= incoming_old_val
;
1288 conflict_base_val
= base_val
;
1292 conflict_base_val
= base_val
;
1295 SVN_ERR(svn_io_write_unique(&file_name
, dirpath
, conflict_base_val
->data
,
1296 conflict_base_val
->len
,
1297 svn_io_file_del_on_pool_cleanup
, scratch_pool
));
1298 cdesc
->base_abspath
= svn_dirent_join(dirpath
, file_name
, scratch_pool
);
1300 cdesc
->prop_value_base
= base_val
;
1301 cdesc
->prop_value_incoming_old
= incoming_old_val
;
1303 if (working_val
&& incoming_new_val
)
1305 svn_stream_t
*mergestream
;
1307 svn_diff_file_options_t
*options
=
1308 svn_diff_file_options_create(scratch_pool
);
1310 SVN_ERR(svn_stream_open_unique(&mergestream
, &cdesc
->prop_reject_abspath
,
1311 NULL
, svn_io_file_del_on_pool_cleanup
,
1312 scratch_pool
, scratch_pool
));
1313 SVN_ERR(svn_diff_mem_string_diff3(&diff
, conflict_base_val
,
1315 incoming_new_val
, options
, scratch_pool
));
1316 SVN_ERR(svn_diff_mem_string_output_merge3(mergestream
, diff
,
1317 conflict_base_val
, working_val
,
1318 incoming_new_val
, NULL
, NULL
, NULL
, NULL
,
1319 svn_diff_conflict_display_modified_latest
,
1320 cancel_func
, cancel_baton
, scratch_pool
));
1321 SVN_ERR(svn_stream_close(mergestream
));
1323 /* ### For property conflicts, cd2 stores prop_reject_abspath in
1324 * ### their_abspath, and stores theirs_abspath in merged_file. */
1325 cdesc
->their_abspath
= cdesc
->prop_reject_abspath
;
1329 if (!incoming_old_val
&& incoming_new_val
)
1330 cdesc
->action
= svn_wc_conflict_action_add
;
1331 else if (incoming_old_val
&& !incoming_new_val
)
1332 cdesc
->action
= svn_wc_conflict_action_delete
;
1334 cdesc
->action
= svn_wc_conflict_action_edit
;
1336 if (base_val
&& !working_val
)
1337 cdesc
->reason
= svn_wc_conflict_reason_deleted
;
1338 else if (!base_val
&& working_val
)
1339 cdesc
->reason
= svn_wc_conflict_reason_obstructed
;
1341 cdesc
->reason
= svn_wc_conflict_reason_edited
;
1343 /* Invoke the interactive conflict callback. */
1344 SVN_ERR(conflict_func(&result
, cdesc
, conflict_baton
, scratch_pool
,
1348 *conflict_remains
= TRUE
;
1349 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
,
1350 NULL
, _("Conflict callback violated API:"
1351 " returned no results"));
1355 switch (result
->choice
)
1358 case svn_wc_conflict_choose_postpone
:
1360 *conflict_remains
= TRUE
;
1363 case svn_wc_conflict_choose_mine_full
:
1365 /* No need to change actual_props; it already contains working_val */
1366 *conflict_remains
= FALSE
;
1367 new_value
= working_val
;
1370 /* I think _mine_full and _theirs_full are appropriate for prop
1371 behavior as well as the text behavior. There should even be
1372 analogous behaviors for _mine and _theirs when those are
1373 ready, namely: fold in all non-conflicting prop changes, and
1374 then choose _mine side or _theirs side for conflicting ones. */
1375 case svn_wc_conflict_choose_theirs_full
:
1377 *conflict_remains
= FALSE
;
1378 new_value
= incoming_new_val
;
1381 case svn_wc_conflict_choose_base
:
1383 *conflict_remains
= FALSE
;
1384 new_value
= base_val
;
1387 case svn_wc_conflict_choose_merged
:
1389 if (!cdesc
->merged_file
1390 && (!result
->merged_file
&& !result
->merged_value
))
1391 return svn_error_create
1392 (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
,
1393 NULL
, _("Conflict callback violated API:"
1394 " returned no merged file"));
1396 if (result
->merged_value
)
1397 new_value
= result
->merged_value
;
1400 svn_stringbuf_t
*merged_stringbuf
;
1402 SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf
,
1403 result
->merged_file
?
1404 result
->merged_file
:
1407 new_value
= svn_stringbuf__morph_into_string(merged_stringbuf
);
1409 *conflict_remains
= FALSE
;
1414 if (!*conflict_remains
)
1418 /* For now, just set the property values. This should really do some of the
1419 more advanced things from svn_wc_prop_set() */
1421 SVN_ERR(svn_wc__db_read_props(&props
, db
, local_abspath
, scratch_pool
,
1424 svn_hash_sets(props
, propname
, new_value
);
1426 SVN_ERR(svn_wc__db_op_set_props(db
, local_abspath
, props
,
1431 return SVN_NO_ERROR
;
1434 /* Perform a 3-way merge in which conflicts are expected, showing the
1435 * conflicts in the way specified by STYLE, and using MERGE_OPTIONS.
1437 * The three input files are LEFT_ABSPATH (the base), DETRANSLATED_TARGET
1438 * and RIGHT_ABSPATH. The output is stored in a new temporary file,
1439 * whose name is put into *CHOSEN_ABSPATH.
1441 * The output file will be deleted according to DELETE_WHEN. If
1442 * DELETE_WHEN is 'on pool cleanup', it refers to RESULT_POOL.
1444 * DB and WRI_ABSPATH are used to choose a directory for the output file.
1446 * Allocate *CHOSEN_ABSPATH in RESULT_POOL. Use SCRATCH_POOL for temporary
1449 static svn_error_t
*
1450 merge_showing_conflicts(const char **chosen_abspath
,
1452 const char *wri_abspath
,
1453 svn_diff_conflict_display_style_t style
,
1454 const apr_array_header_t
*merge_options
,
1455 const char *left_abspath
,
1456 const char *detranslated_target
,
1457 const char *right_abspath
,
1458 svn_io_file_del_t delete_when
,
1459 svn_cancel_func_t cancel_func
,
1461 apr_pool_t
*result_pool
,
1462 apr_pool_t
*scratch_pool
)
1464 const char *temp_dir
;
1465 svn_stream_t
*chosen_stream
;
1467 svn_diff_file_options_t
*diff3_options
;
1469 diff3_options
= svn_diff_file_options_create(scratch_pool
);
1471 SVN_ERR(svn_diff_file_options_parse(diff3_options
,
1475 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir
, db
,
1477 scratch_pool
, scratch_pool
));
1478 /* We need to open the stream in RESULT_POOL because that controls the
1479 * lifetime of the file if DELETE_WHEN is 'on pool cleanup'. (We also
1480 * want to allocate CHOSEN_ABSPATH in RESULT_POOL, but we don't care
1481 * about the stream itself.) */
1482 SVN_ERR(svn_stream_open_unique(&chosen_stream
, chosen_abspath
,
1483 temp_dir
, delete_when
,
1484 result_pool
, scratch_pool
));
1485 SVN_ERR(svn_diff_file_diff3_2(&diff
,
1487 detranslated_target
, right_abspath
,
1488 diff3_options
, scratch_pool
));
1489 SVN_ERR(svn_diff_file_output_merge3(chosen_stream
, diff
,
1491 detranslated_target
,
1493 NULL
, NULL
, NULL
, NULL
, /* markers */
1494 style
, cancel_func
, cancel_baton
,
1496 SVN_ERR(svn_stream_close(chosen_stream
));
1498 return SVN_NO_ERROR
;
1501 /* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the
1502 * working copy at DB/WRI_ABSPATH.
1504 * Set *WORK_ITEMS to a new work item that, when run, will delete the
1505 * artifact file; or to NULL if there is no file to delete.
1507 * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its
1508 * node kind is 'file'; otherwise do not change *FILE_FOUND. FILE_FOUND
1509 * may be NULL if not required.
1511 static svn_error_t
*
1512 remove_artifact_file_if_exists(svn_skel_t
**work_items
,
1513 svn_boolean_t
*file_found
,
1515 const char *wri_abspath
,
1516 const char *artifact_file_abspath
,
1517 apr_pool_t
*result_pool
,
1518 apr_pool_t
*scratch_pool
)
1521 if (artifact_file_abspath
)
1523 svn_node_kind_t node_kind
;
1525 SVN_ERR(svn_io_check_path(artifact_file_abspath
, &node_kind
,
1527 if (node_kind
== svn_node_file
)
1529 SVN_ERR(svn_wc__wq_build_file_remove(work_items
,
1531 artifact_file_abspath
,
1532 result_pool
, scratch_pool
));
1538 return SVN_NO_ERROR
;
1541 /* Create a new file in the same directory as LOCAL_ABSPATH, with the
1542 same basename as LOCAL_ABSPATH, with a ".edited" extension, and set
1543 *WORK_ITEM to a new work item that will copy and translate from the file
1544 SOURCE_ABSPATH to that new file. It will be translated from repository-
1545 normal form to working-copy form according to the versioned properties
1546 of LOCAL_ABSPATH that are current when the work item is executed.
1548 DB should have a write lock for the directory containing SOURCE.
1550 Allocate *WORK_ITEM in RESULT_POOL. */
1551 static svn_error_t
*
1552 save_merge_result(svn_skel_t
**work_item
,
1554 const char *local_abspath
,
1555 const char *source_abspath
,
1556 apr_pool_t
*result_pool
,
1557 apr_pool_t
*scratch_pool
)
1559 const char *edited_copy_abspath
;
1560 const char *dir_abspath
;
1561 const char *filename
;
1563 svn_dirent_split(&dir_abspath
, &filename
, local_abspath
, scratch_pool
);
1565 /* ### Should use preserved-conflict-file-exts. */
1566 /* Create the .edited file within this file's DIR_ABSPATH */
1567 SVN_ERR(svn_io_open_uniquely_named(NULL
,
1568 &edited_copy_abspath
,
1572 svn_io_file_del_none
,
1573 scratch_pool
, scratch_pool
));
1574 SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item
,
1577 edited_copy_abspath
,
1578 result_pool
, scratch_pool
));
1579 return SVN_NO_ERROR
;
1584 /* Resolve the text conflict in CONFLICT, which is currently recorded
1585 * on DB/LOCAL_ABSPATH in the manner specified by CHOICE.
1587 * Set *WORK_ITEMS to new work items that will make the on-disk changes
1588 * needed to complete the resolution (but not to mark it as resolved).
1590 * Set *FOUND_ARTIFACT to true if conflict markers are removed; otherwise
1591 * (which is only if CHOICE is 'postpone') to false.
1593 * CHOICE, MERGED_FILE and SAVE_MERGED are typically values provided by
1594 * the conflict resolver.
1596 * MERGE_OPTIONS allows customizing the diff handling when using
1597 * per hunk conflict resolving.
1599 static svn_error_t
*
1600 build_text_conflict_resolve_items(svn_skel_t
**work_items
,
1601 svn_boolean_t
*found_artifact
,
1603 const char *local_abspath
,
1604 const svn_skel_t
*conflict
,
1605 svn_wc_conflict_choice_t choice
,
1606 const char *merged_file
,
1607 svn_boolean_t save_merged
,
1608 const apr_array_header_t
*merge_options
,
1609 svn_cancel_func_t cancel_func
,
1611 apr_pool_t
*result_pool
,
1612 apr_pool_t
*scratch_pool
)
1614 const char *mine_abspath
;
1615 const char *their_old_abspath
;
1616 const char *their_abspath
;
1617 svn_skel_t
*work_item
;
1618 const char *install_from_abspath
= NULL
;
1619 svn_boolean_t remove_source
= FALSE
;
1624 *found_artifact
= FALSE
;
1626 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath
,
1631 scratch_pool
, scratch_pool
));
1634 SVN_ERR(save_merge_result(work_items
,
1639 result_pool
, scratch_pool
));
1641 if (choice
== svn_wc_conflict_choose_postpone
)
1642 return SVN_NO_ERROR
;
1646 /* If the callback wants to use one of the fulltexts
1647 to resolve the conflict, so be it.*/
1648 case svn_wc_conflict_choose_base
:
1650 install_from_abspath
= their_old_abspath
;
1653 case svn_wc_conflict_choose_theirs_full
:
1655 install_from_abspath
= their_abspath
;
1658 case svn_wc_conflict_choose_mine_full
:
1660 /* In case of selecting to resolve the conflict choosing the full
1661 own file, allow the text conflict resolution to just take the
1662 existing local file if no merged file was present (case: binary
1663 file conflicts do not generate a locally merge file).
1665 install_from_abspath
= mine_abspath
1670 case svn_wc_conflict_choose_theirs_conflict
:
1671 case svn_wc_conflict_choose_mine_conflict
:
1673 svn_diff_conflict_display_style_t style
1674 = choice
== svn_wc_conflict_choose_theirs_conflict
1675 ? svn_diff_conflict_display_latest
1676 : svn_diff_conflict_display_modified
;
1678 if (mine_abspath
== NULL
)
1679 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
1680 _("Conflict on '%s' cannot be resolved to "
1681 "'theirs-conflict' or 'mine-conflict' "
1682 "because a merged version of the file "
1683 "cannot be created."),
1684 svn_dirent_local_style(local_abspath
,
1687 SVN_ERR(merge_showing_conflicts(&install_from_abspath
,
1689 style
, merge_options
,
1693 /* ### why not same as other caller? */
1694 svn_io_file_del_none
,
1695 cancel_func
, cancel_baton
,
1696 scratch_pool
, scratch_pool
));
1697 remove_source
= TRUE
;
1701 /* For the case of 3-way file merging, we don't
1702 really distinguish between these return values;
1703 if the callback claims to have "generally
1704 resolved" the situation, we still interpret
1705 that as "OK, we'll assume the merged version is
1707 case svn_wc_conflict_choose_merged
:
1709 install_from_abspath
= merged_file
1714 case svn_wc_conflict_choose_postpone
:
1716 /* Assume conflict remains. */
1717 return SVN_NO_ERROR
;
1720 SVN_ERR_ASSERT(choice
== svn_wc_conflict_choose_postpone
);
1723 if (install_from_abspath
== NULL
)
1724 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
1725 _("Conflict on '%s' could not be resolved "
1726 "because the chosen version of the file "
1727 "is not available."),
1728 svn_dirent_local_style(local_abspath
,
1731 /* ### It would be nice if we could somehow pass RECORD_FILEINFO
1732 as true in some easy cases. */
1733 SVN_ERR(svn_wc__wq_build_file_install(&work_item
,
1735 install_from_abspath
,
1736 FALSE
/* use_commit_times */,
1737 FALSE
/* record_fileinfo */,
1738 result_pool
, scratch_pool
));
1739 *work_items
= svn_wc__wq_merge(*work_items
, work_item
, result_pool
);
1743 SVN_ERR(svn_wc__wq_build_file_remove(&work_item
,
1745 install_from_abspath
,
1746 result_pool
, scratch_pool
));
1747 *work_items
= svn_wc__wq_merge(*work_items
, work_item
, result_pool
);
1750 SVN_ERR(remove_artifact_file_if_exists(&work_item
, found_artifact
,
1753 result_pool
, scratch_pool
));
1754 *work_items
= svn_wc__wq_merge(*work_items
, work_item
, result_pool
);
1756 SVN_ERR(remove_artifact_file_if_exists(&work_item
, found_artifact
,
1759 result_pool
, scratch_pool
));
1760 *work_items
= svn_wc__wq_merge(*work_items
, work_item
, result_pool
);
1762 SVN_ERR(remove_artifact_file_if_exists(&work_item
, found_artifact
,
1765 result_pool
, scratch_pool
));
1766 *work_items
= svn_wc__wq_merge(*work_items
, work_item
, result_pool
);
1768 return SVN_NO_ERROR
;
1772 /* Set *DESC to a new description of the text conflict in
1773 * CONFLICT_SKEL. If there is no text conflict in CONFLICT_SKEL, return
1776 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1777 * rather than reading them from CONFLICT_SKEL. Use IS_BINARY and
1778 * MIME_TYPE for the corresponding fields of *DESC.
1780 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
1782 static svn_error_t
*
1783 read_text_conflict_desc(svn_wc_conflict_description2_t
**desc
,
1785 const char *local_abspath
,
1786 const svn_skel_t
*conflict_skel
,
1787 const char *mime_type
,
1788 svn_wc_operation_t operation
,
1789 const svn_wc_conflict_version_t
*left_version
,
1790 const svn_wc_conflict_version_t
*right_version
,
1791 apr_pool_t
*result_pool
,
1792 apr_pool_t
*scratch_pool
)
1794 *desc
= svn_wc_conflict_description_create_text2(local_abspath
, result_pool
);
1795 (*desc
)->mime_type
= mime_type
;
1796 (*desc
)->is_binary
= mime_type
? svn_mime_type_is_binary(mime_type
) : FALSE
;
1797 (*desc
)->operation
= operation
;
1798 (*desc
)->src_left_version
= left_version
;
1799 (*desc
)->src_right_version
= right_version
;
1801 SVN_ERR(svn_wc__conflict_read_text_conflict(&(*desc
)->my_abspath
,
1802 &(*desc
)->base_abspath
,
1803 &(*desc
)->their_abspath
,
1806 result_pool
, scratch_pool
));
1807 (*desc
)->merged_file
= apr_pstrdup(result_pool
, local_abspath
);
1809 return SVN_NO_ERROR
;
1812 /* Set *CONFLICT_DESC to a new description of the tree conflict in
1813 * CONFLICT_SKEL. If there is no tree conflict in CONFLICT_SKEL, return
1816 * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1817 * rather than reading them from CONFLICT_SKEL.
1819 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
1821 static svn_error_t
*
1822 read_tree_conflict_desc(svn_wc_conflict_description2_t
**desc
,
1824 const char *local_abspath
,
1825 svn_node_kind_t node_kind
,
1826 const svn_skel_t
*conflict_skel
,
1827 svn_wc_operation_t operation
,
1828 const svn_wc_conflict_version_t
*left_version
,
1829 const svn_wc_conflict_version_t
*right_version
,
1830 apr_pool_t
*result_pool
,
1831 apr_pool_t
*scratch_pool
)
1833 svn_node_kind_t local_kind
;
1834 svn_wc_conflict_reason_t reason
;
1835 svn_wc_conflict_action_t action
;
1837 SVN_ERR(svn_wc__conflict_read_tree_conflict(
1838 &reason
, &action
, NULL
, NULL
,
1839 db
, local_abspath
, conflict_skel
, scratch_pool
, scratch_pool
));
1841 if (reason
== svn_wc_conflict_reason_missing
)
1842 local_kind
= svn_node_none
;
1843 else if (reason
== svn_wc_conflict_reason_unversioned
||
1844 reason
== svn_wc_conflict_reason_obstructed
)
1845 SVN_ERR(svn_io_check_path(local_abspath
, &local_kind
, scratch_pool
));
1846 else if (action
== svn_wc_conflict_action_delete
1848 && (operation
== svn_wc_operation_update
1849 ||operation
== svn_wc_operation_switch
)
1850 && (reason
== svn_wc_conflict_reason_deleted
1851 || reason
== svn_wc_conflict_reason_moved_away
))
1853 /* We have nothing locally to take the kind from */
1854 local_kind
= left_version
->node_kind
;
1857 local_kind
= node_kind
;
1859 *desc
= svn_wc_conflict_description_create_tree2(local_abspath
, local_kind
,
1861 left_version
, right_version
,
1863 (*desc
)->reason
= reason
;
1864 (*desc
)->action
= action
;
1866 return SVN_NO_ERROR
;
1869 /* Forward definition */
1870 static svn_error_t
*
1871 resolve_tree_conflict_on_node(svn_boolean_t
*did_resolve
,
1873 const char *local_abspath
,
1874 const svn_skel_t
*conflict
,
1875 svn_wc_conflict_choice_t conflict_choice
,
1876 apr_hash_t
*resolve_later
,
1877 svn_wc_notify_func2_t notify_func
,
1879 svn_cancel_func_t cancel_func
,
1881 apr_pool_t
*scratch_pool
);
1884 svn_wc__conflict_invoke_resolver(svn_wc__db_t
*db
,
1885 const char *local_abspath
,
1886 svn_node_kind_t kind
,
1887 const svn_skel_t
*conflict_skel
,
1888 const apr_array_header_t
*merge_options
,
1889 svn_wc_conflict_resolver_func2_t resolver_func
,
1890 void *resolver_baton
,
1891 svn_cancel_func_t cancel_func
,
1893 apr_pool_t
*scratch_pool
)
1895 svn_boolean_t text_conflicted
;
1896 svn_boolean_t prop_conflicted
;
1897 svn_boolean_t tree_conflicted
;
1898 svn_wc_operation_t operation
;
1899 const apr_array_header_t
*locations
;
1900 const svn_wc_conflict_version_t
*left_version
= NULL
;
1901 const svn_wc_conflict_version_t
*right_version
= NULL
;
1903 SVN_ERR(svn_wc__conflict_read_info(&operation
, &locations
,
1904 &text_conflicted
, &prop_conflicted
,
1906 db
, local_abspath
, conflict_skel
,
1907 scratch_pool
, scratch_pool
));
1909 if (locations
&& locations
->nelts
> 0)
1910 left_version
= APR_ARRAY_IDX(locations
, 0, const svn_wc_conflict_version_t
*);
1912 if (locations
&& locations
->nelts
> 1)
1913 right_version
= APR_ARRAY_IDX(locations
, 1, const svn_wc_conflict_version_t
*);
1915 /* Quick and dirty compatibility wrapper. My guess would be that most resolvers
1916 would want to look at all properties at the same time.
1918 ### svn currently only invokes this from the merge code to collect the list of
1919 ### conflicted paths. Eventually this code will be the base for 'svn resolve'
1920 ### and at that time the test coverage will improve
1922 if (prop_conflicted
)
1924 apr_hash_t
*old_props
;
1925 apr_hash_t
*mine_props
;
1926 apr_hash_t
*their_props
;
1927 apr_hash_t
*old_their_props
;
1928 apr_hash_t
*conflicted
;
1929 apr_pool_t
*iterpool
;
1930 apr_hash_index_t
*hi
;
1931 svn_boolean_t mark_resolved
= TRUE
;
1933 SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL
,
1940 scratch_pool
, scratch_pool
));
1942 if (operation
== svn_wc_operation_merge
)
1943 SVN_ERR(svn_wc__db_read_pristine_props(&old_props
, db
, local_abspath
,
1944 scratch_pool
, scratch_pool
));
1946 old_props
= old_their_props
;
1948 iterpool
= svn_pool_create(scratch_pool
);
1950 for (hi
= apr_hash_first(scratch_pool
, conflicted
);
1952 hi
= apr_hash_next(hi
))
1954 const char *propname
= apr_hash_this_key(hi
);
1955 svn_boolean_t conflict_remains
= TRUE
;
1957 svn_pool_clear(iterpool
);
1960 SVN_ERR(cancel_func(cancel_baton
));
1962 SVN_ERR(generate_propconflict(&conflict_remains
,
1963 db
, local_abspath
, kind
,
1969 ? svn_hash_gets(old_props
, propname
)
1972 ? svn_hash_gets(mine_props
, propname
)
1975 ? svn_hash_gets(old_their_props
, propname
)
1978 ? svn_hash_gets(their_props
, propname
)
1980 resolver_func
, resolver_baton
,
1981 cancel_func
, cancel_baton
,
1984 if (conflict_remains
)
1985 mark_resolved
= FALSE
;
1990 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db
, local_abspath
,
1993 svn_pool_destroy(iterpool
);
1996 if (text_conflicted
)
1998 svn_skel_t
*work_items
;
1999 svn_boolean_t was_resolved
;
2000 svn_wc_conflict_description2_t
*desc
;
2002 svn_wc_conflict_result_t
*result
;
2004 SVN_ERR(svn_wc__db_read_props(&props
, db
, local_abspath
,
2005 scratch_pool
, scratch_pool
));
2007 SVN_ERR(read_text_conflict_desc(&desc
,
2008 db
, local_abspath
, conflict_skel
,
2009 svn_prop_get_value(props
,
2010 SVN_PROP_MIME_TYPE
),
2011 operation
, left_version
, right_version
,
2012 scratch_pool
, scratch_pool
));
2016 was_resolved
= FALSE
;
2018 /* Give the conflict resolution callback a chance to clean
2019 up the conflicts before we mark the file 'conflicted' */
2021 SVN_ERR(resolver_func(&result
, desc
, resolver_baton
, scratch_pool
,
2024 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
2025 _("Conflict callback violated API:"
2026 " returned no results"));
2028 SVN_ERR(build_text_conflict_resolve_items(&work_items
, &was_resolved
,
2030 conflict_skel
, result
->choice
,
2031 result
->merged_file
,
2032 result
->save_merged
,
2034 cancel_func
, cancel_baton
,
2035 scratch_pool
, scratch_pool
));
2037 if (result
->choice
!= svn_wc_conflict_choose_postpone
)
2039 SVN_ERR(svn_wc__db_op_mark_resolved(db
, local_abspath
,
2041 work_items
, scratch_pool
));
2042 SVN_ERR(svn_wc__wq_run(db
, local_abspath
,
2043 cancel_func
, cancel_baton
,
2048 if (tree_conflicted
)
2050 svn_wc_conflict_result_t
*result
;
2051 svn_wc_conflict_description2_t
*desc
;
2052 svn_boolean_t resolved
;
2053 svn_node_kind_t node_kind
;
2055 SVN_ERR(svn_wc__db_read_kind(&node_kind
, db
, local_abspath
, TRUE
,
2056 TRUE
, FALSE
, scratch_pool
));
2058 SVN_ERR(read_tree_conflict_desc(&desc
,
2059 db
, local_abspath
, node_kind
,
2061 operation
, left_version
, right_version
,
2062 scratch_pool
, scratch_pool
));
2064 /* Tell the resolver func about this conflict. */
2065 SVN_ERR(resolver_func(&result
, desc
, resolver_baton
, scratch_pool
,
2069 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
2070 _("Conflict callback violated API:"
2071 " returned no results"));
2073 /* Pass retry hash to avoid erroring out on cases where update
2074 can continue safely. ### Need notify handling */
2075 if (result
->choice
!= svn_wc_conflict_choose_postpone
)
2076 SVN_ERR(resolve_tree_conflict_on_node(&resolved
,
2077 db
, local_abspath
, conflict_skel
,
2079 apr_hash_make(scratch_pool
),
2080 NULL
, NULL
, /* ### notify */
2081 cancel_func
, cancel_baton
,
2085 return SVN_NO_ERROR
;
2088 /* Read all property conflicts contained in CONFLICT_SKEL into
2089 * individual conflict descriptions, and append those descriptions
2090 * to the CONFLICTS array. If there is no property conflict in
2091 * CONFLICT_SKEL, return an error.
2093 * If NOT create_tempfiles, always create a legacy property conflict
2096 * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
2097 * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
2099 * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
2101 static svn_error_t
*
2102 read_prop_conflict_descs(apr_array_header_t
*conflicts
,
2104 const char *local_abspath
,
2105 svn_skel_t
*conflict_skel
,
2106 svn_boolean_t create_tempfiles
,
2107 svn_node_kind_t node_kind
,
2108 svn_wc_operation_t operation
,
2109 const svn_wc_conflict_version_t
*left_version
,
2110 const svn_wc_conflict_version_t
*right_version
,
2111 apr_pool_t
*result_pool
,
2112 apr_pool_t
*scratch_pool
)
2114 const char *prop_reject_abspath
;
2115 apr_hash_t
*base_props
;
2116 apr_hash_t
*my_props
;
2117 apr_hash_t
*their_old_props
;
2118 apr_hash_t
*their_props
;
2119 apr_hash_t
*conflicted_props
;
2120 apr_hash_index_t
*hi
;
2121 apr_pool_t
*iterpool
;
2122 svn_boolean_t prop_conflicted
;
2124 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
, NULL
, &prop_conflicted
,
2125 NULL
, db
, local_abspath
, conflict_skel
,
2126 scratch_pool
, scratch_pool
));
2128 if (!prop_conflicted
)
2129 return SVN_NO_ERROR
;
2131 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath
,
2138 scratch_pool
, scratch_pool
));
2140 prop_reject_abspath
= apr_pstrdup(result_pool
, prop_reject_abspath
);
2142 if (apr_hash_count(conflicted_props
) == 0)
2144 /* Legacy prop conflict with only a .reject file. */
2145 svn_wc_conflict_description2_t
*desc
;
2147 desc
= svn_wc_conflict_description_create_prop2(local_abspath
,
2151 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2152 * ### their_abspath, and stores theirs_abspath in merged_file. */
2153 desc
->prop_reject_abspath
= prop_reject_abspath
; /* in result_pool */
2154 desc
->their_abspath
= desc
->prop_reject_abspath
;
2156 desc
->operation
= operation
;
2157 desc
->src_left_version
= left_version
;
2158 desc
->src_right_version
= right_version
;
2160 APR_ARRAY_PUSH(conflicts
, svn_wc_conflict_description2_t
*) = desc
;
2162 return SVN_NO_ERROR
;
2165 if (operation
== svn_wc_operation_merge
)
2166 SVN_ERR(svn_wc__db_read_pristine_props(&base_props
, db
, local_abspath
,
2167 result_pool
, scratch_pool
));
2170 iterpool
= svn_pool_create(scratch_pool
);
2171 for (hi
= apr_hash_first(scratch_pool
, conflicted_props
);
2173 hi
= apr_hash_next(hi
))
2175 const char *propname
= apr_hash_this_key(hi
);
2176 svn_string_t
*old_value
;
2177 svn_string_t
*my_value
;
2178 svn_string_t
*their_value
;
2179 svn_wc_conflict_description2_t
*desc
;
2181 svn_pool_clear(iterpool
);
2183 desc
= svn_wc_conflict_description_create_prop2(local_abspath
,
2188 desc
->operation
= operation
;
2189 desc
->src_left_version
= left_version
;
2190 desc
->src_right_version
= right_version
;
2192 desc
->property_name
= apr_pstrdup(result_pool
, propname
);
2194 my_value
= svn_hash_gets(my_props
, propname
);
2195 their_value
= svn_hash_gets(their_props
, propname
);
2196 old_value
= svn_hash_gets(their_old_props
, propname
);
2198 /* Compute the incoming side of the conflict ('action'). */
2199 if (their_value
== NULL
)
2200 desc
->action
= svn_wc_conflict_action_delete
;
2201 else if (old_value
== NULL
)
2202 desc
->action
= svn_wc_conflict_action_add
;
2204 desc
->action
= svn_wc_conflict_action_edit
;
2206 /* Compute the local side of the conflict ('reason'). */
2207 if (my_value
== NULL
)
2208 desc
->reason
= svn_wc_conflict_reason_deleted
;
2209 else if (old_value
== NULL
)
2210 desc
->reason
= svn_wc_conflict_reason_added
;
2212 desc
->reason
= svn_wc_conflict_reason_edited
;
2214 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2215 * ### their_abspath, and stores theirs_abspath in merged_file. */
2216 desc
->prop_reject_abspath
= prop_reject_abspath
; /* in result_pool */
2217 desc
->their_abspath
= desc
->prop_reject_abspath
;
2219 desc
->prop_value_base
= base_props
? svn_hash_gets(base_props
, propname
)
2220 : desc
->prop_value_incoming_old
;
2227 if (create_tempfiles
)
2229 SVN_ERR(svn_stream_open_unique(&s
, &desc
->my_abspath
, NULL
,
2230 svn_io_file_del_on_pool_cleanup
,
2231 result_pool
, iterpool
));
2232 len
= my_value
->len
;
2233 SVN_ERR(svn_stream_write(s
, my_value
->data
, &len
));
2234 SVN_ERR(svn_stream_close(s
));
2237 desc
->prop_value_working
= svn_string_dup(my_value
, result_pool
);
2245 /* ### For property conflicts, cd2 stores prop_reject_abspath in
2246 * ### their_abspath, and stores theirs_abspath in merged_file. */
2247 if (create_tempfiles
)
2249 SVN_ERR(svn_stream_open_unique(&s
, &desc
->merged_file
, NULL
,
2250 svn_io_file_del_on_pool_cleanup
,
2251 result_pool
, iterpool
));
2252 len
= their_value
->len
;
2253 SVN_ERR(svn_stream_write(s
, their_value
->data
, &len
));
2254 SVN_ERR(svn_stream_close(s
));
2257 desc
->prop_value_incoming_new
= svn_string_dup(their_value
, result_pool
);
2265 if (create_tempfiles
)
2267 SVN_ERR(svn_stream_open_unique(&s
, &desc
->base_abspath
, NULL
,
2268 svn_io_file_del_on_pool_cleanup
,
2269 result_pool
, iterpool
));
2270 len
= old_value
->len
;
2271 SVN_ERR(svn_stream_write(s
, old_value
->data
, &len
));
2272 SVN_ERR(svn_stream_close(s
));
2275 desc
->prop_value_incoming_old
= svn_string_dup(old_value
, result_pool
);
2278 APR_ARRAY_PUSH(conflicts
, svn_wc_conflict_description2_t
*) = desc
;
2280 svn_pool_destroy(iterpool
);
2282 return SVN_NO_ERROR
;
2286 svn_wc__read_conflicts(const apr_array_header_t
**conflicts
,
2287 svn_skel_t
**conflict_skel
,
2289 const char *local_abspath
,
2290 svn_boolean_t create_tempfiles
,
2291 svn_boolean_t only_tree_conflict
,
2292 apr_pool_t
*result_pool
,
2293 apr_pool_t
*scratch_pool
)
2295 svn_skel_t
*the_conflict_skel
;
2296 apr_array_header_t
*cflcts
;
2297 svn_boolean_t prop_conflicted
;
2298 svn_boolean_t text_conflicted
;
2299 svn_boolean_t tree_conflicted
;
2300 svn_wc_operation_t operation
;
2301 const apr_array_header_t
*locations
;
2302 const svn_wc_conflict_version_t
*left_version
= NULL
;
2303 const svn_wc_conflict_version_t
*right_version
= NULL
;
2304 svn_node_kind_t node_kind
;
2308 conflict_skel
= &the_conflict_skel
;
2310 SVN_ERR(svn_wc__db_read_conflict(conflict_skel
, &node_kind
, &props
,
2312 (conflict_skel
== &the_conflict_skel
)
2317 if (!*conflict_skel
)
2319 /* Some callers expect not NULL */
2320 *conflicts
= apr_array_make(result_pool
, 0,
2321 sizeof(svn_wc_conflict_description2_t
*));
2322 return SVN_NO_ERROR
;
2325 SVN_ERR(svn_wc__conflict_read_info(&operation
, &locations
, &text_conflicted
,
2326 &prop_conflicted
, &tree_conflicted
,
2327 db
, local_abspath
, *conflict_skel
,
2328 result_pool
, scratch_pool
));
2330 cflcts
= apr_array_make(result_pool
, 4,
2331 sizeof(svn_wc_conflict_description2_t
*));
2333 if (locations
&& locations
->nelts
> 0)
2334 left_version
= APR_ARRAY_IDX(locations
, 0, const svn_wc_conflict_version_t
*);
2335 if (locations
&& locations
->nelts
> 1)
2336 right_version
= APR_ARRAY_IDX(locations
, 1, const svn_wc_conflict_version_t
*);
2338 if (prop_conflicted
&& !only_tree_conflict
)
2340 SVN_ERR(read_prop_conflict_descs(cflcts
,
2341 db
, local_abspath
, *conflict_skel
,
2342 create_tempfiles
, node_kind
,
2343 operation
, left_version
, right_version
,
2344 result_pool
, scratch_pool
));
2347 if (text_conflicted
&& !only_tree_conflict
)
2349 svn_wc_conflict_description2_t
*desc
;
2351 SVN_ERR(read_text_conflict_desc(&desc
,
2352 db
, local_abspath
, *conflict_skel
,
2353 svn_prop_get_value(props
,
2354 SVN_PROP_MIME_TYPE
),
2355 operation
, left_version
, right_version
,
2356 result_pool
, scratch_pool
));
2357 APR_ARRAY_PUSH(cflcts
, svn_wc_conflict_description2_t
*) = desc
;
2360 if (tree_conflicted
)
2362 svn_wc_conflict_description2_t
*desc
;
2364 SVN_ERR(read_tree_conflict_desc(&desc
,
2365 db
, local_abspath
, node_kind
,
2367 operation
, left_version
, right_version
,
2368 result_pool
, scratch_pool
));
2370 APR_ARRAY_PUSH(cflcts
, const svn_wc_conflict_description2_t
*) = desc
;
2373 *conflicts
= cflcts
;
2374 return SVN_NO_ERROR
;
2378 svn_wc__read_conflict_descriptions2_t(const apr_array_header_t
**conflicts
,
2379 svn_wc_context_t
*wc_ctx
,
2380 const char *local_abspath
,
2381 apr_pool_t
*result_pool
,
2382 apr_pool_t
*scratch_pool
)
2384 return svn_wc__read_conflicts(conflicts
, NULL
, wc_ctx
->db
, local_abspath
,
2385 FALSE
, FALSE
, result_pool
, scratch_pool
);
2389 /*** Resolving a conflict automatically ***/
2392 * Resolve the property conflicts found in DB/LOCAL_ABSPATH according
2393 * to CONFLICT_CHOICE.
2395 * It is not an error if there is no prop conflict. If a prop conflict
2396 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2398 * Note: When there are no conflict markers on-disk to remove there is
2399 * no existing text conflict (unless we are still in the process of
2400 * creating the text conflict and we didn't register a marker file yet).
2401 * In this case the database contains old information, which we should
2402 * remove to avoid checking the next time. Resolving a property conflict
2403 * by just removing the marker file is a fully supported scenario since
2406 * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
2407 * In my opinion, 'mine_full'/'theirs_full' should select
2408 * the entire set of properties from 'mine' or 'theirs' respectively,
2409 * while 'mine_conflict'/'theirs_conflict' should select just the
2410 * properties that are in conflict. Or, '_full' should select the
2411 * entire property whereas '_conflict' should do a text merge within
2412 * each property, selecting hunks. Or all three kinds of behaviour
2413 * should be available (full set of props, full value of conflicting
2414 * props, or conflicting text hunks).
2415 * ### BH: If we make *_full select the full set of properties, we should
2416 * check if we shouldn't make it also select the full text for files.
2418 * ### TODO [JAF] All this complexity should not be down here in libsvn_wc
2419 * but in a layer above.
2421 * ### TODO [JAF] Options for 'base' should be like options for 'mine' and
2422 * for 'theirs' -- choose full set of props, full value of conflicting
2423 * props, or conflicting text hunks.
2426 static svn_error_t
*
2427 resolve_prop_conflict_on_node(svn_boolean_t
*did_resolve
,
2429 const char *local_abspath
,
2430 svn_skel_t
*conflicts
,
2431 const char *conflicted_propname
,
2432 svn_wc_conflict_choice_t conflict_choice
,
2433 const char *merged_file
,
2434 const svn_string_t
*merged_value
,
2435 svn_cancel_func_t cancel_func
,
2437 apr_pool_t
*scratch_pool
)
2439 const char *prop_reject_file
;
2440 apr_hash_t
*mine_props
;
2441 apr_hash_t
*their_old_props
;
2442 apr_hash_t
*their_props
;
2443 apr_hash_t
*conflicted_props
;
2444 apr_hash_t
*old_props
;
2445 apr_hash_t
*resolve_from
= NULL
;
2446 svn_skel_t
*work_items
= NULL
;
2447 svn_wc_operation_t operation
;
2448 svn_boolean_t prop_conflicted
;
2449 apr_hash_t
*actual_props
;
2450 svn_boolean_t resolved_all
, resolved_all_prop
;
2452 *did_resolve
= FALSE
;
2454 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
, NULL
, &prop_conflicted
,
2455 NULL
, db
, local_abspath
, conflicts
,
2456 scratch_pool
, scratch_pool
));
2457 if (!prop_conflicted
)
2458 return SVN_NO_ERROR
;
2460 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file
,
2461 &mine_props
, &their_old_props
,
2462 &their_props
, &conflicted_props
,
2463 db
, local_abspath
, conflicts
,
2464 scratch_pool
, scratch_pool
));
2466 if (!conflicted_props
)
2468 /* We have a pre 1.8 property conflict. Just mark it resolved */
2470 SVN_ERR(remove_artifact_file_if_exists(&work_items
, did_resolve
,
2471 db
, local_abspath
, prop_reject_file
,
2472 scratch_pool
, scratch_pool
));
2473 SVN_ERR(svn_wc__db_op_mark_resolved(db
, local_abspath
, FALSE
, TRUE
, FALSE
,
2474 work_items
, scratch_pool
));
2475 SVN_ERR(svn_wc__wq_run(db
, local_abspath
, cancel_func
, cancel_baton
,
2477 return SVN_NO_ERROR
;
2480 if (conflicted_propname
[0] != '\0'
2481 && !svn_hash_gets(conflicted_props
, conflicted_propname
))
2483 return SVN_NO_ERROR
; /* This property is not conflicted! */
2486 if (operation
== svn_wc_operation_merge
)
2487 SVN_ERR(svn_wc__db_read_pristine_props(&old_props
, db
, local_abspath
,
2488 scratch_pool
, scratch_pool
));
2490 old_props
= their_old_props
;
2492 SVN_ERR(svn_wc__db_read_props(&actual_props
, db
, local_abspath
,
2493 scratch_pool
, scratch_pool
));
2495 /* We currently handle *_conflict as *_full as this argument is currently
2496 always applied for all conflicts on a node at the same time. Giving
2497 an error would break some tests that assumed that this would just
2498 resolve property conflicts to working.
2500 An alternative way to handle these conflicts would be to just copy all
2501 property state from mine/theirs on the _full option instead of just the
2502 conflicted properties. In some ways this feels like a sensible option as
2503 that would take both properties and text from mine/theirs, but when not
2504 both properties and text are conflicted we would fail in doing so.
2506 switch (conflict_choice
)
2508 case svn_wc_conflict_choose_base
:
2509 resolve_from
= their_old_props
? their_old_props
: old_props
;
2511 case svn_wc_conflict_choose_mine_full
:
2512 case svn_wc_conflict_choose_mine_conflict
:
2513 resolve_from
= mine_props
;
2515 case svn_wc_conflict_choose_theirs_full
:
2516 case svn_wc_conflict_choose_theirs_conflict
:
2517 resolve_from
= their_props
;
2519 case svn_wc_conflict_choose_merged
:
2520 if ((merged_file
|| merged_value
) && conflicted_propname
[0] != '\0')
2522 resolve_from
= apr_hash_copy(scratch_pool
, actual_props
);
2526 svn_stringbuf_t
*merged_propval
;
2528 SVN_ERR(svn_stringbuf_from_file2(&merged_propval
, merged_file
,
2531 merged_value
= svn_stringbuf__morph_into_string(merged_propval
);
2533 svn_hash_sets(resolve_from
, conflicted_propname
, merged_value
);
2536 resolve_from
= NULL
;
2539 return svn_error_create(SVN_ERR_INCORRECT_PARAMS
, NULL
,
2540 _("Invalid 'conflict_result' argument"));
2546 apr_hash_index_t
*hi
;
2547 apr_hash_t
*apply_on_props
;
2549 if (conflicted_propname
[0] == '\0')
2551 /* Apply to all conflicted properties */
2552 apply_on_props
= conflicted_props
;
2556 /* Apply to a single property */
2557 apply_on_props
= apr_hash_make(scratch_pool
);
2558 svn_hash_sets(apply_on_props
, conflicted_propname
, "");
2561 /* Apply the selected changes */
2562 for (hi
= apr_hash_first(scratch_pool
, apply_on_props
);
2564 hi
= apr_hash_next(hi
))
2566 const char *propname
= apr_hash_this_key(hi
);
2567 svn_string_t
*new_value
= NULL
;
2569 new_value
= svn_hash_gets(resolve_from
, propname
);
2571 svn_hash_sets(actual_props
, propname
, new_value
);
2574 /*else the user accepted the properties as-is */
2576 /* This function handles conflicted_propname "" as resolving
2577 all property conflicts... Just what we need here */
2578 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all
, conflicts
,
2580 FALSE
, conflicted_propname
,
2582 scratch_pool
, scratch_pool
));
2586 /* Are there still property conflicts left? (or only...) */
2587 SVN_ERR(svn_wc__conflict_read_info(NULL
, NULL
, NULL
, &prop_conflicted
,
2588 NULL
, db
, local_abspath
, conflicts
,
2589 scratch_pool
, scratch_pool
));
2591 resolved_all_prop
= (! prop_conflicted
);
2595 resolved_all_prop
= TRUE
;
2599 if (resolved_all_prop
)
2601 /* Legacy behavior: Only report property conflicts as resolved when the
2602 property reject file exists
2604 If not the UI shows the conflict as already resolved
2605 (and in this case we just remove the in-db conflict) */
2606 SVN_ERR(remove_artifact_file_if_exists(&work_items
, did_resolve
,
2609 scratch_pool
, scratch_pool
));
2613 /* Create a new prej file, based on the remaining conflicts */
2614 SVN_ERR(svn_wc__wq_build_prej_install(&work_items
,
2616 scratch_pool
, scratch_pool
));
2617 *did_resolve
= TRUE
; /* We resolved a property conflict */
2620 /* This installs the updated conflict skel */
2621 SVN_ERR(svn_wc__db_op_set_props(db
, local_abspath
, actual_props
,
2622 FALSE
, conflicts
, work_items
,
2627 /* Remove the whole conflict. Should probably be integrated
2628 into the op_set_props() call */
2629 SVN_ERR(svn_wc__db_op_mark_resolved(db
, local_abspath
,
2631 NULL
, scratch_pool
));
2634 SVN_ERR(svn_wc__wq_run(db
, local_abspath
, cancel_func
, cancel_baton
,
2637 return SVN_NO_ERROR
;
2641 * Record a tree conflict resolution failure due to error condition ERR
2642 * in the RESOLVE_LATER hash table. If the hash table is not available
2643 * (meaning the caller does not wish to retry resolution later), or if
2644 * the error condition does not indicate circumstances where another
2645 * existing tree conflict is blocking the resolution attempt, then
2646 * return the error ERR itself.
2648 static svn_error_t
*
2649 handle_tree_conflict_resolution_failure(const char *local_abspath
,
2651 apr_hash_t
*resolve_later
)
2653 const char *dup_abspath
;
2656 || (err
->apr_err
!= SVN_ERR_WC_OBSTRUCTED_UPDATE
2657 && err
->apr_err
!= SVN_ERR_WC_FOUND_CONFLICT
))
2658 return svn_error_trace(err
); /* Give up. Do not retry resolution later. */
2660 svn_error_clear(err
);
2661 dup_abspath
= apr_pstrdup(apr_hash_pool_get(resolve_later
),
2664 svn_hash_sets(resolve_later
, dup_abspath
, dup_abspath
);
2666 return SVN_NO_ERROR
; /* Caller may retry after resolving other conflicts. */
2670 * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
2673 * It is not an error if there is no tree conflict. If a tree conflict
2674 * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2676 * It is not an error if there is no tree conflict.
2678 * If the conflict can't be resolved yet (e.g. because another tree conflict
2679 * is blocking a storage location), and RESOLVE_LATER is not NULL, store the
2680 * tree conflict in RESOLVE_LATER and do not mark the conflict resolved.
2681 * Else if RESOLVE_LATER is NULL, do not mark the conflict resolved and
2682 * return the error which prevented the conflict from being marked resolved.
2684 static svn_error_t
*
2685 resolve_tree_conflict_on_node(svn_boolean_t
*did_resolve
,
2687 const char *local_abspath
,
2688 const svn_skel_t
*conflicts
,
2689 svn_wc_conflict_choice_t conflict_choice
,
2690 apr_hash_t
*resolve_later
,
2691 svn_wc_notify_func2_t notify_func
,
2693 svn_cancel_func_t cancel_func
,
2695 apr_pool_t
*scratch_pool
)
2697 svn_wc_conflict_reason_t reason
;
2698 svn_wc_conflict_action_t action
;
2699 svn_wc_operation_t operation
;
2700 svn_boolean_t tree_conflicted
;
2701 const char *src_op_root_abspath
;
2703 *did_resolve
= FALSE
;
2705 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
, NULL
, NULL
,
2706 &tree_conflicted
, db
, local_abspath
,
2707 conflicts
, scratch_pool
, scratch_pool
));
2708 if (!tree_conflicted
)
2709 return SVN_NO_ERROR
;
2711 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason
, &action
,
2712 &src_op_root_abspath
,
2713 NULL
, db
, local_abspath
,
2715 scratch_pool
, scratch_pool
));
2717 if (operation
== svn_wc_operation_update
2718 || operation
== svn_wc_operation_switch
)
2721 if (reason
== svn_wc_conflict_reason_deleted
||
2722 reason
== svn_wc_conflict_reason_replaced
)
2724 if (conflict_choice
== svn_wc_conflict_choose_merged
)
2726 /* Break moves for any children moved out of this directory,
2727 * and leave this directory deleted. */
2729 if (action
!= svn_wc_conflict_action_delete
)
2731 SVN_ERR(svn_wc__db_op_break_moved_away(
2732 db
, local_abspath
, src_op_root_abspath
, TRUE
,
2733 notify_func
, notify_baton
,
2735 *did_resolve
= TRUE
;
2736 return SVN_NO_ERROR
; /* Marked resolved by function*/
2738 /* else # The move is/moves are already broken */
2741 *did_resolve
= TRUE
;
2743 else if (conflict_choice
== svn_wc_conflict_choose_mine_conflict
)
2745 svn_skel_t
*new_conflicts
;
2747 /* Raise local moved-away vs. incoming edit conflicts on
2748 * any children moved out of this directory, and leave
2749 * this directory as-is.
2751 * The newly conflicted moved-away children will be updated
2752 * if they are resolved with 'mine_conflict' as well. */
2753 err
= svn_wc__db_op_raise_moved_away(
2754 db
, local_abspath
, notify_func
, notify_baton
,
2758 SVN_ERR(handle_tree_conflict_resolution_failure(
2759 local_abspath
, err
, resolve_later
));
2761 /* We might now have a moved-away on *this* path, let's
2762 try to resolve that directly if that is the case */
2763 SVN_ERR(svn_wc__db_read_conflict(&new_conflicts
, NULL
, NULL
,
2765 scratch_pool
, scratch_pool
));
2768 SVN_ERR(svn_wc__conflict_read_info(NULL
, NULL
, NULL
, NULL
,
2775 if (!new_conflicts
|| !tree_conflicted
)
2777 /* TC is marked resolved by calling
2778 svn_wc__db_op_raise_moved_away */
2779 *did_resolve
= TRUE
;
2780 return SVN_NO_ERROR
;
2783 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason
, &action
,
2784 &src_op_root_abspath
,
2791 if (reason
!= svn_wc_conflict_reason_moved_away
)
2793 *did_resolve
= TRUE
;
2794 return SVN_NO_ERROR
; /* We fixed one, but... */
2797 conflicts
= new_conflicts
;
2798 /* Fall through in moved_away handling */
2801 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
,
2803 _("Tree conflict can only be resolved to "
2804 "'working' or 'mine-conflict' state; "
2805 "'%s' not resolved"),
2806 svn_dirent_local_style(local_abspath
,
2810 if (reason
== svn_wc_conflict_reason_moved_away
2811 && action
== svn_wc_conflict_action_edit
)
2813 /* After updates, we can resolve local moved-away
2814 * vs. any incoming change, either by updating the
2815 * moved-away node (mine-conflict) or by breaking the
2816 * move (theirs-conflict). */
2817 if (conflict_choice
== svn_wc_conflict_choose_mine_conflict
)
2819 err
= svn_wc__db_update_moved_away_conflict_victim(
2820 db
, local_abspath
, src_op_root_abspath
,
2821 operation
, action
, reason
,
2822 cancel_func
, cancel_baton
,
2823 notify_func
, notify_baton
,
2827 SVN_ERR(handle_tree_conflict_resolution_failure(
2828 local_abspath
, err
, resolve_later
));
2830 *did_resolve
= TRUE
;
2832 else if (conflict_choice
== svn_wc_conflict_choose_merged
)
2834 /* We must break the move if the user accepts the current
2835 * working copy state instead of updating the move.
2836 * Else the move would be left in an invalid state. */
2838 SVN_ERR(svn_wc__db_op_break_moved_away(db
, local_abspath
,
2839 src_op_root_abspath
, TRUE
,
2840 notify_func
, notify_baton
,
2842 *did_resolve
= TRUE
;
2843 return SVN_NO_ERROR
; /* Conflict is marked resolved */
2846 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
,
2848 _("Tree conflict can only be resolved to "
2849 "'working' or 'mine-conflict' state; "
2850 "'%s' not resolved"),
2851 svn_dirent_local_style(local_abspath
,
2854 else if (reason
== svn_wc_conflict_reason_moved_away
2855 && action
!= svn_wc_conflict_action_edit
)
2857 /* action added is impossible, because that would imply that
2858 something was added, but before that already moved...
2859 (which would imply a replace) */
2860 SVN_ERR_ASSERT(action
== svn_wc_conflict_action_delete
2861 || action
== svn_wc_conflict_action_replace
);
2863 if (conflict_choice
== svn_wc_conflict_choose_merged
)
2865 /* Whatever was moved is removed at its original location by the
2866 update. That must also remove the recording of the move, so
2867 we don't have to do anything here. */
2869 *did_resolve
= TRUE
;
2871 else if (conflict_choice
== svn_wc_conflict_choose_mine_conflict
)
2873 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
,
2875 _("Tree conflict can only be "
2876 "resolved to 'working' state; "
2877 "'%s' is no longer moved"),
2878 svn_dirent_local_style(local_abspath
,
2886 if (conflict_choice
!= svn_wc_conflict_choose_merged
)
2888 /* For other tree conflicts, there is no way to pick
2889 * theirs-full or mine-full, etc. Throw an error if the
2890 * user expects us to be smarter than we really are. */
2891 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
,
2893 _("Tree conflict can only be "
2894 "resolved to 'working' state; "
2895 "'%s' not resolved"),
2896 svn_dirent_local_style(local_abspath
,
2900 *did_resolve
= TRUE
;
2903 SVN_ERR_ASSERT(*did_resolve
);
2905 SVN_ERR(svn_wc__db_op_mark_resolved(db
, local_abspath
, FALSE
, FALSE
, TRUE
,
2906 NULL
, scratch_pool
));
2907 SVN_ERR(svn_wc__wq_run(db
, local_abspath
, cancel_func
, cancel_baton
,
2909 return SVN_NO_ERROR
;
2913 svn_wc__mark_resolved_text_conflict(svn_wc__db_t
*db
,
2914 const char *local_abspath
,
2915 svn_cancel_func_t cancel_func
,
2917 apr_pool_t
*scratch_pool
)
2919 svn_skel_t
*work_items
;
2920 svn_skel_t
*conflict
;
2922 SVN_ERR(svn_wc__db_read_conflict(&conflict
, NULL
, NULL
,
2924 scratch_pool
, scratch_pool
));
2927 return SVN_NO_ERROR
;
2929 SVN_ERR(build_text_conflict_resolve_items(&work_items
, NULL
,
2930 db
, local_abspath
, conflict
,
2931 svn_wc_conflict_choose_merged
,
2933 cancel_func
, cancel_baton
,
2934 scratch_pool
, scratch_pool
));
2936 SVN_ERR(svn_wc__db_op_mark_resolved(db
, local_abspath
, TRUE
, FALSE
, FALSE
,
2937 work_items
, scratch_pool
));
2939 return svn_error_trace(svn_wc__wq_run(db
, local_abspath
,
2940 cancel_func
, cancel_baton
,
2945 svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t
*db
,
2946 const char *local_abspath
,
2947 apr_pool_t
*scratch_pool
)
2949 svn_boolean_t ignored_result
;
2950 svn_skel_t
*conflicts
;
2952 SVN_ERR(svn_wc__db_read_conflict(&conflicts
, NULL
, NULL
,
2954 scratch_pool
, scratch_pool
));
2957 return SVN_NO_ERROR
;
2959 return svn_error_trace(resolve_prop_conflict_on_node(
2961 db
, local_abspath
, conflicts
, "",
2962 svn_wc_conflict_choose_merged
,
2969 /* Baton for conflict_status_walker */
2970 struct conflict_status_walker_baton
2973 svn_boolean_t resolve_text
;
2974 const char *resolve_prop
;
2975 svn_boolean_t resolve_tree
;
2976 svn_wc_conflict_choice_t conflict_choice
;
2977 svn_wc_conflict_resolver_func2_t conflict_func
;
2978 void *conflict_baton
;
2979 svn_cancel_func_t cancel_func
;
2981 svn_wc_notify_func2_t notify_func
;
2983 svn_boolean_t resolved_one
;
2984 apr_hash_t
*resolve_later
;
2987 /* Implements svn_wc_notify_func2_t to collect new conflicts caused by
2988 resolving a tree conflict. */
2990 tree_conflict_collector(void *baton
,
2991 const svn_wc_notify_t
*notify
,
2994 struct conflict_status_walker_baton
*cswb
= baton
;
2996 if (cswb
->notify_func
)
2997 cswb
->notify_func(cswb
->notify_baton
, notify
, pool
);
2999 if (cswb
->resolve_later
3000 && (notify
->action
== svn_wc_notify_tree_conflict
3001 || notify
->prop_state
== svn_wc_notify_state_conflicted
3002 || notify
->content_state
== svn_wc_notify_state_conflicted
))
3004 if (!svn_hash_gets(cswb
->resolve_later
, notify
->path
))
3006 const char *dup_path
;
3008 dup_path
= apr_pstrdup(apr_hash_pool_get(cswb
->resolve_later
),
3011 svn_hash_sets(cswb
->resolve_later
, dup_path
, dup_path
);
3016 /* Implements svn_wc_status4_t to walk all conflicts to resolve.
3018 static svn_error_t
*
3019 conflict_status_walker(void *baton
,
3020 const char *local_abspath
,
3021 const svn_wc_status3_t
*status
,
3022 apr_pool_t
*scratch_pool
)
3024 struct conflict_status_walker_baton
*cswb
= baton
;
3025 svn_wc__db_t
*db
= cswb
->db
;
3026 svn_wc_notify_action_t notify_action
= svn_wc_notify_resolved
;
3027 const apr_array_header_t
*conflicts
;
3028 apr_pool_t
*iterpool
;
3030 svn_boolean_t resolved
= FALSE
;
3031 svn_skel_t
*conflict
;
3032 const svn_wc_conflict_description2_t
*cd
;
3034 if (!status
->conflicted
)
3035 return SVN_NO_ERROR
;
3037 iterpool
= svn_pool_create(scratch_pool
);
3039 SVN_ERR(svn_wc__read_conflicts(&conflicts
, &conflict
,
3041 (cswb
->conflict_func
!= NULL
) /* tmp files */,
3042 FALSE
/* only tree conflicts */,
3043 scratch_pool
, iterpool
));
3045 for (i
= 0; i
< conflicts
->nelts
; i
++)
3047 svn_boolean_t did_resolve
;
3048 svn_wc_conflict_choice_t my_choice
= cswb
->conflict_choice
;
3049 svn_wc_conflict_result_t
*result
= NULL
;
3050 svn_skel_t
*work_items
;
3052 cd
= APR_ARRAY_IDX(conflicts
, i
, const svn_wc_conflict_description2_t
*);
3054 if ((cd
->kind
== svn_wc_conflict_kind_property
3055 && (!cswb
->resolve_prop
3056 || (*cswb
->resolve_prop
!= '\0'
3057 && strcmp(cswb
->resolve_prop
, cd
->property_name
) != 0)))
3058 || (cd
->kind
== svn_wc_conflict_kind_text
&& !cswb
->resolve_text
)
3059 || (cd
->kind
== svn_wc_conflict_kind_tree
&& !cswb
->resolve_tree
))
3061 continue; /* Easy out. Don't call resolver func and ignore result */
3064 svn_pool_clear(iterpool
);
3066 if (my_choice
== svn_wc_conflict_choose_unspecified
)
3068 if (!cswb
->conflict_func
)
3069 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3070 _("No conflict-callback and no "
3071 "pre-defined conflict-choice provided"));
3073 SVN_ERR(cswb
->conflict_func(&result
, cd
, cswb
->conflict_baton
,
3074 iterpool
, iterpool
));
3076 my_choice
= result
->choice
;
3080 if (my_choice
== svn_wc_conflict_choose_postpone
)
3085 case svn_wc_conflict_kind_tree
:
3086 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve
,
3088 local_abspath
, conflict
,
3090 cswb
->resolve_later
,
3091 tree_conflict_collector
,
3100 notify_action
= svn_wc_notify_resolved_tree
;
3104 case svn_wc_conflict_kind_text
:
3105 SVN_ERR(build_text_conflict_resolve_items(
3108 db
, local_abspath
, conflict
,
3110 result
? result
->merged_file
3112 result
? result
->save_merged
3114 NULL
/* merge_options */,
3117 iterpool
, iterpool
));
3119 SVN_ERR(svn_wc__db_op_mark_resolved(db
, local_abspath
,
3121 work_items
, iterpool
));
3122 SVN_ERR(svn_wc__wq_run(db
, local_abspath
,
3123 cswb
->cancel_func
, cswb
->cancel_baton
,
3126 notify_action
= svn_wc_notify_resolved_text
;
3129 case svn_wc_conflict_kind_property
:
3130 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve
,
3137 ? result
->merged_file
3140 ? result
->merged_value
3149 notify_action
= svn_wc_notify_resolved_prop
;
3154 /* We can't resolve other conflict types */
3160 if (cswb
->notify_func
&& resolved
)
3162 svn_wc_notify_t
*notify
;
3164 /* If our caller asked for all conflicts to be resolved,
3165 * send a general 'resolved' notification. */
3166 if (cswb
->resolve_text
&& cswb
->resolve_tree
&&
3167 (cswb
->resolve_prop
== NULL
|| cswb
->resolve_prop
[0] == '\0'))
3168 notify_action
= svn_wc_notify_resolved
;
3170 /* If we resolved a property conflict, but no specific property was
3171 * requested by the caller, send a general 'resolved' notification. */
3172 if (notify_action
== svn_wc_notify_resolved_prop
&&
3173 (cswb
->resolve_prop
== NULL
|| cswb
->resolve_prop
[0] == '\0'))
3174 notify_action
= svn_wc_notify_resolved
;
3176 notify
= svn_wc_create_notify(local_abspath
, notify_action
, iterpool
);
3178 /* Add the property name for property-specific notifications. */
3179 if (notify_action
== svn_wc_notify_resolved_prop
)
3181 notify
->prop_name
= cd
->property_name
;
3182 SVN_ERR_ASSERT(strlen(notify
->prop_name
) > 0);
3185 cswb
->notify_func(cswb
->notify_baton
, notify
, iterpool
);
3189 cswb
->resolved_one
= TRUE
;
3191 svn_pool_destroy(iterpool
);
3193 return SVN_NO_ERROR
;
3197 svn_wc__resolve_conflicts(svn_wc_context_t
*wc_ctx
,
3198 const char *local_abspath
,
3200 svn_boolean_t resolve_text
,
3201 const char *resolve_prop
,
3202 svn_boolean_t resolve_tree
,
3203 svn_wc_conflict_choice_t conflict_choice
,
3204 svn_wc_conflict_resolver_func2_t conflict_func
,
3205 void *conflict_baton
,
3206 svn_cancel_func_t cancel_func
,
3208 svn_wc_notify_func2_t notify_func
,
3210 apr_pool_t
*scratch_pool
)
3212 struct conflict_status_walker_baton cswb
;
3213 apr_pool_t
*iterpool
= NULL
;
3216 if (depth
== svn_depth_unknown
)
3217 depth
= svn_depth_infinity
;
3219 cswb
.db
= wc_ctx
->db
;
3220 cswb
.resolve_text
= resolve_text
;
3221 cswb
.resolve_prop
= resolve_prop
;
3222 cswb
.resolve_tree
= resolve_tree
;
3223 cswb
.conflict_choice
= conflict_choice
;
3225 cswb
.conflict_func
= conflict_func
;
3226 cswb
.conflict_baton
= conflict_baton
;
3228 cswb
.cancel_func
= cancel_func
;
3229 cswb
.cancel_baton
= cancel_baton
;
3231 cswb
.notify_func
= notify_func
;
3232 cswb
.notify_baton
= notify_baton
;
3234 cswb
.resolved_one
= FALSE
;
3235 cswb
.resolve_later
= (depth
!= svn_depth_empty
)
3236 ? apr_hash_make(scratch_pool
)
3240 notify_func(notify_baton
,
3241 svn_wc_create_notify(local_abspath
,
3242 svn_wc_notify_conflict_resolver_starting
,
3246 err
= svn_wc_walk_status(wc_ctx
,
3249 FALSE
/* get_all */,
3250 FALSE
/* no_ignore */,
3251 TRUE
/* ignore_text_mods */,
3252 NULL
/* ignore_patterns */,
3253 conflict_status_walker
, &cswb
,
3254 cancel_func
, cancel_baton
,
3257 /* If we got new tree conflicts (or delayed conflicts) during the initial
3258 walk, we now walk them one by one as closure. */
3259 while (!err
&& cswb
.resolve_later
&& apr_hash_count(cswb
.resolve_later
))
3261 apr_hash_index_t
*hi
;
3262 svn_wc_status3_t
*status
= NULL
;
3263 const char *tc_abspath
= NULL
;
3266 svn_pool_clear(iterpool
);
3268 iterpool
= svn_pool_create(scratch_pool
);
3270 hi
= apr_hash_first(scratch_pool
, cswb
.resolve_later
);
3271 cswb
.resolve_later
= apr_hash_make(scratch_pool
);
3272 cswb
.resolved_one
= FALSE
;
3274 for (; hi
&& !err
; hi
= apr_hash_next(hi
))
3276 const char *relpath
;
3277 svn_pool_clear(iterpool
);
3279 tc_abspath
= apr_hash_this_key(hi
);
3282 SVN_ERR(cancel_func(cancel_baton
));
3284 relpath
= svn_dirent_skip_ancestor(local_abspath
,
3288 || (depth
>= svn_depth_empty
3289 && depth
< svn_depth_infinity
3290 && strchr(relpath
, '/')))
3295 SVN_ERR(svn_wc_status3(&status
, wc_ctx
, tc_abspath
,
3296 iterpool
, iterpool
));
3298 if (depth
== svn_depth_files
3299 && status
->kind
== svn_node_dir
)
3302 err
= svn_error_trace(conflict_status_walker(&cswb
, tc_abspath
,
3303 status
, scratch_pool
));
3306 /* None of the remaining conflicts got resolved, and non did provide
3309 We can fix that if we disable the 'resolve_later' option...
3311 if (!cswb
.resolved_one
&& !err
&& tc_abspath
3312 && apr_hash_count(cswb
.resolve_later
))
3314 /* Run the last resolve operation again. We still have status
3315 and tc_abspath for that one. */
3317 cswb
.resolve_later
= NULL
; /* Produce proper error! */
3319 /* Recreate the error */
3320 err
= svn_error_trace(conflict_status_walker(&cswb
, tc_abspath
,
3321 status
, scratch_pool
));
3323 SVN_ERR_ASSERT(err
!= NULL
);
3325 err
= svn_error_createf(
3326 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, err
,
3327 _("Unable to resolve pending conflict on '%s'"),
3328 svn_dirent_local_style(tc_abspath
, scratch_pool
));
3334 svn_pool_destroy(iterpool
);
3336 if (err
&& err
->apr_err
!= SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
)
3337 err
= svn_error_createf(
3338 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, err
,
3339 _("Unable to resolve conflicts on '%s'"),
3340 svn_dirent_local_style(local_abspath
, scratch_pool
));
3345 notify_func(notify_baton
,
3346 svn_wc_create_notify(local_abspath
,
3347 svn_wc_notify_conflict_resolver_done
,
3351 return SVN_NO_ERROR
;
3355 svn_wc_resolved_conflict5(svn_wc_context_t
*wc_ctx
,
3356 const char *local_abspath
,
3358 svn_boolean_t resolve_text
,
3359 const char *resolve_prop
,
3360 svn_boolean_t resolve_tree
,
3361 svn_wc_conflict_choice_t conflict_choice
,
3362 svn_cancel_func_t cancel_func
,
3364 svn_wc_notify_func2_t notify_func
,
3366 apr_pool_t
*scratch_pool
)
3368 return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx
, local_abspath
,
3369 depth
, resolve_text
,
3370 resolve_prop
, resolve_tree
,
3373 cancel_func
, cancel_baton
,
3374 notify_func
, notify_baton
,
3378 /* Constructor for the result-structure returned by conflict callbacks. */
3379 svn_wc_conflict_result_t
*
3380 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice
,
3381 const char *merged_file
,
3384 svn_wc_conflict_result_t
*result
= apr_pcalloc(pool
, sizeof(*result
));
3385 result
->choice
= choice
;
3386 result
->merged_file
= apr_pstrdup(pool
, merged_file
);
3387 result
->save_merged
= FALSE
;
3388 result
->merged_value
= NULL
;
3390 /* If we add more fields to svn_wc_conflict_result_t, add them here. */
3396 svn_wc__conflict_text_mark_resolved(svn_wc_context_t
*wc_ctx
,
3397 const char *local_abspath
,
3398 svn_wc_conflict_choice_t choice
,
3399 svn_cancel_func_t cancel_func
,
3401 svn_wc_notify_func2_t notify_func
,
3403 apr_pool_t
*scratch_pool
)
3405 svn_skel_t
*work_items
;
3406 svn_skel_t
*conflict
;
3407 svn_boolean_t did_resolve
;
3409 SVN_ERR(svn_wc__db_read_conflict(&conflict
, NULL
, NULL
,
3410 wc_ctx
->db
, local_abspath
,
3411 scratch_pool
, scratch_pool
));
3414 return SVN_NO_ERROR
;
3416 SVN_ERR(build_text_conflict_resolve_items(&work_items
, &did_resolve
,
3417 wc_ctx
->db
, local_abspath
,
3420 cancel_func
, cancel_baton
,
3421 scratch_pool
, scratch_pool
));
3423 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx
->db
, local_abspath
,
3425 work_items
, scratch_pool
));
3427 SVN_ERR(svn_wc__wq_run(wc_ctx
->db
, local_abspath
,
3428 cancel_func
, cancel_baton
,
3431 if (did_resolve
&& notify_func
)
3432 notify_func(notify_baton
,
3433 svn_wc_create_notify(local_abspath
,
3434 svn_wc_notify_resolved_text
,
3438 return SVN_NO_ERROR
;
3442 svn_wc__conflict_prop_mark_resolved(svn_wc_context_t
*wc_ctx
,
3443 const char *local_abspath
,
3444 const char *propname
,
3445 svn_wc_conflict_choice_t choice
,
3446 const svn_string_t
*merged_value
,
3447 svn_wc_notify_func2_t notify_func
,
3449 apr_pool_t
*scratch_pool
)
3451 svn_boolean_t did_resolve
;
3452 svn_skel_t
*conflicts
;
3454 SVN_ERR(svn_wc__db_read_conflict(&conflicts
, NULL
, NULL
,
3455 wc_ctx
->db
, local_abspath
,
3456 scratch_pool
, scratch_pool
));
3459 return SVN_NO_ERROR
;
3461 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve
, wc_ctx
->db
,
3462 local_abspath
, conflicts
,
3463 propname
, choice
, NULL
, merged_value
,
3464 NULL
, NULL
, scratch_pool
));
3466 if (did_resolve
&& notify_func
)
3468 svn_wc_notify_t
*notify
;
3470 /* Send a general notification if no specific property was requested. */
3471 if (propname
== NULL
|| propname
[0] == '\0')
3473 notify
= svn_wc_create_notify(local_abspath
,
3474 svn_wc_notify_resolved
,
3479 notify
= svn_wc_create_notify(local_abspath
,
3480 svn_wc_notify_resolved_prop
,
3482 notify
->prop_name
= propname
;
3485 notify_func(notify_baton
, notify
, scratch_pool
);
3487 return SVN_NO_ERROR
;
3491 svn_wc__conflict_tree_update_break_moved_away(svn_wc_context_t
*wc_ctx
,
3492 const char *local_abspath
,
3493 svn_cancel_func_t cancel_func
,
3495 svn_wc_notify_func2_t notify_func
,
3497 apr_pool_t
*scratch_pool
)
3499 svn_wc_conflict_reason_t reason
;
3500 svn_wc_conflict_action_t action
;
3501 svn_wc_operation_t operation
;
3502 svn_boolean_t tree_conflicted
;
3503 const char *src_op_root_abspath
;
3504 const apr_array_header_t
*conflicts
;
3505 svn_skel_t
*conflict_skel
;
3507 SVN_ERR(svn_wc__read_conflicts(&conflicts
, &conflict_skel
,
3508 wc_ctx
->db
, local_abspath
,
3509 FALSE
, /* no tempfiles */
3510 FALSE
, /* only tree conflicts */
3511 scratch_pool
, scratch_pool
));
3513 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
, NULL
, NULL
,
3514 &tree_conflicted
, wc_ctx
->db
,
3515 local_abspath
, conflict_skel
,
3516 scratch_pool
, scratch_pool
));
3517 if (!tree_conflicted
)
3518 return SVN_NO_ERROR
;
3520 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason
, &action
,
3521 &src_op_root_abspath
, NULL
,
3522 wc_ctx
->db
, local_abspath
,
3524 scratch_pool
, scratch_pool
));
3526 /* Make sure the expected conflict is recorded. */
3527 if (operation
!= svn_wc_operation_update
&&
3528 operation
!= svn_wc_operation_switch
)
3529 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3530 _("Unexpected conflict operation '%s' on '%s'"),
3531 svn_token__to_word(operation_map
, operation
),
3532 svn_dirent_local_style(local_abspath
,
3534 if (reason
!= svn_wc_conflict_reason_deleted
&&
3535 reason
!= svn_wc_conflict_reason_replaced
&&
3536 reason
!= svn_wc_conflict_reason_moved_away
)
3537 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3538 _("Unexpected conflict reason '%s' on '%s'"),
3539 svn_token__to_word(reason_map
, reason
),
3540 svn_dirent_local_style(local_abspath
,
3543 /* Break moves for any children moved out of this directory,
3544 * and leave this directory deleted. */
3545 if (action
!= svn_wc_conflict_action_delete
)
3547 SVN_ERR(svn_wc__db_op_break_moved_away(
3548 wc_ctx
->db
, local_abspath
, src_op_root_abspath
, TRUE
,
3549 notify_func
, notify_baton
, scratch_pool
));
3550 /* Conflict was marked resolved by db_op_break_moved_away() call .*/
3553 notify_func(notify_baton
,
3554 svn_wc_create_notify(local_abspath
,
3555 svn_wc_notify_resolved_tree
,
3558 return SVN_NO_ERROR
;
3560 /* else # The move is/moves are already broken */
3562 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx
->db
, local_abspath
,
3564 NULL
, scratch_pool
));
3565 SVN_ERR(svn_wc__wq_run(wc_ctx
->db
, local_abspath
, cancel_func
, cancel_baton
,
3569 notify_func(notify_baton
,
3570 svn_wc_create_notify(local_abspath
,
3571 svn_wc_notify_resolved_tree
,
3575 return SVN_NO_ERROR
;
3579 svn_wc__conflict_tree_update_raise_moved_away(svn_wc_context_t
*wc_ctx
,
3580 const char *local_abspath
,
3581 svn_cancel_func_t cancel_func
,
3583 svn_wc_notify_func2_t notify_func
,
3585 apr_pool_t
*scratch_pool
)
3587 svn_wc_conflict_reason_t reason
;
3588 svn_wc_conflict_action_t action
;
3589 svn_wc_operation_t operation
;
3590 svn_boolean_t tree_conflicted
;
3591 const apr_array_header_t
*conflicts
;
3592 svn_skel_t
*conflict_skel
;
3594 SVN_ERR(svn_wc__read_conflicts(&conflicts
, &conflict_skel
,
3595 wc_ctx
->db
, local_abspath
,
3596 FALSE
, /* no tempfiles */
3597 FALSE
, /* only tree conflicts */
3598 scratch_pool
, scratch_pool
));
3600 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
, NULL
, NULL
,
3601 &tree_conflicted
, wc_ctx
->db
,
3602 local_abspath
, conflict_skel
,
3603 scratch_pool
, scratch_pool
));
3604 if (!tree_conflicted
)
3605 return SVN_NO_ERROR
;
3607 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason
, &action
, NULL
, NULL
,
3608 wc_ctx
->db
, local_abspath
,
3610 scratch_pool
, scratch_pool
));
3612 /* Make sure the expected conflict is recorded. */
3613 if (operation
!= svn_wc_operation_update
&&
3614 operation
!= svn_wc_operation_switch
)
3615 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3616 _("Unexpected conflict operation '%s' on '%s'"),
3617 svn_token__to_word(operation_map
, operation
),
3618 svn_dirent_local_style(local_abspath
,
3620 if (reason
!= svn_wc_conflict_reason_deleted
&&
3621 reason
!= svn_wc_conflict_reason_replaced
)
3622 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3623 _("Unexpected conflict reason '%s' on '%s'"),
3624 svn_token__to_word(reason_map
, reason
),
3625 svn_dirent_local_style(local_abspath
,
3627 if (action
!= svn_wc_conflict_action_edit
)
3628 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3629 _("Unexpected conflict action '%s' on '%s'"),
3630 svn_token__to_word(action_map
, action
),
3631 svn_dirent_local_style(local_abspath
,
3634 /* Raise local moved-away vs. incoming edit conflicts on any children
3635 * moved out of this directory, and leave this directory as-is.
3636 * The user may choose to update newly conflicted moved-away children
3637 * when resolving them. If this function raises an error, the conflict
3638 * cannot be resolved yet because other conflicts or obstructions
3639 * prevent us from propagating the conflict to moved-away children. */
3640 SVN_ERR(svn_wc__db_op_raise_moved_away(wc_ctx
->db
, local_abspath
,
3641 notify_func
, notify_baton
,
3644 /* The conflict was marked resolved by svn_wc__db_op_raise_moved_away(). */
3646 notify_func(notify_baton
,
3647 svn_wc_create_notify(local_abspath
,
3648 svn_wc_notify_resolved_tree
,
3652 return SVN_NO_ERROR
;
3656 svn_wc__conflict_tree_update_moved_away_node(svn_wc_context_t
*wc_ctx
,
3657 const char *local_abspath
,
3658 svn_cancel_func_t cancel_func
,
3660 svn_wc_notify_func2_t notify_func
,
3662 apr_pool_t
*scratch_pool
)
3664 svn_wc_conflict_reason_t reason
;
3665 svn_wc_conflict_action_t action
;
3666 svn_wc_operation_t operation
;
3667 svn_boolean_t tree_conflicted
;
3668 const char *src_op_root_abspath
;
3669 const apr_array_header_t
*conflicts
;
3670 svn_skel_t
*conflict_skel
;
3672 SVN_ERR(svn_wc__read_conflicts(&conflicts
, &conflict_skel
,
3673 wc_ctx
->db
, local_abspath
,
3674 FALSE
, /* no tempfiles */
3675 FALSE
, /* only tree conflicts */
3676 scratch_pool
, scratch_pool
));
3678 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
, NULL
, NULL
,
3679 &tree_conflicted
, wc_ctx
->db
,
3680 local_abspath
, conflict_skel
,
3681 scratch_pool
, scratch_pool
));
3682 if (!tree_conflicted
)
3683 return SVN_NO_ERROR
;
3685 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason
, &action
,
3686 &src_op_root_abspath
, NULL
,
3687 wc_ctx
->db
, local_abspath
,
3689 scratch_pool
, scratch_pool
));
3691 /* Make sure the expected conflict is recorded. */
3692 if (operation
!= svn_wc_operation_update
&&
3693 operation
!= svn_wc_operation_switch
)
3694 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3695 _("Unexpected conflict operation '%s' on '%s'"),
3696 svn_token__to_word(operation_map
, operation
),
3697 svn_dirent_local_style(local_abspath
,
3699 if (reason
!= svn_wc_conflict_reason_moved_away
)
3700 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3701 _("Unexpected conflict reason '%s' on '%s'"),
3702 svn_token__to_word(reason_map
, reason
),
3703 svn_dirent_local_style(local_abspath
,
3705 if (action
!= svn_wc_conflict_action_edit
)
3706 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3707 _("Unexpected conflict action '%s' on '%s'"),
3708 svn_token__to_word(action_map
, action
),
3709 svn_dirent_local_style(local_abspath
,
3712 /* Update the moved-away conflict victim. */
3713 SVN_ERR(svn_wc__db_update_moved_away_conflict_victim(wc_ctx
->db
,
3715 src_op_root_abspath
,
3725 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx
->db
, local_abspath
,
3727 NULL
, scratch_pool
));
3728 SVN_ERR(svn_wc__wq_run(wc_ctx
->db
, local_abspath
, cancel_func
, cancel_baton
,
3732 notify_func(notify_baton
,
3733 svn_wc_create_notify(local_abspath
,
3734 svn_wc_notify_resolved_tree
,
3738 return SVN_NO_ERROR
;
3742 svn_wc__conflict_tree_update_incoming_move(svn_wc_context_t
*wc_ctx
,
3743 const char *local_abspath
,
3744 const char *dest_abspath
,
3745 svn_cancel_func_t cancel_func
,
3747 svn_wc_notify_func2_t notify_func
,
3749 apr_pool_t
*scratch_pool
)
3751 svn_wc_conflict_reason_t local_change
;
3752 svn_wc_conflict_action_t incoming_change
;
3753 svn_wc_operation_t operation
;
3754 svn_boolean_t tree_conflicted
;
3755 const apr_array_header_t
*conflicts
;
3756 svn_skel_t
*conflict_skel
;
3758 SVN_ERR(svn_wc__read_conflicts(&conflicts
, &conflict_skel
,
3759 wc_ctx
->db
, local_abspath
,
3760 FALSE
, /* no tempfiles */
3761 FALSE
, /* only tree conflicts */
3762 scratch_pool
, scratch_pool
));
3764 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
, NULL
, NULL
,
3765 &tree_conflicted
, wc_ctx
->db
,
3766 local_abspath
, conflict_skel
,
3767 scratch_pool
, scratch_pool
));
3768 if (!tree_conflicted
)
3769 return SVN_NO_ERROR
;
3771 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change
, &incoming_change
,
3772 NULL
, NULL
, wc_ctx
->db
,
3773 local_abspath
, conflict_skel
,
3774 scratch_pool
, scratch_pool
));
3776 /* Make sure the expected conflict is recorded. */
3777 if (operation
!= svn_wc_operation_update
&&
3778 operation
!= svn_wc_operation_switch
&&
3779 operation
!= svn_wc_operation_merge
)
3780 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3781 _("Unexpected conflict operation '%s' on '%s'"),
3782 svn_token__to_word(operation_map
, operation
),
3783 svn_dirent_local_style(local_abspath
,
3785 if (local_change
!= svn_wc_conflict_reason_edited
)
3786 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3787 _("Unexpected conflict reason '%s' on '%s'"),
3788 svn_token__to_word(reason_map
, local_change
),
3789 svn_dirent_local_style(local_abspath
,
3791 if (incoming_change
!= svn_wc_conflict_action_delete
)
3792 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3793 _("Unexpected conflict action '%s' on '%s'"),
3794 svn_token__to_word(action_map
, incoming_change
),
3795 svn_dirent_local_style(local_abspath
,
3798 SVN_ERR(svn_wc__db_update_incoming_move(wc_ctx
->db
, local_abspath
,
3799 dest_abspath
, operation
,
3800 incoming_change
, local_change
,
3801 cancel_func
, cancel_baton
,
3802 notify_func
, notify_baton
,
3805 SVN_ERR(svn_wc__wq_run(wc_ctx
->db
, local_abspath
, cancel_func
, cancel_baton
,
3808 return SVN_NO_ERROR
;
3812 svn_wc__conflict_tree_update_local_add(svn_wc_context_t
*wc_ctx
,
3813 const char *local_abspath
,
3814 svn_cancel_func_t cancel_func
,
3816 svn_wc_notify_func2_t notify_func
,
3818 apr_pool_t
*scratch_pool
)
3820 svn_wc_conflict_reason_t local_change
;
3821 svn_wc_conflict_action_t incoming_change
;
3822 svn_wc_operation_t operation
;
3823 svn_boolean_t tree_conflicted
;
3824 const apr_array_header_t
*conflicts
;
3825 svn_skel_t
*conflict_skel
;
3827 SVN_ERR(svn_wc__read_conflicts(&conflicts
, &conflict_skel
,
3828 wc_ctx
->db
, local_abspath
,
3829 FALSE
, /* no tempfiles */
3830 FALSE
, /* only tree conflicts */
3831 scratch_pool
, scratch_pool
));
3833 SVN_ERR(svn_wc__conflict_read_info(&operation
, NULL
, NULL
, NULL
,
3834 &tree_conflicted
, wc_ctx
->db
,
3835 local_abspath
, conflict_skel
,
3836 scratch_pool
, scratch_pool
));
3837 if (!tree_conflicted
)
3838 return SVN_NO_ERROR
;
3840 SVN_ERR(svn_wc__conflict_read_tree_conflict(&local_change
, &incoming_change
,
3841 NULL
, NULL
, wc_ctx
->db
,
3842 local_abspath
, conflict_skel
,
3843 scratch_pool
, scratch_pool
));
3845 /* Make sure the expected conflict is recorded. */
3846 if (operation
!= svn_wc_operation_update
)
3847 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3848 _("Unexpected conflict operation '%s' on '%s'"),
3849 svn_token__to_word(operation_map
, operation
),
3850 svn_dirent_local_style(local_abspath
,
3852 if (local_change
!= svn_wc_conflict_reason_added
)
3853 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3854 _("Unexpected conflict reason '%s' on '%s'"),
3855 svn_token__to_word(reason_map
, local_change
),
3856 svn_dirent_local_style(local_abspath
,
3858 if (incoming_change
!= svn_wc_conflict_action_add
)
3859 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE
, NULL
,
3860 _("Unexpected conflict action '%s' on '%s'"),
3861 svn_token__to_word(action_map
, incoming_change
),
3862 svn_dirent_local_style(local_abspath
,
3865 SVN_ERR(svn_wc__db_update_local_add(wc_ctx
->db
, local_abspath
,
3866 cancel_func
, cancel_baton
,
3867 notify_func
, notify_baton
,
3870 SVN_ERR(svn_wc__wq_run(wc_ctx
->db
, local_abspath
, cancel_func
, cancel_baton
,
3873 return SVN_NO_ERROR
;
3877 svn_wc__guess_incoming_move_target_nodes(apr_array_header_t
**possible_targets
,
3878 svn_wc_context_t
*wc_ctx
,
3879 const char *victim_abspath
,
3880 svn_node_kind_t victim_node_kind
,
3881 const char *moved_to_repos_relpath
,
3882 apr_pool_t
*result_pool
,
3883 apr_pool_t
*scratch_pool
)
3885 apr_array_header_t
*candidates
;
3886 apr_pool_t
*iterpool
;
3888 apr_size_t longest_ancestor_len
= 0;
3890 *possible_targets
= apr_array_make(result_pool
, 1, sizeof(const char *));
3891 SVN_ERR(svn_wc__db_find_repos_node_in_wc(&candidates
, wc_ctx
->db
, victim_abspath
,
3892 moved_to_repos_relpath
,
3893 scratch_pool
, scratch_pool
));
3895 /* Find a "useful move target" node in our set of candidates.
3896 * Since there is no way to be certain, filter out nodes which seem
3897 * unlikely candidates, and return the first node which is "good enough".
3898 * Nodes which are tree conflict victims don't count, and nodes which
3899 * cannot be modified (e.g. replaced or deleted nodes) don't count.
3900 * Nodes which are of a different node kind don't count either.
3901 * Ignore switched nodes as well, since that is an unlikely case during
3902 * update/swtich/merge conflict resolution. And externals shouldn't even
3903 * be on our candidate list in the first place.
3904 * If multiple candidates match these criteria, choose the one which
3905 * shares the longest common ancestor with the victim. */
3906 iterpool
= svn_pool_create(scratch_pool
);
3907 for (i
= 0; i
< candidates
->nelts
; i
++)
3909 const char *local_abspath
;
3910 const char *ancestor_abspath
;
3911 apr_size_t ancestor_len
;
3912 svn_boolean_t tree_conflicted
;
3913 svn_wc__db_status_t status
;
3914 svn_boolean_t is_wcroot
;
3915 svn_boolean_t is_switched
;
3916 svn_node_kind_t node_kind
;
3917 const char *moved_to_abspath
;
3920 svn_pool_clear(iterpool
);
3922 local_abspath
= APR_ARRAY_IDX(candidates
, i
, const char *);
3924 SVN_ERR(svn_wc__internal_conflicted_p(NULL
, NULL
, &tree_conflicted
,
3925 wc_ctx
->db
, local_abspath
,
3927 if (tree_conflicted
)
3930 SVN_ERR(svn_wc__db_read_info(&status
, &node_kind
,
3931 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
3932 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
3933 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
3934 NULL
, NULL
, NULL
, NULL
,
3935 wc_ctx
->db
, local_abspath
, iterpool
,
3937 if (status
!= svn_wc__db_status_normal
&&
3938 status
!= svn_wc__db_status_added
)
3941 if (victim_node_kind
!= svn_node_none
&& node_kind
!= victim_node_kind
)
3944 SVN_ERR(svn_wc__db_is_switched(&is_wcroot
, &is_switched
, NULL
,
3945 wc_ctx
->db
, local_abspath
, iterpool
));
3946 if (is_wcroot
|| is_switched
)
3949 /* This might be a move target. Fingers crossed ;-) */
3950 moved_to_abspath
= apr_pstrdup(result_pool
, local_abspath
);
3952 /* Insert the move target into the list. Targets which are closer
3953 * (path-wise) to the conflict victim are more likely to be a good
3954 * match, so put them at the front of the list. */
3955 ancestor_abspath
= svn_dirent_get_longest_ancestor(local_abspath
,
3958 ancestor_len
= strlen(ancestor_abspath
);
3959 if (ancestor_len
>= longest_ancestor_len
)
3961 longest_ancestor_len
= ancestor_len
;
3962 insert_index
= 0; /* prepend */
3966 insert_index
= (*possible_targets
)->nelts
; /* append */
3968 svn_sort__array_insert(*possible_targets
, &moved_to_abspath
,
3972 svn_pool_destroy(iterpool
);
3974 return SVN_NO_ERROR
;