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