]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/svn/merge-cmd.c
Update svn-1.9.7 to 1.10.0.
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / svn / merge-cmd.c
1 /*
2  * merge-cmd.c -- Merging changes into a working copy.
3  *
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29
30 #include "svn_client.h"
31 #include "svn_dirent_uri.h"
32 #include "svn_path.h"
33 #include "svn_error.h"
34 #include "svn_types.h"
35 #include "cl.h"
36 #include "private/svn_client_private.h"
37
38 #include "svn_private_config.h"
39
40 \f
41 /*** Code. ***/
42
43 /* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
44  * revision. */
45 static svn_error_t *
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)
49 {
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));
59   return SVN_NO_ERROR;
60 }
61
62 /* Run a merge.
63  *
64  * (No docs yet, as this code was just hoisted out of svn_cl__merge().)
65  *
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.
68  */
69 static svn_error_t *
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)
82 {
83   svn_error_t *merge_err;
84
85   if (opt_state->reintegrate)
86     {
87       merge_err = svn_cl__deprecated_merge_reintegrate(
88                     sourcepath1, &peg_revision1, targetpath,
89                     opt_state->dry_run, options, ctx, scratch_pool);
90     }
91   else if (! two_sources_specified)
92     {
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
95          source. */
96       if ((first_range_start.kind == svn_opt_revision_unspecified)
97           && (first_range_end.kind == svn_opt_revision_unspecified))
98         {
99           ranges_to_merge = NULL;
100         }
101
102       if (opt_state->verbose)
103         SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n")));
104       merge_err = svn_client_merge_peg5(sourcepath1,
105                                         ranges_to_merge,
106                                         &peg_revision1,
107                                         targetpath,
108                                         opt_state->depth,
109                                         opt_state->ignore_ancestry,
110                                         opt_state->ignore_ancestry,
111                                         opt_state->force, /* force_delete */
112                                         opt_state->record_only,
113                                         opt_state->dry_run,
114                                         opt_state->allow_mixed_rev,
115                                         options,
116                                         ctx,
117                                         scratch_pool);
118     }
119   else
120     {
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"));
125
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);
130
131       if (opt_state->verbose)
132         SVN_ERR(svn_cmdline_printf(scratch_pool, _("--- Merging\n")));
133       merge_err = svn_client_merge5(sourcepath1,
134                                     &first_range_start,
135                                     sourcepath2,
136                                     &first_range_end,
137                                     targetpath,
138                                     opt_state->depth,
139                                     opt_state->ignore_ancestry,
140                                     opt_state->ignore_ancestry,
141                                     opt_state->force, /* force_delete */
142                                     opt_state->record_only,
143                                     opt_state->dry_run,
144                                     opt_state->allow_mixed_rev,
145                                     options,
146                                     ctx,
147                                     scratch_pool);
148     }
149
150   return merge_err;
151 }
152
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;
158 };
159
160 /* This implements the `svn_wc_conflict_resolver_func2_t ' interface.
161  *
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
169  */
170 static svn_error_t *
171 conflict_func_merge_cmd(svn_wc_conflict_result_t **result,
172                         const svn_wc_conflict_description2_t *desc,
173                         void *baton,
174                         apr_pool_t *result_pool,
175                         apr_pool_t *scratch_pool)
176 {
177   struct conflict_func_merge_cmd_baton *b = baton;
178   svn_wc_conflict_choice_t choice;
179
180   switch (b->accept_which)
181     {
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;
188       break;
189     case svn_cl__accept_base:
190       choice = svn_wc_conflict_choose_base;
191       break;
192     case svn_cl__accept_working:
193       choice = svn_wc_conflict_choose_merged;
194       break;
195     case svn_cl__accept_mine_conflict:
196       choice = svn_wc_conflict_choose_mine_conflict;
197       break;
198     case svn_cl__accept_theirs_conflict:
199       choice = svn_wc_conflict_choose_theirs_conflict;
200       break;
201     case svn_cl__accept_mine_full:
202       choice = svn_wc_conflict_choose_mine_full;
203       break;
204     case svn_cl__accept_theirs_full:
205       choice = svn_wc_conflict_choose_theirs_full;
206       break;
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;
212       break;
213     }
214
215   *result = svn_wc_create_conflict_result(choice, NULL, result_pool);
216
217   /* If we are resolving a conflict, adjust the summary of conflicts. */
218   if (choice != svn_wc_conflict_choose_postpone)
219     {
220       const char *local_path;
221
222       local_path = svn_cl__local_style_skip_ancestor(b->path_prefix,
223                                                      desc->local_abspath,
224                                                      scratch_pool);
225       svn_cl__conflict_stats_resolved(b->conflict_stats, local_path,
226                                       desc->kind);
227     }
228
229   return SVN_NO_ERROR;
230 }
231
232 /* This implements the `svn_opt_subcommand_t' interface. */
233 svn_error_t *
234 svn_cl__merge(apr_getopt_t *os,
235               void *baton,
236               apr_pool_t *pool)
237 {
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,
247     peg_revision2;
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;
251
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)
256     {
257       return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
258                               _("-r and -c can't be used with --reintegrate"));
259     }
260
261   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
262                                                       opt_state->targets,
263                                                       ctx, FALSE, pool));
264
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:
268
269        <bhuvan>   kfogel: yeah, i think you are correct; we should
270                   specify the source url
271
272        <kfogel>   bhuvan: I'll change the help output and propose for
273                   backport.  Thanks.
274
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
278                   info"""
279
280        <kfogel>   good idea
281
282        <kfogel>   (in the future, 'svn merge' might actually do
283                   something, but that's all the more reason to make
284                   sure it errors now)
285
286        <cmpilato> actually, i'm pretty sure 'svn merge' does something
287
288        <cmpilato> it says "please merge any unmerged changes from
289                   myself to myself."
290
291        <cmpilato> :-)
292
293        <kfogel>   har har
294
295        <cmpilato> kfogel: i was serious.
296
297        <kfogel>   cmpilato: urrr, uh.  Is that meaningful?  Is there
298                   ever a reason for a user to run it?
299
300        <cmpilato> kfogel: not while we don't have support for negated
301                   mergeinfo.
302
303        <kfogel>   cmpilato: do you concur that until it does something
304                   useful it should error?
305
306        <cmpilato> kfogel: yup.
307
308        <kfogel>   cool
309   */
310   if (targets->nelts < 1)
311     {
312       return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
313                               _("Merge source required"));
314     }
315   else  /* Parse at least one, and possible two, sources. */
316     {
317       SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
318                                  APR_ARRAY_IDX(targets, 0, const char *),
319                                  pool));
320       if (targets->nelts >= 2)
321         SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
322                                    APR_ARRAY_IDX(targets, 1, const char *),
323                                    pool));
324     }
325
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)
329     {
330       two_sources_specified = FALSE;
331     }
332   else if (targets->nelts == 2)
333     {
334       if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
335         two_sources_specified = FALSE;
336     }
337
338   if (opt_state->revision_ranges->nelts > 0)
339     {
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;
344     }
345   else
346     {
347       first_range_start.kind = first_range_end.kind =
348         svn_opt_revision_unspecified;
349     }
350
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)
355     {
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"));
360
361       two_sources_specified = FALSE;
362     }
363
364   if (! two_sources_specified) /* TODO: Switch order of if */
365     {
366       if (targets->nelts > 2)
367         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
368                                 _("Too many arguments given"));
369
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;
374
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;
378
379       if (targets->nelts == 2)
380         {
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 "
386                                       "with two URLs"));
387         }
388     }
389   else /* using @rev syntax */
390     {
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"));
396
397       first_range_start = peg_revision1;
398       first_range_end = peg_revision2;
399
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,
406                                                pool));
407       SVN_ERR(ensure_wc_path_has_repo_revision(sourcepath2, &first_range_end,
408                                                pool));
409
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;
415
416       /* Decide where to apply the delta (defaulting to "."). */
417       if (targets->nelts == 3)
418         {
419           targetpath = APR_ARRAY_IDX(targets, 2, const char *);
420           has_explicit_target = TRUE;
421         }
422     }
423
424   /* If no targetpath was specified, see if we can infer it from the
425      sourcepaths. */
426   if (! has_explicit_target
427       && sourcepath1 && sourcepath2
428       && strcmp(targetpath, "") == 0)
429     {
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))
435         {
436           const char *sp1_basename = svn_uri_basename(sourcepath1, pool);
437           const char *sp2_basename = svn_uri_basename(sourcepath2, pool);
438
439           if (strcmp(sp1_basename, sp2_basename) == 0)
440             {
441               const char *target_url;
442               const char *target_base;
443
444               SVN_ERR(svn_client_url_from_path2(&target_url, targetpath, ctx,
445                                                 pool, pool));
446               target_base = svn_uri_basename(target_url, pool);
447
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)
451                 {
452                   svn_node_kind_t kind;
453
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)
461                     {
462                       targetpath = sp1_basename;
463                     }
464                 }
465             }
466         }
467       else if (strcmp(sourcepath1, sourcepath2) == 0)
468         {
469           svn_node_kind_t kind;
470
471           SVN_ERR(svn_io_check_path(sourcepath1, &kind, pool));
472           if (kind == svn_node_file)
473             {
474               targetpath = sourcepath1;
475             }
476         }
477     }
478
479   if (opt_state->extensions)
480     options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
481   else
482     options = NULL;
483
484   /* More input validation. */
485   if (opt_state->reintegrate)
486     {
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"));
491
492       if (opt_state->record_only)
493         return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
494                                 _("--reintegrate cannot be used with "
495                                   "--record-only"));
496
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 "
500                                   "--reintegrate"));
501
502       if (opt_state->force)
503         return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
504                                 _("--force cannot be used with "
505                                   "--reintegrate"));
506
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"));
515     }
516
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)
521     {
522       struct conflict_func_merge_cmd_baton *b = apr_pcalloc(pool, sizeof(*b));
523
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;
527
528       ctx->conflict_func2 = conflict_func_merge_cmd;
529       ctx->conflict_baton2 = b;
530     }
531
532 retry:
533   merge_err = run_merge(two_sources_specified,
534                         sourcepath1, peg_revision1,
535                         sourcepath2,
536                         targetpath,
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)
541     {
542       return svn_error_quick_wrap(
543                merge_err,
544                _("Merge tracking not possible, use --ignore-ancestry or\n"
545                  "fix invalid mergeinfo in target with 'svn propset'"));
546     }
547
548   /* Run the interactive resolver if conflicts were raised. */
549   SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
550                                            pool, pool));
551   if (conflicted_paths)
552     {
553       SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
554                                      opt_state, ctx, pool));
555       if (merge_err &&
556           svn_error_root_cause(merge_err)->apr_err == SVN_ERR_WC_FOUND_CONFLICT)
557         {
558           svn_error_t *err;
559
560           /* Check if all conflicts were resolved just now. */
561           err = svn_cl__conflict_stats_get_paths(&conflicted_paths,
562                                                  conflict_stats, pool, pool);
563           if (err)
564             merge_err = svn_error_compose_create(merge_err, err);
565           else if (conflicted_paths == NULL)
566             {
567               svn_error_clear(merge_err);
568               goto retry; /* ### conflicts resolved; continue merging */
569             }
570         }
571     }
572
573   if (!opt_state->quiet)
574     {
575       svn_error_t *err = svn_cl__notifier_print_conflict_stats(
576                            ctx->notify_baton2, pool);
577
578       merge_err = svn_error_compose_create(merge_err, err);
579     }
580
581   return svn_cl__may_need_force(merge_err);
582 }