예제 #1
0
/* An editor function.  */
static svn_error_t *
delete_entry(const char *path,
             svn_revnum_t base_revision,
             void *parent_baton,
             apr_pool_t *pool)
{
  struct item_baton *ib = parent_baton;
  struct edit_baton *eb = ib->edit_baton;
  svn_client_diff_summarize_t *sum;
  svn_node_kind_t kind;

  /* We need to know if this is a directory or a file */
  SVN_ERR(svn_ra_check_path(eb->ra_session,
                            path,
                            eb->revision,
                            &kind,
                            pool));

  sum = apr_pcalloc(pool, sizeof(*sum));
  sum->summarize_kind = svn_client_diff_summarize_kind_deleted;
  sum->path = path;
  sum->node_kind = kind;

  SVN_ERR(eb->summarize_func(sum, eb->summarize_func_baton, pool));

  return SVN_NO_ERROR;
}
예제 #2
0
svn_error_t*
CommitEditor::get_copysrc_kind_cb(svn_node_kind_t* kind, void* baton,
                                  const char* repos_relpath,
                                  svn_revnum_t src_revision,
                                  apr_pool_t *scratch_pool)
{
  CommitEditor* editor = static_cast<CommitEditor*>(baton);
  if (editor->m_get_kind_cb.get())
    {
      const Java::Env env;
      SVN_JAVAHL_CATCH(env, SVN_ERR_BASE,
                       invoke_get_kind_cb(kind, env,
                                          editor->m_get_kind_cb.get(),
                                          repos_relpath,
                                          src_revision));
    }
  else
    {
      SVN_ERR(open_callback_session(editor->m_callback_session,
                                    editor->m_callback_session_url,
                                    editor->m_callback_session_uuid,
                                    editor->m_session->m_context,
                                    editor->pool));

      return svn_ra_check_path(editor->m_callback_session,
                               repos_relpath, src_revision, kind,
                               scratch_pool);
    }
  return SVN_NO_ERROR;
}
예제 #3
0
svn_error_t*
CommitEditor::provide_props_cb(apr_hash_t **props,
                               svn_revnum_t *revision,
                               void *baton,
                               const char *repos_relpath,
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool)
{
  CommitEditor* editor = static_cast<CommitEditor*>(baton);
  if (editor->m_get_props_cb.get())
    {
      const Java::Env env;
      SVN_JAVAHL_CATCH(env, SVN_ERR_BASE,
                       invoke_get_props_cb(props, revision, env,
                                           editor->m_get_props_cb.get(),
                                           repos_relpath,
                                           result_pool));
    }
  else
    {
      SVN_ERR(open_callback_session(editor->m_callback_session,
                                    editor->m_callback_session_url,
                                    editor->m_callback_session_uuid,
                                    editor->m_session->m_context,
                                    editor->pool));

      svn_node_kind_t kind = svn_node_unknown;
      SVN_ERR(svn_ra_check_path(editor->m_callback_session,
                                repos_relpath, SVN_INVALID_REVNUM, &kind,
                                scratch_pool));

      // FIXME: Getting properties from the youngest revision is in
      // fact not such a bright idea, as the path may have been moved
      // or deleted in the repository. On the other hand, if that
      // happens, the commit would fail due to a conflict anyway.
      if (kind == svn_node_file)
        return svn_ra_get_file(editor->m_callback_session,
                               repos_relpath, SVN_INVALID_REVNUM,
                               NULL, revision, props, scratch_pool);
      else if (kind == svn_node_dir)
        return svn_ra_get_dir2(editor->m_callback_session, NULL, revision,
                               props, repos_relpath, SVN_INVALID_REVNUM, 0,
                               scratch_pool);
      else
        return svn_error_createf(
            SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
            _("Expected node kind '%s' or '%s' but got '%s'"),
            svn_node_kind_to_word(svn_node_file),
            svn_node_kind_to_word(svn_node_dir),
            svn_node_kind_to_word(kind));
    }
  return SVN_NO_ERROR;
}
예제 #4
0
static svn_error_t *
fetch_kind_func(svn_kind_t *kind,
                void *baton,
                const char *path,
                svn_revnum_t base_revision,
                apr_pool_t *scratch_pool)
{
  struct edit_baton *eb = baton;
  svn_node_kind_t node_kind;

  SVN_ERR(svn_ra_check_path(eb->ra_session, path, eb->revision, &node_kind,
                            scratch_pool));

  *kind = svn__kind_from_node_kind(node_kind, FALSE);
  return SVN_NO_ERROR;
}
예제 #5
0
static svn_error_t *
fetch_kind_func(svn_node_kind_t *kind,
                void *baton,
                const char *path,
                svn_revnum_t base_revision,
                apr_pool_t *scratch_pool)
{
  struct revision_baton *rb = baton;

  if (! SVN_IS_VALID_REVNUM(base_revision))
    base_revision = rb->rev - 1;

  SVN_ERR(svn_ra_check_path(rb->pb->aux_session, path, base_revision,
                            kind, scratch_pool));

  return SVN_NO_ERROR;
}
예제 #6
0
파일: update.c 프로젝트: Alkzndr/freebsd
/* Implements svn_wc_dirents_func_t for update and switch handling. Assumes
   a struct svn_client__dirent_fetcher_baton_t * baton */
svn_error_t *
svn_client__dirent_fetcher(void *baton,
                           apr_hash_t **dirents,
                           const char *repos_root_url,
                           const char *repos_relpath,
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool)
{
  struct svn_client__dirent_fetcher_baton_t *dfb = baton;
  const char *old_url = NULL;
  const char *session_relpath;
  svn_node_kind_t kind;
  const char *url;

  url = svn_path_url_add_component2(repos_root_url, repos_relpath,
                                    scratch_pool);

  if (!svn_uri__is_ancestor(dfb->anchor_url, url))
    {
      SVN_ERR(svn_client__ensure_ra_session_url(&old_url, dfb->ra_session,
                                                url, scratch_pool));
      session_relpath = "";
    }
  else
    SVN_ERR(svn_ra_get_path_relative_to_session(dfb->ra_session,
                                                &session_relpath, url,
                                                scratch_pool));

  /* Is session_relpath still a directory? */
  SVN_ERR(svn_ra_check_path(dfb->ra_session, session_relpath,
                            dfb->target_revision, &kind, scratch_pool));

  if (kind == svn_node_dir)
    SVN_ERR(svn_ra_get_dir2(dfb->ra_session, dirents, NULL, NULL,
                            session_relpath, dfb->target_revision,
                            SVN_DIRENT_KIND, result_pool));
  else
    *dirents = NULL;

  if (old_url)
    SVN_ERR(svn_ra_reparent(dfb->ra_session, old_url, scratch_pool));

  return SVN_NO_ERROR;
}
예제 #7
0
static svn_error_t *
fetch_props_func(apr_hash_t **props,
                 void *baton,
                 const char *path,
                 svn_revnum_t base_revision,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
{
  struct revision_baton *rb = baton;
  svn_node_kind_t node_kind;

  if (! SVN_IS_VALID_REVNUM(base_revision))
    base_revision = rb->rev - 1;

  SVN_ERR(svn_ra_check_path(rb->pb->aux_session, path, base_revision,
                            &node_kind, scratch_pool));

  if (node_kind == svn_node_file)
    {
      SVN_ERR(svn_ra_get_file(rb->pb->aux_session, path, base_revision,
                              NULL, NULL, props, result_pool));
    }
  else if (node_kind == svn_node_dir)
    {
      apr_array_header_t *tmp_props;

      SVN_ERR(svn_ra_get_dir2(rb->pb->aux_session, NULL, NULL, props, path,
                              base_revision, 0 /* Dirent fields */,
                              result_pool));
      tmp_props = svn_prop_hash_to_array(*props, result_pool);
      SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
                                   result_pool));
      *props = svn_prop_array_to_hash(tmp_props, result_pool);
    }
  else
    {
      *props = apr_hash_make(result_pool);
    }

  return SVN_NO_ERROR;
}
예제 #8
0
/* Append URL, and all it's non-existent parent directories, to TARGETS.
   Use TEMPPOOL for temporary allocations and POOL for any additions to
   TARGETS. */
