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, <hwri...@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-unsubscr...@subversion.tigris.org > For additional commands, e-mail: svn-h...@subversion.tigris.org > > -- glas...@davidglasser.net | langtonlabs.org | flickr.com/photos/glasser/