1 /*
2 * checkout.c: wrappers around wc checkout 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
28 /*** Includes. ***/
29
30 #include "svn_pools.h"
31 #include "svn_wc.h"
32 #include "svn_client.h"
33 #include "svn_ra.h"
34 #include "svn_types.h"
35 #include "svn_error.h"
36 #include "svn_dirent_uri.h"
37 #include "svn_path.h"
38 #include "svn_io.h"
39 #include "svn_opt.h"
40 #include "svn_time.h"
41 #include "client.h"
42
43 #include "private/svn_wc_private.h"
44
45 #include "svn_private_config.h"
46
47
48 /*** Public Interfaces. ***/
49
50 static svn_error_t *
initialize_area(const char * local_abspath,const svn_client__pathrev_t * pathrev,svn_depth_t depth,svn_client_ctx_t * ctx,apr_pool_t * pool)51 initialize_area(const char *local_abspath,
52 const svn_client__pathrev_t *pathrev,
53 svn_depth_t depth,
54 svn_client_ctx_t *ctx,
55 apr_pool_t *pool)
56 {
57 if (depth == svn_depth_unknown)
58 depth = svn_depth_infinity;
59
60 /* Make the unversioned directory into a versioned one. */
61 SVN_ERR(svn_wc_ensure_adm4(ctx->wc_ctx, local_abspath, pathrev->url,
62 pathrev->repos_root_url, pathrev->repos_uuid,
63 pathrev->rev, depth, pool));
64 return SVN_NO_ERROR;
65 }
66
67
68 svn_error_t *
svn_client__checkout_internal(svn_revnum_t * result_rev,svn_boolean_t * timestamp_sleep,const char * url,const char * local_abspath,const svn_opt_revision_t * peg_revision,const svn_opt_revision_t * revision,svn_depth_t depth,svn_boolean_t ignore_externals,svn_boolean_t allow_unver_obstructions,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)69 svn_client__checkout_internal(svn_revnum_t *result_rev,
70 svn_boolean_t *timestamp_sleep,
71 const char *url,
72 const char *local_abspath,
73 const svn_opt_revision_t *peg_revision,
74 const svn_opt_revision_t *revision,
75 svn_depth_t depth,
76 svn_boolean_t ignore_externals,
77 svn_boolean_t allow_unver_obstructions,
78 svn_ra_session_t *ra_session,
79 svn_client_ctx_t *ctx,
80 apr_pool_t *scratch_pool)
81 {
82 svn_node_kind_t kind;
83 svn_client__pathrev_t *pathrev;
84 svn_opt_revision_t resolved_rev = { svn_opt_revision_number };
85
86 /* Sanity check. Without these, the checkout is meaningless. */
87 SVN_ERR_ASSERT(local_abspath != NULL);
88 SVN_ERR_ASSERT(svn_uri_is_canonical(url, scratch_pool));
89 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
90
91 /* Fulfill the docstring promise of svn_client_checkout: */
92 if ((revision->kind != svn_opt_revision_number)
93 && (revision->kind != svn_opt_revision_date)
94 && (revision->kind != svn_opt_revision_head))
95 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
96
97 /* Get the RA connection, if needed. */
98 if (ra_session)
99 {
100 svn_error_t *err = svn_ra_reparent(ra_session, url, scratch_pool);
101
102 if (err)
103 {
104 if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
105 {
106 svn_error_clear(err);
107 ra_session = NULL;
108 }
109 else
110 return svn_error_trace(err);
111 }
112 else
113 {
114 SVN_ERR(svn_client__resolve_rev_and_url(&pathrev,
115 ra_session, url,
116 peg_revision, revision,
117 ctx, scratch_pool));
118 }
119 }
120
121 if (!ra_session)
122 {
123 SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev,
124 url, NULL, peg_revision,
125 revision, ctx, scratch_pool));
126 }
127
128 SVN_ERR(svn_ra_check_path(ra_session, "", pathrev->rev, &kind, scratch_pool));
129 resolved_rev.value.number = pathrev->rev;
130
131 if (kind == svn_node_none)
132 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
133 _("URL '%s' doesn't exist"), pathrev->url);
134 else if (kind == svn_node_file)
135 return svn_error_createf
136 (SVN_ERR_UNSUPPORTED_FEATURE , NULL,
137 _("URL '%s' refers to a file, not a directory"), pathrev->url);
138
139 SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
140
141 if (kind == svn_node_none)
142 {
143 /* Bootstrap: create an incomplete working-copy root dir. Its
144 entries file should only have an entry for THIS_DIR with a
145 URL, revnum, and an 'incomplete' flag. */
146 SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool));
147 SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx,
148 scratch_pool));
149 }
150 else if (kind == svn_node_dir)
151 {
152 int wc_format;
153 const char *entry_url;
154
155 SVN_ERR(svn_wc_check_wc2(&wc_format, ctx->wc_ctx, local_abspath,
156 scratch_pool));
157
158 if (! wc_format)
159 {
160 SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx,
161 scratch_pool));
162 }
163 else
164 {
165 /* Get PATH's URL. */
166 SVN_ERR(svn_wc__node_get_url(&entry_url, ctx->wc_ctx, local_abspath,
167 scratch_pool, scratch_pool));
168
169 /* If PATH's existing URL matches the incoming one, then
170 just update. This allows 'svn co' to restart an
171 interrupted checkout. Otherwise bail out. */
172 if (strcmp(entry_url, pathrev->url) != 0)
173 return svn_error_createf(
174 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
175 _("'%s' is already a working copy for a"
176 " different URL"),
177 svn_dirent_local_style(local_abspath, scratch_pool));
178 }
179 }
180 else
181 {
182 return svn_error_createf(SVN_ERR_WC_NODE_KIND_CHANGE, NULL,
183 _("'%s' already exists and is not a directory"),
184 svn_dirent_local_style(local_abspath,
185 scratch_pool));
186 }
187
188 /* Have update fix the incompleteness. */
189 SVN_ERR(svn_client__update_internal(result_rev, timestamp_sleep,
190 local_abspath, &resolved_rev, depth,
191 TRUE, ignore_externals,
192 allow_unver_obstructions,
193 TRUE /* adds_as_modification */,
194 FALSE, FALSE, ra_session,
195 ctx, scratch_pool));
196
197 return SVN_NO_ERROR;
198 }
199
200 svn_error_t *
svn_client_checkout3(svn_revnum_t * result_rev,const char * URL,const char * path,const svn_opt_revision_t * peg_revision,const svn_opt_revision_t * revision,svn_depth_t depth,svn_boolean_t ignore_externals,svn_boolean_t allow_unver_obstructions,svn_client_ctx_t * ctx,apr_pool_t * pool)201 svn_client_checkout3(svn_revnum_t *result_rev,
202 const char *URL,
203 const char *path,
204 const svn_opt_revision_t *peg_revision,
205 const svn_opt_revision_t *revision,
206 svn_depth_t depth,
207 svn_boolean_t ignore_externals,
208 svn_boolean_t allow_unver_obstructions,
209 svn_client_ctx_t *ctx,
210 apr_pool_t *pool)
211 {
212 const char *local_abspath;
213 svn_error_t *err;
214 svn_boolean_t sleep_here = FALSE;
215
216 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
217
218 err = svn_client__checkout_internal(result_rev, &sleep_here,
219 URL, local_abspath,
220 peg_revision, revision, depth,
221 ignore_externals,
222 allow_unver_obstructions,
223 NULL /* ra_session */,
224 ctx, pool);
225 if (sleep_here)
226 svn_io_sleep_for_timestamps(local_abspath, pool);
227
228 return svn_error_trace(err);
229 }
230