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_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 *),
62 if (targets
->nelts
>= 2)
63 SVN_ERR(svn_opt_parse_path(&peg_revision2
, &sourcepath2
,
64 APR_ARRAY_IDX(targets
, 1, const char *),
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
;
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
;
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 "
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
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
,
213 if (opt_state
->extensions
)
214 options
= svn_cstring_split(opt_state
->extensions
, " \t\n\r", TRUE
, pool
);
218 if (! two_sources_specified
) /* TODO: Switch order of if */
220 /* If we don't have a source, use the target as the source. */
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
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
,
246 err
= svn_client_merge_peg3(sourcepath1
,
251 opt_state
->ignore_ancestry
,
253 opt_state
->record_only
,
261 err
= svn_client_merge3(sourcepath1
,
267 opt_state
->ignore_ancestry
,
269 opt_state
->record_only
,
276 return svn_cl__may_need_force(err
);