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 /* 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 *),
63 if (targets
->nelts
>= 2)
64 SVN_ERR(svn_opt_parse_path(&peg_revision2
, &sourcepath2
,
65 APR_ARRAY_IDX(targets
, 1, const char *),
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
;
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
;
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 "
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
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
,
214 if (opt_state
->extensions
)
215 options
= svn_cstring_split(opt_state
->extensions
, " \t\n\r", TRUE
, pool
);
219 if (! two_sources_specified
) /* TODO: Switch order of if */
221 /* If we don't have a source, use the target as the source. */
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
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 "
246 err
= svn_client_merge_reintegrate(sourcepath1
,
253 err
= svn_client_merge_peg3(sourcepath1
,
258 opt_state
->ignore_ancestry
,
260 opt_state
->record_only
,
268 err
= svn_client_merge3(sourcepath1
,
274 opt_state
->ignore_ancestry
,
276 opt_state
->record_only
,
283 return svn_cl__may_need_force(err
);