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