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