[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: svn commit: r27653 - in trunk/subversion: libsvn_client libsvn_ra

From: David Glasser <glasser_at_davidglasser.net>
Date: Tue, 20 Jul 2010 11:01:11 -0700

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

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.