Exemplo n.º 1
0
/* Check whether LOCAL_ABSPATH is an external and raise an error if it is.

   A file external should not be deleted since the file external is
   implemented as a switched file and it would delete the file the
   file external is switched to, which is not the behavior the user
   would probably want.

   A directory external should not be deleted since it is the root
   of a different working copy. */
static svn_error_t *
check_external(const char *local_abspath,
               svn_client_ctx_t *ctx,
               apr_pool_t *scratch_pool)
{
    svn_node_kind_t external_kind;
    const char *defining_abspath;

    SVN_ERR(svn_wc__read_external_info(&external_kind, &defining_abspath, NULL,
                                       NULL, NULL,
                                       ctx->wc_ctx, local_abspath,
                                       local_abspath, TRUE,
                                       scratch_pool, scratch_pool));

    if (external_kind != svn_node_none)
        return svn_error_createf(SVN_ERR_WC_CANNOT_DELETE_FILE_EXTERNAL, NULL,
                                 _("Cannot remove the external at '%s'; "
                                   "please edit or delete the svn:externals "
                                   "property on '%s'"),
                                 svn_dirent_local_style(local_abspath,
                                         scratch_pool),
                                 svn_dirent_local_style(defining_abspath,
                                         scratch_pool));

    return SVN_NO_ERROR;
}
Exemplo n.º 2
0
svn_error_t *
svn_client__can_delete(const char *path,
                       svn_client_ctx_t *ctx,
                       apr_pool_t *scratch_pool)
{
    svn_opt_revision_t revision;
    svn_node_kind_t external_kind;
    const char *defining_abspath;
    const char* local_abspath;

    revision.kind = svn_opt_revision_unspecified;

    SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));

    /* A file external should not be deleted since the file external is
       implemented as a switched file and it would delete the file the
       file external is switched to, which is not the behavior the user
       would probably want. */
    SVN_ERR(svn_wc__read_external_info(&external_kind, &defining_abspath, NULL,
                                       NULL, NULL,
                                       ctx->wc_ctx, local_abspath,
                                       local_abspath, TRUE,
                                       scratch_pool, scratch_pool));

    if (external_kind != svn_node_none)
        return svn_error_createf(SVN_ERR_WC_CANNOT_DELETE_FILE_EXTERNAL, NULL,
                                 _("Cannot remove the external at '%s'; "
                                   "please edit or delete the svn:externals "
                                   "property on '%s'"),
                                 svn_dirent_local_style(local_abspath,
                                         scratch_pool),
                                 svn_dirent_local_style(defining_abspath,
                                         scratch_pool));


    /* Use an infinite-depth status check to see if there's anything in
       or under PATH which would make it unsafe for deletion.  The
       status callback function find_undeletables() makes the
       determination, returning an error if it finds anything that shouldn't
       be deleted. */
    return svn_error_trace(svn_client_status5(NULL, ctx, path, &revision,
                           svn_depth_infinity, FALSE,
                           FALSE, FALSE, FALSE, FALSE,
                           NULL,
                           find_undeletables, NULL,
                           scratch_pool));
}
Exemplo n.º 3
0
/* Make an unversioned copy of the versioned file or directory tree at the
 * source path FROM_ABSPATH.  Copy it to the destination path TO_ABSPATH.
 *
 * If REVISION is svn_opt_revision_working, copy the working version,
 * otherwise copy the base version.
 *
 * See copy_one_versioned_file() for details of file copying behaviour,
 * including IGNORE_KEYWORDS and NATIVE_EOL.
 *
 * Include externals unless IGNORE_EXTERNALS is true.
 *
 * Recurse according to DEPTH.
 *

 */
