switch-cmd.c revision 251881
162587Sitojun/*
262587Sitojun * switch-cmd.c -- Bring work tree in sync with a different URL
362587Sitojun *
453541Sshin * ====================================================================
553541Sshin *    Licensed to the Apache Software Foundation (ASF) under one
653541Sshin *    or more contributor license agreements.  See the NOTICE file
753541Sshin *    distributed with this work for additional information
853541Sshin *    regarding copyright ownership.  The ASF licenses this file
953541Sshin *    to you under the Apache License, Version 2.0 (the
1053541Sshin *    "License"); you may not use this file except in compliance
1153541Sshin *    with the License.  You may obtain a copy of the License at
1253541Sshin *
1353541Sshin *      http://www.apache.org/licenses/LICENSE-2.0
1453541Sshin *
1553541Sshin *    Unless required by applicable law or agreed to in writing,
1653541Sshin *    software distributed under the License is distributed on an
1753541Sshin *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1853541Sshin *    KIND, either express or implied.  See the License for the
1953541Sshin *    specific language governing permissions and limitations
2053541Sshin *    under the License.
2153541Sshin * ====================================================================
2253541Sshin */
2353541Sshin
2453541Sshin/* ==================================================================== */
2553541Sshin
2653541Sshin
2753541Sshin
2853541Sshin/*** Includes. ***/
2953541Sshin
3053541Sshin#include "svn_wc.h"
3153541Sshin#include "svn_client.h"
3253541Sshin#include "svn_dirent_uri.h"
3362587Sitojun#include "svn_path.h"
3462587Sitojun#include "svn_error.h"
3562587Sitojun#include "svn_pools.h"
3653541Sshin#include "cl.h"
3753541Sshin
3853541Sshin#include "svn_private_config.h"
3953541Sshin
4053541Sshin/*** Code. ***/
4153541Sshin
4253541Sshinstatic svn_error_t *
4353541Sshinrewrite_urls(const apr_array_header_t *targets,
4453541Sshin             svn_boolean_t ignore_externals,
4553541Sshin             svn_client_ctx_t *ctx,
4653541Sshin             apr_pool_t *pool)
4753541Sshin{
4853541Sshin  apr_pool_t *subpool;
4953541Sshin  const char *from;
5062587Sitojun  const char *to;
5153541Sshin
5262587Sitojun  if (targets->nelts < 2)
5353541Sshin    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
5453541Sshin
5553541Sshin  from = APR_ARRAY_IDX(targets, 0, const char *);
5653541Sshin  to = APR_ARRAY_IDX(targets, 1, const char *);
5753541Sshin
5853541Sshin  /* "--relocate http https" and "--relocate http://foo svn://bar" are OK,
5953541Sshin     but things like "--relocate http://foo svn" are not */
6053541Sshin  if (svn_path_is_url(from) != svn_path_is_url(to))
6153541Sshin    return svn_error_createf
6253541Sshin      (SVN_ERR_INCORRECT_PARAMS, NULL,
6353541Sshin       _("'%s' to '%s' is not a valid relocation"), from, to);
6453541Sshin
6553541Sshin  subpool = svn_pool_create(pool);
6653541Sshin
6753541Sshin  if (targets->nelts == 2)
6862587Sitojun    {
6953541Sshin      SVN_ERR(svn_client_relocate2("", from, to, ignore_externals,
7053541Sshin                                   ctx, pool));
7162587Sitojun    }
7262587Sitojun  else
7362587Sitojun    {
7462587Sitojun      int i;
7562587Sitojun
7653541Sshin      for (i = 2; i < targets->nelts; i++)
7753541Sshin        {
7862587Sitojun          const char *target = APR_ARRAY_IDX(targets, i, const char *);
7953541Sshin          svn_pool_clear(subpool);
8053541Sshin          SVN_ERR(svn_client_relocate2(target, from, to,
8162587Sitojun                                       ignore_externals, ctx, subpool));
8262587Sitojun        }
8362587Sitojun    }
8462587Sitojun
8562587Sitojun  svn_pool_destroy(subpool);
8653541Sshin  return SVN_NO_ERROR;
8753541Sshin}
8853541Sshin
8953541Sshin
9053541Sshin/* This implements the `svn_opt_subcommand_t' interface. */
9153541Sshinsvn_error_t *
9253541Sshinsvn_cl__switch(apr_getopt_t *os,
9353541Sshin               void *baton,
9453541Sshin               apr_pool_t *scratch_pool)
9553541Sshin{
9653541Sshin  svn_error_t *err = SVN_NO_ERROR;
9753541Sshin  svn_error_t *externals_err = SVN_NO_ERROR;
9853541Sshin  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
9953541Sshin  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
10053541Sshin  apr_array_header_t *targets;
10153541Sshin  const char *target, *switch_url;
10253541Sshin  svn_opt_revision_t peg_revision;
10353541Sshin  svn_depth_t depth;
10453541Sshin  svn_boolean_t depth_is_sticky;
10553541Sshin  struct svn_cl__check_externals_failed_notify_baton nwb;
10653541Sshin
10753541Sshin  /* This command should discover (or derive) exactly two cmdline
10853541Sshin     arguments: a local path to update ("target"), and a new url to
10953541Sshin     switch to ("switch_url"). */
11053541Sshin  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
11153541Sshin                                                      opt_state->targets,
11253541Sshin                                                      ctx, FALSE,
11353541Sshin                                                      scratch_pool));
11453541Sshin
11553541Sshin  /* handle only-rewrite case specially */
11653541Sshin  if (opt_state->relocate)
11753541Sshin    return rewrite_urls(targets, opt_state->ignore_externals,
11853541Sshin                        ctx, scratch_pool);
11953541Sshin
12053541Sshin  if (targets->nelts < 1)
12153541Sshin    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
12253541Sshin  if (targets->nelts > 2)
123    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
124
125  /* Get the required SWITCH_URL and its optional PEG_REVISION, and the
126   * optional TARGET argument. */
127  SVN_ERR(svn_opt_parse_path(&peg_revision, &switch_url,
128                             APR_ARRAY_IDX(targets, 0, const char *),
129                             scratch_pool));
130  if (targets->nelts == 1)
131    target = "";
132  else
133    target = APR_ARRAY_IDX(targets, 1, const char *);
134
135  /* Validate the switch_url */
136  if (! svn_path_is_url(switch_url))
137    return svn_error_createf(SVN_ERR_BAD_URL, NULL,
138                             _("'%s' does not appear to be a URL"), switch_url);
139
140  SVN_ERR(svn_cl__check_target_is_local_path(target));
141
142  /* Deal with depthstuffs. */
143  if (opt_state->set_depth != svn_depth_unknown)
144    {
145      depth = opt_state->set_depth;
146      depth_is_sticky = TRUE;
147    }
148  else
149    {
150      depth = opt_state->depth;
151      depth_is_sticky = FALSE;
152    }
153
154  nwb.wrapped_func = ctx->notify_func2;
155  nwb.wrapped_baton = ctx->notify_baton2;
156  nwb.had_externals_error = FALSE;
157  ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper;
158  ctx->notify_baton2 = &nwb;
159
160  /* Do the 'switch' update. */
161  err = svn_client_switch3(NULL, target, switch_url, &peg_revision,
162                           &(opt_state->start_revision), depth,
163                           depth_is_sticky, opt_state->ignore_externals,
164                           opt_state->force, opt_state->ignore_ancestry,
165                           ctx, scratch_pool);
166  if (err)
167    {
168      if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
169        return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, err,
170                                 _("Path '%s' does not share common version "
171                                   "control ancestry with the requested switch "
172                                   "location.  Use --ignore-ancestry to "
173                                   "disable this check."),
174                                   svn_dirent_local_style(target,
175                                                          scratch_pool));
176      if (err->apr_err == SVN_ERR_RA_UUID_MISMATCH
177          || err->apr_err == SVN_ERR_WC_INVALID_SWITCH)
178        return svn_error_quick_wrap(
179                 err,
180                 _("'svn switch' does not support switching a working copy to "
181                   "a different repository"));
182      return err;
183    }
184
185  if (nwb.had_externals_error)
186    externals_err = svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS,
187                                     NULL,
188                                     _("Failure occurred processing one or "
189                                       "more externals definitions"));
190
191  if (! opt_state->quiet)
192    {
193      err = svn_cl__notifier_print_conflict_stats(nwb.wrapped_baton, scratch_pool);
194      if (err)
195        return svn_error_compose_create(externals_err, err);
196    }
197
198  return svn_error_compose_create(externals_err, err);
199}
200