Let's play "incredibly late code review"!
The fallback implementation svn_ra__file_revs_from_log incorrectly
ignores its "path" argument. (The only call from libsvn_client
hardcodes "" as that argument, so people using libsvn_client instead
of libsvn_ra directly will not notice.)
Also, if pointed at the root of a repository (ie, repos_url ==
session_url) it will segfault (svn_path_is_child returns NULL, then
you call strlen on it).
This code is only run against pre-1.2 servers.
The real question, of course, is: does anyone care about this
compatibility code? (We actually are running into this bug doing some
crawls that are hitting old servers.)
On Wed, Nov 7, 2007 at 7:53 AM, <hwright_at_tigris.org> wrote:
> Author: hwright
> Date: Wed Nov 7 06:53:34 2007
> New Revision: 27653
>
> Log:
> Fix issue 2964: Move libsvn_client/blame.c's get-file-revs fallback code into
> libsvn_ra/compat.c. This also generalizes the implementation of the fallback
> for other potential consumers of get-file-revs.
>
> * subversion/libsvn_ra/compat.c
> (svn_ra__file_revs_from_log, fr_log_message_receiver, fr_log_message_baton,
> rev): New.
> (prev_log_path): Remove note about duplication in libsvn_client.
>
> * subversion/libsvn_ra/ra_loader.c
> (svn_ra_get_file_revs2): Fallback to svn_ra__file_revs_from_log() if the
> server does not support get-file-revs.
>
> * subversion/libsvn_ra/ra_loader.h
> (svn_ra__file_revs_from_log): New prototype.
>
> * subversion/libsvn_client/blame.c
> (log_message_baton, prev_log_path, log_message_receiver, old_blame): Remove.
> (svn_client_blame4): Remove fallback to previous versions of get_file_revs().
>
>
> Modified:
> trunk/subversion/libsvn_client/blame.c
> trunk/subversion/libsvn_ra/compat.c
> trunk/subversion/libsvn_ra/ra_loader.c
> trunk/subversion/libsvn_ra/ra_loader.h
>
> Modified: trunk/subversion/libsvn_client/blame.c
> URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_client/blame.c?pathrev=27653&r1=27652&r2=27653
> ==============================================================================
> --- trunk/subversion/libsvn_client/blame.c (original)
> +++ trunk/subversion/libsvn_client/blame.c Wed Nov 7 06:53:34 2007
> @@ -40,7 +40,7 @@
> svn_revnum_t revision; /* the revision number */
> const char *author; /* the author of the revision */
> const char *date; /* the date of the revision */
> - /* Used for merge reporting, and by the pre-1.1 code. */
> + /* Used for merge reporting. */
> const char *path; /* the absolute repository path */
> struct rev *next; /* the next revision */
> };
> @@ -588,11 +588,6 @@
> }
> }
>
> -static svn_error_t *
> -old_blame(const char *target, const char *url,
> - svn_ra_session_t *ra_session,
> - struct file_rev_baton *frb);
> -
> svn_error_t *
> svn_client_blame4(const char *target,
> const svn_opt_revision_t *peg_revision,
> @@ -680,20 +675,10 @@
> We need to ensure that we get one revision before the start_rev,
> if available so that we can know what was actually changed in the start
> revision. */
> - err = svn_ra_get_file_revs2(ra_session, "",
> - start_revnum - (start_revnum > 0 ? 1 : 0),
> - end_revnum, include_merged_revisions,
> - file_rev_handler, &frb, pool);
> -
> - /* Fall back if it wasn't supported by the server. Servers earlier
> - than 1.1 need this. */
> - if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
> - {
> - svn_error_clear(err);
> - err = old_blame(target, url, ra_session, &frb);
> - }
> -
> - SVN_ERR(err);
> + SVN_ERR(svn_ra_get_file_revs2(ra_session, "",
> + start_revnum - (start_revnum > 0 ? 1 : 0),
> + end_revnum, include_merged_revisions,
> + file_rev_handler, &frb, pool));
>
> /* Report the blame to the caller. */
>
> @@ -935,356 +920,3 @@
> return svn_client_blame2(target, end, start, end,
> receiver, receiver_baton, ctx, pool);
> }
> -
> -
> -
> -/* Used only by the old code. */
> -/* The baton used for RA->get_log */
> -struct log_message_baton {
> - const char *path; /* The path to be processed */
> - struct rev *eldest; /* The eldest revision processed */
> - char action; /* The action associated with the eldest */
> - svn_revnum_t copyrev; /* The revision the eldest was copied from */
> - svn_cancel_func_t cancel_func; /* cancellation callback */
> - void *cancel_baton; /* cancellation baton */
> - apr_pool_t *pool;
> -};
> -
> -/* Given the CHANGED_PATHS and REVISION from an instance of a
> - svn_log_message_receiver_t function, determine at which location
> - PATH may be expected in the next log message, and set *PREV_PATH_P
> - to that value. KIND is the node kind of PATH. Set *ACTION_P to a
> - character describing the change that caused this revision (as
> - listed in svn_log_changed_path_t) and set *COPYFROM_REV_P to the
> - revision PATH was copied from, or SVN_INVALID_REVNUM if it was not
> - copied. ACTION_P and COPYFROM_REV_P may be NULL, in which case
> - they are not used. Perform all allocations in POOL.
> -
> - This is useful for tracking the various changes in location a
> - particular resource has undergone when performing an RA->get_logs()
> - operation on that resource.
> -
> - ### NOTE: This is a perfect duplicate of
> - ### libsvn_ra/compat.c:prev_log_path(), and should someday go away
> - ### when this compat code is moved into that file.
> - */
> -static svn_error_t *
> -prev_log_path(const char **prev_path_p,
> - char *action_p,
> - svn_revnum_t *copyfrom_rev_p,
> - apr_hash_t *changed_paths,
> - const char *path,
> - svn_node_kind_t kind,
> - svn_revnum_t revision,
> - apr_pool_t *pool)
> -{
> - svn_log_changed_path_t *change;
> - const char *prev_path = NULL;
> -
> - /* It's impossible to find the predecessor path of a NULL path. */
> - assert(path);
> -
> - /* Initialize our return values for the action and copyfrom_rev in
> - case we have an unhandled case later on. */
> - if (action_p)
> - *action_p = 'M';
> - if (copyfrom_rev_p)
> - *copyfrom_rev_p = SVN_INVALID_REVNUM;
> -
> - /* See if PATH was explicitly changed in this revision. */
> - change = apr_hash_get(changed_paths, path, APR_HASH_KEY_STRING);
> - if (change)
> - {
> - /* If PATH was not newly added in this revision, then it may or may
> - not have also been part of a moved subtree. In this case, set a
> - default previous path, but still look through the parents of this
> - path for a possible copy event. */
> - if (change->action != 'A' && change->action != 'R')
> - {
> - prev_path = path;
> - }
> - else
> - {
> - /* PATH is new in this revision. This means it cannot have been
> - part of a copied subtree. */
> - if (change->copyfrom_path)
> - prev_path = apr_pstrdup(pool, change->copyfrom_path);
> - else
> - prev_path = NULL;
> -
> - *prev_path_p = prev_path;
> - if (action_p)
> - *action_p = change->action;
> - if (copyfrom_rev_p)
> - *copyfrom_rev_p = change->copyfrom_rev;
> - return SVN_NO_ERROR;
> - }
> - }
> -
> - if (apr_hash_count(changed_paths))
> - {
> - /* The path was not explicitly changed in this revision. The
> - fact that we're hearing about this revision implies, then,
> - that the path was a child of some copied directory. We need
> - to find that directory, and effectively "re-base" our path on
> - that directory's copyfrom_path. */
> - int i;
> - apr_array_header_t *paths;
> -
> - /* Build a sorted list of the changed paths. */
> - paths = svn_sort__hash(changed_paths,
> - svn_sort_compare_items_as_paths, pool);
> -
> - /* Now, walk the list of paths backwards, looking a parent of
> - our path that has copyfrom information. */
> - for (i = paths->nelts; i > 0; i--)
> - {
> - svn_sort__item_t item = APR_ARRAY_IDX(paths,
> - i - 1, svn_sort__item_t);
> - const char *ch_path = item.key;
> - int len = strlen(ch_path);
> -
> - /* See if our path is the child of this change path. If
> - not, keep looking. */
> - if (! ((strncmp(ch_path, path, len) == 0) && (path[len] == '/')))
> - continue;
> -
> - /* Okay, our path *is* a child of this change path. If
> - this change was copied, we just need to apply the
> - portion of our path that is relative to this change's
> - path, to the change's copyfrom path. Otherwise, this
> - change isn't really interesting to us, and our search
> - continues. */
> - change = apr_hash_get(changed_paths, ch_path, len);
> - if (change->copyfrom_path)
> - {
> - if (action_p)
> - *action_p = change->action;
> - if (copyfrom_rev_p)
> - *copyfrom_rev_p = change->copyfrom_rev;
> - prev_path = svn_path_join(change->copyfrom_path,
> - path + len + 1, pool);
> - break;
> - }
> - }
> - }
> -
> - /* If we didn't find what we expected to find, return an error.
> - (Because directories bubble-up, we get a bunch of logs we might
> - not want. Be forgiving in that case.) */
> - if (! prev_path)
> - {
> - if (kind == svn_node_dir)
> - prev_path = apr_pstrdup(pool, path);
> - else
> - return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
> - _("Missing changed-path information for "
> - "'%s' in revision %ld"),
> - svn_path_local_style(path, pool), revision);
> - }
> -
> - *prev_path_p = prev_path;
> - return SVN_NO_ERROR;
> -}
> -
> -
> -/* Callback for log messages: accumulates revision metadata into
> - a chronologically ordered list stored in the baton. */
> -static svn_error_t *
> -log_message_receiver(void *baton,
> - apr_hash_t *changed_paths,
> - svn_revnum_t revision,
> - const char *author,
> - const char *date,
> - const char *message,
> - apr_pool_t *pool)
> -{
> - struct log_message_baton *lmb = baton;
> - struct rev *rev;
> -
> - if (lmb->cancel_func)
> - SVN_ERR(lmb->cancel_func(lmb->cancel_baton));
> -
> - rev = apr_palloc(lmb->pool, sizeof(*rev));
> - rev->revision = revision;
> - rev->author = apr_pstrdup(lmb->pool, author);
> - rev->date = apr_pstrdup(lmb->pool, date);
> - rev->path = lmb->path;
> - rev->next = lmb->eldest;
> - lmb->eldest = rev;
> -
> - return prev_log_path(&lmb->path, &lmb->action,
> - &lmb->copyrev, changed_paths,
> - lmb->path, svn_node_file, revision,
> - lmb->pool);
> -}
> -
> -/* This is used when there is no get_file_revs available. */
> -static svn_error_t *
> -old_blame(const char *target, const char *url,
> - svn_ra_session_t *ra_session,
> - struct file_rev_baton *frb)
> -{
> - const char *reposURL;
> - struct log_message_baton lmb;
> - apr_array_header_t *condensed_targets;
> - apr_file_t *file;
> - svn_stream_t *stream;
> - struct rev *rev;
> - svn_node_kind_t kind;
> - apr_pool_t *pool = frb->mainpool;
> -
> - SVN_ERR(svn_ra_check_path(ra_session, "", frb->end_rev, &kind, pool));
> -
> - if (kind == svn_node_dir)
> - return svn_error_createf(SVN_ERR_CLIENT_IS_DIRECTORY, NULL,
> - _("URL '%s' refers to a directory"), url);
> -
> - condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
> - APR_ARRAY_PUSH(condensed_targets, const char *) = "";
> -
> - SVN_ERR(svn_ra_get_repos_root(ra_session, &reposURL, pool));
> -
> - /* URI decode the path before placing it in the baton, since changed_paths
> - passed into log_message_receiver will not be URI encoded. */
> - lmb.path = svn_path_uri_decode(url + strlen(reposURL), pool);
> -
> - lmb.cancel_func = frb->ctx->cancel_func;
> - lmb.cancel_baton = frb->ctx->cancel_baton;
> - 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_log(ra_session,
> - condensed_targets,
> - frb->end_rev,
> - frb->start_rev,
> - 0, /* no limit */
> - TRUE,
> - FALSE,
> - log_message_receiver,
> - &lmb,
> - pool));
> -
> - SVN_ERR(svn_client__open_ra_session_internal(&ra_session, reposURL, NULL,
> - NULL, NULL, FALSE, FALSE,
> - frb->ctx, pool));
> -
> - /* Inspect the first revision's change metadata; if there are any
> - prior revisions, compute a new starting revision/path. If no
> - revisions were selected, no blame is assigned. A modified
> - item certainly has a prior revision. It is reasonable for an
> - added item to have none, but anything else is unexpected. */
> - if (!lmb.eldest)
> - {
> - lmb.eldest = apr_palloc(pool, sizeof(*rev));
> - lmb.eldest->revision = frb->end_rev;
> - lmb.eldest->path = lmb.path;
> - lmb.eldest->next = NULL;
> - rev = apr_palloc(pool, sizeof(*rev));
> - rev->revision = SVN_INVALID_REVNUM;
> - rev->author = NULL;
> - rev->date = NULL;
> - frb->chain->blame = blame_create(frb->chain, rev, 0);
> - }
> - else if (lmb.action == 'M' || SVN_IS_VALID_REVNUM(lmb.copyrev))
> - {
> - rev = apr_palloc(pool, sizeof(*rev));
> - if (SVN_IS_VALID_REVNUM(lmb.copyrev))
> - rev->revision = lmb.copyrev;
> - else
> - rev->revision = lmb.eldest->revision - 1;
> - rev->path = lmb.path;
> - rev->next = lmb.eldest;
> - lmb.eldest = rev;
> - rev = apr_palloc(pool, sizeof(*rev));
> - rev->revision = SVN_INVALID_REVNUM;
> - rev->author = NULL;
> - rev->date = NULL;
> - frb->chain->blame = blame_create(frb->chain, rev, 0);
> - }
> - else if (lmb.action == 'A')
> - {
> - frb->chain->blame = blame_create(frb->chain, lmb.eldest, 0);
> - }
> - else
> - return svn_error_createf(APR_EGENERAL, NULL,
> - _("Revision action '%c' for "
> - "revision %ld of '%s' "
> - "lacks a prior revision"),
> - lmb.action, lmb.eldest->revision,
> - svn_path_local_style(lmb.eldest->path, pool));
> -
> - /* Walk the revision list in chronological order, downloading
> - each fulltext, diffing it with its predecessor, and accumulating
> - the blame information into db.blame. 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 *tmp;
> - const char *temp_dir;
> - apr_hash_t *props;
> - svn_string_t *mimetype;
> -
> - apr_pool_clear(frb->currpool);
> - SVN_ERR(svn_io_temp_dir(&temp_dir, frb->currpool));
> - SVN_ERR(svn_io_open_unique_file2
> - (&file, &tmp,
> - svn_path_join(temp_dir, "tmp", frb->currpool), ".tmp",
> - svn_io_file_del_on_pool_cleanup, frb->currpool));
> -
> - stream = svn_stream_from_aprfile(file, frb->currpool);
> - SVN_ERR(svn_ra_get_file(ra_session, rev->path + 1, rev->revision,
> - stream, NULL, &props, frb->currpool));
> - SVN_ERR(svn_stream_close(stream));
> - SVN_ERR(svn_io_file_close(file, frb->currpool));
> -
> - /* If this file has a non-textual mime-type, bail out. */
> - if (! frb->ignore_mime_type && props &&
> - ((mimetype = apr_hash_get(props, SVN_PROP_MIME_TYPE,
> - sizeof(SVN_PROP_MIME_TYPE) - 1))))
> - {
> - if (svn_mime_type_is_binary(mimetype->data))
> - return svn_error_createf
> - (SVN_ERR_CLIENT_IS_BINARY_FILE, 0,
> - _("Cannot calculate blame information for binary file '%s'"),
> - svn_path_local_style(target, frb->currpool));
> - }
> -
> - if (frb->ctx->notify_func2)
> - {
> - svn_wc_notify_t *notify
> - = svn_wc_create_notify(rev->path, svn_wc_notify_blame_revision,
> - pool);
> - notify->kind = svn_node_none;
> - notify->content_state = notify->prop_state
> - = svn_wc_notify_state_inapplicable;
> - notify->lock_state = svn_wc_notify_lock_state_inapplicable;
> - notify->revision = rev->revision;
> - frb->ctx->notify_func2(frb->ctx->notify_baton2, notify, pool);
> - }
> -
> - if (frb->ctx->cancel_func)
> - SVN_ERR(frb->ctx->cancel_func(frb->ctx->cancel_baton));
> -
> - if (frb->last_filename)
> - {
> - frb->rev = rev;
> - SVN_ERR(add_file_blame(frb->last_filename, tmp, frb->chain,
> - frb->rev, frb->diff_options, frb->currpool));
> - }
> -
> - frb->last_filename = tmp;
> - {
> - apr_pool_t *tmppool = frb->currpool;
> - frb->currpool = frb->lastpool;
> - frb->lastpool = tmppool;
> - }
> - }
> -
> - return SVN_NO_ERROR;
> -}
>
> Modified: trunk/subversion/libsvn_ra/compat.c
> URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_ra/compat.c?pathrev=27653&r1=27652&r2=27653
> ==============================================================================
> --- trunk/subversion/libsvn_ra/compat.c (original)
> +++ trunk/subversion/libsvn_ra/compat.c Wed Nov 7 06:53:34 2007
> @@ -27,6 +27,8 @@
> #include "svn_sorts.h"
> #include "svn_path.h"
> #include "svn_ra.h"
> +#include "svn_io.h"
> +#include "svn_compat.h"
> #include "ra_loader.h"
> #include "svn_private_config.h"
>
> @@ -57,10 +59,6 @@
> This is useful for tracking the various changes in location a
> particular resource has undergone when performing an RA->get_logs()
> operation on that resource.
> -
> - ### NOTE: This is a perfect duplicate of
> - ### libsvn_client/blame.c:prev_log_path(), which should someday go
> - ### away when the blame compat code is moved into this file, too.
> */
> static svn_error_t *
> prev_log_path(const char **prev_path_p,
> @@ -577,3 +575,203 @@
>
> return SVN_NO_ERROR;
> }
> +
> +
> +
> +/*** Fallback implementation of svn_ra_get_file_revs(). ***/
> +
> +/* The metadata associated with a particular revision. */
> +struct rev
> +{
> + svn_revnum_t revision; /* the revision number */
> + const char *path; /* the absolute repository path */
> + apr_hash_t *props; /* the revprops for this revision */
> + struct rev *next; /* the next revision */
> +};
> +
> +/* File revs log message baton. */
> +struct fr_log_message_baton {
> + const char *path; /* The path to be processed */
> + struct rev *eldest; /* The eldest revision processed */
> + char action; /* The action associated with the eldest */
> + svn_revnum_t copyrev; /* The revision the eldest was copied from */
> + apr_pool_t *pool;
> +};
> +
> +/* Callback for log messages: implements svn_log_entry_receiver_t and
> + accumulates revision metadata into a chronologically ordered list stored in
> + the baton. */
> +static svn_error_t *
> +fr_log_message_receiver(void *baton,
> + svn_log_entry_t *log_entry,
> + apr_pool_t *pool)
> +{
> + struct fr_log_message_baton *lmb = baton;
> + struct rev *rev;
> + apr_hash_index_t *hi;
> +
> + rev = apr_palloc(lmb->pool, sizeof(*rev));
> + rev->revision = log_entry->revision;
> + rev->path = lmb->path;
> + rev->next = lmb->eldest;
> + lmb->eldest = rev;
> +
> + /* Duplicate log_entry revprops into rev->props */
> + rev->props = apr_hash_make(lmb->pool);
> + for (hi = apr_hash_first(pool, log_entry->revprops); hi;
> + hi = apr_hash_next(hi))
> + {
> + svn_string_t *val;
> + const char *key;
> +
> + apr_hash_this(hi, (const void **)&key, NULL, (void **)&val);
> + apr_hash_set(rev->props, apr_pstrdup(lmb->pool, key), APR_HASH_KEY_STRING,
> + svn_string_dup(val, lmb->pool));
> + }
> +
> + return prev_log_path(&lmb->path, &lmb->action,
> + &lmb->copyrev, log_entry->changed_paths,
> + lmb->path, svn_node_file, log_entry->revision,
> + lmb->pool);
> +}
> +
> +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;
> + const char *session_url;
> + const char *tmp;
> + char *repos_abs_path;
> + apr_array_header_t *condensed_targets;
> + struct fr_log_message_baton lmb;
> + struct rev *rev;
> + apr_hash_t *last_props;
> + const char *last_path;
> + svn_stream_t *last_stream;
> + apr_pool_t *currpool, *lastpool;
> +
> + SVN_ERR(svn_ra_get_repos_root(ra_session, &repos_url, pool));
> + SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
> +
> + /* Create the initial path, using the repos_url and session_url */
> + tmp = svn_path_is_child(repos_url, session_url, pool);
> + repos_abs_path = apr_palloc(pool, strlen(tmp) + 1);
> + repos_abs_path[0] = '/';
> + memcpy(repos_abs_path + 1, tmp, strlen(tmp));
> +
> + /* Check to make sure we're dealing with a file. */
> + SVN_ERR(svn_ra_check_path(ra_session, "", end, &kind, pool));
> +
> + if (kind == svn_node_dir)
> + return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
> + _("'%s' is not a file"), repos_abs_path);
> +
> + condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
> + APR_ARRAY_PUSH(condensed_targets, const char *) = "";
> +
> + lmb.path = svn_path_uri_decode(repos_abs_path, pool);
> + 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_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_path = NULL;
> + 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;
> + const char *temp_dir;
> + 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;
> +
> + apr_pool_clear(currpool);
> +
> + /* Get the contents of the file from the repository, and put them in
> + a temporary local file. */
> + SVN_ERR(svn_io_temp_dir(&temp_dir, currpool));
> + SVN_ERR(svn_io_open_unique_file2
> + (&file, &temp_path,
> + svn_path_join(temp_dir, "tmp", currpool), ".tmp",
> + svn_io_file_del_on_pool_cleanup, currpool));
> + stream = svn_stream_from_aprfile(file, currpool);
> + SVN_ERR(svn_ra_get_file(ra_session, rev->path + 1, rev->revision,
> + stream, NULL, &props, currpool));
> + SVN_ERR(svn_stream_close(stream));
> + SVN_ERR(svn_io_file_close(file, currpool));
> +
> + /* 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. */
> + svn_txdelta(&delta_stream, last_stream, stream, 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_stream_close(last_stream);
> + last_stream = stream;
> + last_props = props;
> + }
> +
> + 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);
> +}
>
> Modified: trunk/subversion/libsvn_ra/ra_loader.c
> URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_ra/ra_loader.c?pathrev=27653&r1=27652&r2=27653
> ==============================================================================
> --- trunk/subversion/libsvn_ra/ra_loader.c (original)
> +++ trunk/subversion/libsvn_ra/ra_loader.c Wed Nov 7 06:53:34 2007
> @@ -1001,9 +1001,20 @@
> void *handler_baton,
> apr_pool_t *pool)
> {
> - return session->vtable->get_file_revs(session, path, start, end,
> - include_merged_revisions, handler,
> - handler_baton, pool);
> +
> + svn_error_t *err = session->vtable->get_file_revs(session, path, start, end,
> + include_merged_revisions,
> + handler, handler_baton,
> + pool);
> + if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
> + {
> + svn_error_clear(err);
> +
> + /* Do it the slow way, using get-logs, for older servers. */
> + err = svn_ra__file_revs_from_log(session, path, start, end,
> + handler, handler_baton, pool);
> + }
> + return err;
> }
>
> svn_error_t *svn_ra_lock(svn_ra_session_t *session,
>
> Modified: trunk/subversion/libsvn_ra/ra_loader.h
> URL: http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_ra/ra_loader.h?pathrev=27653&r1=27652&r2=27653
> ==============================================================================
> --- trunk/subversion/libsvn_ra/ra_loader.h (original)
> +++ trunk/subversion/libsvn_ra/ra_loader.h Wed Nov 7 06:53:34 2007
> @@ -334,6 +334,37 @@
> void *receiver_baton,
> apr_pool_t *pool);
>
> +/**
> + * Retrieve a subset of the interesting revisions of a file PATH
> + * as seen in revision END (see svn_fs_history_prev() for a
> + * definition of "interesting revisions"). Invoke HANDLER with
> + * @a handler_baton as its first argument for each such revision.
> + * @a session is an open RA session. Use POOL for all allocations.
> + *
> + * If there is an interesting revision of the file that is less than or
> + * equal to START, the iteration will begin at that revision.
> + * Else, the iteration will begin at the first revision of the file in
> + * the repository, which has to be less than or equal to END. Note
> + * that if the function succeeds, HANDLER will have been called at
> + * least once.
> + *
> + * In a series of calls to HANDLER, the file contents for the first
> + * interesting revision will be provided as a text delta against the
> + * empty file. In the following calls, the delta will be against the
> + * fulltext contents for the previous call.
> + *
> + * NOTE: This function uses the RA get_log interfaces to do its work,
> + * as a fallback mechanism for servers which don't support the native
> + * get_location_segments API.
> + */
> +svn_error_t *
> +svn_ra__file_revs_from_log(svn_ra_session_t *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);
>
> #ifdef __cplusplus
> }
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: svn-unsubscribe_at_subversion.tigris.org
> For additional commands, e-mail: svn-help_at_subversion.tigris.org
>
>
--
glasser_at_davidglasser.net | langtonlabs.org | flickr.com/photos/glasser/
Received on 2010-07-20 20:03:01 CEST