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