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