Пример #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;
}
Пример #2
0
/* Diff the file PATH against its text base.  At this
 * stage we are dealing with a file that does exist in the working copy.
 *
 * DIR_BATON is the parent directory baton, PATH is the path to the file to
 * be compared.
 *
 * Do all allocation in POOL.
 *
 * ### TODO: Need to work on replace if the new filename used to be a
 * directory.
 */
static svn_error_t *
file_diff(struct diff_baton *eb,
          const char *local_abspath,
          const char *path,
          apr_pool_t *scratch_pool)
{
    svn_wc__db_t *db = eb->db;
    const char *empty_file;
    const char *original_repos_relpath;
    svn_wc__db_status_t status;
    svn_kind_t kind;
    svn_revnum_t revision;
    const svn_checksum_t *checksum;
    svn_boolean_t op_root;
    svn_boolean_t had_props, props_mod;
    svn_boolean_t have_base, have_more_work;
    svn_boolean_t replaced = FALSE;
    svn_boolean_t base_replace = FALSE;
    svn_wc__db_status_t base_status;
    svn_revnum_t base_revision = SVN_INVALID_REVNUM;
    const svn_checksum_t *base_checksum;
    const char *pristine_abspath;

    SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL, &checksum, NULL,
                                 &original_repos_relpath, NULL, NULL, NULL,
                                 NULL, NULL, NULL, NULL, NULL,
                                 &op_root, &had_props, &props_mod,
                                 &have_base, &have_more_work, NULL,
                                 db, local_abspath, scratch_pool, scratch_pool));

    if ((status == svn_wc__db_status_added) && (have_base || have_more_work))
    {
        SVN_ERR(svn_wc__db_node_check_replace(&replaced, &base_replace,
                                              NULL, db, local_abspath,
                                              scratch_pool));

        if (replaced && base_replace /* && !have_more_work */)
        {
            svn_kind_t base_kind;
            SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind,
                                             &base_revision,
                                             NULL, NULL, NULL, NULL, NULL, NULL,
                                             NULL, &base_checksum, NULL,
                                             NULL, NULL, NULL,
                                             db, local_abspath,
                                             scratch_pool, scratch_pool));

            if (base_status != svn_wc__db_status_normal
                    || base_kind != kind)
            {
                /* We can't show a replacement here */
                replaced = FALSE;
                base_replace = FALSE;
            }
        }
        else
        {
            /* We can't look in this middle working layer (yet).
               We just report the change itself.

               And if we could look at it, how would we report the addition
               of this middle layer (and maybe different layers below that)?

               For 1.7 we just do what we did before: Ignore this layering
               problem and just show how the current file got in your wc.
             */
            replaced = FALSE;
            base_replace = FALSE;
        }
    }

    /* Now refine ADDED to one of: ADDED, COPIED, MOVED_HERE. Note that only
       the latter two have corresponding pristine info to diff against.  */
    if (status == svn_wc__db_status_added)
        SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL,
                                         &original_repos_relpath, NULL, NULL,
                                         NULL, NULL, NULL, db, local_abspath,
                                         scratch_pool, scratch_pool));


    SVN_ERR(get_empty_file(eb, &empty_file, scratch_pool));

    /* When we show a delete, we show a diff of the original pristine against
     * an empty file.
     * A base-replace is treated like a delete plus an add.
     *
     * For this kind of diff we prefer to show the deletion of what was checked
     * out over showing what was actually deleted (if that was defined by
     * a higher layer). */
    if (status == svn_wc__db_status_deleted ||
            (base_replace && ! eb->ignore_ancestry))
    {
        apr_hash_t *del_props;
        const svn_checksum_t *del_checksum;
        const char *del_text_abspath;
        const char *del_mimetype;

        if (base_replace && ! eb->ignore_ancestry)
        {
            /* We show a deletion of the information in the BASE layer */
            SVN_ERR(svn_wc__db_base_get_props(&del_props, db, local_abspath,
                                              scratch_pool, scratch_pool));

            del_checksum = base_checksum;
        }
        else
        {
            /* We show a deletion of what was actually deleted */
            SVN_ERR_ASSERT(status == svn_wc__db_status_deleted);

            SVN_ERR(svn_wc__get_pristine_props(&del_props, db, local_abspath,
                                               scratch_pool, scratch_pool));

            SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
                                                  NULL, &del_checksum, NULL,
                                                  NULL, db, local_abspath,
                                                  scratch_pool, scratch_pool));
        }

        SVN_ERR_ASSERT(del_checksum != NULL);

        SVN_ERR(svn_wc__db_pristine_get_path(&del_text_abspath, db,
                                             local_abspath, del_checksum,
                                             scratch_pool, scratch_pool));

        if (del_props == NULL)
            del_props = apr_hash_make(scratch_pool);

        del_mimetype = get_prop_mimetype(del_props);

        SVN_ERR(eb->callbacks->file_deleted(NULL, NULL, path,
                                            del_text_abspath,
                                            empty_file,
                                            del_mimetype,
                                            NULL,
                                            del_props,
                                            eb->callback_baton,
                                            scratch_pool));

        if (status == svn_wc__db_status_deleted)
        {
            /* We're here only for showing a delete, so we're done. */
            return SVN_NO_ERROR;
        }
    }

    if (checksum != NULL)
        SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, db, local_abspath,
                                             checksum,
                                             scratch_pool, scratch_pool));
    else if (base_replace && eb->ignore_ancestry)
        SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, db, local_abspath,
                                             base_checksum,
                                             scratch_pool, scratch_pool));
    else
        pristine_abspath = empty_file;

    /* Now deal with showing additions, or the add-half of replacements.
     * If the item is schedule-add *with history*, then we usually want
     * to see the usual working vs. text-base comparison, which will show changes
     * made since the file was copied.  But in case we're showing copies as adds,
     * we need to compare the copied file to the empty file. If we're doing a git
     * diff, and the file was copied, we need to report the file as added and
     * diff it against the text base, so that a "copied" git diff header, and
     * possibly a diff against the copy source, will be generated for it. */
    if ((! base_replace && status == svn_wc__db_status_added) ||
            (base_replace && ! eb->ignore_ancestry) ||
            ((status == svn_wc__db_status_copied ||
              status == svn_wc__db_status_moved_here) &&
             (eb->show_copies_as_adds || eb->use_git_diff_format)))
    {
        const char *translated = NULL;
        apr_hash_t *pristine_props;
        apr_hash_t *actual_props;
        const char *actual_mimetype;
        apr_array_header_t *propchanges;


        /* Get svn:mime-type from ACTUAL props of PATH. */
        SVN_ERR(svn_wc__get_actual_props(&actual_props, db, local_abspath,
                                         scratch_pool, scratch_pool));
        actual_mimetype = get_prop_mimetype(actual_props);

        /* Set the original properties to empty, then compute "changes" from
           that. Essentially, all ACTUAL props will be "added".  */
        pristine_props = apr_hash_make(scratch_pool);
        SVN_ERR(svn_prop_diffs(&propchanges, actual_props, pristine_props,
                               scratch_pool));

        SVN_ERR(svn_wc__internal_translated_file(
                    &translated, local_abspath, db, local_abspath,
                    SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
                    eb->cancel_func, eb->cancel_baton,
                    scratch_pool, scratch_pool));

        SVN_ERR(eb->callbacks->file_added(NULL, NULL, NULL, path,
                                          (! eb->show_copies_as_adds &&
                                           eb->use_git_diff_format &&
                                           status != svn_wc__db_status_added) ?
                                          pristine_abspath : empty_file,
                                          translated,
                                          0, revision,
                                          NULL,
                                          actual_mimetype,
                                          original_repos_relpath,
                                          SVN_INVALID_REVNUM, propchanges,
                                          pristine_props, eb->callback_baton,
                                          scratch_pool));
    }
    else
    {
        const char *translated = NULL;
        apr_hash_t *pristine_props;
        const char *pristine_mimetype;
        const char *actual_mimetype;
        apr_hash_t *actual_props;
        apr_array_header_t *propchanges;
        svn_boolean_t modified;

        /* Here we deal with showing pure modifications. */
        SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, local_abspath,
                FALSE, scratch_pool));
        if (modified)
        {
            /* Note that this might be the _second_ time we translate
               the file, as svn_wc__text_modified_internal_p() might have used a
               tmp translated copy too.  But what the heck, diff is
               already expensive, translating twice for the sake of code
               modularity is liveable. */
            SVN_ERR(svn_wc__internal_translated_file(
                        &translated, local_abspath, db, local_abspath,
                        SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
                        eb->cancel_func, eb->cancel_baton,
                        scratch_pool, scratch_pool));
        }

        /* Get the properties, the svn:mime-type values, and compute the
           differences between the two.  */
        if (base_replace
                && eb->ignore_ancestry)
        {
            /* We don't want the normal pristine properties (which are
               from the WORKING tree). We want the pristines associated
               with the BASE tree, which are saved as "revert" props.  */
            SVN_ERR(svn_wc__db_base_get_props(&pristine_props,
                                              db, local_abspath,
                                              scratch_pool, scratch_pool));
        }
        else
        {
            /* We can only fetch the pristine props (from BASE or WORKING) if
               the node has not been replaced, or it was copied/moved here.  */
            SVN_ERR_ASSERT(!replaced
                           || status == svn_wc__db_status_copied
                           || status == svn_wc__db_status_moved_here);

            SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db,
                                                   local_abspath,
                                                   scratch_pool, scratch_pool));

            /* baseprops will be NULL for added nodes */
            if (!pristine_props)
                pristine_props = apr_hash_make(scratch_pool);
        }
        pristine_mimetype = get_prop_mimetype(pristine_props);

        SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath,
                                      scratch_pool, scratch_pool));
        actual_mimetype = get_prop_mimetype(actual_props);

        SVN_ERR(svn_prop_diffs(&propchanges, actual_props, pristine_props,
                               scratch_pool));

        if (modified || propchanges->nelts > 0)
        {
            SVN_ERR(eb->callbacks->file_changed(NULL, NULL, NULL,
                                                path,
                                                modified ? pristine_abspath
                                                : NULL,
                                                translated,
                                                revision,
                                                SVN_INVALID_REVNUM,
                                                pristine_mimetype,
                                                actual_mimetype,
                                                propchanges,
                                                pristine_props,
                                                eb->callback_baton,
                                                scratch_pool));
        }
    }

    return SVN_NO_ERROR;
}
Пример #3
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;
}