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