]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_wc/conflicts.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_wc / conflicts.c
1 /*
2  * conflicts.c: routines for managing conflict data.
3  *            NOTE: this code doesn't know where the conflict is
4  *            actually stored.
5  *
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
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
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  * ====================================================================
24  */
25
26
27 \f
28 #include <string.h>
29
30 #include <apr_pools.h>
31 #include <apr_tables.h>
32 #include <apr_hash.h>
33 #include <apr_errno.h>
34
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"
44
45 #include "wc.h"
46 #include "wc_db.h"
47 #include "conflicts.h"
48 #include "workqueue.h"
49 #include "props.h"
50
51 #include "private/svn_wc_private.h"
52 #include "private/svn_skel.h"
53 #include "private/svn_string_private.h"
54
55 #include "svn_private_config.h"
56
57 /* --------------------------------------------------------------------
58  * Conflict skel management
59  */
60
61 svn_skel_t *
62 svn_wc__conflict_skel_create(apr_pool_t *result_pool)
63 {
64   svn_skel_t *conflict_skel = svn_skel__make_empty_list(result_pool);
65
66   /* Add empty CONFLICTS list */
67   svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel);
68
69   /* Add empty WHY list */
70   svn_skel__prepend(svn_skel__make_empty_list(result_pool), conflict_skel);
71
72   return conflict_skel;
73 }
74
75 svn_error_t *
76 svn_wc__conflict_skel_is_complete(svn_boolean_t *complete,
77                                   const svn_skel_t *conflict_skel)
78 {
79   *complete = FALSE;
80
81   if (svn_skel__list_length(conflict_skel) < 2)
82     return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
83                             _("Not a conflict skel"));
84
85   if (svn_skel__list_length(conflict_skel->children) < 2)
86     return SVN_NO_ERROR; /* WHY is not set */
87
88   if (svn_skel__list_length(conflict_skel->children->next) == 0)
89     return SVN_NO_ERROR; /* No conflict set */
90
91   *complete = TRUE;
92   return SVN_NO_ERROR;
93 }
94
95 /* Serialize a svn_wc_conflict_version_t before the existing data in skel */
96 static svn_error_t *
97 conflict__prepend_location(svn_skel_t *skel,
98                            const svn_wc_conflict_version_t *location,
99                            svn_boolean_t allow_NULL,
100                            apr_pool_t *result_pool,
101                            apr_pool_t *scratch_pool)
102 {
103   svn_skel_t *loc;
104   SVN_ERR_ASSERT(location || allow_NULL);
105
106   if (!location)
107     {
108       svn_skel__prepend(svn_skel__make_empty_list(result_pool), skel);
109       return SVN_NO_ERROR;
110     }
111
112   /* ("subversion" repos_root_url repos_uuid repos_relpath rev kind) */
113   loc = svn_skel__make_empty_list(result_pool);
114
115   svn_skel__prepend_str(svn_node_kind_to_word(location->node_kind),
116                         loc, result_pool);
117
118   svn_skel__prepend_int(location->peg_rev, loc, result_pool);
119
120   svn_skel__prepend_str(apr_pstrdup(result_pool, location->path_in_repos), loc,
121                         result_pool);
122
123   if (!location->repos_uuid) /* Can theoretically be NULL */
124     svn_skel__prepend(svn_skel__make_empty_list(result_pool), loc);
125   else
126     svn_skel__prepend_str(location->repos_uuid, loc, result_pool);
127
128   svn_skel__prepend_str(apr_pstrdup(result_pool, location->repos_url), loc,
129                         result_pool);
130
131   svn_skel__prepend_str(SVN_WC__CONFLICT_SRC_SUBVERSION, loc, result_pool);
132
133   svn_skel__prepend(loc, skel);
134   return SVN_NO_ERROR;
135 }
136
137 /* Deserialize a svn_wc_conflict_version_t from the skel.
138    Set *LOCATION to NULL when the data is not a svn_wc_conflict_version_t. */
139 static svn_error_t *
140 conflict__read_location(svn_wc_conflict_version_t **location,
141                         const svn_skel_t *skel,
142                         apr_pool_t *result_pool,
143                         apr_pool_t *scratch_pool)
144 {
145   const char *repos_root_url;
146   const char *repos_uuid;
147   const char *repos_relpath;
148   svn_revnum_t revision;
149   apr_int64_t v;
150   svn_node_kind_t node_kind;  /* note that 'none' is a legitimate value */
151   const char *kind_str;
152
153   const svn_skel_t *c = skel->children;
154
155   if (!svn_skel__matches_atom(c, SVN_WC__CONFLICT_SRC_SUBVERSION))
156     {
157       *location = NULL;
158       return SVN_NO_ERROR;
159     }
160   c = c->next;
161
162   repos_root_url = apr_pstrmemdup(result_pool, c->data, c->len);
163   c = c->next;
164
165   if (c->is_atom)
166     repos_uuid = apr_pstrmemdup(result_pool, c->data, c->len);
167   else
168     repos_uuid = NULL;
169   c = c->next;
170
171   repos_relpath = apr_pstrmemdup(result_pool, c->data, c->len);
172   c = c->next;
173
174   SVN_ERR(svn_skel__parse_int(&v, c, scratch_pool));
175   revision = (svn_revnum_t)v;
176   c = c->next;
177
178   kind_str = apr_pstrmemdup(scratch_pool, c->data, c->len);
179   node_kind = svn_node_kind_from_word(kind_str);
180
181   *location = svn_wc_conflict_version_create2(repos_root_url,
182                                               repos_uuid,
183                                               repos_relpath,
184                                               revision,
185                                               node_kind,
186                                               result_pool);
187   return SVN_NO_ERROR;
188 }
189
190 /* Get the operation part of CONFLICT_SKELL or NULL if no operation is set
191    at this time */
192 static svn_error_t *
193 conflict__get_operation(svn_skel_t **why,
194                         const svn_skel_t *conflict_skel)
195 {
196   SVN_ERR_ASSERT(conflict_skel
197                  && conflict_skel->children
198                  && conflict_skel->children->next
199                  && !conflict_skel->children->next->is_atom);
200
201   *why = conflict_skel->children;
202
203   if (!(*why)->children)
204     *why = NULL; /* Operation is not set yet */
205
206   return SVN_NO_ERROR;
207 }
208
209
210 svn_error_t *
211 svn_wc__conflict_skel_set_op_update(svn_skel_t *conflict_skel,
212                                     const svn_wc_conflict_version_t *original,
213                                     const svn_wc_conflict_version_t *target,
214                                     apr_pool_t *result_pool,
215                                     apr_pool_t *scratch_pool)
216 {
217   svn_skel_t *why;
218   svn_skel_t *origins;
219
220   SVN_ERR_ASSERT(conflict_skel
221                  && conflict_skel->children
222                  && conflict_skel->children->next
223                  && !conflict_skel->children->next->is_atom);
224
225   SVN_ERR(conflict__get_operation(&why, conflict_skel));
226
227   SVN_ERR_ASSERT(why == NULL); /* No operation set */
228
229   why = conflict_skel->children;
230
231   origins = svn_skel__make_empty_list(result_pool);
232
233   SVN_ERR(conflict__prepend_location(origins, target, TRUE,
234                                      result_pool, scratch_pool));
235   SVN_ERR(conflict__prepend_location(origins, original, TRUE,
236                                      result_pool, scratch_pool));
237
238   svn_skel__prepend(origins, why);
239   svn_skel__prepend_str(SVN_WC__CONFLICT_OP_UPDATE, why, result_pool);
240
241   return SVN_NO_ERROR;
242 }
243
244 svn_error_t *
245 svn_wc__conflict_skel_set_op_switch(svn_skel_t *conflict_skel,
246                                     const svn_wc_conflict_version_t *original,
247                                     const svn_wc_conflict_version_t *target,
248                                     apr_pool_t *result_pool,
249                                     apr_pool_t *scratch_pool)
250 {
251   svn_skel_t *why;
252   svn_skel_t *origins;
253
254   SVN_ERR_ASSERT(conflict_skel
255                  && conflict_skel->children
256                  && conflict_skel->children->next
257                  && !conflict_skel->children->next->is_atom);
258
259   SVN_ERR(conflict__get_operation(&why, conflict_skel));
260
261   SVN_ERR_ASSERT(why == NULL); /* No operation set */
262
263   why = conflict_skel->children;
264
265   origins = svn_skel__make_empty_list(result_pool);
266
267   SVN_ERR(conflict__prepend_location(origins, target, TRUE,
268                                      result_pool, scratch_pool));
269   SVN_ERR(conflict__prepend_location(origins, original, TRUE,
270                                      result_pool, scratch_pool));
271
272   svn_skel__prepend(origins, why);
273   svn_skel__prepend_str(SVN_WC__CONFLICT_OP_SWITCH, why, result_pool);
274
275   return SVN_NO_ERROR;
276 }
277
278 svn_error_t *
279 svn_wc__conflict_skel_set_op_merge(svn_skel_t *conflict_skel,
280                                    const svn_wc_conflict_version_t *left,
281                                    const svn_wc_conflict_version_t *right,
282                                    apr_pool_t *result_pool,
283                                    apr_pool_t *scratch_pool)
284 {
285   svn_skel_t *why;
286   svn_skel_t *origins;
287
288   SVN_ERR_ASSERT(conflict_skel
289                  && conflict_skel->children
290                  && conflict_skel->children->next
291                  && !conflict_skel->children->next->is_atom);
292
293   SVN_ERR(conflict__get_operation(&why, conflict_skel));
294
295   SVN_ERR_ASSERT(why == NULL); /* No operation set */
296
297   why = conflict_skel->children;
298
299   origins = svn_skel__make_empty_list(result_pool);
300
301   SVN_ERR(conflict__prepend_location(origins, right, TRUE,
302                                      result_pool, scratch_pool));
303
304   SVN_ERR(conflict__prepend_location(origins, left, TRUE,
305                                      result_pool, scratch_pool));
306
307   svn_skel__prepend(origins, why);
308   svn_skel__prepend_str(SVN_WC__CONFLICT_OP_MERGE, why, result_pool);
309
310   return SVN_NO_ERROR;
311 }
312
313 /* Gets the conflict data of the specified type CONFLICT_TYPE from
314    CONFLICT_SKEL, or NULL if no such conflict is recorded */
315 static svn_error_t *
316 conflict__get_conflict(svn_skel_t **conflict,
317                        const svn_skel_t *conflict_skel,
318                        const char *conflict_type)
319 {
320   svn_skel_t *c;
321
322   SVN_ERR_ASSERT(conflict_skel
323                  && conflict_skel->children
324                  && conflict_skel->children->next
325                  && !conflict_skel->children->next->is_atom);
326
327   for(c = conflict_skel->children->next->children;
328       c;
329       c = c->next)
330     {
331       if (svn_skel__matches_atom(c->children, conflict_type))
332         {
333           *conflict = c;
334           return SVN_NO_ERROR;
335         }
336     }
337
338   *conflict = NULL;
339
340   return SVN_NO_ERROR;
341 }
342
343 svn_error_t *
344 svn_wc__conflict_skel_add_text_conflict(svn_skel_t *conflict_skel,
345                                         svn_wc__db_t *db,
346                                         const char *wri_abspath,
347                                         const char *mine_abspath,
348                                         const char *their_old_abspath,
349                                         const char *their_abspath,
350                                         apr_pool_t *result_pool,
351                                         apr_pool_t *scratch_pool)
352 {
353   svn_skel_t *text_conflict;
354   svn_skel_t *markers;
355
356   SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
357                                  SVN_WC__CONFLICT_KIND_TEXT));
358
359   SVN_ERR_ASSERT(!text_conflict); /* ### Use proper error? */
360
361   /* Current skel format
362      ("text"
363       (OLD MINE OLD-THEIRS THEIRS)) */
364
365   text_conflict = svn_skel__make_empty_list(result_pool);
366   markers = svn_skel__make_empty_list(result_pool);
367
368 if (their_abspath)
369     {
370       const char *their_relpath;
371
372       SVN_ERR(svn_wc__db_to_relpath(&their_relpath,
373                                     db, wri_abspath, their_abspath,
374                                     result_pool, scratch_pool));
375       svn_skel__prepend_str(their_relpath, markers, result_pool);
376     }
377   else
378     svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
379
380   if (mine_abspath)
381     {
382       const char *mine_relpath;
383
384       SVN_ERR(svn_wc__db_to_relpath(&mine_relpath,
385                                     db, wri_abspath, mine_abspath,
386                                     result_pool, scratch_pool));
387       svn_skel__prepend_str(mine_relpath, markers, result_pool);
388     }
389   else
390     svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
391
392   if (their_old_abspath)
393     {
394       const char *original_relpath;
395
396       SVN_ERR(svn_wc__db_to_relpath(&original_relpath,
397                                     db, wri_abspath, their_old_abspath,
398                                     result_pool, scratch_pool));
399       svn_skel__prepend_str(original_relpath, markers, result_pool);
400     }
401   else
402     svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);
403
404   svn_skel__prepend(markers, text_conflict);
405   svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TEXT, text_conflict,
406                         result_pool);
407
408   /* And add it to the conflict skel */
409   svn_skel__prepend(text_conflict, conflict_skel->children->next);
410
411   return SVN_NO_ERROR;
412 }
413
414 svn_error_t *
415 svn_wc__conflict_skel_add_prop_conflict(svn_skel_t *conflict_skel,
416                                         svn_wc__db_t *db,
417                                         const char *wri_abspath,
418                                         const char *marker_abspath,
419                                         const apr_hash_t *mine_props,
420                                         const apr_hash_t *their_old_props,
421                                         const apr_hash_t *their_props,
422                                         const apr_hash_t *conflicted_prop_names,
423                                         apr_pool_t *result_pool,
424                                         apr_pool_t *scratch_pool)
425 {
426   svn_skel_t *prop_conflict;
427   svn_skel_t *props;
428   svn_skel_t *conflict_names;
429   svn_skel_t *markers;
430   apr_hash_index_t *hi;
431
432   SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
433                                  SVN_WC__CONFLICT_KIND_PROP));
434
435   SVN_ERR_ASSERT(!prop_conflict); /* ### Use proper error? */
436
437   /* This function currently implements:
438      ("prop"
439       ("marker_relpath")
440       prop-conflicted_prop_names
441       old-props
442       mine-props
443       their-props)
444      NULL lists are recorded as "" */
445   /* ### Seems that this may not match what we read out.  Read-out of
446    * 'theirs-old' comes as NULL. */
447
448   prop_conflict = svn_skel__make_empty_list(result_pool);
449
450   if (their_props)
451     {
452       SVN_ERR(svn_skel__unparse_proplist(&props, their_props, result_pool));
453       svn_skel__prepend(props, prop_conflict);
454     }
455   else
456     svn_skel__prepend_str("", prop_conflict, result_pool); /* No their_props */
457
458   if (mine_props)
459     {
460       SVN_ERR(svn_skel__unparse_proplist(&props, mine_props, result_pool));
461       svn_skel__prepend(props, prop_conflict);
462     }
463   else
464     svn_skel__prepend_str("", prop_conflict, result_pool); /* No mine_props */
465
466   if (their_old_props)
467     {
468       SVN_ERR(svn_skel__unparse_proplist(&props, their_old_props,
469                                          result_pool));
470       svn_skel__prepend(props, prop_conflict);
471     }
472   else
473     svn_skel__prepend_str("", prop_conflict, result_pool); /* No old_props */
474
475   conflict_names = svn_skel__make_empty_list(result_pool);
476   for (hi = apr_hash_first(scratch_pool, (apr_hash_t *)conflicted_prop_names);
477        hi;
478        hi = apr_hash_next(hi))
479     {
480       svn_skel__prepend_str(apr_pstrdup(result_pool, apr_hash_this_key(hi)),
481                             conflict_names,
482                             result_pool);
483     }
484   svn_skel__prepend(conflict_names, prop_conflict);
485
486   markers = svn_skel__make_empty_list(result_pool);
487
488   if (marker_abspath)
489     {
490       const char *marker_relpath;
491       SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, wri_abspath,
492                                     marker_abspath,
493                                     result_pool, scratch_pool));
494
495       svn_skel__prepend_str(marker_relpath, markers, result_pool);
496     }
497 /*else // ### set via svn_wc__conflict_create_markers
498     svn_skel__prepend(svn_skel__make_empty_list(result_pool), markers);*/
499
500   svn_skel__prepend(markers, prop_conflict);
501
502   svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_PROP, prop_conflict, result_pool);
503
504   /* And add it to the conflict skel */
505   svn_skel__prepend(prop_conflict, conflict_skel->children->next);
506
507   return SVN_NO_ERROR;
508 }
509
510 /* A map for svn_wc_conflict_reason_t values. */
511 static const svn_token_map_t reason_map[] =
512 {
513   { "edited",           svn_wc_conflict_reason_edited },
514   { "obstructed",       svn_wc_conflict_reason_obstructed },
515   { "deleted",          svn_wc_conflict_reason_deleted },
516   { "missing",          svn_wc_conflict_reason_missing },
517   { "unversioned",      svn_wc_conflict_reason_unversioned },
518   { "added",            svn_wc_conflict_reason_added },
519   { "replaced",         svn_wc_conflict_reason_replaced },
520   { "moved-away",       svn_wc_conflict_reason_moved_away },
521   { "moved-here",       svn_wc_conflict_reason_moved_here },
522   { NULL }
523 };
524
525 static const svn_token_map_t action_map[] =
526 {
527   { "edited",           svn_wc_conflict_action_edit },
528   { "added",            svn_wc_conflict_action_add },
529   { "deleted",          svn_wc_conflict_action_delete },
530   { "replaced",         svn_wc_conflict_action_replace },
531   { NULL }
532 };
533
534 svn_error_t *
535 svn_wc__conflict_skel_add_tree_conflict(svn_skel_t *conflict_skel,
536                                         svn_wc__db_t *db,
537                                         const char *wri_abspath,
538                                         svn_wc_conflict_reason_t reason,
539                                         svn_wc_conflict_action_t action,
540                                         const char *move_src_op_root_abspath,
541                                         apr_pool_t *result_pool,
542                                         apr_pool_t *scratch_pool)
543 {
544   svn_skel_t *tree_conflict;
545   svn_skel_t *markers;
546
547   SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
548                                  SVN_WC__CONFLICT_KIND_TREE));
549
550   SVN_ERR_ASSERT(!tree_conflict); /* ### Use proper error? */
551
552   SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_moved_away
553                  || !move_src_op_root_abspath); /* ### Use proper error? */
554
555   tree_conflict = svn_skel__make_empty_list(result_pool);
556
557   if (reason == svn_wc_conflict_reason_moved_away
558       && move_src_op_root_abspath)
559     {
560       const char *move_src_op_root_relpath;
561
562       SVN_ERR(svn_wc__db_to_relpath(&move_src_op_root_relpath,
563                                     db, wri_abspath,
564                                     move_src_op_root_abspath,
565                                     result_pool, scratch_pool));
566
567       svn_skel__prepend_str(move_src_op_root_relpath, tree_conflict,
568                             result_pool);
569     }
570
571   svn_skel__prepend_str(svn_token__to_word(action_map, action),
572                         tree_conflict, result_pool);
573
574   svn_skel__prepend_str(svn_token__to_word(reason_map, reason),
575                         tree_conflict, result_pool);
576
577   /* Tree conflicts have no marker files */
578   markers = svn_skel__make_empty_list(result_pool);
579   svn_skel__prepend(markers, tree_conflict);
580
581   svn_skel__prepend_str(SVN_WC__CONFLICT_KIND_TREE, tree_conflict,
582                         result_pool);
583
584   /* And add it to the conflict skel */
585   svn_skel__prepend(tree_conflict, conflict_skel->children->next);
586
587   return SVN_NO_ERROR;
588 }
589
590 svn_error_t *
591 svn_wc__conflict_skel_resolve(svn_boolean_t *completely_resolved,
592                               svn_skel_t *conflict_skel,
593                               svn_wc__db_t *db,
594                               const char *wri_abspath,
595                               svn_boolean_t resolve_text,
596                               const char *resolve_prop,
597                               svn_boolean_t resolve_tree,
598                               apr_pool_t *result_pool,
599                               apr_pool_t *scratch_pool)
600 {
601   svn_skel_t *op;
602   svn_skel_t **pconflict;
603   SVN_ERR(conflict__get_operation(&op, conflict_skel));
604
605   if (!op)
606     return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
607                             _("Not a completed conflict skel"));
608
609   /* We are going to drop items from a linked list. Instead of keeping
610      a pointer to the item we want to drop we store a pointer to the
611      pointer of what we may drop, to allow setting it to the next item. */
612
613   pconflict = &(conflict_skel->children->next->children);
614   while (*pconflict)
615     {
616       svn_skel_t *c = (*pconflict)->children;
617
618       if (resolve_text
619           && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TEXT))
620         {
621           /* Remove the text conflict from the linked list */
622           *pconflict = (*pconflict)->next;
623           continue;
624         }
625       else if (resolve_prop
626                && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_PROP))
627         {
628           svn_skel_t **ppropnames = &(c->next->next->children);
629
630           if (resolve_prop[0] == '\0')
631             *ppropnames = NULL; /* remove all conflicted property names */
632           else
633             while (*ppropnames)
634               {
635                 if (svn_skel__matches_atom(*ppropnames, resolve_prop))
636                   {
637                     *ppropnames = (*ppropnames)->next;
638                     break;
639                   }
640                 ppropnames = &((*ppropnames)->next);
641               }
642
643           /* If no conflicted property names left */
644           if (!c->next->next->children)
645             {
646               /* Remove the propery conflict skel from the linked list */
647              *pconflict = (*pconflict)->next;
648              continue;
649             }
650         }
651       else if (resolve_tree
652                && svn_skel__matches_atom(c, SVN_WC__CONFLICT_KIND_TREE))
653         {
654           /* Remove the tree conflict from the linked list */
655           *pconflict = (*pconflict)->next;
656           continue;
657         }
658
659       pconflict = &((*pconflict)->next);
660     }
661
662   if (completely_resolved)
663     {
664       /* Nice, we can just call the complete function */
665       svn_boolean_t complete_conflict;
666       SVN_ERR(svn_wc__conflict_skel_is_complete(&complete_conflict,
667                                                 conflict_skel));
668
669       *completely_resolved = !complete_conflict;
670     }
671   return SVN_NO_ERROR;
672 }
673
674
675 /* A map for svn_wc_operation_t values. */
676 static const svn_token_map_t operation_map[] =
677 {
678   { "",   svn_wc_operation_none },
679   { SVN_WC__CONFLICT_OP_UPDATE, svn_wc_operation_update },
680   { SVN_WC__CONFLICT_OP_SWITCH, svn_wc_operation_switch },
681   { SVN_WC__CONFLICT_OP_MERGE,  svn_wc_operation_merge },
682   { NULL }
683 };
684
685 svn_error_t *
686 svn_wc__conflict_read_info(svn_wc_operation_t *operation,
687                            const apr_array_header_t **locations,
688                            svn_boolean_t *text_conflicted,
689                            svn_boolean_t *prop_conflicted,
690                            svn_boolean_t *tree_conflicted,
691                            svn_wc__db_t *db,
692                            const char *wri_abspath,
693                            const svn_skel_t *conflict_skel,
694                            apr_pool_t *result_pool,
695                            apr_pool_t *scratch_pool)
696 {
697   svn_skel_t *op;
698   const svn_skel_t *c;
699
700   SVN_ERR(conflict__get_operation(&op, conflict_skel));
701
702   if (!op)
703     return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
704                             _("Not a completed conflict skel"));
705
706   c = op->children;
707   if (operation)
708     {
709       int value = svn_token__from_mem(operation_map, c->data, c->len);
710
711       if (value != SVN_TOKEN_UNKNOWN)
712         *operation = value;
713       else
714         *operation = svn_wc_operation_none;
715     }
716   c = c->next;
717
718   if (locations && c->children)
719     {
720       const svn_skel_t *loc_skel;
721       svn_wc_conflict_version_t *loc;
722       apr_array_header_t *locs = apr_array_make(result_pool, 2, sizeof(loc));
723
724       for (loc_skel = c->children; loc_skel; loc_skel = loc_skel->next)
725         {
726           SVN_ERR(conflict__read_location(&loc, loc_skel, result_pool,
727                                           scratch_pool));
728
729           APR_ARRAY_PUSH(locs, svn_wc_conflict_version_t *) = loc;
730         }
731
732       *locations = locs;
733     }
734   else if (locations)
735     *locations = NULL;
736
737   if (text_conflicted)
738     {
739       svn_skel_t *c_skel;
740       SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
741                                      SVN_WC__CONFLICT_KIND_TEXT));
742
743       *text_conflicted = (c_skel != NULL);
744     }
745
746   if (prop_conflicted)
747     {
748       svn_skel_t *c_skel;
749       SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
750                                      SVN_WC__CONFLICT_KIND_PROP));
751
752       *prop_conflicted = (c_skel != NULL);
753     }
754
755   if (tree_conflicted)
756     {
757       svn_skel_t *c_skel;
758       SVN_ERR(conflict__get_conflict(&c_skel, conflict_skel,
759                                      SVN_WC__CONFLICT_KIND_TREE));
760
761       *tree_conflicted = (c_skel != NULL);
762     }
763
764   return SVN_NO_ERROR;
765 }
766
767
768 svn_error_t *
769 svn_wc__conflict_read_text_conflict(const char **mine_abspath,
770                                     const char **their_old_abspath,
771                                     const char **their_abspath,
772                                     svn_wc__db_t *db,
773                                     const char *wri_abspath,
774                                     const svn_skel_t *conflict_skel,
775                                     apr_pool_t *result_pool,
776                                     apr_pool_t *scratch_pool)
777 {
778   svn_skel_t *text_conflict;
779   const svn_skel_t *m;
780
781   SVN_ERR(conflict__get_conflict(&text_conflict, conflict_skel,
782                                  SVN_WC__CONFLICT_KIND_TEXT));
783
784   if (!text_conflict)
785     return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
786
787   m = text_conflict->children->next->children;
788
789   if (their_old_abspath)
790     {
791       if (m->is_atom)
792         {
793           const char *original_relpath;
794
795           original_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
796           SVN_ERR(svn_wc__db_from_relpath(their_old_abspath,
797                                           db, wri_abspath, original_relpath,
798                                           result_pool, scratch_pool));
799         }
800       else
801         *their_old_abspath = NULL;
802     }
803   m = m->next;
804
805   if (mine_abspath)
806     {
807       if (m->is_atom)
808         {
809           const char *mine_relpath;
810
811           mine_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
812           SVN_ERR(svn_wc__db_from_relpath(mine_abspath,
813                                           db, wri_abspath, mine_relpath,
814                                           result_pool, scratch_pool));
815         }
816       else
817         *mine_abspath = NULL;
818     }
819   m = m->next;
820
821   if (their_abspath)
822     {
823       if (m->is_atom)
824         {
825           const char *their_relpath;
826
827           their_relpath = apr_pstrmemdup(scratch_pool, m->data, m->len);
828           SVN_ERR(svn_wc__db_from_relpath(their_abspath,
829                                           db, wri_abspath, their_relpath,
830                                           result_pool, scratch_pool));
831         }
832       else
833         *their_abspath = NULL;
834     }
835
836   return SVN_NO_ERROR;
837 }
838
839 svn_error_t *
840 svn_wc__conflict_read_prop_conflict(const char **marker_abspath,
841                                     apr_hash_t **mine_props,
842                                     apr_hash_t **their_old_props,
843                                     apr_hash_t **their_props,
844                                     apr_hash_t **conflicted_prop_names,
845                                     svn_wc__db_t *db,
846                                     const char *wri_abspath,
847                                     const svn_skel_t *conflict_skel,
848                                     apr_pool_t *result_pool,
849                                     apr_pool_t *scratch_pool)
850 {
851   svn_skel_t *prop_conflict;
852   const svn_skel_t *c;
853
854   SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
855                                  SVN_WC__CONFLICT_KIND_PROP));
856
857   if (!prop_conflict)
858     return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
859
860   c = prop_conflict->children;
861
862   c = c->next; /* Skip "prop" */
863
864   /* Get marker file */
865   if (marker_abspath)
866     {
867       const char *marker_relpath;
868
869       if (c->children && c->children->is_atom)
870         {
871           marker_relpath = apr_pstrmemdup(result_pool, c->children->data,
872                                         c->children->len);
873
874           SVN_ERR(svn_wc__db_from_relpath(marker_abspath, db, wri_abspath,
875                                           marker_relpath,
876                                           result_pool, scratch_pool));
877         }
878       else
879         *marker_abspath = NULL;
880     }
881   c = c->next;
882
883   /* Get conflicted properties */
884   if (conflicted_prop_names)
885     {
886       const svn_skel_t *name;
887       *conflicted_prop_names = apr_hash_make(result_pool);
888
889       for (name = c->children; name; name = name->next)
890         {
891           svn_hash_sets(*conflicted_prop_names,
892                         apr_pstrmemdup(result_pool, name->data, name->len),
893                         "");
894         }
895     }
896   c = c->next;
897
898   /* Get original properties */
899   if (their_old_props)
900     {
901       if (c->is_atom)
902         *their_old_props = apr_hash_make(result_pool);
903       else
904         SVN_ERR(svn_skel__parse_proplist(their_old_props, c, result_pool));
905     }
906   c = c->next;
907
908   /* Get mine properties */
909   if (mine_props)
910     {
911       if (c->is_atom)
912         *mine_props = apr_hash_make(result_pool);
913       else
914         SVN_ERR(svn_skel__parse_proplist(mine_props, c, result_pool));
915     }
916   c = c->next;
917
918   /* Get their properties */
919   if (their_props)
920     {
921       if (c->is_atom)
922         *their_props = apr_hash_make(result_pool);
923       else
924         SVN_ERR(svn_skel__parse_proplist(their_props, c, result_pool));
925     }
926
927   return SVN_NO_ERROR;
928 }
929
930 svn_error_t *
931 svn_wc__conflict_read_tree_conflict(svn_wc_conflict_reason_t *reason,
932                                     svn_wc_conflict_action_t *action,
933                                     const char **move_src_op_root_abspath,
934                                     svn_wc__db_t *db,
935                                     const char *wri_abspath,
936                                     const svn_skel_t *conflict_skel,
937                                     apr_pool_t *result_pool,
938                                     apr_pool_t *scratch_pool)
939 {
940   svn_skel_t *tree_conflict;
941   const svn_skel_t *c;
942   svn_boolean_t is_moved_away = FALSE;
943
944   SVN_ERR(conflict__get_conflict(&tree_conflict, conflict_skel,
945                                  SVN_WC__CONFLICT_KIND_TREE));
946
947   if (!tree_conflict)
948     return svn_error_create(SVN_ERR_WC_MISSING, NULL, _("Conflict not set"));
949
950   c = tree_conflict->children;
951
952   c = c->next; /* Skip "tree" */
953
954   c = c->next; /* Skip markers */
955
956   {
957     int value = svn_token__from_mem(reason_map, c->data, c->len);
958
959     if (reason)
960       {
961         if (value != SVN_TOKEN_UNKNOWN)
962           *reason = value;
963         else
964           *reason = svn_wc_conflict_reason_edited;
965       }
966
967       is_moved_away = (value == svn_wc_conflict_reason_moved_away);
968     }
969   c = c->next;
970
971   if (action)
972     {
973       int value = svn_token__from_mem(action_map, c->data, c->len);
974
975       if (value != SVN_TOKEN_UNKNOWN)
976         *action = value;
977       else
978         *action = svn_wc_conflict_action_edit;
979     }
980
981   c = c->next;
982
983   if (move_src_op_root_abspath)
984     {
985       /* Only set for update and switch tree conflicts */
986       if (c && is_moved_away)
987         {
988           const char *move_src_op_root_relpath
989                             = apr_pstrmemdup(scratch_pool, c->data, c->len);
990
991           SVN_ERR(svn_wc__db_from_relpath(move_src_op_root_abspath,
992                                           db, wri_abspath,
993                                           move_src_op_root_relpath,
994                                           result_pool, scratch_pool));
995         }
996       else
997         *move_src_op_root_abspath = NULL;
998     }
999
1000   return SVN_NO_ERROR;
1001 }
1002
1003 svn_error_t *
1004 svn_wc__conflict_read_markers(const apr_array_header_t **markers,
1005                               svn_wc__db_t *db,
1006                               const char *wri_abspath,
1007                               const svn_skel_t *conflict_skel,
1008                               apr_pool_t *result_pool,
1009                               apr_pool_t *scratch_pool)
1010 {
1011   const svn_skel_t *conflict;
1012   apr_array_header_t *list = NULL;
1013
1014   SVN_ERR_ASSERT(conflict_skel != NULL);
1015
1016   /* Walk the conflicts */
1017   for (conflict = conflict_skel->children->next->children;
1018        conflict;
1019        conflict = conflict->next)
1020     {
1021       const svn_skel_t *marker;
1022
1023       /* Get the list of markers stored per conflict */
1024       for (marker = conflict->children->next->children;
1025            marker;
1026            marker = marker->next)
1027         {
1028           /* Skip placeholders */
1029           if (! marker->is_atom)
1030             continue;
1031
1032           if (! list)
1033             list = apr_array_make(result_pool, 4, sizeof(const char *));
1034
1035           SVN_ERR(svn_wc__db_from_relpath(
1036                         &APR_ARRAY_PUSH(list, const char*),
1037                         db, wri_abspath,
1038                         apr_pstrmemdup(scratch_pool, marker->data,
1039                                        marker->len),
1040                         result_pool, scratch_pool));
1041         }
1042     }
1043   *markers = list;
1044
1045   return SVN_NO_ERROR;
1046 }
1047
1048 /* --------------------------------------------------------------------
1049  */
1050
1051
1052 svn_error_t *
1053 svn_wc__conflict_create_markers(svn_skel_t **work_items,
1054                                 svn_wc__db_t *db,
1055                                 const char *local_abspath,
1056                                 svn_skel_t *conflict_skel,
1057                                 apr_pool_t *result_pool,
1058                                 apr_pool_t *scratch_pool)
1059 {
1060   svn_boolean_t prop_conflicted;
1061   svn_wc_operation_t operation;
1062   *work_items = NULL;
1063
1064   SVN_ERR(svn_wc__conflict_read_info(&operation, NULL,
1065                                      NULL, &prop_conflicted, NULL,
1066                                      db, local_abspath,
1067                                      conflict_skel,
1068                                      scratch_pool, scratch_pool));
1069
1070   if (prop_conflicted)
1071     {
1072       const char *marker_abspath = NULL;
1073       svn_node_kind_t kind;
1074       const char *marker_dir;
1075       const char *marker_name;
1076       const char *marker_relpath;
1077
1078       /* Ok, currently we have to do a few things for property conflicts:
1079          - Create a marker file
1080          - Store the name in the conflict_skel
1081          - Create a WQ item that fills the marker with the expected data */
1082
1083       SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
1084
1085       if (kind == svn_node_dir)
1086         {
1087           marker_dir = local_abspath;
1088           marker_name = SVN_WC__THIS_DIR_PREJ;
1089         }
1090       else
1091         svn_dirent_split(&marker_dir, &marker_name, local_abspath,
1092                          scratch_pool);
1093
1094       SVN_ERR(svn_io_open_uniquely_named(NULL, &marker_abspath,
1095                                          marker_dir,
1096                                          marker_name,
1097                                          SVN_WC__PROP_REJ_EXT,
1098                                          svn_io_file_del_none,
1099                                          scratch_pool, scratch_pool));
1100
1101       SVN_ERR(svn_wc__db_to_relpath(&marker_relpath, db, local_abspath,
1102                                     marker_abspath, result_pool, result_pool));
1103
1104       /* And store the marker in the skel */
1105       {
1106         svn_skel_t *prop_conflict;
1107         SVN_ERR(conflict__get_conflict(&prop_conflict, conflict_skel,
1108                                        SVN_WC__CONFLICT_KIND_PROP));
1109
1110         svn_skel__prepend_str(marker_relpath, prop_conflict->children->next,
1111                             result_pool);
1112       }
1113       SVN_ERR(svn_wc__wq_build_prej_install(work_items,
1114                                             db, local_abspath,
1115                                             scratch_pool, scratch_pool));
1116     }
1117
1118   return SVN_NO_ERROR;
1119 }
1120
1121 /* Helper function for the three apply_* functions below, used when
1122  * merging properties together.
1123  *
1124  * Given property PROPNAME on LOCAL_ABSPATH, and four possible property
1125  * values, generate four tmpfiles and pass them to CONFLICT_FUNC callback.
1126  * This gives the client an opportunity to interactively resolve the
1127  * property conflict.
1128  *
1129  * BASE_VAL/WORKING_VAL represent the current state of the working
1130  * copy, and INCOMING_OLD_VAL/INCOMING_NEW_VAL represents the incoming
1131  * propchange.  Any of these values might be NULL, indicating either
1132  * non-existence or intent-to-delete.
1133  *
1134  * If the callback isn't available, or if it responds with
1135  * 'choose_postpone', then set *CONFLICT_REMAINS to TRUE and return.
1136  *
1137  * If the callback responds with a choice of 'base', 'theirs', 'mine',
1138  * or 'merged', then install the proper value into ACTUAL_PROPS and
1139  * set *CONFLICT_REMAINS to FALSE.
1140  */
1141 static svn_error_t *
1142 generate_propconflict(svn_boolean_t *conflict_remains,
1143                       svn_wc__db_t *db,
1144                       const char *local_abspath,
1145                       svn_node_kind_t kind,
1146                       svn_wc_operation_t operation,
1147                       const svn_wc_conflict_version_t *left_version,
1148                       const svn_wc_conflict_version_t *right_version,
1149                       const char *propname,
1150                       const svn_string_t *base_val,
1151                       const svn_string_t *working_val,
1152                       const svn_string_t *incoming_old_val,
1153                       const svn_string_t *incoming_new_val,
1154                       svn_wc_conflict_resolver_func2_t conflict_func,
1155                       void *conflict_baton,
1156                       svn_cancel_func_t cancel_func,
1157                       void *cancel_baton,
1158                       apr_pool_t *scratch_pool)
1159 {
1160   svn_wc_conflict_result_t *result = NULL;
1161   svn_wc_conflict_description2_t *cdesc;
1162   const char *dirpath = svn_dirent_dirname(local_abspath, scratch_pool);
1163   const svn_string_t *new_value = NULL;
1164
1165   cdesc = svn_wc_conflict_description_create_prop2(
1166                 local_abspath,
1167                 kind,
1168                 propname, scratch_pool);
1169
1170   cdesc->operation = operation;
1171   cdesc->src_left_version = left_version;
1172   cdesc->src_right_version = right_version;
1173
1174   /* Create a tmpfile for each of the string_t's we've got.  */
1175   if (working_val)
1176     {
1177       const char *file_name;
1178
1179       SVN_ERR(svn_io_write_unique(&file_name, dirpath, working_val->data,
1180                                   working_val->len,
1181                                   svn_io_file_del_on_pool_cleanup,
1182                                   scratch_pool));
1183       cdesc->my_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1184       cdesc->prop_value_working = working_val;
1185     }
1186
1187   if (incoming_new_val)
1188     {
1189       const char *file_name;
1190
1191       SVN_ERR(svn_io_write_unique(&file_name, dirpath, incoming_new_val->data,
1192                                   incoming_new_val->len,
1193                                   svn_io_file_del_on_pool_cleanup,
1194                                   scratch_pool));
1195
1196       /* ### For property conflicts, cd2 stores prop_reject_abspath in
1197        * ### their_abspath, and stores theirs_abspath in merged_file. */
1198       cdesc->merged_file = svn_dirent_join(dirpath, file_name, scratch_pool);
1199       cdesc->prop_value_incoming_new = incoming_new_val;
1200     }
1201
1202   if (!base_val && !incoming_old_val)
1203     {
1204       /* If base and old are both NULL, then that's fine, we just let
1205          base_file stay NULL as-is.  Both agents are attempting to add a
1206          new property.  */
1207     }
1208   else if ((base_val && !incoming_old_val)
1209            || (!base_val && incoming_old_val))
1210     {
1211       /* If only one of base and old are defined, then we've got a
1212          situation where one agent is attempting to add the property
1213          for the first time, and the other agent is changing a
1214          property it thinks already exists.  In this case, we return
1215          whichever older-value happens to be defined, so that the
1216          conflict-callback can still attempt a 3-way merge. */
1217
1218       const svn_string_t *conflict_base_val = base_val ? base_val
1219                                                        : incoming_old_val;
1220       const char *file_name;
1221
1222       SVN_ERR(svn_io_write_unique(&file_name, dirpath,
1223                                   conflict_base_val->data,
1224                                   conflict_base_val->len,
1225                                   svn_io_file_del_on_pool_cleanup,
1226                                   scratch_pool));
1227       cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1228     }
1229   else  /* base and old are both non-NULL */
1230     {
1231       const svn_string_t *conflict_base_val;
1232       const char *file_name;
1233
1234       if (! svn_string_compare(base_val, incoming_old_val))
1235         {
1236           /* What happens if 'base' and 'old' don't match up?  In an
1237              ideal situation, they would.  But if they don't, this is
1238              a classic example of a patch 'hunk' failing to apply due
1239              to a lack of context.  For example: imagine that the user
1240              is busy changing the property from a value of "cat" to
1241              "dog", but the incoming propchange wants to change the
1242              same property value from "red" to "green".  Total context
1243              mismatch.
1244
1245              HOWEVER: we can still pass one of the two base values as
1246              'base_file' to the callback anyway.  It's still useful to
1247              present the working and new values to the user to
1248              compare. */
1249
1250           if (working_val && svn_string_compare(base_val, working_val))
1251             conflict_base_val = incoming_old_val;
1252           else
1253             conflict_base_val = base_val;
1254         }
1255       else
1256         {
1257           conflict_base_val = base_val;
1258         }
1259
1260       SVN_ERR(svn_io_write_unique(&file_name, dirpath, conflict_base_val->data,
1261                                   conflict_base_val->len,
1262                                   svn_io_file_del_on_pool_cleanup, scratch_pool));
1263       cdesc->base_abspath = svn_dirent_join(dirpath, file_name, scratch_pool);
1264
1265       cdesc->prop_value_base = base_val;
1266       cdesc->prop_value_incoming_old = incoming_old_val;
1267
1268       if (working_val && incoming_new_val)
1269         {
1270           svn_stream_t *mergestream;
1271           svn_diff_t *diff;
1272           svn_diff_file_options_t *options =
1273             svn_diff_file_options_create(scratch_pool);
1274
1275           SVN_ERR(svn_stream_open_unique(&mergestream, &cdesc->prop_reject_abspath,
1276                                          NULL, svn_io_file_del_on_pool_cleanup,
1277                                          scratch_pool, scratch_pool));
1278           SVN_ERR(svn_diff_mem_string_diff3(&diff, conflict_base_val,
1279                                             working_val,
1280                                             incoming_new_val, options, scratch_pool));
1281           SVN_ERR(svn_diff_mem_string_output_merge3(mergestream, diff,
1282                    conflict_base_val, working_val,
1283                    incoming_new_val, NULL, NULL, NULL, NULL,
1284                    svn_diff_conflict_display_modified_latest,
1285                    cancel_func, cancel_baton, scratch_pool));
1286           SVN_ERR(svn_stream_close(mergestream));
1287
1288           /* ### For property conflicts, cd2 stores prop_reject_abspath in
1289            * ### their_abspath, and stores theirs_abspath in merged_file. */
1290           cdesc->their_abspath = cdesc->prop_reject_abspath;
1291         }
1292     }
1293
1294   if (!incoming_old_val && incoming_new_val)
1295     cdesc->action = svn_wc_conflict_action_add;
1296   else if (incoming_old_val && !incoming_new_val)
1297     cdesc->action = svn_wc_conflict_action_delete;
1298   else
1299     cdesc->action = svn_wc_conflict_action_edit;
1300
1301   if (base_val && !working_val)
1302     cdesc->reason = svn_wc_conflict_reason_deleted;
1303   else if (!base_val && working_val)
1304     cdesc->reason = svn_wc_conflict_reason_obstructed;
1305   else
1306     cdesc->reason = svn_wc_conflict_reason_edited;
1307
1308   /* Invoke the interactive conflict callback. */
1309   SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
1310                         scratch_pool));
1311   if (result == NULL)
1312     {
1313       *conflict_remains = TRUE;
1314       return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
1315                               NULL, _("Conflict callback violated API:"
1316                                       " returned no results"));
1317     }
1318
1319
1320   switch (result->choice)
1321     {
1322       default:
1323       case svn_wc_conflict_choose_postpone:
1324         {
1325           *conflict_remains = TRUE;
1326           break;
1327         }
1328       case svn_wc_conflict_choose_mine_full:
1329         {
1330           /* No need to change actual_props; it already contains working_val */
1331           *conflict_remains = FALSE;
1332           new_value = working_val;
1333           break;
1334         }
1335       /* I think _mine_full and _theirs_full are appropriate for prop
1336          behavior as well as the text behavior.  There should even be
1337          analogous behaviors for _mine and _theirs when those are
1338          ready, namely: fold in all non-conflicting prop changes, and
1339          then choose _mine side or _theirs side for conflicting ones. */
1340       case svn_wc_conflict_choose_theirs_full:
1341         {
1342           *conflict_remains = FALSE;
1343           new_value = incoming_new_val;
1344           break;
1345         }
1346       case svn_wc_conflict_choose_base:
1347         {
1348           *conflict_remains = FALSE;
1349           new_value = base_val;
1350           break;
1351         }
1352       case svn_wc_conflict_choose_merged:
1353         {
1354           svn_stringbuf_t *merged_stringbuf;
1355
1356           if (!cdesc->merged_file 
1357               && (!result->merged_file && !result->merged_value))
1358             return svn_error_create
1359                 (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
1360                  NULL, _("Conflict callback violated API:"
1361                          " returned no merged file"));
1362
1363           if (result->merged_value)
1364             new_value = result->merged_value;
1365           else
1366             {
1367               SVN_ERR(svn_stringbuf_from_file2(&merged_stringbuf,
1368                                                result->merged_file ?
1369                                                     result->merged_file :
1370                                                     cdesc->merged_file,
1371                                                scratch_pool));
1372               new_value = svn_stringbuf__morph_into_string(merged_stringbuf);
1373             }
1374           *conflict_remains = FALSE;
1375           break;
1376         }
1377     }
1378
1379   if (!*conflict_remains)
1380     {
1381       apr_hash_t *props;
1382
1383       /* For now, just set the property values. This should really do some of the
1384          more advanced things from svn_wc_prop_set() */
1385
1386       SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool,
1387                                     scratch_pool));
1388
1389       svn_hash_sets(props, propname, new_value);
1390
1391       SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, props,
1392                                       FALSE, NULL, NULL,
1393                                       scratch_pool));
1394     }
1395
1396   return SVN_NO_ERROR;
1397 }
1398
1399 /* Perform a 3-way merge in which conflicts are expected, showing the
1400  * conflicts in the way specified by STYLE, and using MERGE_OPTIONS.
1401  *
1402  * The three input files are LEFT_ABSPATH (the base), DETRANSLATED_TARGET
1403  * and RIGHT_ABSPATH.  The output is stored in a new temporary file,
1404  * whose name is put into *CHOSEN_ABSPATH.
1405  *
1406  * The output file will be deleted according to DELETE_WHEN.  If
1407  * DELETE_WHEN is 'on pool cleanup', it refers to RESULT_POOL.
1408  *
1409  * DB and WRI_ABSPATH are used to choose a directory for the output file.
1410  *
1411  * Allocate *CHOSEN_ABSPATH in RESULT_POOL.  Use SCRATCH_POOL for temporary
1412  * allocations.
1413  */
1414 static svn_error_t *
1415 merge_showing_conflicts(const char **chosen_abspath,
1416                         svn_wc__db_t *db,
1417                         const char *wri_abspath,
1418                         svn_diff_conflict_display_style_t style,
1419                         const apr_array_header_t *merge_options,
1420                         const char *left_abspath,
1421                         const char *detranslated_target,
1422                         const char *right_abspath,
1423                         svn_io_file_del_t delete_when,
1424                         svn_cancel_func_t cancel_func,
1425                         void *cancel_baton,
1426                         apr_pool_t *result_pool,
1427                         apr_pool_t *scratch_pool)
1428 {
1429   const char *temp_dir;
1430   svn_stream_t *chosen_stream;
1431   svn_diff_t *diff;
1432   svn_diff_file_options_t *diff3_options;
1433
1434   diff3_options = svn_diff_file_options_create(scratch_pool);
1435   if (merge_options)
1436     SVN_ERR(svn_diff_file_options_parse(diff3_options,
1437                                         merge_options,
1438                                         scratch_pool));
1439
1440   SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, db,
1441                                          wri_abspath,
1442                                          scratch_pool, scratch_pool));
1443   /* We need to open the stream in RESULT_POOL because that controls the
1444    * lifetime of the file if DELETE_WHEN is 'on pool cleanup'.  (We also
1445    * want to allocate CHOSEN_ABSPATH in RESULT_POOL, but we don't care
1446    * about the stream itself.) */
1447   SVN_ERR(svn_stream_open_unique(&chosen_stream, chosen_abspath,
1448                                  temp_dir, delete_when,
1449                                  result_pool, scratch_pool));
1450   SVN_ERR(svn_diff_file_diff3_2(&diff,
1451                                 left_abspath,
1452                                 detranslated_target, right_abspath,
1453                                 diff3_options, scratch_pool));
1454   SVN_ERR(svn_diff_file_output_merge3(chosen_stream, diff,
1455                                       left_abspath,
1456                                       detranslated_target,
1457                                       right_abspath,
1458                                       NULL, NULL, NULL, NULL, /* markers */
1459                                       style, cancel_func, cancel_baton,
1460                                       scratch_pool));
1461   SVN_ERR(svn_stream_close(chosen_stream));
1462
1463   return SVN_NO_ERROR;
1464 }
1465
1466 /* Prepare to delete an artifact file at ARTIFACT_FILE_ABSPATH in the
1467  * working copy at DB/WRI_ABSPATH.
1468  *
1469  * Set *WORK_ITEMS to a new work item that, when run, will delete the
1470  * artifact file; or to NULL if there is no file to delete.
1471  *
1472  * Set *FILE_FOUND to TRUE if the artifact file is found on disk and its
1473  * node kind is 'file'; otherwise do not change *FILE_FOUND.  FILE_FOUND
1474  * may be NULL if not required.
1475  */
1476 static svn_error_t *
1477 remove_artifact_file_if_exists(svn_skel_t **work_items,
1478                                svn_boolean_t *file_found,
1479                                svn_wc__db_t *db,
1480                                const char *wri_abspath,
1481                                const char *artifact_file_abspath,
1482                                apr_pool_t *result_pool,
1483                                apr_pool_t *scratch_pool)
1484 {
1485   *work_items = NULL;
1486   if (artifact_file_abspath)
1487     {
1488       svn_node_kind_t node_kind;
1489
1490       SVN_ERR(svn_io_check_path(artifact_file_abspath, &node_kind,
1491                                 scratch_pool));
1492       if (node_kind == svn_node_file)
1493         {
1494           SVN_ERR(svn_wc__wq_build_file_remove(work_items,
1495                                                db, wri_abspath,
1496                                                artifact_file_abspath,
1497                                                result_pool, scratch_pool));
1498           if (file_found)
1499             *file_found = TRUE;
1500         }
1501     }
1502
1503   return SVN_NO_ERROR;
1504 }
1505
1506 /* Create a new file in the same directory as LOCAL_ABSPATH, with the
1507    same basename as LOCAL_ABSPATH, with a ".edited" extension, and set
1508    *WORK_ITEM to a new work item that will copy and translate from the file
1509    SOURCE_ABSPATH to that new file.  It will be translated from repository-
1510    normal form to working-copy form according to the versioned properties
1511    of LOCAL_ABSPATH that are current when the work item is executed.
1512
1513    DB should have a write lock for the directory containing SOURCE.
1514
1515    Allocate *WORK_ITEM in RESULT_POOL. */
1516 static svn_error_t *
1517 save_merge_result(svn_skel_t **work_item,
1518                   svn_wc__db_t *db,
1519                   const char *local_abspath,
1520                   const char *source_abspath,
1521                   apr_pool_t *result_pool,
1522                   apr_pool_t *scratch_pool)
1523 {
1524   const char *edited_copy_abspath;
1525   const char *dir_abspath;
1526   const char *filename;
1527
1528   svn_dirent_split(&dir_abspath, &filename, local_abspath, scratch_pool);
1529
1530   /* ### Should use preserved-conflict-file-exts. */
1531   /* Create the .edited file within this file's DIR_ABSPATH  */
1532   SVN_ERR(svn_io_open_uniquely_named(NULL,
1533                                      &edited_copy_abspath,
1534                                      dir_abspath,
1535                                      filename,
1536                                      ".edited",
1537                                      svn_io_file_del_none,
1538                                      scratch_pool, scratch_pool));
1539   SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item,
1540                                                 db, local_abspath,
1541                                                 source_abspath,
1542                                                 edited_copy_abspath,
1543                                                 result_pool, scratch_pool));
1544   return SVN_NO_ERROR;
1545 }
1546
1547
1548
1549 /* Resolve the text conflict in CONFLICT, which is currently recorded
1550  * on DB/LOCAL_ABSPATH in the manner specified by CHOICE.
1551  *
1552  * Set *WORK_ITEMS to new work items that will make the on-disk changes
1553  * needed to complete the resolution (but not to mark it as resolved).
1554  *
1555  * Set *FOUND_ARTIFACT to true if conflict markers are removed; otherwise
1556  * (which is only if CHOICE is 'postpone') to false.
1557  *
1558  * CHOICE, MERGED_FILE and SAVE_MERGED are typically values provided by
1559  * the conflict resolver.
1560  *
1561  * MERGE_OPTIONS allows customizing the diff handling when using
1562  * per hunk conflict resolving.
1563  */
1564 static svn_error_t *
1565 build_text_conflict_resolve_items(svn_skel_t **work_items,
1566                                   svn_boolean_t *found_artifact,
1567                                   svn_wc__db_t *db,
1568                                   const char *local_abspath,
1569                                   const svn_skel_t *conflict,
1570                                   svn_wc_conflict_choice_t choice,
1571                                   const char *merged_file,
1572                                   svn_boolean_t save_merged,
1573                                   const apr_array_header_t *merge_options,
1574                                   svn_cancel_func_t cancel_func,
1575                                   void *cancel_baton,
1576                                   apr_pool_t *result_pool,
1577                                   apr_pool_t *scratch_pool)
1578 {
1579   const char *mine_abspath;
1580   const char *their_old_abspath;
1581   const char *their_abspath;
1582   svn_skel_t *work_item;
1583   const char *install_from_abspath = NULL;
1584   svn_boolean_t remove_source = FALSE;
1585
1586   *work_items = NULL;
1587
1588   if (found_artifact)
1589     *found_artifact = FALSE;
1590
1591   SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
1592                                               &their_old_abspath,
1593                                               &their_abspath,
1594                                               db, local_abspath,
1595                                               conflict,
1596                                               scratch_pool, scratch_pool));
1597
1598   if (save_merged)
1599     SVN_ERR(save_merge_result(work_items,
1600                               db, local_abspath,
1601                               merged_file
1602                                 ? merged_file
1603                                 : local_abspath,
1604                               result_pool, scratch_pool));
1605
1606   if (choice == svn_wc_conflict_choose_postpone)
1607     return SVN_NO_ERROR;
1608
1609   switch (choice)
1610     {
1611       /* If the callback wants to use one of the fulltexts
1612          to resolve the conflict, so be it.*/
1613       case svn_wc_conflict_choose_base:
1614         {
1615           install_from_abspath = their_old_abspath;
1616           break;
1617         }
1618       case svn_wc_conflict_choose_theirs_full:
1619         {
1620           install_from_abspath = their_abspath;
1621           break;
1622         }
1623       case svn_wc_conflict_choose_mine_full:
1624         {
1625           install_from_abspath = mine_abspath;
1626           break;
1627         }
1628       case svn_wc_conflict_choose_theirs_conflict:
1629       case svn_wc_conflict_choose_mine_conflict:
1630         {
1631           svn_diff_conflict_display_style_t style
1632             = choice == svn_wc_conflict_choose_theirs_conflict
1633                 ? svn_diff_conflict_display_latest
1634                 : svn_diff_conflict_display_modified;
1635
1636           SVN_ERR(merge_showing_conflicts(&install_from_abspath,
1637                                           db, local_abspath,
1638                                           style, merge_options,
1639                                           their_old_abspath,
1640                                           mine_abspath,
1641                                           their_abspath,
1642                                           /* ### why not same as other caller? */
1643                                           svn_io_file_del_none,
1644                                           cancel_func, cancel_baton,
1645                                           scratch_pool, scratch_pool));
1646           remove_source = TRUE;
1647           break;
1648         }
1649
1650         /* For the case of 3-way file merging, we don't
1651            really distinguish between these return values;
1652            if the callback claims to have "generally
1653            resolved" the situation, we still interpret
1654            that as "OK, we'll assume the merged version is
1655            good to use". */
1656       case svn_wc_conflict_choose_merged:
1657         {
1658           install_from_abspath = merged_file
1659                                   ? merged_file
1660                                   : local_abspath;
1661           break;
1662         }
1663       case svn_wc_conflict_choose_postpone:
1664         {
1665           /* Assume conflict remains. */
1666           return SVN_NO_ERROR;
1667         }
1668       default:
1669         SVN_ERR_ASSERT(choice == svn_wc_conflict_choose_postpone);
1670     }
1671
1672   if (install_from_abspath == NULL)
1673     return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1674                              _("Conflict on '%s' could not be resolved "
1675                                "because the chosen version of the file "
1676                                "is not available."),
1677                              svn_dirent_local_style(local_abspath,
1678                                                     scratch_pool));
1679
1680   /* ### It would be nice if we could somehow pass RECORD_FILEINFO
1681          as true in some easy cases. */
1682   SVN_ERR(svn_wc__wq_build_file_install(&work_item,
1683                                         db, local_abspath,
1684                                         install_from_abspath,
1685                                         FALSE /* use_commit_times */,
1686                                         FALSE /* record_fileinfo */,
1687                                         result_pool, scratch_pool));
1688   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1689
1690   if (remove_source)
1691     {
1692       SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
1693                                            db, local_abspath,
1694                                            install_from_abspath,
1695                                            result_pool, scratch_pool));
1696       *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1697     }
1698
1699   SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1700                                          db, local_abspath,
1701                                          their_old_abspath,
1702                                          result_pool, scratch_pool));
1703   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1704
1705   SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1706                                          db, local_abspath,
1707                                          their_abspath,
1708                                          result_pool, scratch_pool));
1709   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1710
1711   SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1712                                          db, local_abspath,
1713                                          mine_abspath,
1714                                          result_pool, scratch_pool));
1715   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1716
1717   return SVN_NO_ERROR;
1718 }
1719
1720
1721 /* Set *DESC to a new description of the text conflict in
1722  * CONFLICT_SKEL.  If there is no text conflict in CONFLICT_SKEL, return
1723  * an error.
1724  *
1725  * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1726  * rather than reading them from CONFLICT_SKEL.  Use IS_BINARY and
1727  * MIME_TYPE for the corresponding fields of *DESC.
1728  *
1729  * Allocate results in RESULT_POOL.  SCRATCH_POOL is used for temporary
1730  * allocations. */
1731 static svn_error_t *
1732 read_text_conflict_desc(svn_wc_conflict_description2_t **desc,
1733                         svn_wc__db_t *db,
1734                         const char *local_abspath,
1735                         const svn_skel_t *conflict_skel,
1736                         const char *mime_type,
1737                         svn_wc_operation_t operation,
1738                         const svn_wc_conflict_version_t *left_version,
1739                         const svn_wc_conflict_version_t *right_version,
1740                         apr_pool_t *result_pool,
1741                         apr_pool_t *scratch_pool)
1742 {
1743   *desc = svn_wc_conflict_description_create_text2(local_abspath, result_pool);
1744   (*desc)->mime_type = mime_type;
1745   (*desc)->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
1746   (*desc)->operation = operation;
1747   (*desc)->src_left_version = left_version;
1748   (*desc)->src_right_version = right_version;
1749
1750   SVN_ERR(svn_wc__conflict_read_text_conflict(&(*desc)->my_abspath,
1751                                               &(*desc)->base_abspath,
1752                                               &(*desc)->their_abspath,
1753                                               db, local_abspath,
1754                                               conflict_skel,
1755                                               result_pool, scratch_pool));
1756   (*desc)->merged_file = apr_pstrdup(result_pool, local_abspath);
1757
1758   return SVN_NO_ERROR;
1759 }
1760
1761 /* Set *CONFLICT_DESC to a new description of the tree conflict in
1762  * CONFLICT_SKEL.  If there is no tree conflict in CONFLICT_SKEL, return
1763  * an error.
1764  *
1765  * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1766  * rather than reading them from CONFLICT_SKEL.
1767  *
1768  * Allocate results in RESULT_POOL.  SCRATCH_POOL is used for temporary
1769  * allocations. */
1770 static svn_error_t *
1771 read_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
1772                         svn_wc__db_t *db,
1773                         const char *local_abspath,
1774                         svn_node_kind_t node_kind,
1775                         const svn_skel_t *conflict_skel,
1776                         svn_wc_operation_t operation,
1777                         const svn_wc_conflict_version_t *left_version,
1778                         const svn_wc_conflict_version_t *right_version,
1779                         apr_pool_t *result_pool,
1780                         apr_pool_t *scratch_pool)
1781 {
1782   svn_node_kind_t local_kind;
1783   svn_wc_conflict_reason_t reason;
1784   svn_wc_conflict_action_t action;
1785
1786   SVN_ERR(svn_wc__conflict_read_tree_conflict(
1787             &reason, &action, NULL,
1788             db, local_abspath, conflict_skel, scratch_pool, scratch_pool));
1789
1790   if (reason == svn_wc_conflict_reason_missing)
1791     local_kind = svn_node_none;
1792   else if (reason == svn_wc_conflict_reason_unversioned ||
1793            reason == svn_wc_conflict_reason_obstructed)
1794     SVN_ERR(svn_io_check_path(local_abspath, &local_kind, scratch_pool));
1795   else if (action == svn_wc_conflict_action_delete
1796            && left_version
1797            && (operation == svn_wc_operation_update
1798                ||operation == svn_wc_operation_switch)
1799            && (reason == svn_wc_conflict_reason_deleted
1800                || reason == svn_wc_conflict_reason_moved_away))
1801     {
1802       /* We have nothing locally to take the kind from */
1803       local_kind = left_version->node_kind;
1804     }
1805   else
1806     local_kind = node_kind;
1807
1808   *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind,
1809                                                    operation,
1810                                                    left_version, right_version,
1811                                                    result_pool);
1812   (*desc)->reason = reason;
1813   (*desc)->action = action;
1814
1815   return SVN_NO_ERROR;
1816 }
1817
1818 /* Forward definition */
1819 static svn_error_t *
1820 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
1821                               svn_wc__db_t *db,
1822                               const char *local_abspath,
1823                               const svn_skel_t *conflict,
1824                               svn_wc_conflict_choice_t conflict_choice,
1825                               apr_hash_t *resolve_later,
1826                               svn_wc_notify_func2_t notify_func,
1827                               void *notify_baton,
1828                               svn_cancel_func_t cancel_func,
1829                               void *cancel_baton,
1830                               apr_pool_t *scratch_pool);
1831
1832 svn_error_t *
1833 svn_wc__conflict_invoke_resolver(svn_wc__db_t *db,
1834                                  const char *local_abspath,
1835                                  svn_node_kind_t kind,
1836                                  const svn_skel_t *conflict_skel,
1837                                  const apr_array_header_t *merge_options,
1838                                  svn_wc_conflict_resolver_func2_t resolver_func,
1839                                  void *resolver_baton,
1840                                  svn_cancel_func_t cancel_func,
1841                                  void *cancel_baton,
1842                                  apr_pool_t *scratch_pool)
1843 {
1844   svn_boolean_t text_conflicted;
1845   svn_boolean_t prop_conflicted;
1846   svn_boolean_t tree_conflicted;
1847   svn_wc_operation_t operation;
1848   const apr_array_header_t *locations;
1849   const svn_wc_conflict_version_t *left_version = NULL;
1850   const svn_wc_conflict_version_t *right_version = NULL;
1851
1852   SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
1853                                      &text_conflicted, &prop_conflicted,
1854                                      &tree_conflicted,
1855                                      db, local_abspath, conflict_skel,
1856                                      scratch_pool, scratch_pool));
1857
1858   if (locations && locations->nelts > 0)
1859     left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
1860
1861   if (locations && locations->nelts > 1)
1862     right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
1863
1864   /* Quick and dirty compatibility wrapper. My guess would be that most resolvers
1865      would want to look at all properties at the same time.
1866
1867      ### svn currently only invokes this from the merge code to collect the list of
1868      ### conflicted paths. Eventually this code will be the base for 'svn resolve'
1869      ### and at that time the test coverage will improve
1870      */
1871   if (prop_conflicted)
1872     {
1873       apr_hash_t *old_props;
1874       apr_hash_t *mine_props;
1875       apr_hash_t *their_props;
1876       apr_hash_t *old_their_props;
1877       apr_hash_t *conflicted;
1878       apr_pool_t *iterpool;
1879       apr_hash_index_t *hi;
1880       svn_boolean_t mark_resolved = TRUE;
1881
1882       SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
1883                                                   &mine_props,
1884                                                   &old_their_props,
1885                                                   &their_props,
1886                                                   &conflicted,
1887                                                   db, local_abspath,
1888                                                   conflict_skel,
1889                                                   scratch_pool, scratch_pool));
1890
1891       if (operation == svn_wc_operation_merge)
1892         SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
1893                                                scratch_pool, scratch_pool));
1894       else
1895         old_props = old_their_props;
1896
1897       iterpool = svn_pool_create(scratch_pool);
1898
1899       for (hi = apr_hash_first(scratch_pool, conflicted);
1900            hi;
1901            hi = apr_hash_next(hi))
1902         {
1903           const char *propname = apr_hash_this_key(hi);
1904           svn_boolean_t conflict_remains = TRUE;
1905
1906           svn_pool_clear(iterpool);
1907
1908           if (cancel_func)
1909             SVN_ERR(cancel_func(cancel_baton));
1910
1911           SVN_ERR(generate_propconflict(&conflict_remains,
1912                                         db, local_abspath, kind,
1913                                         operation,
1914                                         left_version,
1915                                         right_version,
1916                                         propname,
1917                                         old_props
1918                                           ? svn_hash_gets(old_props, propname)
1919                                           : NULL,
1920                                         mine_props
1921                                           ? svn_hash_gets(mine_props, propname)
1922                                           : NULL,
1923                                         old_their_props
1924                                           ? svn_hash_gets(old_their_props, propname)
1925                                           : NULL,
1926                                         their_props
1927                                           ? svn_hash_gets(their_props, propname)
1928                                           : NULL,
1929                                         resolver_func, resolver_baton,
1930                                         cancel_func, cancel_baton,
1931                                         iterpool));
1932
1933           if (conflict_remains)
1934             mark_resolved = FALSE;
1935         }
1936
1937       if (mark_resolved)
1938         {
1939           SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
1940                                                        scratch_pool));
1941         }
1942       svn_pool_destroy(iterpool);
1943     }
1944
1945   if (text_conflicted)
1946     {
1947       svn_skel_t *work_items;
1948       svn_boolean_t was_resolved;
1949       svn_wc_conflict_description2_t *desc;
1950       apr_hash_t *props;
1951       svn_wc_conflict_result_t *result;
1952
1953       SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
1954                                     scratch_pool, scratch_pool));
1955
1956       SVN_ERR(read_text_conflict_desc(&desc,
1957                                       db, local_abspath, conflict_skel,
1958                                       svn_prop_get_value(props,
1959                                                          SVN_PROP_MIME_TYPE),
1960                                       operation, left_version, right_version,
1961                                       scratch_pool, scratch_pool));
1962
1963
1964       work_items = NULL;
1965       was_resolved = FALSE;
1966
1967       /* Give the conflict resolution callback a chance to clean
1968          up the conflicts before we mark the file 'conflicted' */
1969
1970       SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
1971                             scratch_pool));
1972       if (result == NULL)
1973         return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1974                                 _("Conflict callback violated API:"
1975                                   " returned no results"));
1976
1977       SVN_ERR(build_text_conflict_resolve_items(&work_items, &was_resolved,
1978                                                 db, local_abspath,
1979                                                 conflict_skel, result->choice,
1980                                                 result->merged_file,
1981                                                 result->save_merged,
1982                                                 merge_options,
1983                                                 cancel_func, cancel_baton,
1984                                                 scratch_pool, scratch_pool));
1985
1986       if (result->choice != svn_wc_conflict_choose_postpone)
1987         {
1988           SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
1989                                               TRUE, FALSE, FALSE,
1990                                               work_items, scratch_pool));
1991           SVN_ERR(svn_wc__wq_run(db, local_abspath,
1992                                  cancel_func, cancel_baton,
1993                                  scratch_pool));
1994         }
1995     }
1996
1997   if (tree_conflicted)
1998     {
1999       svn_wc_conflict_result_t *result;
2000       svn_wc_conflict_description2_t *desc;
2001       svn_boolean_t resolved;
2002       svn_node_kind_t node_kind;
2003
2004       SVN_ERR(svn_wc__db_read_kind(&node_kind, db, local_abspath, TRUE,
2005                                    TRUE, FALSE, scratch_pool));
2006
2007       SVN_ERR(read_tree_conflict_desc(&desc,
2008                                       db, local_abspath, node_kind,
2009                                       conflict_skel,
2010                                       operation, left_version, right_version,
2011                                       scratch_pool, scratch_pool));
2012
2013       /* Tell the resolver func about this conflict. */
2014       SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
2015                             scratch_pool));
2016
2017       if (result == NULL)
2018         return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2019                                 _("Conflict callback violated API:"
2020                                   " returned no results"));
2021
2022       /* Pass retry hash to avoid erroring out on cases where update
2023          can continue safely. ### Need notify handling */
2024       if (result->choice != svn_wc_conflict_choose_postpone)
2025         SVN_ERR(resolve_tree_conflict_on_node(&resolved,
2026                                               db, local_abspath, conflict_skel,
2027                                               result->choice,
2028                                               apr_hash_make(scratch_pool),
2029                                               NULL, NULL, /* ### notify */
2030                                               cancel_func, cancel_baton,
2031                                               scratch_pool));
2032     }
2033
2034   return SVN_NO_ERROR;
2035 }
2036
2037 /* Read all property conflicts contained in CONFLICT_SKEL into
2038  * individual conflict descriptions, and append those descriptions
2039  * to the CONFLICTS array.  If there is no property conflict in
2040  * CONFLICT_SKEL, return an error.
2041  *
2042  * If NOT create_tempfiles, always create a legacy property conflict
2043  * descriptor.
2044  *
2045  * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
2046  * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
2047  *
2048  * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
2049  * allocations. */
2050 static svn_error_t *
2051 read_prop_conflict_descs(apr_array_header_t *conflicts,
2052                          svn_wc__db_t *db,
2053                          const char *local_abspath,
2054                          svn_skel_t *conflict_skel,
2055                          svn_boolean_t create_tempfiles,
2056                          svn_node_kind_t node_kind,
2057                          svn_wc_operation_t operation,
2058                          const svn_wc_conflict_version_t *left_version,
2059                          const svn_wc_conflict_version_t *right_version,
2060                          apr_pool_t *result_pool,
2061                          apr_pool_t *scratch_pool)
2062 {
2063   const char *prop_reject_abspath;
2064   apr_hash_t *base_props;
2065   apr_hash_t *my_props;
2066   apr_hash_t *their_old_props;
2067   apr_hash_t *their_props;
2068   apr_hash_t *conflicted_props;
2069   apr_hash_index_t *hi;
2070   apr_pool_t *iterpool;
2071   svn_boolean_t prop_conflicted;
2072
2073   SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2074                                      NULL, db, local_abspath, conflict_skel,
2075                                      scratch_pool, scratch_pool));
2076
2077   if (!prop_conflicted)
2078     return SVN_NO_ERROR;
2079
2080   SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath,
2081                                               &my_props,
2082                                               &their_old_props,
2083                                               &their_props,
2084                                               &conflicted_props,
2085                                               db, local_abspath,
2086                                               conflict_skel,
2087                                               scratch_pool, scratch_pool));
2088
2089   prop_reject_abspath = apr_pstrdup(result_pool, prop_reject_abspath);
2090
2091   if (apr_hash_count(conflicted_props) == 0)
2092     {
2093       /* Legacy prop conflict with only a .reject file. */
2094       svn_wc_conflict_description2_t *desc;
2095
2096       desc  = svn_wc_conflict_description_create_prop2(local_abspath,
2097                                                        node_kind,
2098                                                        "", result_pool);
2099
2100       /* ### For property conflicts, cd2 stores prop_reject_abspath in
2101        * ### their_abspath, and stores theirs_abspath in merged_file. */
2102       desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2103       desc->their_abspath = desc->prop_reject_abspath;
2104
2105       desc->operation = operation;
2106       desc->src_left_version = left_version;
2107       desc->src_right_version = right_version;
2108
2109       APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2110
2111       return SVN_NO_ERROR;
2112     }
2113
2114   if (operation == svn_wc_operation_merge)
2115     SVN_ERR(svn_wc__db_read_pristine_props(&base_props, db, local_abspath,
2116                                            result_pool, scratch_pool));
2117   else
2118     base_props = NULL;
2119   iterpool = svn_pool_create(scratch_pool);
2120   for (hi = apr_hash_first(scratch_pool, conflicted_props);
2121        hi;
2122        hi = apr_hash_next(hi))
2123     {
2124       const char *propname = apr_hash_this_key(hi);
2125       svn_string_t *old_value;
2126       svn_string_t *my_value;
2127       svn_string_t *their_value;
2128       svn_wc_conflict_description2_t *desc;
2129
2130       svn_pool_clear(iterpool);
2131
2132       desc = svn_wc_conflict_description_create_prop2(local_abspath,
2133                                                       node_kind,
2134                                                       propname,
2135                                                       result_pool);
2136
2137       desc->operation = operation;
2138       desc->src_left_version = left_version;
2139       desc->src_right_version = right_version;
2140
2141       desc->property_name = apr_pstrdup(result_pool, propname);
2142
2143       my_value = svn_hash_gets(my_props, propname);
2144       their_value = svn_hash_gets(their_props, propname);
2145       old_value = svn_hash_gets(their_old_props, propname);
2146
2147       /* Compute the incoming side of the conflict ('action'). */
2148       if (their_value == NULL)
2149         desc->action = svn_wc_conflict_action_delete;
2150       else if (old_value == NULL)
2151         desc->action = svn_wc_conflict_action_add;
2152       else
2153         desc->action = svn_wc_conflict_action_edit;
2154
2155       /* Compute the local side of the conflict ('reason'). */
2156       if (my_value == NULL)
2157         desc->reason = svn_wc_conflict_reason_deleted;
2158       else if (old_value == NULL)
2159         desc->reason = svn_wc_conflict_reason_added;
2160       else
2161         desc->reason = svn_wc_conflict_reason_edited;
2162
2163       /* ### For property conflicts, cd2 stores prop_reject_abspath in
2164        * ### their_abspath, and stores theirs_abspath in merged_file. */
2165       desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2166       desc->their_abspath = desc->prop_reject_abspath;
2167
2168       desc->prop_value_base = base_props ? svn_hash_gets(base_props, propname)
2169                                          : desc->prop_value_incoming_old;
2170
2171       if (my_value)
2172         {
2173           svn_stream_t *s;
2174           apr_size_t len;
2175
2176           if (create_tempfiles)
2177             {
2178               SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL,
2179                                              svn_io_file_del_on_pool_cleanup,
2180                                              result_pool, iterpool));
2181               len = my_value->len;
2182               SVN_ERR(svn_stream_write(s, my_value->data, &len));
2183               SVN_ERR(svn_stream_close(s));
2184             }
2185
2186           desc->prop_value_working = svn_string_dup(my_value, result_pool);
2187         }
2188
2189       if (their_value)
2190         {
2191           svn_stream_t *s;
2192           apr_size_t len;
2193
2194           /* ### For property conflicts, cd2 stores prop_reject_abspath in
2195            * ### their_abspath, and stores theirs_abspath in merged_file. */
2196           if (create_tempfiles)
2197             {
2198               SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL,
2199                                              svn_io_file_del_on_pool_cleanup,
2200                                              result_pool, iterpool));
2201               len = their_value->len;
2202               SVN_ERR(svn_stream_write(s, their_value->data, &len));
2203               SVN_ERR(svn_stream_close(s));
2204             }
2205
2206           desc->prop_value_incoming_new = svn_string_dup(their_value, result_pool);
2207         }
2208
2209       if (old_value)
2210         {
2211           svn_stream_t *s;
2212           apr_size_t len;
2213
2214           if (create_tempfiles)
2215             {
2216               SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL,
2217                                              svn_io_file_del_on_pool_cleanup,
2218                                              result_pool, iterpool));
2219               len = old_value->len;
2220               SVN_ERR(svn_stream_write(s, old_value->data, &len));
2221               SVN_ERR(svn_stream_close(s));
2222             }
2223
2224           desc->prop_value_incoming_old = svn_string_dup(old_value, result_pool);
2225         }
2226
2227       APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2228     }
2229   svn_pool_destroy(iterpool);
2230
2231   return SVN_NO_ERROR;
2232 }
2233
2234 svn_error_t *
2235 svn_wc__read_conflicts(const apr_array_header_t **conflicts,
2236                        svn_skel_t **conflict_skel,
2237                        svn_wc__db_t *db,
2238                        const char *local_abspath,
2239                        svn_boolean_t create_tempfiles,
2240                        svn_boolean_t only_tree_conflict,
2241                        apr_pool_t *result_pool,
2242                        apr_pool_t *scratch_pool)
2243 {
2244   svn_skel_t *the_conflict_skel;
2245   apr_array_header_t *cflcts;
2246   svn_boolean_t prop_conflicted;
2247   svn_boolean_t text_conflicted;
2248   svn_boolean_t tree_conflicted;
2249   svn_wc_operation_t operation;
2250   const apr_array_header_t *locations;
2251   const svn_wc_conflict_version_t *left_version = NULL;
2252   const svn_wc_conflict_version_t *right_version = NULL;
2253   svn_node_kind_t node_kind;
2254   apr_hash_t *props;
2255
2256   if (!conflict_skel)
2257     conflict_skel = &the_conflict_skel;
2258
2259   SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props,
2260                                    db, local_abspath,
2261                                    (conflict_skel == &the_conflict_skel)
2262                                         ? scratch_pool
2263                                         : result_pool,
2264                                    scratch_pool));
2265
2266   if (!*conflict_skel)
2267     {
2268       /* Some callers expect not NULL */
2269       *conflicts = apr_array_make(result_pool, 0,
2270                                   sizeof(svn_wc_conflict_description2_t *));
2271       return SVN_NO_ERROR;
2272     }
2273
2274   SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted,
2275                                      &prop_conflicted, &tree_conflicted,
2276                                      db, local_abspath, *conflict_skel,
2277                                      result_pool, scratch_pool));
2278
2279   cflcts = apr_array_make(result_pool, 4,
2280                           sizeof(svn_wc_conflict_description2_t *));
2281
2282   if (locations && locations->nelts > 0)
2283     left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
2284   if (locations && locations->nelts > 1)
2285     right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
2286
2287   if (prop_conflicted && !only_tree_conflict)
2288     {
2289       SVN_ERR(read_prop_conflict_descs(cflcts,
2290                                        db, local_abspath, *conflict_skel,
2291                                        create_tempfiles, node_kind,
2292                                        operation, left_version, right_version,
2293                                        result_pool, scratch_pool));
2294     }
2295
2296   if (text_conflicted && !only_tree_conflict)
2297     {
2298       svn_wc_conflict_description2_t *desc;
2299
2300       SVN_ERR(read_text_conflict_desc(&desc,
2301                                       db, local_abspath, *conflict_skel,
2302                                       svn_prop_get_value(props,
2303                                                          SVN_PROP_MIME_TYPE),
2304                                       operation, left_version, right_version,
2305                                       result_pool, scratch_pool));
2306       APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t *) = desc;
2307     }
2308
2309   if (tree_conflicted)
2310     {
2311       svn_wc_conflict_description2_t *desc;
2312
2313       SVN_ERR(read_tree_conflict_desc(&desc,
2314                                       db, local_abspath, node_kind,
2315                                       *conflict_skel,
2316                                       operation, left_version, right_version,
2317                                       result_pool, scratch_pool));
2318
2319       APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc;
2320     }
2321
2322   *conflicts = cflcts;
2323   return SVN_NO_ERROR;
2324 }
2325
2326 \f
2327 /*** Resolving a conflict automatically ***/
2328
2329 /*
2330  * Resolve the property conflicts found in DB/LOCAL_ABSPATH according
2331  * to CONFLICT_CHOICE.
2332  *
2333  * It is not an error if there is no prop conflict. If a prop conflict
2334  * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2335  *
2336  * Note: When there are no conflict markers on-disk to remove there is
2337  * no existing text conflict (unless we are still in the process of
2338  * creating the text conflict and we didn't register a marker file yet).
2339  * In this case the database contains old information, which we should
2340  * remove to avoid checking the next time. Resolving a property conflict
2341  * by just removing the marker file is a fully supported scenario since
2342  * Subversion 1.0.
2343  *
2344  * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
2345  *     In my opinion, 'mine_full'/'theirs_full' should select
2346  *     the entire set of properties from 'mine' or 'theirs' respectively,
2347  *     while 'mine_conflict'/'theirs_conflict' should select just the
2348  *     properties that are in conflict.  Or, '_full' should select the
2349  *     entire property whereas '_conflict' should do a text merge within
2350  *     each property, selecting hunks.  Or all three kinds of behaviour
2351  *     should be available (full set of props, full value of conflicting
2352  *     props, or conflicting text hunks).
2353  * ### BH: If we make *_full select the full set of properties, we should
2354  *     check if we shouldn't make it also select the full text for files.
2355  *
2356  * ### TODO [JAF] All this complexity should not be down here in libsvn_wc
2357  *     but in a layer above.
2358  *
2359  * ### TODO [JAF] Options for 'base' should be like options for 'mine' and
2360  *     for 'theirs' -- choose full set of props, full value of conflicting
2361  *     props, or conflicting text hunks.
2362  *
2363  */
2364 static svn_error_t *
2365 resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
2366                               svn_wc__db_t *db,
2367                               const char *local_abspath,
2368                               svn_skel_t *conflicts,
2369                               const char *conflicted_propname,
2370                               svn_wc_conflict_choice_t conflict_choice,
2371                               const char *merged_file,
2372                               const svn_string_t *merged_value,
2373                               svn_cancel_func_t cancel_func,
2374                               void *cancel_baton,
2375                               apr_pool_t *scratch_pool)
2376 {
2377   const char *prop_reject_file;
2378   apr_hash_t *mine_props;
2379   apr_hash_t *their_old_props;
2380   apr_hash_t *their_props;
2381   apr_hash_t *conflicted_props;
2382   apr_hash_t *old_props;
2383   apr_hash_t *resolve_from = NULL;
2384   svn_skel_t *work_items = NULL;
2385   svn_wc_operation_t operation;
2386   svn_boolean_t prop_conflicted;
2387   apr_hash_t *actual_props;
2388   svn_boolean_t resolved_all, resolved_all_prop;
2389
2390   *did_resolve = FALSE;
2391
2392   SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2393                                      NULL, db, local_abspath, conflicts,
2394                                      scratch_pool, scratch_pool));
2395   if (!prop_conflicted)
2396     return SVN_NO_ERROR;
2397
2398   SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2399                                               &mine_props, &their_old_props,
2400                                               &their_props, &conflicted_props,
2401                                               db, local_abspath, conflicts,
2402                                               scratch_pool, scratch_pool));
2403
2404   if (!conflicted_props)
2405     {
2406       /* We have a pre 1.8 property conflict. Just mark it resolved */
2407
2408       SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2409                                              db, local_abspath, prop_reject_file,
2410                                              scratch_pool, scratch_pool));
2411       SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE,
2412                                       work_items, scratch_pool));
2413       SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2414                              scratch_pool));
2415       return SVN_NO_ERROR;
2416     }
2417
2418   if (conflicted_propname[0] != '\0'
2419       && !svn_hash_gets(conflicted_props, conflicted_propname))
2420     {
2421       return SVN_NO_ERROR; /* This property is not conflicted! */
2422     }
2423
2424   if (operation == svn_wc_operation_merge)
2425       SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
2426                                              scratch_pool, scratch_pool));
2427     else
2428       old_props = their_old_props;
2429
2430   SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2431                                 scratch_pool, scratch_pool));
2432
2433   /* We currently handle *_conflict as *_full as this argument is currently
2434      always applied for all conflicts on a node at the same time. Giving
2435      an error would break some tests that assumed that this would just
2436      resolve property conflicts to working.
2437
2438      An alternative way to handle these conflicts would be to just copy all
2439      property state from mine/theirs on the _full option instead of just the
2440      conflicted properties. In some ways this feels like a sensible option as
2441      that would take both properties and text from mine/theirs, but when not
2442      both properties and text are conflicted we would fail in doing so.
2443    */
2444   switch (conflict_choice)
2445     {
2446     case svn_wc_conflict_choose_base:
2447       resolve_from = their_old_props ? their_old_props : old_props;
2448       break;
2449     case svn_wc_conflict_choose_mine_full:
2450     case svn_wc_conflict_choose_mine_conflict:
2451       resolve_from = mine_props;
2452       break;
2453     case svn_wc_conflict_choose_theirs_full:
2454     case svn_wc_conflict_choose_theirs_conflict:
2455       resolve_from = their_props;
2456       break;
2457     case svn_wc_conflict_choose_merged:
2458       if ((merged_file || merged_value) && conflicted_propname[0] != '\0')
2459         {
2460           resolve_from = apr_hash_copy(scratch_pool, actual_props);
2461
2462           if (!merged_value)
2463             {
2464               svn_stream_t *stream;
2465               svn_string_t *merged_propval;
2466
2467               SVN_ERR(svn_stream_open_readonly(&stream, merged_file,
2468                                                scratch_pool, scratch_pool));
2469               SVN_ERR(svn_string_from_stream(&merged_propval, stream,
2470                                              scratch_pool, scratch_pool));
2471
2472               merged_value = merged_propval;
2473             }
2474           svn_hash_sets(resolve_from, conflicted_propname, merged_value);
2475         }
2476       else
2477         resolve_from = NULL;
2478       break;
2479     default:
2480       return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2481                               _("Invalid 'conflict_result' argument"));
2482     }
2483
2484
2485   if (resolve_from)
2486     {
2487       apr_hash_index_t *hi;
2488       apr_hash_t *apply_on_props;
2489
2490       if (conflicted_propname[0] == '\0')
2491         {
2492           /* Apply to all conflicted properties */
2493           apply_on_props = conflicted_props;
2494         }
2495       else
2496         {
2497           /* Apply to a single property */
2498           apply_on_props = apr_hash_make(scratch_pool);
2499           svn_hash_sets(apply_on_props, conflicted_propname, "");
2500         }
2501
2502       /* Apply the selected changes */
2503       for (hi = apr_hash_first(scratch_pool, apply_on_props);
2504            hi;
2505            hi = apr_hash_next(hi))
2506         {
2507           const char *propname = apr_hash_this_key(hi);
2508           svn_string_t *new_value = NULL;
2509
2510           new_value = svn_hash_gets(resolve_from, propname);
2511
2512           svn_hash_sets(actual_props, propname, new_value);
2513         }
2514     }
2515   /*else the user accepted the properties as-is */
2516
2517   /* This function handles conflicted_propname "" as resolving
2518      all property conflicts... Just what we need here */
2519   SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
2520                                         db, local_abspath,
2521                                         FALSE, conflicted_propname,
2522                                         FALSE,
2523                                         scratch_pool, scratch_pool));
2524
2525   if (!resolved_all)
2526     {
2527       /* Are there still property conflicts left? (or only...) */
2528       SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, &prop_conflicted,
2529                                          NULL, db, local_abspath, conflicts,
2530                                          scratch_pool, scratch_pool));
2531
2532       resolved_all_prop = (! prop_conflicted);
2533     }
2534   else
2535     {
2536       resolved_all_prop = TRUE;
2537       conflicts = NULL;
2538     }
2539
2540   if (resolved_all_prop)
2541     {
2542       /* Legacy behavior: Only report property conflicts as resolved when the
2543          property reject file exists
2544
2545          If not the UI shows the conflict as already resolved
2546          (and in this case we just remove the in-db conflict) */
2547       SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2548                                              db, local_abspath,
2549                                              prop_reject_file,
2550                                              scratch_pool, scratch_pool));
2551     }
2552   else
2553     {
2554       /* Create a new prej file, based on the remaining conflicts */
2555       SVN_ERR(svn_wc__wq_build_prej_install(&work_items,
2556                                             db, local_abspath,
2557                                             scratch_pool, scratch_pool));
2558       *did_resolve = TRUE; /* We resolved a property conflict */
2559     }
2560
2561   /* This installs the updated conflict skel */
2562   SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props,
2563                                   FALSE, conflicts, work_items,
2564                                   scratch_pool));
2565
2566   if (resolved_all)
2567     {
2568       /* Remove the whole conflict. Should probably be integrated
2569          into the op_set_props() call */
2570       SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2571                                           FALSE, TRUE, FALSE,
2572                                           NULL, scratch_pool));
2573     }
2574
2575   SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2576                          scratch_pool));
2577
2578   return SVN_NO_ERROR;
2579 }
2580
2581 /*
2582  * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
2583  * CONFLICT_CHOICE.
2584  *
2585  * It is not an error if there is no tree conflict. If a tree conflict
2586  * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2587  *
2588  * It is not an error if there is no tree conflict.
2589  *
2590  * If the conflict can't be resolved yet because another tree conflict is
2591  * blocking a storage location, store the tree conflict in the RESOLVE_LATER
2592  * hash.
2593  */
2594 static svn_error_t *
2595 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
2596                               svn_wc__db_t *db,
2597                               const char *local_abspath,
2598                               const svn_skel_t *conflicts,
2599                               svn_wc_conflict_choice_t conflict_choice,
2600                               apr_hash_t *resolve_later,
2601                               svn_wc_notify_func2_t notify_func,
2602                               void *notify_baton,
2603                               svn_cancel_func_t cancel_func,
2604                               void *cancel_baton,
2605                               apr_pool_t *scratch_pool)
2606 {
2607   svn_wc_conflict_reason_t reason;
2608   svn_wc_conflict_action_t action;
2609   svn_wc_operation_t operation;
2610   svn_boolean_t tree_conflicted;
2611   const char *src_op_root_abspath;
2612
2613   *did_resolve = FALSE;
2614
2615   SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
2616                                      &tree_conflicted, db, local_abspath,
2617                                      conflicts, scratch_pool, scratch_pool));
2618   if (!tree_conflicted)
2619     return SVN_NO_ERROR;
2620
2621   SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2622                                               &src_op_root_abspath,
2623                                               db, local_abspath,
2624                                               conflicts,
2625                                               scratch_pool, scratch_pool));
2626
2627   if (operation == svn_wc_operation_update
2628       || operation == svn_wc_operation_switch)
2629     {
2630       svn_error_t *err;
2631       if (reason == svn_wc_conflict_reason_deleted ||
2632           reason == svn_wc_conflict_reason_replaced)
2633         {
2634           if (conflict_choice == svn_wc_conflict_choose_merged)
2635             {
2636               /* Break moves for any children moved out of this directory,
2637                * and leave this directory deleted. */
2638
2639               if (action != svn_wc_conflict_action_delete)
2640                 {
2641                   SVN_ERR(svn_wc__db_op_break_moved_away(
2642                                   db, local_abspath, src_op_root_abspath, TRUE,
2643                                   notify_func, notify_baton,
2644                                   scratch_pool));
2645                   *did_resolve = TRUE;
2646                   return SVN_NO_ERROR; /* Marked resolved by function*/
2647                 }
2648               /* else # The move is/moves are already broken */
2649
2650
2651               *did_resolve = TRUE;
2652             }
2653           else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2654             {
2655               svn_skel_t *new_conflicts;
2656
2657               /* Raise moved-away conflicts on any children moved out of
2658                * this directory, and leave this directory as-is.
2659                *
2660                * The newly conflicted moved-away children will be updated
2661                * if they are resolved with 'mine_conflict' as well. */
2662               err = svn_wc__db_op_raise_moved_away(
2663                         db, local_abspath, notify_func, notify_baton,
2664                         scratch_pool);
2665
2666               if (err)
2667                 {
2668                   const char *dup_abspath;
2669
2670                   if (!resolve_later
2671                       || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
2672                     return svn_error_trace(err);
2673
2674                   svn_error_clear(err);
2675                   dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2676                                             local_abspath);
2677
2678                   svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2679
2680                   return SVN_NO_ERROR; /* Retry after other conflicts */
2681                 }
2682
2683               /* We might now have a moved-away on *this* path, let's
2684                  try to resolve that directly if that is the case */
2685               SVN_ERR(svn_wc__db_read_conflict(&new_conflicts, NULL, NULL,
2686                                                db, local_abspath,
2687                                                scratch_pool, scratch_pool));
2688
2689               if (new_conflicts)
2690                 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
2691                                                    &tree_conflicted,
2692                                                    db, local_abspath,
2693                                                    new_conflicts,
2694                                                    scratch_pool,
2695                                                    scratch_pool));
2696
2697               if (!new_conflicts || !tree_conflicted)
2698                 {
2699                   /* TC is marked resolved by calling
2700                      svn_wc__db_resolve_delete_raise_moved_away */
2701                   *did_resolve = TRUE;
2702                   return SVN_NO_ERROR;
2703                 }
2704
2705               SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2706                                                           &src_op_root_abspath,
2707                                                           db, local_abspath,
2708                                                           new_conflicts,
2709                                                           scratch_pool,
2710                                                           scratch_pool));
2711
2712               if (reason != svn_wc_conflict_reason_moved_away)
2713                 {
2714                   *did_resolve = TRUE;
2715                   return SVN_NO_ERROR; /* We fixed one, but... */
2716                 }
2717
2718               conflicts = new_conflicts;
2719               /* Fall through in moved_away handling */
2720             }
2721           else
2722             return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2723                                      NULL,
2724                                      _("Tree conflict can only be resolved to "
2725                                        "'working' or 'mine-conflict' state; "
2726                                        "'%s' not resolved"),
2727                                      svn_dirent_local_style(local_abspath,
2728                                                             scratch_pool));
2729         }
2730
2731       if (reason == svn_wc_conflict_reason_moved_away
2732            && action == svn_wc_conflict_action_edit)
2733         {
2734           /* After updates, we can resolve local moved-away
2735            * vs. any incoming change, either by updating the
2736            * moved-away node (mine-conflict) or by breaking the
2737            * move (theirs-conflict). */
2738           if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2739             {
2740               err = svn_wc__db_update_moved_away_conflict_victim(
2741                         db, local_abspath, src_op_root_abspath,
2742                         operation, action, reason,
2743                         cancel_func, cancel_baton,
2744                         notify_func, notify_baton,
2745                         scratch_pool);
2746
2747               if (err)
2748                 {
2749                   const char *dup_abspath;
2750
2751                   if (!resolve_later
2752                       || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
2753                     return svn_error_trace(err);
2754
2755                   svn_error_clear(err);
2756                   dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2757                                             local_abspath);
2758
2759                   svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2760
2761                   return SVN_NO_ERROR; /* Retry after other conflicts */
2762                 }
2763               else
2764                 *did_resolve = TRUE;
2765             }
2766           else if (conflict_choice == svn_wc_conflict_choose_merged)
2767             {
2768               /* We must break the move if the user accepts the current
2769                * working copy state instead of updating the move.
2770                * Else the move would be left in an invalid state. */
2771
2772               SVN_ERR(svn_wc__db_op_break_moved_away(db, local_abspath,
2773                                                      src_op_root_abspath, TRUE,
2774                                                      notify_func, notify_baton,
2775                                                      scratch_pool));
2776               *did_resolve = TRUE;
2777               return SVN_NO_ERROR; /* Conflict is marked resolved */
2778             }
2779           else
2780             return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2781                                      NULL,
2782                                      _("Tree conflict can only be resolved to "
2783                                        "'working' or 'mine-conflict' state; "
2784                                        "'%s' not resolved"),
2785                                      svn_dirent_local_style(local_abspath,
2786                                                             scratch_pool));
2787         }
2788       else if (reason == svn_wc_conflict_reason_moved_away
2789                && action != svn_wc_conflict_action_edit)
2790         {
2791           /* action added is impossible, because that would imply that
2792              something was added, but before that already moved...
2793              (which would imply a replace) */
2794           SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete
2795                          || action == svn_wc_conflict_action_replace);
2796
2797           if (conflict_choice == svn_wc_conflict_choose_merged)
2798             {
2799               /* Whatever was moved is removed at its original location by the
2800                  update. That must also remove the recording of the move, so
2801                  we don't have to do anything here. */
2802
2803               *did_resolve = TRUE;
2804             }
2805           else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2806             {
2807               return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2808                                        NULL,
2809                                        _("Tree conflict can only be "
2810                                          "resolved to 'working' state; "
2811                                          "'%s' is no longer moved"),
2812                                        svn_dirent_local_style(local_abspath,
2813                                                               scratch_pool));
2814             }
2815         }
2816     }
2817
2818   if (! *did_resolve)
2819     {
2820       if (conflict_choice != svn_wc_conflict_choose_merged)
2821         {
2822           /* For other tree conflicts, there is no way to pick
2823            * theirs-full or mine-full, etc. Throw an error if the
2824            * user expects us to be smarter than we really are. */
2825           return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2826                                    NULL,
2827                                    _("Tree conflict can only be "
2828                                      "resolved to 'working' state; "
2829                                      "'%s' not resolved"),
2830                                    svn_dirent_local_style(local_abspath,
2831                                                           scratch_pool));
2832         }
2833       else
2834         *did_resolve = TRUE;
2835     }
2836
2837   SVN_ERR_ASSERT(*did_resolve);
2838
2839   SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE,
2840                                       NULL, scratch_pool));
2841   SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2842                          scratch_pool));
2843   return SVN_NO_ERROR;
2844 }
2845
2846 svn_error_t *
2847 svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
2848                                     const char *local_abspath,
2849                                     svn_cancel_func_t cancel_func,
2850                                     void *cancel_baton,
2851                                     apr_pool_t *scratch_pool)
2852 {
2853   svn_skel_t *work_items;
2854   svn_skel_t *conflict;
2855
2856   SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
2857                                    db, local_abspath,
2858                                    scratch_pool, scratch_pool));
2859
2860   if (!conflict)
2861     return SVN_NO_ERROR;
2862
2863   SVN_ERR(build_text_conflict_resolve_items(&work_items, NULL,
2864                                             db, local_abspath, conflict,
2865                                             svn_wc_conflict_choose_merged,
2866                                             NULL, FALSE, NULL,
2867                                             cancel_func, cancel_baton,
2868                                             scratch_pool, scratch_pool));
2869
2870   SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, TRUE, FALSE, FALSE,
2871                                       work_items, scratch_pool));
2872
2873   return svn_error_trace(svn_wc__wq_run(db, local_abspath,
2874                                         cancel_func, cancel_baton,
2875                                         scratch_pool));
2876 }
2877
2878 svn_error_t *
2879 svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db,
2880                                      const char *local_abspath,
2881                                      apr_pool_t *scratch_pool)
2882 {
2883   svn_boolean_t ignored_result;
2884   svn_skel_t *conflicts;
2885
2886   SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
2887                                    db, local_abspath,
2888                                    scratch_pool, scratch_pool));
2889
2890   if (!conflicts)
2891     return SVN_NO_ERROR;
2892
2893   return svn_error_trace(resolve_prop_conflict_on_node(
2894                            &ignored_result,
2895                            db, local_abspath, conflicts, "",
2896                            svn_wc_conflict_choose_merged,
2897                            NULL, NULL,
2898                            NULL, NULL,
2899                            scratch_pool));
2900 }
2901
2902
2903 /* Baton for conflict_status_walker */
2904 struct conflict_status_walker_baton
2905 {
2906   svn_wc__db_t *db;
2907   svn_boolean_t resolve_text;
2908   const char *resolve_prop;
2909   svn_boolean_t resolve_tree;
2910   svn_wc_conflict_choice_t conflict_choice;
2911   svn_wc_conflict_resolver_func2_t conflict_func;
2912   void *conflict_baton;
2913   svn_cancel_func_t cancel_func;
2914   void *cancel_baton;
2915   svn_wc_notify_func2_t notify_func;
2916   void *notify_baton;
2917   svn_boolean_t resolved_one;
2918   apr_hash_t *resolve_later;
2919 };
2920
2921 /* Implements svn_wc_notify_func2_t to collect new conflicts caused by
2922    resolving a tree conflict. */
2923 static void
2924 tree_conflict_collector(void *baton,
2925                         const svn_wc_notify_t *notify,
2926                         apr_pool_t *pool)
2927 {
2928   struct conflict_status_walker_baton *cswb = baton;
2929
2930   if (cswb->notify_func)
2931     cswb->notify_func(cswb->notify_baton, notify, pool);
2932
2933   if (cswb->resolve_later
2934       && (notify->action == svn_wc_notify_tree_conflict
2935           || notify->prop_state == svn_wc_notify_state_conflicted
2936           || notify->content_state == svn_wc_notify_state_conflicted))
2937     {
2938       if (!svn_hash_gets(cswb->resolve_later, notify->path))
2939         {
2940           const char *dup_path;
2941
2942           dup_path = apr_pstrdup(apr_hash_pool_get(cswb->resolve_later),
2943                                  notify->path);
2944
2945           svn_hash_sets(cswb->resolve_later, dup_path, dup_path);
2946         }
2947     }
2948 }
2949
2950 /* Implements svn_wc_status4_t to walk all conflicts to resolve.
2951  */
2952 static svn_error_t *
2953 conflict_status_walker(void *baton,
2954                        const char *local_abspath,
2955                        const svn_wc_status3_t *status,
2956                        apr_pool_t *scratch_pool)
2957 {
2958   struct conflict_status_walker_baton *cswb = baton;
2959   svn_wc__db_t *db = cswb->db;
2960
2961   const apr_array_header_t *conflicts;
2962   apr_pool_t *iterpool;
2963   int i;
2964   svn_boolean_t resolved = FALSE;
2965   svn_skel_t *conflict;
2966
2967   if (!status->conflicted)
2968     return SVN_NO_ERROR;
2969
2970   iterpool = svn_pool_create(scratch_pool);
2971
2972   SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict,
2973                                  db, local_abspath,
2974                                  (cswb->conflict_func != NULL) /* tmp files */,
2975                                  FALSE /* only tree conflicts */,
2976                                  scratch_pool, iterpool));
2977
2978   for (i = 0; i < conflicts->nelts; i++)
2979     {
2980       const svn_wc_conflict_description2_t *cd;
2981       svn_boolean_t did_resolve;
2982       svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
2983       svn_wc_conflict_result_t *result = NULL;
2984       svn_skel_t *work_items;
2985
2986       cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
2987
2988       if ((cd->kind == svn_wc_conflict_kind_property
2989            && (!cswb->resolve_prop
2990                || (*cswb->resolve_prop != '\0'
2991                    && strcmp(cswb->resolve_prop, cd->property_name) != 0)))
2992           || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text)
2993           || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree))
2994         {
2995           continue; /* Easy out. Don't call resolver func and ignore result */
2996         }
2997
2998       svn_pool_clear(iterpool);
2999
3000       if (my_choice == svn_wc_conflict_choose_unspecified)
3001         {
3002           if (!cswb->conflict_func)
3003             return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3004                                     _("No conflict-callback and no "
3005                                       "pre-defined conflict-choice provided"));
3006
3007           SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton,
3008                                       iterpool, iterpool));
3009
3010           my_choice = result->choice;
3011         }
3012
3013
3014       if (my_choice == svn_wc_conflict_choose_postpone)
3015         continue;
3016
3017       switch (cd->kind)
3018         {
3019           case svn_wc_conflict_kind_tree:
3020             SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
3021                                                   db,
3022                                                   local_abspath, conflict,
3023                                                   my_choice,
3024                                                   cswb->resolve_later,
3025                                                   tree_conflict_collector,
3026                                                   cswb,
3027                                                   cswb->cancel_func,
3028                                                   cswb->cancel_baton,
3029                                                   iterpool));
3030
3031             if (did_resolve)
3032               resolved = TRUE;
3033             break;
3034
3035           case svn_wc_conflict_kind_text:
3036             SVN_ERR(build_text_conflict_resolve_items(
3037                                         &work_items,
3038                                         &resolved,
3039                                         db, local_abspath, conflict,
3040                                         my_choice,
3041                                         result ? result->merged_file
3042                                                : NULL,
3043                                         result ? result->save_merged
3044                                                : FALSE,
3045                                         NULL /* merge_options */,
3046                                         cswb->cancel_func,
3047                                         cswb->cancel_baton,
3048                                         iterpool, iterpool));
3049
3050             SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
3051                                                 TRUE, FALSE, FALSE,
3052                                                 work_items, iterpool));
3053             SVN_ERR(svn_wc__wq_run(db, local_abspath,
3054                                    cswb->cancel_func, cswb->cancel_baton,
3055                                    iterpool));
3056             break;
3057
3058           case svn_wc_conflict_kind_property:
3059             SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
3060                                                   db,
3061                                                   local_abspath,
3062                                                   conflict,
3063                                                   cd->property_name,
3064                                                   my_choice,
3065                                                   result
3066                                                     ? result->merged_file
3067                                                     : NULL,
3068                                                   result
3069                                                     ? result->merged_value
3070                                                     : NULL,
3071                                                   cswb->cancel_func,
3072                                                   cswb->cancel_baton,
3073                                                   iterpool));
3074
3075             if (did_resolve)
3076               resolved = TRUE;
3077             break;
3078
3079           default:
3080             /* We can't resolve other conflict types */
3081             break;
3082         }
3083     }
3084
3085   /* Notify */
3086   if (cswb->notify_func && resolved)
3087     cswb->notify_func(cswb->notify_baton,
3088                       svn_wc_create_notify(local_abspath,
3089                                            svn_wc_notify_resolved,
3090                                            iterpool),
3091                       iterpool);
3092
3093   if (resolved)
3094     cswb->resolved_one = TRUE;
3095
3096   svn_pool_destroy(iterpool);
3097
3098   return SVN_NO_ERROR;
3099 }
3100
3101 svn_error_t *
3102 svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
3103                           const char *local_abspath,
3104                           svn_depth_t depth,
3105                           svn_boolean_t resolve_text,
3106                           const char *resolve_prop,
3107                           svn_boolean_t resolve_tree,
3108                           svn_wc_conflict_choice_t conflict_choice,
3109                           svn_wc_conflict_resolver_func2_t conflict_func,
3110                           void *conflict_baton,
3111                           svn_cancel_func_t cancel_func,
3112                           void *cancel_baton,
3113                           svn_wc_notify_func2_t notify_func,
3114                           void *notify_baton,
3115                           apr_pool_t *scratch_pool)
3116 {
3117   svn_node_kind_t kind;
3118   svn_boolean_t conflicted;
3119   struct conflict_status_walker_baton cswb;
3120   apr_pool_t *iterpool = NULL;
3121   svn_error_t *err;
3122
3123   /* ### Just a versioned check? */
3124   /* Conflicted is set to allow invoking on actual only nodes */
3125   SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
3126                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3127                                NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
3128                                NULL, NULL, NULL, NULL, NULL, NULL,
3129                                wc_ctx->db, local_abspath,
3130                                scratch_pool, scratch_pool));
3131
3132   /* When the implementation still used the entry walker, depth
3133      unknown was translated to infinity. */
3134   if (kind != svn_node_dir)
3135     depth = svn_depth_empty;
3136   else if (depth == svn_depth_unknown)
3137     depth = svn_depth_infinity;
3138
3139   cswb.db = wc_ctx->db;
3140   cswb.resolve_text = resolve_text;
3141   cswb.resolve_prop = resolve_prop;
3142   cswb.resolve_tree = resolve_tree;
3143   cswb.conflict_choice = conflict_choice;
3144
3145   cswb.conflict_func = conflict_func;
3146   cswb.conflict_baton = conflict_baton;
3147
3148   cswb.cancel_func = cancel_func;
3149   cswb.cancel_baton = cancel_baton;
3150
3151   cswb.notify_func = notify_func;
3152   cswb.notify_baton = notify_baton;
3153
3154   cswb.resolved_one = FALSE;
3155   cswb.resolve_later = (depth != svn_depth_empty)
3156                           ? apr_hash_make(scratch_pool)
3157                           : NULL;
3158
3159   if (notify_func)
3160     notify_func(notify_baton,
3161                 svn_wc_create_notify(local_abspath,
3162                                     svn_wc_notify_conflict_resolver_starting,
3163                                     scratch_pool),
3164                 scratch_pool);
3165
3166   err = svn_wc_walk_status(wc_ctx,
3167                            local_abspath,
3168                            depth,
3169                            FALSE /* get_all */,
3170                            FALSE /* no_ignore */,
3171                            TRUE /* ignore_text_mods */,
3172                            NULL /* ignore_patterns */,
3173                            conflict_status_walker, &cswb,
3174                            cancel_func, cancel_baton,
3175                            scratch_pool);
3176
3177   /* If we got new tree conflicts (or delayed conflicts) during the initial
3178      walk, we now walk them one by one as closure. */
3179   while (!err && cswb.resolve_later && apr_hash_count(cswb.resolve_later))
3180     {
3181       apr_hash_index_t *hi;
3182       svn_wc_status3_t *status = NULL;
3183       const char *tc_abspath = NULL;
3184
3185       if (iterpool)
3186         svn_pool_clear(iterpool);
3187       else
3188         iterpool = svn_pool_create(scratch_pool);
3189
3190       hi = apr_hash_first(scratch_pool, cswb.resolve_later);
3191       cswb.resolve_later = apr_hash_make(scratch_pool);
3192       cswb.resolved_one = FALSE;
3193
3194       for (; hi && !err; hi = apr_hash_next(hi))
3195         {
3196           const char *relpath;
3197           svn_pool_clear(iterpool);
3198
3199           tc_abspath = apr_hash_this_key(hi);
3200
3201           if (cancel_func)
3202             SVN_ERR(cancel_func(cancel_baton));
3203
3204           relpath = svn_dirent_skip_ancestor(local_abspath,
3205                                              tc_abspath);
3206
3207           if (!relpath
3208               || (depth >= svn_depth_empty
3209                   && depth < svn_depth_infinity
3210                   && strchr(relpath, '/')))
3211             {
3212               continue;
3213             }
3214
3215           SVN_ERR(svn_wc_status3(&status, wc_ctx, tc_abspath,
3216                                  iterpool, iterpool));
3217
3218           if (depth == svn_depth_files
3219               && status->kind == svn_node_dir)
3220             continue;
3221
3222           err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3223                                                        status, scratch_pool));
3224         }
3225
3226       /* None of the remaining conflicts got resolved, and non did provide
3227          an error...
3228
3229          We can fix that if we disable the 'resolve_later' option...
3230        */
3231       if (!cswb.resolved_one && !err && tc_abspath
3232           && apr_hash_count(cswb.resolve_later))
3233         {
3234           /* Run the last resolve operation again. We still have status
3235              and tc_abspath for that one. */
3236
3237           cswb.resolve_later = NULL; /* Produce proper error! */
3238
3239           /* Recreate the error */
3240           err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3241                                                        status, scratch_pool));
3242
3243           SVN_ERR_ASSERT(err != NULL);
3244
3245           err = svn_error_createf(
3246                     SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3247                     _("Unable to resolve pending conflict on '%s'"),
3248                     svn_dirent_local_style(tc_abspath, scratch_pool));
3249           break;
3250         }
3251     }
3252
3253   if (iterpool)
3254     svn_pool_destroy(iterpool);
3255
3256   if (err && err->apr_err != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE)
3257     err = svn_error_createf(
3258                 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3259                 _("Unable to resolve conflicts on '%s'"),
3260                 svn_dirent_local_style(local_abspath, scratch_pool));
3261
3262   SVN_ERR(err);
3263
3264   if (notify_func)
3265     notify_func(notify_baton,
3266                 svn_wc_create_notify(local_abspath,
3267                                     svn_wc_notify_conflict_resolver_done,
3268                                     scratch_pool),
3269                 scratch_pool);
3270
3271   return SVN_NO_ERROR;
3272 }
3273
3274 svn_error_t *
3275 svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
3276                           const char *local_abspath,
3277                           svn_depth_t depth,
3278                           svn_boolean_t resolve_text,
3279                           const char *resolve_prop,
3280                           svn_boolean_t resolve_tree,
3281                           svn_wc_conflict_choice_t conflict_choice,
3282                           svn_cancel_func_t cancel_func,
3283                           void *cancel_baton,
3284                           svn_wc_notify_func2_t notify_func,
3285                           void *notify_baton,
3286                           apr_pool_t *scratch_pool)
3287 {
3288   return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath,
3289                                                    depth, resolve_text,
3290                                                    resolve_prop, resolve_tree,
3291                                                    conflict_choice,
3292                                                    NULL, NULL,
3293                                                    cancel_func, cancel_baton,
3294                                                    notify_func, notify_baton,
3295                                                    scratch_pool));
3296 }
3297
3298 /* Constructor for the result-structure returned by conflict callbacks. */
3299 svn_wc_conflict_result_t *
3300 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
3301                               const char *merged_file,
3302                               apr_pool_t *pool)
3303 {
3304   svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
3305   result->choice = choice;
3306   result->merged_file = apr_pstrdup(pool, merged_file);
3307   result->save_merged = FALSE;
3308
3309   /* If we add more fields to svn_wc_conflict_result_t, add them here. */
3310
3311   return result;
3312 }