static svn_error_t *
copy_versioned_files(const char *from_abspath,
                     const char *to_abspath,
                     const svn_opt_revision_t *revision,
                     svn_boolean_t force,
                     svn_boolean_t ignore_externals,
                     svn_boolean_t ignore_keywords,
                     svn_depth_t depth,
                     const char *native_eol,
                     svn_client_ctx_t *ctx,
                     apr_pool_t *pool)
{
  svn_error_t *err;
  apr_pool_t *iterpool;
  const apr_array_header_t *children;
  svn_node_kind_t from_kind;
  svn_depth_t node_depth;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(from_abspath));
  SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath));

  /* Only export 'added' and 'replaced' files when the revision is WORKING;
     when the revision is BASE (i.e. != WORKING), only export 'added' and
     'replaced' files when they are part of a copy-/move-here. Otherwise, skip
     them, since they don't have an associated text-base. This condition for
     added/replaced simply is an optimization. Added and replaced files would
     be handled similarly by svn_wc_get_pristine_contents2(), which would
     return NULL if they have no base associated.
     TODO: We may prefer not to duplicate this condition and rather use
     svn_wc_get_pristine_contents2() or a dedicated new function instead.

     Don't export 'deleted' files and directories unless it's a
     revision other than WORKING.  These files and directories
     don't really exist in WORKING. */
  if (revision->kind != svn_opt_revision_working)
    {
      svn_boolean_t is_added;
      const char *repos_relpath;

      SVN_ERR(svn_wc__node_get_origin(&is_added, NULL, &repos_relpath,
                                      NULL, NULL, NULL,
                                      ctx->wc_ctx, from_abspath, FALSE,
                                      pool, pool));

      if (is_added && !repos_relpath)
        return SVN_NO_ERROR; /* Local addition */
    }
  else
    {
      svn_boolean_t is_deleted;

      SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx,
                                             from_abspath, pool));
      if (is_deleted)
        return SVN_NO_ERROR;
    }

  SVN_ERR(svn_wc_read_kind(&from_kind, ctx->wc_ctx, from_abspath, FALSE,
                           pool));

  if (from_kind == svn_node_dir)
    {
      apr_fileperms_t perm = APR_OS_DEFAULT;
      int j;

      /* Try to make the new directory.  If this fails because the
         directory already exists, check our FORCE flag to see if we
         care. */

      /* Keep the source directory's permissions if applicable.
         Skip retrieving the umask on windows. Apr does not implement setting
         filesystem privileges on Windows.
         Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER
         is documented to be 'incredibly expensive' */
#ifndef WIN32
      if (revision->kind == svn_opt_revision_working)
        {
          apr_finfo_t finfo;
          SVN_ERR(svn_io_stat(&finfo, from_abspath, APR_FINFO_PROT, pool));
          perm = finfo.protection;
        }
#endif
      err = svn_io_dir_make(to_abspath, perm, pool);
      if (err)
        {
          if (! APR_STATUS_IS_EEXIST(err->apr_err))
            return svn_error_trace(err);
          if (! force)
            SVN_ERR_W(err, _("Destination directory exists, and will not be "
                             "overwritten unless forced"));
          else
            svn_error_clear(err);
        }

      SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx, from_abspath,
                                        FALSE, pool, pool));

      iterpool = svn_pool_create(pool);
      for (j = 0; j < children->nelts; j++)
        {
          const char *child_abspath = APR_ARRAY_IDX(children, j, const char *);
          const char *child_name = svn_dirent_basename(child_abspath, NULL);
          const char *target_abspath;
          svn_node_kind_t child_kind;

          svn_pool_clear(iterpool);

          if (ctx->cancel_func)
            SVN_ERR(ctx->cancel_func(ctx->cancel_baton));

          target_abspath = svn_dirent_join(to_abspath, child_name, iterpool);

          SVN_ERR(svn_wc_read_kind(&child_kind, ctx->wc_ctx, child_abspath,
                                   FALSE, iterpool));

          if (child_kind == svn_node_dir)
            {
              if (depth == svn_depth_infinity
                  || depth == svn_depth_immediates)
                {
                  if (ctx->notify_func2)
                    {
                      svn_wc_notify_t *notify =
                          svn_wc_create_notify(target_abspath,
                                               svn_wc_notify_update_add, pool);
                      notify->kind = svn_node_dir;
                      (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
                    }

                  if (depth == svn_depth_infinity)
                    SVN_ERR(copy_versioned_files(child_abspath, target_abspath,
                                                 revision, force,
                                                 ignore_externals,
                                                 ignore_keywords, depth,
                                                 native_eol, ctx, iterpool));
                  else
                    SVN_ERR(svn_io_make_dir_recursively(target_abspath,
                                                        iterpool));
                }
            }
          else if (child_kind == svn_node_file
                   && depth >= svn_depth_files)
            {
              svn_node_kind_t external_kind;

              SVN_ERR(svn_wc__read_external_info(&external_kind,
                                                 NULL, NULL, NULL,
                                                 NULL, ctx->wc_ctx,
                                                 child_abspath,
                                                 child_abspath, TRUE,
                                                 pool, pool));

              if (external_kind != svn_node_file)
                SVN_ERR(copy_one_versioned_file(child_abspath, target_abspath,
                                                ctx, revision,
                                                native_eol, ignore_keywords,
                                                iterpool));
            }
        }

      SVN_ERR(svn_wc__node_get_depth(&node_depth, ctx->wc_ctx,
                                     from_abspath, pool));

      /* Handle externals. */
      if (! ignore_externals && depth == svn_depth_infinity
          && node_depth == svn_depth_infinity)
        {
          apr_array_header_t *ext_items;
          const svn_string_t *prop_val;

          SVN_ERR(svn_wc_prop_get2(&prop_val, ctx->wc_ctx, from_abspath,
                                   SVN_PROP_EXTERNALS, pool, pool));
          if (prop_val != NULL)
            {
              int i;

              SVN_ERR(svn_wc_parse_externals_description3(&ext_items,
                                                          from_abspath,
                                                          prop_val->data,
                                                          FALSE, pool));
              for (i = 0; i < ext_items->nelts; ++i)
                {
                  svn_wc_external_item2_t *ext_item;
                  const char *new_from, *new_to;

                  svn_pool_clear(iterpool);

                  ext_item = APR_ARRAY_IDX(ext_items, i,
                                           svn_wc_external_item2_t *);
                  new_from = svn_dirent_join(from_abspath,
                                             ext_item->target_dir,
                                             iterpool);
                  new_to = svn_dirent_join(to_abspath, ext_item->target_dir,
                                           iterpool);

                   /* The target dir might have parents that don't exist.
                      Guarantee the path upto the last component. */
                  if (!svn_dirent_is_root(ext_item->target_dir,
                                          strlen(ext_item->target_dir)))
                    {
                      const char *parent = svn_dirent_dirname(new_to, iterpool);
                      SVN_ERR(svn_io_make_dir_recursively(parent, iterpool));
                    }

                  SVN_ERR(copy_versioned_files(new_from, new_to,
                                               revision, force, FALSE,
                                               ignore_keywords,
                                               svn_depth_infinity, native_eol,
                                               ctx, iterpool));
                }
            }
Exemplo n.º 4
0
/* Called when an external that is in the EXTERNALS table is no longer
   referenced from an svn:externals property */
static svn_error_t *
handle_external_item_removal(const svn_client_ctx_t *ctx,
                             const char *defining_abspath,
                             const char *local_abspath,
                             apr_pool_t *scratch_pool)
{
  svn_error_t *err;
  svn_node_kind_t external_kind;
  svn_node_kind_t kind;
  svn_boolean_t removed = FALSE;

  /* local_abspath should be a wcroot or a file external */
  SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL,
                                     ctx->wc_ctx, defining_abspath,
                                     local_abspath, FALSE,
                                     scratch_pool, scratch_pool));

  SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath, TRUE, FALSE,
                           scratch_pool));

  if (external_kind != kind)
    external_kind = svn_node_none; /* Only remove the registration */

  err = remove_external(&removed,
                        ctx->wc_ctx, defining_abspath, local_abspath,
                        external_kind,
                        ctx->cancel_func, ctx->cancel_baton,
                        scratch_pool);

  if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED && removed)
    {
      svn_error_clear(err);
      err = NULL; /* We removed the working copy, so we can't release the
                     lock that was stored inside */
    }

  if (ctx->notify_func2)
    {
      svn_wc_notify_t *notify =
          svn_wc_create_notify(local_abspath,
                               svn_wc_notify_update_external_removed,
                               scratch_pool);

      notify->kind = kind;
      notify->err = err;

      (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);

      if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
        {
          notify = svn_wc_create_notify(local_abspath,
                                      svn_wc_notify_left_local_modifications,
                                      scratch_pool);
          notify->kind = svn_node_dir;
          notify->err = err;

          (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
        }
    }

  if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
    {
      svn_error_clear(err);
      err = NULL;
    }

  return svn_error_trace(err);
}
Exemplo n.º 5
0
/* Try to update a file external at LOCAL_ABSPATH to URL at REVISION using a
   access baton that has a write lock.  Use SCRATCH_POOL for temporary
   allocations, and use the client context CTX. */
