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 (opt_state->verbose)
127 SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n")));
128 merge_err = svn_client_merge5(sourcepath1,
134 opt_state->ignore_ancestry,
135 opt_state->ignore_ancestry,
136 opt_state->force, /* force_delete */
137 opt_state->record_only,
139 opt_state->allow_mixed_rev,
148 /* This implements the `svn_opt_subcommand_t' interface. */
150 svn_cl__merge(apr_getopt_t *os,
154 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
155 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
156 apr_array_header_t *targets;
157 const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = "";
158 svn_boolean_t two_sources_specified = TRUE;
159 svn_error_t *merge_err;
160 svn_opt_revision_t first_range_start, first_range_end, peg_revision1,
162 apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
163 svn_boolean_t has_explicit_target = FALSE;
165 /* Merge doesn't support specifying a revision or revision range
166 when using --reintegrate. */
167 if (opt_state->reintegrate
168 && opt_state->start_revision.kind != svn_opt_revision_unspecified)
170 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
171 _("-r and -c can't be used with --reintegrate"));
174 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
178 /* For now, we require at least one source. That may change in
179 future versions of Subversion, for example if we have support for
180 negated mergeinfo. See this IRC conversation:
182 <bhuvan> kfogel: yeah, i think you are correct; we should
183 specify the source url
185 <kfogel> bhuvan: I'll change the help output and propose for
188 <bhuvan> kfogel: np; while we are at it, 'svn merge' simply
189 returns nothing; i think we should say: """svn: Not
190 enough arguments provided; try 'svn help' for more
195 <kfogel> (in the future, 'svn merge' might actually do
196 something, but that's all the more reason to make
199 <cmpilato> actually, i'm pretty sure 'svn merge' does something
201 <cmpilato> it says "please merge any unmerged changes from
208 <cmpilato> kfogel: i was serious.
210 <kfogel> cmpilato: urrr, uh. Is that meaningful? Is there
211 ever a reason for a user to run it?
213 <cmpilato> kfogel: not while we don't have support for negated
216 <kfogel> cmpilato: do you concur that until it does something
217 useful it should error?
219 <cmpilato> kfogel: yup.
223 if (targets->nelts < 1)
225 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
226 _("Merge source required"));
228 else /* Parse at least one, and possible two, sources. */
230 SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
231 APR_ARRAY_IDX(targets, 0, const char *),
233 if (targets->nelts >= 2)
234 SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
235 APR_ARRAY_IDX(targets, 1, const char *),
239 /* We could have one or two sources. Deliberately written to stay
240 correct even if we someday permit implied merge source. */
241 if (targets->nelts <= 1)
243 two_sources_specified = FALSE;
245 else if (targets->nelts == 2)
247 if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
248 two_sources_specified = FALSE;
251 if (opt_state->revision_ranges->nelts > 0)
253 first_range_start = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
254 svn_opt_revision_range_t *)->start;
255 first_range_end = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
256 svn_opt_revision_range_t *)->end;
260 first_range_start.kind = first_range_end.kind =
261 svn_opt_revision_unspecified;
264 /* If revision_ranges has at least one real range at this point, then
265 we know the user must have used the '-r' and/or '-c' switch(es).
266 This means we're *not* doing two distinct sources. */
267 if (first_range_start.kind != svn_opt_revision_unspecified)
269 /* A revision *range* is required. */
270 if (first_range_end.kind == svn_opt_revision_unspecified)
271 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
272 _("Second revision required"));
274 two_sources_specified = FALSE;
277 if (! two_sources_specified) /* TODO: Switch order of if */
279 if (targets->nelts > 2)
280 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
281 _("Too many arguments given"));
283 /* Set the default value for unspecified paths and peg revision. */
284 /* targets->nelts is 1 ("svn merge SOURCE") or 2 ("svn merge
285 SOURCE WCPATH") here. */
286 sourcepath2 = sourcepath1;
288 if (peg_revision1.kind == svn_opt_revision_unspecified)
289 peg_revision1.kind = svn_path_is_url(sourcepath1)
290 ? svn_opt_revision_head : svn_opt_revision_working;
292 if (targets->nelts == 2)
294 targetpath = APR_ARRAY_IDX(targets, 1, const char *);
295 has_explicit_target = TRUE;
296 if (svn_path_is_url(targetpath))
297 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
298 _("Cannot specify a revision range "
302 else /* using @rev syntax */
304 if (targets->nelts < 2)
305 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
306 if (targets->nelts > 3)
307 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
308 _("Too many arguments given"));
310 first_range_start = peg_revision1;
311 first_range_end = peg_revision2;
313 /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit
314 revisions--since it ignores local modifications it may not do what
315 the user expects. That is, it doesn't read from the WC itself, it
316 reads from the WC's URL. Forcing the user to specify a repository
317 revision should avoid any confusion. */
318 SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath1, &first_range_start,
320 SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath2, &first_range_end,
323 /* Default peg revisions to each URL's youngest revision. */
324 if (first_range_start.kind == svn_opt_revision_unspecified)
325 first_range_start.kind = svn_opt_revision_head;
326 if (first_range_end.kind == svn_opt_revision_unspecified)
327 first_range_end.kind = svn_opt_revision_head;
329 /* Decide where to apply the delta (defaulting to "."). */
330 if (targets->nelts == 3)
332 targetpath = APR_ARRAY_IDX(targets, 2, const char *);
333 has_explicit_target = TRUE;
337 /* If no targetpath was specified, see if we can infer it from the
339 if (! has_explicit_target
340 && sourcepath1 && sourcepath2
341 && strcmp(targetpath, "") == 0)
343 /* If the sourcepath is a URL, it can only refer to a target in
344 the current working directory or which is the current working
345 directory. However, if the sourcepath is a local path, it can
346 refer to a target somewhere deeper in the directory structure. */
347 if (svn_path_is_url(sourcepath1))
349 const char *sp1_basename = svn_uri_basename(sourcepath1, pool);
350 const char *sp2_basename = svn_uri_basename(sourcepath2, pool);
352 if (strcmp(sp1_basename, sp2_basename) == 0)
354 const char *target_url;
355 const char *target_base;
357 SVN_ERR(svn_client_url_from_path2(&target_url, targetpath, ctx,
359 target_base = svn_uri_basename(target_url, pool);
361 /* If the basename of the source is the same as the basename of
362 the cwd assume the cwd is the target. */
363 if (strcmp(sp1_basename, target_base) != 0)
365 svn_node_kind_t kind;
367 /* If the basename of the source differs from the basename
368 of the target. We still might assume the cwd is the
369 target, but first check if there is a file in the cwd
370 with the same name as the source basename. If there is,
371 then assume that file is the target. */
372 SVN_ERR(svn_io_check_path(sp1_basename, &kind, pool));
373 if (kind == svn_node_file)
375 targetpath = sp1_basename;
380 else if (strcmp(sourcepath1, sourcepath2) == 0)
382 svn_node_kind_t kind;
384 SVN_ERR(svn_io_check_path(sourcepath1, &kind, pool));
385 if (kind == svn_node_file)
387 targetpath = sourcepath1;
392 if (opt_state->extensions)
393 options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
397 /* More input validation. */
398 if (opt_state->reintegrate)
400 if (opt_state->ignore_ancestry)
401 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
402 _("--reintegrate cannot be used with "
403 "--ignore-ancestry"));
405 if (opt_state->record_only)
406 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
407 _("--reintegrate cannot be used with "
410 if (opt_state->depth != svn_depth_unknown)
411 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
412 _("--depth cannot be used with "
415 if (opt_state->force)
416 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
417 _("--force cannot be used with "
420 if (two_sources_specified)
421 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
422 _("--reintegrate can only be used with "
423 "a single merge source"));
424 if (opt_state->allow_mixed_rev)
425 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
426 _("--allow-mixed-revisions cannot be used "
427 "with --reintegrate"));
430 merge_err = run_merge(two_sources_specified,
431 sourcepath1, peg_revision1,
434 ranges_to_merge, first_range_start, first_range_end,
435 opt_state, options, ctx, pool);
436 if (merge_err && merge_err->apr_err
437 == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
439 return svn_error_quick_wrap(
441 _("Merge tracking not possible, use --ignore-ancestry or\n"
442 "fix invalid mergeinfo in target with 'svn propset'"));
445 if (!opt_state->quiet)
447 svn_error_t *err = svn_cl__notifier_print_conflict_stats(
448 ctx->notify_baton2, pool);
450 merge_err = svn_error_compose_create(merge_err, err);
453 return svn_cl__may_need_force(merge_err);