2 * merge-cmd.c -- Merging changes into a working copy.
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 * ====================================================================
24 /* ==================================================================== */
30 #include "svn_client.h"
31 #include "svn_dirent_uri.h"
33 #include "svn_error.h"
34 #include "svn_types.h"
36 #include "private/svn_client_private.h"
38 #include "svn_private_config.h"
43 /* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
46 ensure_wc_path_has_repo_revision(const char *path_or_url,
47 const svn_opt_revision_t *revision,
48 apr_pool_t *scratch_pool)
50 if (revision->kind != svn_opt_revision_number
51 && revision->kind != svn_opt_revision_date
52 && revision->kind != svn_opt_revision_head
53 && ! svn_path_is_url(path_or_url))
54 return svn_error_createf(
55 SVN_ERR_CLIENT_BAD_REVISION, NULL,
56 _("Invalid merge source '%s'; a working copy path can only be "
57 "used with a repository revision (a number, a date, or head)"),
58 svn_dirent_local_style(path_or_url, scratch_pool));
64 * (No docs yet, as this code was just hoisted out of svn_cl__merge().)
66 * Having FIRST_RANGE_START/_END params is ugly -- we should be able to use
67 * PEG_REVISION1/2 and/or RANGES_TO_MERGE instead, maybe adjusting the caller.
70 run_merge(svn_boolean_t two_sources_specified,
71 const char *sourcepath1,
72 svn_opt_revision_t peg_revision1,
73 const char *sourcepath2,
74 const char *targetpath,
75 apr_array_header_t *ranges_to_merge,
76 svn_opt_revision_t first_range_start,
77 svn_opt_revision_t first_range_end,
78 svn_cl__opt_state_t *opt_state,
79 apr_array_header_t *options,
80 svn_client_ctx_t *ctx,
81 apr_pool_t *scratch_pool)
83 svn_error_t *merge_err;
85 if (opt_state->reintegrate)
87 merge_err = svn_cl__deprecated_merge_reintegrate(
88 sourcepath1, &peg_revision1, targetpath,
89 opt_state->dry_run, options, ctx, scratch_pool);
91 else if (! two_sources_specified)
93 /* If we don't have at least one valid revision range, pick a
94 good one that spans the entire set of revisions on our
96 if ((first_range_start.kind == svn_opt_revision_unspecified)
97 && (first_range_end.kind == svn_opt_revision_unspecified))
99 ranges_to_merge = NULL;
102 if (opt_state->verbose)
103 SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n")));
104 merge_err = svn_client_merge_peg5(sourcepath1,
109 opt_state->ignore_ancestry,
110 opt_state->ignore_ancestry,
111 opt_state->force, /* force_delete */
112 opt_state->record_only,
114 opt_state->allow_mixed_rev,
121 if (svn_path_is_url(sourcepath1) != svn_path_is_url(sourcepath2))
122 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
123 _("Merge sources must both be "
124 "either paths or URLs"));
126 if (svn_path_is_url(targetpath))
127 return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
128 _("Merge target '%s' must be a local path "
129 "but looks like a URL"), targetpath);
131 if (opt_state->verbose)
132 SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n")));
133 merge_err = svn_client_merge5(sourcepath1,
139 opt_state->ignore_ancestry,
140 opt_state->ignore_ancestry,
141 opt_state->force, /* force_delete */
142 opt_state->record_only,
144 opt_state->allow_mixed_rev,
153 /* Baton type for conflict_func_merge_cmd(). */
154 struct conflict_func_merge_cmd_baton {
155 svn_cl__accept_t accept_which;
156 const char *path_prefix;
157 svn_cl__conflict_stats_t *conflict_stats;
160 /* This implements the `svn_wc_conflict_resolver_func2_t ' interface.
162 * The merge subcommand needs to install this legacy conflict callback
163 * in case the user passed an --accept option to 'svn merge'.
164 * Otherwise, merges involving multiple editor drives might encounter a
165 * conflict during one of the editor drives and abort with an error,
166 * rather than resolving conflicts as per the --accept option and
167 * continuing with the next editor drive.
168 * ### TODO add an svn_client_merge API that makes this callback unnecessary
171 conflict_func_merge_cmd(svn_wc_conflict_result_t **result,
172 const svn_wc_conflict_description2_t *desc,
174 apr_pool_t *result_pool,
175 apr_pool_t *scratch_pool)
177 struct conflict_func_merge_cmd_baton *b = baton;
178 svn_wc_conflict_choice_t choice;
180 switch (b->accept_which)
182 case svn_cl__accept_postpone:
183 case svn_cl__accept_invalid:
184 case svn_cl__accept_unspecified:
185 case svn_cl__accept_recommended:
186 /* Postpone or no valid --accept option, postpone the conflict. */
187 choice = svn_wc_conflict_choose_postpone;
189 case svn_cl__accept_base:
190 choice = svn_wc_conflict_choose_base;
192 case svn_cl__accept_working:
193 choice = svn_wc_conflict_choose_merged;
195 case svn_cl__accept_mine_conflict:
196 choice = svn_wc_conflict_choose_mine_conflict;
198 case svn_cl__accept_theirs_conflict:
199 choice = svn_wc_conflict_choose_theirs_conflict;
201 case svn_cl__accept_mine_full:
202 choice = svn_wc_conflict_choose_mine_full;
204 case svn_cl__accept_theirs_full:
205 choice = svn_wc_conflict_choose_theirs_full;
207 case svn_cl__accept_edit:
208 case svn_cl__accept_launch:
209 /* The 'edit' and 'launch' options used to be valid in Subversion 1.9 but
210 * we can't support these options for the purposes of this callback. */
211 choice = svn_wc_conflict_choose_postpone;
215 *result = svn_wc_create_conflict_result(choice, NULL, result_pool);
217 /* If we are resolving a conflict, adjust the summary of conflicts. */
218 if (choice != svn_wc_conflict_choose_postpone)
220 const char *local_path;
222 local_path = svn_cl__local_style_skip_ancestor(b->path_prefix,
225 svn_cl__conflict_stats_resolved(b->conflict_stats, local_path,
232 /* This implements the `svn_opt_subcommand_t' interface. */
234 svn_cl__merge(apr_getopt_t *os,
238 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
239 svn_cl__conflict_stats_t *conflict_stats =
240 ((svn_cl__cmd_baton_t *) baton)->conflict_stats;
241 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
242 apr_array_header_t *targets;
243 const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = "";
244 svn_boolean_t two_sources_specified = TRUE;
245 svn_error_t *merge_err;
246 svn_opt_revision_t first_range_start, first_range_end, peg_revision1,
248 apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
249 apr_array_header_t *conflicted_paths;
250 svn_boolean_t has_explicit_target = FALSE;
252 /* Merge doesn't support specifying a revision or revision range
253 when using --reintegrate. */
254 if (opt_state->reintegrate
255 && opt_state->start_revision.kind != svn_opt_revision_unspecified)
257 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
258 _("-r and -c can't be used with --reintegrate"));
261 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
265 /* For now, we require at least one source. That may change in
266 future versions of Subversion, for example if we have support for
267 negated mergeinfo. See this IRC conversation:
269 <bhuvan> kfogel: yeah, i think you are correct; we should
270 specify the source url
272 <kfogel> bhuvan: I'll change the help output and propose for
275 <bhuvan> kfogel: np; while we are at it, 'svn merge' simply
276 returns nothing; i think we should say: """svn: Not
277 enough arguments provided; try 'svn help' for more
282 <kfogel> (in the future, 'svn merge' might actually do
283 something, but that's all the more reason to make
286 <cmpilato> actually, i'm pretty sure 'svn merge' does something
288 <cmpilato> it says "please merge any unmerged changes from
295 <cmpilato> kfogel: i was serious.
297 <kfogel> cmpilato: urrr, uh. Is that meaningful? Is there
298 ever a reason for a user to run it?
300 <cmpilato> kfogel: not while we don't have support for negated
303 <kfogel> cmpilato: do you concur that until it does something
304 useful it should error?
306 <cmpilato> kfogel: yup.
310 if (targets->nelts < 1)
312 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
313 _("Merge source required"));
315 else /* Parse at least one, and possible two, sources. */
317 SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
318 APR_ARRAY_IDX(targets, 0, const char *),
320 if (targets->nelts >= 2)
321 SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
322 APR_ARRAY_IDX(targets, 1, const char *),
326 /* We could have one or two sources. Deliberately written to stay
327 correct even if we someday permit implied merge source. */
328 if (targets->nelts <= 1)
330 two_sources_specified = FALSE;
332 else if (targets->nelts == 2)
334 if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
335 two_sources_specified = FALSE;
338 if (opt_state->revision_ranges->nelts > 0)
340 first_range_start = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
341 svn_opt_revision_range_t *)->start;
342 first_range_end = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
343 svn_opt_revision_range_t *)->end;
347 first_range_start.kind = first_range_end.kind =
348 svn_opt_revision_unspecified;
351 /* If revision_ranges has at least one real range at this point, then
352 we know the user must have used the '-r' and/or '-c' switch(es).
353 This means we're *not* doing two distinct sources. */
354 if (first_range_start.kind != svn_opt_revision_unspecified)
356 /* A revision *range* is required. */
357 if (first_range_end.kind == svn_opt_revision_unspecified)
358 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
359 _("Second revision required"));
361 two_sources_specified = FALSE;
364 if (! two_sources_specified) /* TODO: Switch order of if */
366 if (targets->nelts > 2)
367 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
368 _("Too many arguments given"));
370 /* Set the default value for unspecified paths and peg revision. */
371 /* targets->nelts is 1 ("svn merge SOURCE") or 2 ("svn merge
372 SOURCE WCPATH") here. */
373 sourcepath2 = sourcepath1;
375 if (peg_revision1.kind == svn_opt_revision_unspecified)
376 peg_revision1.kind = svn_path_is_url(sourcepath1)
377 ? svn_opt_revision_head : svn_opt_revision_working;
379 if (targets->nelts == 2)
381 targetpath = APR_ARRAY_IDX(targets, 1, const char *);
382 has_explicit_target = TRUE;
383 if (svn_path_is_url(targetpath))
384 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
385 _("Cannot specify a revision range "
389 else /* using @rev syntax */
391 if (targets->nelts < 2)
392 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
393 if (targets->nelts > 3)
394 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
395 _("Too many arguments given"));
397 first_range_start = peg_revision1;
398 first_range_end = peg_revision2;
400 /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit
401 revisions--since it ignores local modifications it may not do what
402 the user expects. That is, it doesn't read from the WC itself, it
403 reads from the WC's URL. Forcing the user to specify a repository
404 revision should avoid any confusion. */
405 SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath1, &first_range_start,
407 SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath2, &first_range_end,
410 /* Default peg revisions to each URL's youngest revision. */
411 if (first_range_start.kind == svn_opt_revision_unspecified)
412 first_range_start.kind = svn_opt_revision_head;
413 if (first_range_end.kind == svn_opt_revision_unspecified)
414 first_range_end.kind = svn_opt_revision_head;
416 /* Decide where to apply the delta (defaulting to "."). */
417 if (targets->nelts == 3)
419 targetpath = APR_ARRAY_IDX(targets, 2, const char *);
420 has_explicit_target = TRUE;
424 /* If no targetpath was specified, see if we can infer it from the
426 if (! has_explicit_target
427 && sourcepath1 && sourcepath2
428 && strcmp(targetpath, "") == 0)
430 /* If the sourcepath is a URL, it can only refer to a target in
431 the current working directory or which is the current working
432 directory. However, if the sourcepath is a local path, it can
433 refer to a target somewhere deeper in the directory structure. */
434 if (svn_path_is_url(sourcepath1))
436 const char *sp1_basename = svn_uri_basename(sourcepath1, pool);
437 const char *sp2_basename = svn_uri_basename(sourcepath2, pool);
439 if (strcmp(sp1_basename, sp2_basename) == 0)
441 const char *target_url;
442 const char *target_base;
444 SVN_ERR(svn_client_url_from_path2(&target_url, targetpath, ctx,
446 target_base = svn_uri_basename(target_url, pool);
448 /* If the basename of the source is the same as the basename of
449 the cwd assume the cwd is the target. */
450 if (strcmp(sp1_basename, target_base) != 0)
452 svn_node_kind_t kind;
454 /* If the basename of the source differs from the basename
455 of the target. We still might assume the cwd is the
456 target, but first check if there is a file in the cwd
457 with the same name as the source basename. If there is,
458 then assume that file is the target. */
459 SVN_ERR(svn_io_check_path(sp1_basename, &kind, pool));
460 if (kind == svn_node_file)
462 targetpath = sp1_basename;
467 else if (strcmp(sourcepath1, sourcepath2) == 0)
469 svn_node_kind_t kind;
471 SVN_ERR(svn_io_check_path(sourcepath1, &kind, pool));
472 if (kind == svn_node_file)
474 targetpath = sourcepath1;
479 if (opt_state->extensions)
480 options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
484 /* More input validation. */
485 if (opt_state->reintegrate)
487 if (opt_state->ignore_ancestry)
488 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
489 _("--reintegrate cannot be used with "
490 "--ignore-ancestry"));
492 if (opt_state->record_only)
493 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
494 _("--reintegrate cannot be used with "
497 if (opt_state->depth != svn_depth_unknown)
498 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
499 _("--depth cannot be used with "
502 if (opt_state->force)
503 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
504 _("--force cannot be used with "
507 if (two_sources_specified)
508 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
509 _("--reintegrate can only be used with "
510 "a single merge source"));
511 if (opt_state->allow_mixed_rev)
512 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
513 _("--allow-mixed-revisions cannot be used "
514 "with --reintegrate"));
517 /* Install a legacy conflict handler if the --accept option was given.
518 * Else, svn_client_merge5() may abort the merge in an undesirable way.
519 * See the docstring at conflict_func_merge_cmd() for details */
520 if (opt_state->accept_which != svn_cl__accept_unspecified)
522 struct conflict_func_merge_cmd_baton *b = apr_pcalloc(pool, sizeof(*b));
524 b->accept_which = opt_state->accept_which;
525 SVN_ERR(svn_dirent_get_absolute(&b->path_prefix, "", pool));
526 b->conflict_stats = conflict_stats;
528 ctx->conflict_func2 = conflict_func_merge_cmd;
529 ctx->conflict_baton2 = b;
533 merge_err = run_merge(two_sources_specified,
534 sourcepath1, peg_revision1,
537 ranges_to_merge, first_range_start, first_range_end,
538 opt_state, options, ctx, pool);
539 if (merge_err && merge_err->apr_err
540 == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
542 return svn_error_quick_wrap(
544 _("Merge tracking not possible, use --ignore-ancestry or\n"
545 "fix invalid mergeinfo in target with 'svn propset'"));
548 /* Run the interactive resolver if conflicts were raised. */
549 SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
551 if (conflicted_paths)
553 SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
554 opt_state, ctx, pool));
556 svn_error_root_cause(merge_err)->apr_err == SVN_ERR_WC_FOUND_CONFLICT)
560 /* Check if all conflicts were resolved just now. */
561 err = svn_cl__conflict_stats_get_paths(&conflicted_paths,
562 conflict_stats, pool, pool);
564 merge_err = svn_error_compose_create(merge_err, err);
565 else if (conflicted_paths == NULL)
567 svn_error_clear(merge_err);
568 goto retry; /* ### conflicts resolved; continue merging */
573 if (!opt_state->quiet)
575 svn_error_t *err = svn_cl__notifier_print_conflict_stats(
576 ctx->notify_baton2, pool);
578 merge_err = svn_error_compose_create(merge_err, err);
581 return svn_cl__may_need_force(merge_err);