Beispiel #1
0
/* The guts of svn_wc_copy3() and svn_wc_move().
 * The additional parameter IS_MOVE indicates whether this is a copy or
 * a move operation.
 *
 * If MOVE_DEGRADED_TO_COPY is not NULL and a move had to be degraded
 * to a copy, then set *MOVE_DEGRADED_TO_COPY. */
static svn_error_t *
copy_or_move(svn_boolean_t *move_degraded_to_copy,
             svn_wc_context_t *wc_ctx,
             const char *src_abspath,
             const char *dst_abspath,
             svn_boolean_t metadata_only,
             svn_boolean_t is_move,
             svn_boolean_t allow_mixed_revisions,
             svn_cancel_func_t cancel_func,
             void *cancel_baton,
             svn_wc_notify_func2_t notify_func,
             void *notify_baton,
             apr_pool_t *scratch_pool)
{
  svn_wc__db_t *db = wc_ctx->db;
  svn_node_kind_t src_db_kind;
  const char *dstdir_abspath;
  svn_boolean_t conflicted;
  const char *tmpdir_abspath;
  const char *src_wcroot_abspath;
  const char *dst_wcroot_abspath;
  svn_boolean_t within_one_wc;
  svn_wc__db_status_t src_status;
  svn_error_t *err;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));

  dstdir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);

  /* Ensure DSTDIR_ABSPATH belongs to the same repository as SRC_ABSPATH;
     throw an error if not. */
  {
    svn_wc__db_status_t dstdir_status;
    const char *src_repos_root_url, *dst_repos_root_url;
    const char *src_repos_uuid, *dst_repos_uuid;
    const char *src_repos_relpath;

    err = svn_wc__db_read_info(&src_status, &src_db_kind, NULL,
                               &src_repos_relpath, &src_repos_root_url,
                               &src_repos_uuid, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, &conflicted, NULL, NULL, NULL, NULL,
                               NULL, NULL,
                               db, src_abspath, scratch_pool, scratch_pool);

    if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
      {
        /* Replicate old error code and text */
        svn_error_clear(err);
        return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
                                 _("'%s' is not under version control"),
                                 svn_dirent_local_style(src_abspath,
                                                        scratch_pool));
      }
    else
      SVN_ERR(err);

    /* Do this now, as we know the right data is cached */
    SVN_ERR(svn_wc__db_get_wcroot(&src_wcroot_abspath, db, src_abspath,
                                  scratch_pool, scratch_pool));

    switch (src_status)
      {
        case svn_wc__db_status_deleted:
          return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
                                   _("Deleted node '%s' can't be copied."),
                                   svn_dirent_local_style(src_abspath,
                                                          scratch_pool));

        case svn_wc__db_status_excluded:
        case svn_wc__db_status_server_excluded:
        case svn_wc__db_status_not_present:
          return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
                                   _("The node '%s' was not found."),
                                   svn_dirent_local_style(src_abspath,
                                                          scratch_pool));
        default:
          break;
      }

     if (is_move && ! strcmp(src_abspath, src_wcroot_abspath))
      {
        return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
                                 _("'%s' is the root of a working copy and "
                                   "cannot be moved"),
                                   svn_dirent_local_style(src_abspath,
                                                          scratch_pool));
      }
    if (is_move && src_repos_relpath && !src_repos_relpath[0])
      {
        return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
                                 _("'%s' represents the repository root "
                                   "and cannot be moved"),
                                 svn_dirent_local_style(src_abspath,
                                                        scratch_pool));
      }

    err = svn_wc__db_read_info(&dstdir_status, NULL, NULL, NULL,
                               &dst_repos_root_url, &dst_repos_uuid, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL,
                               db, dstdir_abspath,
                               scratch_pool, scratch_pool);

    if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
      {
        /* An unversioned destination directory exists on disk. */
        svn_error_clear(err);
        return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
                                 _("'%s' is not under version control"),
                                 svn_dirent_local_style(dstdir_abspath,
                                                        scratch_pool));
      }
    else
      SVN_ERR(err);

    /* Do this now, as we know the right data is cached */
    SVN_ERR(svn_wc__db_get_wcroot(&dst_wcroot_abspath, db, dstdir_abspath,
                                  scratch_pool, scratch_pool));

    if (!src_repos_root_url)
      {
        if (src_status == svn_wc__db_status_added)
          SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
                                           &src_repos_root_url,
                                           &src_repos_uuid, NULL, NULL, NULL,
                                           NULL,
                                           db, src_abspath,
                                           scratch_pool, scratch_pool));
        else
          /* If not added, the node must have a base or we can't copy */
          SVN_ERR(svn_wc__db_scan_base_repos(NULL, &src_repos_root_url,
                                             &src_repos_uuid,
                                             db, src_abspath,
                                             scratch_pool, scratch_pool));
      }

    if (!dst_repos_root_url)
      {
        if (dstdir_status == svn_wc__db_status_added)
          SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
                                           &dst_repos_root_url,
                                           &dst_repos_uuid, NULL, NULL, NULL,
                                           NULL,
                                           db, dstdir_abspath,
                                           scratch_pool, scratch_pool));
        else
          /* If not added, the node must have a base or we can't copy */
          SVN_ERR(svn_wc__db_scan_base_repos(NULL, &dst_repos_root_url,
                                             &dst_repos_uuid,
                                             db, dstdir_abspath,
                                             scratch_pool, scratch_pool));
      }

    if (strcmp(src_repos_root_url, dst_repos_root_url) != 0
        || strcmp(src_repos_uuid, dst_repos_uuid) != 0)
      return svn_error_createf(
         SVN_ERR_WC_INVALID_SCHEDULE, NULL,
         _("Cannot copy to '%s', as it is not from repository '%s'; "
           "it is from '%s'"),
         svn_dirent_local_style(dst_abspath, scratch_pool),
         src_repos_root_url, dst_repos_root_url);

    if (dstdir_status == svn_wc__db_status_deleted)
      return svn_error_createf(
         SVN_ERR_WC_INVALID_SCHEDULE, NULL,
         _("Cannot copy to '%s' as it is scheduled for deletion"),
         svn_dirent_local_style(dst_abspath, scratch_pool));
         /* ### should report dstdir_abspath instead of dst_abspath? */
  }

  /* TODO(#2843): Rework the error report. */
  /* Check if the copy target is missing or hidden and thus not exist on the
     disk, before actually doing the file copy. */
  {
    svn_wc__db_status_t dst_status;

    err = svn_wc__db_read_info(&dst_status, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL,
                               db, dst_abspath, scratch_pool, scratch_pool);

    if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
      return svn_error_trace(err);

    svn_error_clear(err);

    if (!err)
      switch (dst_status)
        {
          case svn_wc__db_status_excluded:
            return svn_error_createf(
                     SVN_ERR_ENTRY_EXISTS, NULL,
                     _("'%s' is already under version control "
                       "but is excluded."),
                     svn_dirent_local_style(dst_abspath, scratch_pool));
          case svn_wc__db_status_server_excluded:
            return svn_error_createf(
                     SVN_ERR_ENTRY_EXISTS, NULL,
                     _("'%s' is already under version control"),
                     svn_dirent_local_style(dst_abspath, scratch_pool));

          case svn_wc__db_status_deleted:
          case svn_wc__db_status_not_present:
            break; /* OK to add */

          default:
            return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
                               _("There is already a versioned item '%s'"),
                               svn_dirent_local_style(dst_abspath,
                                                      scratch_pool));
        }
  }

  /* Check that the target path is not obstructed, if required. */
  if (!metadata_only)
    {
      svn_node_kind_t dst_kind;

      /* (We need only to check the root of the copy, not every path inside
         copy_versioned_file/_dir.) */
      SVN_ERR(svn_io_check_path(dst_abspath, &dst_kind, scratch_pool));
      if (dst_kind != svn_node_none)
        return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
                                 _("'%s' already exists and is in the way"),
                                 svn_dirent_local_style(dst_abspath,
                                                        scratch_pool));
    }

  SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
                                         dstdir_abspath,
                                         scratch_pool, scratch_pool));

  within_one_wc = (strcmp(src_wcroot_abspath, dst_wcroot_abspath) == 0);

  if (is_move
      && !within_one_wc)
    {
      if (move_degraded_to_copy)
        *move_degraded_to_copy = TRUE;

      is_move = FALSE;
    }

  if (!within_one_wc)
    SVN_ERR(svn_wc__db_pristine_transfer(db, src_abspath, dst_wcroot_abspath,
                                         cancel_func, cancel_baton,
                                         scratch_pool));

  if (src_db_kind == svn_node_file
      || src_db_kind == svn_node_symlink)
    {
      err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath,
                                tmpdir_abspath,
                                metadata_only, conflicted, is_move,
                                cancel_func, cancel_baton,
                                notify_func, notify_baton,
                                scratch_pool);
    }
  else
    {
      if (is_move
          && src_status == svn_wc__db_status_normal)
        {
          svn_revnum_t min_rev;
          svn_revnum_t max_rev;

          /* Verify that the move source is a single-revision subtree. */
          SVN_ERR(svn_wc__db_min_max_revisions(&min_rev, &max_rev, db,
                                               src_abspath, FALSE, scratch_pool));
          if (SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev) &&
              min_rev != max_rev)
            {
              if (!allow_mixed_revisions)
                return svn_error_createf(SVN_ERR_WC_MIXED_REVISIONS, NULL,
                                         _("Cannot move mixed-revision "
                                           "subtree '%s' [%ld:%ld]; "
                                           "try updating it first"),
                                         svn_dirent_local_style(src_abspath,
                                                                scratch_pool),
                                         min_rev, max_rev);

              is_move = FALSE;
              if (move_degraded_to_copy)
                *move_degraded_to_copy = TRUE;
            }
        }

      err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath,
                               tmpdir_abspath, metadata_only, is_move,
                               cancel_func, cancel_baton,
                               notify_func, notify_baton,
                               scratch_pool);
    }

  if (err && svn_error_find_cause(err, SVN_ERR_CANCELLED))
    return svn_error_trace(err);

  if (is_move)
    err = svn_error_compose_create(err,
                svn_wc__db_op_handle_move_back(NULL,
                                               db, dst_abspath, src_abspath,
                                               NULL /* work_items */,
                                               scratch_pool));

  /* Run the work queue with the remaining work */
  SVN_ERR(svn_error_compose_create(
                                err,
                                svn_wc__wq_run(db, dst_abspath,
                                                   cancel_func, cancel_baton,
                                                   scratch_pool)));

  return SVN_NO_ERROR;
}
Beispiel #2
0
svn_error_t *
svn_wc__internal_ensure_adm(svn_wc__db_t *db,
                            const char *local_abspath,
                            const char *url,
                            const char *repos_root_url,
                            const char *repos_uuid,
                            svn_revnum_t revision,
                            svn_depth_t depth,
                            apr_pool_t *scratch_pool)
{
  int format;
  const char *repos_relpath;
  svn_wc__db_status_t status;
  const char *db_repos_relpath, *db_repos_root_url, *db_repos_uuid;
  svn_revnum_t db_revision;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
  SVN_ERR_ASSERT(url != NULL);
  SVN_ERR_ASSERT(repos_root_url != NULL);
  SVN_ERR_ASSERT(repos_uuid != NULL);
  SVN_ERR_ASSERT(svn_uri__is_ancestor(repos_root_url, url));

  SVN_ERR(svn_wc__internal_check_wc(&format, db, local_abspath, TRUE,
                                    scratch_pool));

  repos_relpath = svn_uri__is_child(repos_root_url, url, scratch_pool);
  if (repos_relpath == NULL)
    repos_relpath = "";

  /* Early out: we know we're not dealing with an existing wc, so
     just create one. */
  if (format == 0)
    return svn_error_trace(init_adm(db, local_abspath,
                                    repos_relpath, repos_root_url, repos_uuid,
                                    revision, depth, scratch_pool));

  SVN_ERR(svn_wc__db_read_info(&status, NULL,
                               &db_revision, &db_repos_relpath,
                               &db_repos_root_url, &db_repos_uuid,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL, NULL,
                               NULL, NULL, NULL,
                               db, local_abspath, scratch_pool, scratch_pool));

  /* When the directory exists and is scheduled for deletion or is not-present
   * do not check the revision or the URL.  The revision can be any
   * arbitrary revision and the URL may differ if the add is
   * being driven from a merge which will have a different URL. */
  if (status != svn_wc__db_status_deleted
      && status != svn_wc__db_status_not_present)
    {
      /* ### Should we match copyfrom_revision? */
      if (db_revision != revision)
        return
          svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
                            _("Revision %ld doesn't match existing "
                              "revision %ld in '%s'"),
                            revision, db_revision, local_abspath);

      if (!db_repos_root_url)
        {
          if (status == svn_wc__db_status_added)
            SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
                                             &db_repos_relpath,
                                             &db_repos_root_url,
                                             &db_repos_uuid,
                                             NULL, NULL, NULL, NULL,
                                             db, local_abspath,
                                             scratch_pool, scratch_pool));
          else
            SVN_ERR(svn_wc__db_scan_base_repos(&db_repos_relpath,
                                               &db_repos_root_url,
                                               &db_repos_uuid,
                                               db, local_abspath,
                                               scratch_pool, scratch_pool));
        }

      /* The caller gives us a URL which should match the entry. However,
         some callers compensate for an old problem in entry->url and pass
         the copyfrom_url instead. See ^/notes/api-errata/wc002.txt. As
         a result, we allow the passed URL to match copyfrom_url if it
         does not match the entry's primary URL.  */
      /* ### comparing URLs, should they be canonicalized first? */
      if (strcmp(db_repos_uuid, repos_uuid)
          || strcmp(db_repos_root_url, repos_root_url)
          || !svn_relpath__is_ancestor(db_repos_relpath, repos_relpath))
        {
          const char *copyfrom_root_url, *copyfrom_repos_relpath;

          SVN_ERR(svn_wc__internal_get_copyfrom_info(&copyfrom_root_url,
                                                     &copyfrom_repos_relpath,
                                                     NULL, NULL, NULL,
                                                     db, local_abspath,
                                                     scratch_pool,
                                                     scratch_pool));

          if (copyfrom_root_url == NULL
              || strcmp(copyfrom_root_url, repos_root_url)
              || strcmp(copyfrom_repos_relpath, repos_relpath))
            return
              svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
                                _("URL '%s' (uuid: '%s') doesn't match existing "
                                  "URL '%s' (uuid: '%s') in '%s'"),
                                url,
                                db_repos_uuid,
                                svn_path_url_add_component2(db_repos_root_url,
                                                            db_repos_relpath,
                                                            scratch_pool),
                                repos_uuid,
                                local_abspath);
        }
    }

  return SVN_NO_ERROR;
}