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