static svn_error_t *
add_url_parents(svn_ra_session_t *ra_session,
                const char *url,
                apr_array_header_t *targets,
                apr_pool_t *temppool,
                apr_pool_t *pool)
{
  svn_node_kind_t kind;
  const char *parent_url = svn_uri_dirname(url, pool);

  SVN_ERR(svn_ra_reparent(ra_session, parent_url, temppool));
  SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
                            temppool));

  if (kind == svn_node_none)
    SVN_ERR(add_url_parents(ra_session, parent_url, targets, temppool, pool));

  APR_ARRAY_PUSH(targets, const char *) = url;

  return SVN_NO_ERROR;
}
예제 #9
0
파일: session.c 프로젝트: epu/rsvndump
/* Reparents the session if the current root is a file */
char session_check_reparent(session_t *session, svn_revnum_t rev)
{
	svn_error_t *err;
	svn_node_kind_t kind;
	apr_pool_t *pool = svn_pool_create(session->pool);

	/* Check if the current root is a file */
	err = svn_ra_check_path(session->ra, "", rev, &kind, pool);
	if (err) {
		utils_handle_error(err, stderr, FALSE, "ERROR: ");
		svn_error_clear(err);
		svn_pool_destroy(pool);
		return 1;
	}

	if (kind == svn_node_file) {
#ifdef USE_SINGLEFILE_DUMP
		/* Determine the parent directory */
		const char *new_parent;
		svn_path_split(session->encoded_url, &new_parent, &session->file, session->pool);
		/* Reparent the session to the parent repository */
		err = svn_ra_reparent(session->ra, new_parent, pool);
		if (err) {
			utils_handle_error(err, stderr, FALSE, "ERROR: ");
			svn_error_clear(err);
			svn_pool_destroy(pool);
			return 1;
		}
#else
		/* Sorry, we're unable to dump single files for now */
		fprintf(stderr, _("ERROR: '%s' refers to a file.\n"), session->encoded_url);
		svn_pool_destroy(pool);
		return 1;
#endif
	}

	svn_pool_destroy(pool);
	return 0;
}
예제 #10
0
파일: compat.c 프로젝트: 2asoft/freebsd
svn_error_t *
svn_ra__location_segments_from_log(svn_ra_session_t *session,
                                   const char *path,
                                   svn_revnum_t peg_revision,
                                   svn_revnum_t start_rev,
                                   svn_revnum_t end_rev,
                                   svn_location_segment_receiver_t receiver,
                                   void *receiver_baton,
                                   apr_pool_t *pool)
{
  struct gls_log_receiver_baton lrb = { 0 };
  apr_array_header_t *targets;
  svn_node_kind_t kind;
  svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
  const char *fs_path;

  /* Fetch the absolute FS path associated with PATH. */
  SVN_ERR(get_fs_path(&fs_path, session, path, pool));

  /* If PEG_REVISION is invalid, it means HEAD.  If START_REV is
     invalid, it means HEAD.  If END_REV is SVN_INVALID_REVNUM, we'll
     use 0. */
  if (! SVN_IS_VALID_REVNUM(peg_revision))
    {
      SVN_ERR(svn_ra_get_latest_revnum(session, &youngest_rev, pool));
      peg_revision = youngest_rev;
    }
  if (! SVN_IS_VALID_REVNUM(start_rev))
    {
      if (SVN_IS_VALID_REVNUM(youngest_rev))
        start_rev = youngest_rev;
      else
        SVN_ERR(svn_ra_get_latest_revnum(session, &start_rev, pool));
    }
  if (! SVN_IS_VALID_REVNUM(end_rev))
    {
      end_rev = 0;
    }

  /* The API demands a certain ordering of our revision inputs. Enforce it. */
  SVN_ERR_ASSERT((peg_revision >= start_rev) && (start_rev >= end_rev));

  /* Sanity check: verify that the peg-object exists in repos. */
  SVN_ERR(svn_ra_check_path(session, path, peg_revision, &kind, pool));
  if (kind == svn_node_none)
    return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
                             _("Path '%s' doesn't exist in revision %ld"),
                             fs_path, start_rev);

  /* Populate most of our log receiver baton structure. */
  lrb.kind = kind;
  lrb.last_path = fs_path;
  lrb.done = FALSE;
  lrb.start_rev = start_rev;
  lrb.range_end = start_rev;
  lrb.receiver = receiver;
  lrb.receiver_baton = receiver_baton;
  lrb.pool = pool;

  /* Let the RA layer drive our log information handler, which will do
     the work of finding the actual locations for our resource.
     Notice that we always run on the youngest rev of the 3 inputs. */
  targets = apr_array_make(pool, 1, sizeof(const char *));
  APR_ARRAY_PUSH(targets, const char *) = path;
  SVN_ERR(svn_ra_get_log2(session, targets, peg_revision, end_rev, 0,
                          TRUE, FALSE, FALSE,
                          apr_array_make(pool, 0, sizeof(const char *)),
                          gls_log_receiver, &lrb, pool));

  /* If we didn't finish, we need to do so with a final segment send. */
  if (! lrb.done)
    SVN_ERR(maybe_crop_and_send_segment(lrb.last_path, start_rev,
                                        end_rev, lrb.range_end,
                                        receiver, receiver_baton, pool));

  return SVN_NO_ERROR;
}
예제 #11
0
파일: compat.c 프로젝트: 2asoft/freebsd
svn_error_t *
svn_ra__file_revs_from_log(svn_ra_session_t *ra_session,
                           const char *path,
                           svn_revnum_t start,
                           svn_revnum_t end,
                           svn_file_rev_handler_t handler,
                           void *handler_baton,
                           apr_pool_t *pool)
{
  svn_node_kind_t kind;
  const char *repos_url, *session_url, *fs_path;
  apr_array_header_t *condensed_targets;
  struct fr_log_message_baton lmb;
  struct rev *rev;
  apr_hash_t *last_props;
  svn_stream_t *last_stream;
  apr_pool_t *currpool, *lastpool;

  /* Fetch the absolute FS path associated with PATH. */
  SVN_ERR(get_fs_path(&fs_path, ra_session, path, pool));

  /* Check to make sure we're dealing with a file. */
  SVN_ERR(svn_ra_check_path(ra_session, path, end, &kind, pool));
  if (kind == svn_node_dir)
    return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
                             _("'%s' is not a file"), fs_path);

  condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
  APR_ARRAY_PUSH(condensed_targets, const char *) = path;

  lmb.path = fs_path;
  lmb.eldest = NULL;
  lmb.pool = pool;

  /* Accumulate revision metadata by walking the revisions
     backwards; this allows us to follow moves/copies
     correctly. */
  SVN_ERR(svn_ra_get_log2(ra_session,
                          condensed_targets,
                          end, start, 0, /* no limit */
                          TRUE, FALSE, FALSE,
                          NULL, fr_log_message_receiver, &lmb,
                          pool));

  /* Reparent the session while we go back through the history. */
  SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
  SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, pool));
  SVN_ERR(svn_ra_reparent(ra_session, repos_url, pool));

  currpool = svn_pool_create(pool);
  lastpool = svn_pool_create(pool);

  /* We want the first txdelta to be against the empty file. */
  last_props = apr_hash_make(lastpool);
  last_stream = svn_stream_empty(lastpool);

  /* Walk the revision list in chronological order, downloading each fulltext,
     diffing it with its predecessor, and calling the file_revs handler for
     each one.  Use two iteration pools rather than one, because the diff
     routines need to look at a sliding window of revisions.  Two pools gives
     us a ring buffer of sorts. */
  for (rev = lmb.eldest; rev; rev = rev->next)
    {
      const char *temp_path;
      apr_pool_t *tmppool;
      apr_hash_t *props;
      apr_file_t *file;
      svn_stream_t *stream;
      apr_array_header_t *prop_diffs;
      svn_txdelta_stream_t *delta_stream;
      svn_txdelta_window_handler_t delta_handler = NULL;
      void *delta_baton = NULL;

      svn_pool_clear(currpool);

      /* Get the contents of the file from the repository, and put them in
         a temporary local file. */
      SVN_ERR(svn_stream_open_unique(&stream, &temp_path, NULL,
                                     svn_io_file_del_on_pool_cleanup,
                                     currpool, currpool));
      SVN_ERR(svn_ra_get_file(ra_session, rev->path + 1, rev->revision,
                              stream, NULL, &props, currpool));
      SVN_ERR(svn_stream_close(stream));

      /* Open up a stream to the local file. */
      SVN_ERR(svn_io_file_open(&file, temp_path, APR_READ, APR_OS_DEFAULT,
                               currpool));
      stream = svn_stream_from_aprfile2(file, FALSE, currpool);

      /* Calculate the property diff */
      SVN_ERR(svn_prop_diffs(&prop_diffs, props, last_props, lastpool));

      /* Call the file_rev handler */
      SVN_ERR(handler(handler_baton, rev->path, rev->revision, rev->props,
                      FALSE, /* merged revision */
                      &delta_handler, &delta_baton, prop_diffs, lastpool));

      /* Compute and send delta if client asked for it. */
      if (delta_handler)
        {
          /* Get the content delta. Don't calculate checksums as we don't
           * use them. */
          svn_txdelta2(&delta_stream, last_stream, stream, FALSE, lastpool);

          /* And send. */
          SVN_ERR(svn_txdelta_send_txstream(delta_stream, delta_handler,
                                            delta_baton, lastpool));
        }

      /* Switch the pools and data for the next iteration */
      tmppool = currpool;
      currpool = lastpool;
      lastpool = tmppool;

      SVN_ERR(svn_stream_close(last_stream));
      last_stream = stream;
      last_props = props;
    }

  SVN_ERR(svn_stream_close(last_stream));
  svn_pool_destroy(currpool);
  svn_pool_destroy(lastpool);

  /* Reparent the session back to the original URL. */
  return svn_ra_reparent(ra_session, session_url, pool);
}
예제 #12
0
/* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */
static svn_error_t *
ra_stat_compatible(svn_ra_session_t *ra_session,
                   svn_revnum_t rev,
                   svn_dirent_t **dirent_p,
                   apr_uint32_t dirent_fields,
                   svn_client_ctx_t *ctx,
                   apr_pool_t *pool)
{
  const char *repos_root_URL, *url;
  svn_error_t *err;
  svn_dirent_t *the_ent;

  SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_URL, pool));
  SVN_ERR(svn_ra_get_session_url(ra_session, &url, pool));

  err = svn_ra_stat(ra_session, "", rev, &the_ent, pool);

  /* svn_ra_stat() will work against old versions of mod_dav_svn, but
     not old versions of svnserve.  In the case of a pre-1.2 svnserve,
     catch the specific error it throws:*/
  if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
    {
      /* Fall back to pre-1.2 strategy for fetching dirent's URL. */
      svn_node_kind_t url_kind;
      svn_ra_session_t *parent_ra_session;
      apr_hash_t *parent_ents;
      const char *parent_url, *base_name;

      if (strcmp(url, repos_root_URL) == 0)
        {
          /* In this universe, there's simply no way to fetch
             information about the repository's root directory! */
          return err;
        }

      svn_error_clear(err);

      SVN_ERR(svn_ra_check_path(ra_session, "", rev, &url_kind, pool));
      if (url_kind == svn_node_none)
        return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                 _("URL '%s' non-existent in revision %ld"),
                                 url, rev);

      /* Open a new RA session to the item's parent. */
      svn_uri_split(&parent_url, &base_name, url, pool);
      SVN_ERR(svn_client__open_ra_session_internal(&parent_ra_session, NULL,
                                                   parent_url, NULL,
                                                   NULL, FALSE, TRUE,
                                                   ctx, pool));

      /* Get all parent's entries, and find the item's dirent in the hash. */
      SVN_ERR(svn_ra_get_dir2(parent_ra_session, &parent_ents, NULL, NULL,
                              "", rev, dirent_fields, pool));
      the_ent = apr_hash_get(parent_ents, base_name, APR_HASH_KEY_STRING);
      if (the_ent == NULL)
        return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                 _("URL '%s' non-existent in revision %ld"),
                                 url, rev);
    }
  else if (err)
    {
      return svn_error_trace(err);
    }

  *dirent_p = the_ent;
  return SVN_NO_ERROR;
}
예제 #13
0
static svn_error_t *
bench_null_export(svn_revnum_t *result_rev,
                  const char *from_path_or_url,
                  svn_opt_revision_t *peg_revision,
                  svn_opt_revision_t *revision,
                  svn_depth_t depth,
                  void *baton,
                  svn_client_ctx_t *ctx,
                  svn_boolean_t quiet,
                  apr_pool_t *pool)
{
    svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
    svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);

    SVN_ERR_ASSERT(peg_revision != NULL);
    SVN_ERR_ASSERT(revision != NULL);

    if (peg_revision->kind == svn_opt_revision_unspecified)
        peg_revision->kind = svn_path_is_url(from_path_or_url)
                             ? svn_opt_revision_head
                             : svn_opt_revision_working;

    if (revision->kind == svn_opt_revision_unspecified)
        revision = peg_revision;

    if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
    {
        svn_client__pathrev_t *loc;
        svn_ra_session_t *ra_session;
        svn_node_kind_t kind;

        /* Get the RA connection. */
        SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
                from_path_or_url, NULL,
                peg_revision,
                revision, ctx, pool));

        SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, pool));

        if (kind == svn_node_file)
        {
            apr_hash_t *props;

            /* Since you cannot actually root an editor at a file, we
             * manually drive a few functions of our editor. */

            /* Step outside the editor-likeness for a moment, to actually talk
             * to the repository. */
            /* ### note: the stream will not be closed */
            SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
                                    svn_stream_empty(pool),
                                    NULL, &props, pool));
        }
        else if (kind == svn_node_dir)
        {
            void *edit_baton = NULL;
            const svn_delta_editor_t *export_editor = NULL;
            const svn_ra_reporter3_t *reporter;
            void *report_baton;

            svn_delta_editor_t *editor = svn_delta_default_editor(pool);

            editor->set_target_revision = set_target_revision;
            editor->open_root = open_root;
            editor->add_directory = add_directory;
            editor->add_file = add_file;
            editor->apply_textdelta = apply_textdelta;
            editor->close_file = close_file;
            editor->change_file_prop = change_file_prop;
            editor->change_dir_prop = change_dir_prop;

            /* for ra_svn, we don't need an editior in quiet mode */
            if (!quiet || strncmp(loc->repos_root_url, "svn:", 4))
                SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func,
                        ctx->cancel_baton,
                        editor,
                        baton,
                        &export_editor,
                        &edit_baton,
                        pool));

            /* Manufacture a basic 'report' to the update reporter. */
            SVN_ERR(svn_ra_do_update3(ra_session,
                                      &reporter, &report_baton,
                                      loc->rev,
                                      "", /* no sub-target */
                                      depth,
                                      FALSE, /* don't want copyfrom-args */
                                      FALSE, /* don't want ignore_ancestry */
                                      export_editor, edit_baton,
                                      pool, pool));

            SVN_ERR(reporter->set_path(report_baton, "", loc->rev,
                                       /* Depth is irrelevant, as we're
                                          passing start_empty=TRUE anyway. */
                                       svn_depth_infinity,
                                       TRUE, /* "help, my dir is empty!" */
                                       NULL, pool));

            SVN_ERR(reporter->finish_report(report_baton, pool));
        }
        else if (kind == svn_node_none)
        {
            return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                     _("URL '%s' doesn't exist"),
                                     from_path_or_url);
        }
        /* kind == svn_node_unknown not handled */
    }


    if (result_rev)
        *result_rev = edit_revision;

    return SVN_NO_ERROR;
}
예제 #14
0
파일: checkout.c 프로젝트: Alkzndr/freebsd
svn_error_t *
svn_client__checkout_internal(svn_revnum_t *result_rev,
                              const char *url,
                              const char *local_abspath,
                              const svn_opt_revision_t *peg_revision,
                              const svn_opt_revision_t *revision,
                              svn_depth_t depth,
                              svn_boolean_t ignore_externals,
                              svn_boolean_t allow_unver_obstructions,
                              svn_boolean_t *timestamp_sleep,
                              svn_client_ctx_t *ctx,
                              apr_pool_t *pool)
{
  svn_node_kind_t kind;
  apr_pool_t *session_pool = svn_pool_create(pool);
  svn_ra_session_t *ra_session;
  svn_client__pathrev_t *pathrev;

  /* Sanity check.  Without these, the checkout is meaningless. */
  SVN_ERR_ASSERT(local_abspath != NULL);
  SVN_ERR_ASSERT(svn_uri_is_canonical(url, pool));
  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));

  /* 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);

  /* Get the RA connection. */
  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev,
                                            url, NULL, peg_revision, revision,
                                            ctx, session_pool));

  pathrev = svn_client__pathrev_dup(pathrev, pool);
  SVN_ERR(svn_ra_check_path(ra_session, "", pathrev->rev, &kind, pool));

  svn_pool_destroy(session_pool);

  if (kind == svn_node_none)
    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                             _("URL '%s' doesn't exist"), pathrev->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"), pathrev->url);

  SVN_ERR(svn_io_check_path(local_abspath, &kind, 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(local_abspath, pool));
      SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, pool));
    }
  else if (kind == svn_node_dir)
    {
      int wc_format;
      const char *entry_url;

      SVN_ERR(svn_wc_check_wc2(&wc_format, ctx->wc_ctx, local_abspath, pool));
      if (! wc_format)
        {
          SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, pool));
        }
      else
        {
          /* Get PATH's URL. */
          SVN_ERR(svn_wc__node_get_url(&entry_url, ctx->wc_ctx, local_abspath,
                                       pool, pool));

          /* If PATH's existing URL matches the incoming one, then
             just update.  This allows 'svn co' to restart an
             interrupted checkout.  Otherwise bail out. */
          if (strcmp(entry_url, pathrev->url) != 0)
            return svn_error_createf(
                          SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
                          _("'%s' is already a working copy for a"
                            " different URL"),
                          svn_dirent_local_style(local_abspath, pool));
        }
    }
  else
    {
      return svn_error_createf(SVN_ERR_WC_NODE_KIND_CHANGE, NULL,
                               _("'%s' already exists and is not a directory"),
                               svn_dirent_local_style(local_abspath, pool));
    }

  /* Have update fix the incompleteness. */
  SVN_ERR(svn_client__update_internal(result_rev, local_abspath,
                                      revision, depth, TRUE,
                                      ignore_externals,
                                      allow_unver_obstructions,
                                      TRUE /* adds_as_modification */,
                                      FALSE, FALSE,
                                      timestamp_sleep, ctx, pool));

  return SVN_NO_ERROR;
}
예제 #15
0
static svn_error_t *
handle_external_item_change(svn_client_ctx_t *ctx,
                            const char *repos_root_url,
                            const char *parent_dir_abspath,
                            const char *parent_dir_url,
                            const char *local_abspath,
                            const char *old_defining_abspath,
                            const svn_wc_external_item2_t *new_item,
                            svn_boolean_t *timestamp_sleep,
                            apr_pool_t *scratch_pool)
{
  svn_ra_session_t *ra_session;
  svn_client__pathrev_t *new_loc;
  const char *new_url;
  svn_node_kind_t ext_kind;

  SVN_ERR_ASSERT(repos_root_url && parent_dir_url);
  SVN_ERR_ASSERT(new_item != NULL);

  /* Don't bother to check status, since we'll get that for free by
     attempting to retrieve the hash values anyway.  */

  /* When creating the absolute URL, use the pool and not the
     iterpool, since the hash table values outlive the iterpool and
     any pointers they have should also outlive the iterpool.  */

  SVN_ERR(svn_wc__resolve_relative_external_url(&new_url,
                                                new_item, repos_root_url,
                                                parent_dir_url,
                                                scratch_pool, scratch_pool));

  /* Determine if the external is a file or directory. */
  /* Get the RA connection. */
  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
                                            new_url, NULL,
                                            &(new_item->peg_revision),
                                            &(new_item->revision), ctx,
                                            scratch_pool));

  SVN_ERR(svn_ra_check_path(ra_session, "", new_loc->rev, &ext_kind,
                            scratch_pool));

  if (svn_node_none == ext_kind)
    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                             _("URL '%s' at revision %ld doesn't exist"),
                             new_loc->url, new_loc->rev);

  if (svn_node_dir != ext_kind && svn_node_file != ext_kind)
    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                             _("URL '%s' at revision %ld is not a file "
                               "or a directory"),
                             new_loc->url, new_loc->rev);


  /* Not protecting against recursive externals.  Detecting them in
     the global case is hard, and it should be pretty obvious to a
     user when it happens.  Worst case: your disk fills up :-). */

  /* First notify that we're about to handle an external. */
  if (ctx->notify_func2)
    {
      (*ctx->notify_func2)(
         ctx->notify_baton2,
         svn_wc_create_notify(local_abspath,
                              svn_wc_notify_update_external,
                              scratch_pool),
         scratch_pool);
    }

  if (! old_defining_abspath)
    {
      /* The target dir might have multiple components.  Guarantee the path
         leading down to the last component. */
      SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath,
                                                             scratch_pool),
                                          scratch_pool));
    }

  switch (ext_kind)
    {
      case svn_node_dir:
        SVN_ERR(switch_dir_external(local_abspath, new_loc->url,
                                    new_item->url,
                                    &(new_item->peg_revision),
                                    &(new_item->revision),
                                    parent_dir_abspath,
                                    timestamp_sleep, ctx,
                                    scratch_pool));
        break;
      case svn_node_file:
        if (strcmp(repos_root_url, new_loc->repos_root_url))
          {
            const char *local_repos_root_url;
            const char *local_repos_uuid;
            const char *ext_repos_relpath;
            svn_error_t *err;

            /*
             * The working copy library currently requires that all files
             * in the working copy have the same repository root URL.
             * The URL from the file external's definition differs from the
             * one used by the working copy. As a workaround, replace the
             * root URL portion of the file external's URL, after making
             * sure both URLs point to the same repository. See issue #4087.
             */

            err = svn_wc__node_get_repos_info(NULL, NULL,
                                              &local_repos_root_url,
                                              &local_repos_uuid,
                                              ctx->wc_ctx, parent_dir_abspath,
                                              scratch_pool, scratch_pool);
            if (err)
              {
                if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
                    && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
                  return svn_error_trace(err);

                svn_error_clear(err);
                local_repos_root_url = NULL;
                local_repos_uuid = NULL;
              }

            ext_repos_relpath = svn_uri_skip_ancestor(new_loc->repos_root_url,
                                                      new_url, scratch_pool);
            if (local_repos_uuid == NULL || local_repos_root_url == NULL ||
                ext_repos_relpath == NULL ||
                strcmp(local_repos_uuid, new_loc->repos_uuid) != 0)
              return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                        _("Unsupported external: URL of file external '%s' "
                          "is not in repository '%s'"),
                        new_url, repos_root_url);

            new_url = svn_path_url_add_component2(local_repos_root_url,
                                                  ext_repos_relpath,
                                                  scratch_pool);
            SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
                                                      new_url,
                                                      NULL,
                                                      &(new_item->peg_revision),
                                                      &(new_item->revision),
                                                      ctx, scratch_pool));
          }

        SVN_ERR(switch_file_external(local_abspath,
                                     new_url,
                                     &new_item->peg_revision,
                                     &new_item->revision,
                                     parent_dir_abspath,
                                     ra_session,
                                     ctx,
                                     scratch_pool));
        break;

      default:
        SVN_ERR_MALFUNCTION();
        break;
    }

  return SVN_NO_ERROR;
}
예제 #16
0
svn_error_t *
svn_client__copy_foreign(const char *url,
                         const char *dst_abspath,
                         svn_opt_revision_t *peg_revision,
                         svn_opt_revision_t *revision,
                         svn_depth_t depth,
                         svn_boolean_t make_parents,
                         svn_boolean_t already_locked,
                         svn_client_ctx_t *ctx,
                         apr_pool_t *scratch_pool)
{
  svn_ra_session_t *ra_session;
  svn_client__pathrev_t *loc;
  svn_node_kind_t kind;
  svn_node_kind_t wc_kind;
  const char *dir_abspath;

  SVN_ERR_ASSERT(svn_path_is_url(url));
  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));

  /* Do we need to validate/update revisions? */

  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
                                            url, NULL,
                                            peg_revision,
                                            revision, ctx,
                                            scratch_pool));

  SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, scratch_pool));

  if (kind != svn_node_file && kind != svn_node_dir)
    return svn_error_createf(
                SVN_ERR_ILLEGAL_TARGET, NULL,
                _("'%s' is not a valid location inside a repository"),
                url);

  SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dst_abspath, FALSE, TRUE,
                            scratch_pool));

  if (wc_kind != svn_node_none)
    {
      return svn_error_createf(
                SVN_ERR_ENTRY_EXISTS, NULL,
                _("'%s' is already under version control"),
                svn_dirent_local_style(dst_abspath, scratch_pool));
    }

  dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
  SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dir_abspath,
                            FALSE, FALSE, scratch_pool));

  if (wc_kind == svn_node_none)
    {
      if (make_parents)
        SVN_ERR(svn_client__make_local_parents(dir_abspath, make_parents, ctx,
                                               scratch_pool));

      SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dir_abspath,
                                FALSE, FALSE, scratch_pool));
    }

  if (wc_kind != svn_node_dir)
    return svn_error_createf(
                SVN_ERR_ENTRY_NOT_FOUND, NULL,
                _("Can't add '%s', because no parent directory is found"),
                svn_dirent_local_style(dst_abspath, scratch_pool));


  if (kind == svn_node_file)
    {
      svn_stream_t *target;
      apr_hash_t *props;
      apr_hash_index_t *hi;
      SVN_ERR(svn_stream_open_writable(&target, dst_abspath, scratch_pool,
                                       scratch_pool));

      SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, target, NULL, &props,
                              scratch_pool));

      if (props != NULL)
        for (hi = apr_hash_first(scratch_pool, props); hi;
             hi = apr_hash_next(hi))
          {
            const char *name = apr_hash_this_key(hi);

            if (svn_property_kind2(name) != svn_prop_regular_kind
                || ! strcmp(name, SVN_PROP_MERGEINFO))
              {
                /* We can't handle DAV, ENTRY and merge specific props here */
                svn_hash_sets(props, name, NULL);
              }
          }

      if (!already_locked)
        SVN_WC__CALL_WITH_WRITE_LOCK(
              svn_wc_add_from_disk3(ctx->wc_ctx, dst_abspath, props,
                                    TRUE /* skip checks */,
                                    ctx->notify_func2, ctx->notify_baton2,
                                    scratch_pool),
              ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
      else
        SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, dst_abspath, props,
                                      TRUE /* skip checks */,
                                      ctx->notify_func2, ctx->notify_baton2,
                                      scratch_pool));
    }
  else
    {
      if (!already_locked)
        SVN_WC__CALL_WITH_WRITE_LOCK(
              copy_foreign_dir(ra_session, loc,
                               ctx->wc_ctx, dst_abspath,
                               depth,
                               ctx->notify_func2, ctx->notify_baton2,
                               ctx->cancel_func, ctx->cancel_baton,
                               scratch_pool),
              ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
      else
        SVN_ERR(copy_foreign_dir(ra_session, loc,
                                 ctx->wc_ctx, dst_abspath,
                                 depth,
                                 ctx->notify_func2, ctx->notify_baton2,
                                 ctx->cancel_func, ctx->cancel_baton,
                                 scratch_pool));
    }

  return SVN_NO_ERROR;
}
예제 #17
0
/* ### TODO: Handle depth. */
static svn_error_t *
diff_deleted_dir(const char *dir,
                 svn_revnum_t revision,
                 svn_ra_session_t *ra_session,
                 void *edit_baton,
                 svn_cancel_func_t cancel_func,
                 void *cancel_baton,
                 apr_pool_t *pool)
{
  struct edit_baton *eb = edit_baton;
  apr_hash_t *dirents;
  apr_pool_t *iterpool = svn_pool_create(pool);
  apr_hash_index_t *hi;

  if (cancel_func)
    SVN_ERR(cancel_func(cancel_baton));

  SVN_ERR(svn_ra_get_dir2(ra_session,
                          &dirents,
                          NULL, NULL,
                          dir,
                          revision,
                          SVN_DIRENT_KIND,
                          pool));

  for (hi = apr_hash_first(pool, dirents); hi;
       hi = apr_hash_next(hi))
    {
      const char *path;
      const char *name = svn__apr_hash_index_key(hi);
      svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
      svn_node_kind_t kind;
      svn_client_diff_summarize_t *sum;

      svn_pool_clear(iterpool);

      path = svn_relpath_join(dir, name, iterpool);

      SVN_ERR(svn_ra_check_path(eb->ra_session,
                                path,
                                eb->revision,
                                &kind,
                                iterpool));

      sum = apr_pcalloc(iterpool, sizeof(*sum));
      sum->summarize_kind = svn_client_diff_summarize_kind_deleted;
      sum->path = path;
      sum->node_kind = kind;

      SVN_ERR(eb->summarize_func(sum,
                                 eb->summarize_func_baton,
                                 iterpool));

      if (dirent->kind == svn_node_dir)
        SVN_ERR(diff_deleted_dir(path,
                                 revision,
                                 ra_session,
                                 eb,
                                 cancel_func,
                                 cancel_baton,
                                 iterpool));
    }

  svn_pool_destroy(iterpool);
  return SVN_NO_ERROR;
}
예제 #18
0
/* An svn_delta_editor_t function.  */
static svn_error_t *
delete_entry(const char *path,
             svn_revnum_t base_revision,
             void *parent_baton,
             apr_pool_t *pool)
{
  struct dir_baton *pb = parent_baton;
  struct edit_baton *eb = pb->edit_baton;
  svn_node_kind_t kind;
  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
  svn_wc_notify_action_t action = svn_wc_notify_skip;
  svn_boolean_t tree_conflicted = FALSE;
  apr_pool_t *scratch_pool;

  /* Skip *everything* within a newly tree-conflicted directory,
   * and directories the children of which should be skipped. */
  if (pb->skip || pb->tree_conflicted || pb->skip_children)
    return SVN_NO_ERROR;

  scratch_pool = svn_pool_create(eb->pool);

  /* We need to know if this is a directory or a file */
  SVN_ERR(svn_ra_check_path(eb->ra_session, path, eb->revision, &kind,
                            scratch_pool));

  switch (kind)
    {
    case svn_node_file:
      {
        SVN_ERR(diff_deleted_file(&state, &tree_conflicted, path, eb,
                                  scratch_pool));
        break;
      }
    case svn_node_dir:
      {
        SVN_ERR(diff_deleted_dir(&state, &tree_conflicted, path, eb,
                                 scratch_pool));
        break;
      }
    default:
      break;
    }

  if ((state != svn_wc_notify_state_missing)
      && (state != svn_wc_notify_state_obstructed)
      && !tree_conflicted)
    {
      action = svn_wc_notify_update_delete;
    }

  if (eb->notify_func)
    {
      const char *deleted_path = apr_pstrdup(eb->pool, path);
      deleted_path_notify_t *dpn = apr_pcalloc(eb->pool, sizeof(*dpn));

      dpn->kind = kind;
      dpn->action = tree_conflicted ? svn_wc_notify_tree_conflict : action;
      dpn->state = state;
      dpn->tree_conflicted = tree_conflicted;
      apr_hash_set(eb->deleted_paths, deleted_path, APR_HASH_KEY_STRING, dpn);
    }

  svn_pool_destroy(scratch_pool);

  return SVN_NO_ERROR;
}
예제 #19
0
/* Note: this implementation is very similar to svn_client_proplist. */
svn_error_t *
svn_client_propget4(apr_hash_t **props,
                    const char *propname,
                    const char *target,
                    const svn_opt_revision_t *peg_revision,
                    const svn_opt_revision_t *revision,
                    svn_revnum_t *actual_revnum,
                    svn_depth_t depth,
                    const apr_array_header_t *changelists,
                    svn_client_ctx_t *ctx,
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
{
  svn_revnum_t revnum;

  SVN_ERR(error_if_wcprop_name(propname));
  if (!svn_path_is_url(target))
    SVN_ERR_ASSERT(svn_dirent_is_absolute(target));

  peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision,
                                                        target);
  revision = svn_cl__rev_default_to_peg(revision, peg_revision);

  *props = apr_hash_make(result_pool);

  if (! svn_path_is_url(target)
      && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind)
      && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
    {
      svn_node_kind_t kind;
      svn_boolean_t pristine;
      svn_error_t *err;

      /* If FALSE, we want the working revision. */
      pristine = (revision->kind == svn_opt_revision_committed
                  || revision->kind == svn_opt_revision_base);

      SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, target, FALSE,
                               scratch_pool));

      if (kind == svn_node_unknown || kind == svn_node_none)
        {
          /* svn uses SVN_ERR_UNVERSIONED_RESOURCE as warning only
             for this function. */
          return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
                                   _("'%s' is not under version control"),
                                   svn_dirent_local_style(target,
                                                          scratch_pool));
        }

      err = svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx,
                                            target, NULL, revision,
                                            scratch_pool);
      if (err && err->apr_err == SVN_ERR_CLIENT_BAD_REVISION)
        {
          svn_error_clear(err);
          revnum = SVN_INVALID_REVNUM;
        }
      else if (err)
        return svn_error_trace(err);

      SVN_ERR(get_prop_from_wc(*props, propname, target,
                               pristine, kind,
                               depth, changelists, ctx, scratch_pool,
                               result_pool));
    }
  else
    {
      const char *url;
      svn_ra_session_t *ra_session;
      svn_node_kind_t kind;

      /* Get an RA plugin for this filesystem object. */
      SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum,
                                               &url, target, NULL,
                                               peg_revision,
                                               revision, ctx, scratch_pool));

      SVN_ERR(svn_ra_check_path(ra_session, "", revnum, &kind, scratch_pool));

      SVN_ERR(remote_propget(*props, propname, url, "",
                             kind, revnum, ra_session,
                             depth, result_pool, scratch_pool));
    }

  if (actual_revnum)
    *actual_revnum = revnum;
  return SVN_NO_ERROR;
}
예제 #20
0
svn_error_t *
svn_client_proplist3(const char *path_or_url,
                     const svn_opt_revision_t *peg_revision,
                     const svn_opt_revision_t *revision,
                     svn_depth_t depth,
                     const apr_array_header_t *changelists,
                     svn_proplist_receiver_t receiver,
                     void *receiver_baton,
                     svn_client_ctx_t *ctx,
                     apr_pool_t *pool)
{
  const char *url;

  peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision,
                                                        path_or_url);
  revision = svn_cl__rev_default_to_peg(revision, peg_revision);

  if (depth == svn_depth_unknown)
    depth = svn_depth_empty;

  if (! svn_path_is_url(path_or_url)
      && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind)
      && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
    {
      svn_boolean_t pristine;
      svn_node_kind_t kind;
      apr_hash_t *changelist_hash = NULL;
      const char *local_abspath;

      SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url, pool));

      pristine = ((revision->kind == svn_opt_revision_committed)
                  || (revision->kind == svn_opt_revision_base));

      SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, local_abspath, FALSE,
                               pool));

      if (kind == svn_node_unknown || kind == svn_node_none)
        {
          /* svn uses SVN_ERR_UNVERSIONED_RESOURCE as warning only
             for this function. */
          return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
                                   _("'%s' is not under version control"),
                                   svn_dirent_local_style(local_abspath,
                                                          pool));
        }

      if (changelists && changelists->nelts)
        SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash,
                                           changelists, pool));

      /* Fetch, recursively or not. */
      if (kind == svn_node_dir)
        {
          struct recursive_proplist_receiver_baton rb;

          rb.wc_ctx = ctx->wc_ctx;
          rb.wrapped_receiver = receiver;
          rb.wrapped_receiver_baton = receiver_baton;

          if (strcmp(path_or_url, local_abspath) != 0)
            {
              rb.anchor = path_or_url;
              rb.anchor_abspath = local_abspath;
            }
          else
            {
              rb.anchor = NULL;
              rb.anchor_abspath = NULL;
            }

          SVN_ERR(svn_wc__prop_list_recursive(ctx->wc_ctx, local_abspath, NULL,
                                              depth,
                                              FALSE, pristine, changelists,
                                              recursive_proplist_receiver, &rb,
                                              ctx->cancel_func,
                                              ctx->cancel_baton, pool));
        }
      else if (svn_wc__changelist_match(ctx->wc_ctx, local_abspath,
                                        changelist_hash, pool))
        {
          apr_hash_t *hash;

          SVN_ERR(pristine_or_working_props(&hash, ctx->wc_ctx, local_abspath,
                                            pristine, pool, pool));
          SVN_ERR(call_receiver(path_or_url, hash,
                                receiver, receiver_baton, pool));

        }
    }
  else /* remote target */
    {
      svn_ra_session_t *ra_session;
      svn_node_kind_t kind;
      apr_pool_t *subpool = svn_pool_create(pool);
      svn_revnum_t revnum;

      /* Get an RA session for this URL. */
      SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum,
                                               &url, path_or_url, NULL,
                                               peg_revision,
                                               revision, ctx, pool));

      SVN_ERR(svn_ra_check_path(ra_session, "", revnum, &kind, pool));

      SVN_ERR(remote_proplist(url, "", kind, revnum, ra_session, depth,
                              receiver, receiver_baton, pool, subpool));
      svn_pool_destroy(subpool);
    }

  return SVN_NO_ERROR;
}
예제 #21
0
svn_error_t *
svn_client_status6(svn_revnum_t *result_rev,
                   svn_client_ctx_t *ctx,
                   const char *path,
                   const svn_opt_revision_t *revision,
                   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,
                   svn_boolean_t ignore_externals,
                   svn_boolean_t depth_as_sticky,
                   const apr_array_header_t *changelists,
                   svn_client_status_func_t status_func,
                   void *status_baton,
                   apr_pool_t *pool)  /* ### aka scratch_pool */
{
    struct status_baton sb;
    const char *dir, *dir_abspath;
    const char *target_abspath;
    const char *target_basename;
    apr_array_header_t *ignores;
    svn_error_t *err;
    apr_hash_t *changelist_hash = NULL;

    /* Override invalid combinations of the check_out_of_date and
       check_working_copy flags. */
    if (!check_out_of_date)
        check_working_copy = TRUE;

    if (svn_path_is_url(path))
        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
                                 _("'%s' is not a local path"), path);

    if (changelists && changelists->nelts)
        SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));

    if (result_rev)
        *result_rev = SVN_INVALID_REVNUM;

    sb.real_status_func = status_func;
    sb.real_status_baton = status_baton;
    sb.deleted_in_repos = FALSE;
    sb.changelist_hash = changelist_hash;
    sb.wc_ctx = ctx->wc_ctx;

    SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool));

    if (check_out_of_date)
    {
        /* The status editor only works on directories, so get the ancestor
           if necessary */

        svn_node_kind_t kind;

        SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath,
                                  TRUE, FALSE, pool));

        /* Dir must be a working copy directory or the status editor fails */
        if (kind == svn_node_dir)
        {
            dir_abspath = target_abspath;
            target_basename = "";
            dir = path;
        }
        else
        {
            dir_abspath = svn_dirent_dirname(target_abspath, pool);
            target_basename = svn_dirent_basename(target_abspath, NULL);
            dir = svn_dirent_dirname(path, pool);

            if (kind == svn_node_file)
            {
                if (depth == svn_depth_empty)
                    depth = svn_depth_files;
            }
            else
            {
                err = svn_wc_read_kind2(&kind, ctx->wc_ctx, dir_abspath,
                                        FALSE, FALSE, pool);

                svn_error_clear(err);

                if (err || kind != svn_node_dir)
                {
                    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
                                             _("'%s' is not a working copy"),
                                             svn_dirent_local_style(path, pool));
                }
            }
        }
    }
    else
    {
        dir = path;
        dir_abspath = target_abspath;
    }

    if (svn_dirent_is_absolute(dir))
    {
        sb.anchor_abspath = NULL;
        sb.anchor_relpath = NULL;
    }
    else
    {
        sb.anchor_abspath = dir_abspath;
        sb.anchor_relpath = dir;
    }

    /* Get the status edit, and use our wrapping status function/baton
       as the callback pair. */
    SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));

    /* If we want to know about out-of-dateness, we crawl the working copy and
       let the RA layer drive the editor for real.  Otherwise, we just close the
       edit.  :-) */
    if (check_out_of_date)
    {
        svn_ra_session_t *ra_session;
        const char *URL;
        svn_node_kind_t kind;
        svn_boolean_t server_supports_depth;
        const svn_delta_editor_t *editor;
        void *edit_baton, *set_locks_baton;
        svn_revnum_t edit_revision = SVN_INVALID_REVNUM;

        /* Get full URL from the ANCHOR. */
        SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx,
                                          pool, pool));

        if (!URL)
            return svn_error_createf
                   (SVN_ERR_ENTRY_MISSING_URL, NULL,
                    _("Entry '%s' has no URL"),
                    svn_dirent_local_style(dir, pool));

        /* Open a repository session to the URL. */
        SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL,
                dir_abspath, NULL,
                FALSE, TRUE,
                ctx, pool, pool));

        SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
                                      SVN_RA_CAPABILITY_DEPTH, pool));

        SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton,
                                          &edit_revision, ctx->wc_ctx,
                                          dir_abspath, target_basename,
                                          depth, get_all, check_working_copy,
                                          no_ignore, depth_as_sticky,
                                          server_supports_depth,
                                          ignores, tweak_status, &sb,
                                          ctx->cancel_func, ctx->cancel_baton,
                                          pool, pool));


        /* Verify that URL exists in HEAD.  If it doesn't, this can save
           us a whole lot of hassle; if it does, the cost of this
           request should be minimal compared to the size of getting
           back the average amount of "out-of-date" information. */
        SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM,
                                  &kind, pool));
        if (kind == svn_node_none)
        {
            svn_boolean_t added;

            /* Our status target does not exist in HEAD.  If we've got
               it locally added, that's okay.  But if it was previously
               versioned, then it must have since been deleted from the
               repository.  (Note that "locally replaced" doesn't count
               as "added" in this case.)  */
            SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx,
                                          dir_abspath, pool));
            if (! added)
                sb.deleted_in_repos = TRUE;

            /* And now close the edit. */
            SVN_ERR(editor->close_edit(edit_baton, pool));
        }
        else
        {
            svn_revnum_t revnum;
            report_baton_t rb;
            svn_depth_t status_depth;

            if (revision->kind == svn_opt_revision_head)
            {
                /* Cause the revision number to be omitted from the request,
                   which implies HEAD. */
                revnum = SVN_INVALID_REVNUM;
            }
            else
            {
                /* Get a revision number for our status operation. */
                SVN_ERR(svn_client__get_revision_number(&revnum, NULL,
                                                        ctx->wc_ctx,
                                                        target_abspath,
                                                        ra_session, revision,
                                                        pool));
            }

            if (depth_as_sticky || !server_supports_depth)
                status_depth = depth;
            else
                status_depth = svn_depth_unknown; /* Use depth from WC */

            /* Do the deed.  Let the RA layer drive the status editor. */
            SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
                                      &rb.wrapped_report_baton,
                                      target_basename, revnum, status_depth,
                                      editor, edit_baton, pool));

            /* Init the report baton. */
            rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */
            rb.set_locks_baton = set_locks_baton;
            rb.ctx = ctx;
            rb.pool = pool;

            if (depth == svn_depth_unknown)
                rb.depth = svn_depth_infinity;
            else
                rb.depth = depth;

            /* Drive the reporter structure, describing the revisions
               within PATH.  When we call reporter->finish_report,
               EDITOR will be driven to describe differences between our
               working copy and HEAD. */
            SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx,
                                            target_abspath,
                                            &lock_fetch_reporter, &rb,
                                            FALSE /* restore_files */,
                                            depth, (! depth_as_sticky),
                                            (! server_supports_depth),
                                            FALSE /* use_commit_times */,
                                            ctx->cancel_func, ctx->cancel_baton,
                                            NULL, NULL, pool));
        }

        if (ctx->notify_func2)
        {
            svn_wc_notify_t *notify
                = svn_wc_create_notify(target_abspath,
                                       svn_wc_notify_status_completed, pool);
            notify->revision = edit_revision;
            ctx->notify_func2(ctx->notify_baton2, notify, pool);
        }

        /* If the caller wants the result revision, give it to them. */
        if (result_rev)
            *result_rev = edit_revision;
    }
    else
    {
        err = svn_wc_walk_status(ctx->wc_ctx, target_abspath,
                                 depth, get_all, no_ignore, FALSE, ignores,
                                 tweak_status, &sb,
                                 ctx->cancel_func, ctx->cancel_baton,
                                 pool);

        if (err && err->apr_err == SVN_ERR_WC_MISSING)
        {
            /* This error code is checked for in svn to continue after
               this error */
            svn_error_clear(err);
            return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
                                     _("'%s' is not a working copy"),
                                     svn_dirent_local_style(path, pool));
        }

        SVN_ERR(err);
    }

    /* We only descend into an external if depth is svn_depth_infinity or
       svn_depth_unknown.  However, there are conceivable behaviors that
       would involve descending under other circumstances; thus, we pass
       depth anyway, so the code will DTRT if we change the conditional
       in the future.
    */
    if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
    {
        apr_hash_t *external_map;
        SVN_ERR(svn_wc__externals_defined_below(&external_map,
                                                ctx->wc_ctx, target_abspath,
                                                pool, pool));


        SVN_ERR(do_external_status(ctx, external_map,
                                   depth, get_all,
                                   check_out_of_date, check_working_copy,
                                   no_ignore, changelists,
                                   sb.anchor_abspath, sb.anchor_relpath,
                                   status_func, status_baton, pool));
    }

    return SVN_NO_ERROR;
}
예제 #22
0
파일: status.c 프로젝트: aosm/subversion
svn_error_t *
svn_client_status4(svn_revnum_t *result_rev,
                   const char *path,
                   const svn_opt_revision_t *revision,
                   svn_wc_status_func3_t status_func,
                   void *status_baton,
                   svn_depth_t depth,
                   svn_boolean_t get_all,
                   svn_boolean_t update,
                   svn_boolean_t no_ignore,
                   svn_boolean_t ignore_externals,
                   const apr_array_header_t *changelists,
                   svn_client_ctx_t *ctx,
                   apr_pool_t *pool)
{
  svn_wc_adm_access_t *anchor_access, *target_access;
  svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info(pool);
  const char *anchor, *target;
  const svn_delta_editor_t *editor;
  void *edit_baton, *set_locks_baton;
  const svn_wc_entry_t *entry = NULL;
  struct status_baton sb;
  apr_array_header_t *ignores;
  svn_error_t *err;
  apr_hash_t *changelist_hash = NULL;
  svn_revnum_t edit_revision = SVN_INVALID_REVNUM;

  if (changelists && changelists->nelts)
    SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));

  sb.real_status_func = status_func;
  sb.real_status_baton = status_baton;
  sb.deleted_in_repos = FALSE;
  sb.changelist_hash = changelist_hash;

  /* Try to open the target directory. If the target is a file or an
     unversioned directory, open the parent directory instead */
  err = svn_wc_adm_open3(&anchor_access, NULL, path, FALSE,
                         SVN_DEPTH_IS_RECURSIVE(depth) ? -1 : 1,
                         ctx->cancel_func, ctx->cancel_baton,
                         pool);
  if (err && err->apr_err == SVN_ERR_WC_NOT_DIRECTORY)
    {
      svn_error_clear(err);
      SVN_ERR(svn_wc_adm_open_anchor(&anchor_access, &target_access, &target,
                                     path, FALSE,
                                     SVN_DEPTH_IS_RECURSIVE(depth) ? -1 : 1,
                                     ctx->cancel_func, ctx->cancel_baton,
                                     pool));
    }
  else if (!err)
    {
      target = "";
      target_access = anchor_access;
    }
  else
    return err;

  anchor = svn_wc_adm_access_path(anchor_access);

  /* Get the status edit, and use our wrapping status function/baton
     as the callback pair. */
  SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));
  SVN_ERR(svn_wc_get_status_editor4(&editor, &edit_baton, &set_locks_baton,
                                    &edit_revision, anchor_access, target,
                                    depth, get_all, no_ignore, ignores,
                                    tweak_status, &sb, ctx->cancel_func,
                                    ctx->cancel_baton, traversal_info,
                                    pool));

  /* If we want to know about out-of-dateness, we crawl the working copy and
     let the RA layer drive the editor for real.  Otherwise, we just close the
     edit.  :-) */
  if (update)
    {
      svn_ra_session_t *ra_session;
      const char *URL;
      svn_node_kind_t kind;
      svn_boolean_t server_supports_depth;

      /* Get full URL from the ANCHOR. */
      if (! entry)
        SVN_ERR(svn_wc__entry_versioned(&entry, anchor, anchor_access, FALSE,
                                        pool));
      if (! entry->url)
        return svn_error_createf
          (SVN_ERR_ENTRY_MISSING_URL, NULL,
           _("Entry '%s' has no URL"),
           svn_path_local_style(anchor, pool));
      URL = apr_pstrdup(pool, entry->url);

      /* Open a repository session to the URL. */
      SVN_ERR(svn_client__open_ra_session_internal(&ra_session, URL, anchor,
                                                   anchor_access, NULL,
                                                   FALSE, TRUE,
                                                   ctx, pool));

      /* Verify that URL exists in HEAD.  If it doesn't, this can save
         us a whole lot of hassle; if it does, the cost of this
         request should be minimal compared to the size of getting
         back the average amount of "out-of-date" information. */
      SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM,
                                &kind, pool));
      if (kind == svn_node_none)
        {
          /* Our status target does not exist in HEAD of the
             repository.  If we're just adding this thing, that's
             fine.  But if it was previously versioned, then it must
             have been deleted from the repository. */
          if (entry->schedule != svn_wc_schedule_add)
            sb.deleted_in_repos = TRUE;

          /* And now close the edit. */
          SVN_ERR(editor->close_edit(edit_baton, pool));
        }
      else
        {
          svn_revnum_t revnum;
          report_baton_t rb;

          if (revision->kind == svn_opt_revision_head)
            {
              /* Cause the revision number to be omitted from the request,
                 which implies HEAD. */
              revnum = SVN_INVALID_REVNUM;
            }
          else
            {
              /* Get a revision number for our status operation. */
              SVN_ERR(svn_client__get_revision_number
                      (&revnum, NULL, ra_session, revision, target, pool));
            }

          /* Do the deed.  Let the RA layer drive the status editor. */
          SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
                                    &rb.wrapped_report_baton,
                                    target, revnum, depth, editor,
                                    edit_baton, pool));

          /* Init the report baton. */
          rb.ancestor = apr_pstrdup(pool, URL);
          rb.set_locks_baton = set_locks_baton;
          rb.ctx = ctx;
          rb.pool = pool;

          SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
                                        SVN_RA_CAPABILITY_DEPTH, pool));

          /* Drive the reporter structure, describing the revisions
             within PATH.  When we call reporter->finish_report,
             EDITOR will be driven to describe differences between our
             working copy and HEAD. */
          SVN_ERR(svn_wc_crawl_revisions4(path, target_access,
                                          &lock_fetch_reporter, &rb, FALSE,
                                          depth, TRUE, (! server_supports_depth),
                                          FALSE, NULL, NULL, NULL, pool));
        }
    }
  else
    {
      SVN_ERR(editor->close_edit(edit_baton, pool));
    }

  if (ctx->notify_func2 && update)
    {
      svn_wc_notify_t *notify
        = svn_wc_create_notify(path, svn_wc_notify_status_completed, pool);
      notify->revision = edit_revision;
      (ctx->notify_func2)(ctx->notify_baton2, notify, pool);
    }

  /* If the caller wants the result revision, give it to them. */
  if (result_rev)
    *result_rev = edit_revision;

  /* Close the access baton here, as svn_client__do_external_status()
     calls back into this function and thus will be re-opening the
     working copy. */
  SVN_ERR(svn_wc_adm_close2(anchor_access, pool));

  /* If there are svn:externals set, we don't want those to show up as
     unversioned or unrecognized, so patch up the hash.  If caller wants
     all the statuses, we will change unversioned status items that
     are interesting to an svn:externals property to
     svn_wc_status_unversioned, otherwise we'll just remove the status
     item altogether.

     We only descend into an external if depth is svn_depth_infinity or
     svn_depth_unknown.  However, there are conceivable behaviors that
     would involve descending under other circumstances; thus, we pass
     depth anyway, so the code will DTRT if we change the conditional
     in the future.
  */
  if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
    SVN_ERR(svn_client__do_external_status(traversal_info, status_func,
                                           status_baton, depth, get_all,
                                           update, no_ignore, ctx, pool));

  return SVN_NO_ERROR;
}
예제 #23
0
파일: compat.c 프로젝트: 2asoft/freebsd
svn_error_t *
svn_ra__locations_from_log(svn_ra_session_t *session,
                           apr_hash_t **locations_p,
                           const char *path,
                           svn_revnum_t peg_revision,
                           const apr_array_header_t *location_revisions,
                           apr_pool_t *pool)
{
  apr_hash_t *locations = apr_hash_make(pool);
  struct log_receiver_baton lrb = { 0 };
  apr_array_header_t *targets;
  svn_revnum_t youngest_requested, oldest_requested, youngest, oldest;
  svn_node_kind_t kind;
  const char *fs_path;
  apr_array_header_t *sorted_location_revisions;

  /* Fetch the absolute FS path associated with PATH. */
  SVN_ERR(get_fs_path(&fs_path, session, path, pool));

  /* Sanity check: verify that the peg-object exists in repos. */
  SVN_ERR(svn_ra_check_path(session, path, peg_revision, &kind, pool));
  if (kind == svn_node_none)
    return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
                             _("Path '%s' doesn't exist in revision %ld"),
                             fs_path, peg_revision);

  /* Easy out: no location revisions. */
  if (! location_revisions->nelts)
    {
      *locations_p = locations;
      return SVN_NO_ERROR;
    }

  /* Figure out the youngest and oldest revs (amongst the set of
     requested revisions + the peg revision) so we can avoid
     unnecessary log parsing. */
  sorted_location_revisions = apr_array_copy(pool, location_revisions);
  svn_sort__array(sorted_location_revisions, compare_revisions);
  oldest_requested = APR_ARRAY_IDX(sorted_location_revisions, 0, svn_revnum_t);
  youngest_requested = APR_ARRAY_IDX(sorted_location_revisions,
                                     sorted_location_revisions->nelts - 1,
                                     svn_revnum_t);
  youngest = peg_revision;
  youngest = (oldest_requested > youngest) ? oldest_requested : youngest;
  youngest = (youngest_requested > youngest) ? youngest_requested : youngest;
  oldest = peg_revision;
  oldest = (oldest_requested < oldest) ? oldest_requested : oldest;
  oldest = (youngest_requested < oldest) ? youngest_requested : oldest;

  /* Populate most of our log receiver baton structure. */
  lrb.kind = kind;
  lrb.last_path = fs_path;
  lrb.location_revisions = apr_array_copy(pool, sorted_location_revisions);
  lrb.peg_revision = peg_revision;
  lrb.peg_path = NULL;
  lrb.locations = locations;
  lrb.pool = pool;

  /* Let the RA layer drive our log information handler, which will do
     the work of finding the actual locations for our resource.
     Notice that we always run on the youngest rev of the 3 inputs. */
  targets = apr_array_make(pool, 1, sizeof(const char *));
  APR_ARRAY_PUSH(targets, const char *) = path;
  SVN_ERR(svn_ra_get_log2(session, targets, youngest, oldest, 0,
                          TRUE, FALSE, FALSE,
                          apr_array_make(pool, 0, sizeof(const char *)),
                          log_receiver, &lrb, pool));

  /* If the received log information did not cover any of the
     requested revisions, use the last known path.  (This normally
     just means that FS_PATH was not modified between the requested
     revision and OLDEST.  If the file was created at some point after
     OLDEST, then lrb.last_path should be NULL.) */
  if (! lrb.peg_path)
    lrb.peg_path = lrb.last_path;
  if (lrb.last_path)
    {
      int i;
      for (i = 0; i < sorted_location_revisions->nelts; i++)
        {
          svn_revnum_t rev = APR_ARRAY_IDX(sorted_location_revisions, i,
                                           svn_revnum_t);
          if (! apr_hash_get(locations, &rev, sizeof(rev)))
            apr_hash_set(locations, apr_pmemdup(pool, &rev, sizeof(rev)),
                         sizeof(rev), apr_pstrdup(pool, lrb.last_path));
        }
    }

  /* Check that we got the peg path. */
  if (! lrb.peg_path)
    return svn_error_createf
      (APR_EGENERAL, NULL,
       _("Unable to find repository location for '%s' in revision %ld"),
       fs_path, peg_revision);

  /* Sanity check: make sure that our calculated peg path is the same
     as what we expected it to be. */
  if (strcmp(fs_path, lrb.peg_path) != 0)
    return svn_error_createf
      (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
       _("'%s' in revision %ld is an unrelated object"),
       fs_path, youngest);

  *locations_p = locations;
  return SVN_NO_ERROR;
}
예제 #24
0
svn_error_t *
svn_client_info3(const char *abspath_or_url,
                 const svn_opt_revision_t *peg_revision,
                 const svn_opt_revision_t *revision,
                 svn_depth_t depth,
                 svn_boolean_t fetch_excluded,
                 svn_boolean_t fetch_actual_only,
                 const apr_array_header_t *changelists,
                 svn_client_info_receiver2_t receiver,
                 void *receiver_baton,
                 svn_client_ctx_t *ctx,
                 apr_pool_t *pool)
{
  svn_ra_session_t *ra_session, *parent_ra_session;
  svn_revnum_t rev;
  const char *url;
  svn_node_kind_t url_kind;
  const char *repos_root_URL, *repos_UUID;
  svn_lock_t *lock;
  svn_boolean_t related;
  apr_hash_t *parent_ents;
  const char *parent_url, *base_name;
  svn_dirent_t *the_ent;
  svn_client_info2_t *info;
  svn_error_t *err;

  if (depth == svn_depth_unknown)
    depth = svn_depth_empty;

  if ((revision == NULL
       || revision->kind == svn_opt_revision_unspecified)
      && (peg_revision == NULL
          || peg_revision->kind == svn_opt_revision_unspecified))
    {
      /* Do all digging in the working copy. */
      wc_info_receiver_baton_t b = { receiver, receiver_baton };
      return svn_error_trace(
        svn_wc__get_info(ctx->wc_ctx, abspath_or_url, depth,
                        fetch_excluded, fetch_actual_only, changelists,
                         wc_info_receiver, &b,
                         ctx->cancel_func, ctx->cancel_baton, pool));
    }

  /* Go repository digging instead. */

  /* Trace rename history (starting at path_or_url@peg_revision) and
     return RA session to the possibly-renamed URL as it exists in REVISION.
     The ra_session returned will be anchored on this "final" URL. */
  SVN_ERR(svn_client__ra_session_from_path(&ra_session, &rev,
                                           &url, abspath_or_url, NULL,
                                           peg_revision,
                                           revision, ctx, pool));

  SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_URL, pool));
  SVN_ERR(svn_ra_get_uuid2(ra_session, &repos_UUID, pool));

  svn_uri_split(&parent_url, &base_name, url, pool);

  /* Get the dirent for the URL itself. */
  err = svn_ra_stat(ra_session, "", rev, &the_ent, pool);

  /* svn_ra_stat() will work against old versions of mod_dav_svn, but
     not old versions of svnserve.  In the case of a pre-1.2 svnserve,
     catch the specific error it throws:*/
  if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
    {
      /* Fall back to pre-1.2 strategy for fetching dirent's URL. */
      svn_error_clear(err);

      if (strcmp(url, repos_root_URL) == 0)
        {
          /* In this universe, there's simply no way to fetch
             information about the repository's root directory!
             If we're recursing, degrade gracefully: rather than
             throw an error, return no information about the
             repos root. */
          if (depth > svn_depth_empty)
            goto pre_1_2_recurse;

          /* Otherwise, we really are stuck.  Better tell the user
             what's going on. */
          return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                                   _("Server does not support retrieving "
                                     "information about the repository root"));
        }

      SVN_ERR(svn_ra_check_path(ra_session, "", rev, &url_kind, pool));
      if (url_kind == svn_node_none)
        return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                 _("URL '%s' non-existent in revision %ld"),
                                 url, rev);

      /* Open a new RA session to the item's parent. */
      SVN_ERR(svn_client__open_ra_session_internal(&parent_ra_session, NULL,
                                                   parent_url, NULL,
                                                   NULL, FALSE, TRUE,
                                                   ctx, pool));

      /* Get all parent's entries, and find the item's dirent in the hash. */
      SVN_ERR(svn_ra_get_dir2(parent_ra_session, &parent_ents, NULL, NULL,
                              "", rev, DIRENT_FIELDS, pool));
      the_ent = apr_hash_get(parent_ents, base_name, APR_HASH_KEY_STRING);
      if (the_ent == NULL)
        return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                 _("URL '%s' non-existent in revision %ld"),
                                 url, rev);
    }
  else if (err)
    {
      return svn_error_trace(err);
    }

  if (! the_ent)
    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                             _("URL '%s' non-existent in revision %ld"),
                             url, rev);

  /* Check if the URL exists in HEAD and refers to the same resource.
     In this case, we check the repository for a lock on this URL.

     ### There is a possible race here, since HEAD might have changed since
     ### we checked it.  A solution to this problem could be to do the below
     ### check in a loop which only terminates if the HEAD revision is the same
     ### before and after this check.  That could, however, lead to a
     ### starvation situation instead.  */
  SVN_ERR(same_resource_in_head(&related, url, rev, ra_session, ctx, pool));
  if (related)
    {
      err = svn_ra_get_lock(ra_session, &lock, "", pool);

      /* An old mod_dav_svn will always work; there's nothing wrong with
         doing a PROPFIND for a property named "DAV:supportedlock". But
         an old svnserve will error. */
      if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
        {
          svn_error_clear(err);
          lock = NULL;
        }
      else if (err)
        return svn_error_trace(err);
    }
  else
    lock = NULL;

  /* Push the URL's dirent (and lock) at the callback.*/
  SVN_ERR(build_info_from_dirent(&info, the_ent, lock, url, rev,
                                 repos_UUID, repos_root_URL, pool));
  SVN_ERR(receiver(receiver_baton, base_name, info, pool));

  /* Possibly recurse, using the original RA session. */
  if (depth > svn_depth_empty && (the_ent->kind == svn_node_dir))
    {
      apr_hash_t *locks;

pre_1_2_recurse:
      if (peg_revision->kind == svn_opt_revision_head)
        {
          err = svn_ra_get_locks2(ra_session, &locks, "", depth,
                                  pool);

          /* Catch specific errors thrown by old mod_dav_svn or svnserve. */
          if (err &&
              (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED
               || err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))
            {
              svn_error_clear(err);
              locks = apr_hash_make(pool); /* use an empty hash */
            }
          else if (err)
            return svn_error_trace(err);
        }
      else
        locks = apr_hash_make(pool); /* use an empty hash */

      SVN_ERR(push_dir_info(ra_session, url, "", rev,
                            repos_UUID, repos_root_URL,
                            receiver, receiver_baton,
                            depth, ctx, locks, pool));
    }

  return SVN_NO_ERROR;
}
예제 #25
0
static svn_error_t *
propset_on_url(const char *propname,
               const svn_string_t *propval,
               const char *target,
               svn_boolean_t skip_checks,
               svn_revnum_t base_revision_for_url,
               const apr_hash_t *revprop_table,
               svn_commit_callback2_t commit_callback,
               void *commit_baton,
               svn_client_ctx_t *ctx,
               apr_pool_t *pool)
{
  enum svn_prop_kind prop_kind = svn_property_kind(NULL, propname);
  svn_ra_session_t *ra_session;
  svn_node_kind_t node_kind;
  const char *message;
  const svn_delta_editor_t *editor;
  void *edit_baton;
  apr_hash_t *commit_revprops;
  svn_error_t *err;

  if (prop_kind != svn_prop_regular_kind)
    return svn_error_createf
      (SVN_ERR_BAD_PROP_KIND, NULL,
       _("Property '%s' is not a regular property"), propname);

  /* Open an RA session for the URL. Note that we don't have a local
     directory, nor a place to put temp files. */
  SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, target,
                                               NULL, NULL, FALSE, TRUE,
                                               ctx, pool));

  SVN_ERR(svn_ra_check_path(ra_session, "", base_revision_for_url,
                            &node_kind, pool));
  if (node_kind == svn_node_none)
    return svn_error_createf
      (SVN_ERR_FS_NOT_FOUND, NULL,
       _("Path '%s' does not exist in revision %ld"),
       target, base_revision_for_url);

  /* Setting an inappropriate property is not allowed (unless
     overridden by 'skip_checks', in some circumstances).  Deleting an
     inappropriate property is allowed, however, since older clients
     allowed (and other clients possibly still allow) setting it in
     the first place. */
  if (propval && svn_prop_is_svn_prop(propname))
    {
      const svn_string_t *new_value;
      struct getter_baton gb;

      gb.ra_session = ra_session;
      gb.base_revision_for_url = base_revision_for_url;
      SVN_ERR(svn_wc_canonicalize_svn_prop(&new_value, propname, propval,
                                           target, node_kind, skip_checks,
                                           get_file_for_validation, &gb, pool));
      propval = new_value;
    }

  /* Create a new commit item and add it to the array. */
  if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
    {
      svn_client_commit_item3_t *item;
      const char *tmp_file;
      apr_array_header_t *commit_items = apr_array_make(pool, 1, sizeof(item));

      item = svn_client_commit_item3_create(pool);
      item->url = target;
      item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
      APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
      SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
                                      ctx, pool));
      if (! message)
        return SVN_NO_ERROR;
    }
  else
    message = "";

  SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
                                           message, ctx, pool));

  /* Fetch RA commit editor. */
  SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
                                    commit_revprops,
                                    commit_callback,
                                    commit_baton,
                                    NULL, TRUE, /* No lock tokens */
                                    pool));

  err = do_url_propset(propname, propval, node_kind, base_revision_for_url,
                       editor, edit_baton, pool);

  if (err)
    {
      /* At least try to abort the edit (and fs txn) before throwing err. */
      svn_error_clear(editor->abort_edit(edit_baton, pool));
      return svn_error_trace(err);
    }

  /* Close the edit. */
  return editor->close_edit(edit_baton, pool);
}
예제 #26
0
/* Add PATH to the operations tree rooted at OPERATION, creating any
   intermediate nodes that are required.  Here's what's expected for
   each action type:

      ACTION          URL    REV      SRC-FILE  PROPNAME
      ------------    -----  -------  --------  --------
      ACTION_MKDIR    NULL   invalid  NULL      NULL
      ACTION_CP       valid  valid    NULL      NULL
      ACTION_PUT      NULL   invalid  valid     NULL
      ACTION_RM       NULL   invalid  NULL      NULL
      ACTION_PROPSET  valid  invalid  NULL      valid
      ACTION_PROPDEL  valid  invalid  NULL      valid

   Node type information is obtained for any copy source (to determine
   whether to create a file or directory) and for any deleted path (to
   ensure it exists since svn_delta_editor_t->delete_entry doesn't
   return an error on non-existent nodes). */
