]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_client/upgrade.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.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 /* Forward definition. Upgrades svn:externals properties in the working copy
86    LOCAL_ABSPATH to the WC-NG  storage. INFO_BATON will be used to fetch
87    repository info using fetch_repos_info() function if needed.
88  */
89 static svn_error_t *
90 upgrade_externals_from_properties(svn_client_ctx_t *ctx,
91                                   const char *local_abspath,
92                                   struct repos_info_baton *info_baton,
93                                   apr_pool_t *scratch_pool);
94
95 svn_error_t *
96 svn_client_upgrade(const char *path,
97                    svn_client_ctx_t *ctx,
98                    apr_pool_t *scratch_pool)
99 {
100   const char *local_abspath;
101   apr_hash_t *externals;
102   struct repos_info_baton info_baton;
103
104   info_baton.state_pool = scratch_pool;
105   info_baton.ctx = ctx;
106   info_baton.last_repos = NULL;
107   info_baton.last_uuid = NULL;
108
109   if (svn_path_is_url(path))
110     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
111                              _("'%s' is not a local path"), path);
112
113   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
114   SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath,
115                          fetch_repos_info, &info_baton,
116                          ctx->cancel_func, ctx->cancel_baton,
117                          ctx->notify_func2, ctx->notify_baton2,
118                          scratch_pool));
119
120   SVN_ERR(svn_wc__externals_defined_below(&externals,
121                                           ctx->wc_ctx, local_abspath,
122                                           scratch_pool, scratch_pool));
123
124   if (apr_hash_count(externals) > 0)
125     {
126       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
127       apr_hash_index_t *hi;
128
129       /* We are upgrading from >= 1.7. No need to upgrade from
130          svn:externals properties. And by that avoiding the removal
131          of recorded externals information (issue #4519)
132
133          Only directory externals need an explicit upgrade */
134       for (hi = apr_hash_first(scratch_pool, externals);
135            hi;
136            hi = apr_hash_next(hi))
137         {
138           const char *ext_abspath;
139           svn_node_kind_t kind;
140
141           svn_pool_clear(iterpool);
142
143           ext_abspath = apr_hash_this_key(hi);
144
145           SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
146                                              ctx->wc_ctx, local_abspath,
147                                              ext_abspath, FALSE,
148                                              iterpool, iterpool));
149
150           if (kind == svn_node_dir)
151             {
152               svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool);
153
154               if (err)
155                 {
156                   svn_wc_notify_t *notify =
157                             svn_wc_create_notify(ext_abspath,
158                                                  svn_wc_notify_failed_external,
159                                                  iterpool);
160                   notify->err = err;
161                   ctx->notify_func2(ctx->notify_baton2,
162                                     notify, iterpool);
163                   svn_error_clear(err);
164                   /* Next external node, please... */
165                 }
166             }
167         }
168
169       svn_pool_destroy(iterpool);
170     }
171   else
172     {
173       /* Upgrading from <= 1.6, or no svn:properties defined.
174          (There is no way to detect the difference from libsvn_client :( ) */
175
176       SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath,
177                                                 &info_baton, scratch_pool));
178     }
179   return SVN_NO_ERROR;
180 }
181
182 static svn_error_t *
183 upgrade_externals_from_properties(svn_client_ctx_t *ctx,
184                                   const char *local_abspath,
185                                   struct repos_info_baton *info_baton,
186                                   apr_pool_t *scratch_pool)
187 {
188   apr_hash_index_t *hi;
189   apr_pool_t *iterpool;
190   apr_pool_t *iterpool2;
191   apr_hash_t *externals;
192   svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
193
194   /* Now it's time to upgrade the externals too. We do it after the wc
195      upgrade to avoid that errors in the externals causes the wc upgrade to
196      fail. Thanks to caching the performance penalty of walking the wc a
197      second time shouldn't be too severe */
198   SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
199                               local_abspath, &rev, &rev, NULL,
200                               svn_depth_infinity, NULL, ctx,
201                               scratch_pool, scratch_pool));
202
203   iterpool = svn_pool_create(scratch_pool);
204   iterpool2 = svn_pool_create(scratch_pool);
205
206   for (hi = apr_hash_first(scratch_pool, externals); hi;
207        hi = apr_hash_next(hi))
208     {
209       int i;
210       const char *externals_parent_abspath;
211       const char *externals_parent_url;
212       const char *externals_parent_repos_root_url;
213       const char *externals_parent_repos_relpath;
214       const char *externals_parent = apr_hash_this_key(hi);
215       svn_string_t *external_desc = apr_hash_this_val(hi);
216       apr_array_header_t *externals_p;
217       svn_error_t *err;
218
219       svn_pool_clear(iterpool);
220       externals_p = apr_array_make(iterpool, 1,
221                                    sizeof(svn_wc_external_item2_t*));
222
223       /* In this loop, an error causes the respective externals definition, or
224        * the external (inner loop), to be skipped, so that upgrade carries on
225        * with the other externals. */
226
227       err = svn_dirent_get_absolute(&externals_parent_abspath,
228                                     externals_parent, iterpool);
229
230       if (!err)
231         err = svn_wc__node_get_repos_info(NULL,
232                                           &externals_parent_repos_relpath,
233                                           &externals_parent_repos_root_url,
234                                           NULL,
235                                           ctx->wc_ctx,
236                                           externals_parent_abspath,
237                                           iterpool, iterpool);
238
239       if (!err)
240         externals_parent_url = svn_path_url_add_component2(
241                                     externals_parent_repos_root_url,
242                                     externals_parent_repos_relpath,
243                                     iterpool);
244       if (!err)
245         err = svn_wc_parse_externals_description3(
246                   &externals_p, svn_dirent_dirname(local_abspath, iterpool),
247                   external_desc->data, FALSE, iterpool);
248       if (err)
249         {
250           svn_wc_notify_t *notify =
251               svn_wc_create_notify(externals_parent,
252                                    svn_wc_notify_failed_external,
253                                    scratch_pool);
254           notify->err = err;
255
256           ctx->notify_func2(ctx->notify_baton2,
257                             notify, scratch_pool);
258
259           svn_error_clear(err);
260
261           /* Next externals definition, please... */
262           continue;
263         }
264
265       for (i = 0; i < externals_p->nelts; i++)
266         {
267           svn_wc_external_item2_t *item;
268           const char *resolved_url;
269           const char *external_abspath;
270           const char *repos_relpath;
271           const char *repos_root_url;
272           const char *repos_uuid;
273           svn_node_kind_t external_kind;
274           svn_revnum_t peg_revision;
275           svn_revnum_t revision;
276
277           item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
278
279           svn_pool_clear(iterpool2);
280           external_abspath = svn_dirent_join(externals_parent_abspath,
281                                              item->target_dir,
282                                              iterpool2);
283
284           err = svn_wc__resolve_relative_external_url(
285                                               &resolved_url,
286                                               item,
287                                               externals_parent_repos_root_url,
288                                               externals_parent_url,
289                                               scratch_pool, scratch_pool);
290           if (err)
291             goto handle_error;
292
293           /* This is a hack. We only need to call svn_wc_upgrade() on external
294            * dirs, as file externals are upgraded along with their defining
295            * WC.  Reading the kind will throw an exception on an external dir,
296            * saying that the wc must be upgraded.  If it's a file, the lookup
297            * is done in an adm_dir belonging to the defining wc (which has
298            * already been upgraded) and no error is returned.  If it doesn't
299            * exist (external that isn't checked out yet), we'll just get
300            * svn_node_none. */
301           err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
302                                   external_abspath, TRUE, FALSE, iterpool2);
303           if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
304             {
305               svn_error_clear(err);
306
307               err = svn_client_upgrade(external_abspath, ctx, iterpool2);
308               if (err)
309                 goto handle_error;
310             }
311           else if (err)
312             goto handle_error;
313
314           /* The upgrade of any dir should be done now, get the now reliable
315            * kind. */
316           err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
317                                   TRUE, FALSE, iterpool2);
318           if (err)
319             goto handle_error;
320
321           /* Update the EXTERNALS table according to the root URL,
322            * relpath and uuid known in the upgraded external WC. */
323
324           /* We should probably have a function that provides all three
325            * of root URL, repos relpath and uuid at once, but here goes... */
326
327           /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
328            * when the node is not present in the file system.
329            * svn_wc__node_get_repos_info() would try to derive the URL. */
330           err = svn_wc__node_get_repos_info(NULL,
331                                             &repos_relpath,
332                                             &repos_root_url,
333                                             &repos_uuid,
334                                             ctx->wc_ctx,
335                                             external_abspath,
336                                             iterpool2, iterpool2);
337           if (err)
338             goto handle_error;
339
340           /* If we haven't got any information from the checked out external,
341            * or if the URL information mismatches the external's definition,
342            * ask fetch_repos_info() to find out the repos root. */
343           if (0 != strcmp(resolved_url,
344                           svn_path_url_add_component2(repos_root_url,
345                                                       repos_relpath,
346                                                       scratch_pool)))
347             {
348               err = fetch_repos_info(&repos_root_url,
349                                      &repos_uuid,
350                                      info_baton,
351                                      resolved_url,
352                                      scratch_pool, scratch_pool);
353               if (err)
354                 goto handle_error;
355
356               repos_relpath = svn_uri_skip_ancestor(repos_root_url,
357                                                     resolved_url,
358                                                     iterpool2);
359
360               /* There's just the URL, no idea what kind the external is.
361                * That's fine, as the external isn't even checked out yet.
362                * The kind will be set during the next 'update'. */
363               external_kind = svn_node_unknown;
364             }
365
366           if (err)
367             goto handle_error;
368
369           peg_revision = (item->peg_revision.kind == svn_opt_revision_number
370                           ? item->peg_revision.value.number
371                           : SVN_INVALID_REVNUM);
372
373           revision = (item->revision.kind == svn_opt_revision_number
374                       ? item->revision.value.number
375                       : SVN_INVALID_REVNUM);
376
377           err = svn_wc__upgrade_add_external_info(ctx->wc_ctx,
378                                                   external_abspath,
379                                                   external_kind,
380                                                   externals_parent,
381                                                   repos_relpath,
382                                                   repos_root_url,
383                                                   repos_uuid,
384                                                   peg_revision,
385                                                   revision,
386                                                   iterpool2);
387 handle_error:
388           if (err)
389             {
390               svn_wc_notify_t *notify =
391                   svn_wc_create_notify(external_abspath,
392                                        svn_wc_notify_failed_external,
393                                        scratch_pool);
394               notify->err = err;
395               ctx->notify_func2(ctx->notify_baton2,
396                                 notify, scratch_pool);
397               svn_error_clear(err);
398               /* Next external node, please... */
399             }
400         }
401     }
402
403   svn_pool_destroy(iterpool);
404   svn_pool_destroy(iterpool2);
405
406   return SVN_NO_ERROR;
407 }