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 50static svn_error_t * 51initialize_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 68svn_error_t * 69svn_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 200svn_error_t * 201svn_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