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