1251881Speter/* 2251881Speter * merge-cmd.c -- Merging changes into a working copy. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter 30251881Speter#include "svn_client.h" 31251881Speter#include "svn_dirent_uri.h" 32251881Speter#include "svn_path.h" 33251881Speter#include "svn_error.h" 34251881Speter#include "svn_types.h" 35251881Speter#include "cl.h" 36251881Speter#include "private/svn_client_private.h" 37251881Speter 38251881Speter#include "svn_private_config.h" 39251881Speter 40251881Speter/* A handy constant */ 41251881Speterstatic const svn_opt_revision_t unspecified_revision 42251881Speter = { svn_opt_revision_unspecified, { 0 } }; 43251881Speter 44251881Speter 45251881Speter/*** Code. ***/ 46251881Speter 47251881Speter/* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository 48251881Speter * revision. */ 49251881Speterstatic svn_error_t * 50251881Speterensure_wc_path_has_repo_revision(const char *path_or_url, 51251881Speter const svn_opt_revision_t *revision, 52251881Speter apr_pool_t *scratch_pool) 53251881Speter{ 54251881Speter if (revision->kind != svn_opt_revision_number 55251881Speter && revision->kind != svn_opt_revision_date 56251881Speter && revision->kind != svn_opt_revision_head 57251881Speter && ! svn_path_is_url(path_or_url)) 58251881Speter return svn_error_createf( 59251881Speter SVN_ERR_CLIENT_BAD_REVISION, NULL, 60251881Speter _("Invalid merge source '%s'; a working copy path can only be " 61251881Speter "used with a repository revision (a number, a date, or head)"), 62251881Speter svn_dirent_local_style(path_or_url, scratch_pool)); 63251881Speter return SVN_NO_ERROR; 64251881Speter} 65251881Speter 66251881Speter/* Run a merge. 67251881Speter * 68251881Speter * (No docs yet, as this code was just hoisted out of svn_cl__merge().) 69251881Speter * 70251881Speter * Having FIRST_RANGE_START/_END params is ugly -- we should be able to use 71251881Speter * PEG_REVISION1/2 and/or RANGES_TO_MERGE instead, maybe adjusting the caller. 72251881Speter */ 73251881Speterstatic svn_error_t * 74251881Speterrun_merge(svn_boolean_t two_sources_specified, 75251881Speter const char *sourcepath1, 76251881Speter svn_opt_revision_t peg_revision1, 77251881Speter const char *sourcepath2, 78251881Speter const char *targetpath, 79251881Speter apr_array_header_t *ranges_to_merge, 80251881Speter svn_opt_revision_t first_range_start, 81251881Speter svn_opt_revision_t first_range_end, 82251881Speter svn_cl__opt_state_t *opt_state, 83251881Speter apr_array_header_t *options, 84251881Speter svn_client_ctx_t *ctx, 85251881Speter apr_pool_t *scratch_pool) 86251881Speter{ 87251881Speter svn_error_t *merge_err; 88251881Speter 89251881Speter if (opt_state->reintegrate) 90251881Speter { 91251881Speter merge_err = svn_cl__deprecated_merge_reintegrate( 92251881Speter sourcepath1, &peg_revision1, targetpath, 93251881Speter opt_state->dry_run, options, ctx, scratch_pool); 94251881Speter } 95251881Speter else if (! two_sources_specified) 96251881Speter { 97251881Speter /* If we don't have at least one valid revision range, pick a 98251881Speter good one that spans the entire set of revisions on our 99251881Speter source. */ 100251881Speter if ((first_range_start.kind == svn_opt_revision_unspecified) 101251881Speter && (first_range_end.kind == svn_opt_revision_unspecified)) 102251881Speter { 103251881Speter ranges_to_merge = NULL; 104251881Speter } 105251881Speter 106251881Speter if (opt_state->verbose) 107251881Speter SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n"))); 108251881Speter merge_err = svn_client_merge_peg5(sourcepath1, 109251881Speter ranges_to_merge, 110251881Speter &peg_revision1, 111251881Speter targetpath, 112251881Speter opt_state->depth, 113251881Speter opt_state->ignore_ancestry, 114251881Speter opt_state->ignore_ancestry, 115251881Speter opt_state->force, /* force_delete */ 116251881Speter opt_state->record_only, 117251881Speter opt_state->dry_run, 118251881Speter opt_state->allow_mixed_rev, 119251881Speter options, 120251881Speter ctx, 121251881Speter scratch_pool); 122251881Speter } 123251881Speter else 124251881Speter { 125251881Speter if (svn_path_is_url(sourcepath1) != svn_path_is_url(sourcepath2)) 126251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 127251881Speter _("Merge sources must both be " 128251881Speter "either paths or URLs")); 129251881Speter 130251881Speter if (opt_state->verbose) 131251881Speter SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n"))); 132251881Speter merge_err = svn_client_merge5(sourcepath1, 133251881Speter &first_range_start, 134251881Speter sourcepath2, 135251881Speter &first_range_end, 136251881Speter targetpath, 137251881Speter opt_state->depth, 138251881Speter opt_state->ignore_ancestry, 139251881Speter opt_state->ignore_ancestry, 140251881Speter opt_state->force, /* force_delete */ 141251881Speter opt_state->record_only, 142251881Speter opt_state->dry_run, 143251881Speter opt_state->allow_mixed_rev, 144251881Speter options, 145251881Speter ctx, 146251881Speter scratch_pool); 147251881Speter } 148251881Speter 149251881Speter return merge_err; 150251881Speter} 151251881Speter 152251881Speter/* This implements the `svn_opt_subcommand_t' interface. */ 153251881Spetersvn_error_t * 154251881Spetersvn_cl__merge(apr_getopt_t *os, 155251881Speter void *baton, 156251881Speter apr_pool_t *pool) 157251881Speter{ 158251881Speter svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 159251881Speter svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 160251881Speter apr_array_header_t *targets; 161251881Speter const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = ""; 162251881Speter svn_boolean_t two_sources_specified = TRUE; 163251881Speter svn_error_t *merge_err; 164251881Speter svn_opt_revision_t first_range_start, first_range_end, peg_revision1, 165251881Speter peg_revision2; 166251881Speter apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges; 167251881Speter svn_boolean_t has_explicit_target = FALSE; 168251881Speter 169251881Speter /* Merge doesn't support specifying a revision or revision range 170251881Speter when using --reintegrate. */ 171251881Speter if (opt_state->reintegrate 172251881Speter && opt_state->start_revision.kind != svn_opt_revision_unspecified) 173251881Speter { 174251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 175251881Speter _("-r and -c can't be used with --reintegrate")); 176251881Speter } 177251881Speter 178251881Speter SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 179251881Speter opt_state->targets, 180251881Speter ctx, FALSE, pool)); 181251881Speter 182251881Speter /* For now, we require at least one source. That may change in 183251881Speter future versions of Subversion, for example if we have support for 184251881Speter negated mergeinfo. See this IRC conversation: 185251881Speter 186251881Speter <bhuvan> kfogel: yeah, i think you are correct; we should 187251881Speter specify the source url 188251881Speter 189251881Speter <kfogel> bhuvan: I'll change the help output and propose for 190251881Speter backport. Thanks. 191251881Speter 192251881Speter <bhuvan> kfogel: np; while we are at it, 'svn merge' simply 193251881Speter returns nothing; i think we should say: """svn: Not 194251881Speter enough arguments provided; try 'svn help' for more 195251881Speter info""" 196251881Speter 197251881Speter <kfogel> good idea 198251881Speter 199251881Speter <kfogel> (in the future, 'svn merge' might actually do 200251881Speter something, but that's all the more reason to make 201251881Speter sure it errors now) 202251881Speter 203251881Speter <cmpilato> actually, i'm pretty sure 'svn merge' does something 204251881Speter 205251881Speter <cmpilato> it says "please merge any unmerged changes from 206251881Speter myself to myself." 207251881Speter 208251881Speter <cmpilato> :-) 209251881Speter 210251881Speter <kfogel> har har 211251881Speter 212251881Speter <cmpilato> kfogel: i was serious. 213251881Speter 214251881Speter <kfogel> cmpilato: urrr, uh. Is that meaningful? Is there 215251881Speter ever a reason for a user to run it? 216251881Speter 217251881Speter <cmpilato> kfogel: not while we don't have support for negated 218251881Speter mergeinfo. 219251881Speter 220251881Speter <kfogel> cmpilato: do you concur that until it does something 221251881Speter useful it should error? 222251881Speter 223251881Speter <cmpilato> kfogel: yup. 224251881Speter 225251881Speter <kfogel> cool 226251881Speter */ 227251881Speter if (targets->nelts < 1) 228251881Speter { 229251881Speter return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, 230251881Speter _("Merge source required")); 231251881Speter } 232251881Speter else /* Parse at least one, and possible two, sources. */ 233251881Speter { 234251881Speter SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1, 235251881Speter APR_ARRAY_IDX(targets, 0, const char *), 236251881Speter pool)); 237251881Speter if (targets->nelts >= 2) 238251881Speter SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2, 239251881Speter APR_ARRAY_IDX(targets, 1, const char *), 240251881Speter pool)); 241251881Speter } 242251881Speter 243251881Speter /* We could have one or two sources. Deliberately written to stay 244251881Speter correct even if we someday permit implied merge source. */ 245251881Speter if (targets->nelts <= 1) 246251881Speter { 247251881Speter two_sources_specified = FALSE; 248251881Speter } 249251881Speter else if (targets->nelts == 2) 250251881Speter { 251251881Speter if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2)) 252251881Speter two_sources_specified = FALSE; 253251881Speter } 254251881Speter 255251881Speter if (opt_state->revision_ranges->nelts > 0) 256251881Speter { 257251881Speter first_range_start = APR_ARRAY_IDX(opt_state->revision_ranges, 0, 258251881Speter svn_opt_revision_range_t *)->start; 259251881Speter first_range_end = APR_ARRAY_IDX(opt_state->revision_ranges, 0, 260251881Speter svn_opt_revision_range_t *)->end; 261251881Speter } 262251881Speter else 263251881Speter { 264251881Speter first_range_start.kind = first_range_end.kind = 265251881Speter svn_opt_revision_unspecified; 266251881Speter } 267251881Speter 268251881Speter /* If revision_ranges has at least one real range at this point, then 269251881Speter we know the user must have used the '-r' and/or '-c' switch(es). 270251881Speter This means we're *not* doing two distinct sources. */ 271251881Speter if (first_range_start.kind != svn_opt_revision_unspecified) 272251881Speter { 273251881Speter /* A revision *range* is required. */ 274251881Speter if (first_range_end.kind == svn_opt_revision_unspecified) 275251881Speter return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, 276251881Speter _("Second revision required")); 277251881Speter 278251881Speter two_sources_specified = FALSE; 279251881Speter } 280251881Speter 281251881Speter if (! two_sources_specified) /* TODO: Switch order of if */ 282251881Speter { 283251881Speter if (targets->nelts > 2) 284251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 285251881Speter _("Too many arguments given")); 286251881Speter 287251881Speter /* Set the default value for unspecified paths and peg revision. */ 288251881Speter /* targets->nelts is 1 ("svn merge SOURCE") or 2 ("svn merge 289251881Speter SOURCE WCPATH") here. */ 290251881Speter sourcepath2 = sourcepath1; 291251881Speter 292251881Speter if (peg_revision1.kind == svn_opt_revision_unspecified) 293251881Speter peg_revision1.kind = svn_path_is_url(sourcepath1) 294251881Speter ? svn_opt_revision_head : svn_opt_revision_working; 295251881Speter 296251881Speter if (targets->nelts == 2) 297251881Speter { 298251881Speter targetpath = APR_ARRAY_IDX(targets, 1, const char *); 299251881Speter has_explicit_target = TRUE; 300251881Speter if (svn_path_is_url(targetpath)) 301251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 302251881Speter _("Cannot specify a revision range " 303251881Speter "with two URLs")); 304251881Speter } 305251881Speter } 306251881Speter else /* using @rev syntax */ 307251881Speter { 308251881Speter if (targets->nelts < 2) 309251881Speter return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL); 310251881Speter if (targets->nelts > 3) 311251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 312251881Speter _("Too many arguments given")); 313251881Speter 314251881Speter first_range_start = peg_revision1; 315251881Speter first_range_end = peg_revision2; 316251881Speter 317251881Speter /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit 318251881Speter revisions--since it ignores local modifications it may not do what 319251881Speter the user expects. That is, it doesn't read from the WC itself, it 320251881Speter reads from the WC's URL. Forcing the user to specify a repository 321251881Speter revision should avoid any confusion. */ 322251881Speter SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath1, &first_range_start, 323251881Speter pool)); 324251881Speter SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath2, &first_range_end, 325251881Speter pool)); 326251881Speter 327251881Speter /* Default peg revisions to each URL's youngest revision. */ 328251881Speter if (first_range_start.kind == svn_opt_revision_unspecified) 329251881Speter first_range_start.kind = svn_opt_revision_head; 330251881Speter if (first_range_end.kind == svn_opt_revision_unspecified) 331251881Speter first_range_end.kind = svn_opt_revision_head; 332251881Speter 333251881Speter /* Decide where to apply the delta (defaulting to "."). */ 334251881Speter if (targets->nelts == 3) 335251881Speter { 336251881Speter targetpath = APR_ARRAY_IDX(targets, 2, const char *); 337251881Speter has_explicit_target = TRUE; 338251881Speter } 339251881Speter } 340251881Speter 341251881Speter /* If no targetpath was specified, see if we can infer it from the 342251881Speter sourcepaths. */ 343251881Speter if (! has_explicit_target 344251881Speter && sourcepath1 && sourcepath2 345251881Speter && strcmp(targetpath, "") == 0) 346251881Speter { 347251881Speter /* If the sourcepath is a URL, it can only refer to a target in 348251881Speter the current working directory or which is the current working 349251881Speter directory. However, if the sourcepath is a local path, it can 350251881Speter refer to a target somewhere deeper in the directory structure. */ 351251881Speter if (svn_path_is_url(sourcepath1)) 352251881Speter { 353251881Speter const char *sp1_basename = svn_uri_basename(sourcepath1, pool); 354251881Speter const char *sp2_basename = svn_uri_basename(sourcepath2, pool); 355251881Speter 356251881Speter if (strcmp(sp1_basename, sp2_basename) == 0) 357251881Speter { 358251881Speter const char *target_url; 359251881Speter const char *target_base; 360251881Speter 361251881Speter SVN_ERR(svn_client_url_from_path2(&target_url, targetpath, ctx, 362251881Speter pool, pool)); 363251881Speter target_base = svn_uri_basename(target_url, pool); 364251881Speter 365251881Speter /* If the basename of the source is the same as the basename of 366251881Speter the cwd assume the cwd is the target. */ 367251881Speter if (strcmp(sp1_basename, target_base) != 0) 368251881Speter { 369251881Speter svn_node_kind_t kind; 370251881Speter 371251881Speter /* If the basename of the source differs from the basename 372251881Speter of the target. We still might assume the cwd is the 373251881Speter target, but first check if there is a file in the cwd 374251881Speter with the same name as the source basename. If there is, 375251881Speter then assume that file is the target. */ 376251881Speter SVN_ERR(svn_io_check_path(sp1_basename, &kind, pool)); 377251881Speter if (kind == svn_node_file) 378251881Speter { 379251881Speter targetpath = sp1_basename; 380251881Speter } 381251881Speter } 382251881Speter } 383251881Speter } 384251881Speter else if (strcmp(sourcepath1, sourcepath2) == 0) 385251881Speter { 386251881Speter svn_node_kind_t kind; 387251881Speter 388251881Speter SVN_ERR(svn_io_check_path(sourcepath1, &kind, pool)); 389251881Speter if (kind == svn_node_file) 390251881Speter { 391251881Speter targetpath = sourcepath1; 392251881Speter } 393251881Speter } 394251881Speter } 395251881Speter 396251881Speter if (opt_state->extensions) 397251881Speter options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool); 398251881Speter else 399251881Speter options = NULL; 400251881Speter 401251881Speter /* More input validation. */ 402251881Speter if (opt_state->reintegrate) 403251881Speter { 404251881Speter if (opt_state->ignore_ancestry) 405251881Speter return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, 406251881Speter _("--reintegrate cannot be used with " 407251881Speter "--ignore-ancestry")); 408251881Speter 409251881Speter if (opt_state->record_only) 410251881Speter return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, 411251881Speter _("--reintegrate cannot be used with " 412251881Speter "--record-only")); 413251881Speter 414251881Speter if (opt_state->depth != svn_depth_unknown) 415251881Speter return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, 416251881Speter _("--depth cannot be used with " 417251881Speter "--reintegrate")); 418251881Speter 419251881Speter if (opt_state->force) 420251881Speter return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, 421251881Speter _("--force cannot be used with " 422251881Speter "--reintegrate")); 423251881Speter 424251881Speter if (two_sources_specified) 425251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 426251881Speter _("--reintegrate can only be used with " 427251881Speter "a single merge source")); 428251881Speter if (opt_state->allow_mixed_rev) 429251881Speter return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL, 430251881Speter _("--allow-mixed-revisions cannot be used " 431251881Speter "with --reintegrate")); 432251881Speter } 433251881Speter 434251881Speter merge_err = run_merge(two_sources_specified, 435251881Speter sourcepath1, peg_revision1, 436251881Speter sourcepath2, 437251881Speter targetpath, 438251881Speter ranges_to_merge, first_range_start, first_range_end, 439251881Speter opt_state, options, ctx, pool); 440251881Speter if (merge_err && merge_err->apr_err 441251881Speter == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING) 442251881Speter { 443251881Speter return svn_error_quick_wrap( 444251881Speter merge_err, 445251881Speter _("Merge tracking not possible, use --ignore-ancestry or\n" 446251881Speter "fix invalid mergeinfo in target with 'svn propset'")); 447251881Speter } 448251881Speter 449251881Speter if (!opt_state->quiet) 450251881Speter { 451251881Speter svn_error_t *err = svn_cl__notifier_print_conflict_stats( 452251881Speter ctx->notify_baton2, pool); 453251881Speter 454251881Speter merge_err = svn_error_compose_create(merge_err, err); 455251881Speter } 456251881Speter 457251881Speter return svn_cl__may_need_force(merge_err); 458251881Speter} 459