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 /* ==================================================================== */
25 #include "svn_client.h"
27 #include "svn_error.h"
28 #include "svn_types.h"
31 #include "svn_private_config.h"
37 /* This implements the `svn_opt_subcommand_t' interface. */
39 svn_cl__merge(apr_getopt_t
*os
,
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
;
49 svn_opt_revision_t first_range_start
, first_range_end
, peg_revision1
,
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
,
57 /* For now, we require at least one source. That may change in
58 future versions of Subversion, for example if we have support for
59 negated mergeinfo. See this IRC conversation:
61 <bhuvan> kfogel: yeah, i think you are correct; we should
62 specify the source url
64 <kfogel> bhuvan: I'll change the help output and propose for
67 <bhuvan> kfogel: np; while we are at it, 'svn merge' simply
68 returns nothing; i think we should say: """svn: Not
69 enough arguments provided; try 'svn help' for more
74 <kfogel> (in the future, 'svn merge' might actually do
75 something, but that's all the more reason to make
78 <cmpilato> actually, i'm pretty sure 'svn merge' does something
80 <cmpilato> it says "please merge any unmerged changes from
87 <cmpilato> kfogel: i was serious.
89 <kfogel> cmpilato: urrr, uh. Is that meaningful? Is there
90 ever a reason for a user to run it?
92 <cmpilato> kfogel: not while we don't have support for negated
95 <kfogel> cmpilato: do you concur that until it does something
96 useful it should error?
98 <cmpilato> kfogel: yup.
102 if (targets
->nelts
< 1)
104 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS
, 0,
105 _("Merge source required"));
107 else /* Parse at least one, and possible two, sources. */
109 SVN_ERR(svn_opt_parse_path(&peg_revision1
, &sourcepath1
,
110 APR_ARRAY_IDX(targets
, 0, const char *),
112 if (targets
->nelts
>= 2)
113 SVN_ERR(svn_opt_parse_path(&peg_revision2
, &sourcepath2
,
114 APR_ARRAY_IDX(targets
, 1, const char *),
118 /* We could have one or two sources. Deliberately written to stay
119 correct even if we someday permit implied merge source. */
120 if (targets
->nelts
<= 1)
122 two_sources_specified
= FALSE
;
124 else if (targets
->nelts
== 2)
126 if (svn_path_is_url(sourcepath1
) && !svn_path_is_url(sourcepath2
))
127 two_sources_specified
= FALSE
;
130 if (opt_state
->revision_ranges
->nelts
> 0)
132 first_range_start
= APR_ARRAY_IDX(opt_state
->revision_ranges
, 0,
133 svn_opt_revision_range_t
*)->start
;
134 first_range_end
= APR_ARRAY_IDX(opt_state
->revision_ranges
, 0,
135 svn_opt_revision_range_t
*)->end
;
139 first_range_start
.kind
= first_range_end
.kind
=
140 svn_opt_revision_unspecified
;
143 /* If revision_ranges has at least one real range at this point, then
144 we know the user must have used the '-r' and/or '-c' switch(es).
145 This means we're *not* doing two distinct sources. */
146 if (first_range_start
.kind
!= svn_opt_revision_unspecified
)
148 /* A revision *range* is required. */
149 if (first_range_end
.kind
== svn_opt_revision_unspecified
)
150 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS
, 0,
151 _("Second revision required"));
153 two_sources_specified
= FALSE
;
156 if (! two_sources_specified
) /* TODO: Switch order of if */
158 if (targets
->nelts
> 2)
159 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR
, NULL
,
160 _("Too many arguments given"));
162 /* Set the default value for unspecified paths and peg revision. */
163 if (targets
->nelts
== 0)
165 peg_revision1
.kind
= svn_opt_revision_head
;
169 /* targets->nelts is 1 ("svn merge SOURCE") or 2 ("svn merge
170 SOURCE WCPATH") here. */
171 sourcepath2
= sourcepath1
;
173 if (peg_revision1
.kind
== svn_opt_revision_unspecified
)
174 peg_revision1
.kind
= svn_path_is_url(sourcepath1
)
175 ? svn_opt_revision_head
: svn_opt_revision_working
;
177 if (targets
->nelts
== 2)
179 targetpath
= APR_ARRAY_IDX(targets
, 1, const char *);
180 if (svn_path_is_url(targetpath
))
181 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR
, NULL
,
182 _("Cannot specify a revision range "
187 else /* using @rev syntax */
189 if (targets
->nelts
< 2)
190 return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS
, NULL
, NULL
);
191 if (targets
->nelts
> 3)
192 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR
, NULL
,
193 _("Too many arguments given"));
195 first_range_start
= peg_revision1
;
196 first_range_end
= peg_revision2
;
198 /* Catch 'svn merge wc_path1 wc_path2 [target]' without explicit
199 revisions--since it ignores local modifications it may not do what
200 the user expects. Forcing the user to specify a repository
201 revision should avoid any confusion. */
202 if ((first_range_start
.kind
== svn_opt_revision_unspecified
203 && ! svn_path_is_url(sourcepath1
))
205 (first_range_end
.kind
== svn_opt_revision_unspecified
206 && ! svn_path_is_url(sourcepath2
)))
207 return svn_error_create
208 (SVN_ERR_CLIENT_BAD_REVISION
, 0,
209 _("A working copy merge source needs an explicit revision"));
211 /* Default peg revisions to each URL's youngest revision. */
212 if (first_range_start
.kind
== svn_opt_revision_unspecified
)
213 first_range_start
.kind
= svn_opt_revision_head
;
214 if (first_range_end
.kind
== svn_opt_revision_unspecified
)
215 first_range_end
.kind
= svn_opt_revision_head
;
217 /* Decide where to apply the delta (defaulting to "."). */
218 if (targets
->nelts
== 3)
219 targetpath
= APR_ARRAY_IDX(targets
, 2, const char *);
222 /* If no targetpath was specified, see if we can infer it from the
224 if (sourcepath1
&& sourcepath2
&& strcmp(targetpath
, "") == 0)
226 /* If the sourcepath is a URL, it can only refer to a target in the
227 current working directory.
228 However, if the sourcepath is a local path, it can refer to a target
229 somewhere deeper in the directory structure. */
230 if (svn_path_is_url(sourcepath1
))
232 char *sp1_basename
, *sp2_basename
;
233 sp1_basename
= svn_path_basename(sourcepath1
, pool
);
234 sp2_basename
= svn_path_basename(sourcepath2
, pool
);
236 if (strcmp(sp1_basename
, sp2_basename
) == 0)
238 svn_node_kind_t kind
;
239 const char *decoded_path
= svn_path_uri_decode(sp1_basename
, pool
);
240 SVN_ERR(svn_io_check_path(decoded_path
, &kind
, pool
));
241 if (kind
== svn_node_file
)
243 targetpath
= decoded_path
;
247 else if (strcmp(sourcepath1
, sourcepath2
) == 0)
249 svn_node_kind_t kind
;
250 const char *decoded_path
= svn_path_uri_decode(sourcepath1
, pool
);
251 SVN_ERR(svn_io_check_path(decoded_path
, &kind
, pool
));
252 if (kind
== svn_node_file
)
254 targetpath
= decoded_path
;
259 if (! opt_state
->quiet
)
260 svn_cl__get_notifier(&ctx
->notify_func2
, &ctx
->notify_baton2
, FALSE
,
263 if (opt_state
->extensions
)
264 options
= svn_cstring_split(opt_state
->extensions
, " \t\n\r", TRUE
, pool
);
268 if (! two_sources_specified
) /* TODO: Switch order of if */
270 /* If we don't have a source, use the target as the source. */
272 sourcepath1
= targetpath
;
274 /* If we don't have at least one valid revision range, pick a
275 good one that spans the entire set of revisions on our
277 if ((first_range_start
.kind
== svn_opt_revision_unspecified
)
278 && (first_range_end
.kind
== svn_opt_revision_unspecified
))
280 svn_opt_revision_range_t
*range
= apr_pcalloc(pool
, sizeof(*range
));
281 ranges_to_merge
= apr_array_make(pool
, 1, sizeof(range
));
282 range
->start
.kind
= svn_opt_revision_number
;
283 range
->start
.value
.number
= 1;
284 range
->end
= peg_revision1
;
285 APR_ARRAY_PUSH(ranges_to_merge
, svn_opt_revision_range_t
*) = range
;
288 if (opt_state
->reintegrate
)
290 if (opt_state
->depth
!= svn_depth_unknown
)
291 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS
, NULL
,
292 _("--depth cannot be used with "
295 if (opt_state
->force
)
296 return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS
, NULL
,
297 _("--force cannot be used with "
300 err
= svn_client_merge_reintegrate(sourcepath1
,
307 err
= svn_client_merge_peg3(sourcepath1
,
312 opt_state
->ignore_ancestry
,
314 opt_state
->record_only
,
322 err
= svn_client_merge3(sourcepath1
,
328 opt_state
->ignore_ancestry
,
330 opt_state
->record_only
,
337 if (err
&& (! opt_state
->reintegrate
))
338 return svn_cl__may_need_force(err
);