Fix compiler warning due to missing function prototype.
[svn.git] / subversion / libsvn_wc / merge.c
blob2989417c6247e01fce6b45170308b26443c8da60
1 /*
2 * merge.c: merging changes into a working file
4 * ====================================================================
5 * Copyright (c) 2000-2006 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #include "svn_wc.h"
22 #include "svn_diff.h"
23 #include "svn_path.h"
25 #include "wc.h"
26 #include "entries.h"
27 #include "translate.h"
28 #include "questions.h"
29 #include "log.h"
31 #include "svn_private_config.h"
34 /* Return a pointer to the svn_prop_t structure from PROP_DIFF
35 belonging to PROP_NAME, if any. NULL otherwise.*/
36 static const svn_prop_t *
37 get_prop(const apr_array_header_t *prop_diff,
38 const char *prop_name)
40 if (prop_diff)
42 int i;
43 for (i = 0; i < prop_diff->nelts; i++)
45 const svn_prop_t *elt = &APR_ARRAY_IDX(prop_diff, i, svn_prop_t);
46 if (strcmp(elt->name,prop_name) == 0)
47 return elt;
51 return NULL;
55 /* Detranslate a working copy file MERGE_TARGET to achieve the effect of:
57 1. Detranslate
58 2. Install new props
59 3. Retranslate
60 4. Detranslate
62 in 1 pass to get a file which can be compared with the left and right
63 files which were created with the 'new props' above.
65 Property changes make this a little complex though. Changes in
67 - svn:mime-type
68 - svn:eol-style
69 - svn:keywords
70 - svn:special
72 may change the way a file is translated.
74 Effect for svn:mime-type:
76 The value for svn:mime-type affects the translation wrt keywords
77 and eol-style settings.
79 I) both old and new mime-types are texty
80 -> just do the translation dance (as lined out below)
82 II) the old one is texty, the new one is binary
83 -> detranslate with the old eol-style and keywords
84 (the new re+detranslation is a no-op)
86 III) the old one is binary, the new one texty
87 -> detranslate with the new eol-style
88 (the old detranslation is a no-op)
90 IV) the old and new ones are binary
91 -> don't detranslate, just make a straight copy
94 Effect for svn:eol-style
96 I) On add or change use the new value
98 II) otherwise: use the old value (absent means 'no translation')
101 Effect for svn:keywords
103 Always use old settings (re+detranslation are no-op)
106 Effect for svn:special
108 Always use the old settings (same reasons as for svn:keywords)
111 static svn_error_t *
112 detranslate_wc_file(const char **detranslated_file,
113 const char *merge_target,
114 svn_wc_adm_access_t *adm_access,
115 svn_boolean_t force_copy,
116 const apr_array_header_t *prop_diff,
117 apr_pool_t *pool)
119 svn_boolean_t is_binary;
120 const svn_prop_t *prop;
121 svn_subst_eol_style_t style;
122 const char *eol;
123 apr_hash_t *keywords;
124 svn_boolean_t special;
126 /* Decide if the merge target currently is a text or binary file. */
127 SVN_ERR(svn_wc_has_binary_prop(&is_binary,
128 merge_target, adm_access, pool));
131 /* See if we need to do a straight copy:
132 - old and new mime-types are binary, or
133 - old mime-type is binary and no new mime-type specified */
134 if (is_binary
135 && (((prop = get_prop(prop_diff, SVN_PROP_MIME_TYPE))
136 && prop->value && svn_mime_type_is_binary(prop->value->data))
137 || prop == NULL))
139 /* this is case IV above */
140 keywords = NULL;
141 special = FALSE;
142 eol = NULL;
143 style = svn_subst_eol_style_none;
145 else if ((!is_binary)
146 && (prop = get_prop(prop_diff, SVN_PROP_MIME_TYPE))
147 && prop->value && svn_mime_type_is_binary(prop->value->data))
149 /* Old props indicate texty, new props indicate binary:
150 detranslate keywords and old eol-style */
151 SVN_ERR(svn_wc__get_keywords(&keywords, merge_target,
152 adm_access, NULL, pool));
153 SVN_ERR(svn_wc__get_special(&special, merge_target, adm_access, pool));
155 else
157 /* New props indicate texty, regardless of old props */
159 /* In case the file used to be special, detranslate specially */
160 SVN_ERR(svn_wc__get_special(&special, merge_target, adm_access, pool));
162 if (special)
164 keywords = NULL;
165 eol = NULL;
166 style = svn_subst_eol_style_none;
168 else
170 /* In case a new eol style was set, use that for detranslation */
171 if ((prop = get_prop(prop_diff, SVN_PROP_EOL_STYLE)) && prop->value)
173 /* Value added or changed */
174 svn_subst_eol_style_from_value(&style, &eol, prop->value->data);
176 else if (!is_binary)
177 SVN_ERR(svn_wc__get_eol_style(&style, &eol, merge_target,
178 adm_access, pool));
179 else
181 eol = NULL;
182 style = svn_subst_eol_style_none;
185 /* In case there were keywords, detranslate with keywords
186 (iff we were texty) */
187 if (!is_binary)
188 SVN_ERR(svn_wc__get_keywords(&keywords, merge_target,
189 adm_access, NULL, pool));
190 else
191 keywords = NULL;
195 /* Now, detranslate with the settings we created above */
197 if (force_copy || keywords || eol || special)
199 const char *detranslated;
200 /* Force a copy into the temporary wc area to avoid having
201 temporary files created below to appear in the actual wc. */
203 SVN_ERR(svn_wc_create_tmp_file2
204 (NULL, &detranslated,
205 svn_wc_adm_access_path(adm_access),
206 svn_io_file_del_none, pool));
208 SVN_ERR(svn_subst_translate_to_normal_form(merge_target,
209 detranslated,
210 style,
211 eol, eol ? FALSE : TRUE,
212 keywords,
213 special,
214 pool));
215 *detranslated_file = detranslated;
217 else
218 *detranslated_file = merge_target;
220 return SVN_NO_ERROR;
223 /* Updates (by copying and translating) the eol style in
224 OLD_TARGET returning the filename containing the
225 correct eol style in NEW_TARGET, if an eol style
226 change is contained in PROP_DIFF */
227 static svn_error_t *
228 maybe_update_target_eols(const char **new_target,
229 const char *old_target,
230 svn_wc_adm_access_t *adm_access,
231 const apr_array_header_t *prop_diff,
232 apr_pool_t *pool)
234 const svn_prop_t *prop = get_prop(prop_diff, SVN_PROP_EOL_STYLE);
236 if (prop && prop->value)
238 const char *eol;
239 const char *tmp_new;
241 svn_subst_eol_style_from_value(NULL, &eol, prop->value->data);
242 SVN_ERR(svn_wc_create_tmp_file2(NULL, &tmp_new,
243 svn_wc_adm_access_path(adm_access),
244 svn_io_file_del_none,
245 pool));
246 SVN_ERR(svn_subst_copy_and_translate3(old_target,
247 tmp_new,
248 eol, TRUE,
249 NULL, FALSE,
250 FALSE, pool));
251 *new_target = tmp_new;
253 else
254 *new_target = old_target;
256 return SVN_NO_ERROR;
260 svn_error_t *
261 svn_wc__merge_internal(svn_stringbuf_t **log_accum,
262 enum svn_wc_merge_outcome_t *merge_outcome,
263 const char *left,
264 const char *right,
265 const char *merge_target,
266 const char *copyfrom_text,
267 svn_wc_adm_access_t *adm_access,
268 const char *left_label,
269 const char *right_label,
270 const char *target_label,
271 svn_boolean_t dry_run,
272 const char *diff3_cmd,
273 const apr_array_header_t *merge_options,
274 const apr_array_header_t *prop_diff,
275 svn_wc_conflict_resolver_func_t conflict_func,
276 void *conflict_baton,
277 apr_pool_t *pool)
279 const char *tmp_target, *result_target, *working_text;
280 const char *adm_path = svn_wc_adm_access_path(adm_access);
281 apr_file_t *result_f;
282 svn_boolean_t is_binary = FALSE;
283 const svn_wc_entry_t *entry;
284 svn_boolean_t contains_conflicts;
285 const svn_prop_t *mimeprop;
287 /* Sanity check: the merge target must be under revision control (unless
288 this is an add-with-history). */
289 SVN_ERR(svn_wc_entry(&entry, merge_target, adm_access, FALSE, pool));
290 if (! entry && ! copyfrom_text)
292 *merge_outcome = svn_wc_merge_no_merge;
293 return SVN_NO_ERROR;
296 /* Decide if the merge target is a text or binary file. */
297 if ((mimeprop = get_prop(prop_diff, SVN_PROP_MIME_TYPE))
298 && mimeprop->value)
299 is_binary = svn_mime_type_is_binary(mimeprop->value->data);
300 else if (! copyfrom_text)
301 SVN_ERR(svn_wc_has_binary_prop(&is_binary, merge_target, adm_access, pool));
303 working_text = copyfrom_text ? copyfrom_text : merge_target;
304 SVN_ERR(detranslate_wc_file(&tmp_target, working_text, adm_access,
305 (! is_binary) && diff3_cmd != NULL,
306 prop_diff, pool));
308 /* We cannot depend on the left file to contain the same eols as the
309 right file. If the merge target has mods, this will mark the entire
310 file as conflicted, so we need to compensate. */
311 SVN_ERR(maybe_update_target_eols(&left, left, adm_access, prop_diff, pool));
313 if (! is_binary) /* this is a text file */
315 /* Open a second temporary file for writing; this is where diff3
316 will write the merged results. */
317 SVN_ERR(svn_wc_create_tmp_file2(&result_f, &result_target,
318 adm_path, svn_io_file_del_none,
319 pool));
321 /* Run an external merge if requested. */
322 if (diff3_cmd)
324 int exit_code;
326 SVN_ERR(svn_io_run_diff3_2(&exit_code, ".",
327 tmp_target, left, right,
328 target_label, left_label, right_label,
329 result_f, diff3_cmd,
330 merge_options, pool));
332 contains_conflicts = exit_code == 1;
334 else
336 svn_diff_t *diff;
337 const char *target_marker;
338 const char *left_marker;
339 const char *right_marker;
340 svn_stream_t *ostream;
341 svn_diff_file_options_t *options;
343 ostream = svn_stream_from_aprfile(result_f, pool);
344 options = svn_diff_file_options_create(pool);
346 if (merge_options)
347 SVN_ERR(svn_diff_file_options_parse(options, merge_options, pool));
349 SVN_ERR(svn_diff_file_diff3_2(&diff,
350 left, tmp_target, right,
351 options, pool));
353 /* Labels fall back to sensible defaults if not specified. */
354 if (target_label)
355 target_marker = apr_psprintf(pool, "<<<<<<< %s", target_label);
356 else
357 target_marker = "<<<<<<< .working";
359 if (left_label)
360 left_marker = apr_psprintf(pool, "||||||| %s", left_label);
361 else
362 left_marker = "||||||| .old";
364 if (right_label)
365 right_marker = apr_psprintf(pool, ">>>>>>> %s", right_label);
366 else
367 right_marker = ">>>>>>> .new";
369 SVN_ERR(svn_diff_file_output_merge(ostream, diff,
370 left, tmp_target, right,
371 left_marker,
372 target_marker,
373 right_marker,
374 "=======", /* seperator */
375 FALSE, /* display original */
376 FALSE, /* resolve conflicts */
377 pool));
378 SVN_ERR(svn_stream_close(ostream));
380 contains_conflicts = svn_diff_contains_conflicts(diff);
383 /* Close the output file */
384 SVN_ERR(svn_io_file_close(result_f, pool));
386 if (contains_conflicts && ! dry_run) /* got a conflict */
388 const char *left_copy, *right_copy, *target_copy;
389 const char *tmp_left, *tmp_right, *tmp_target_copy;
390 const char *parentt, *target_base;
391 svn_wc_adm_access_t *parent_access;
392 svn_wc_entry_t tmp_entry;
394 /* Give the conflict resolution callback a chance to clean
395 up the conflict before we mark the file 'conflicted' */
396 if (conflict_func)
398 svn_wc_conflict_result_t *result = NULL;
399 svn_wc_conflict_description_t cdesc;
401 cdesc.path = merge_target;
402 cdesc.node_kind = svn_node_file;
403 cdesc.kind = svn_wc_conflict_kind_text;
404 cdesc.is_binary = FALSE;
405 cdesc.mime_type = (mimeprop && mimeprop->value)
406 ? mimeprop->value->data : NULL;
407 cdesc.access = adm_access;
408 cdesc.action = svn_wc_conflict_action_edit;
409 cdesc.reason = svn_wc_conflict_reason_edited;
410 cdesc.base_file = left;
411 cdesc.their_file = right;
412 cdesc.my_file = tmp_target;
413 cdesc.merged_file = result_target;
414 cdesc.property_name = NULL;
416 SVN_ERR(conflict_func(&result, &cdesc, conflict_baton, pool));
417 if (result == NULL)
418 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
419 NULL, _("Conflict callback violated API:"
420 " returned no results"));
422 switch (result->choice)
424 /* If the callback wants to use one of the fulltexts
425 to resolve the conflict, so be it.*/
426 case svn_wc_conflict_choose_base:
428 SVN_ERR(svn_wc__loggy_copy
429 (log_accum, NULL, adm_access,
430 svn_wc__copy_translate,
431 left, merge_target,
432 FALSE, pool));
433 *merge_outcome = svn_wc_merge_merged;
434 contains_conflicts = FALSE;
435 goto merge_complete;
437 case svn_wc_conflict_choose_theirs_full:
439 SVN_ERR(svn_wc__loggy_copy
440 (log_accum, NULL, adm_access,
441 svn_wc__copy_translate,
442 right, merge_target,
443 FALSE, pool));
444 *merge_outcome = svn_wc_merge_merged;
445 contains_conflicts = FALSE;
446 goto merge_complete;
448 case svn_wc_conflict_choose_mine_full:
450 /* Do nothing to merge_target, let it live untouched! */
451 *merge_outcome = svn_wc_merge_merged;
452 contains_conflicts = FALSE;
453 goto merge_complete;
456 /* For the case of 3-way file merging, we don't
457 really distinguish between these return values;
458 if the callback claims to have "generally
459 resolved" the situation, we still interpret
460 that as "OK, we'll assume the merged version is
461 good to use". */
462 case svn_wc_conflict_choose_merged:
464 SVN_ERR(svn_wc__loggy_copy
465 (log_accum, NULL, adm_access,
466 svn_wc__copy_translate,
467 /* Look for callback's own merged-file first: */
468 result->merged_file ?
469 result->merged_file : result_target,
470 merge_target,
471 FALSE, pool));
472 *merge_outcome = svn_wc_merge_merged;
473 contains_conflicts = FALSE;
474 goto merge_complete;
476 case svn_wc_conflict_choose_postpone:
477 default:
479 /* Assume conflict remains, fall through to code below. */
484 /* Preserve the three pre-merge files, and modify the
485 entry (mark as conflicted, track the preserved files). */
487 /* I miss Lisp. */
489 SVN_ERR(svn_io_open_unique_file2(NULL,
490 &left_copy,
491 merge_target,
492 left_label,
493 svn_io_file_del_none,
494 pool));
496 /* Have I mentioned how much I miss Lisp? */
498 SVN_ERR(svn_io_open_unique_file2(NULL,
499 &right_copy,
500 merge_target,
501 right_label,
502 svn_io_file_del_none,
503 pool));
505 /* Why, how much more pleasant to be forced to unroll my loops.
506 If I'd been writing in Lisp, I might have mapped an inline
507 lambda form over a list, or something equally disgusting.
508 Thank goodness C was here to protect me! */
510 SVN_ERR(svn_io_open_unique_file2(NULL,
511 &target_copy,
512 merge_target,
513 target_label,
514 svn_io_file_del_none,
515 pool));
517 /* We preserve all the files with keywords expanded and line
518 endings in local (working) form. */
520 svn_path_split(target_copy, &parentt, &target_base, pool);
521 SVN_ERR(svn_wc_adm_retrieve(&parent_access, adm_access, parentt,
522 pool));
524 /* Log files require their paths to be in the subtree
525 relative to the adm_access path they are executed in.
527 Make our LEFT and RIGHT files 'local' if they aren't... */
528 if (! svn_path_is_child(adm_path, left, pool))
530 SVN_ERR(svn_wc_create_tmp_file2
531 (NULL, &tmp_left,
532 adm_path, svn_io_file_del_none, pool));
533 SVN_ERR(svn_io_copy_file(left, tmp_left, TRUE, pool));
535 else
536 tmp_left = left;
538 if (! svn_path_is_child(adm_path, right, pool))
540 SVN_ERR(svn_wc_create_tmp_file2
541 (NULL, &tmp_right,
542 adm_path, svn_io_file_del_none, pool));
543 SVN_ERR(svn_io_copy_file(right, tmp_right, TRUE, pool));
545 else
546 tmp_right = right;
548 /* NOTE: Callers must ensure that the svn:eol-style and
549 svn:keywords property values are correct in the currently
550 installed props. With 'svn merge', it's no big deal. But
551 when 'svn up' calls this routine, it needs to make sure that
552 this routine is using the newest property values that may
553 have been received *during* the update. Since this routine
554 will be run from within a log-command, merge_file()
555 needs to make sure that a previous log-command to 'install
556 latest props' has already executed first. Ben and I just
557 checked, and that is indeed the order in which the log items
558 are written, so everything should be fine. Really. */
560 /* Create LEFT and RIGHT backup files, in expanded form.
561 We use merge_target's current properties to do the translation. */
562 /* Derive the basenames of the 3 backup files. */
563 SVN_ERR(svn_wc__loggy_translated_file(log_accum,
564 adm_access,
565 left_copy, tmp_left,
566 merge_target, pool));
567 SVN_ERR(svn_wc__loggy_translated_file(log_accum,
568 adm_access,
569 right_copy, tmp_right,
570 merge_target, pool));
572 /* Back up MERGE_TARGET through detranslation/retranslation:
573 the new translation properties may not match the current ones */
574 SVN_ERR(svn_wc_translated_file2(&tmp_target_copy,
575 merge_target,
576 merge_target,
577 adm_access,
578 SVN_WC_TRANSLATE_TO_NF
579 | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
580 pool));
581 SVN_ERR(svn_wc__loggy_translated_file
582 (log_accum, adm_access,
583 target_copy, tmp_target_copy, merge_target, pool));
585 tmp_entry.conflict_old
586 = svn_path_is_child(adm_path, left_copy, pool);
587 tmp_entry.conflict_new
588 = svn_path_is_child(adm_path, right_copy, pool);
589 tmp_entry.conflict_wrk = target_base;
591 /* Mark merge_target's entry as "Conflicted", and start tracking
592 the backup files in the entry as well. */
593 SVN_ERR(svn_wc__loggy_entry_modify
594 (log_accum, adm_access,
595 merge_target, &tmp_entry,
596 SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
597 | SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
598 | SVN_WC__ENTRY_MODIFY_CONFLICT_WRK,
599 pool));
601 *merge_outcome = svn_wc_merge_conflict;
603 else if (contains_conflicts && dry_run)
605 *merge_outcome = svn_wc_merge_conflict;
606 } /* end of conflict handling */
607 else if (copyfrom_text)
609 *merge_outcome = svn_wc_merge_merged;
611 else
613 svn_boolean_t same;
614 SVN_ERR(svn_io_files_contents_same_p(&same, result_target,
615 merge_target, pool));
617 *merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged;
620 if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run)
621 /* replace MERGE_TARGET with the new merged file, expanding. */
622 SVN_ERR(svn_wc__loggy_copy(log_accum, NULL,
623 adm_access,
624 svn_wc__copy_translate,
625 result_target, merge_target,
626 FALSE, pool));
628 } /* end of merging for text files */
630 else if (! dry_run) /* merging procedure for binary files */
632 /* ### when making the binary-file backups, should we be honoring
633 keywords and eol stuff? */
635 const char *left_copy, *right_copy;
636 const char *parentt, *left_base, *right_base;
637 svn_wc_entry_t tmp_entry;
639 /* Give the conflict resolution callback a chance to clean
640 up the conflict before we mark the file 'conflicted' */
641 if (conflict_func)
643 svn_wc_conflict_result_t *result = NULL;
644 svn_wc_conflict_description_t cdesc;
646 cdesc.path = merge_target;
647 cdesc.node_kind = svn_node_file;
648 cdesc.kind = svn_wc_conflict_kind_text;
649 cdesc.is_binary = TRUE;
650 cdesc.mime_type = (mimeprop && mimeprop->value)
651 ? mimeprop->value->data : NULL;
652 cdesc.access = adm_access;
653 cdesc.action = svn_wc_conflict_action_edit;
654 cdesc.reason = svn_wc_conflict_reason_edited;
655 cdesc.base_file = left;
656 cdesc.their_file = right;
657 cdesc.my_file = tmp_target;
658 cdesc.merged_file = NULL; /* notice there is NO merged file! */
659 cdesc.property_name = NULL;
661 SVN_ERR(conflict_func(&result, &cdesc, conflict_baton, pool));
662 if (result == NULL)
663 return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
664 NULL, _("Conflict callback violated API:"
665 " returned no results"));
667 switch (result->choice)
669 /* For a binary file, there's no merged file to look at,
670 unless the conflict-callback did the merging itself. */
671 case svn_wc_conflict_choose_base:
673 SVN_ERR(svn_wc__loggy_copy
674 (log_accum, NULL, adm_access,
675 svn_wc__copy_translate,
676 left, merge_target,
677 FALSE, pool));
678 *merge_outcome = svn_wc_merge_merged;
679 contains_conflicts = FALSE;
680 goto merge_complete;
682 case svn_wc_conflict_choose_theirs_full:
684 SVN_ERR(svn_wc__loggy_copy
685 (log_accum, NULL, adm_access,
686 svn_wc__copy_translate,
687 right, merge_target,
688 FALSE, pool));
689 *merge_outcome = svn_wc_merge_merged;
690 contains_conflicts = FALSE;
691 goto merge_complete;
693 /* For a binary file, if the response is to use the
694 user's file, we do nothing. We also do nothing if
695 the response claims to have already resolved the
696 problem.*/
697 case svn_wc_conflict_choose_mine_full:
699 *merge_outcome = svn_wc_merge_merged;
700 contains_conflicts = FALSE;
701 goto merge_complete;
703 case svn_wc_conflict_choose_merged:
705 if (! result->merged_file)
707 /* Callback asked us to choose its own
708 merged file, but didn't provide one! */
709 return svn_error_create
710 (SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
711 NULL, _("Conflict callback violated API:"
712 " returned no merged file"));
714 else
716 SVN_ERR(svn_wc__loggy_copy
717 (log_accum, NULL, adm_access,
718 svn_wc__copy_translate,
719 result->merged_file, merge_target,
720 FALSE, pool));
721 *merge_outcome = svn_wc_merge_merged;
722 contains_conflicts = FALSE;
723 goto merge_complete;
726 case svn_wc_conflict_choose_postpone:
727 default:
729 /* Assume conflict remains, fall through to code below. */
734 /* reserve names for backups of left and right fulltexts */
735 SVN_ERR(svn_io_open_unique_file2(NULL,
736 &left_copy,
737 merge_target,
738 left_label,
739 svn_io_file_del_none,
740 pool));
742 SVN_ERR(svn_io_open_unique_file2(NULL,
743 &right_copy,
744 merge_target,
745 right_label,
746 svn_io_file_del_none,
747 pool));
749 /* create the backup files */
750 SVN_ERR(svn_io_copy_file(left,
751 left_copy, TRUE, pool));
752 SVN_ERR(svn_io_copy_file(right,
753 right_copy, TRUE, pool));
755 /* Was the merge target detranslated? */
756 if (merge_target != tmp_target)
758 /* Create a .mine file too */
759 const char *mine_copy;
761 SVN_ERR(svn_io_open_unique_file2(NULL,
762 &mine_copy,
763 merge_target,
764 target_label,
765 svn_io_file_del_none,
766 pool));
767 SVN_ERR(svn_wc__loggy_move(log_accum, NULL,
768 adm_access,
769 tmp_target,
770 mine_copy,
771 FALSE, pool));
772 mine_copy = svn_path_is_child(adm_path, mine_copy, pool);
773 tmp_entry.conflict_wrk = mine_copy;
775 else
776 tmp_entry.conflict_wrk = NULL;
778 /* Derive the basenames of the backup files. */
779 svn_path_split(left_copy, &parentt, &left_base, pool);
780 svn_path_split(right_copy, &parentt, &right_base, pool);
782 /* Mark merge_target's entry as "Conflicted", and start tracking
783 the backup files in the entry as well. */
784 tmp_entry.conflict_old = left_base;
785 tmp_entry.conflict_new = right_base;
786 SVN_ERR(svn_wc__loggy_entry_modify
787 (log_accum,
788 adm_access, merge_target,
789 &tmp_entry,
790 SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
791 | SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
792 | SVN_WC__ENTRY_MODIFY_CONFLICT_WRK,
793 pool));
795 *merge_outcome = svn_wc_merge_conflict; /* a conflict happened */
797 } /* end of binary conflict handling */
798 else
799 *merge_outcome = svn_wc_merge_conflict; /* dry_run for binary files. */
801 merge_complete:
802 /* Merging is complete. Regardless of text or binariness, we might
803 need to tweak the executable bit on the new working file. */
804 if (! dry_run)
806 SVN_ERR(svn_wc__loggy_maybe_set_executable(log_accum,
807 adm_access, merge_target,
808 pool));
810 SVN_ERR(svn_wc__loggy_maybe_set_readonly(log_accum,
811 adm_access, merge_target,
812 pool));
816 return SVN_NO_ERROR;
821 svn_error_t *
822 svn_wc_merge3(enum svn_wc_merge_outcome_t *merge_outcome,
823 const char *left,
824 const char *right,
825 const char *merge_target,
826 svn_wc_adm_access_t *adm_access,
827 const char *left_label,
828 const char *right_label,
829 const char *target_label,
830 svn_boolean_t dry_run,
831 const char *diff3_cmd,
832 const apr_array_header_t *merge_options,
833 const apr_array_header_t *prop_diff,
834 svn_wc_conflict_resolver_func_t conflict_func,
835 void *conflict_baton,
836 apr_pool_t *pool)
838 svn_stringbuf_t *log_accum = svn_stringbuf_create("", pool);
840 SVN_ERR(svn_wc__merge_internal(&log_accum, merge_outcome,
841 left, right, merge_target,
842 NULL,
843 adm_access,
844 left_label, right_label, target_label,
845 dry_run,
846 diff3_cmd,
847 merge_options,
848 prop_diff,
849 conflict_func, conflict_baton,
850 pool));
852 /* Write our accumulation of log entries into a log file */
853 SVN_ERR(svn_wc__write_log(adm_access, 0, log_accum, pool));
855 SVN_ERR(svn_wc__run_log(adm_access, NULL, pool));
857 return SVN_NO_ERROR;
861 svn_error_t *
862 svn_wc_merge2(enum svn_wc_merge_outcome_t *merge_outcome,
863 const char *left,
864 const char *right,
865 const char *merge_target,
866 svn_wc_adm_access_t *adm_access,
867 const char *left_label,
868 const char *right_label,
869 const char *target_label,
870 svn_boolean_t dry_run,
871 const char *diff3_cmd,
872 const apr_array_header_t *merge_options,
873 apr_pool_t *pool)
875 return svn_wc_merge3(merge_outcome,
876 left, right, merge_target, adm_access,
877 left_label, right_label, target_label,
878 dry_run, diff3_cmd, merge_options, NULL,
879 NULL, NULL, pool);
882 svn_error_t *
883 svn_wc_merge(const char *left,
884 const char *right,
885 const char *merge_target,
886 svn_wc_adm_access_t *adm_access,
887 const char *left_label,
888 const char *right_label,
889 const char *target_label,
890 svn_boolean_t dry_run,
891 enum svn_wc_merge_outcome_t *merge_outcome,
892 const char *diff3_cmd,
893 apr_pool_t *pool)
895 return svn_wc_merge3(merge_outcome,
896 left, right, merge_target, adm_access,
897 left_label, right_label, target_label,
898 dry_run, diff3_cmd, NULL, NULL, NULL,
899 NULL, pool);
904 /* Constructor for the result-structure returned by conflict callbacks. */
905 svn_wc_conflict_result_t *
906 svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
907 const char *merged_file,
908 apr_pool_t *pool)
910 svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
911 result->choice = choice;
912 result->merged_file = merged_file;
914 /* If we add more fields to svn_wc_conflict_result_t, add them here. */
916 return result;