#include "svn_client.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_error.h"
#include "svn_types.h"
#include "cl.h"
#include "private/svn_client_private.h"
#include "svn_private_config.h"
static svn_error_t *
ensure_wc_path_has_repo_revision(const char *path_or_url,
const svn_opt_revision_t *revision,
apr_pool_t *scratch_pool)
{
if (revision->kind != svn_opt_revision_number
&& revision->kind != svn_opt_revision_date
&& revision->kind != svn_opt_revision_head
&& ! svn_path_is_url(path_or_url))
return svn_error_createf(
SVN_ERR_CLIENT_BAD_REVISION, NULL,
_("Invalid merge source '%s'; a working copy path can only be "
"used with a repository revision (a number, a date, or head)"),
svn_dirent_local_style(path_or_url, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
run_merge(svn_boolean_t two_sources_specified,
const char *sourcepath1,
svn_opt_revision_t peg_revision1,
const char *sourcepath2,
const char *targetpath,
apr_array_header_t *ranges_to_merge,
svn_opt_revision_t first_range_start,
svn_opt_revision_t first_range_end,
svn_cl__opt_state_t *opt_state,
apr_array_header_t *options,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
svn_error_t *merge_err;
if (opt_state->reintegrate)
{
merge_err = svn_cl__deprecated_merge_reintegrate(
sourcepath1, &peg_revision1, targetpath,
opt_state->dry_run, options, ctx, scratch_pool);
}
else if (! two_sources_specified)
{
if ((first_range_start.kind == svn_opt_revision_unspecified)
&& (first_range_end.kind == svn_opt_revision_unspecified))
{
ranges_to_merge = NULL;
}
if (opt_state->verbose)
SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n")));
merge_err = svn_client_merge_peg5(sourcepath1,
ranges_to_merge,
&peg_revision1,
targetpath,
opt_state->depth,
opt_state->ignore_ancestry,
opt_state->ignore_ancestry,
opt_state->force,
opt_state->record_only,
opt_state->dry_run,
opt_state->allow_mixed_rev,
options,
ctx,
scratch_pool);
}
else
{
if (svn_path_is_url(sourcepath1) != svn_path_is_url(sourcepath2))
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Merge sources must both be "
"either paths or URLs"));
if (svn_path_is_url(targetpath))
return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Merge target '%s' must be a local path "
"but looks like a URL"), targetpath);
if (opt_state->verbose)
SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n")));
merge_err = svn_client_merge5(sourcepath1,
&first_range_start,
sourcepath2,
&first_range_end,
targetpath,
opt_state->depth,
opt_state->ignore_ancestry,
opt_state->ignore_ancestry,
opt_state->force,
opt_state->record_only,
opt_state->dry_run,
opt_state->allow_mixed_rev,
options,
ctx,
scratch_pool);
}
return merge_err;
}
svn_error_t *
svn_cl__merge(apr_getopt_t *os,
void *baton,
apr_pool_t *pool)
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
apr_array_header_t *targets;
const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = "";
svn_boolean_t two_sources_specified = TRUE;
svn_error_t *merge_err;
svn_opt_revision_t first_range_start, first_range_end, peg_revision1,
peg_revision2;
apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
svn_boolean_t has_explicit_target = FALSE;
if (opt_state->reintegrate
&& opt_state->start_revision.kind != svn_opt_revision_unspecified)
{
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("-r and -c can't be used with --reintegrate"));
}
SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
opt_state->targets,
ctx, FALSE, pool));
if (targets->nelts < 1)
{
return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
_("Merge source required"));
}
else
{
SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
APR_ARRAY_IDX(targets, 0, const char *),
pool));
if (targets->nelts >= 2)
SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
APR_ARRAY_IDX(targets, 1, const char *),
pool));
}
if (targets->nelts <= 1)
{
two_sources_specified = FALSE;
}
else if (targets->nelts == 2)
{
if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
two_sources_specified = FALSE;
}
if (opt_state->revision_ranges->nelts > 0)
{
first_range_start = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
svn_opt_revision_range_t *)->start;
first_range_end = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
svn_opt_revision_range_t *)->end;
}
else
{
first_range_start.kind = first_range_end.kind =
svn_opt_revision_unspecified;
}
if (first_range_start.kind != svn_opt_revision_unspecified)
{
if (first_range_end.kind == svn_opt_revision_unspecified)
return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
_("Second revision required"));
two_sources_specified = FALSE;
}
if (! two_sources_specified)
{
if (targets->nelts > 2)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Too many arguments given"));
sourcepath2 = sourcepath1;
if (peg_revision1.kind == svn_opt_revision_unspecified)
peg_revision1.kind = svn_path_is_url(sourcepath1)
? svn_opt_revision_head : svn_opt_revision_working;
if (targets->nelts == 2)
{
targetpath = APR_ARRAY_IDX(targets, 1, const char *);
has_explicit_target = TRUE;
if (svn_path_is_url(targetpath))
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Cannot specify a revision range "
"with two URLs"));
}
}
else
{
if (targets->nelts < 2)
return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
if (targets->nelts > 3)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Too many arguments given"));
first_range_start = peg_revision1;
first_range_end = peg_revision2;
SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath1, &first_range_start,
pool));
SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath2, &first_range_end,
pool));
if (first_range_start.kind == svn_opt_revision_unspecified)
first_range_start.kind = svn_opt_revision_head;
if (first_range_end.kind == svn_opt_revision_unspecified)
first_range_end.kind = svn_opt_revision_head;
if (targets->nelts == 3)
{
targetpath = APR_ARRAY_IDX(targets, 2, const char *);
has_explicit_target = TRUE;
}
}
if (! has_explicit_target
&& sourcepath1 && sourcepath2
&& strcmp(targetpath, "") == 0)
{
if (svn_path_is_url(sourcepath1))
{
const char *sp1_basename = svn_uri_basename(sourcepath1, pool);
const char *sp2_basename = svn_uri_basename(sourcepath2, pool);
if (strcmp(sp1_basename, sp2_basename) == 0)
{
const char *target_url;
const char *target_base;
SVN_ERR(svn_client_url_from_path2(&target_url, targetpath, ctx,
pool, pool));
target_base = svn_uri_basename(target_url, pool);
if (strcmp(sp1_basename, target_base) != 0)
{
svn_node_kind_t kind;
SVN_ERR(svn_io_check_path(sp1_basename, &kind, pool));
if (kind == svn_node_file)
{
targetpath = sp1_basename;
}
}
}
}
else if (strcmp(sourcepath1, sourcepath2) == 0)
{
svn_node_kind_t kind;
SVN_ERR(svn_io_check_path(sourcepath1, &kind, pool));
if (kind == svn_node_file)
{
targetpath = sourcepath1;
}
}
}
if (opt_state->extensions)
options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
else
options = NULL;
if (opt_state->reintegrate)
{
if (opt_state->ignore_ancestry)
return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--reintegrate cannot be used with "
"--ignore-ancestry"));
if (opt_state->record_only)
return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--reintegrate cannot be used with "
"--record-only"));
if (opt_state->depth != svn_depth_unknown)
return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--depth cannot be used with "
"--reintegrate"));
if (opt_state->force)
return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--force cannot be used with "
"--reintegrate"));
if (two_sources_specified)
return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("--reintegrate can only be used with "
"a single merge source"));
if (opt_state->allow_mixed_rev)
return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
_("--allow-mixed-revisions cannot be used "
"with --reintegrate"));
}
merge_err = run_merge(two_sources_specified,
sourcepath1, peg_revision1,
sourcepath2,
targetpath,
ranges_to_merge, first_range_start, first_range_end,
opt_state, options, ctx, pool);
if (merge_err && merge_err->apr_err
== SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
{
return svn_error_quick_wrap(
merge_err,
_("Merge tracking not possible, use --ignore-ancestry or\n"
"fix invalid mergeinfo in target with 'svn propset'"));
}
if (!opt_state->quiet)
{
svn_error_t *err = svn_cl__notifier_print_conflict_stats(
ctx->notify_baton2, pool);
merge_err = svn_error_compose_create(merge_err, err);
}
return svn_cl__may_need_force(merge_err);
}