]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_client/upgrade.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_client / upgrade.c
1 /*
2  * upgrade.c:  wrapper around wc upgrade functionality.
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 \f
28 /*** Includes. ***/
29
30 #include "svn_time.h"
31 #include "svn_wc.h"
32 #include "svn_client.h"
33 #include "svn_config.h"
34 #include "svn_dirent_uri.h"
35 #include "svn_path.h"
36 #include "svn_pools.h"
37 #include "client.h"
38 #include "svn_props.h"
39
40 #include "svn_private_config.h"
41 #include "private/svn_wc_private.h"
42
43 \f
44 /*** Code. ***/
45
46 /* callback baton for fetch_repos_info */
47 struct repos_info_baton
48 {
49   apr_pool_t *state_pool;
50   svn_client_ctx_t *ctx;
51   const char *last_repos;
52   const char *last_uuid;
53 };
54
55 /* svn_wc_upgrade_get_repos_info_t implementation for calling
56    svn_wc_upgrade() from svn_client_upgrade() */
57 static svn_error_t *
58 fetch_repos_info(const char **repos_root,
59                  const char **repos_uuid,
60                  void *baton,
61                  const char *url,
62                  apr_pool_t *result_pool,
63                  apr_pool_t *scratch_pool)
64 {
65   struct repos_info_baton *ri = baton;
66
67   /* The same info is likely to retrieved multiple times (e.g. externals) */
68   if (ri->last_repos && svn_uri__is_ancestor(ri->last_repos, url))
69     {
70       *repos_root = apr_pstrdup(result_pool, ri->last_repos);
71       *repos_uuid = apr_pstrdup(result_pool, ri->last_uuid);
72       return SVN_NO_ERROR;
73     }
74
75   SVN_ERR(svn_client_get_repos_root(repos_root, repos_uuid, url, ri->ctx,
76                                     result_pool, scratch_pool));
77
78   /* Store data for further calls */
79   ri->last_repos = apr_pstrdup(ri->state_pool, *repos_root);
80   ri->last_uuid = apr_pstrdup(ri->state_pool, *repos_uuid);
81
82   return SVN_NO_ERROR;
83 }
84
85 svn_error_t *
86 svn_client_upgrade(const char *path,
87                    svn_client_ctx_t *ctx,
88                    apr_pool_t *scratch_pool)
89 {
90   const char *local_abspath;
91   apr_hash_t *externals;
92   apr_hash_index_t *hi;
93   apr_pool_t *iterpool;
94   apr_pool_t *iterpool2;
95   svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
96   struct repos_info_baton info_baton;
97
98   info_baton.state_pool = scratch_pool;
99   info_baton.ctx = ctx;
100   info_baton.last_repos = NULL;
101   info_baton.last_uuid = NULL;
102
103   if (svn_path_is_url(path))
104     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
105                              _("'%s' is not a local path"), path);
106
107   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
108   SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath,
109                          fetch_repos_info, &info_baton,
110                          ctx->cancel_func, ctx->cancel_baton,
111                          ctx->notify_func2, ctx->notify_baton2,
112                          scratch_pool));
113
114   /* Now it's time to upgrade the externals too. We do it after the wc
115      upgrade to avoid that errors in the externals causes the wc upgrade to
116      fail. Thanks to caching the performance penalty of walking the wc a
117      second time shouldn't be too severe */
118   SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
119                               local_abspath, &rev, &rev, NULL,
120                               svn_depth_infinity, NULL, ctx,
121                               scratch_pool, scratch_pool));
122
123   iterpool = svn_pool_create(scratch_pool);
124   iterpool2 = svn_pool_create(scratch_pool);
125
126   for (hi = apr_hash_first(scratch_pool, externals); hi;
127        hi = apr_hash_next(hi))
128     {
129       int i;
130       const char *externals_parent_abspath;
131       const char *externals_parent_url;
132       const char *externals_parent_repos_root_url;
133       const char *externals_parent_repos_relpath;
134       const char *externals_parent = svn__apr_hash_index_key(hi);
135       svn_string_t *external_desc = svn__apr_hash_index_val(hi);
136       apr_array_header_t *externals_p;
137       svn_error_t *err;
138
139       svn_pool_clear(iterpool);
140       externals_p = apr_array_make(iterpool, 1,
141                                    sizeof(svn_wc_external_item2_t*));
142
143       /* In this loop, an error causes the respective externals definition, or
144        * the external (inner loop), to be skipped, so that upgrade carries on
145        * with the other externals. */
146
147       err = svn_dirent_get_absolute(&externals_parent_abspath,
148                                     externals_parent, iterpool);
149
150       if (!err)
151         err = svn_wc__node_get_repos_info(NULL,
152                                           &externals_parent_repos_relpath,
153                                           &externals_parent_repos_root_url,
154                                           NULL,
155                                           ctx->wc_ctx,
156                                           externals_parent_abspath,
157                                           iterpool, iterpool);
158
159       if (!err)
160         externals_parent_url = svn_path_url_add_component2(
161                                     externals_parent_repos_root_url,
162                                     externals_parent_repos_relpath,
163                                     iterpool);
164       if (!err)
165         err = svn_wc_parse_externals_description3(
166                   &externals_p, svn_dirent_dirname(path, iterpool),
167                   external_desc->data, FALSE, iterpool);
168       if (err)
169         {
170           svn_wc_notify_t *notify =
171               svn_wc_create_notify(externals_parent,
172                                    svn_wc_notify_failed_external,
173                                    scratch_pool);
174           notify->err = err;
175
176           ctx->notify_func2(ctx->notify_baton2,
177                             notify, scratch_pool);
178
179           svn_error_clear(err);
180
181           /* Next externals definition, please... */
182           continue;
183         }
184
185       for (i = 0; i < externals_p->nelts; i++)
186         {
187           svn_wc_external_item2_t *item;
188           const char *resolved_url;
189           const char *external_abspath;
190           const char *repos_relpath;
191           const char *repos_root_url;
192           const char *repos_uuid;
193           svn_node_kind_t external_kind;
194           svn_revnum_t peg_revision;
195           svn_revnum_t revision;
196
197           item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
198
199           svn_pool_clear(iterpool2);
200           external_abspath = svn_dirent_join(externals_parent_abspath,
201                                              item->target_dir,
202                                              iterpool2);
203
204           err = svn_wc__resolve_relative_external_url(
205                                               &resolved_url,
206                                               item,
207                                               externals_parent_repos_root_url,
208                                               externals_parent_url,
209                                               scratch_pool, scratch_pool);
210           if (err)
211             goto handle_error;
212
213           /* This is a hack. We only need to call svn_wc_upgrade() on external
214            * dirs, as file externals are upgraded along with their defining
215            * WC.  Reading the kind will throw an exception on an external dir,
216            * saying that the wc must be upgraded.  If it's a file, the lookup
217            * is done in an adm_dir belonging to the defining wc (which has
218            * already been upgraded) and no error is returned.  If it doesn't
219            * exist (external that isn't checked out yet), we'll just get
220            * svn_node_none. */
221           err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
222                                   external_abspath, TRUE, FALSE, iterpool2);
223           if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
224             {
225               svn_error_clear(err);
226
227               err = svn_client_upgrade(external_abspath, ctx, iterpool2);
228               if (err)
229                 goto handle_error;
230             }
231           else if (err)
232             goto handle_error;
233
234           /* The upgrade of any dir should be done now, get the now reliable
235            * kind. */
236           err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
237                                   TRUE, FALSE, iterpool2);
238           if (err)
239             goto handle_error;
240
241           /* Update the EXTERNALS table according to the root URL,
242            * relpath and uuid known in the upgraded external WC. */
243
244           /* We should probably have a function that provides all three
245            * of root URL, repos relpath and uuid at once, but here goes... */
246
247           /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
248            * when the node is not present in the file system.
249            * svn_wc__node_get_repos_info() would try to derive the URL. */
250           err = svn_wc__node_get_repos_info(NULL,
251                                             &repos_relpath,
252                                             &repos_root_url,
253                                             &repos_uuid,
254                                             ctx->wc_ctx,
255                                             external_abspath,
256                                             iterpool2, iterpool2);
257           if (err)
258             goto handle_error;
259
260           /* If we haven't got any information from the checked out external,
261            * or if the URL information mismatches the external's definition,
262            * ask fetch_repos_info() to find out the repos root. */
263           if (0 != strcmp(resolved_url,
264                           svn_path_url_add_component2(repos_root_url,
265                                                       repos_relpath,
266                                                       scratch_pool)))
267             {
268               err = fetch_repos_info(&repos_root_url,
269                                      &repos_uuid,
270                                      &info_baton,
271                                      resolved_url,
272                                      scratch_pool, scratch_pool);
273               if (err)
274                 goto handle_error;
275
276               repos_relpath = svn_uri_skip_ancestor(repos_root_url,
277                                                     resolved_url,
278                                                     iterpool2);
279
280               /* There's just the URL, no idea what kind the external is.
281                * That's fine, as the external isn't even checked out yet.
282                * The kind will be set during the next 'update'. */
283               external_kind = svn_node_unknown;
284             }
285
286           if (err)
287             goto handle_error;
288
289           peg_revision = (item->peg_revision.kind == svn_opt_revision_number
290                           ? item->peg_revision.value.number
291                           : SVN_INVALID_REVNUM);
292
293           revision = (item->revision.kind == svn_opt_revision_number
294                       ? item->revision.value.number
295                       : SVN_INVALID_REVNUM);
296
297           err = svn_wc__upgrade_add_external_info(ctx->wc_ctx,
298                                                   external_abspath,
299                                                   external_kind,
300                                                   externals_parent,
301                                                   repos_relpath,
302                                                   repos_root_url,
303                                                   repos_uuid,
304                                                   peg_revision,
305                                                   revision,
306                                                   iterpool2);
307 handle_error:
308           if (err)
309             {
310               svn_wc_notify_t *notify =
311                   svn_wc_create_notify(external_abspath,
312                                        svn_wc_notify_failed_external,
313                                        scratch_pool);
314               notify->err = err;
315               ctx->notify_func2(ctx->notify_baton2,
316                                 notify, scratch_pool);
317               svn_error_clear(err);
318               /* Next external node, please... */
319             }
320         }
321     }
322
323   svn_pool_destroy(iterpool);
324   svn_pool_destroy(iterpool2);
325
326   return SVN_NO_ERROR;
327 }