#include "svn_wc.h"
#include "svn_diff.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "wc.h"
#include "adm_files.h"
#include "translate.h"
#include "workqueue.h"
#include "private/svn_skel.h"
#include "svn_private_config.h"
typedef struct merge_target_t
{
svn_wc__db_t *db;
const char *local_abspath;
const char *wri_abspath;
apr_hash_t *actual_props;
const apr_array_header_t *prop_diff;
const char *diff3_cmd;
const apr_array_header_t *merge_options;
} merge_target_t;
static const svn_prop_t *
get_prop(const merge_target_t *mt,
const char *prop_name)
{
if (mt && mt->prop_diff)
{
int i;
for (i = 0; i < mt->prop_diff->nelts; i++)
{
const svn_prop_t *elt = &APR_ARRAY_IDX(mt->prop_diff, i,
svn_prop_t);
if (strcmp(elt->name,prop_name) == 0)
return elt;
}
}
return NULL;
}
static svn_error_t *
detranslate_wc_file(const char **detranslated_abspath,
const merge_target_t *mt,
svn_boolean_t force_copy,
const char *source_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_boolean_t is_binary;
const svn_prop_t *prop;
svn_subst_eol_style_t style;
const char *eol;
apr_hash_t *keywords;
svn_boolean_t special;
const char *mime_value = svn_prop_get_value(mt->actual_props,
SVN_PROP_MIME_TYPE);
is_binary = (mime_value && svn_mime_type_is_binary(mime_value));
if (is_binary
&& (((prop = get_prop(mt, SVN_PROP_MIME_TYPE))
&& prop->value && svn_mime_type_is_binary(prop->value->data))
|| prop == NULL))
{
keywords = NULL;
special = FALSE;
eol = NULL;
style = svn_subst_eol_style_none;
}
else if ((!is_binary)
&& (prop = get_prop(mt, SVN_PROP_MIME_TYPE))
&& prop->value && svn_mime_type_is_binary(prop->value->data))
{
SVN_ERR(svn_wc__get_translate_info(&style, &eol,
&keywords,
&special,
mt->db, mt->local_abspath,
mt->actual_props, TRUE,
scratch_pool, scratch_pool));
}
else
{
SVN_ERR(svn_wc__get_translate_info(&style, &eol,
&keywords,
&special,
mt->db, mt->local_abspath,
mt->actual_props, TRUE,
scratch_pool, scratch_pool));
if (special)
{
keywords = NULL;
eol = NULL;
style = svn_subst_eol_style_none;
}
else
{
if ((prop = get_prop(mt, SVN_PROP_EOL_STYLE)) && prop->value)
{
svn_subst_eol_style_from_value(&style, &eol, prop->value->data);
}
else if (!is_binary)
{
}
else
{
eol = NULL;
style = svn_subst_eol_style_none;
}
if (is_binary)
keywords = NULL;
}
}
if (force_copy || keywords || eol || special)
{
const char *wcroot_abspath, *temp_dir_abspath;
const char *detranslated;
SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, mt->db, mt->wri_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, mt->db,
mt->wri_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_open_unique_file3(NULL, &detranslated, temp_dir_abspath,
(force_copy
? svn_io_file_del_none
: svn_io_file_del_on_pool_cleanup),
result_pool, scratch_pool));
if (style == svn_subst_eol_style_native)
eol = SVN_SUBST_NATIVE_EOL_STR;
else if (style != svn_subst_eol_style_fixed
&& style != svn_subst_eol_style_none)
return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
SVN_ERR(svn_subst_copy_and_translate4(source_abspath,
detranslated,
eol,
TRUE ,
keywords,
FALSE ,
special,
cancel_func, cancel_baton,
scratch_pool));
SVN_ERR(svn_dirent_get_absolute(detranslated_abspath, detranslated,
result_pool));
}
else
*detranslated_abspath = apr_pstrdup(result_pool, source_abspath);
return SVN_NO_ERROR;
}
static svn_error_t *
maybe_update_target_eols(const char **new_target_abspath,
const merge_target_t *mt,
const char *old_target_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const svn_prop_t *prop = get_prop(mt, SVN_PROP_EOL_STYLE);
if (prop && prop->value)
{
const char *eol;
const char *tmp_new;
svn_subst_eol_style_from_value(NULL, &eol, prop->value->data);
SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_new, NULL,
svn_io_file_del_on_pool_cleanup,
result_pool, scratch_pool));
SVN_ERR(svn_subst_copy_and_translate4(old_target_abspath,
tmp_new,
eol,
TRUE ,
NULL ,
FALSE ,
FALSE ,
cancel_func, cancel_baton,
scratch_pool));
*new_target_abspath = apr_pstrdup(result_pool, tmp_new);
}
else
*new_target_abspath = apr_pstrdup(result_pool, old_target_abspath);
return SVN_NO_ERROR;
}
static void
init_conflict_markers(const char **target_marker,
const char **left_marker,
const char **right_marker,
const char *target_label,
const char *left_label,
const char *right_label,
apr_pool_t *pool)
{
if (target_label)
*target_marker = apr_psprintf(pool, "<<<<<<< %s", target_label);
else
*target_marker = "<<<<<<< .working";
if (left_label)
*left_marker = apr_psprintf(pool, "||||||| %s", left_label);
else
*left_marker = "||||||| .old";
if (right_label)
*right_marker = apr_psprintf(pool, ">>>>>>> %s", right_label);
else
*right_marker = ">>>>>>> .new";
}
static svn_error_t*
do_text_merge(svn_boolean_t *contains_conflicts,
apr_file_t *result_f,
const merge_target_t *mt,
const char *detranslated_target,
const char *left,
const char *right,
const char *target_label,
const char *left_label,
const char *right_label,
apr_pool_t *pool)
{
svn_diff_t *diff;
svn_stream_t *ostream;
const char *target_marker;
const char *left_marker;
const char *right_marker;
svn_diff_file_options_t *diff3_options;
diff3_options = svn_diff_file_options_create(pool);
if (mt->merge_options)
SVN_ERR(svn_diff_file_options_parse(diff3_options,
mt->merge_options, pool));
init_conflict_markers(&target_marker, &left_marker, &right_marker,
target_label, left_label, right_label, pool);
SVN_ERR(svn_diff_file_diff3_2(&diff, left, detranslated_target, right,
diff3_options, pool));
ostream = svn_stream_from_aprfile2(result_f, TRUE, pool);
SVN_ERR(svn_diff_file_output_merge2(ostream, diff,
left, detranslated_target, right,
left_marker,
target_marker,
right_marker,
"=======",
svn_diff_conflict_display_modified_latest,
pool));
SVN_ERR(svn_stream_close(ostream));
*contains_conflicts = svn_diff_contains_conflicts(diff);
return SVN_NO_ERROR;
}
static svn_error_t*
do_text_merge_external(svn_boolean_t *contains_conflicts,
apr_file_t *result_f,
const merge_target_t *mt,
const char *detranslated_target,
const char *left_abspath,
const char *right_abspath,
const char *target_label,
const char *left_label,
const char *right_label,
apr_pool_t *scratch_pool)
{
int exit_code;
SVN_ERR(svn_io_run_diff3_3(&exit_code, ".",
detranslated_target, left_abspath, right_abspath,
target_label, left_label, right_label,
result_f, mt->diff3_cmd,
mt->merge_options, scratch_pool));
*contains_conflicts = exit_code == 1;
return SVN_NO_ERROR;
}
static svn_error_t*
save_merge_result(svn_skel_t **work_item,
const merge_target_t *mt,
const char *source,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *edited_copy_abspath;
const char *dir_abspath;
const char *filename;
svn_dirent_split(&dir_abspath, &filename, mt->local_abspath, scratch_pool);
SVN_ERR(svn_io_open_uniquely_named(NULL,
&edited_copy_abspath,
dir_abspath,
filename,
".edited",
svn_io_file_del_none,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__wq_build_file_copy_translated(work_item,
mt->db, mt->local_abspath,
source, edited_copy_abspath,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t*
eval_conflict_func_result(svn_skel_t **work_items,
enum svn_wc_merge_outcome_t *merge_outcome,
svn_wc_conflict_choice_t choice,
const merge_target_t *mt,
const char *left_abspath,
const char *right_abspath,
const char *merged_file,
const char *detranslated_target,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *install_from = NULL;
svn_boolean_t remove_source = FALSE;
*work_items = NULL;
switch (choice)
{
case svn_wc_conflict_choose_base:
{
install_from = left_abspath;
*merge_outcome = svn_wc_merge_merged;
break;
}
case svn_wc_conflict_choose_theirs_full:
{
install_from = right_abspath;
*merge_outcome = svn_wc_merge_merged;
break;
}
case svn_wc_conflict_choose_mine_full:
{
*merge_outcome = svn_wc_merge_merged;
return SVN_NO_ERROR;
}
case svn_wc_conflict_choose_theirs_conflict:
case svn_wc_conflict_choose_mine_conflict:
{
const char *chosen_path;
const char *temp_dir;
svn_stream_t *chosen_stream;
svn_diff_t *diff;
svn_diff_conflict_display_style_t style;
svn_diff_file_options_t *diff3_options;
diff3_options = svn_diff_file_options_create(scratch_pool);
if (mt->merge_options)
SVN_ERR(svn_diff_file_options_parse(diff3_options,
mt->merge_options,
scratch_pool));
style = choice == svn_wc_conflict_choose_theirs_conflict
? svn_diff_conflict_display_latest
: svn_diff_conflict_display_modified;
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, mt->db,
mt->wri_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_open_unique(&chosen_stream, &chosen_path,
temp_dir, svn_io_file_del_none,
scratch_pool, scratch_pool));
SVN_ERR(svn_diff_file_diff3_2(&diff,
left_abspath,
detranslated_target, right_abspath,
diff3_options, scratch_pool));
SVN_ERR(svn_diff_file_output_merge2(chosen_stream, diff,
left_abspath,
detranslated_target,
right_abspath,
NULL, NULL,
NULL, NULL,
style,
scratch_pool));
SVN_ERR(svn_stream_close(chosen_stream));
install_from = chosen_path;
remove_source = TRUE;
*merge_outcome = svn_wc_merge_merged;
break;
}
case svn_wc_conflict_choose_merged:
{
install_from = merged_file;
*merge_outcome = svn_wc_merge_merged;
break;
}
case svn_wc_conflict_choose_postpone:
default:
{
#if 0
*merge_outcome = svn_wc_merge_merged;
#endif
return SVN_NO_ERROR;
}
}
SVN_ERR_ASSERT(install_from != NULL);
{
svn_skel_t *work_item;
SVN_ERR(svn_wc__wq_build_file_install(&work_item,
mt->db, mt->local_abspath,
install_from,
FALSE ,
FALSE ,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
if (remove_source)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
mt->db, install_from,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
preserve_pre_merge_files(svn_skel_t **work_items,
const char **left_copy,
const char **right_copy,
const char **target_copy,
const merge_target_t *mt,
const char *left_abspath,
const char *right_abspath,
const char *left_label,
const char *right_label,
const char *target_label,
const char *detranslated_target_abspath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *tmp_left, *tmp_right, *detranslated_target_copy;
const char *dir_abspath, *target_name;
const char *wcroot_abspath, *temp_dir_abspath;
svn_skel_t *work_item, *last_items = NULL;
*work_items = NULL;
svn_dirent_split(&dir_abspath, &target_name, mt->local_abspath,
scratch_pool);
SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, mt->db, mt->wri_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, mt->db,
mt->wri_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_open_uniquely_named(
NULL, left_copy, dir_abspath, target_name, left_label,
svn_io_file_del_none, result_pool, scratch_pool));
SVN_ERR(svn_io_open_uniquely_named(
NULL, right_copy, dir_abspath, target_name, right_label,
svn_io_file_del_none, result_pool, scratch_pool));
SVN_ERR(svn_io_open_uniquely_named(
NULL, target_copy, dir_abspath, target_name, target_label,
svn_io_file_del_none, result_pool, scratch_pool));
if (! svn_dirent_is_ancestor(wcroot_abspath, left_abspath))
{
SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_left, temp_dir_abspath,
svn_io_file_del_none,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_copy_file(left_abspath, tmp_left, TRUE, scratch_pool));
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, tmp_left,
result_pool, scratch_pool));
last_items = svn_wc__wq_merge(last_items, work_item, result_pool);
}
else
tmp_left = left_abspath;
if (! svn_dirent_is_ancestor(wcroot_abspath, right_abspath))
{
SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_right, temp_dir_abspath,
svn_io_file_del_none,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_copy_file(right_abspath, tmp_right, TRUE, scratch_pool));
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db, tmp_right,
result_pool, scratch_pool));
last_items = svn_wc__wq_merge(last_items, work_item, result_pool);
}
else
tmp_right = right_abspath;
SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
mt->db, mt->local_abspath,
tmp_left, *left_copy,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
mt->db, mt->local_abspath,
tmp_right, *right_copy,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
SVN_ERR(detranslate_wc_file(&detranslated_target_copy, mt, TRUE,
mt->local_abspath,
cancel_func, cancel_baton,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__wq_build_file_copy_translated(&work_item,
mt->db, mt->local_abspath,
detranslated_target_copy,
*target_copy,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, mt->db,
detranslated_target_copy,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
*work_items = svn_wc__wq_merge(*work_items, last_items, result_pool);
return SVN_NO_ERROR;
}
static const svn_wc_conflict_description2_t *
setup_text_conflict_desc(const char *left_abspath,
const char *right_abspath,
const char *target_abspath,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
const char *result_target,
const char *detranslated_target,
const svn_prop_t *mimeprop,
svn_boolean_t is_binary,
apr_pool_t *pool)
{
svn_wc_conflict_description2_t *cdesc;
cdesc = svn_wc_conflict_description_create_text2(target_abspath, pool);
cdesc->is_binary = is_binary;
cdesc->mime_type = (mimeprop && mimeprop->value)
? mimeprop->value->data : NULL,
cdesc->base_abspath = left_abspath;
cdesc->their_abspath = right_abspath;
cdesc->my_abspath = detranslated_target;
cdesc->merged_file = result_target;
cdesc->src_left_version = left_version;
cdesc->src_right_version = right_version;
return cdesc;
}
static svn_error_t*
maybe_resolve_conflicts(svn_skel_t **work_items,
const merge_target_t *mt,
const char *left_abspath,
const char *right_abspath,
const char *left_label,
const char *right_label,
const char *target_label,
enum svn_wc_merge_outcome_t *merge_outcome,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
const char *result_target,
const char *detranslated_target,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_wc_conflict_result_t *result;
svn_skel_t *work_item;
*work_items = NULL;
if (!conflict_func)
{
result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone,
NULL, result_pool);
}
else
{
const svn_wc_conflict_description2_t *cdesc;
cdesc = setup_text_conflict_desc(left_abspath,
right_abspath,
mt->local_abspath,
left_version,
right_version,
result_target,
detranslated_target,
get_prop(mt, SVN_PROP_MIME_TYPE),
FALSE,
scratch_pool);
SVN_ERR(conflict_func(&result, cdesc, conflict_baton, scratch_pool,
scratch_pool));
if (result == NULL)
return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
NULL, _("Conflict callback violated API:"
" returned no results"));
if (result->save_merged)
{
SVN_ERR(save_merge_result(work_items,
mt,
result->merged_file
? result->merged_file
: result_target,
result_pool, scratch_pool));
}
}
SVN_ERR(eval_conflict_func_result(&work_item,
merge_outcome,
result->choice,
mt,
left_abspath,
right_abspath,
result->merged_file
? result->merged_file
: result_target,
detranslated_target,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
if (result->choice != svn_wc_conflict_choose_postpone)
return SVN_NO_ERROR;
*merge_outcome = svn_wc_merge_conflict;
return SVN_NO_ERROR;
}
static svn_error_t *
merge_file_trivial(svn_skel_t **work_items,
enum svn_wc_merge_outcome_t *merge_outcome,
const char *left_abspath,
const char *right_abspath,
const char *target_abspath,
svn_boolean_t dry_run,
svn_wc__db_t *db,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_skel_t *work_item;
svn_boolean_t same_contents = FALSE;
svn_node_kind_t kind;
svn_boolean_t is_special;
SVN_ERR(svn_io_check_special_path(target_abspath, &kind, &is_special,
scratch_pool));
if (kind != svn_node_file || is_special)
{
*merge_outcome = svn_wc_merge_no_merge;
return SVN_NO_ERROR;
}
SVN_ERR(svn_io_files_contents_same_p(&same_contents, left_abspath,
target_abspath, scratch_pool));
if (same_contents)
{
SVN_ERR(svn_io_files_contents_same_p(&same_contents, left_abspath,
right_abspath, scratch_pool));
if (same_contents)
{
*merge_outcome = svn_wc_merge_unchanged;
}
else
{
*merge_outcome = svn_wc_merge_merged;
if (!dry_run)
{
const char *wcroot_abspath;
svn_boolean_t delete_src = FALSE;
SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath,
db, target_abspath,
scratch_pool, scratch_pool));
if (!svn_dirent_is_child(wcroot_abspath, right_abspath, NULL))
{
svn_stream_t *tmp_src;
svn_stream_t *tmp_dst;
SVN_ERR(svn_stream_open_readonly(&tmp_src, right_abspath,
scratch_pool,
scratch_pool));
SVN_ERR(svn_wc__open_writable_base(&tmp_dst, &right_abspath,
NULL, NULL,
db, target_abspath,
scratch_pool,
scratch_pool));
SVN_ERR(svn_stream_copy3(tmp_src, tmp_dst,
cancel_func, cancel_baton,
scratch_pool));
delete_src = TRUE;
}
SVN_ERR(svn_wc__wq_build_file_install(
&work_item, db, target_abspath, right_abspath,
FALSE ,
FALSE ,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item,
result_pool);
if (delete_src)
{
SVN_ERR(svn_wc__wq_build_file_remove(
&work_item, db, right_abspath,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item,
result_pool);
}
}
}
return SVN_NO_ERROR;
}
*merge_outcome = svn_wc_merge_no_merge;
return SVN_NO_ERROR;
}
static svn_error_t*
merge_text_file(svn_skel_t **work_items,
enum svn_wc_merge_outcome_t *merge_outcome,
const merge_target_t *mt,
const char *left_abspath,
const char *right_abspath,
const char *left_label,
const char *right_label,
const char *target_label,
svn_boolean_t dry_run,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
const char *detranslated_target_abspath,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *pool = scratch_pool;
svn_boolean_t contains_conflicts;
apr_file_t *result_f;
const char *result_target;
const char *base_name;
const char *temp_dir;
svn_skel_t *work_item;
*work_items = NULL;
base_name = svn_dirent_basename(mt->local_abspath, scratch_pool);
SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir, mt->db, mt->wri_abspath,
pool, pool));
SVN_ERR(svn_io_open_uniquely_named(&result_f, &result_target,
temp_dir, base_name, ".tmp",
svn_io_file_del_none, pool, pool));
if (mt->diff3_cmd)
SVN_ERR(do_text_merge_external(&contains_conflicts,
result_f,
mt,
detranslated_target_abspath,
left_abspath,
right_abspath,
target_label,
left_label,
right_label,
pool));
else
SVN_ERR(do_text_merge(&contains_conflicts,
result_f,
mt,
detranslated_target_abspath,
left_abspath,
right_abspath,
target_label,
left_label,
right_label,
pool));
SVN_ERR(svn_io_file_close(result_f, pool));
if (contains_conflicts && ! dry_run)
{
SVN_ERR(maybe_resolve_conflicts(work_items,
mt,
left_abspath,
right_abspath,
left_label,
right_label,
target_label,
merge_outcome,
left_version,
right_version,
result_target,
detranslated_target_abspath,
conflict_func, conflict_baton,
cancel_func, cancel_baton,
result_pool, scratch_pool));
if (*merge_outcome == svn_wc_merge_conflict)
{
const char *left_copy, *right_copy, *target_copy;
SVN_ERR(preserve_pre_merge_files(
&work_item,
&left_copy, &right_copy, &target_copy,
mt, left_abspath, right_abspath,
left_label, right_label, target_label,
detranslated_target_abspath,
cancel_func, cancel_baton,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
SVN_ERR(svn_wc__wq_tmp_build_set_text_conflict_markers(
&work_item, mt->db, mt->local_abspath,
left_copy, right_copy, target_copy,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
if (*merge_outcome == svn_wc_merge_merged)
goto done;
}
else if (contains_conflicts && dry_run)
*merge_outcome = svn_wc_merge_conflict;
else
{
svn_boolean_t same, special;
SVN_ERR(svn_wc__get_translate_info(NULL, NULL, NULL,
&special, mt->db, mt->local_abspath,
mt->actual_props, TRUE,
pool, pool));
SVN_ERR(svn_io_files_contents_same_p(&same, result_target,
(special ?
detranslated_target_abspath :
mt->local_abspath),
pool));
*merge_outcome = same ? svn_wc_merge_unchanged : svn_wc_merge_merged;
}
if (*merge_outcome != svn_wc_merge_unchanged && ! dry_run)
{
SVN_ERR(svn_wc__wq_build_file_install(&work_item,
mt->db, mt->local_abspath,
result_target,
FALSE ,
FALSE ,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
done:
SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
mt->db, result_target,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
merge_binary_file(svn_skel_t **work_items,
enum svn_wc_merge_outcome_t *merge_outcome,
const merge_target_t *mt,
const char *left_abspath,
const char *right_abspath,
const char *left_label,
const char *right_label,
const char *target_label,
svn_boolean_t dry_run,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
const char *detranslated_target_abspath,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *pool = scratch_pool;
const char *left_copy, *right_copy;
const char *merge_dirpath, *merge_filename;
const char *conflict_wrk;
svn_skel_t *work_item;
*work_items = NULL;
svn_dirent_split(&merge_dirpath, &merge_filename, mt->local_abspath, pool);
if (dry_run)
{
*merge_outcome = svn_wc_merge_conflict;
return SVN_NO_ERROR;
}
if (conflict_func)
{
svn_wc_conflict_result_t *result = NULL;
const svn_wc_conflict_description2_t *cdesc;
const char *install_from = NULL;
cdesc = setup_text_conflict_desc(left_abspath, right_abspath,
mt->local_abspath,
left_version, right_version,
NULL ,
detranslated_target_abspath,
get_prop(mt, SVN_PROP_MIME_TYPE),
TRUE, pool);
SVN_ERR(conflict_func(&result, cdesc, conflict_baton, pool, pool));
if (result == NULL)
return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
NULL, _("Conflict callback violated API:"
" returned no results"));
switch (result->choice)
{
case svn_wc_conflict_choose_base:
{
install_from = left_abspath;
*merge_outcome = svn_wc_merge_merged;
break;
}
case svn_wc_conflict_choose_theirs_full:
{
install_from = right_abspath;
*merge_outcome = svn_wc_merge_merged;
break;
}
case svn_wc_conflict_choose_mine_full:
{
*merge_outcome = svn_wc_merge_merged;
return SVN_NO_ERROR;
}
case svn_wc_conflict_choose_merged:
{
if (! result->merged_file)
{
return svn_error_create
(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE,
NULL, _("Conflict callback violated API:"
" returned no merged file"));
}
else
{
install_from = result->merged_file;
*merge_outcome = svn_wc_merge_merged;
break;
}
}
case svn_wc_conflict_choose_postpone:
default:
{
}
}
if (install_from != NULL)
{
SVN_ERR(svn_wc__wq_build_file_install(work_items,
mt->db, mt->local_abspath,
install_from,
FALSE ,
FALSE ,
result_pool, scratch_pool));
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_io_open_uniquely_named(NULL,
&left_copy,
merge_dirpath,
merge_filename,
left_label,
svn_io_file_del_none,
pool, pool));
SVN_ERR(svn_io_open_uniquely_named(NULL,
&right_copy,
merge_dirpath,
merge_filename,
right_label,
svn_io_file_del_none,
pool, pool));
SVN_ERR(svn_io_copy_file(left_abspath, left_copy, TRUE, pool));
SVN_ERR(svn_io_copy_file(right_abspath, right_copy, TRUE, pool));
if (strcmp(mt->local_abspath, detranslated_target_abspath) != 0)
{
SVN_ERR(svn_io_open_uniquely_named(NULL,
&conflict_wrk,
merge_dirpath,
merge_filename,
target_label,
svn_io_file_del_none,
pool, pool));
SVN_ERR(svn_wc__wq_build_file_move(work_items, mt->db,
mt->local_abspath,
detranslated_target_abspath,
conflict_wrk,
pool, result_pool));
}
else
{
conflict_wrk = NULL;
}
SVN_ERR(svn_wc__wq_tmp_build_set_text_conflict_markers(&work_item,
mt->db,
mt->local_abspath,
left_copy,
right_copy,
conflict_wrk,
result_pool,
scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
*merge_outcome = svn_wc_merge_conflict;
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc__internal_merge(svn_skel_t **work_items,
enum svn_wc_merge_outcome_t *merge_outcome,
svn_wc__db_t *db,
const char *left_abspath,
const svn_wc_conflict_version_t *left_version,
const char *right_abspath,
const svn_wc_conflict_version_t *right_version,
const char *target_abspath,
const char *wri_abspath,
const char *left_label,
const char *right_label,
const char *target_label,
apr_hash_t *actual_props,
svn_boolean_t dry_run,
const char *diff3_cmd,
const apr_array_header_t *merge_options,
const apr_array_header_t *prop_diff,
svn_wc_conflict_resolver_func2_t conflict_func,
void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *pool = scratch_pool;
const char *detranslated_target_abspath;
svn_boolean_t is_binary = FALSE;
const svn_prop_t *mimeprop;
svn_skel_t *work_item;
merge_target_t mt;
SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath));
SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath));
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
*work_items = NULL;
mt.db = db;
mt.local_abspath = target_abspath;
mt.wri_abspath = wri_abspath;
mt.actual_props = actual_props;
mt.prop_diff = prop_diff;
mt.diff3_cmd = diff3_cmd;
mt.merge_options = merge_options;
if ((mimeprop = get_prop(&mt, SVN_PROP_MIME_TYPE))
&& mimeprop->value)
is_binary = svn_mime_type_is_binary(mimeprop->value->data);
else
{
const char *value = svn_prop_get_value(mt.actual_props,
SVN_PROP_MIME_TYPE);
is_binary = value && svn_mime_type_is_binary(value);
}
SVN_ERR(detranslate_wc_file(&detranslated_target_abspath, &mt,
(! is_binary) && diff3_cmd != NULL,
target_abspath,
cancel_func, cancel_baton, pool, pool));
SVN_ERR(maybe_update_target_eols(&left_abspath, &mt, left_abspath,
cancel_func, cancel_baton, pool, pool));
SVN_ERR(merge_file_trivial(work_items, merge_outcome,
left_abspath, right_abspath,
target_abspath, dry_run, db,
cancel_func, cancel_baton,
result_pool, scratch_pool));
if (*merge_outcome == svn_wc_merge_no_merge)
{
if (is_binary)
{
SVN_ERR(merge_binary_file(work_items,
merge_outcome,
&mt,
left_abspath,
right_abspath,
left_label,
right_label,
target_label,
dry_run,
left_version,
right_version,
detranslated_target_abspath,
conflict_func,
conflict_baton,
result_pool, scratch_pool));
}
else
{
SVN_ERR(merge_text_file(work_items,
merge_outcome,
&mt,
left_abspath,
right_abspath,
left_label,
right_label,
target_label,
dry_run,
left_version,
right_version,
detranslated_target_abspath,
conflict_func, conflict_baton,
cancel_func, cancel_baton,
result_pool, scratch_pool));
}
}
if (! dry_run)
{
SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db,
target_abspath,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
return SVN_NO_ERROR;
}
svn_error_t *
svn_wc_merge4(enum svn_wc_merge_outcome_t *merge_outcome,
svn_wc_context_t *wc_ctx,
const char *left_abspath,
const char *right_abspath,
const char *target_abspath,
const char *left_label,
const char *right_label,
const char *target_label,
const svn_wc_conflict_version_t *left_version,
const svn_wc_conflict_version_t *right_version,
svn_boolean_t dry_run,
const char *diff3_cmd,
const apr_array_header_t *merge_options,
const apr_array_header_t *prop_diff,
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)
{
const char *dir_abspath = svn_dirent_dirname(target_abspath, scratch_pool);
svn_skel_t *work_items;
apr_hash_t *actual_props;
SVN_ERR_ASSERT(svn_dirent_is_absolute(left_abspath));
SVN_ERR_ASSERT(svn_dirent_is_absolute(right_abspath));
SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
if (!dry_run)
SVN_ERR(svn_wc__write_check(wc_ctx->db, dir_abspath, scratch_pool));
{
svn_wc__db_kind_t kind;
svn_boolean_t hidden;
SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, target_abspath, TRUE,
scratch_pool));
if (kind == svn_wc__db_kind_unknown)
{
*merge_outcome = svn_wc_merge_no_merge;
return SVN_NO_ERROR;
}
SVN_ERR(svn_wc__db_node_hidden(&hidden, wc_ctx->db, target_abspath,
scratch_pool));
if (hidden)
{
*merge_outcome = svn_wc_merge_no_merge;
return SVN_NO_ERROR;
}
}
SVN_ERR(svn_wc__db_read_props(&actual_props, wc_ctx->db, target_abspath,
scratch_pool, scratch_pool));
SVN_ERR(svn_wc__internal_merge(&work_items,
merge_outcome,
wc_ctx->db,
left_abspath, left_version,
right_abspath, right_version,
target_abspath,
target_abspath,
left_label, right_label, target_label,
actual_props,
dry_run,
diff3_cmd,
merge_options,
prop_diff,
conflict_func, conflict_baton,
cancel_func, cancel_baton,
scratch_pool, scratch_pool));
if (!dry_run)
{
SVN_ERR(svn_wc__db_wq_add(wc_ctx->db, target_abspath, work_items,
scratch_pool));
SVN_ERR(svn_wc__wq_run(wc_ctx->db, target_abspath,
cancel_func, cancel_baton,
scratch_pool));
}
return SVN_NO_ERROR;
}
svn_wc_conflict_result_t *
svn_wc_create_conflict_result(svn_wc_conflict_choice_t choice,
const char *merged_file,
apr_pool_t *pool)
{
svn_wc_conflict_result_t *result = apr_pcalloc(pool, sizeof(*result));
result->choice = choice;
result->merged_file = merged_file;
result->save_merged = FALSE;
return result;
}