]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_client/upgrade.c
Update svn-1.9.7 to 1.10.0.
[FreeBSD/FreeBSD.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 /* Helper for upgrade_externals_from_properties: upgrades one external ITEM
183    in EXTERNALS_PARENT. Uses SCRATCH_POOL for temporary allocations. */
184 static svn_error_t *
185 upgrade_external_item(svn_client_ctx_t *ctx,
186                       const char *externals_parent_abspath,
187                       const char *externals_parent_url,
188                       const char *externals_parent_repos_root_url,
189                       svn_wc_external_item2_t *item,
190                       struct repos_info_baton *info_baton,
191                       apr_pool_t *scratch_pool)
192 {
193   const char *resolved_url;
194   const char *external_abspath;
195   const char *repos_relpath;
196   const char *repos_root_url;
197   const char *repos_uuid;
198   svn_node_kind_t external_kind;
199   svn_revnum_t peg_revision;
200   svn_revnum_t revision;
201   svn_error_t *err;
202
203   external_abspath = svn_dirent_join(externals_parent_abspath,
204                                      item->target_dir,
205                                      scratch_pool);
206
207   SVN_ERR(svn_wc__resolve_relative_external_url(
208               &resolved_url,
209               item,
210               externals_parent_repos_root_url,
211               externals_parent_url,
212               scratch_pool, scratch_pool));
213
214   /* This is a hack. We only need to call svn_wc_upgrade() on external
215    * dirs, as file externals are upgraded along with their defining
216    * WC.  Reading the kind will throw an exception on an external dir,
217    * saying that the wc must be upgraded.  If it's a file, the lookup
218    * is done in an adm_dir belonging to the defining wc (which has
219    * already been upgraded) and no error is returned.  If it doesn't
220    * exist (external that isn't checked out yet), we'll just get
221    * svn_node_none. */
222   err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
223                           external_abspath, TRUE, FALSE, scratch_pool);
224   if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
225     {
226       svn_error_clear(err);
227
228       SVN_ERR(svn_client_upgrade(external_abspath, ctx, scratch_pool));
229     }
230   else if (err)
231     return svn_error_trace(err);
232
233   /* The upgrade of any dir should be done now, get the now reliable
234    * kind. */
235   SVN_ERR(svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
236                             TRUE, FALSE, scratch_pool));
237
238   /* Update the EXTERNALS table according to the root URL,
239    * relpath and uuid known in the upgraded external WC. */
240
241   /* We should probably have a function that provides all three
242    * of root URL, repos relpath and uuid at once, but here goes... */
243
244   /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
245    * when the node is not present in the file system.
246    * svn_wc__node_get_repos_info() would try to derive the URL. */
247   SVN_ERR(svn_wc__node_get_repos_info(NULL,
248                                       &repos_relpath,
249                                       &repos_root_url,
250                                       &repos_uuid,
251                                       ctx->wc_ctx,
252                                       external_abspath,
253                                       scratch_pool, scratch_pool));
254
255   /* If we haven't got any information from the checked out external,
256    * or if the URL information mismatches the external's definition,
257    * ask fetch_repos_info() to find out the repos root. */
258   if (0 != strcmp(resolved_url,
259                   svn_path_url_add_component2(repos_root_url,
260                                               repos_relpath,
261                                               scratch_pool)))
262     {
263       SVN_ERR(fetch_repos_info(&repos_root_url, &repos_uuid, info_baton,
264                                resolved_url, scratch_pool, scratch_pool));
265
266       repos_relpath = svn_uri_skip_ancestor(repos_root_url,
267                                             resolved_url,
268                                             scratch_pool);
269
270       /* There's just the URL, no idea what kind the external is.
271        * That's fine, as the external isn't even checked out yet.
272        * The kind will be set during the next 'update'. */
273       external_kind = svn_node_unknown;
274     }
275
276   peg_revision = (item->peg_revision.kind == svn_opt_revision_number
277                      ? item->peg_revision.value.number
278                      : SVN_INVALID_REVNUM);
279
280   revision = (item->revision.kind == svn_opt_revision_number
281                  ? item->revision.value.number
282                  : SVN_INVALID_REVNUM);
283
284   SVN_ERR(svn_wc__upgrade_add_external_info(ctx->wc_ctx,
285                                             external_abspath,
286                                             external_kind,
287                                             externals_parent_abspath,
288                                             repos_relpath,
289                                             repos_root_url,
290                                             repos_uuid,
291                                             peg_revision,
292                                             revision,
293                                             scratch_pool));
294
295   return SVN_NO_ERROR;
296 }
297
298 static svn_error_t *
299 upgrade_externals_from_properties(svn_client_ctx_t *ctx,
300                                   const char *local_abspath,
301                                   struct repos_info_baton *info_baton,
302                                   apr_pool_t *scratch_pool)
303 {
304   apr_hash_index_t *hi;
305   apr_pool_t *iterpool;
306   apr_pool_t *iterpool2;
307   apr_hash_t *externals;
308   svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
309
310   /* Now it's time to upgrade the externals too. We do it after the wc
311      upgrade to avoid that errors in the externals causes the wc upgrade to
312      fail. Thanks to caching the performance penalty of walking the wc a
313      second time shouldn't be too severe */
314   SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
315                               local_abspath, &rev, &rev, NULL,
316                               svn_depth_infinity, NULL, ctx,
317                               scratch_pool, scratch_pool));
318
319   iterpool = svn_pool_create(scratch_pool);
320   iterpool2 = svn_pool_create(scratch_pool);
321
322   for (hi = apr_hash_first(scratch_pool, externals); hi;
323        hi = apr_hash_next(hi))
324     {
325       int i;
326       const char *externals_parent_url;
327       const char *externals_parent_repos_root_url;
328       const char *externals_parent_repos_relpath;
329       const char *externals_parent_abspath = apr_hash_this_key(hi);
330       svn_string_t *external_desc = apr_hash_this_val(hi);
331       apr_array_header_t *externals_p;
332       svn_error_t *err;
333
334       svn_pool_clear(iterpool);
335
336       /* svn_client_propget5() has API promise to return absolute paths. */
337       SVN_ERR_ASSERT(svn_dirent_is_absolute(externals_parent_abspath));
338
339       externals_p = apr_array_make(iterpool, 1,
340                                    sizeof(svn_wc_external_item2_t*));
341
342       /* In this loop, an error causes the respective externals definition, or
343        * the external (inner loop), to be skipped, so that upgrade carries on
344        * with the other externals. */
345       err = svn_wc__node_get_repos_info(NULL,
346                                         &externals_parent_repos_relpath,
347                                         &externals_parent_repos_root_url,
348                                         NULL,
349                                         ctx->wc_ctx,
350                                         externals_parent_abspath,
351                                         iterpool, iterpool);
352
353       if (!err)
354         externals_parent_url = svn_path_url_add_component2(
355                                     externals_parent_repos_root_url,
356                                     externals_parent_repos_relpath,
357                                     iterpool);
358       if (!err)
359         err = svn_wc_parse_externals_description3(
360                   &externals_p, svn_dirent_dirname(local_abspath, iterpool),
361                   external_desc->data, FALSE, iterpool);
362       if (err)
363         {
364           svn_wc_notify_t *notify =
365               svn_wc_create_notify(externals_parent_abspath,
366                                    svn_wc_notify_failed_external,
367                                    scratch_pool);
368           notify->err = err;
369
370           ctx->notify_func2(ctx->notify_baton2,
371                             notify, scratch_pool);
372
373           svn_error_clear(err);
374
375           /* Next externals definition, please... */
376           continue;
377         }
378
379       for (i = 0; i < externals_p->nelts; i++)
380         {
381           svn_wc_external_item2_t *item;
382
383           item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
384
385           svn_pool_clear(iterpool2);
386           err = upgrade_external_item(ctx, externals_parent_abspath,
387                                       externals_parent_url,
388                                       externals_parent_repos_root_url,
389                                       item, info_baton, iterpool2);
390
391           if (err)
392             {
393               svn_wc_notify_t *notify =
394                   svn_wc_create_notify(svn_dirent_join(externals_parent_abspath,
395                                                        item->target_dir,
396                                                        iterpool2),
397                                        svn_wc_notify_failed_external,
398                                        scratch_pool);
399               notify->err = err;
400               ctx->notify_func2(ctx->notify_baton2,
401                                 notify, scratch_pool);
402               svn_error_clear(err);
403               /* Next external node, please... */
404             }
405         }
406     }
407
408   svn_pool_destroy(iterpool);
409   svn_pool_destroy(iterpool2);
410
411   return SVN_NO_ERROR;
412 }