static svn_error_t *
switch_file_external(const char *local_abspath,
                     const char *url,
                     const svn_opt_revision_t *peg_revision,
                     const svn_opt_revision_t *revision,
                     const char *def_dir_abspath,
                     svn_ra_session_t *ra_session,
                     svn_client_ctx_t *ctx,
                     apr_pool_t *scratch_pool)
{
  svn_config_t *cfg = ctx->config
                      ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
                      : NULL;
  svn_boolean_t use_commit_times;
  const char *diff3_cmd;
  const char *preserved_exts_str;
  const apr_array_header_t *preserved_exts;
  svn_node_kind_t kind, external_kind;

  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));

  /* See if the user wants last-commit timestamps instead of current ones. */
  SVN_ERR(svn_config_get_bool(cfg, &use_commit_times,
                              SVN_CONFIG_SECTION_MISCELLANY,
                              SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE));

  /* Get the external diff3, if any. */
  svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
                 SVN_CONFIG_OPTION_DIFF3_CMD, NULL);

  if (diff3_cmd != NULL)
    SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));

  /* See which files the user wants to preserve the extension of when
     conflict files are made. */
  svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
                 SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
  preserved_exts = *preserved_exts_str
    ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, scratch_pool)
    : NULL;

  {
    const char *wcroot_abspath;

    SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, local_abspath,
                               scratch_pool, scratch_pool));

    /* File externals can only be installed inside the current working copy.
       So verify if the working copy that contains/will contain the target
       is the defining abspath, or one of its ancestors */

    if (!svn_dirent_is_ancestor(wcroot_abspath, def_dir_abspath))
        return svn_error_createf(
                        SVN_ERR_WC_BAD_PATH, NULL,
                        _("Cannot insert a file external defined on '%s' "
                          "into the working copy '%s'."),
                        svn_dirent_local_style(def_dir_abspath,
                                               scratch_pool),
                        svn_dirent_local_style(wcroot_abspath,
                                               scratch_pool));
  }

  SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath,
                            TRUE, FALSE, scratch_pool));

  SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL,
                                     ctx->wc_ctx, local_abspath, local_abspath,
                                     TRUE, scratch_pool, scratch_pool));

  /* If there is a versioned item with this name, ensure it's a file
     external before working with it.  If there is no entry in the
     working copy, then create an empty file and add it to the working
     copy. */
  if (kind != svn_node_none && kind != svn_node_unknown)
    {
      if (external_kind != svn_node_file)
        {
          return svn_error_createf(
              SVN_ERR_CLIENT_FILE_EXTERNAL_OVERWRITE_VERSIONED, 0,
             _("The file external from '%s' cannot overwrite the existing "
               "versioned item at '%s'"),
             url, svn_dirent_local_style(local_abspath, scratch_pool));
        }
    }
  else
    {
      svn_node_kind_t disk_kind;

      SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool));

      if (kind == svn_node_file || kind == svn_node_dir)
        return svn_error_createf(SVN_ERR_WC_PATH_FOUND, NULL,
                                 _("The file external '%s' can not be "
                                   "created because the node exists."),
                                 svn_dirent_local_style(local_abspath,
                                                        scratch_pool));
    }

  {
    const svn_ra_reporter3_t *reporter;
    void *report_baton;
    const svn_delta_editor_t *switch_editor;
    void *switch_baton;
    svn_client__pathrev_t *switch_loc;
    svn_revnum_t revnum;
    apr_array_header_t *inherited_props;
    const char *dir_abspath;
    const char *target;

    svn_dirent_split(&dir_abspath, &target, local_abspath, scratch_pool);

    /* ### Why do we open a new session?  RA_SESSION is a valid
       ### session -- the caller used it to call svn_ra_check_path on
       ### this very URL, the caller also did the resolving and
       ### reparenting that is repeated here. */
    SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc,
                                              url, dir_abspath,
                                              peg_revision, revision,
                                              ctx, scratch_pool));
    /* Get the external file's iprops. */
    SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "",
                                       switch_loc->rev,
                                       scratch_pool, scratch_pool));

    SVN_ERR(svn_ra_reparent(ra_session, svn_uri_dirname(url, scratch_pool),
                            scratch_pool));

    SVN_ERR(svn_wc__get_file_external_editor(&switch_editor, &switch_baton,
                                             &revnum, ctx->wc_ctx,
                                             local_abspath,
                                             def_dir_abspath,
                                             switch_loc->url,
                                             switch_loc->repos_root_url,
                                             switch_loc->repos_uuid,
                                             inherited_props,
                                             use_commit_times,
                                             diff3_cmd, preserved_exts,
                                             def_dir_abspath,
                                             url, peg_revision, revision,
                                             ctx->conflict_func2,
                                             ctx->conflict_baton2,
                                             ctx->cancel_func,
                                             ctx->cancel_baton,
                                             ctx->notify_func2,
                                             ctx->notify_baton2,
                                             scratch_pool, scratch_pool));

    /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an
     invalid revnum, that means RA will use the latest revision. */
    SVN_ERR(svn_ra_do_switch3(ra_session, &reporter, &report_baton,
                              switch_loc->rev,
                              target, svn_depth_unknown, switch_loc->url,
                              FALSE /* send_copyfrom */,
                              TRUE /* ignore_ancestry */,
                              switch_editor, switch_baton,
                              scratch_pool, scratch_pool));

    SVN_ERR(svn_wc__crawl_file_external(ctx->wc_ctx, local_abspath,
                                        reporter, report_baton,
                                        TRUE,  use_commit_times,
                                        ctx->cancel_func, ctx->cancel_baton,
                                        ctx->notify_func2, ctx->notify_baton2,
                                        scratch_pool));

    if (ctx->notify_func2)
      {
        svn_wc_notify_t *notify
          = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed,
                                 scratch_pool);
        notify->kind = svn_node_none;
        notify->content_state = notify->prop_state
          = svn_wc_notify_state_inapplicable;
        notify->lock_state = svn_wc_notify_lock_state_inapplicable;
        notify->revision = revnum;
        (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
      }
  }

  return SVN_NO_ERROR;
}
Exemplo n.º 6
0
/* Perform status operations on each external in EXTERNAL_MAP, a const char *
   local_abspath of all externals mapping to the const char* defining_abspath.
   All other options are the same as those passed to svn_client_status().

   If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide
   properly formatted relative paths */
