Add a little more to the svn_rangelist_intersect test to test the
[svn.git] / subversion / svn / merge-cmd.c
blob8d1a4259eda4b3298839dffff768f20eb56bdc0e
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_opt_args_to_target_array2(&targets, os,
54 opt_state->targets, pool));
56 /* Parse at least one, and possible two, sources. */
57 if (targets->nelts >= 1)
59 SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
60 APR_ARRAY_IDX(targets, 0, const char *),
61 pool));
62 if (targets->nelts >= 2)
63 SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
64 APR_ARRAY_IDX(targets, 1, const char *),
65 pool));
68 /* If nothing (ie, "."), a single source, or a source URL plus WC path is
69 provided, then we don't have two distinct sources. */
70 if (targets->nelts <= 1)
72 two_sources_specified = FALSE;
74 else if (targets->nelts == 2)
76 if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
77 two_sources_specified = FALSE;
80 if (opt_state->revision_ranges->nelts > 0)
82 first_range_start = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
83 svn_opt_revision_range_t *)->start;
84 first_range_end = APR_ARRAY_IDX(opt_state->revision_ranges, 0,
85 svn_opt_revision_range_t *)->end;
87 else
89 first_range_start.kind = first_range_end.kind =
90 svn_opt_revision_unspecified;
93 /* If revision_ranges has at least one real range at this point, then
94 we know the user must have used the '-r' and/or '-c' switch(es).
95 This means we're *not* doing two distinct sources. */
96 if (first_range_start.kind != svn_opt_revision_unspecified)
98 /* A revision *range* is required. */
99 if (first_range_end.kind == svn_opt_revision_unspecified)
100 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
101 _("Second revision required"));
103 two_sources_specified = FALSE;
106 if (! two_sources_specified) /* TODO: Switch order of if */
108 if (targets->nelts > 2)
109 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
110 _("Too many arguments given"));
112 /* Set the default value for unspecified paths and peg revision. */
113 if (targets->nelts == 0)
115 peg_revision1.kind = svn_opt_revision_head;
117 else
119 /* targets->nelts is 1 ("svn merge SOURCE") or 2 ("svn merge
120 SOURCE WCPATH") here. */
121 sourcepath2 = sourcepath1;
123 if (peg_revision1.kind == svn_opt_revision_unspecified)
124 peg_revision1.kind = svn_path_is_url(sourcepath1)
125 ? svn_opt_revision_head : svn_opt_revision_working;
127 if (targets->nelts == 2)
129 targetpath = APR_ARRAY_IDX(targets, 1, const char *);
130 if (svn_path_is_url(targetpath))
131 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
132 _("Cannot specify a revision range "
133 "with two URLs"));
137 else /* using @rev syntax */
139 if (targets->nelts < 2)
140 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL, NULL);
141 if (targets->nelts > 3)
142 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
143 _("Too many arguments given"));
145 first_range_start = peg_revision1;
146 first_range_end = peg_revision2;
148 /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit
149 revisions--since it ignores local modifications it may not do what
150 the user expects. Forcing the user to specify a repository
151 revision should avoid any confusion. */
152 if ((first_range_start.kind == svn_opt_revision_unspecified
153 && ! svn_path_is_url(sourcepath1))
155 (first_range_end.kind == svn_opt_revision_unspecified
156 && ! svn_path_is_url(sourcepath2)))
157 return svn_error_create
158 (SVN_ERR_CLIENT_BAD_REVISION, 0,
159 _("A working copy merge source needs an explicit revision"));
161 /* Default peg revisions to each URL's youngest revision. */
162 if (first_range_start.kind == svn_opt_revision_unspecified)
163 first_range_start.kind = svn_opt_revision_head;
164 if (first_range_end.kind == svn_opt_revision_unspecified)
165 first_range_end.kind = svn_opt_revision_head;
167 /* Decide where to apply the delta (defaulting to "."). */
168 if (targets->nelts == 3)
169 targetpath = APR_ARRAY_IDX(targets, 2, const char *);
172 /* If no targetpath was specified, see if we can infer it from the
173 sourcepaths. */
174 if (sourcepath1 && sourcepath2 && strcmp(targetpath, "") == 0)
176 /* If the sourcepath is a URL, it can only refer to a target in the
177 current working directory.
178 However, if the sourcepath is a local path, it can refer to a target
179 somewhere deeper in the directory structure. */
180 if (svn_path_is_url(sourcepath1))
182 char *sp1_basename, *sp2_basename;
183 sp1_basename = svn_path_basename(sourcepath1, pool);
184 sp2_basename = svn_path_basename(sourcepath2, pool);
186 if (strcmp(sp1_basename, sp2_basename) == 0)
188 svn_node_kind_t kind;
189 const char *decoded_path = svn_path_uri_decode(sp1_basename, pool);
190 SVN_ERR(svn_io_check_path(decoded_path, &kind, pool));
191 if (kind == svn_node_file)
193 targetpath = decoded_path;
197 else if (strcmp(sourcepath1, sourcepath2) == 0)
199 svn_node_kind_t kind;
200 const char *decoded_path = svn_path_uri_decode(sourcepath1, pool);
201 SVN_ERR(svn_io_check_path(decoded_path, &kind, pool));
202 if (kind == svn_node_file)
204 targetpath = decoded_path;
209 if (! opt_state->quiet)
210 svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE,
211 FALSE, FALSE, pool);
213 if (opt_state->extensions)
214 options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
215 else
216 options = NULL;
218 if (! two_sources_specified) /* TODO: Switch order of if */
220 /* If we don't have a source, use the target as the source. */
221 if (! sourcepath1)
222 sourcepath1 = targetpath;
224 /* If we don't have at least one valid revision range, pick a
225 good one that spans the entire set of revisions on our
226 source. */
227 if ((first_range_start.kind == svn_opt_revision_unspecified)
228 && (first_range_end.kind == svn_opt_revision_unspecified))
230 svn_opt_revision_range_t *range = apr_pcalloc(pool, sizeof(*range));
231 ranges_to_merge = apr_array_make(pool, 1, sizeof(range));
232 range->start.kind = svn_opt_revision_number;
233 range->start.value.number = 1;
234 range->end.kind = svn_opt_revision_head;
235 APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = range;
238 if (opt_state->reintegrate)
239 err = svn_client_merge_reintegrate(sourcepath1,
240 &peg_revision1,
241 targetpath,
242 opt_state->force,
243 opt_state->dry_run,
244 options, ctx, pool);
245 else
246 err = svn_client_merge_peg3(sourcepath1,
247 ranges_to_merge,
248 &peg_revision1,
249 targetpath,
250 opt_state->depth,
251 opt_state->ignore_ancestry,
252 opt_state->force,
253 opt_state->record_only,
254 opt_state->dry_run,
255 options,
256 ctx,
257 pool);
259 else
261 err = svn_client_merge3(sourcepath1,
262 &first_range_start,
263 sourcepath2,
264 &first_range_end,
265 targetpath,
266 opt_state->depth,
267 opt_state->ignore_ancestry,
268 opt_state->force,
269 opt_state->record_only,
270 opt_state->dry_run,
271 options,
272 ctx,
273 pool);
275 if (err)
276 return svn_cl__may_need_force(err);
278 return SVN_NO_ERROR;