source: valtobtest/subversion-1.6.2/subversion/svn/merge-cmd.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

File size: 12.6 KB
Line 
1/*
2 * merge-cmd.c -- Merging changes into a working copy.
3 *
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet.  All rights reserved.
6 *
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.
12 *
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 * ====================================================================
17 */
18
19/* ==================================================================== */
20
21
22
23/*** Includes. ***/
24
25#include "svn_client.h"
26#include "svn_path.h"
27#include "svn_error.h"
28#include "svn_types.h"
29#include "cl.h"
30
31#include "svn_private_config.h"
32
33
34/*** Code. ***/
35
36
37/* This implements the `svn_opt_subcommand_t' interface. */
38svn_error_t *
39svn_cl__merge(apr_getopt_t *os,
40              void *baton,
41              apr_pool_t *pool)
42{
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;
52
53  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
54                                                      opt_state->targets,
55                                                      ctx, pool));
56
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:
60
61       <bhuvan>   kfogel: yeah, i think you are correct; we should
62                  specify the source url
63
64       <kfogel>   bhuvan: I'll change the help output and propose for
65                  backport.  Thanks.
66
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
70                  info"""
71
72       <kfogel>   good idea
73
74       <kfogel>   (in the future, 'svn merge' might actually do
75                  something, but that's all the more reason to make
76                  sure it errors now)
77
78       <cmpilato> actually, i'm pretty sure 'svn merge' does something
79
80       <cmpilato> it says "please merge any unmerged changes from
81                  myself to myself."
82
83       <cmpilato> :-)
84
85       <kfogel>   har har
86
87       <cmpilato> kfogel: i was serious.
88
89       <kfogel>   cmpilato: urrr, uh.  Is that meaningful?  Is there
90                  ever a reason for a user to run it?
91
92       <cmpilato> kfogel: not while we don't have support for negated
93                  mergeinfo.
94
95       <kfogel>   cmpilato: do you concur that until it does something
96                  useful it should error?
97
98       <cmpilato> kfogel: yup.
99
100       <kfogel>   cool
101  */
102  if (targets->nelts < 1)
103    {
104      return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0,
105                              _("Merge source required"));
106    }
107  else  /* Parse at least one, and possible two, sources. */
108    {
109      SVN_ERR(svn_opt_parse_path(&peg_revision1, &sourcepath1,
110                                 APR_ARRAY_IDX(targets, 0, const char *),
111                                 pool));
112      if (targets->nelts >= 2)
113        SVN_ERR(svn_opt_parse_path(&peg_revision2, &sourcepath2,
114                                   APR_ARRAY_IDX(targets, 1, const char *),
115                                   pool));
116    }
117
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)
121    {
122      two_sources_specified = FALSE;
123    }
124  else if (targets->nelts == 2)
125    {
126      if (svn_path_is_url(sourcepath1) && !svn_path_is_url(sourcepath2))
127        two_sources_specified = FALSE;
128    }
129
130  if (opt_state->revision_ranges->nelts > 0)
131    {
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;
136    }
137  else
138    {
139      first_range_start.kind = first_range_end.kind =
140        svn_opt_revision_unspecified;
141    }
142
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)
147    {
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"));
152
153      two_sources_specified = FALSE;
154    }
155
156  if (! two_sources_specified) /* TODO: Switch order of if */
157    {
158      if (targets->nelts > 2)
159        return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
160                                _("Too many arguments given"));
161
162      /* Set the default value for unspecified paths and peg revision. */
163      if (targets->nelts == 0)
164        {
165          peg_revision1.kind = svn_opt_revision_head;
166        }
167      else
168        {
169          /* targets->nelts is 1 ("svn merge SOURCE") or 2 ("svn merge
170             SOURCE WCPATH") here. */
171          sourcepath2 = sourcepath1;
172
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;
176
177          if (targets->nelts == 2)
178            {
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 "
183                                          "with two URLs"));
184            }
185        }
186    }
187  else /* using @rev syntax */
188    {
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"));
194
195      first_range_start = peg_revision1;
196      first_range_end = peg_revision2;
197
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))
204          ||
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"));
210
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;
216
217      /* Decide where to apply the delta (defaulting to "."). */
218      if (targets->nelts == 3)
219        targetpath = APR_ARRAY_IDX(targets, 2, const char *);
220    }
221
222  /* If no targetpath was specified, see if we can infer it from the
223     sourcepaths. */
224  if (sourcepath1 && sourcepath2 && strcmp(targetpath, "") == 0)
225    {
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))
231        {
232          char *sp1_basename, *sp2_basename;
233          sp1_basename = svn_path_basename(sourcepath1, pool);
234          sp2_basename = svn_path_basename(sourcepath2, pool);
235
236          if (strcmp(sp1_basename, sp2_basename) == 0)
237            {
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)
242                {
243                  targetpath = decoded_path;
244                }
245            }
246        }
247      else if (strcmp(sourcepath1, sourcepath2) == 0)
248        {
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)
253            {
254              targetpath = decoded_path;
255            }
256        }
257    }
258
259  if (! opt_state->quiet)
260    svn_cl__get_notifier(&ctx->notify_func2, &ctx->notify_baton2, FALSE,
261                         FALSE, FALSE, pool);
262
263  if (opt_state->extensions)
264    options = svn_cstring_split(opt_state->extensions, " \t\n\r", TRUE, pool);
265  else
266    options = NULL;
267
268  if (! two_sources_specified) /* TODO: Switch order of if */
269    {
270      /* If we don't have a source, use the target as the source. */
271      if (! sourcepath1)
272        sourcepath1 = targetpath;
273
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
276         source. */
277      if ((first_range_start.kind == svn_opt_revision_unspecified)
278          && (first_range_end.kind == svn_opt_revision_unspecified))
279        {
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;
286        }
287
288      if (opt_state->reintegrate)
289        {
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 "
293                                      "--reintegrate"));
294
295          if (opt_state->force)
296            return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,
297                                    _("--force cannot be used with "
298                                      "--reintegrate"));
299
300          err = svn_client_merge_reintegrate(sourcepath1,
301                                             &peg_revision1,
302                                             targetpath,
303                                             opt_state->dry_run,
304                                             options, ctx, pool);
305        }
306      else
307        err = svn_client_merge_peg3(sourcepath1,
308                                    ranges_to_merge,
309                                    &peg_revision1,
310                                    targetpath,
311                                    opt_state->depth,
312                                    opt_state->ignore_ancestry,
313                                    opt_state->force,
314                                    opt_state->record_only,
315                                    opt_state->dry_run,
316                                    options,
317                                    ctx,
318                                    pool);
319    }
320  else
321    {
322      err = svn_client_merge3(sourcepath1,
323                              &first_range_start,
324                              sourcepath2,
325                              &first_range_end,
326                              targetpath,
327                              opt_state->depth,
328                              opt_state->ignore_ancestry,
329                              opt_state->force,
330                              opt_state->record_only,
331                              opt_state->dry_run,
332                              options,
333                              ctx,
334                              pool);
335    }
336
337  if (err && (! opt_state->reintegrate))
338    return svn_cl__may_need_force(err);
339
340  return err;
341}
Note: See TracBrowser for help on using the repository browser.