2 * merge.c: merging changes into a working file
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
26 #include "svn_dirent_uri.h"
28 #include "svn_pools.h"
31 #include "adm_files.h"
32 #include "conflicts.h"
33 #include "translate.h"
34 #include "workqueue.h"
36 #include "private/svn_skel.h"
38 #include "svn_private_config.h"
40 /* Contains some information on the merge target before merge, and some
41 information needed for the diff processing. */
42 typedef struct merge_target_t
44 svn_wc__db_t *db; /* The DB used to access target */
45 const char *local_abspath; /* The absolute path to target */
46 const char *wri_abspath; /* The working copy of target */
48 apr_hash_t *old_actual_props; /* The set of actual properties
50 const apr_array_header_t *prop_diff; /* The property changes */
52 const char *diff3_cmd; /* The diff3 command and options */
53 const apr_array_header_t *merge_options;
58 /* Return a pointer to the svn_prop_t structure from PROP_DIFF
59 belonging to PROP_NAME, if any. NULL otherwise.*/
60 static const svn_prop_t *
61 get_prop(const apr_array_header_t *prop_diff,
62 const char *prop_name)
67 for (i = 0; i < prop_diff->nelts; i++)
69 const svn_prop_t *elt = &APR_ARRAY_IDX(prop_diff, i,
72 if (strcmp(elt->name, prop_name) == 0)
81 /* Detranslate a working copy file MERGE_TARGET to achieve the effect of:
88 in one pass, to get a file which can be compared with the left and right
89 files which are in repository normal form.
91 Property changes make this a little complex though. Changes in
98 may change the way a file is translated.
100 Effect for svn:mime-type:
102 If svn:mime-type is considered 'binary', we ignore svn:eol-style (but
103 still translate keywords).
105 I) both old and new mime-types are texty
106 -> just do the translation dance (as lined out below)
107 ### actually we do a shortcut with just one translation:
108 detranslate with the old keywords and ... eol-style
109 (the new re+detranslation is a no-op w.r.t. keywords [1])
111 II) the old one is texty, the new one is binary
112 -> detranslate with the old eol-style and keywords
113 (the new re+detranslation is a no-op [1])
115 III) the old one is binary, the new one texty
116 -> detranslate with the old keywords and new eol-style
117 (the old detranslation is a no-op w.r.t. eol, and
118 the new re+detranslation is a no-op w.r.t. keywords [1])
120 IV) the old and new ones are binary
121 -> detranslate with the old keywords
122 (the new re+detranslation is a no-op [1])
124 Effect for svn:eol-style
126 I) On add or change of svn:eol-style, use the new value
128 II) otherwise: use the old value (absent means 'no translation')
130 Effect for svn:keywords
132 Always use the old settings (re+detranslation are no-op [1]).
134 [1] Translation of keywords from repository normal form to WC form and
135 back is normally a no-op, but is not a no-op if text contains a kw
136 that is only enabled by the new props and is present in non-
137 contracted form (such as "$Rev: 1234 $"). If we want to catch this
138 case we should detranslate with both the old & the new keywords
141 Effect for svn:special
143 Always use the old settings (re+detranslation are no-op).
145 Sets *DETRANSLATED_ABSPATH to the path to the detranslated file,
146 this may be the same as SOURCE_ABSPATH if FORCE_COPY is FALSE and no
147 translation is required.
149 If FORCE_COPY is FALSE and *DETRANSLATED_ABSPATH is a file distinct
150 from SOURCE_ABSPATH then the file will be deleted on RESULT_POOL
153 If FORCE_COPY is TRUE then *DETRANSLATED_ABSPATH will always be a
154 new file distinct from SOURCE_ABSPATH and it will be the callers
155 responsibility to delete the file.
159 detranslate_wc_file(const char **detranslated_abspath,
160 const merge_target_t *mt,
161 svn_boolean_t force_copy,
162 const char *source_abspath,
163 svn_cancel_func_t cancel_func,
165 apr_pool_t *result_pool,
166 apr_pool_t *scratch_pool)
168 svn_boolean_t old_is_binary, new_is_binary;
169 svn_subst_eol_style_t style;
171 apr_hash_t *keywords;
172 svn_boolean_t special;
175 const char *old_mime_value
176 = svn_prop_get_value(mt->old_actual_props, SVN_PROP_MIME_TYPE);
177 const svn_prop_t *prop = get_prop(mt->prop_diff, SVN_PROP_MIME_TYPE);
178 const char *new_mime_value
179 = prop ? (prop->value ? prop->value->data : NULL) : old_mime_value;
181 old_is_binary = old_mime_value && svn_mime_type_is_binary(old_mime_value);
182 new_is_binary = new_mime_value && svn_mime_type_is_binary(new_mime_value);;
185 /* See what translations we want to do */
186 if (old_is_binary && new_is_binary)
188 /* Case IV. Old and new props 'binary': detranslate keywords only */
189 SVN_ERR(svn_wc__get_translate_info(NULL, NULL, &keywords, NULL,
190 mt->db, mt->local_abspath,
191 mt->old_actual_props, TRUE,
192 scratch_pool, scratch_pool));
193 /* ### Why override 'special'? Elsewhere it has precedence. */
196 style = svn_subst_eol_style_none;
198 else if (!old_is_binary && new_is_binary)
200 /* Case II. Old props indicate texty, new props indicate binary:
201 detranslate keywords and old eol-style */
202 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
205 mt->db, mt->local_abspath,
206 mt->old_actual_props, TRUE,
207 scratch_pool, scratch_pool));
211 /* Case I & III. New props indicate texty, regardless of old props */
213 /* In case the file used to be special, detranslate specially */
214 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
217 mt->db, mt->local_abspath,
218 mt->old_actual_props, TRUE,
219 scratch_pool, scratch_pool));
225 style = svn_subst_eol_style_none;
229 const svn_prop_t *prop;
231 /* In case a new eol style was set, use that for detranslation */
232 if ((prop = get_prop(mt->prop_diff, SVN_PROP_EOL_STYLE)) && prop->value)
234 /* Value added or changed */
235 svn_subst_eol_style_from_value(&style, &eol, prop->value->data);
237 else if (!old_is_binary)
239 /* Already fetched */
244 style = svn_subst_eol_style_none;
249 /* Now, detranslate with the settings we created above */
251 if (force_copy || keywords || eol || special)
253 const char *temp_dir_abspath;
254 const char *detranslated;
256 /* Force a copy into the temporary wc area to avoid having
257 temporary files created below to appear in the actual wc. */
258 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, mt->db,
260 scratch_pool, scratch_pool));
262 /* ### svn_subst_copy_and_translate4() also creates a tempfile
263 ### internally. Anyway to piggyback on that? */
264 SVN_ERR(svn_io_open_unique_file3(NULL, &detranslated, temp_dir_abspath,
266 ? svn_io_file_del_none
267 : svn_io_file_del_on_pool_cleanup),
268 result_pool, scratch_pool));
270 /* Always 'repair' EOLs here, so that we can apply a diff that
271 changes from inconsistent newlines and no 'svn:eol-style' to
272 consistent newlines and 'svn:eol-style' set. */
274 if (style == svn_subst_eol_style_native)
275 eol = SVN_SUBST_NATIVE_EOL_STR;
276 else if (style != svn_subst_eol_style_fixed
277 && style != svn_subst_eol_style_none)
278 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
280 SVN_ERR(svn_subst_copy_and_translate4(source_abspath,
285 FALSE /* contract keywords */,
287 cancel_func, cancel_baton,
290 SVN_ERR(svn_dirent_get_absolute(detranslated_abspath, detranslated,
294 *detranslated_abspath = apr_pstrdup(result_pool, source_abspath);
299 /* Updates (by copying and translating) the eol style in
300 OLD_TARGET_ABSPATH returning the filename containing the
301 correct eol style in NEW_TARGET_ABSPATH, if an eol style
302 change is contained in PROP_DIFF. */
304 maybe_update_target_eols(const char **new_target_abspath,
305 const apr_array_header_t *prop_diff,
306 const char *old_target_abspath,
307 svn_cancel_func_t cancel_func,
309 apr_pool_t *result_pool,
310 apr_pool_t *scratch_pool)
312 const svn_prop_t *prop = get_prop(prop_diff, SVN_PROP_EOL_STYLE);
314 if (prop && prop->value)
319 svn_subst_eol_style_from_value(NULL, &eol, prop->value->data);
320 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_new, NULL,
321 svn_io_file_del_on_pool_cleanup,
322 result_pool, scratch_pool));
324 /* Always 'repair' EOLs here, so that we can apply a diff that
325 changes from inconsistent newlines and no 'svn:eol-style' to
326 consistent newlines and 'svn:eol-style' set. */
327 SVN_ERR(svn_subst_copy_and_translate4(old_target_abspath,
334 cancel_func, cancel_baton,
336 *new_target_abspath = apr_pstrdup(result_pool, tmp_new);
339 *new_target_abspath = apr_pstrdup(result_pool, old_target_abspath);
345 /* Set *TARGET_MARKER, *LEFT_MARKER and *RIGHT_MARKER to strings suitable
346 for delimiting the alternative texts in a text conflict. Include in each
347 marker a string that may be given by TARGET_LABEL, LEFT_LABEL and
348 RIGHT_LABEL respectively or a default value where any of those are NULL.
350 Allocate the results in POOL or statically. */
352 init_conflict_markers(const char **target_marker,
353 const char **left_marker,
354 const char **right_marker,
355 const char *target_label,
356 const char *left_label,
357 const char *right_label,
360 /* Labels fall back to sensible defaults if not specified. */
362 *target_marker = apr_psprintf(pool, "<<<<<<< %s", target_label);
364 *target_marker = "<<<<<<< .working";
367 *left_marker = apr_psprintf(pool, "||||||| %s", left_label);
369 *left_marker = "||||||| .old";
372 *right_marker = apr_psprintf(pool, ">>>>>>> %s", right_label);
374 *right_marker = ">>>>>>> .new";
377 /* Do a 3-way merge of the files at paths LEFT, DETRANSLATED_TARGET,
378 * and RIGHT, using diff options provided in MERGE_OPTIONS. Store the merge
379 * result in the file RESULT_F.
380 * If there are conflicts, set *CONTAINS_CONFLICTS to true, and use
381 * TARGET_LABEL, LEFT_LABEL, and RIGHT_LABEL as labels for conflict
382 * markers. Else, set *CONTAINS_CONFLICTS to false.
383 * Do all allocations in POOL. */
385 do_text_merge(svn_boolean_t *contains_conflicts,
386 apr_file_t *result_f,
387 const apr_array_header_t *merge_options,
388 const char *detranslated_target,
391 const char *target_label,
392 const char *left_label,
393 const char *right_label,
397 svn_stream_t *ostream;
398 const char *target_marker;
399 const char *left_marker;
400 const char *right_marker;
401 svn_diff_file_options_t *diff3_options;
403 diff3_options = svn_diff_file_options_create(pool);
406 SVN_ERR(svn_diff_file_options_parse(diff3_options,
407 merge_options, pool));
410 init_conflict_markers(&target_marker, &left_marker, &right_marker,
411 target_label, left_label, right_label, pool);
413 SVN_ERR(svn_diff_file_diff3_2(&diff, left, detranslated_target, right,
414 diff3_options, pool));
416 ostream = svn_stream_from_aprfile2(result_f, TRUE, pool);
418 SVN_ERR(svn_diff_file_output_merge2(ostream, diff,
419 left, detranslated_target, right,
423 "=======", /* separator */
424 svn_diff_conflict_display_modified_original_latest,
426 SVN_ERR(svn_stream_close(ostream));
428 *contains_conflicts = svn_diff_contains_conflicts(diff);
433 /* Same as do_text_merge() above, but use the external diff3
434 * command DIFF3_CMD to perform the merge. Pass MERGE_OPTIONS
435 * to the diff3 command. Do all allocations in POOL. */
437 do_text_merge_external(svn_boolean_t *contains_conflicts,
438 apr_file_t *result_f,
439 const char *diff3_cmd,
440 const apr_array_header_t *merge_options,
441 const char *detranslated_target,
442 const char *left_abspath,
443 const char *right_abspath,
444 const char *target_label,
445 const char *left_label,
446 const char *right_label,
447 apr_pool_t *scratch_pool)
451 SVN_ERR(svn_io_run_diff3_3(&exit_code, ".",
452 detranslated_target, left_abspath, right_abspath,
453 target_label, left_label, right_label,
455 merge_options, scratch_pool));
457 *contains_conflicts = exit_code == 1;
462 /* Preserve the three pre-merge files.
464 Create three empty files, with unique names that each include the
465 basename of TARGET_ABSPATH and one of LEFT_LABEL, RIGHT_LABEL and
466 TARGET_LABEL, in the directory that contains TARGET_ABSPATH. Typical
467 names are "foo.c.r37" or "foo.c.2.mine". Set *LEFT_COPY, *RIGHT_COPY and
468 *TARGET_COPY to their absolute paths.
470 Set *WORK_ITEMS to a list of new work items that will write copies of
471 LEFT_ABSPATH, RIGHT_ABSPATH and TARGET_ABSPATH into the three files,
472 translated to working-copy form.
474 The translation to working-copy form will be done according to the
475 versioned properties of TARGET_ABSPATH that are current when the work
476 queue items are executed.
478 If target_abspath is not versioned use detranslated_target_abspath
480 ### NOT IMPLEMENTED -- 'detranslated_target_abspath' is not used.
483 preserve_pre_merge_files(svn_skel_t **work_items,
484 const char **left_copy,
485 const char **right_copy,
486 const char **target_copy,
487 const merge_target_t *mt,
488 const char *left_abspath,
489 const char *right_abspath,
490 const char *left_label,
491 const char *right_label,
492 const char *target_label,
493 const char *detranslated_target_abspath,
494 svn_cancel_func_t cancel_func,
496 apr_pool_t *result_pool,
497 apr_pool_t *scratch_pool)
499 const char *tmp_left, *tmp_right, *detranslated_target_copy;
500 const char *dir_abspath, *target_name;
501 const char *wcroot_abspath, *temp_dir_abspath;
502 svn_skel_t *work_item, *last_items = NULL;
506 svn_dirent_split(&dir_abspath, &target_name, mt->local_abspath,
509 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, mt->db, mt->wri_abspath,
510 scratch_pool, scratch_pool));
511 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, mt->db,
513 scratch_pool, scratch_pool));
515 /* Create three empty files in DIR_ABSPATH, naming them with unique names
516 that each include TARGET_NAME and one of {LEFT,RIGHT,TARGET}_LABEL,
517 and set *{LEFT,RIGHT,TARGET}_COPY to those names. */
518 SVN_ERR(svn_io_open_uniquely_named(
519 NULL, left_copy, dir_abspath, target_name, left_label,
520 svn_io_file_del_none, result_pool, scratch_pool));
521 SVN_ERR(svn_io_open_uniquely_named(
522 NULL, right_copy, dir_abspath, target_name, right_label,
523 svn_io_file_del_none, result_pool, scratch_pool));
524 SVN_ERR(svn_io_open_uniquely_named(
525 NULL, target_copy, dir_abspath, target_name, target_label,
526 svn_io_file_del_none, result_pool, scratch_pool));
528 /* We preserve all the files with keywords expanded and line
529 endings in local (working) form. */
531 /* The workingqueue requires its paths to be in the subtree
532 relative to the wcroot path they are executed in.
534 Make our LEFT and RIGHT files 'local' if they aren't... */
535 if (! svn_dirent_is_ancestor(wcroot_abspath, left_abspath))
537 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_left, temp_dir_abspath,
538 svn_io_file_del_none,
539 scratch_pool, scratch_pool));
540 SVN_ERR(svn_io_copy_file(left_abspath, tmp_left, TRUE, scratch_pool));
542 /* And create a wq item to remove the file later */
543 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath,
545 result_pool, scratch_pool));
547 last_items = svn_wc__wq_merge(last_items, work_item, result_pool);
550 tmp_left = left_abspath;
552 if (! svn_dirent_is_ancestor(wcroot_abspath, right_abspath))
554 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_right, temp_dir_abspath,
555 svn_io_file_del_none,
556 scratch_pool, scratch_pool));
557 SVN_ERR(svn_io_copy_file(right_abspath, tmp_right, TRUE, scratch_pool));
559 /* And create a wq item to remove the file later */
560 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath,
562 result_pool, scratch_pool));
564 last_items = svn_wc__wq_merge(last_items, work_item, result_pool);
567 tmp_right = right_abspath;
569 /* NOTE: Callers must ensure that the svn:eol-style and
570 svn:keywords property values are correct in the currently
571 installed props. With 'svn merge', it's no big deal. But
572 when 'svn up' calls this routine, it needs to make sure that
573 this routine is using the newest property values that may
574 have been received *during* the update. Since this routine
575 will be run from within a log-command, merge_file()
576 needs to make sure that a previous log-command to 'install
577 latest props' has already executed first. Ben and I just
578 checked, and that is indeed the order in which the log items
579 are written, so everything should be fine. Really. */
581 /* Create LEFT and RIGHT backup files, in expanded form.
582 We use TARGET_ABSPATH's current properties to do the translation. */
583 /* Derive the basenames of the 3 backup files. */
584 SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
585 mt->db, mt->local_abspath,
586 tmp_left, *left_copy,
587 result_pool, scratch_pool));
588 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
590 SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
591 mt->db, mt->local_abspath,
592 tmp_right, *right_copy,
593 result_pool, scratch_pool));
594 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
596 /* Back up TARGET_ABSPATH through detranslation/retranslation:
597 the new translation properties may not match the current ones */
598 SVN_ERR(detranslate_wc_file(&detranslated_target_copy, mt, TRUE,
600 cancel_func, cancel_baton,
601 scratch_pool, scratch_pool));
603 SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
604 mt->db, mt->local_abspath,
605 detranslated_target_copy,
607 result_pool, scratch_pool));
608 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
610 /* And maybe delete some tempfiles */
611 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, wcroot_abspath,
612 detranslated_target_copy,
613 result_pool, scratch_pool));
614 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
616 *work_items = svn_wc__wq_merge(*work_items, last_items, result_pool);
621 /* Attempt a trivial merge of LEFT_ABSPATH and RIGHT_ABSPATH to
622 * the target file at TARGET_ABSPATH.
624 * These are the inherently trivial cases:
626 * left == right == target => no-op
627 * left != right, left == target => target := right
629 * This case is also treated as trivial:
631 * left != right, right == target => no-op
633 * ### Strictly, this case is a conflict, and the no-op outcome is only
634 * one of the possible resolutions.
636 * TODO: Raise a conflict at this level and implement the 'no-op'
637 * resolution of that conflict at a higher level, in preparation for
638 * being able to support stricter conflict detection.
640 * This case is inherently trivial but not currently handled here:
642 * left == right != target => no-op
644 * The files at LEFT_ABSPATH and RIGHT_ABSPATH are in repository normal
645 * form. The file at DETRANSLATED_TARGET_ABSPATH is a copy of the target,
646 * 'detranslated' to repository normal form, or may be the target file
647 * itself if no translation is necessary.
649 * When this function updates the target file, it translates to working copy
652 * On success, set *MERGE_OUTCOME to SVN_WC_MERGE_MERGED in case the
653 * target was changed, or to SVN_WC_MERGE_UNCHANGED if the target was not
654 * changed. Install work queue items allocated in RESULT_POOL in *WORK_ITEMS.
655 * On failure, set *MERGE_OUTCOME to SVN_WC_MERGE_NO_MERGE.
658 merge_file_trivial(svn_skel_t **work_items,
659 enum svn_wc_merge_outcome_t *merge_outcome,
660 const char *left_abspath,
661 const char *right_abspath,
662 const char *target_abspath,
663 const char *detranslated_target_abspath,
664 svn_boolean_t dry_run,
666 svn_cancel_func_t cancel_func,
668 apr_pool_t *result_pool,
669 apr_pool_t *scratch_pool)
671 svn_skel_t *work_item;
672 svn_boolean_t same_left_right;
673 svn_boolean_t same_right_target;
674 svn_boolean_t same_left_target;
675 svn_node_kind_t kind;
676 svn_boolean_t is_special;
678 /* If the target is not a normal file, do not attempt a trivial merge. */
679 SVN_ERR(svn_io_check_special_path(target_abspath, &kind, &is_special,
681 if (kind != svn_node_file || is_special)
683 *merge_outcome = svn_wc_merge_no_merge;
687 /* Check the files */
688 SVN_ERR(svn_io_files_contents_three_same_p(&same_left_right,
693 detranslated_target_abspath,
696 /* If the LEFT side of the merge is equal to WORKING, then we can
697 * copy RIGHT directly. */
698 if (same_left_target)
700 /* If the left side equals the right side, there is no change to merge
701 * so we leave the target unchanged. */
704 *merge_outcome = svn_wc_merge_unchanged;
708 *merge_outcome = svn_wc_merge_merged;
711 const char *wcroot_abspath;
712 svn_boolean_t delete_src = FALSE;
714 /* The right_abspath might be outside our working copy. In that
715 case we should copy the file to a safe location before
716 installing to avoid breaking the workqueue.
718 This matches the behavior in preserve_pre_merge_files */
720 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath,
722 scratch_pool, scratch_pool));
724 if (!svn_dirent_is_child(wcroot_abspath, right_abspath, NULL))
726 svn_stream_t *tmp_src;
727 svn_stream_t *tmp_dst;
729 SVN_ERR(svn_stream_open_readonly(&tmp_src, right_abspath,
733 SVN_ERR(svn_wc__open_writable_base(&tmp_dst, &right_abspath,
739 SVN_ERR(svn_stream_copy3(tmp_src, tmp_dst,
740 cancel_func, cancel_baton,
746 SVN_ERR(svn_wc__wq_build_file_install(
747 &work_item, db, target_abspath, right_abspath,
748 FALSE /* use_commit_times */,
749 FALSE /* record_fileinfo */,
750 result_pool, scratch_pool));
751 *work_items = svn_wc__wq_merge(*work_items, work_item,
756 SVN_ERR(svn_wc__wq_build_file_remove(
757 &work_item, db, wcroot_abspath,
759 result_pool, scratch_pool));
760 *work_items = svn_wc__wq_merge(*work_items, work_item,
770 /* If the locally existing, changed file equals the incoming 'right'
771 * file, there is no conflict. For binary files, we historically
772 * conflicted them needlessly, while merge_text_file figured it out
773 * eventually and returned svn_wc_merge_unchanged for them, which
774 * is what we do here. */
775 if (same_right_target)
777 *merge_outcome = svn_wc_merge_unchanged;
782 *merge_outcome = svn_wc_merge_no_merge;
787 /* Handle a non-trivial merge of 'text' files. (Assume that a trivial
788 * merge was not possible.)
790 * Set *WORK_ITEMS, *CONFLICT_SKEL and *MERGE_OUTCOME according to the
791 * result -- to install the merged file, or to indicate a conflict.
793 * On successful merge, leave the result in a temporary file and set
794 * *WORK_ITEMS to hold work items that will translate and install that
795 * file into its proper form and place (unless DRY_RUN) and delete the
796 * temporary file (in any case). Set *MERGE_OUTCOME to 'merged' or
799 * If a conflict occurs, set *MERGE_OUTCOME to 'conflicted', and (unless
800 * DRY_RUN) set *WORK_ITEMS and *CONFLICT_SKEL to record the conflict
801 * and copies of the pre-merge files. See preserve_pre_merge_files()
804 * On entry, all of the output pointers must be non-null and *CONFLICT_SKEL
805 * must either point to an existing conflict skel or be NULL.
808 merge_text_file(svn_skel_t **work_items,
809 svn_skel_t **conflict_skel,
810 enum svn_wc_merge_outcome_t *merge_outcome,
811 const merge_target_t *mt,
812 const char *left_abspath,
813 const char *right_abspath,
814 const char *left_label,
815 const char *right_label,
816 const char *target_label,
817 svn_boolean_t dry_run,
818 const char *detranslated_target_abspath,
819 svn_cancel_func_t cancel_func,
821 apr_pool_t *result_pool,
822 apr_pool_t *scratch_pool)
824 apr_pool_t *pool = scratch_pool; /* ### temporary rename */
825 svn_boolean_t contains_conflicts;
826 apr_file_t *result_f;
827 const char *result_target;
828 const char *base_name;
829 const char *temp_dir;
830 svn_skel_t *work_item;
834 base_name = svn_dirent_basename(mt->local_abspath, scratch_pool);
836 /* Open a second temporary file for writing; this is where diff3
837 will write the merged results. We want to use a tempfile
838 with a name that reflects the original, in case this
839 ultimately winds up in a conflict resolution editor. */
840 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, mt->db, mt->wri_abspath,
842 SVN_ERR(svn_io_open_uniquely_named(&result_f, &result_target,
843 temp_dir, base_name, ".tmp",
844 svn_io_file_del_none, pool, pool));
846 /* Run the external or internal merge, as requested. */
848 SVN_ERR(do_text_merge_external(&contains_conflicts,
852 detranslated_target_abspath,
859 else /* Use internal merge. */
860 SVN_ERR(do_text_merge(&contains_conflicts,
863 detranslated_target_abspath,
871 SVN_ERR(svn_io_file_close(result_f, pool));
873 /* Determine the MERGE_OUTCOME, and record any conflict. */
874 if (contains_conflicts && ! dry_run)
876 *merge_outcome = svn_wc_merge_conflict;
877 if (*merge_outcome == svn_wc_merge_conflict)
879 const char *left_copy, *right_copy, *target_copy;
881 /* Preserve the three conflict files */
882 SVN_ERR(preserve_pre_merge_files(
884 &left_copy, &right_copy, &target_copy,
885 mt, left_abspath, right_abspath,
886 left_label, right_label, target_label,
887 detranslated_target_abspath,
888 cancel_func, cancel_baton,
889 result_pool, scratch_pool));
890 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
892 /* Track the conflict marker files in the metadata. */
895 *conflict_skel = svn_wc__conflict_skel_create(result_pool);
897 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(*conflict_skel,
898 mt->db, mt->local_abspath,
906 if (*merge_outcome == svn_wc_merge_merged)
909 else if (contains_conflicts && dry_run)
910 *merge_outcome = svn_wc_merge_conflict;
913 svn_boolean_t same, special;
915 /* If 'special', then use the detranslated form of the
916 target file. This is so we don't try to follow symlinks,
917 but the same treatment is probably also appropriate for
918 whatever special file types we may invent in the future. */
919 SVN_ERR(svn_wc__get_translate_info(NULL, NULL, NULL,
920 &special, mt->db, mt->local_abspath,
921 mt->old_actual_props, TRUE,
923 SVN_ERR(svn_io_files_contents_same_p(&same, result_target,
925 detranslated_target_abspath :
929 *merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged;
932 if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run)
934 /* replace TARGET_ABSPATH with the new merged file, expanding. */
935 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
936 mt->db, mt->local_abspath,
938 FALSE /* use_commit_times */,
939 FALSE /* record_fileinfo */,
940 result_pool, scratch_pool));
941 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
945 /* Remove the tempfile after use */
946 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, mt->local_abspath,
948 result_pool, scratch_pool));
950 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
955 /* Handle a non-trivial merge of 'binary' files: don't actually merge, just
956 * flag a conflict. (Assume that a trivial merge was not possible.)
958 * Copy* the files at LEFT_ABSPATH and RIGHT_ABSPATH into the same directory
959 * as the target file, giving them unique names that start with the target
960 * file's name and end with LEFT_LABEL and RIGHT_LABEL respectively.
961 * If the merge target has been 'detranslated' to repository normal form,
962 * move the detranslated file similarly to a unique name ending with
965 * ### * Why do we copy the left and right temp files when we could (maybe
966 * not always?) move them?
968 * On entry, all of the output pointers must be non-null and *CONFLICT_SKEL
969 * must either point to an existing conflict skel or be NULL.
971 * Set *WORK_ITEMS, *CONFLICT_SKEL and *MERGE_OUTCOME to indicate the
974 * ### Why do we not use preserve_pre_merge_files() in here? The
975 * behaviour would be slightly different, more consistent: the
976 * preserved 'left' and 'right' files would be translated to working
977 * copy form, which may make a difference when a binary file
978 * contains keyword expansions or when some versions of the file are
979 * not 'binary' even though we're merging in 'binary files' mode.
982 merge_binary_file(svn_skel_t **work_items,
983 svn_skel_t **conflict_skel,
984 enum svn_wc_merge_outcome_t *merge_outcome,
985 const merge_target_t *mt,
986 const char *left_abspath,
987 const char *right_abspath,
988 const char *left_label,
989 const char *right_label,
990 const char *target_label,
991 svn_boolean_t dry_run,
992 const char *detranslated_target_abspath,
993 apr_pool_t *result_pool,
994 apr_pool_t *scratch_pool)
996 apr_pool_t *pool = scratch_pool; /* ### temporary rename */
997 /* ### when making the binary-file backups, should we be honoring
998 keywords and eol stuff? */
999 const char *left_copy, *right_copy;
1000 const char *merge_dirpath, *merge_filename;
1001 const char *conflict_wrk;
1005 svn_dirent_split(&merge_dirpath, &merge_filename, mt->local_abspath, pool);
1009 *merge_outcome = svn_wc_merge_conflict;
1010 return SVN_NO_ERROR;
1013 /* reserve names for backups of left and right fulltexts */
1014 SVN_ERR(svn_io_open_uniquely_named(NULL,
1019 svn_io_file_del_none,
1022 SVN_ERR(svn_io_open_uniquely_named(NULL,
1027 svn_io_file_del_none,
1030 /* create the backup files */
1031 SVN_ERR(svn_io_copy_file(left_abspath, left_copy, TRUE, pool));
1032 SVN_ERR(svn_io_copy_file(right_abspath, right_copy, TRUE, pool));
1034 /* Was the merge target detranslated? */
1035 if (strcmp(mt->local_abspath, detranslated_target_abspath) != 0)
1037 /* Create a .mine file too */
1038 SVN_ERR(svn_io_open_uniquely_named(NULL,
1043 svn_io_file_del_none,
1045 SVN_ERR(svn_wc__wq_build_file_move(work_items, mt->db,
1047 detranslated_target_abspath,
1049 pool, result_pool));
1053 conflict_wrk = NULL;
1056 /* Mark target_abspath's entry as "Conflicted", and start tracking
1057 the backup files in the entry as well. */
1058 if (!*conflict_skel)
1059 *conflict_skel = svn_wc__conflict_skel_create(result_pool);
1061 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(*conflict_skel,
1062 mt->db, mt->local_abspath,
1066 result_pool, scratch_pool));
1068 *merge_outcome = svn_wc_merge_conflict; /* a conflict happened */
1070 return SVN_NO_ERROR;
1074 svn_wc__internal_merge(svn_skel_t **work_items,
1075 svn_skel_t **conflict_skel,
1076 enum svn_wc_merge_outcome_t *merge_outcome,
1078 const char *left_abspath,
1079 const char *right_abspath,
1080 const char *target_abspath,
1081 const char *wri_abspath,
1082 const char *left_label,
1083 const char *right_label,
1084 const char *target_label,
1085 apr_hash_t *old_actual_props,
1086 svn_boolean_t dry_run,
1087 const char *diff3_cmd,
1088 const apr_array_header_t *merge_options,
1089 const apr_array_header_t *prop_diff,
1090 svn_cancel_func_t cancel_func,
1092 apr_pool_t *result_pool,
1093 apr_pool_t *scratch_pool)
1095 const char *detranslated_target_abspath;
1096 svn_boolean_t is_binary = FALSE;
1097 const svn_prop_t *mimeprop;
1098 svn_skel_t *work_item;
1101 SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath));
1102 SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath));
1103 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
1107 /* Fill the merge target baton */
1109 mt.local_abspath = target_abspath;
1110 mt.wri_abspath = wri_abspath;
1111 mt.old_actual_props = old_actual_props;
1112 mt.prop_diff = prop_diff;
1113 mt.diff3_cmd = diff3_cmd;
1114 mt.merge_options = merge_options;
1116 /* Decide if the merge target is a text or binary file. */
1117 if ((mimeprop = get_prop(prop_diff, SVN_PROP_MIME_TYPE))
1119 is_binary = svn_mime_type_is_binary(mimeprop->value->data);
1122 const char *value = svn_prop_get_value(mt.old_actual_props,
1123 SVN_PROP_MIME_TYPE);
1125 is_binary = value && svn_mime_type_is_binary(value);
1128 SVN_ERR(detranslate_wc_file(&detranslated_target_abspath, &mt,
1129 (! is_binary) && diff3_cmd != NULL,
1131 cancel_func, cancel_baton,
1132 scratch_pool, scratch_pool));
1134 /* We cannot depend on the left file to contain the same eols as the
1135 right file. If the merge target has mods, this will mark the entire
1136 file as conflicted, so we need to compensate. */
1137 SVN_ERR(maybe_update_target_eols(&left_abspath, prop_diff, left_abspath,
1138 cancel_func, cancel_baton,
1139 scratch_pool, scratch_pool));
1141 SVN_ERR(merge_file_trivial(work_items, merge_outcome,
1142 left_abspath, right_abspath,
1143 target_abspath, detranslated_target_abspath,
1144 dry_run, db, cancel_func, cancel_baton,
1145 result_pool, scratch_pool));
1146 if (*merge_outcome == svn_wc_merge_no_merge)
1148 /* We have a non-trivial merge. If we classify it as a merge of
1149 * 'binary' files we'll just raise a conflict, otherwise we'll do
1150 * the actual merge of 'text' file contents. */
1153 /* Raise a text conflict */
1154 SVN_ERR(merge_binary_file(work_items,
1164 detranslated_target_abspath,
1165 result_pool, scratch_pool));
1169 SVN_ERR(merge_text_file(work_items,
1179 detranslated_target_abspath,
1180 cancel_func, cancel_baton,
1181 result_pool, scratch_pool));
1185 /* Merging is complete. Regardless of text or binariness, we might
1186 need to tweak the executable bit on the new working file, and
1187 possibly make it read-only. */
1190 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db,
1192 result_pool, scratch_pool));
1193 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
1196 return SVN_NO_ERROR;
1201 svn_wc_merge5(enum svn_wc_merge_outcome_t *merge_content_outcome,
1202 enum svn_wc_notify_state_t *merge_props_outcome,
1203 svn_wc_context_t *wc_ctx,
1204 const char *left_abspath,
1205 const char *right_abspath,
1206 const char *target_abspath,
1207 const char *left_label,
1208 const char *right_label,
1209 const char *target_label,
1210 const svn_wc_conflict_version_t *left_version,
1211 const svn_wc_conflict_version_t *right_version,
1212 svn_boolean_t dry_run,
1213 const char *diff3_cmd,
1214 const apr_array_header_t *merge_options,
1215 apr_hash_t *original_props,
1216 const apr_array_header_t *prop_diff,
1217 svn_wc_conflict_resolver_func2_t conflict_func,
1218 void *conflict_baton,
1219 svn_cancel_func_t cancel_func,
1221 apr_pool_t *scratch_pool)
1223 const char *dir_abspath = svn_dirent_dirname(target_abspath, scratch_pool);
1224 svn_skel_t *work_items;
1225 svn_skel_t *conflict_skel = NULL;
1226 apr_hash_t *pristine_props = NULL;
1227 apr_hash_t *old_actual_props;
1228 apr_hash_t *new_actual_props = NULL;
1230 SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath));
1231 SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath));
1232 SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
1234 /* Before we do any work, make sure we hold a write lock. */
1236 SVN_ERR(svn_wc__write_check(wc_ctx->db, dir_abspath, scratch_pool));
1238 /* Sanity check: the merge target must be a file under revision control */
1240 svn_wc__db_status_t status;
1241 svn_node_kind_t kind;
1242 svn_boolean_t had_props;
1243 svn_boolean_t props_mod;
1244 svn_boolean_t conflicted;
1246 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
1247 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1248 NULL, NULL, NULL, NULL, NULL, NULL,
1249 &conflicted, NULL, &had_props, &props_mod,
1251 wc_ctx->db, target_abspath,
1252 scratch_pool, scratch_pool));
1254 if (kind != svn_node_file || (status != svn_wc__db_status_normal
1255 && status != svn_wc__db_status_added))
1257 *merge_content_outcome = svn_wc_merge_no_merge;
1258 if (merge_props_outcome)
1259 *merge_props_outcome = svn_wc_notify_state_unchanged;
1260 return SVN_NO_ERROR;
1265 svn_boolean_t text_conflicted;
1266 svn_boolean_t prop_conflicted;
1267 svn_boolean_t tree_conflicted;
1269 SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted,
1272 wc_ctx->db, target_abspath,
1275 /* We can't install two prop conflicts on a single node, so
1276 avoid even checking that we have to merge it */
1277 if (text_conflicted || prop_conflicted || tree_conflicted)
1279 return svn_error_createf(
1280 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
1281 _("Can't merge into conflicted node '%s'"),
1282 svn_dirent_local_style(target_abspath,
1285 /* else: Conflict was resolved by removing markers */
1288 if (merge_props_outcome && had_props)
1290 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
1291 wc_ctx->db, target_abspath,
1292 scratch_pool, scratch_pool));
1294 else if (merge_props_outcome)
1295 pristine_props = apr_hash_make(scratch_pool);
1299 SVN_ERR(svn_wc__db_read_props(&old_actual_props,
1300 wc_ctx->db, target_abspath,
1301 scratch_pool, scratch_pool));
1303 else if (pristine_props)
1304 old_actual_props = pristine_props;
1306 old_actual_props = apr_hash_make(scratch_pool);
1309 /* Merge the properties, if requested. We merge the properties first
1310 * because the properties can affect the text (EOL style, keywords). */
1311 if (merge_props_outcome)
1315 /* The PROPCHANGES may not have non-"normal" properties in it. If entry
1316 or wc props were allowed, then the following code would install them
1317 into the BASE and/or WORKING properties(!). */
1318 for (i = prop_diff->nelts; i--; )
1320 const svn_prop_t *change = &APR_ARRAY_IDX(prop_diff, i, svn_prop_t);
1322 if (!svn_wc_is_normal_prop(change->name))
1323 return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
1324 _("The property '%s' may not be merged "
1327 svn_dirent_local_style(target_abspath,
1331 SVN_ERR(svn_wc__merge_props(&conflict_skel,
1332 merge_props_outcome,
1334 wc_ctx->db, target_abspath,
1335 original_props, pristine_props, old_actual_props,
1337 scratch_pool, scratch_pool));
1340 /* Merge the text. */
1341 SVN_ERR(svn_wc__internal_merge(&work_items,
1343 merge_content_outcome,
1349 left_label, right_label, target_label,
1355 cancel_func, cancel_baton,
1356 scratch_pool, scratch_pool));
1358 /* If this isn't a dry run, then update the DB, run the work, and
1359 * call the conflict resolver callback. */
1364 svn_skel_t *work_item;
1366 SVN_ERR(svn_wc__conflict_skel_set_op_merge(conflict_skel,
1372 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
1373 wc_ctx->db, target_abspath,
1375 scratch_pool, scratch_pool));
1377 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
1380 if (new_actual_props)
1381 SVN_ERR(svn_wc__db_op_set_props(wc_ctx->db, target_abspath,
1383 svn_wc__has_magic_property(prop_diff),
1384 conflict_skel, work_items,
1386 else if (conflict_skel)
1387 SVN_ERR(svn_wc__db_op_mark_conflict(wc_ctx->db, target_abspath,
1388 conflict_skel, work_items,
1390 else if (work_items)
1391 SVN_ERR(svn_wc__db_wq_add(wc_ctx->db, target_abspath, work_items,
1395 SVN_ERR(svn_wc__wq_run(wc_ctx->db, target_abspath,
1396 cancel_func, cancel_baton,
1399 if (conflict_skel && conflict_func)
1401 svn_boolean_t text_conflicted, prop_conflicted;
1403 SVN_ERR(svn_wc__conflict_invoke_resolver(
1404 wc_ctx->db, target_abspath,
1405 conflict_skel, merge_options,
1406 conflict_func, conflict_baton,
1407 cancel_func, cancel_baton,
1410 /* Reset *MERGE_CONTENT_OUTCOME etc. if a conflict was resolved. */
1411 SVN_ERR(svn_wc__internal_conflicted_p(
1412 &text_conflicted, &prop_conflicted, NULL,
1413 wc_ctx->db, target_abspath, scratch_pool));
1414 if (*merge_props_outcome == svn_wc_notify_state_conflicted
1415 && ! prop_conflicted)
1416 *merge_props_outcome = svn_wc_notify_state_merged;
1417 if (*merge_content_outcome == svn_wc_merge_conflict
1418 && ! text_conflicted)
1419 *merge_content_outcome = svn_wc_merge_merged;
1423 return SVN_NO_ERROR;