Win32: fix an incorrect error status being propagated to the caller in case
[svn/apache.git] / subversion / libsvn_wc / conflicts.c
blobab5db3131ab997073632ea046cf091be7687618b
1 /*
2 * conflicts.c: routines for managing conflict data.
3 * NOTE: this code doesn't know where the conflict is
4 * actually stored.
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
22 * under the License.
23 * ====================================================================
28 #include <string.h>
30 #include <apr_pools.h>
31 #include <apr_tables.h>
32 #include <apr_hash.h>
33 #include <apr_errno.h>
35 #include "svn_hash.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"
41 #include "svn_wc.h"
42 #include "svn_io.h"
43 #include "svn_diff.h"
45 #include "wc.h"
46 #include "wc_db.h"
47 #include "conflicts.h"
48 #include "workqueue.h"
49 #include "props.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
62 svn_skel_t *
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);
73 return conflict_skel;
76 svn_error_t *
77 svn_wc__conflict_skel_is_complete(svn_boolean_t *complete,
78 const svn_skel_t *conflict_skel)
80 *complete = FALSE;
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 */
92 *complete = TRUE;
93 return SVN_NO_ERROR;
96 /* Serialize a svn_wc_conflict_version_t before the existing data in skel */
97 static svn_error_t *
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)
104 svn_skel_t *loc;
105 SVN_ERR_ASSERT(location || allow_NULL);
107 if (!location)
109 svn_skel__prepend(svn_skel__make_empty_list(result_pool), skel);
110 return SVN_NO_ERROR;
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),
117 loc, result_pool);
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,
122 result_pool);
124 if (!location->repos_uuid) /* Can theoretically be NULL */
125 svn_skel__prepend(svn_skel__make_empty_list(result_pool), loc);
126 else
127 svn_skel__prepend_str(location->repos_uuid, loc, result_pool);
129 svn_skel__prepend_str(apr_pstrdup(result_pool, location->repos_url), loc,
130 result_pool);
132 svn_skel__prepend_str(SVN_WC__CONFLICT_SRC_SUBVERSION, loc, result_pool);
134 svn_skel__prepend(loc, skel);
135 return SVN_NO_ERROR;
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. */
140 static svn_error_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;
150 apr_int64_t v;
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))
158 *location = NULL;
159 return SVN_NO_ERROR;
161 c = c->next;
163 repos_root_url = apr_pstrmemdup(result_pool, c->data, c->len);
164 c = c->next;
166 if (c->is_atom)
167 repos_uuid = apr_pstrmemdup(result_pool, c->data, c->len);
168 else
169 repos_uuid = NULL;
170 c = c->next;
172 repos_relpath = apr_pstrmemdup(result_pool, c->data, c->len);
173 c = c->next;
175 SVN_ERR(svn_skel__parse_int(&v, c, scratch_pool));
176 revision = (svn_revnum_t)v;
177 c = c->next;
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,
183 repos_uuid,
184 repos_relpath,
185 revision,
186 node_kind,
187 result_pool);
188 return SVN_NO_ERROR;
191 /* Get the operation part of CONFLICT_SKELL or NULL if no operation is set
192 at this time */
193 static svn_error_t *
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 */
207 return SVN_NO_ERROR;
211 svn_error_t *
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)
218 svn_skel_t *why;
219 svn_skel_t *origins;
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);
242 return SVN_NO_ERROR;
245 svn_error_t *
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)
252 svn_skel_t *why;
253 svn_skel_t *origins;
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);
276 return SVN_NO_ERROR;
279 svn_error_t *
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)
286 svn_skel_t *why;
287 svn_skel_t *origins;
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);
311 return SVN_NO_ERROR;
314 /* Gets the conflict data of the specified type CONFLICT_TYPE from
315 CONFLICT_SKEL, or NULL if no such conflict is recorded */
316 static svn_error_t *
317 conflict__get_conflict(svn_skel_t **conflict,
318 const svn_skel_t *conflict_skel,
319 const char *conflict_type)
321 svn_skel_t *c;
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;
330 c = c->next)
332 if (svn_skel__matches_atom(c->children, conflict_type))
334 *conflict = c;
335 return SVN_NO_ERROR;
339 *conflict = NULL;
341 return SVN_NO_ERROR;
344 svn_error_t *
345 svn_wc__conflict_skel_add_text_conflict(svn_skel_t *conflict_skel,
346 svn_wc__db_t *db,
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;
355 svn_skel_t *markers;
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
363 ("text"
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);
369 if (their_abspath)
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);
378 else
379 svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
381 if (mine_abspath)
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);
390 else
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);
402 else
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,
407 result_pool);
409 /* And add it to the conflict skel */
410 svn_skel__prepend(text_conflict, conflict_skel->children->next);
412 return SVN_NO_ERROR;
415 svn_error_t *
416 svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel,
417 svn_wc__db_t *db,
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;
428 svn_skel_t *props;
429 svn_skel_t *conflict_names;
430 svn_skel_t *markers;
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:
439 ("prop"
440 ("marker_relpath")
441 prop-conflicted_prop_names
442 old-props
443 mine-props
444 their-props)
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);
451 if (their_props)
453 SVN_ERR(svn_skel__unparse_proplist(&props, their_props, result_pool));
454 svn_skel__prepend(props, prop_conflict);
456 else
457 svn_skel__prepend_str("", prop_conflict, result_pool); /* No their_props */
459 if (mine_props)
461 SVN_ERR(svn_skel__unparse_proplist(&props, mine_props, result_pool));
462 svn_skel__prepend(props, prop_conflict);
464 else
465 svn_skel__prepend_str("", prop_conflict, result_pool); /* No mine_props */
467 if (their_old_props)
469 SVN_ERR(svn_skel__unparse_proplist(&props, their_old_props,
470 result_pool));
471 svn_skel__prepend(props, prop_conflict);
473 else
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)),
482 conflict_names,
483 result_pool);
485 svn_skel__prepend(conflict_names, prop_conflict);
487 markers = svn_skel__make_empty_list(result_pool);
489 if (marker_abspath)
491 const char *marker_relpath;
492 SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, wri_abspath,
493 marker_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);
508 return SVN_NO_ERROR;
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 },
523 { NULL }
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 },
532 { NULL }
535 svn_error_t *
536 svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel,
537 svn_wc__db_t *db,
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;
547 svn_skel_t *markers;
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,
566 db, wri_abspath,
567 move_dst_op_root_abspath,
568 result_pool, scratch_pool));
570 svn_skel__prepend_str(move_dst_op_root_relpath, tree_conflict,
571 result_pool);
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,
579 db, wri_abspath,
580 move_src_op_root_abspath,
581 result_pool, scratch_pool));
583 svn_skel__prepend_str(move_src_op_root_relpath, tree_conflict,
584 result_pool);
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,
599 result_pool);
601 /* And add it to the conflict skel */
602 svn_skel__prepend(tree_conflict, conflict_skel->children->next);
604 return SVN_NO_ERROR;
607 svn_error_t *
608 svn_wc__conflict_skel_resolve(svn_boolean_t *completely_resolved,
609 svn_skel_t *conflict_skel,
610 svn_wc__db_t *db,
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)
618 svn_skel_t *op;
619 svn_skel_t **pconflict;
620 SVN_ERR(conflict__get_operation(&op, conflict_skel));
622 if (!op)
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);
631 while (*pconflict)
633 svn_skel_t *c = (*pconflict)->children;
635 if (resolve_text
636 && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TEXT))
638 /* Remove the text conflict from the linked list */
639 *pconflict = (*pconflict)->next;
640 continue;
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 */
649 else
650 while (*ppropnames)
652 if (svn_skel__matches_atom(*ppropnames, resolve_prop))
654 *ppropnames = (*ppropnames)->next;
655 break;
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;
665 continue;
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;
673 continue;
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,
684 conflict_skel));
686 *completely_resolved = !complete_conflict;
688 return SVN_NO_ERROR;
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 },
699 { NULL }
702 svn_error_t *
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,
708 svn_wc__db_t *db,
709 const char *wri_abspath,
710 const svn_skel_t *conflict_skel,
711 apr_pool_t *result_pool,
712 apr_pool_t *scratch_pool)
714 svn_skel_t *op;
715 const svn_skel_t *c;
717 SVN_ERR(conflict__get_operation(&op, conflict_skel));
719 if (!op)
720 return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
721 _("Not a completed conflict skel"));
723 c = op->children;
724 if (operation)
726 int value = svn_token__from_mem(operation_map, c->data, c->len);
728 if (value != SVN_TOKEN_UNKNOWN)
729 *operation = value;
730 else
731 *operation = svn_wc_operation_none;
733 c = c->next;
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,
744 scratch_pool));
746 APR_ARRAY_PUSH(locs, svn_wc_conflict_version_t *) = loc;
749 *locations = locs;
751 else if (locations)
752 *locations = NULL;
754 if (text_conflicted)
756 svn_skel_t *c_skel;
757 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
758 SVN_WC__CONFLICT_KIND_TEXT));
760 *text_conflicted = (c_skel != NULL);
763 if (prop_conflicted)
765 svn_skel_t *c_skel;
766 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
767 SVN_WC__CONFLICT_KIND_PROP));
769 *prop_conflicted = (c_skel != NULL);
772 if (tree_conflicted)
774 svn_skel_t *c_skel;
775 SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
776 SVN_WC__CONFLICT_KIND_TREE));
778 *tree_conflicted = (c_skel != NULL);
781 return SVN_NO_ERROR;
785 svn_error_t *
786 svn_wc__conflict_read_text_conflict(const char **mine_abspath,
787 const char **their_old_abspath,
788 const char **their_abspath,
789 svn_wc__db_t *db,
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;
796 const svn_skel_t *m;
798 SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
799 SVN_WC__CONFLICT_KIND_TEXT));
801 if (!text_conflict)
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)
808 if (m->is_atom)
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));
817 else
818 *their_old_abspath = NULL;
820 m = m->next;
822 if (mine_abspath)
824 if (m->is_atom)
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));
833 else
834 *mine_abspath = NULL;
836 m = m->next;
838 if (their_abspath)
840 if (m->is_atom)
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));
849 else
850 *their_abspath = NULL;
853 return SVN_NO_ERROR;
856 svn_error_t *
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,
862 svn_wc__db_t *db,
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;
869 const svn_skel_t *c;
871 SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
872 SVN_WC__CONFLICT_KIND_PROP));
874 if (!prop_conflict)
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 */
882 if (marker_abspath)
884 const char *marker_relpath;
886 if (c->children && c->children->is_atom)
888 marker_relpath = apr_pstrmemdup(result_pool, c->children->data,
889 c->children->len);
891 SVN_ERR(svn_wc__db_from_relpath(marker_abspath, db, wri_abspath,
892 marker_relpath,
893 result_pool, scratch_pool));
895 else
896 *marker_abspath = NULL;
898 c = c->next;
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),
910 "");
913 c = c->next;
915 /* Get original properties */
916 if (their_old_props)
918 if (c->is_atom)
919 *their_old_props = apr_hash_make(result_pool);
920 else
921 SVN_ERR(svn_skel__parse_proplist(their_old_props, c, result_pool));
923 c = c->next;
925 /* Get mine properties */
926 if (mine_props)
928 if (c->is_atom)
929 *mine_props = apr_hash_make(result_pool);
930 else
931 SVN_ERR(svn_skel__parse_proplist(mine_props, c, result_pool));
933 c = c->next;
935 /* Get their properties */
936 if (their_props)
938 if (c->is_atom)
939 *their_props = apr_hash_make(result_pool);
940 else
941 SVN_ERR(svn_skel__parse_proplist(their_props, c, result_pool));
944 return SVN_NO_ERROR;
947 svn_error_t *
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,
952 svn_wc__db_t *db,
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;
959 const svn_skel_t *c;
960 svn_boolean_t is_moved_away = FALSE;
962 SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
963 SVN_WC__CONFLICT_KIND_TREE));
965 if (!tree_conflict)
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);
977 if (reason)
979 if (value != SVN_TOKEN_UNKNOWN)
980 *reason = value;
981 else
982 *reason = svn_wc_conflict_reason_edited;
985 is_moved_away = (value == svn_wc_conflict_reason_moved_away);
987 c = c->next;
989 if (action)
991 int value = svn_token__from_mem(action_map, c->data, c->len);
993 if (value != SVN_TOKEN_UNKNOWN)
994 *action = value;
995 else
996 *action = svn_wc_conflict_action_edit;
999 c = c->next;
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,
1010 db, wri_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;
1017 if (c)
1018 c = c->next;
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,
1026 db, wri_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;
1038 svn_error_t *
1039 svn_wc__conflict_read_markers(const apr_array_header_t **markers,
1040 svn_wc__db_t *db,
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;
1053 conflict;
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;
1060 marker;
1061 marker = marker->next)
1063 /* Skip placeholders */
1064 if (! marker->is_atom)
1065 continue;
1067 if (! list)
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*),
1072 db, wri_abspath,
1073 apr_pstrmemdup(scratch_pool, marker->data,
1074 marker->len),
1075 result_pool, scratch_pool));
1078 *markers = list;
1080 return SVN_NO_ERROR;
1083 /* --------------------------------------------------------------------
1087 svn_error_t *
1088 svn_wc__conflict_create_markers(svn_skel_t **work_items,
1089 svn_wc__db_t *db,
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;
1097 *work_items = NULL;
1099 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL,
1100 NULL, &prop_conflicted, NULL,
1101 db, local_abspath,
1102 conflict_skel,
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;
1125 else
1126 svn_dirent_split(&marker_dir, &marker_name, local_abspath,
1127 scratch_pool);
1129 SVN_ERR(svn_io_open_uniquely_named(NULL, &marker_abspath,
1130 marker_dir,
1131 marker_name,
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,
1146 result_pool);
1148 SVN_ERR(svn_wc__wq_build_prej_install(work_items,
1149 db, local_abspath,
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,
1178 svn_wc__db_t *db,
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,
1192 void *cancel_baton,
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(
1201 local_abspath,
1202 kind,
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. */
1210 if (working_val)
1212 const char *file_name;
1214 SVN_ERR(svn_io_write_unique(&file_name, dirpath, working_val->data,
1215 working_val->len,
1216 svn_io_file_del_on_pool_cleanup,
1217 scratch_pool));
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,
1229 scratch_pool));
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
1241 new property. */
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
1254 : incoming_old_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,
1261 scratch_pool));
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
1278 mismatch.
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
1283 compare. */
1285 if (working_val && svn_string_compare(base_val, working_val))
1286 conflict_base_val = incoming_old_val;
1287 else
1288 conflict_base_val = base_val;
1290 else
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;
1306 svn_diff_t *diff;
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,
1314 working_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;
1333 else
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;
1340 else
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,
1345 scratch_pool));
1346 if (result == NULL)
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)
1357 default:
1358 case svn_wc_conflict_choose_postpone:
1360 *conflict_remains = TRUE;
1361 break;
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;
1368 break;
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;
1379 break;
1381 case svn_wc_conflict_choose_base:
1383 *conflict_remains = FALSE;
1384 new_value = base_val;
1385 break;
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;
1398 else
1400 svn_stringbuf_t *merged_stringbuf;
1402 SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf,
1403 result->merged_file ?
1404 result->merged_file :
1405 cdesc->merged_file,
1406 scratch_pool));
1407 new_value = svn_stringbuf__morph_into_string(merged_stringbuf);
1409 *conflict_remains = FALSE;
1410 break;
1414 if (!*conflict_remains)
1416 apr_hash_t *props;
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,
1422 scratch_pool));
1424 svn_hash_sets(props, propname, new_value);
1426 SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, props,
1427 FALSE, NULL, NULL,
1428 scratch_pool));
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
1447 * allocations.
1449 static svn_error_t *
1450 merge_showing_conflicts(const char **chosen_abspath,
1451 svn_wc__db_t *db,
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,
1460 void *cancel_baton,
1461 apr_pool_t *result_pool,
1462 apr_pool_t *scratch_pool)
1464 const char *temp_dir;
1465 svn_stream_t *chosen_stream;
1466 svn_diff_t *diff;
1467 svn_diff_file_options_t *diff3_options;
1469 diff3_options = svn_diff_file_options_create(scratch_pool);
1470 if (merge_options)
1471 SVN_ERR(svn_diff_file_options_parse(diff3_options,
1472 merge_options,
1473 scratch_pool));
1475 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
1476 wri_abspath,
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,
1486 left_abspath,
1487 detranslated_target, right_abspath,
1488 diff3_options, scratch_pool));
1489 SVN_ERR(svn_diff_file_output_merge3(chosen_stream, diff,
1490 left_abspath,
1491 detranslated_target,
1492 right_abspath,
1493 NULL, NULL, NULL, NULL, /* markers */
1494 style, cancel_func, cancel_baton,
1495 scratch_pool));
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,
1514 svn_wc__db_t *db,
1515 const char *wri_abspath,
1516 const char *artifact_file_abspath,
1517 apr_pool_t *result_pool,
1518 apr_pool_t *scratch_pool)
1520 *work_items = NULL;
1521 if (artifact_file_abspath)
1523 svn_node_kind_t node_kind;
1525 SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind,
1526 scratch_pool));
1527 if (node_kind == svn_node_file)
1529 SVN_ERR(svn_wc__wq_build_file_remove(work_items,
1530 db, wri_abspath,
1531 artifact_file_abspath,
1532 result_pool, scratch_pool));
1533 if (file_found)
1534 *file_found = TRUE;
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,
1553 svn_wc__db_t *db,
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,
1569 dir_abspath,
1570 filename,
1571 ".edited",
1572 svn_io_file_del_none,
1573 scratch_pool, scratch_pool));
1574 SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item,
1575 db, local_abspath,
1576 source_abspath,
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,
1602 svn_wc__db_t *db,
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,
1610 void *cancel_baton,
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;
1621 *work_items = NULL;
1623 if (found_artifact)
1624 *found_artifact = FALSE;
1626 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
1627 &their_old_abspath,
1628 &their_abspath,
1629 db, local_abspath,
1630 conflict,
1631 scratch_pool, scratch_pool));
1633 if (save_merged)
1634 SVN_ERR(save_merge_result(work_items,
1635 db, local_abspath,
1636 merged_file
1637 ? merged_file
1638 : local_abspath,
1639 result_pool, scratch_pool));
1641 if (choice == svn_wc_conflict_choose_postpone)
1642 return SVN_NO_ERROR;
1644 switch (choice)
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;
1651 break;
1653 case svn_wc_conflict_choose_theirs_full:
1655 install_from_abspath = their_abspath;
1656 break;
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
1666 ? mine_abspath
1667 : local_abspath;
1668 break;
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,
1685 scratch_pool));
1687 SVN_ERR(merge_showing_conflicts(&install_from_abspath,
1688 db, local_abspath,
1689 style, merge_options,
1690 their_old_abspath,
1691 mine_abspath,
1692 their_abspath,
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;
1698 break;
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
1706 good to use". */
1707 case svn_wc_conflict_choose_merged:
1709 install_from_abspath = merged_file
1710 ? merged_file
1711 : local_abspath;
1712 break;
1714 case svn_wc_conflict_choose_postpone:
1716 /* Assume conflict remains. */
1717 return SVN_NO_ERROR;
1719 default:
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,
1729 scratch_pool));
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,
1734 db, local_abspath,
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);
1741 if (remove_source)
1743 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
1744 db, local_abspath,
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,
1751 db, local_abspath,
1752 their_old_abspath,
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,
1757 db, local_abspath,
1758 their_abspath,
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,
1763 db, local_abspath,
1764 mine_abspath,
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
1774 * an error.
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
1781 * allocations. */
1782 static svn_error_t *
1783 read_text_conflict_desc(svn_wc_conflict_description2_t **desc,
1784 svn_wc__db_t *db,
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,
1804 db, local_abspath,
1805 conflict_skel,
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
1814 * an error.
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
1820 * allocations. */
1821 static svn_error_t *
1822 read_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
1823 svn_wc__db_t *db,
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
1847 && left_version
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;
1856 else
1857 local_kind = node_kind;
1859 *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind,
1860 operation,
1861 left_version, right_version,
1862 result_pool);
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,
1872 svn_wc__db_t *db,
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,
1878 void *notify_baton,
1879 svn_cancel_func_t cancel_func,
1880 void *cancel_baton,
1881 apr_pool_t *scratch_pool);
1883 svn_error_t *
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,
1892 void *cancel_baton,
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,
1905 &tree_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,
1934 &mine_props,
1935 &old_their_props,
1936 &their_props,
1937 &conflicted,
1938 db, local_abspath,
1939 conflict_skel,
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));
1945 else
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);
1959 if (cancel_func)
1960 SVN_ERR(cancel_func(cancel_baton));
1962 SVN_ERR(generate_propconflict(&conflict_remains,
1963 db, local_abspath, kind,
1964 operation,
1965 left_version,
1966 right_version,
1967 propname,
1968 old_props
1969 ? svn_hash_gets(old_props, propname)
1970 : NULL,
1971 mine_props
1972 ? svn_hash_gets(mine_props, propname)
1973 : NULL,
1974 old_their_props
1975 ? svn_hash_gets(old_their_props, propname)
1976 : NULL,
1977 their_props
1978 ? svn_hash_gets(their_props, propname)
1979 : NULL,
1980 resolver_func, resolver_baton,
1981 cancel_func, cancel_baton,
1982 iterpool));
1984 if (conflict_remains)
1985 mark_resolved = FALSE;
1988 if (mark_resolved)
1990 SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
1991 scratch_pool));
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;
2001 apr_hash_t *props;
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));
2015 work_items = NULL;
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,
2022 scratch_pool));
2023 if (result == NULL)
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,
2029 db, local_abspath,
2030 conflict_skel, result->choice,
2031 result->merged_file,
2032 result->save_merged,
2033 merge_options,
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,
2040 TRUE, FALSE, FALSE,
2041 work_items, scratch_pool));
2042 SVN_ERR(svn_wc__wq_run(db, local_abspath,
2043 cancel_func, cancel_baton,
2044 scratch_pool));
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,
2060 conflict_skel,
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,
2066 scratch_pool));
2068 if (result == NULL)
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,
2078 result->choice,
2079 apr_hash_make(scratch_pool),
2080 NULL, NULL, /* ### notify */
2081 cancel_func, cancel_baton,
2082 scratch_pool));
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
2094 * descriptor.
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
2100 * allocations. */
2101 static svn_error_t *
2102 read_prop_conflict_descs(apr_array_header_t *conflicts,
2103 svn_wc__db_t *db,
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,
2132 &my_props,
2133 &their_old_props,
2134 &their_props,
2135 &conflicted_props,
2136 db, local_abspath,
2137 conflict_skel,
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,
2148 node_kind,
2149 "", result_pool);
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));
2168 else
2169 base_props = NULL;
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,
2184 node_kind,
2185 propname,
2186 result_pool);
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;
2203 else
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;
2211 else
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;
2222 if (my_value)
2224 svn_stream_t *s;
2225 apr_size_t len;
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);
2240 if (their_value)
2242 svn_stream_t *s;
2243 apr_size_t len;
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);
2260 if (old_value)
2262 svn_stream_t *s;
2263 apr_size_t len;
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;
2285 svn_error_t *
2286 svn_wc__read_conflicts(const apr_array_header_t **conflicts,
2287 svn_skel_t **conflict_skel,
2288 svn_wc__db_t *db,
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;
2305 apr_hash_t *props;
2307 if (!conflict_skel)
2308 conflict_skel = &the_conflict_skel;
2310 SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props,
2311 db, local_abspath,
2312 (conflict_skel == &the_conflict_skel)
2313 ? scratch_pool
2314 : result_pool,
2315 scratch_pool));
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,
2366 *conflict_skel,
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;
2377 svn_error_t *
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
2404 * Subversion 1.0.
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,
2428 svn_wc__db_t *db,
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,
2436 void *cancel_baton,
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,
2476 scratch_pool));
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));
2489 else
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;
2510 break;
2511 case svn_wc_conflict_choose_mine_full:
2512 case svn_wc_conflict_choose_mine_conflict:
2513 resolve_from = mine_props;
2514 break;
2515 case svn_wc_conflict_choose_theirs_full:
2516 case svn_wc_conflict_choose_theirs_conflict:
2517 resolve_from = their_props;
2518 break;
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);
2524 if (!merged_value)
2526 svn_stringbuf_t *merged_propval;
2528 SVN_ERR(svn_stringbuf_from_file2(&merged_propval, merged_file,
2529 scratch_pool));
2531 merged_value = svn_stringbuf__morph_into_string(merged_propval);
2533 svn_hash_sets(resolve_from, conflicted_propname, merged_value);
2535 else
2536 resolve_from = NULL;
2537 break;
2538 default:
2539 return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2540 _("Invalid 'conflict_result' argument"));
2544 if (resolve_from)
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;
2554 else
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,
2579 db, local_abspath,
2580 FALSE, conflicted_propname,
2581 FALSE,
2582 scratch_pool, scratch_pool));
2584 if (!resolved_all)
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);
2593 else
2595 resolved_all_prop = TRUE;
2596 conflicts = NULL;
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,
2607 db, local_abspath,
2608 prop_reject_file,
2609 scratch_pool, scratch_pool));
2611 else
2613 /* Create a new prej file, based on the remaining conflicts */
2614 SVN_ERR(svn_wc__wq_build_prej_install(&work_items,
2615 db, local_abspath,
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,
2623 scratch_pool));
2625 if (resolved_all)
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,
2630 FALSE, TRUE, FALSE,
2631 NULL, scratch_pool));
2634 SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2635 scratch_pool));
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,
2650 svn_error_t *err,
2651 apr_hash_t *resolve_later)
2653 const char *dup_abspath;
2655 if (!resolve_later
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),
2662 local_abspath);
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
2671 * CONFLICT_CHOICE.
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,
2686 svn_wc__db_t *db,
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,
2692 void *notify_baton,
2693 svn_cancel_func_t cancel_func,
2694 void *cancel_baton,
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,
2714 conflicts,
2715 scratch_pool, scratch_pool));
2717 if (operation == svn_wc_operation_update
2718 || operation == svn_wc_operation_switch)
2720 svn_error_t *err;
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,
2734 scratch_pool));
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,
2755 scratch_pool);
2757 if (err)
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,
2764 db, local_abspath,
2765 scratch_pool, scratch_pool));
2767 if (new_conflicts)
2768 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
2769 &tree_conflicted,
2770 db, local_abspath,
2771 new_conflicts,
2772 scratch_pool,
2773 scratch_pool));
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,
2785 NULL,
2786 db, local_abspath,
2787 new_conflicts,
2788 scratch_pool,
2789 scratch_pool));
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 */
2800 else
2801 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2802 NULL,
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,
2807 scratch_pool));
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,
2824 scratch_pool);
2826 if (err)
2827 SVN_ERR(handle_tree_conflict_resolution_failure(
2828 local_abspath, err, resolve_later));
2829 else
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,
2841 scratch_pool));
2842 *did_resolve = TRUE;
2843 return SVN_NO_ERROR; /* Conflict is marked resolved */
2845 else
2846 return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2847 NULL,
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,
2852 scratch_pool));
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,
2874 NULL,
2875 _("Tree conflict can only be "
2876 "resolved to 'working' state; "
2877 "'%s' is no longer moved"),
2878 svn_dirent_local_style(local_abspath,
2879 scratch_pool));
2884 if (! *did_resolve)
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,
2892 NULL,
2893 _("Tree conflict can only be "
2894 "resolved to 'working' state; "
2895 "'%s' not resolved"),
2896 svn_dirent_local_style(local_abspath,
2897 scratch_pool));
2899 else
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,
2908 scratch_pool));
2909 return SVN_NO_ERROR;
2912 svn_error_t *
2913 svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
2914 const char *local_abspath,
2915 svn_cancel_func_t cancel_func,
2916 void *cancel_baton,
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,
2923 db, local_abspath,
2924 scratch_pool, scratch_pool));
2926 if (!conflict)
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,
2932 NULL, FALSE, NULL,
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,
2941 scratch_pool));
2944 svn_error_t *
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,
2953 db, local_abspath,
2954 scratch_pool, scratch_pool));
2956 if (!conflicts)
2957 return SVN_NO_ERROR;
2959 return svn_error_trace(resolve_prop_conflict_on_node(
2960 &ignored_result,
2961 db, local_abspath, conflicts, "",
2962 svn_wc_conflict_choose_merged,
2963 NULL, NULL,
2964 NULL, NULL,
2965 scratch_pool));
2969 /* Baton for conflict_status_walker */
2970 struct conflict_status_walker_baton
2972 svn_wc__db_t *db;
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;
2980 void *cancel_baton;
2981 svn_wc_notify_func2_t notify_func;
2982 void *notify_baton;
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. */
2989 static void
2990 tree_conflict_collector(void *baton,
2991 const svn_wc_notify_t *notify,
2992 apr_pool_t *pool)
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),
3009 notify->path);
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;
3029 int i;
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,
3040 db, local_abspath,
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)
3081 continue;
3083 switch (cd->kind)
3085 case svn_wc_conflict_kind_tree:
3086 SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
3088 local_abspath, conflict,
3089 my_choice,
3090 cswb->resolve_later,
3091 tree_conflict_collector,
3092 cswb,
3093 cswb->cancel_func,
3094 cswb->cancel_baton,
3095 iterpool));
3097 if (did_resolve)
3099 resolved = TRUE;
3100 notify_action = svn_wc_notify_resolved_tree;
3102 break;
3104 case svn_wc_conflict_kind_text:
3105 SVN_ERR(build_text_conflict_resolve_items(
3106 &work_items,
3107 &resolved,
3108 db, local_abspath, conflict,
3109 my_choice,
3110 result ? result->merged_file
3111 : NULL,
3112 result ? result->save_merged
3113 : FALSE,
3114 NULL /* merge_options */,
3115 cswb->cancel_func,
3116 cswb->cancel_baton,
3117 iterpool, iterpool));
3119 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
3120 TRUE, FALSE, FALSE,
3121 work_items, iterpool));
3122 SVN_ERR(svn_wc__wq_run(db, local_abspath,
3123 cswb->cancel_func, cswb->cancel_baton,
3124 iterpool));
3125 if (resolved)
3126 notify_action = svn_wc_notify_resolved_text;
3127 break;
3129 case svn_wc_conflict_kind_property:
3130 SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
3132 local_abspath,
3133 conflict,
3134 cd->property_name,
3135 my_choice,
3136 result
3137 ? result->merged_file
3138 : NULL,
3139 result
3140 ? result->merged_value
3141 : NULL,
3142 cswb->cancel_func,
3143 cswb->cancel_baton,
3144 iterpool));
3146 if (did_resolve)
3148 resolved = TRUE;
3149 notify_action = svn_wc_notify_resolved_prop;
3151 break;
3153 default:
3154 /* We can't resolve other conflict types */
3155 break;
3159 /* Notify */
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);
3188 if (resolved)
3189 cswb->resolved_one = TRUE;
3191 svn_pool_destroy(iterpool);
3193 return SVN_NO_ERROR;
3196 svn_error_t *
3197 svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
3198 const char *local_abspath,
3199 svn_depth_t depth,
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,
3207 void *cancel_baton,
3208 svn_wc_notify_func2_t notify_func,
3209 void *notify_baton,
3210 apr_pool_t *scratch_pool)
3212 struct conflict_status_walker_baton cswb;
3213 apr_pool_t *iterpool = NULL;
3214 svn_error_t *err;
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)
3237 : NULL;
3239 if (notify_func)
3240 notify_func(notify_baton,
3241 svn_wc_create_notify(local_abspath,
3242 svn_wc_notify_conflict_resolver_starting,
3243 scratch_pool),
3244 scratch_pool);
3246 err = svn_wc_walk_status(wc_ctx,
3247 local_abspath,
3248 depth,
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,
3255 scratch_pool);
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;
3265 if (iterpool)
3266 svn_pool_clear(iterpool);
3267 else
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);
3281 if (cancel_func)
3282 SVN_ERR(cancel_func(cancel_baton));
3284 relpath = svn_dirent_skip_ancestor(local_abspath,
3285 tc_abspath);
3287 if (!relpath
3288 || (depth >= svn_depth_empty
3289 && depth < svn_depth_infinity
3290 && strchr(relpath, '/')))
3292 continue;
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)
3300 continue;
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
3307 an error...
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));
3329 break;
3333 if (iterpool)
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));
3342 SVN_ERR(err);
3344 if (notify_func)
3345 notify_func(notify_baton,
3346 svn_wc_create_notify(local_abspath,
3347 svn_wc_notify_conflict_resolver_done,
3348 scratch_pool),
3349 scratch_pool);
3351 return SVN_NO_ERROR;
3354 svn_error_t *
3355 svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
3356 const char *local_abspath,
3357 svn_depth_t depth,
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,
3363 void *cancel_baton,
3364 svn_wc_notify_func2_t notify_func,
3365 void *notify_baton,
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,
3371 conflict_choice,
3372 NULL, NULL,
3373 cancel_func, cancel_baton,
3374 notify_func, notify_baton,
3375 scratch_pool));
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,
3382 apr_pool_t *pool)
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. */
3392 return result;
3395 svn_error_t *
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,
3400 void *cancel_baton,
3401 svn_wc_notify_func2_t notify_func,
3402 void *notify_baton,
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));
3413 if (!conflict)
3414 return SVN_NO_ERROR;
3416 SVN_ERR(build_text_conflict_resolve_items(&work_items, &did_resolve,
3417 wc_ctx->db, local_abspath,
3418 conflict, choice,
3419 NULL, FALSE, NULL,
3420 cancel_func, cancel_baton,
3421 scratch_pool, scratch_pool));
3423 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath,
3424 TRUE, FALSE, FALSE,
3425 work_items, scratch_pool));
3427 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath,
3428 cancel_func, cancel_baton,
3429 scratch_pool));
3431 if (did_resolve && notify_func)
3432 notify_func(notify_baton,
3433 svn_wc_create_notify(local_abspath,
3434 svn_wc_notify_resolved_text,
3435 scratch_pool),
3436 scratch_pool);
3438 return SVN_NO_ERROR;
3441 svn_error_t *
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,
3448 void *notify_baton,
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));
3458 if (!conflicts)
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,
3475 scratch_pool);
3477 else
3479 notify = svn_wc_create_notify(local_abspath,
3480 svn_wc_notify_resolved_prop,
3481 scratch_pool);
3482 notify->prop_name = propname;
3485 notify_func(notify_baton, notify, scratch_pool);
3487 return SVN_NO_ERROR;
3490 svn_error_t *
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,
3494 void *cancel_baton,
3495 svn_wc_notify_func2_t notify_func,
3496 void *notify_baton,
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,
3523 conflict_skel,
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,
3533 scratch_pool));
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,
3541 scratch_pool));
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 .*/
3552 if (notify_func)
3553 notify_func(notify_baton,
3554 svn_wc_create_notify(local_abspath,
3555 svn_wc_notify_resolved_tree,
3556 scratch_pool),
3557 scratch_pool);
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,
3563 FALSE, FALSE, TRUE,
3564 NULL, scratch_pool));
3565 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
3566 scratch_pool));
3568 if (notify_func)
3569 notify_func(notify_baton,
3570 svn_wc_create_notify(local_abspath,
3571 svn_wc_notify_resolved_tree,
3572 scratch_pool),
3573 scratch_pool);
3575 return SVN_NO_ERROR;
3578 svn_error_t *
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,
3582 void *cancel_baton,
3583 svn_wc_notify_func2_t notify_func,
3584 void *notify_baton,
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,
3609 conflict_skel,
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,
3619 scratch_pool));
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,
3626 scratch_pool));
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,
3632 scratch_pool));
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,
3642 scratch_pool));
3644 /* The conflict was marked resolved by svn_wc__db_op_raise_moved_away(). */
3645 if (notify_func)
3646 notify_func(notify_baton,
3647 svn_wc_create_notify(local_abspath,
3648 svn_wc_notify_resolved_tree,
3649 scratch_pool),
3650 scratch_pool);
3652 return SVN_NO_ERROR;
3655 svn_error_t *
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,
3659 void *cancel_baton,
3660 svn_wc_notify_func2_t notify_func,
3661 void *notify_baton,
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,
3688 conflict_skel,
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,
3698 scratch_pool));
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,
3704 scratch_pool));
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,
3710 scratch_pool));
3712 /* Update the moved-away conflict victim. */
3713 SVN_ERR(svn_wc__db_update_moved_away_conflict_victim(wc_ctx->db,
3714 local_abspath,
3715 src_op_root_abspath,
3716 operation,
3717 action,
3718 reason,
3719 cancel_func,
3720 cancel_baton,
3721 notify_func,
3722 notify_baton,
3723 scratch_pool));
3725 SVN_ERR(svn_wc__db_op_mark_resolved(wc_ctx->db, local_abspath,
3726 FALSE, FALSE, TRUE,
3727 NULL, scratch_pool));
3728 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
3729 scratch_pool));
3731 if (notify_func)
3732 notify_func(notify_baton,
3733 svn_wc_create_notify(local_abspath,
3734 svn_wc_notify_resolved_tree,
3735 scratch_pool),
3736 scratch_pool);
3738 return SVN_NO_ERROR;
3741 svn_error_t *
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,
3746 void *cancel_baton,
3747 svn_wc_notify_func2_t notify_func,
3748 void *notify_baton,
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,
3784 scratch_pool));
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,
3790 scratch_pool));
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,
3796 scratch_pool));
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,
3803 scratch_pool));
3805 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
3806 scratch_pool));
3808 return SVN_NO_ERROR;
3811 svn_error_t *
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,
3815 void *cancel_baton,
3816 svn_wc_notify_func2_t notify_func,
3817 void *notify_baton,
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,
3851 scratch_pool));
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,
3857 scratch_pool));
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,
3863 scratch_pool));
3865 SVN_ERR(svn_wc__db_update_local_add(wc_ctx->db, local_abspath,
3866 cancel_func, cancel_baton,
3867 notify_func, notify_baton,
3868 scratch_pool));
3870 SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath, cancel_func, cancel_baton,
3871 scratch_pool));
3873 return SVN_NO_ERROR;
3876 svn_error_t *
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;
3887 int i;
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;
3918 int insert_index;
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,
3926 iterpool));
3927 if (tree_conflicted)
3928 continue;
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,
3936 iterpool));
3937 if (status != svn_wc__db_status_normal &&
3938 status != svn_wc__db_status_added)
3939 continue;
3941 if (victim_node_kind != svn_node_none && node_kind != victim_node_kind)
3942 continue;
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)
3947 continue;
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,
3956 victim_abspath,
3957 iterpool);
3958 ancestor_len = strlen(ancestor_abspath);
3959 if (ancestor_len >= longest_ancestor_len)
3961 longest_ancestor_len = ancestor_len;
3962 insert_index = 0; /* prepend */
3964 else
3966 insert_index = (*possible_targets)->nelts; /* append */
3968 svn_sort__array_insert(*possible_targets, &moved_to_abspath,
3969 insert_index);
3972 svn_pool_destroy(iterpool);
3974 return SVN_NO_ERROR;