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