/* * checkout.c: wrappers around wc checkout functionality * * ==================================================================== * Copyright (c) 2000-2004 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */ /* ==================================================================== */ /*** Includes. ***/ #include #include "svn_pools.h" #include "svn_wc.h" #include "svn_client.h" #include "svn_ra.h" #include "svn_types.h" #include "svn_error.h" #include "svn_path.h" #include "svn_io.h" #include "svn_opt.h" #include "svn_time.h" #include "client.h" #include "svn_private_config.h" /*** Public Interfaces. ***/ svn_error_t * svn_client__checkout_internal(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_boolean_t recurse, svn_boolean_t ignore_externals, svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info(pool); svn_error_t *err = NULL; svn_revnum_t revnum; svn_boolean_t sleep_here = FALSE; svn_boolean_t *use_sleep = timestamp_sleep ? timestamp_sleep : &sleep_here; const char *session_url; /* Sanity check. Without these, the checkout is meaningless. */ assert(path != NULL); assert(url != NULL); /* Fulfill the docstring promise of svn_client_checkout: */ if ((revision->kind != svn_opt_revision_number) && (revision->kind != svn_opt_revision_date) && (revision->kind != svn_opt_revision_head)) return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); /* Canonicalize the URL. */ url = svn_path_canonicalize(url, pool); { svn_ra_session_t *ra_session; svn_node_kind_t kind; const char *uuid, *repos; apr_pool_t *session_pool = svn_pool_create(pool); /* Get the RA connection. */ SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum, &session_url, url, peg_revision, revision, ctx, session_pool)); SVN_ERR(svn_ra_check_path(ra_session, "", revnum, &kind, pool)); if (kind == svn_node_none) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' doesn't exist"), session_url); else if (kind == svn_node_file) return svn_error_createf (SVN_ERR_UNSUPPORTED_FEATURE , NULL, _("URL '%s' refers to a file, not a directory"), session_url); /* Get the repos UUID and root URL. */ SVN_ERR(svn_ra_get_uuid(ra_session, &uuid, pool)); SVN_ERR(svn_ra_get_repos_root(ra_session, &repos, pool)); SVN_ERR(svn_io_check_path(path, &kind, pool)); /* Finished with the RA session -- close up, but not without copying out useful information that needs to survive. */ session_url = apr_pstrdup(pool, session_url); uuid = (uuid ? apr_pstrdup(pool, uuid) : NULL); repos = (repos ? apr_pstrdup(pool, repos) : NULL); svn_pool_destroy(session_pool); if (kind == svn_node_none) { /* Bootstrap: create an incomplete working-copy root dir. Its entries file should only have an entry for THIS_DIR with a URL, revnum, and an 'incomplete' flag. */ SVN_ERR(svn_io_make_dir_recursively(path, pool)); SVN_ERR(svn_wc_ensure_adm2(path, uuid, session_url, repos, revnum, pool)); /* Have update fix the incompleteness. */ err = svn_client__update_internal(result_rev, path, revision, recurse, ignore_externals, use_sleep, ctx, pool); } else if (kind == svn_node_dir) { int wc_format; const svn_wc_entry_t *entry; svn_wc_adm_access_t *adm_access; SVN_ERR(svn_wc_check_wc(path, &wc_format, pool)); if (! wc_format) { /* Make the unversioned directory into a versioned one. */ SVN_ERR(svn_wc_ensure_adm2(path, uuid, session_url, repos, revnum, pool)); err = svn_client__update_internal(result_rev, path, revision, recurse, ignore_externals, use_sleep, ctx, pool); goto done; } /* Get PATH's entry. */ SVN_ERR(svn_wc_adm_open3(&adm_access, NULL, path, FALSE, 0, ctx->cancel_func, ctx->cancel_baton, pool)); SVN_ERR(svn_wc_entry(&entry, path, adm_access, FALSE, pool)); SVN_ERR(svn_wc_adm_close(adm_access)); /* If PATH's existing URL matches the incoming one, then just update. This allows 'svn co' to restart an interrupted checkout. */ if (entry->url && (strcmp(entry->url, session_url) == 0)) { err = svn_client__update_internal(result_rev, path, revision, recurse, ignore_externals, use_sleep, ctx, pool); } else { const char *errmsg; errmsg = apr_psprintf (pool, _("'%s' is already a working copy for a different URL"), svn_path_local_style(path, pool)); if (entry->incomplete) errmsg = apr_pstrcat (pool, errmsg, _("; run 'svn update' to complete it"), NULL); return svn_error_create(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, errmsg); } } else { return svn_error_createf (SVN_ERR_WC_NODE_KIND_CHANGE, NULL, _("'%s' already exists and is not a directory"), svn_path_local_style(path, pool)); } done: if (err) { /* Don't rely on the error handling to handle the sleep later, do it now */ svn_sleep_for_timestamps(); return err; } *use_sleep = TRUE; } /* We handle externals after the initial checkout is complete, so that fetching external items (and any errors therefrom) doesn't delay the primary checkout. ### Should we really do externals if recurse is false? */ SVN_ERR(svn_client__handle_externals(traversal_info, FALSE, use_sleep, ctx, pool)); if (sleep_here) svn_sleep_for_timestamps(); return SVN_NO_ERROR; } svn_error_t * svn_client_checkout2(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_boolean_t recurse, svn_boolean_t ignore_externals, svn_client_ctx_t *ctx, apr_pool_t *pool) { return svn_client__checkout_internal(result_rev, URL, path, peg_revision, revision, recurse, ignore_externals, NULL, ctx, pool); } svn_error_t * svn_client_checkout(svn_revnum_t *result_rev, const char *URL, const char *path, const svn_opt_revision_t *revision, svn_boolean_t recurse, svn_client_ctx_t *ctx, apr_pool_t *pool) { svn_opt_revision_t peg_revision; peg_revision.kind = svn_opt_revision_unspecified; return svn_client__checkout_internal(result_rev, URL, path, &peg_revision, revision, recurse, FALSE, NULL, ctx, pool); }