1 /*
2  * switch-cmd.c -- Bring work tree in sync with a different URL
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23 
24 /* ==================================================================== */
25 
26 
27 
28 /*** Includes. ***/
29 
30 #include "svn_wc.h"
31 #include "svn_client.h"
32 #include "svn_dirent_uri.h"
33 #include "svn_path.h"
34 #include "svn_error.h"
35 #include "svn_pools.h"
36 #include "cl.h"
37 
38 #include "svn_private_config.h"
39 
40 /*** Code. ***/
41 
42 static svn_error_t *
rewrite_urls(const apr_array_header_t * targets,svn_boolean_t ignore_externals,svn_client_ctx_t * ctx,apr_pool_t * pool)43 rewrite_urls(const apr_array_header_t *targets,
44              svn_boolean_t ignore_externals,
45              svn_client_ctx_t *ctx,
46              apr_pool_t *pool)
47 {
48   apr_pool_t *subpool;
49   const char *from;
50   const char *to;
51 
52   if (targets->nelts < 2)
53     return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
54 
55   from = APR_ARRAY_IDX(targets, 0, const char *);
56   to = APR_ARRAY_IDX(targets, 1, const char *);
57 
58   /* "--relocate http https" and "--relocate http://foo svn://bar" are OK,
59      but things like "--relocate http://foo svn" are not */
60   if (svn_path_is_url(from) != svn_path_is_url(to))
61     return svn_error_createf
62       (SVN_ERR_INCORRECT_PARAMS, NULL,
63        _("'%s' to '%s' is not a valid relocation"), from, to);
64 
65   subpool = svn_pool_create(pool);
66 
67   if (targets->nelts == 2)
68     {
69       SVN_ERR(svn_client_relocate2("", from, to, ignore_externals,
70                                    ctx, pool));
71     }
72   else
73     {
74       int i;
75 
76       for (i = 2; i < targets->nelts; i++)
77         {
78           const char *target = APR_ARRAY_IDX(targets, i, const char *);
79           svn_pool_clear(subpool);
80           SVN_ERR(svn_client_relocate2(target, from, to,
81                                        ignore_externals, ctx, subpool));
82         }
83     }
84 
85   svn_pool_destroy(subpool);
86   return SVN_NO_ERROR;
87 }
88 
89 
90 /* This implements the `svn_opt_subcommand_t' interface. */
91 svn_error_t *
svn_cl__switch(apr_getopt_t * os,void * baton,apr_pool_t * scratch_pool)92 svn_cl__switch(apr_getopt_t *os,
93                void *baton,
94                apr_pool_t *scratch_pool)
95 {
96   svn_error_t *err = SVN_NO_ERROR;
97   svn_error_t *externals_err = SVN_NO_ERROR;
98   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
99   svn_cl__conflict_stats_t *conflict_stats =
100     ((svn_cl__cmd_baton_t *) baton)->conflict_stats;
101   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
102   apr_array_header_t *targets;
103   apr_array_header_t *conflicted_paths;
104   const char *target, *switch_url;
105   svn_opt_revision_t peg_revision;
106   svn_depth_t depth;
107   svn_boolean_t depth_is_sticky;
108   struct svn_cl__check_externals_failed_notify_baton nwb;
109 
110   /* This command should discover (or derive) exactly two cmdline
111      arguments: a local path to update ("target"), and a new url to
112      switch to ("switch_url"). */
113   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
114                                                       opt_state->targets,
115                                                       ctx, FALSE,
116                                                       scratch_pool));
117 
118   /* handle only-rewrite case specially */
119   if (opt_state->relocate)
120     return rewrite_urls(targets, opt_state->ignore_externals,
121                         ctx, scratch_pool);
122 
123   if (targets->nelts < 1)
124     return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
125   if (targets->nelts > 2)
126     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
127 
128   /* Get the required SWITCH_URL and its optional PEG_REVISION, and the
129    * optional TARGET argument. */
130   SVN_ERR(svn_opt_parse_path(&peg_revision, &switch_url,
131                              APR_ARRAY_IDX(targets, 0, const char *),
132                              scratch_pool));
133   if (targets->nelts == 1)
134     target = "";
135   else
136     target = APR_ARRAY_IDX(targets, 1, const char *);
137 
138   /* Validate the switch_url */
139   if (! svn_path_is_url(switch_url))
140     return svn_error_createf(SVN_ERR_BAD_URL, NULL,
141                              _("'%s' does not appear to be a URL"), switch_url);
142 
143   SVN_ERR(svn_cl__check_target_is_local_path(target));
144 
145   /* Deal with depthstuffs. */
146   if (opt_state->set_depth != svn_depth_unknown)
147     {
148       depth = opt_state->set_depth;
149       depth_is_sticky = TRUE;
150     }
151   else
152     {
153       depth = opt_state->depth;
154       depth_is_sticky = FALSE;
155     }
156 
157   nwb.wrapped_func = ctx->notify_func2;
158   nwb.wrapped_baton = ctx->notify_baton2;
159   nwb.had_externals_error = FALSE;
160   ctx->notify_func2 = svn_cl__check_externals_failed_notify_wrapper;
161   ctx->notify_baton2 = &nwb;
162 
163   /* Do the 'switch' update. */
164   err = svn_client_switch3(NULL, target, switch_url, &peg_revision,
165                            &(opt_state->start_revision), depth,
166                            depth_is_sticky, opt_state->ignore_externals,
167                            opt_state->force, opt_state->ignore_ancestry,
168                            ctx, scratch_pool);
169   if (err)
170     {
171       if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
172         return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, err,
173                                  _("Path '%s' does not share common version "
174                                    "control ancestry with the requested switch "
175                                    "location.  Use --ignore-ancestry to "
176                                    "disable this check."),
177                                    svn_dirent_local_style(target,
178                                                           scratch_pool));
179       if (err->apr_err == SVN_ERR_RA_UUID_MISMATCH
180           || err->apr_err == SVN_ERR_WC_INVALID_SWITCH)
181         return svn_error_quick_wrap(
182                  err,
183                  _("'svn switch' does not support switching a working copy to "
184                    "a different repository"));
185       return err;
186     }
187 
188   if (nwb.had_externals_error)
189     externals_err = svn_error_create(SVN_ERR_CL_ERROR_PROCESSING_EXTERNALS,
190                                      NULL,
191                                      _("Failure occurred processing one or "
192                                        "more externals definitions"));
193 
194   /* Run the interactive resolver if conflicts were raised. */
195   SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
196                                            scratch_pool, scratch_pool));
197   if (conflicted_paths)
198     SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
199                                    opt_state, ctx, scratch_pool));
200 
201   if (! opt_state->quiet)
202     {
203       err = svn_cl__notifier_print_conflict_stats(nwb.wrapped_baton, scratch_pool);
204       if (err)
205         return svn_error_compose_create(externals_err, err);
206     }
207 
208   return svn_error_compose_create(externals_err, err);
209 }
210