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