]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_wc/conflicts.c
Update from subversion 1.9.4 to 1.9.5.
[FreeBSD/FreeBSD.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           /* In case of selecting to resolve the conflict choosing the full
1626              own file, allow the text conflict resolution to just take the
1627              existing local file if no merged file was present (case: binary
1628              file conflicts do not generate a locally merge file).
1629           */
1630           install_from_abspath = mine_abspath
1631                                    ? mine_abspath
1632                                    : local_abspath;
1633           break;
1634         }
1635       case svn_wc_conflict_choose_theirs_conflict:
1636       case svn_wc_conflict_choose_mine_conflict:
1637         {
1638           svn_diff_conflict_display_style_t style
1639             = choice == svn_wc_conflict_choose_theirs_conflict
1640                 ? svn_diff_conflict_display_latest
1641                 : svn_diff_conflict_display_modified;
1642
1643           if (mine_abspath == NULL)
1644             return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1645                                      _("Conflict on '%s' cannot be resolved to "
1646                                        "'theirs-conflict' or 'mine-conflict' "
1647                                        "because a merged version of the file "
1648                                        "cannot be created."),
1649                                      svn_dirent_local_style(local_abspath,
1650                                                             scratch_pool));
1651
1652           SVN_ERR(merge_showing_conflicts(&install_from_abspath,
1653                                           db, local_abspath,
1654                                           style, merge_options,
1655                                           their_old_abspath,
1656                                           mine_abspath,
1657                                           their_abspath,
1658                                           /* ### why not same as other caller? */
1659                                           svn_io_file_del_none,
1660                                           cancel_func, cancel_baton,
1661                                           scratch_pool, scratch_pool));
1662           remove_source = TRUE;
1663           break;
1664         }
1665
1666         /* For the case of 3-way file merging, we don't
1667            really distinguish between these return values;
1668            if the callback claims to have "generally
1669            resolved" the situation, we still interpret
1670            that as "OK, we'll assume the merged version is
1671            good to use". */
1672       case svn_wc_conflict_choose_merged:
1673         {
1674           install_from_abspath = merged_file
1675                                   ? merged_file
1676                                   : local_abspath;
1677           break;
1678         }
1679       case svn_wc_conflict_choose_postpone:
1680         {
1681           /* Assume conflict remains. */
1682           return SVN_NO_ERROR;
1683         }
1684       default:
1685         SVN_ERR_ASSERT(choice == svn_wc_conflict_choose_postpone);
1686     }
1687
1688   if (install_from_abspath == NULL)
1689     return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1690                              _("Conflict on '%s' could not be resolved "
1691                                "because the chosen version of the file "
1692                                "is not available."),
1693                              svn_dirent_local_style(local_abspath,
1694                                                     scratch_pool));
1695
1696   /* ### It would be nice if we could somehow pass RECORD_FILEINFO
1697          as true in some easy cases. */
1698   SVN_ERR(svn_wc__wq_build_file_install(&work_item,
1699                                         db, local_abspath,
1700                                         install_from_abspath,
1701                                         FALSE /* use_commit_times */,
1702                                         FALSE /* record_fileinfo */,
1703                                         result_pool, scratch_pool));
1704   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1705
1706   if (remove_source)
1707     {
1708       SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
1709                                            db, local_abspath,
1710                                            install_from_abspath,
1711                                            result_pool, scratch_pool));
1712       *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1713     }
1714
1715   SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1716                                          db, local_abspath,
1717                                          their_old_abspath,
1718                                          result_pool, scratch_pool));
1719   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1720
1721   SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1722                                          db, local_abspath,
1723                                          their_abspath,
1724                                          result_pool, scratch_pool));
1725   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1726
1727   SVN_ERR(remove_artifact_file_if_exists(&work_item, found_artifact,
1728                                          db, local_abspath,
1729                                          mine_abspath,
1730                                          result_pool, scratch_pool));
1731   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1732
1733   return SVN_NO_ERROR;
1734 }
1735
1736
1737 /* Set *DESC to a new description of the text conflict in
1738  * CONFLICT_SKEL.  If there is no text conflict in CONFLICT_SKEL, return
1739  * an error.
1740  *
1741  * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1742  * rather than reading them from CONFLICT_SKEL.  Use IS_BINARY and
1743  * MIME_TYPE for the corresponding fields of *DESC.
1744  *
1745  * Allocate results in RESULT_POOL.  SCRATCH_POOL is used for temporary
1746  * allocations. */
1747 static svn_error_t *
1748 read_text_conflict_desc(svn_wc_conflict_description2_t **desc,
1749                         svn_wc__db_t *db,
1750                         const char *local_abspath,
1751                         const svn_skel_t *conflict_skel,
1752                         const char *mime_type,
1753                         svn_wc_operation_t operation,
1754                         const svn_wc_conflict_version_t *left_version,
1755                         const svn_wc_conflict_version_t *right_version,
1756                         apr_pool_t *result_pool,
1757                         apr_pool_t *scratch_pool)
1758 {
1759   *desc = svn_wc_conflict_description_create_text2(local_abspath, result_pool);
1760   (*desc)->mime_type = mime_type;
1761   (*desc)->is_binary = mime_type ? svn_mime_type_is_binary(mime_type) : FALSE;
1762   (*desc)->operation = operation;
1763   (*desc)->src_left_version = left_version;
1764   (*desc)->src_right_version = right_version;
1765
1766   SVN_ERR(svn_wc__conflict_read_text_conflict(&(*desc)->my_abspath,
1767                                               &(*desc)->base_abspath,
1768                                               &(*desc)->their_abspath,
1769                                               db, local_abspath,
1770                                               conflict_skel,
1771                                               result_pool, scratch_pool));
1772   (*desc)->merged_file = apr_pstrdup(result_pool, local_abspath);
1773
1774   return SVN_NO_ERROR;
1775 }
1776
1777 /* Set *CONFLICT_DESC to a new description of the tree conflict in
1778  * CONFLICT_SKEL.  If there is no tree conflict in CONFLICT_SKEL, return
1779  * an error.
1780  *
1781  * Use OPERATION and shallow copies of LEFT_VERSION and RIGHT_VERSION,
1782  * rather than reading them from CONFLICT_SKEL.
1783  *
1784  * Allocate results in RESULT_POOL.  SCRATCH_POOL is used for temporary
1785  * allocations. */
1786 static svn_error_t *
1787 read_tree_conflict_desc(svn_wc_conflict_description2_t **desc,
1788                         svn_wc__db_t *db,
1789                         const char *local_abspath,
1790                         svn_node_kind_t node_kind,
1791                         const svn_skel_t *conflict_skel,
1792                         svn_wc_operation_t operation,
1793                         const svn_wc_conflict_version_t *left_version,
1794                         const svn_wc_conflict_version_t *right_version,
1795                         apr_pool_t *result_pool,
1796                         apr_pool_t *scratch_pool)
1797 {
1798   svn_node_kind_t local_kind;
1799   svn_wc_conflict_reason_t reason;
1800   svn_wc_conflict_action_t action;
1801
1802   SVN_ERR(svn_wc__conflict_read_tree_conflict(
1803             &reason, &action, NULL,
1804             db, local_abspath, conflict_skel, scratch_pool, scratch_pool));
1805
1806   if (reason == svn_wc_conflict_reason_missing)
1807     local_kind = svn_node_none;
1808   else if (reason == svn_wc_conflict_reason_unversioned ||
1809            reason == svn_wc_conflict_reason_obstructed)
1810     SVN_ERR(svn_io_check_path(local_abspath, &local_kind, scratch_pool));
1811   else if (action == svn_wc_conflict_action_delete
1812            && left_version
1813            && (operation == svn_wc_operation_update
1814                ||operation == svn_wc_operation_switch)
1815            && (reason == svn_wc_conflict_reason_deleted
1816                || reason == svn_wc_conflict_reason_moved_away))
1817     {
1818       /* We have nothing locally to take the kind from */
1819       local_kind = left_version->node_kind;
1820     }
1821   else
1822     local_kind = node_kind;
1823
1824   *desc = svn_wc_conflict_description_create_tree2(local_abspath, local_kind,
1825                                                    operation,
1826                                                    left_version, right_version,
1827                                                    result_pool);
1828   (*desc)->reason = reason;
1829   (*desc)->action = action;
1830
1831   return SVN_NO_ERROR;
1832 }
1833
1834 /* Forward definition */
1835 static svn_error_t *
1836 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
1837                               svn_wc__db_t *db,
1838                               const char *local_abspath,
1839                               const svn_skel_t *conflict,
1840                               svn_wc_conflict_choice_t conflict_choice,
1841                               apr_hash_t *resolve_later,
1842                               svn_wc_notify_func2_t notify_func,
1843                               void *notify_baton,
1844                               svn_cancel_func_t cancel_func,
1845                               void *cancel_baton,
1846                               apr_pool_t *scratch_pool);
1847
1848 svn_error_t *
1849 svn_wc__conflict_invoke_resolver(svn_wc__db_t *db,
1850                                  const char *local_abspath,
1851                                  svn_node_kind_t kind,
1852                                  const svn_skel_t *conflict_skel,
1853                                  const apr_array_header_t *merge_options,
1854                                  svn_wc_conflict_resolver_func2_t resolver_func,
1855                                  void *resolver_baton,
1856                                  svn_cancel_func_t cancel_func,
1857                                  void *cancel_baton,
1858                                  apr_pool_t *scratch_pool)
1859 {
1860   svn_boolean_t text_conflicted;
1861   svn_boolean_t prop_conflicted;
1862   svn_boolean_t tree_conflicted;
1863   svn_wc_operation_t operation;
1864   const apr_array_header_t *locations;
1865   const svn_wc_conflict_version_t *left_version = NULL;
1866   const svn_wc_conflict_version_t *right_version = NULL;
1867
1868   SVN_ERR(svn_wc__conflict_read_info(&operation, &locations,
1869                                      &text_conflicted, &prop_conflicted,
1870                                      &tree_conflicted,
1871                                      db, local_abspath, conflict_skel,
1872                                      scratch_pool, scratch_pool));
1873
1874   if (locations && locations->nelts > 0)
1875     left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
1876
1877   if (locations && locations->nelts > 1)
1878     right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
1879
1880   /* Quick and dirty compatibility wrapper. My guess would be that most resolvers
1881      would want to look at all properties at the same time.
1882
1883      ### svn currently only invokes this from the merge code to collect the list of
1884      ### conflicted paths. Eventually this code will be the base for 'svn resolve'
1885      ### and at that time the test coverage will improve
1886      */
1887   if (prop_conflicted)
1888     {
1889       apr_hash_t *old_props;
1890       apr_hash_t *mine_props;
1891       apr_hash_t *their_props;
1892       apr_hash_t *old_their_props;
1893       apr_hash_t *conflicted;
1894       apr_pool_t *iterpool;
1895       apr_hash_index_t *hi;
1896       svn_boolean_t mark_resolved = TRUE;
1897
1898       SVN_ERR(svn_wc__conflict_read_prop_conflict(NULL,
1899                                                   &mine_props,
1900                                                   &old_their_props,
1901                                                   &their_props,
1902                                                   &conflicted,
1903                                                   db, local_abspath,
1904                                                   conflict_skel,
1905                                                   scratch_pool, scratch_pool));
1906
1907       if (operation == svn_wc_operation_merge)
1908         SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
1909                                                scratch_pool, scratch_pool));
1910       else
1911         old_props = old_their_props;
1912
1913       iterpool = svn_pool_create(scratch_pool);
1914
1915       for (hi = apr_hash_first(scratch_pool, conflicted);
1916            hi;
1917            hi = apr_hash_next(hi))
1918         {
1919           const char *propname = apr_hash_this_key(hi);
1920           svn_boolean_t conflict_remains = TRUE;
1921
1922           svn_pool_clear(iterpool);
1923
1924           if (cancel_func)
1925             SVN_ERR(cancel_func(cancel_baton));
1926
1927           SVN_ERR(generate_propconflict(&conflict_remains,
1928                                         db, local_abspath, kind,
1929                                         operation,
1930                                         left_version,
1931                                         right_version,
1932                                         propname,
1933                                         old_props
1934                                           ? svn_hash_gets(old_props, propname)
1935                                           : NULL,
1936                                         mine_props
1937                                           ? svn_hash_gets(mine_props, propname)
1938                                           : NULL,
1939                                         old_their_props
1940                                           ? svn_hash_gets(old_their_props, propname)
1941                                           : NULL,
1942                                         their_props
1943                                           ? svn_hash_gets(their_props, propname)
1944                                           : NULL,
1945                                         resolver_func, resolver_baton,
1946                                         cancel_func, cancel_baton,
1947                                         iterpool));
1948
1949           if (conflict_remains)
1950             mark_resolved = FALSE;
1951         }
1952
1953       if (mark_resolved)
1954         {
1955           SVN_ERR(svn_wc__mark_resolved_prop_conflicts(db, local_abspath,
1956                                                        scratch_pool));
1957         }
1958       svn_pool_destroy(iterpool);
1959     }
1960
1961   if (text_conflicted)
1962     {
1963       svn_skel_t *work_items;
1964       svn_boolean_t was_resolved;
1965       svn_wc_conflict_description2_t *desc;
1966       apr_hash_t *props;
1967       svn_wc_conflict_result_t *result;
1968
1969       SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath,
1970                                     scratch_pool, scratch_pool));
1971
1972       SVN_ERR(read_text_conflict_desc(&desc,
1973                                       db, local_abspath, conflict_skel,
1974                                       svn_prop_get_value(props,
1975                                                          SVN_PROP_MIME_TYPE),
1976                                       operation, left_version, right_version,
1977                                       scratch_pool, scratch_pool));
1978
1979
1980       work_items = NULL;
1981       was_resolved = FALSE;
1982
1983       /* Give the conflict resolution callback a chance to clean
1984          up the conflicts before we mark the file 'conflicted' */
1985
1986       SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
1987                             scratch_pool));
1988       if (result == NULL)
1989         return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
1990                                 _("Conflict callback violated API:"
1991                                   " returned no results"));
1992
1993       SVN_ERR(build_text_conflict_resolve_items(&work_items, &was_resolved,
1994                                                 db, local_abspath,
1995                                                 conflict_skel, result->choice,
1996                                                 result->merged_file,
1997                                                 result->save_merged,
1998                                                 merge_options,
1999                                                 cancel_func, cancel_baton,
2000                                                 scratch_pool, scratch_pool));
2001
2002       if (result->choice != svn_wc_conflict_choose_postpone)
2003         {
2004           SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2005                                               TRUE, FALSE, FALSE,
2006                                               work_items, scratch_pool));
2007           SVN_ERR(svn_wc__wq_run(db, local_abspath,
2008                                  cancel_func, cancel_baton,
2009                                  scratch_pool));
2010         }
2011     }
2012
2013   if (tree_conflicted)
2014     {
2015       svn_wc_conflict_result_t *result;
2016       svn_wc_conflict_description2_t *desc;
2017       svn_boolean_t resolved;
2018       svn_node_kind_t node_kind;
2019
2020       SVN_ERR(svn_wc__db_read_kind(&node_kind, db, local_abspath, TRUE,
2021                                    TRUE, FALSE, scratch_pool));
2022
2023       SVN_ERR(read_tree_conflict_desc(&desc,
2024                                       db, local_abspath, node_kind,
2025                                       conflict_skel,
2026                                       operation, left_version, right_version,
2027                                       scratch_pool, scratch_pool));
2028
2029       /* Tell the resolver func about this conflict. */
2030       SVN_ERR(resolver_func(&result, desc, resolver_baton, scratch_pool,
2031                             scratch_pool));
2032
2033       if (result == NULL)
2034         return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
2035                                 _("Conflict callback violated API:"
2036                                   " returned no results"));
2037
2038       /* Pass retry hash to avoid erroring out on cases where update
2039          can continue safely. ### Need notify handling */
2040       if (result->choice != svn_wc_conflict_choose_postpone)
2041         SVN_ERR(resolve_tree_conflict_on_node(&resolved,
2042                                               db, local_abspath, conflict_skel,
2043                                               result->choice,
2044                                               apr_hash_make(scratch_pool),
2045                                               NULL, NULL, /* ### notify */
2046                                               cancel_func, cancel_baton,
2047                                               scratch_pool));
2048     }
2049
2050   return SVN_NO_ERROR;
2051 }
2052
2053 /* Read all property conflicts contained in CONFLICT_SKEL into
2054  * individual conflict descriptions, and append those descriptions
2055  * to the CONFLICTS array.  If there is no property conflict in
2056  * CONFLICT_SKEL, return an error.
2057  *
2058  * If NOT create_tempfiles, always create a legacy property conflict
2059  * descriptor.
2060  *
2061  * Use NODE_KIND, OPERATION and shallow copies of LEFT_VERSION and
2062  * RIGHT_VERSION, rather than reading them from CONFLICT_SKEL.
2063  *
2064  * Allocate results in RESULT_POOL. SCRATCH_POOL is used for temporary
2065  * allocations. */
2066 static svn_error_t *
2067 read_prop_conflict_descs(apr_array_header_t *conflicts,
2068                          svn_wc__db_t *db,
2069                          const char *local_abspath,
2070                          svn_skel_t *conflict_skel,
2071                          svn_boolean_t create_tempfiles,
2072                          svn_node_kind_t node_kind,
2073                          svn_wc_operation_t operation,
2074                          const svn_wc_conflict_version_t *left_version,
2075                          const svn_wc_conflict_version_t *right_version,
2076                          apr_pool_t *result_pool,
2077                          apr_pool_t *scratch_pool)
2078 {
2079   const char *prop_reject_abspath;
2080   apr_hash_t *base_props;
2081   apr_hash_t *my_props;
2082   apr_hash_t *their_old_props;
2083   apr_hash_t *their_props;
2084   apr_hash_t *conflicted_props;
2085   apr_hash_index_t *hi;
2086   apr_pool_t *iterpool;
2087   svn_boolean_t prop_conflicted;
2088
2089   SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2090                                      NULL, db, local_abspath, conflict_skel,
2091                                      scratch_pool, scratch_pool));
2092
2093   if (!prop_conflicted)
2094     return SVN_NO_ERROR;
2095
2096   SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_abspath,
2097                                               &my_props,
2098                                               &their_old_props,
2099                                               &their_props,
2100                                               &conflicted_props,
2101                                               db, local_abspath,
2102                                               conflict_skel,
2103                                               scratch_pool, scratch_pool));
2104
2105   prop_reject_abspath = apr_pstrdup(result_pool, prop_reject_abspath);
2106
2107   if (apr_hash_count(conflicted_props) == 0)
2108     {
2109       /* Legacy prop conflict with only a .reject file. */
2110       svn_wc_conflict_description2_t *desc;
2111
2112       desc  = svn_wc_conflict_description_create_prop2(local_abspath,
2113                                                        node_kind,
2114                                                        "", result_pool);
2115
2116       /* ### For property conflicts, cd2 stores prop_reject_abspath in
2117        * ### their_abspath, and stores theirs_abspath in merged_file. */
2118       desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2119       desc->their_abspath = desc->prop_reject_abspath;
2120
2121       desc->operation = operation;
2122       desc->src_left_version = left_version;
2123       desc->src_right_version = right_version;
2124
2125       APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2126
2127       return SVN_NO_ERROR;
2128     }
2129
2130   if (operation == svn_wc_operation_merge)
2131     SVN_ERR(svn_wc__db_read_pristine_props(&base_props, db, local_abspath,
2132                                            result_pool, scratch_pool));
2133   else
2134     base_props = NULL;
2135   iterpool = svn_pool_create(scratch_pool);
2136   for (hi = apr_hash_first(scratch_pool, conflicted_props);
2137        hi;
2138        hi = apr_hash_next(hi))
2139     {
2140       const char *propname = apr_hash_this_key(hi);
2141       svn_string_t *old_value;
2142       svn_string_t *my_value;
2143       svn_string_t *their_value;
2144       svn_wc_conflict_description2_t *desc;
2145
2146       svn_pool_clear(iterpool);
2147
2148       desc = svn_wc_conflict_description_create_prop2(local_abspath,
2149                                                       node_kind,
2150                                                       propname,
2151                                                       result_pool);
2152
2153       desc->operation = operation;
2154       desc->src_left_version = left_version;
2155       desc->src_right_version = right_version;
2156
2157       desc->property_name = apr_pstrdup(result_pool, propname);
2158
2159       my_value = svn_hash_gets(my_props, propname);
2160       their_value = svn_hash_gets(their_props, propname);
2161       old_value = svn_hash_gets(their_old_props, propname);
2162
2163       /* Compute the incoming side of the conflict ('action'). */
2164       if (their_value == NULL)
2165         desc->action = svn_wc_conflict_action_delete;
2166       else if (old_value == NULL)
2167         desc->action = svn_wc_conflict_action_add;
2168       else
2169         desc->action = svn_wc_conflict_action_edit;
2170
2171       /* Compute the local side of the conflict ('reason'). */
2172       if (my_value == NULL)
2173         desc->reason = svn_wc_conflict_reason_deleted;
2174       else if (old_value == NULL)
2175         desc->reason = svn_wc_conflict_reason_added;
2176       else
2177         desc->reason = svn_wc_conflict_reason_edited;
2178
2179       /* ### For property conflicts, cd2 stores prop_reject_abspath in
2180        * ### their_abspath, and stores theirs_abspath in merged_file. */
2181       desc->prop_reject_abspath = prop_reject_abspath; /* in result_pool */
2182       desc->their_abspath = desc->prop_reject_abspath;
2183
2184       desc->prop_value_base = base_props ? svn_hash_gets(base_props, propname)
2185                                          : desc->prop_value_incoming_old;
2186
2187       if (my_value)
2188         {
2189           svn_stream_t *s;
2190           apr_size_t len;
2191
2192           if (create_tempfiles)
2193             {
2194               SVN_ERR(svn_stream_open_unique(&s, &desc->my_abspath, NULL,
2195                                              svn_io_file_del_on_pool_cleanup,
2196                                              result_pool, iterpool));
2197               len = my_value->len;
2198               SVN_ERR(svn_stream_write(s, my_value->data, &len));
2199               SVN_ERR(svn_stream_close(s));
2200             }
2201
2202           desc->prop_value_working = svn_string_dup(my_value, result_pool);
2203         }
2204
2205       if (their_value)
2206         {
2207           svn_stream_t *s;
2208           apr_size_t len;
2209
2210           /* ### For property conflicts, cd2 stores prop_reject_abspath in
2211            * ### their_abspath, and stores theirs_abspath in merged_file. */
2212           if (create_tempfiles)
2213             {
2214               SVN_ERR(svn_stream_open_unique(&s, &desc->merged_file, NULL,
2215                                              svn_io_file_del_on_pool_cleanup,
2216                                              result_pool, iterpool));
2217               len = their_value->len;
2218               SVN_ERR(svn_stream_write(s, their_value->data, &len));
2219               SVN_ERR(svn_stream_close(s));
2220             }
2221
2222           desc->prop_value_incoming_new = svn_string_dup(their_value, result_pool);
2223         }
2224
2225       if (old_value)
2226         {
2227           svn_stream_t *s;
2228           apr_size_t len;
2229
2230           if (create_tempfiles)
2231             {
2232               SVN_ERR(svn_stream_open_unique(&s, &desc->base_abspath, NULL,
2233                                              svn_io_file_del_on_pool_cleanup,
2234                                              result_pool, iterpool));
2235               len = old_value->len;
2236               SVN_ERR(svn_stream_write(s, old_value->data, &len));
2237               SVN_ERR(svn_stream_close(s));
2238             }
2239
2240           desc->prop_value_incoming_old = svn_string_dup(old_value, result_pool);
2241         }
2242
2243       APR_ARRAY_PUSH(conflicts, svn_wc_conflict_description2_t *) = desc;
2244     }
2245   svn_pool_destroy(iterpool);
2246
2247   return SVN_NO_ERROR;
2248 }
2249
2250 svn_error_t *
2251 svn_wc__read_conflicts(const apr_array_header_t **conflicts,
2252                        svn_skel_t **conflict_skel,
2253                        svn_wc__db_t *db,
2254                        const char *local_abspath,
2255                        svn_boolean_t create_tempfiles,
2256                        svn_boolean_t only_tree_conflict,
2257                        apr_pool_t *result_pool,
2258                        apr_pool_t *scratch_pool)
2259 {
2260   svn_skel_t *the_conflict_skel;
2261   apr_array_header_t *cflcts;
2262   svn_boolean_t prop_conflicted;
2263   svn_boolean_t text_conflicted;
2264   svn_boolean_t tree_conflicted;
2265   svn_wc_operation_t operation;
2266   const apr_array_header_t *locations;
2267   const svn_wc_conflict_version_t *left_version = NULL;
2268   const svn_wc_conflict_version_t *right_version = NULL;
2269   svn_node_kind_t node_kind;
2270   apr_hash_t *props;
2271
2272   if (!conflict_skel)
2273     conflict_skel = &the_conflict_skel;
2274
2275   SVN_ERR(svn_wc__db_read_conflict(conflict_skel, &node_kind, &props,
2276                                    db, local_abspath,
2277                                    (conflict_skel == &the_conflict_skel)
2278                                         ? scratch_pool
2279                                         : result_pool,
2280                                    scratch_pool));
2281
2282   if (!*conflict_skel)
2283     {
2284       /* Some callers expect not NULL */
2285       *conflicts = apr_array_make(result_pool, 0,
2286                                   sizeof(svn_wc_conflict_description2_t *));
2287       return SVN_NO_ERROR;
2288     }
2289
2290   SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, &text_conflicted,
2291                                      &prop_conflicted, &tree_conflicted,
2292                                      db, local_abspath, *conflict_skel,
2293                                      result_pool, scratch_pool));
2294
2295   cflcts = apr_array_make(result_pool, 4,
2296                           sizeof(svn_wc_conflict_description2_t *));
2297
2298   if (locations && locations->nelts > 0)
2299     left_version = APR_ARRAY_IDX(locations, 0, const svn_wc_conflict_version_t *);
2300   if (locations && locations->nelts > 1)
2301     right_version = APR_ARRAY_IDX(locations, 1, const svn_wc_conflict_version_t *);
2302
2303   if (prop_conflicted && !only_tree_conflict)
2304     {
2305       SVN_ERR(read_prop_conflict_descs(cflcts,
2306                                        db, local_abspath, *conflict_skel,
2307                                        create_tempfiles, node_kind,
2308                                        operation, left_version, right_version,
2309                                        result_pool, scratch_pool));
2310     }
2311
2312   if (text_conflicted && !only_tree_conflict)
2313     {
2314       svn_wc_conflict_description2_t *desc;
2315
2316       SVN_ERR(read_text_conflict_desc(&desc,
2317                                       db, local_abspath, *conflict_skel,
2318                                       svn_prop_get_value(props,
2319                                                          SVN_PROP_MIME_TYPE),
2320                                       operation, left_version, right_version,
2321                                       result_pool, scratch_pool));
2322       APR_ARRAY_PUSH(cflcts, svn_wc_conflict_description2_t *) = desc;
2323     }
2324
2325   if (tree_conflicted)
2326     {
2327       svn_wc_conflict_description2_t *desc;
2328
2329       SVN_ERR(read_tree_conflict_desc(&desc,
2330                                       db, local_abspath, node_kind,
2331                                       *conflict_skel,
2332                                       operation, left_version, right_version,
2333                                       result_pool, scratch_pool));
2334
2335       APR_ARRAY_PUSH(cflcts, const svn_wc_conflict_description2_t *) = desc;
2336     }
2337
2338   *conflicts = cflcts;
2339   return SVN_NO_ERROR;
2340 }
2341
2342 \f
2343 /*** Resolving a conflict automatically ***/
2344
2345 /*
2346  * Resolve the property conflicts found in DB/LOCAL_ABSPATH according
2347  * to CONFLICT_CHOICE.
2348  *
2349  * It is not an error if there is no prop conflict. If a prop conflict
2350  * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2351  *
2352  * Note: When there are no conflict markers on-disk to remove there is
2353  * no existing text conflict (unless we are still in the process of
2354  * creating the text conflict and we didn't register a marker file yet).
2355  * In this case the database contains old information, which we should
2356  * remove to avoid checking the next time. Resolving a property conflict
2357  * by just removing the marker file is a fully supported scenario since
2358  * Subversion 1.0.
2359  *
2360  * ### TODO [JAF] The '*_full' and '*_conflict' choices should differ.
2361  *     In my opinion, 'mine_full'/'theirs_full' should select
2362  *     the entire set of properties from 'mine' or 'theirs' respectively,
2363  *     while 'mine_conflict'/'theirs_conflict' should select just the
2364  *     properties that are in conflict.  Or, '_full' should select the
2365  *     entire property whereas '_conflict' should do a text merge within
2366  *     each property, selecting hunks.  Or all three kinds of behaviour
2367  *     should be available (full set of props, full value of conflicting
2368  *     props, or conflicting text hunks).
2369  * ### BH: If we make *_full select the full set of properties, we should
2370  *     check if we shouldn't make it also select the full text for files.
2371  *
2372  * ### TODO [JAF] All this complexity should not be down here in libsvn_wc
2373  *     but in a layer above.
2374  *
2375  * ### TODO [JAF] Options for 'base' should be like options for 'mine' and
2376  *     for 'theirs' -- choose full set of props, full value of conflicting
2377  *     props, or conflicting text hunks.
2378  *
2379  */
2380 static svn_error_t *
2381 resolve_prop_conflict_on_node(svn_boolean_t *did_resolve,
2382                               svn_wc__db_t *db,
2383                               const char *local_abspath,
2384                               svn_skel_t *conflicts,
2385                               const char *conflicted_propname,
2386                               svn_wc_conflict_choice_t conflict_choice,
2387                               const char *merged_file,
2388                               const svn_string_t *merged_value,
2389                               svn_cancel_func_t cancel_func,
2390                               void *cancel_baton,
2391                               apr_pool_t *scratch_pool)
2392 {
2393   const char *prop_reject_file;
2394   apr_hash_t *mine_props;
2395   apr_hash_t *their_old_props;
2396   apr_hash_t *their_props;
2397   apr_hash_t *conflicted_props;
2398   apr_hash_t *old_props;
2399   apr_hash_t *resolve_from = NULL;
2400   svn_skel_t *work_items = NULL;
2401   svn_wc_operation_t operation;
2402   svn_boolean_t prop_conflicted;
2403   apr_hash_t *actual_props;
2404   svn_boolean_t resolved_all, resolved_all_prop;
2405
2406   *did_resolve = FALSE;
2407
2408   SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, &prop_conflicted,
2409                                      NULL, db, local_abspath, conflicts,
2410                                      scratch_pool, scratch_pool));
2411   if (!prop_conflicted)
2412     return SVN_NO_ERROR;
2413
2414   SVN_ERR(svn_wc__conflict_read_prop_conflict(&prop_reject_file,
2415                                               &mine_props, &their_old_props,
2416                                               &their_props, &conflicted_props,
2417                                               db, local_abspath, conflicts,
2418                                               scratch_pool, scratch_pool));
2419
2420   if (!conflicted_props)
2421     {
2422       /* We have a pre 1.8 property conflict. Just mark it resolved */
2423
2424       SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2425                                              db, local_abspath, prop_reject_file,
2426                                              scratch_pool, scratch_pool));
2427       SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, TRUE, FALSE,
2428                                       work_items, scratch_pool));
2429       SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2430                              scratch_pool));
2431       return SVN_NO_ERROR;
2432     }
2433
2434   if (conflicted_propname[0] != '\0'
2435       && !svn_hash_gets(conflicted_props, conflicted_propname))
2436     {
2437       return SVN_NO_ERROR; /* This property is not conflicted! */
2438     }
2439
2440   if (operation == svn_wc_operation_merge)
2441       SVN_ERR(svn_wc__db_read_pristine_props(&old_props, db, local_abspath,
2442                                              scratch_pool, scratch_pool));
2443     else
2444       old_props = their_old_props;
2445
2446   SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
2447                                 scratch_pool, scratch_pool));
2448
2449   /* We currently handle *_conflict as *_full as this argument is currently
2450      always applied for all conflicts on a node at the same time. Giving
2451      an error would break some tests that assumed that this would just
2452      resolve property conflicts to working.
2453
2454      An alternative way to handle these conflicts would be to just copy all
2455      property state from mine/theirs on the _full option instead of just the
2456      conflicted properties. In some ways this feels like a sensible option as
2457      that would take both properties and text from mine/theirs, but when not
2458      both properties and text are conflicted we would fail in doing so.
2459    */
2460   switch (conflict_choice)
2461     {
2462     case svn_wc_conflict_choose_base:
2463       resolve_from = their_old_props ? their_old_props : old_props;
2464       break;
2465     case svn_wc_conflict_choose_mine_full:
2466     case svn_wc_conflict_choose_mine_conflict:
2467       resolve_from = mine_props;
2468       break;
2469     case svn_wc_conflict_choose_theirs_full:
2470     case svn_wc_conflict_choose_theirs_conflict:
2471       resolve_from = their_props;
2472       break;
2473     case svn_wc_conflict_choose_merged:
2474       if ((merged_file || merged_value) && conflicted_propname[0] != '\0')
2475         {
2476           resolve_from = apr_hash_copy(scratch_pool, actual_props);
2477
2478           if (!merged_value)
2479             {
2480               svn_stream_t *stream;
2481               svn_string_t *merged_propval;
2482
2483               SVN_ERR(svn_stream_open_readonly(&stream, merged_file,
2484                                                scratch_pool, scratch_pool));
2485               SVN_ERR(svn_string_from_stream(&merged_propval, stream,
2486                                              scratch_pool, scratch_pool));
2487
2488               merged_value = merged_propval;
2489             }
2490           svn_hash_sets(resolve_from, conflicted_propname, merged_value);
2491         }
2492       else
2493         resolve_from = NULL;
2494       break;
2495     default:
2496       return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
2497                               _("Invalid 'conflict_result' argument"));
2498     }
2499
2500
2501   if (resolve_from)
2502     {
2503       apr_hash_index_t *hi;
2504       apr_hash_t *apply_on_props;
2505
2506       if (conflicted_propname[0] == '\0')
2507         {
2508           /* Apply to all conflicted properties */
2509           apply_on_props = conflicted_props;
2510         }
2511       else
2512         {
2513           /* Apply to a single property */
2514           apply_on_props = apr_hash_make(scratch_pool);
2515           svn_hash_sets(apply_on_props, conflicted_propname, "");
2516         }
2517
2518       /* Apply the selected changes */
2519       for (hi = apr_hash_first(scratch_pool, apply_on_props);
2520            hi;
2521            hi = apr_hash_next(hi))
2522         {
2523           const char *propname = apr_hash_this_key(hi);
2524           svn_string_t *new_value = NULL;
2525
2526           new_value = svn_hash_gets(resolve_from, propname);
2527
2528           svn_hash_sets(actual_props, propname, new_value);
2529         }
2530     }
2531   /*else the user accepted the properties as-is */
2532
2533   /* This function handles conflicted_propname "" as resolving
2534      all property conflicts... Just what we need here */
2535   SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
2536                                         db, local_abspath,
2537                                         FALSE, conflicted_propname,
2538                                         FALSE,
2539                                         scratch_pool, scratch_pool));
2540
2541   if (!resolved_all)
2542     {
2543       /* Are there still property conflicts left? (or only...) */
2544       SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, &prop_conflicted,
2545                                          NULL, db, local_abspath, conflicts,
2546                                          scratch_pool, scratch_pool));
2547
2548       resolved_all_prop = (! prop_conflicted);
2549     }
2550   else
2551     {
2552       resolved_all_prop = TRUE;
2553       conflicts = NULL;
2554     }
2555
2556   if (resolved_all_prop)
2557     {
2558       /* Legacy behavior: Only report property conflicts as resolved when the
2559          property reject file exists
2560
2561          If not the UI shows the conflict as already resolved
2562          (and in this case we just remove the in-db conflict) */
2563       SVN_ERR(remove_artifact_file_if_exists(&work_items, did_resolve,
2564                                              db, local_abspath,
2565                                              prop_reject_file,
2566                                              scratch_pool, scratch_pool));
2567     }
2568   else
2569     {
2570       /* Create a new prej file, based on the remaining conflicts */
2571       SVN_ERR(svn_wc__wq_build_prej_install(&work_items,
2572                                             db, local_abspath,
2573                                             scratch_pool, scratch_pool));
2574       *did_resolve = TRUE; /* We resolved a property conflict */
2575     }
2576
2577   /* This installs the updated conflict skel */
2578   SVN_ERR(svn_wc__db_op_set_props(db, local_abspath, actual_props,
2579                                   FALSE, conflicts, work_items,
2580                                   scratch_pool));
2581
2582   if (resolved_all)
2583     {
2584       /* Remove the whole conflict. Should probably be integrated
2585          into the op_set_props() call */
2586       SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
2587                                           FALSE, TRUE, FALSE,
2588                                           NULL, scratch_pool));
2589     }
2590
2591   SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2592                          scratch_pool));
2593
2594   return SVN_NO_ERROR;
2595 }
2596
2597 /*
2598  * Resolve the tree conflict found in DB/LOCAL_ABSPATH according to
2599  * CONFLICT_CHOICE.
2600  *
2601  * It is not an error if there is no tree conflict. If a tree conflict
2602  * existed and was resolved, set *DID_RESOLVE to TRUE, else set it to FALSE.
2603  *
2604  * It is not an error if there is no tree conflict.
2605  *
2606  * If the conflict can't be resolved yet because another tree conflict is
2607  * blocking a storage location, store the tree conflict in the RESOLVE_LATER
2608  * hash.
2609  */
2610 static svn_error_t *
2611 resolve_tree_conflict_on_node(svn_boolean_t *did_resolve,
2612                               svn_wc__db_t *db,
2613                               const char *local_abspath,
2614                               const svn_skel_t *conflicts,
2615                               svn_wc_conflict_choice_t conflict_choice,
2616                               apr_hash_t *resolve_later,
2617                               svn_wc_notify_func2_t notify_func,
2618                               void *notify_baton,
2619                               svn_cancel_func_t cancel_func,
2620                               void *cancel_baton,
2621                               apr_pool_t *scratch_pool)
2622 {
2623   svn_wc_conflict_reason_t reason;
2624   svn_wc_conflict_action_t action;
2625   svn_wc_operation_t operation;
2626   svn_boolean_t tree_conflicted;
2627   const char *src_op_root_abspath;
2628
2629   *did_resolve = FALSE;
2630
2631   SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
2632                                      &tree_conflicted, db, local_abspath,
2633                                      conflicts, scratch_pool, scratch_pool));
2634   if (!tree_conflicted)
2635     return SVN_NO_ERROR;
2636
2637   SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2638                                               &src_op_root_abspath,
2639                                               db, local_abspath,
2640                                               conflicts,
2641                                               scratch_pool, scratch_pool));
2642
2643   if (operation == svn_wc_operation_update
2644       || operation == svn_wc_operation_switch)
2645     {
2646       svn_error_t *err;
2647       if (reason == svn_wc_conflict_reason_deleted ||
2648           reason == svn_wc_conflict_reason_replaced)
2649         {
2650           if (conflict_choice == svn_wc_conflict_choose_merged)
2651             {
2652               /* Break moves for any children moved out of this directory,
2653                * and leave this directory deleted. */
2654
2655               if (action != svn_wc_conflict_action_delete)
2656                 {
2657                   SVN_ERR(svn_wc__db_op_break_moved_away(
2658                                   db, local_abspath, src_op_root_abspath, TRUE,
2659                                   notify_func, notify_baton,
2660                                   scratch_pool));
2661                   *did_resolve = TRUE;
2662                   return SVN_NO_ERROR; /* Marked resolved by function*/
2663                 }
2664               /* else # The move is/moves are already broken */
2665
2666
2667               *did_resolve = TRUE;
2668             }
2669           else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2670             {
2671               svn_skel_t *new_conflicts;
2672
2673               /* Raise moved-away conflicts on any children moved out of
2674                * this directory, and leave this directory as-is.
2675                *
2676                * The newly conflicted moved-away children will be updated
2677                * if they are resolved with 'mine_conflict' as well. */
2678               err = svn_wc__db_op_raise_moved_away(
2679                         db, local_abspath, notify_func, notify_baton,
2680                         scratch_pool);
2681
2682               if (err)
2683                 {
2684                   const char *dup_abspath;
2685
2686                   if (!resolve_later
2687                       || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
2688                     return svn_error_trace(err);
2689
2690                   svn_error_clear(err);
2691                   dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2692                                             local_abspath);
2693
2694                   svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2695
2696                   return SVN_NO_ERROR; /* Retry after other conflicts */
2697                 }
2698
2699               /* We might now have a moved-away on *this* path, let's
2700                  try to resolve that directly if that is the case */
2701               SVN_ERR(svn_wc__db_read_conflict(&new_conflicts, NULL, NULL,
2702                                                db, local_abspath,
2703                                                scratch_pool, scratch_pool));
2704
2705               if (new_conflicts)
2706                 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, NULL, NULL,
2707                                                    &tree_conflicted,
2708                                                    db, local_abspath,
2709                                                    new_conflicts,
2710                                                    scratch_pool,
2711                                                    scratch_pool));
2712
2713               if (!new_conflicts || !tree_conflicted)
2714                 {
2715                   /* TC is marked resolved by calling
2716                      svn_wc__db_resolve_delete_raise_moved_away */
2717                   *did_resolve = TRUE;
2718                   return SVN_NO_ERROR;
2719                 }
2720
2721               SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
2722                                                           &src_op_root_abspath,
2723                                                           db, local_abspath,
2724                                                           new_conflicts,
2725                                                           scratch_pool,
2726                                                           scratch_pool));
2727
2728               if (reason != svn_wc_conflict_reason_moved_away)
2729                 {
2730                   *did_resolve = TRUE;
2731                   return SVN_NO_ERROR; /* We fixed one, but... */
2732                 }
2733
2734               conflicts = new_conflicts;
2735               /* Fall through in moved_away handling */
2736             }
2737           else
2738             return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2739                                      NULL,
2740                                      _("Tree conflict can only be resolved to "
2741                                        "'working' or 'mine-conflict' state; "
2742                                        "'%s' not resolved"),
2743                                      svn_dirent_local_style(local_abspath,
2744                                                             scratch_pool));
2745         }
2746
2747       if (reason == svn_wc_conflict_reason_moved_away
2748            && action == svn_wc_conflict_action_edit)
2749         {
2750           /* After updates, we can resolve local moved-away
2751            * vs. any incoming change, either by updating the
2752            * moved-away node (mine-conflict) or by breaking the
2753            * move (theirs-conflict). */
2754           if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2755             {
2756               err = svn_wc__db_update_moved_away_conflict_victim(
2757                         db, local_abspath, src_op_root_abspath,
2758                         operation, action, reason,
2759                         cancel_func, cancel_baton,
2760                         notify_func, notify_baton,
2761                         scratch_pool);
2762
2763               if (err)
2764                 {
2765                   const char *dup_abspath;
2766
2767                   if (!resolve_later
2768                       || err->apr_err != SVN_ERR_WC_OBSTRUCTED_UPDATE)
2769                     return svn_error_trace(err);
2770
2771                   svn_error_clear(err);
2772                   dup_abspath = apr_pstrdup(apr_hash_pool_get(resolve_later),
2773                                             local_abspath);
2774
2775                   svn_hash_sets(resolve_later, dup_abspath, dup_abspath);
2776
2777                   return SVN_NO_ERROR; /* Retry after other conflicts */
2778                 }
2779               else
2780                 *did_resolve = TRUE;
2781             }
2782           else if (conflict_choice == svn_wc_conflict_choose_merged)
2783             {
2784               /* We must break the move if the user accepts the current
2785                * working copy state instead of updating the move.
2786                * Else the move would be left in an invalid state. */
2787
2788               SVN_ERR(svn_wc__db_op_break_moved_away(db, local_abspath,
2789                                                      src_op_root_abspath, TRUE,
2790                                                      notify_func, notify_baton,
2791                                                      scratch_pool));
2792               *did_resolve = TRUE;
2793               return SVN_NO_ERROR; /* Conflict is marked resolved */
2794             }
2795           else
2796             return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2797                                      NULL,
2798                                      _("Tree conflict can only be resolved to "
2799                                        "'working' or 'mine-conflict' state; "
2800                                        "'%s' not resolved"),
2801                                      svn_dirent_local_style(local_abspath,
2802                                                             scratch_pool));
2803         }
2804       else if (reason == svn_wc_conflict_reason_moved_away
2805                && action != svn_wc_conflict_action_edit)
2806         {
2807           /* action added is impossible, because that would imply that
2808              something was added, but before that already moved...
2809              (which would imply a replace) */
2810           SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete
2811                          || action == svn_wc_conflict_action_replace);
2812
2813           if (conflict_choice == svn_wc_conflict_choose_merged)
2814             {
2815               /* Whatever was moved is removed at its original location by the
2816                  update. That must also remove the recording of the move, so
2817                  we don't have to do anything here. */
2818
2819               *did_resolve = TRUE;
2820             }
2821           else if (conflict_choice == svn_wc_conflict_choose_mine_conflict)
2822             {
2823               return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2824                                        NULL,
2825                                        _("Tree conflict can only be "
2826                                          "resolved to 'working' state; "
2827                                          "'%s' is no longer moved"),
2828                                        svn_dirent_local_style(local_abspath,
2829                                                               scratch_pool));
2830             }
2831         }
2832     }
2833
2834   if (! *did_resolve)
2835     {
2836       if (conflict_choice != svn_wc_conflict_choose_merged)
2837         {
2838           /* For other tree conflicts, there is no way to pick
2839            * theirs-full or mine-full, etc. Throw an error if the
2840            * user expects us to be smarter than we really are. */
2841           return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
2842                                    NULL,
2843                                    _("Tree conflict can only be "
2844                                      "resolved to 'working' state; "
2845                                      "'%s' not resolved"),
2846                                    svn_dirent_local_style(local_abspath,
2847                                                           scratch_pool));
2848         }
2849       else
2850         *did_resolve = TRUE;
2851     }
2852
2853   SVN_ERR_ASSERT(*did_resolve);
2854
2855   SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, FALSE, FALSE, TRUE,
2856                                       NULL, scratch_pool));
2857   SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
2858                          scratch_pool));
2859   return SVN_NO_ERROR;
2860 }
2861
2862 svn_error_t *
2863 svn_wc__mark_resolved_text_conflict(svn_wc__db_t *db,
2864                                     const char *local_abspath,
2865                                     svn_cancel_func_t cancel_func,
2866                                     void *cancel_baton,
2867                                     apr_pool_t *scratch_pool)
2868 {
2869   svn_skel_t *work_items;
2870   svn_skel_t *conflict;
2871
2872   SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL,
2873                                    db, local_abspath,
2874                                    scratch_pool, scratch_pool));
2875
2876   if (!conflict)
2877     return SVN_NO_ERROR;
2878
2879   SVN_ERR(build_text_conflict_resolve_items(&work_items, NULL,
2880                                             db, local_abspath, conflict,
2881                                             svn_wc_conflict_choose_merged,
2882                                             NULL, FALSE, NULL,
2883                                             cancel_func, cancel_baton,
2884                                             scratch_pool, scratch_pool));
2885
2886   SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, TRUE, FALSE, FALSE,
2887                                       work_items, scratch_pool));
2888
2889   return svn_error_trace(svn_wc__wq_run(db, local_abspath,
2890                                         cancel_func, cancel_baton,
2891                                         scratch_pool));
2892 }
2893
2894 svn_error_t *
2895 svn_wc__mark_resolved_prop_conflicts(svn_wc__db_t *db,
2896                                      const char *local_abspath,
2897                                      apr_pool_t *scratch_pool)
2898 {
2899   svn_boolean_t ignored_result;
2900   svn_skel_t *conflicts;
2901
2902   SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
2903                                    db, local_abspath,
2904                                    scratch_pool, scratch_pool));
2905
2906   if (!conflicts)
2907     return SVN_NO_ERROR;
2908
2909   return svn_error_trace(resolve_prop_conflict_on_node(
2910                            &ignored_result,
2911                            db, local_abspath, conflicts, "",
2912                            svn_wc_conflict_choose_merged,
2913                            NULL, NULL,
2914                            NULL, NULL,
2915                            scratch_pool));
2916 }
2917
2918
2919 /* Baton for conflict_status_walker */
2920 struct conflict_status_walker_baton
2921 {
2922   svn_wc__db_t *db;
2923   svn_boolean_t resolve_text;
2924   const char *resolve_prop;
2925   svn_boolean_t resolve_tree;
2926   svn_wc_conflict_choice_t conflict_choice;
2927   svn_wc_conflict_resolver_func2_t conflict_func;
2928   void *conflict_baton;
2929   svn_cancel_func_t cancel_func;
2930   void *cancel_baton;
2931   svn_wc_notify_func2_t notify_func;
2932   void *notify_baton;
2933   svn_boolean_t resolved_one;
2934   apr_hash_t *resolve_later;
2935 };
2936
2937 /* Implements svn_wc_notify_func2_t to collect new conflicts caused by
2938    resolving a tree conflict. */
2939 static void
2940 tree_conflict_collector(void *baton,
2941                         const svn_wc_notify_t *notify,
2942                         apr_pool_t *pool)
2943 {
2944   struct conflict_status_walker_baton *cswb = baton;
2945
2946   if (cswb->notify_func)
2947     cswb->notify_func(cswb->notify_baton, notify, pool);
2948
2949   if (cswb->resolve_later
2950       && (notify->action == svn_wc_notify_tree_conflict
2951           || notify->prop_state == svn_wc_notify_state_conflicted
2952           || notify->content_state == svn_wc_notify_state_conflicted))
2953     {
2954       if (!svn_hash_gets(cswb->resolve_later, notify->path))
2955         {
2956           const char *dup_path;
2957
2958           dup_path = apr_pstrdup(apr_hash_pool_get(cswb->resolve_later),
2959                                  notify->path);
2960
2961           svn_hash_sets(cswb->resolve_later, dup_path, dup_path);
2962         }
2963     }
2964 }
2965
2966 /* Implements svn_wc_status4_t to walk all conflicts to resolve.
2967  */
2968 static svn_error_t *
2969 conflict_status_walker(void *baton,
2970                        const char *local_abspath,
2971                        const svn_wc_status3_t *status,
2972                        apr_pool_t *scratch_pool)
2973 {
2974   struct conflict_status_walker_baton *cswb = baton;
2975   svn_wc__db_t *db = cswb->db;
2976
2977   const apr_array_header_t *conflicts;
2978   apr_pool_t *iterpool;
2979   int i;
2980   svn_boolean_t resolved = FALSE;
2981   svn_skel_t *conflict;
2982
2983   if (!status->conflicted)
2984     return SVN_NO_ERROR;
2985
2986   iterpool = svn_pool_create(scratch_pool);
2987
2988   SVN_ERR(svn_wc__read_conflicts(&conflicts, &conflict,
2989                                  db, local_abspath,
2990                                  (cswb->conflict_func != NULL) /* tmp files */,
2991                                  FALSE /* only tree conflicts */,
2992                                  scratch_pool, iterpool));
2993
2994   for (i = 0; i < conflicts->nelts; i++)
2995     {
2996       const svn_wc_conflict_description2_t *cd;
2997       svn_boolean_t did_resolve;
2998       svn_wc_conflict_choice_t my_choice = cswb->conflict_choice;
2999       svn_wc_conflict_result_t *result = NULL;
3000       svn_skel_t *work_items;
3001
3002       cd = APR_ARRAY_IDX(conflicts, i, const svn_wc_conflict_description2_t *);
3003
3004       if ((cd->kind == svn_wc_conflict_kind_property
3005            && (!cswb->resolve_prop
3006                || (*cswb->resolve_prop != '\0'
3007                    && strcmp(cswb->resolve_prop, cd->property_name) != 0)))
3008           || (cd->kind == svn_wc_conflict_kind_text && !cswb->resolve_text)
3009           || (cd->kind == svn_wc_conflict_kind_tree && !cswb->resolve_tree))
3010         {
3011           continue; /* Easy out. Don't call resolver func and ignore result */
3012         }
3013
3014       svn_pool_clear(iterpool);
3015
3016       if (my_choice == svn_wc_conflict_choose_unspecified)
3017         {
3018           if (!cswb->conflict_func)
3019             return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
3020                                     _("No conflict-callback and no "
3021                                       "pre-defined conflict-choice provided"));
3022
3023           SVN_ERR(cswb->conflict_func(&result, cd, cswb->conflict_baton,
3024                                       iterpool, iterpool));
3025
3026           my_choice = result->choice;
3027         }
3028
3029
3030       if (my_choice == svn_wc_conflict_choose_postpone)
3031         continue;
3032
3033       switch (cd->kind)
3034         {
3035           case svn_wc_conflict_kind_tree:
3036             SVN_ERR(resolve_tree_conflict_on_node(&did_resolve,
3037                                                   db,
3038                                                   local_abspath, conflict,
3039                                                   my_choice,
3040                                                   cswb->resolve_later,
3041                                                   tree_conflict_collector,
3042                                                   cswb,
3043                                                   cswb->cancel_func,
3044                                                   cswb->cancel_baton,
3045                                                   iterpool));
3046
3047             if (did_resolve)
3048               resolved = TRUE;
3049             break;
3050
3051           case svn_wc_conflict_kind_text:
3052             SVN_ERR(build_text_conflict_resolve_items(
3053                                         &work_items,
3054                                         &resolved,
3055                                         db, local_abspath, conflict,
3056                                         my_choice,
3057                                         result ? result->merged_file
3058                                                : NULL,
3059                                         result ? result->save_merged
3060                                                : FALSE,
3061                                         NULL /* merge_options */,
3062                                         cswb->cancel_func,
3063                                         cswb->cancel_baton,
3064                                         iterpool, iterpool));
3065
3066             SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
3067                                                 TRUE, FALSE, FALSE,
3068                                                 work_items, iterpool));
3069             SVN_ERR(svn_wc__wq_run(db, local_abspath,
3070                                    cswb->cancel_func, cswb->cancel_baton,
3071                                    iterpool));
3072             break;
3073
3074           case svn_wc_conflict_kind_property:
3075             SVN_ERR(resolve_prop_conflict_on_node(&did_resolve,
3076                                                   db,
3077                                                   local_abspath,
3078                                                   conflict,
3079                                                   cd->property_name,
3080                                                   my_choice,
3081                                                   result
3082                                                     ? result->merged_file
3083                                                     : NULL,
3084                                                   result
3085                                                     ? result->merged_value
3086                                                     : NULL,
3087                                                   cswb->cancel_func,
3088                                                   cswb->cancel_baton,
3089                                                   iterpool));
3090
3091             if (did_resolve)
3092               resolved = TRUE;
3093             break;
3094
3095           default:
3096             /* We can't resolve other conflict types */
3097             break;
3098         }
3099     }
3100
3101   /* Notify */
3102   if (cswb->notify_func && resolved)
3103     cswb->notify_func(cswb->notify_baton,
3104                       svn_wc_create_notify(local_abspath,
3105                                            svn_wc_notify_resolved,
3106                                            iterpool),
3107                       iterpool);
3108
3109   if (resolved)
3110     cswb->resolved_one = TRUE;
3111
3112   svn_pool_destroy(iterpool);
3113
3114   return SVN_NO_ERROR;
3115 }
3116
3117 svn_error_t *
3118 svn_wc__resolve_conflicts(svn_wc_context_t *wc_ctx,
3119                           const char *local_abspath,
3120                           svn_depth_t depth,
3121                           svn_boolean_t resolve_text,
3122                           const char *resolve_prop,
3123                           svn_boolean_t resolve_tree,
3124                           svn_wc_conflict_choice_t conflict_choice,
3125                           svn_wc_conflict_resolver_func2_t conflict_func,
3126                           void *conflict_baton,
3127                           svn_cancel_func_t cancel_func,
3128                           void *cancel_baton,
3129                           svn_wc_notify_func2_t notify_func,
3130                           void *notify_baton,
3131                           apr_pool_t *scratch_pool)
3132 {
3133   svn_node_kind_t kind;
3134   svn_boolean_t conflicted;
3135   struct conflict_status_walker_baton cswb;
3136   apr_pool_t *iterpool = NULL;
3137   svn_error_t *err;
3138
3139   /* ### Just a versioned check? */
3140   /* Conflicted is set to allow invoking on actual only nodes */
3141   SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,
3142                                NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3143                                NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
3144                                NULL, NULL, NULL, NULL, NULL, NULL,
3145                                wc_ctx->db, local_abspath,
3146                                scratch_pool, scratch_pool));
3147
3148   /* When the implementation still used the entry walker, depth
3149      unknown was translated to infinity. */
3150   if (kind != svn_node_dir)
3151     depth = svn_depth_empty;
3152   else if (depth == svn_depth_unknown)
3153     depth = svn_depth_infinity;
3154
3155   cswb.db = wc_ctx->db;
3156   cswb.resolve_text = resolve_text;
3157   cswb.resolve_prop = resolve_prop;
3158   cswb.resolve_tree = resolve_tree;
3159   cswb.conflict_choice = conflict_choice;
3160
3161   cswb.conflict_func = conflict_func;
3162   cswb.conflict_baton = conflict_baton;
3163
3164   cswb.cancel_func = cancel_func;
3165   cswb.cancel_baton = cancel_baton;
3166
3167   cswb.notify_func = notify_func;
3168   cswb.notify_baton = notify_baton;
3169
3170   cswb.resolved_one = FALSE;
3171   cswb.resolve_later = (depth != svn_depth_empty)
3172                           ? apr_hash_make(scratch_pool)
3173                           : NULL;
3174
3175   if (notify_func)
3176     notify_func(notify_baton,
3177                 svn_wc_create_notify(local_abspath,
3178                                     svn_wc_notify_conflict_resolver_starting,
3179                                     scratch_pool),
3180                 scratch_pool);
3181
3182   err = svn_wc_walk_status(wc_ctx,
3183                            local_abspath,
3184                            depth,
3185                            FALSE /* get_all */,
3186                            FALSE /* no_ignore */,
3187                            TRUE /* ignore_text_mods */,
3188                            NULL /* ignore_patterns */,
3189                            conflict_status_walker, &cswb,
3190                            cancel_func, cancel_baton,
3191                            scratch_pool);
3192
3193   /* If we got new tree conflicts (or delayed conflicts) during the initial
3194      walk, we now walk them one by one as closure. */
3195   while (!err && cswb.resolve_later && apr_hash_count(cswb.resolve_later))
3196     {
3197       apr_hash_index_t *hi;
3198       svn_wc_status3_t *status = NULL;
3199       const char *tc_abspath = NULL;
3200
3201       if (iterpool)
3202         svn_pool_clear(iterpool);
3203       else
3204         iterpool = svn_pool_create(scratch_pool);
3205
3206       hi = apr_hash_first(scratch_pool, cswb.resolve_later);
3207       cswb.resolve_later = apr_hash_make(scratch_pool);
3208       cswb.resolved_one = FALSE;
3209
3210       for (; hi && !err; hi = apr_hash_next(hi))
3211         {
3212           const char *relpath;
3213           svn_pool_clear(iterpool);
3214
3215           tc_abspath = apr_hash_this_key(hi);
3216
3217           if (cancel_func)
3218             SVN_ERR(cancel_func(cancel_baton));
3219
3220           relpath = svn_dirent_skip_ancestor(local_abspath,
3221                                              tc_abspath);
3222
3223           if (!relpath
3224               || (depth >= svn_depth_empty
3225                   && depth < svn_depth_infinity
3226                   && strchr(relpath, '/')))
3227             {
3228               continue;
3229             }
3230
3231           SVN_ERR(svn_wc_status3(&status, wc_ctx, tc_abspath,
3232                                  iterpool, iterpool));
3233
3234           if (depth == svn_depth_files
3235               && status->kind == svn_node_dir)
3236             continue;
3237
3238           err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3239                                                        status, scratch_pool));
3240         }
3241
3242       /* None of the remaining conflicts got resolved, and non did provide
3243          an error...
3244
3245          We can fix that if we disable the 'resolve_later' option...
3246        */
3247       if (!cswb.resolved_one && !err && tc_abspath
3248           && apr_hash_count(cswb.resolve_later))
3249         {
3250           /* Run the last resolve operation again. We still have status
3251              and tc_abspath for that one. */
3252
3253           cswb.resolve_later = NULL; /* Produce proper error! */
3254
3255           /* Recreate the error */
3256           err = svn_error_trace(conflict_status_walker(&cswb, tc_abspath,
3257                                                        status, scratch_pool));
3258
3259           SVN_ERR_ASSERT(err != NULL);
3260
3261           err = svn_error_createf(
3262                     SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3263                     _("Unable to resolve pending conflict on '%s'"),
3264                     svn_dirent_local_style(tc_abspath, scratch_pool));
3265           break;
3266         }
3267     }
3268
3269   if (iterpool)
3270     svn_pool_destroy(iterpool);
3271
3272   if (err && err->apr_err != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE)
3273     err = svn_error_createf(
3274                 SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, err,
3275                 _("Unable to resolve conflicts on '%s'"),
3276                 svn_dirent_local_style(local_abspath, scratch_pool));
3277
3278   SVN_ERR(err);
3279
3280   if (notify_func)
3281     notify_func(notify_baton,
3282                 svn_wc_create_notify(local_abspath,
3283                                     svn_wc_notify_conflict_resolver_done,
3284                                     scratch_pool),
3285                 scratch_pool);
3286
3287   return SVN_NO_ERROR;
3288 }
3289
3290 svn_error_t *
3291 svn_wc_resolved_conflict5(svn_wc_context_t *wc_ctx,
3292                           const char *local_abspath,
3293                           svn_depth_t depth,
3294                           svn_boolean_t resolve_text,
3295                           const char *resolve_prop,
3296                           svn_boolean_t resolve_tree,
3297                           svn_wc_conflict_choice_t conflict_choice,
3298                           svn_cancel_func_t cancel_func,
3299                           void *cancel_baton,
3300                           svn_wc_notify_func2_t notify_func,
3301                           void *notify_baton,
3302                           apr_pool_t *scratch_pool)
3303 {
3304   return svn_error_trace(svn_wc__resolve_conflicts(wc_ctx, local_abspath,
3305                                                    depth, resolve_text,
3306                                                    resolve_prop, resolve_tree,
3307                                                    conflict_choice,
3308                                                    NULL, NULL,
3309                                                    cancel_func, cancel_baton,
3310                                                    notify_func, notify_baton,
3311                                                    scratch_pool));
3312 }
3313
3314 /* Constructor for the result-structure returned by conflict callbacks. */
3315 svn_wc_conflict_result_t *
3316 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
3317                               const char *merged_file,
3318                               apr_pool_t *pool)
3319 {
3320   svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
3321   result->choice = choice;
3322   result->merged_file = apr_pstrdup(pool, merged_file);
3323   result->save_merged = FALSE;
3324
3325   /* If we add more fields to svn_wc_conflict_result_t, add them here. */
3326
3327   return result;
3328 }