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