static svn_error_t *
do_external_status(svn_client_ctx_t *ctx,
                   apr_hash_t *external_map,
                   svn_depth_t depth,
                   svn_boolean_t get_all,
                   svn_boolean_t check_out_of_date,
                   svn_boolean_t check_working_copy,
                   svn_boolean_t no_ignore,
                   const apr_array_header_t *changelists,
                   const char *anchor_abspath,
                   const char *anchor_relpath,
                   svn_client_status_func_t status_func,
                   void *status_baton,
                   apr_pool_t *scratch_pool)
{
    apr_pool_t *iterpool = svn_pool_create(scratch_pool);
    apr_array_header_t *externals;
    int i;

    externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically,
                               scratch_pool);

    /* Loop over the hash of new values (we don't care about the old
       ones).  This is a mapping of versioned directories to property
       values. */
    for (i = 0; i < externals->nelts; i++)
    {
        svn_node_kind_t external_kind;
        svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t);
        const char *local_abspath = item.key;
        const char *defining_abspath = item.value;
        svn_node_kind_t kind;
        svn_opt_revision_t opt_rev;
        const char *status_path;

        svn_pool_clear(iterpool);

        /* Obtain information on the expected external. */
        SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
                                           &opt_rev.value.number,
                                           ctx->wc_ctx, defining_abspath,
                                           local_abspath, FALSE,
                                           iterpool, iterpool));

        if (external_kind != svn_node_dir)
            continue;

        SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool));
        if (kind != svn_node_dir)
            continue;

        if (SVN_IS_VALID_REVNUM(opt_rev.value.number))
            opt_rev.kind = svn_opt_revision_number;
        else
            opt_rev.kind = svn_opt_revision_unspecified;

        /* Tell the client we're starting an external status set. */
        if (ctx->notify_func2)
            ctx->notify_func2(
                ctx->notify_baton2,
                svn_wc_create_notify(local_abspath,
                                     svn_wc_notify_status_external,
                                     iterpool), iterpool);

        status_path = local_abspath;
        if (anchor_abspath)
        {
            status_path = svn_dirent_join(anchor_relpath,
                                          svn_dirent_skip_ancestor(anchor_abspath,
                                                  status_path),
                                          iterpool);
        }

        /* And then do the status. */
        SVN_ERR(svn_client_status6(NULL, ctx, status_path, &opt_rev, depth,
                                   get_all, check_out_of_date,
                                   check_working_copy, no_ignore,
                                   FALSE /* ignore_exernals */,
                                   FALSE /* depth_as_sticky */,
                                   changelists, status_func, status_baton,
                                   iterpool));
    }

    /* Destroy SUBPOOL and (implicitly) ITERPOOL. */
    svn_pool_destroy(iterpool);

    return SVN_NO_ERROR;
}