static svn_error_t *
build(action_code_t action,
      const char *path,
      const char *url,
      svn_revnum_t rev,
      const char *prop_name,
      const char *prop_value,
      const char *src_file,
      svn_revnum_t head,
      const char *anchor,
      svn_ra_session_t *session,
      struct operation *operation,
      apr_pool_t *pool)
{
  apr_array_header_t *path_bits = svn_path_decompose(path, pool);
  const char *path_so_far = "";
  const char *copy_src = NULL;
  svn_revnum_t copy_rev = SVN_INVALID_REVNUM;
  int i;

  /* Look for any previous operations we've recognized for PATH.  If
     any of PATH's ancestors have not yet been traversed, we'll be
     creating OP_OPEN operations for them as we walk down PATH's path
     components. */
  for (i = 0; i < path_bits->nelts; ++i)
    {
      const char *path_bit = APR_ARRAY_IDX(path_bits, i, const char *);
      path_so_far = svn_path_join(path_so_far, path_bit, pool);
      operation = get_operation(path_so_far, operation, pool);

      /* If we cross a replace- or add-with-history, remember the
      source of those things in case we need to lookup the node kind
      of one of their children.  And if this isn't such a copy,
      but we've already seen one in of our parent paths, we just need
      to extend that copy source path by our current path
      component. */
      if (operation->url
          && SVN_IS_VALID_REVNUM(operation->rev)
          && (operation->operation == OP_REPLACE
              || operation->operation == OP_ADD))
        {
          copy_src = subtract_anchor(anchor, operation->url, pool);
          copy_rev = operation->rev;
        }
      else if (copy_src)
        {
          copy_src = svn_path_join(copy_src, path_bit, pool);
        }
    }

  /* Handle property changes. */
  if (prop_name)
    {
      if (operation->operation == OP_DELETE)
        return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                 "cannot set properties on a location being"
                                 " deleted ('%s')", path);
      SVN_ERR(svn_ra_check_path(session,
                                copy_src ? copy_src : path,
                                copy_src ? copy_rev : head,
                                &operation->kind, pool));
      if (operation->kind == svn_node_none)
        return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                 "propset: '%s' not found", path);
      else if ((operation->kind == svn_node_file)
               && (operation->operation == OP_OPEN))
        operation->operation = OP_PROPSET;
      apr_table_set(operation->props, prop_name, prop_value);
      if (!operation->rev)
        operation->rev = rev;
      return SVN_NO_ERROR;
    }

  /* We won't fuss about multiple operations on the same path in the
     following cases:

       - the prior operation was, in fact, a no-op (open)
       - the prior operation was a propset placeholder
       - the prior operation was a deletion

     Note: while the operation structure certainly supports the
     ability to do a copy of a file followed by a put of new contents
     for the file, we don't let that happen (yet).
  */
  if (operation->operation != OP_OPEN
      && operation->operation != OP_PROPSET
      && operation->operation != OP_DELETE)
    return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                             "unsupported multiple operations on '%s'", path);

  /* For deletions, we validate that there's actually something to
     delete.  If this is a deletion of the child of a copied
     directory, we need to remember to look in the copy source tree to
     verify that this thing actually exists. */
  if (action == ACTION_RM)
    {
      operation->operation = OP_DELETE;
      SVN_ERR(svn_ra_check_path(session,
                                copy_src ? copy_src : path,
                                copy_src ? copy_rev : head,
                                &operation->kind, pool));
      if (operation->kind == svn_node_none)
        {
          if (copy_src && strcmp(path, copy_src))
            return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                     "'%s' (from '%s:%ld') not found",
                                     path, copy_src, copy_rev);
          else
            return svn_error_createf(SVN_ERR_BAD_URL, NULL, "'%s' not found",
                                     path);
        }
    }
  /* Handle copy operations (which can be adds or replacements). */
  else if (action == ACTION_CP)
    {
      if (rev > head)
        return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
                                "Copy source revision cannot be younger "
                                "than base revision");
      operation->operation =
        operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
      if (operation->operation == OP_ADD)
        {
          /* There is a bug in the current version of mod_dav_svn
             which incorrectly replaces existing directories.
             Therefore we need to check if the target exists
             and raise an error here. */
          SVN_ERR(svn_ra_check_path(session,
                                    copy_src ? copy_src : path,
                                    copy_src ? copy_rev : head,
                                    &operation->kind, pool));
          if (operation->kind != svn_node_none)
            {
              if (copy_src && strcmp(path, copy_src))
                return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                         "'%s' (from '%s:%ld') already exists",
                                         path, copy_src, copy_rev);
              else
                return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                         "'%s' already exists", path);
            }
        }
      SVN_ERR(svn_ra_check_path(session, subtract_anchor(anchor, url, pool),
                                rev, &operation->kind, pool));
      if (operation->kind == svn_node_none)
        return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                 "'%s' not found",
                                  subtract_anchor(anchor, url, pool));
      operation->url = url;
      operation->rev = rev;
    }
  /* Handle mkdir operations (which can be adds or replacements). */
  else if (action == ACTION_MKDIR)
    {
      operation->operation =
        operation->operation == OP_DELETE ? OP_REPLACE : OP_ADD;
      operation->kind = svn_node_dir;
    }
  /* Handle put operations (which can be adds, replacements, or opens). */
  else if (action == ACTION_PUT)
    {
      if (operation->operation == OP_DELETE)
        {
          operation->operation = OP_REPLACE;
        }
      else
        {
          SVN_ERR(svn_ra_check_path(session,
                                    copy_src ? copy_src : path,
                                    copy_src ? copy_rev : head,
                                    &operation->kind, pool));
          if (operation->kind == svn_node_file)
            operation->operation = OP_OPEN;
          else if (operation->kind == svn_node_none)
            operation->operation = OP_ADD;
          else
            return svn_error_createf(SVN_ERR_BAD_URL, NULL,
                                     "'%s' is not a file", path);
        }
      operation->kind = svn_node_file;
      operation->src_file = src_file;
    }
  else
    {
      /* We shouldn't get here. */
      abort();
    }

  return SVN_NO_ERROR;
}