Followup to r29625: fix getopt tests.
[svn.git] / subversion / svn / merge-cmd.c
blob57af62b6f02ecc3c76ae407fcbce72c9b9fb28a0
1 /*
2 * merge-cmd.c -- Merging changes into a working copy.
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes. ***/
25 #include "svn_client.h"
26 #include "svn_path.h"
27 #include "svn_error.h"
28 #include "svn_types.h"
29 #include "cl.h"
31 #include "svn_private_config.h"
34 /*** Code. ***/
37 /* This implements the `svn_opt_subcommand_t' interface. */
38 svn_error_t *
39 svn_cl__merge(apr_getopt_t *os,
40 void *baton,
41 apr_pool_t *pool)
43 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
44 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
45 apr_array_header_t *targets;
46 const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = "";
47 svn_boolean_t two_sources_specified = TRUE;
48 svn_error_t *err;
49 svn_opt_revision_t first_range_start, first_range_end, peg_revision1,
50 peg_revision2;
51 apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
53 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
54 opt_state->targets,
55 pool));
57 /* Parse at least one, and possible two, sources. */
58 if (targets->nelts >= 1)
60 SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
61 APR_ARRAY_IDX(targets, 0, const char *),
62 pool));
63 if (targets->nelts >= 2)
64 SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
65 APR_ARRAY_IDX(targets, 1, const char *),
66 pool));
69 /* If nothing (ie, "."), a single source, or a source URL plus WC path is
70 provided, then we don't have two distinct sources. */
71 if (targets->nelts <= 1)
73 two_sources_specified = FALSE;
75 else if (targets->nelts == 2)
77 if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
78 two_sources_specified = FALSE;
81 if (opt_state->revision_ranges->nelts > 0)
83 first_range_start = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
84 svn_opt_revision_range_t *)->start;
85 first_range_end = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
86 svn_opt_revision_range_t *)->end;
88 else
90 first_range_start.kind = first_range_end.kind =
91 svn_opt_revision_unspecified;
94 /* If revision_ranges has at least one real range at this point, then
95 we know the user must have used the '-r' and/or '-c' switch(es).
96 This means we're *not* doing two distinct sources. */
97 if (first_range_start.kind != svn_opt_revision_unspecified)
99 /* A revision *range* is required. */
100 if (first_range_end.kind == svn_opt_revision_unspecified)
101 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
102 _("Second revision required"));
104 two_sources_specified = FALSE;
107 if (! two_sources_specified) /* TODO: Switch order of if */
109 if (targets->nelts > 2)
110 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
111 _("Too many arguments given"));
113 /* Set the default value for unspecified paths and peg revision. */
114 if (targets->nelts == 0)
116 peg_revision1.kind = svn_opt_revision_head;
118 else
120 /* targets->nelts is 1 ("svn merge SOURCE") or 2 ("svn merge
121 SOURCE WCPATH") here. */
122 sourcepath2 = sourcepath1;
124 if (peg_revision1.kind == svn_opt_revision_unspecified)
125 peg_revision1.kind = svn_path_is_url(sourcepath1)
126 ? svn_opt_revision_head : svn_opt_revision_working;
128 if (targets->nelts == 2)
130 targetpath = APR_ARRAY_IDX(targets, 1, const char *);
131 if (svn_path_is_url(targetpath))
132 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
133 _("Cannot specify a revision range "
134 "with two URLs"));
138 else /* using @rev syntax */
140 if (targets->nelts < 2)
141 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
142 if (targets->nelts > 3)
143 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
144 _("Too many arguments given"));
146 first_range_start = peg_revision1;
147 first_range_end = peg_revision2;
149 /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit
150 revisions--since it ignores local modifications it may not do what
151 the user expects. Forcing the user to specify a repository
152 revision should avoid any confusion. */
153 if ((first_range_start.kind == svn_opt_revision_unspecified
154 && ! svn_path_is_url(sourcepath1))
156 (first_range_end.kind == svn_opt_revision_unspecified
157 && ! svn_path_is_url(sourcepath2)))
158 return svn_error_create
159 (SVN_ERR_CLIENT_BAD_REVISION, 0,
160 _("A working copy merge source needs an explicit revision"));
162 /* Default peg revisions to each URL's youngest revision. */
163 if (first_range_start.kind == svn_opt_revision_unspecified)
164 first_range_start.kind = svn_opt_revision_head;
165 if (first_range_end.kind == svn_opt_revision_unspecified)
166 first_range_end.kind = svn_opt_revision_head;
168 /* Decide where to apply the delta (defaulting to "."). */
169 if (targets->nelts == 3)
170 targetpath = APR_ARRAY_IDX(targets, 2, const char *);
173 /* If no targetpath was specified, see if we can infer it from the
174 sourcepaths. */
175 if (sourcepath1 && sourcepath2 && strcmp(targetpath, "") == 0)
177 /* If the sourcepath is a URL, it can only refer to a target in the
178 current working directory.
179 However, if the sourcepath is a local path, it can refer to a target
180 somewhere deeper in the directory structure. */
181 if (svn_path_is_url(sourcepath1))
183 char *sp1_basename, *sp2_basename;
184 sp1_basename = svn_path_basename(sourcepath1, pool);
185 sp2_basename = svn_path_basename(sourcepath2, pool);
187 if (strcmp(sp1_basename, sp2_basename) == 0)
189 svn_node_kind_t kind;
190 const char *decoded_path = svn_path_uri_decode(sp1_basename, pool);
191 SVN_ERR(svn_io_check_path(decoded_path, &kind, pool));
192 if (kind == svn_node_file)
194 targetpath = decoded_path;
198 else if (strcmp(sourcepath1, sourcepath2) == 0)
200 svn_node_kind_t kind;
201 const char *decoded_path = svn_path_uri_decode(sourcepath1, pool);
202 SVN_ERR(svn_io_check_path(decoded_path, &kind, pool));
203 if (kind == svn_node_file)
205 targetpath = decoded_path;
210 if (! opt_state->quiet)
211 svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE,
212 FALSE, FALSE, pool);
214 if (opt_state->extensions)
215 options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
216 else
217 options = NULL;
219 if (! two_sources_specified) /* TODO: Switch order of if */
221 /* If we don't have a source, use the target as the source. */
222 if (! sourcepath1)
223 sourcepath1 = targetpath;
225 /* If we don't have at least one valid revision range, pick a
226 good one that spans the entire set of revisions on our
227 source. */
228 if ((first_range_start.kind == svn_opt_revision_unspecified)
229 && (first_range_end.kind == svn_opt_revision_unspecified))
231 svn_opt_revision_range_t *range = apr_pcalloc(pool, sizeof(*range));
232 ranges_to_merge = apr_array_make(pool, 1, sizeof(range));
233 range->start.kind = svn_opt_revision_number;
234 range->start.value.number = 1;
235 range->end.kind = svn_opt_revision_head;
236 APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = range;
239 if (opt_state->reintegrate)
241 if (opt_state->force)
242 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
243 _("--force cannot be used with "
244 "--reintegrate"));
246 err = svn_client_merge_reintegrate(sourcepath1,
247 &peg_revision1,
248 targetpath,
249 opt_state->dry_run,
250 options, ctx, pool);
252 else
253 err = svn_client_merge_peg3(sourcepath1,
254 ranges_to_merge,
255 &peg_revision1,
256 targetpath,
257 opt_state->depth,
258 opt_state->ignore_ancestry,
259 opt_state->force,
260 opt_state->record_only,
261 opt_state->dry_run,
262 options,
263 ctx,
264 pool);
266 else
268 err = svn_client_merge3(sourcepath1,
269 &first_range_start,
270 sourcepath2,
271 &first_range_end,
272 targetpath,
273 opt_state->depth,
274 opt_state->ignore_ancestry,
275 opt_state->force,
276 opt_state->record_only,
277 opt_state->dry_run,
278 options,
279 ctx,
280 pool);
282 if (err)
283 return svn_cl__may_need_force(err);
285 return SVN_NO_ERROR;