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