svn_error_t *svn_txdelta_send_stream(svn_stream_t *stream,
                                     svn_txdelta_window_handler_t handler,
                                     void *handler_baton,
                                     unsigned char *digest,
                                     apr_pool_t *pool)
{
  svn_txdelta_stream_t *txstream;
  svn_error_t *err;

  /* ### this is a hack. we should simply read from the stream, construct
     ### some windows, and pass those to the handler. there isn't any reason
     ### to crank up a full "diff" algorithm just to copy a stream.
     ###
     ### will fix RSN. */

  /* Create a delta stream which converts an *empty* bytestream into the
     target bytestream. */
  svn_txdelta(&txstream, svn_stream_empty(pool), stream, pool);
  err = svn_txdelta_send_txstream(txstream, handler, handler_baton, pool);

  if (digest && (! err))
    {
      const unsigned char *result_md5;
      result_md5 = svn_txdelta_md5_digest(txstream);
      /* Since err is null, result_md5 "cannot" be null. */
      memcpy(digest, result_md5, APR_MD5_DIGESTSIZE);
    }

  return err;
}
Exemplo n.º 2
0
static void
do_one_diff(apr_file_t *source_file, apr_file_t *target_file,
            int *count, apr_off_t *len,
            int quiet, apr_pool_t *pool,
            const char *tag, FILE* stream)
{
  svn_txdelta_stream_t *delta_stream = NULL;
  svn_txdelta_window_t *delta_window = NULL;
  apr_pool_t *fpool = svn_pool_create(pool);
  apr_pool_t *wpool = svn_pool_create(pool);

  *count = 0;
  *len = 0;
  svn_txdelta(&delta_stream,
              svn_stream_from_aprfile(source_file, fpool),
              svn_stream_from_aprfile(target_file, fpool),
              fpool);
  do {
    svn_error_t *err;
    err = svn_txdelta_next_window(&delta_window, delta_stream, wpool);
    if (err)
      svn_handle_error2(err, stderr, TRUE, "vdelta-test: ");
    if (delta_window != NULL)
      {
        *len += print_delta_window(delta_window, tag, quiet, stream);
        svn_pool_clear(wpool);
        ++*count;
      }
  } while (delta_window != NULL);
  fprintf(stream, "%s: (LENGTH %" APR_OFF_T_FMT " +%d)\n", tag, *len, *count);

  svn_pool_destroy(fpool);
  svn_pool_destroy(wpool);
}
Exemplo n.º 3
0
/* Deltifies a node, i.e. generates a svndiff that can be dumped */
static svn_error_t *delta_deltify_node(de_node_baton_t *node)
{
	svn_txdelta_stream_t *stream;
	svn_txdelta_window_handler_t handler;
	void *handler_baton;
	svn_stream_t *source, *target, *dest;
	apr_file_t *source_file = NULL, *target_file = NULL, *dest_file = NULL;
	dump_options_t *opts = node->de_baton->opts;
	apr_pool_t *pool = svn_pool_create(node->pool);
	svn_error_t *err;

	DEBUG_MSG("delta_deltify_node(%s): %s -> %s\n", node->path, node->old_filename, node->filename);

	/* Open source and target */
	apr_file_open(&target_file, node->filename, APR_READ, 0600, pool);
	target = svn_stream_from_aprfile2(target_file, FALSE, pool);
	if (node->old_filename) {
		apr_file_open(&source_file, node->old_filename, APR_READ, 0600, pool);
		source = svn_stream_from_aprfile2(source_file, FALSE, pool);
	} else {
		source = svn_stream_empty(pool);
	}

	/* Open temporary output file */
	node->delta_filename = apr_psprintf(node->pool, "%s/XXXXXX", opts->temp_dir);
	apr_file_mktemp(&dest_file, node->delta_filename, APR_CREATE | APR_READ | APR_WRITE | APR_EXCL, pool);
	dest = svn_stream_from_aprfile2(dest_file, FALSE, pool);

	DEBUG_MSG("delta_deltify_node(%s): writing to %s\n", node->path, node->delta_filename);

	/* Produce delta in svndiff format */
	svn_txdelta(&stream, source, target, pool);
	svn_txdelta_to_svndiff2(&handler, &handler_baton, dest, 0, pool);

	err = svn_txdelta_send_txstream(stream, handler, handler_baton, pool);
	svn_pool_destroy(pool);
	return err;
}
Exemplo n.º 4
0
int
main(int argc, char **argv)
{
  apr_file_t *source_file_A = NULL;
  apr_file_t *target_file_A = NULL;
  int count_A = 0;
  apr_off_t len_A = 0;

  apr_file_t *source_file_B = NULL;
  apr_file_t *target_file_B = NULL;
  int count_B = 0;
  apr_off_t len_B = 0;

  apr_pool_t *pool;
  int quiet = 0;

  if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'q')
    {
      quiet = 1;
      --argc; ++argv;
    }

  apr_initialize();
  pool = svn_pool_create(NULL);

  if (argc == 2)
    {
      target_file_A = open_binary_read(argv[1], pool);
    }
  else if (argc == 3)
    {
      source_file_A = open_binary_read(argv[1], pool);
      target_file_A = open_binary_read(argv[2], pool);
    }
  else if (argc == 4)
    {
      source_file_A = open_binary_read(argv[1], pool);
      target_file_A = open_binary_read(argv[2], pool);
      source_file_B = open_binary_read(argv[2], pool);
      target_file_B = open_binary_read(argv[3], pool);
    }
  else
    {
      fprintf(stderr,
              "Usage: vdelta-test [-q] <target>\n"
              "   or: vdelta-test [-q] <source> <target>\n"
              "   or: vdelta-test [-q] <source> <intermediate> <target>\n");
      exit(1);
    }

  do_one_diff(source_file_A, target_file_A,
              &count_A, &len_A, quiet, pool, "A ", stdout);

  if (source_file_B)
    {
      apr_pool_t *fpool = svn_pool_create(pool);
      apr_pool_t *wpool = svn_pool_create(pool);
      svn_txdelta_stream_t *stream_A = NULL;
      svn_txdelta_stream_t *stream_B = NULL;
      svn_txdelta_window_t *window_A = NULL;
      svn_txdelta_window_t *window_B = NULL;
      svn_txdelta_window_t *window_AB = NULL;
      int count_AB = 0;
      apr_off_t len_AB = 0;

      putc('\n', stdout);
      do_one_diff(source_file_B, target_file_B,
                  &count_B, &len_B, quiet, pool, "B ", stdout);

      putc('\n', stdout);

      {
        apr_off_t offset = 0;

        apr_file_seek(source_file_A, APR_SET, &offset);
        apr_file_seek(target_file_A, APR_SET, &offset);
        apr_file_seek(source_file_B, APR_SET, &offset);
        apr_file_seek(target_file_B, APR_SET, &offset);
      }

      svn_txdelta(&stream_A,
                  svn_stream_from_aprfile(source_file_A, fpool),
                  svn_stream_from_aprfile(target_file_A, fpool),
                  fpool);
      svn_txdelta(&stream_B,
                  svn_stream_from_aprfile(source_file_B, fpool),
                  svn_stream_from_aprfile(target_file_B, fpool),
                  fpool);

      for (count_AB = 0; count_AB < count_B; ++count_AB)
        {
          svn_error_t *err;

          err = svn_txdelta_next_window(&window_A, stream_A, wpool);
          if (err)
            svn_handle_error2(err, stderr, TRUE, "vdelta-test: ");
          err = svn_txdelta_next_window(&window_B, stream_B, wpool);
          if (err)
            svn_handle_error2(err, stderr, TRUE, "vdelta-test: ");

          /* Note: It's not possible that window_B is null, we already
             counted the number of windows in the second delta. */
          assert(window_A != NULL || window_B->src_ops == 0);
          if (window_B->src_ops == 0)
            {
              window_AB = window_B;
              window_AB->sview_len = 0;
            }
          else
            window_AB = svn_txdelta_compose_windows(window_A, window_B,
                                                    wpool);
          len_AB += print_delta_window(window_AB, "AB", quiet, stdout);
          svn_pool_clear(wpool);
        }

      fprintf(stdout, "AB: (LENGTH %" APR_OFF_T_FMT " +%d)\n",
              len_AB, count_AB);
    }

  if (source_file_A) apr_file_close(source_file_A);
  if (target_file_A) apr_file_close(target_file_A);
  if (source_file_B) apr_file_close(source_file_B);
  if (target_file_B) apr_file_close(source_file_B);

  svn_pool_destroy(pool);
  apr_terminate();
  exit(0);
}
Exemplo n.º 5
0
inline Try<Diff> diff(const std::string& from, const std::string& to)
{
  // Initialize the Apache Portable Runtime subsystem, as necessary
  // for using the svn library.
  initialize();

  // Note that svn_pool_create wraps apr_pool_create_ex, which is
  // thread safe, see: http://goo.gl/NX0hps.
  apr_pool_t* pool = svn_pool_create(NULL);

  // First we need to produce a text delta stream by diffing 'source'
  // against 'target'.
  svn_string_t source;
  source.data = from.data();
  source.len = from.length();

  svn_string_t target;
  target.data = to.data();
  target.len = to.length();

  svn_txdelta_stream_t* delta;

#if SVN_VER_MAJOR >= 1 && SVN_VER_MINOR >= 8
  svn_txdelta2(
      &delta,
      svn_stream_from_string(&source, pool),
      svn_stream_from_string(&target, pool),
      false,
      pool);
#else
  svn_txdelta(
      &delta,
      svn_stream_from_string(&source, pool),
      svn_stream_from_string(&target, pool),
      pool);
#endif

  // Now we want to convert this text delta stream into an svndiff
  // format based diff. Setup the handler that will consume the text
  // delta and produce the svndiff.
  svn_txdelta_window_handler_t handler;
  void* baton = NULL;
  svn_stringbuf_t* diff = svn_stringbuf_create_ensure(1024, pool);

#if SVN_VER_MAJOR >= 1 && SVN_VER_MINOR >= 7
  svn_txdelta_to_svndiff3(
      &handler,
      &baton,
      svn_stream_from_stringbuf(diff, pool),
      0,
      SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
      pool);
#elif SVN_VER_MAJOR >= 1 && SVN_VER_MINOR >= 4
  svn_txdelta_to_svndiff2(
      &handler,
      &baton,
      svn_stream_from_stringbuf(diff, pool),
      0,
      pool);
#else
  svn_txdelta_to_svndiff(
      svn_stream_from_stringbuf(diff, pool),
      pool,
      &handler,
      &baton);
#endif

  // Now feed the text delta to the handler.
  svn_error_t* error = svn_txdelta_send_txstream(delta, handler, baton, pool);

  if (error != NULL) {
    char buffer[1024];
    std::string message(svn_err_best_message(error, buffer, 1024));
    svn_pool_destroy(pool);
    return Error(message);
  }

  Diff d(std::string(diff->data, diff->len));

  svn_pool_destroy(pool);

  return d;
}
int
main(int argc, char **argv)
{
  svn_error_t *err;
  apr_status_t apr_err;
  apr_file_t *source_file;
  apr_file_t *target_file;
  svn_stream_t *stdout_stream;
  svn_txdelta_stream_t *txdelta_stream;
  svn_txdelta_window_handler_t svndiff_handler;
  svn_stream_t *encoder;
  void *svndiff_baton;
  apr_pool_t *pool;
  int version = 0;

  if (argc < 3)
    {
      printf("usage: %s source target [version]\n", argv[0]);
      exit(0);
    }

  apr_initialize();
  pool = svn_pool_create(NULL);
  apr_err = apr_file_open(&source_file, argv[1], (APR_READ | APR_BINARY),
                          APR_OS_DEFAULT, pool);
  if (apr_err)
    {
      fprintf(stderr, "unable to open \"%s\" for reading\n", argv[1]);
      exit(1);
    }

  apr_err = apr_file_open(&target_file, argv[2], (APR_READ | APR_BINARY),
                          APR_OS_DEFAULT, pool);
  if (apr_err)
    {
      fprintf(stderr, "unable to open \"%s\" for reading\n", argv[2]);
      exit(1);
    }
  if (argc == 4)
    version = atoi(argv[3]);

  svn_txdelta(&txdelta_stream,
              svn_stream_from_aprfile(source_file, pool),
              svn_stream_from_aprfile(target_file, pool),
              pool);

  err = svn_stream_for_stdout(&stdout_stream, pool);
  if (err)
    svn_handle_error2(err, stdout, TRUE, "svndiff-test: ");

#ifdef QUOPRINT_SVNDIFFS
  encoder = svn_quoprint_encode(stdout_stream, pool);
#else
  encoder = svn_base64_encode(stdout_stream, pool);
#endif
  /* use maximum compression level */
  svn_txdelta_to_svndiff3(&svndiff_handler, &svndiff_baton,
                          encoder, version, 9, pool);
  err = svn_txdelta_send_txstream(txdelta_stream,
                                  svndiff_handler,
                                  svndiff_baton,
                                  pool);
  if (err)
    svn_handle_error2(err, stdout, TRUE, "svndiff-test: ");

  apr_file_close(source_file);
  apr_file_close(target_file);
  svn_pool_destroy(pool);
  apr_terminate();
  exit(0);
}
Exemplo n.º 7
0
/* (Note: *LAST_SEED is an output parameter.) */
static svn_error_t *
do_random_combine_test(const char **msg,
                       svn_boolean_t msg_only,
                       apr_pool_t *pool,
                       apr_uint32_t *last_seed)
{
  static char msg_buff[256];

  apr_uint32_t seed, bytes_range, maxlen;
  int i, iterations, dump_files, print_windows;
  const char *random_bytes;

  /* Initialize parameters and print out the seed in case we dump core
     or something. */
  init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows,
              &random_bytes, &bytes_range, pool);
  sprintf(msg_buff,
          "random combine delta test, seed = %lu", (unsigned long) seed);
  *msg = msg_buff;

  if (msg_only)
    return SVN_NO_ERROR;
  else
    printf("SEED:  %s\n", msg_buff);

  for (i = 0; i < iterations; i++)
    {
      /* Generate source and target for the delta and its application.  */
      apr_uint32_t subseed_base = svn_test_rand((*last_seed = seed, &seed));
      apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed,
                                                random_bytes, bytes_range,
                                                dump_files, pool);
      apr_file_t *middle = generate_random_file(maxlen, subseed_base, &seed,
                                                random_bytes, bytes_range,
                                                dump_files, pool);
      apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed,
                                                random_bytes, bytes_range,
                                                dump_files, pool);
      apr_file_t *source_copy = copy_tempfile(source, pool);
      apr_file_t *middle_copy = copy_tempfile(middle, pool);
      apr_file_t *target_regen = open_tempfile(NULL, pool);

      svn_txdelta_stream_t *txdelta_stream_A;
      svn_txdelta_stream_t *txdelta_stream_B;
      svn_txdelta_window_handler_t handler;
      svn_stream_t *stream;
      void *handler_baton;

      /* Set up a four-stage pipeline: create two deltas, combine them
         and convert the result to svndiff format, parse that back
         into delta format, and apply it to a copy of the source file
         to see if we get the same target back.  */
      apr_pool_t *delta_pool = svn_pool_create(pool);

      /* Make stage 4: apply the text delta.  */
      svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool),
                        svn_stream_from_aprfile(target_regen, delta_pool),
                        NULL, NULL, delta_pool, &handler, &handler_baton);

      /* Make stage 3: reparse the text delta.  */
      stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE,
                                         delta_pool);

      /* Make stage 2: encode the text delta in svndiff format.  */
      svn_txdelta_to_svndiff2(&handler, &handler_baton, stream, 1,
                              delta_pool);

      /* Make stage 1: create the text deltas.  */

      svn_txdelta(&txdelta_stream_A,
                  svn_stream_from_aprfile(source, delta_pool),
                  svn_stream_from_aprfile(middle, delta_pool),
                  delta_pool);

      svn_txdelta(&txdelta_stream_B,
                  svn_stream_from_aprfile(middle_copy, delta_pool),
                  svn_stream_from_aprfile(target, delta_pool),
                  delta_pool);

      {
        svn_txdelta_window_t *window_A;
        svn_txdelta_window_t *window_B;
        svn_txdelta_window_t *composite;
        apr_pool_t *wpool = svn_pool_create(delta_pool);

        do
          {
            SVN_ERR(svn_txdelta_next_window(&window_A, txdelta_stream_A,
                                            wpool));
            if (print_windows)
              delta_window_print(window_A, "A ", stdout);
            SVN_ERR(svn_txdelta_next_window(&window_B, txdelta_stream_B,
                                            wpool));
            if (print_windows)
              delta_window_print(window_B, "B ", stdout);
            if (!window_B)
              break;
            assert(window_A != NULL || window_B->src_ops == 0);
            if (window_B->src_ops == 0)
              {
                composite = window_B;
                composite->sview_len = 0;
              }
            else
              composite = svn_txdelta_compose_windows(window_A, window_B,
                                                      wpool);
            if (print_windows)
              delta_window_print(composite, "AB", stdout);

            /* The source view length should not be 0 if there are
               source copy ops in the window. */
            if (composite
                && composite->sview_len == 0 && composite->src_ops > 0)
              return svn_error_create
                (SVN_ERR_FS_GENERAL, NULL,
                 "combined delta window is inconsistent");

            SVN_ERR(handler(composite, handler_baton));
            svn_pool_clear(wpool);
          }
        while (composite != NULL);
        svn_pool_destroy(wpool);
      }

      svn_pool_destroy(delta_pool);

      SVN_ERR(compare_files(target, target_regen, dump_files));

      apr_file_close(source);
      apr_file_close(middle);
      apr_file_close(target);
      apr_file_close(source_copy);
      apr_file_close(middle_copy);
      apr_file_close(target_regen);
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 8
0
/* Implements svn_test_driver_t. */
static svn_error_t *
random_test(const char **msg,
            svn_boolean_t msg_only,
            svn_test_opts_t *opts,
            apr_pool_t *pool)
{
  static char msg_buff[256];

  apr_uint32_t seed, bytes_range, maxlen;
  int i, iterations, dump_files, print_windows;
  const char *random_bytes;

  /* Initialize parameters and print out the seed in case we dump core
     or something. */
  init_params(&seed, &maxlen, &iterations, &dump_files, &print_windows,
              &random_bytes, &bytes_range, pool);
  sprintf(msg_buff, "random delta test, seed = %lu", (unsigned long) seed);
  *msg = msg_buff;

  if (msg_only)
    return SVN_NO_ERROR;
  else
    printf("SEED:  %s\n", msg_buff);

  for (i = 0; i < iterations; i++)
    {
      /* Generate source and target for the delta and its application.  */
      apr_uint32_t subseed_base = svn_test_rand(&seed);
      apr_file_t *source = generate_random_file(maxlen, subseed_base, &seed,
                                                random_bytes, bytes_range,
                                                dump_files, pool);
      apr_file_t *target = generate_random_file(maxlen, subseed_base, &seed,
                                                random_bytes, bytes_range,
                                                dump_files, pool);
      apr_file_t *source_copy = copy_tempfile(source, pool);
      apr_file_t *target_regen = open_tempfile(NULL, pool);

      svn_txdelta_stream_t *txdelta_stream;
      svn_txdelta_window_handler_t handler;
      svn_stream_t *stream;
      void *handler_baton;

      /* Set up a four-stage pipeline: create a delta, convert it to
         svndiff format, parse it back into delta format, and apply it
         to a copy of the source file to see if we get the same target
         back.  */
      apr_pool_t *delta_pool = svn_pool_create(pool);

      /* Make stage 4: apply the text delta.  */
      svn_txdelta_apply(svn_stream_from_aprfile(source_copy, delta_pool),
                        svn_stream_from_aprfile(target_regen, delta_pool),
                        NULL, NULL, delta_pool, &handler, &handler_baton);

      /* Make stage 3: reparse the text delta.  */
      stream = svn_txdelta_parse_svndiff(handler, handler_baton, TRUE,
                                         delta_pool);

      /* Make stage 2: encode the text delta in svndiff format.  */
      svn_txdelta_to_svndiff2(&handler, &handler_baton, stream, 1,
                              delta_pool);

      /* Make stage 1: create the text delta.  */
      svn_txdelta(&txdelta_stream,
                  svn_stream_from_aprfile(source, delta_pool),
                  svn_stream_from_aprfile(target, delta_pool),
                  delta_pool);

      SVN_ERR(svn_txdelta_send_txstream(txdelta_stream,
                                        handler,
                                        handler_baton,
                                        delta_pool));

      svn_pool_destroy(delta_pool);

      SVN_ERR(compare_files(target, target_regen, dump_files));

      apr_file_close(source);
      apr_file_close(target);
      apr_file_close(source_copy);
      apr_file_close(target_regen);
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 9
0
static svn_error_t *
stream_window_test(const char **msg,
                   svn_boolean_t msg_only,
                   svn_test_opts_t *opts,
                   apr_pool_t *pool)
{
  /* Note: put these in data segment, not the stack */
  static char source[109001];
  static char target[109001];
  int i;
  char *p = &source[9];
  svn_checksum_t *expected;
  svn_checksum_t *actual;
  svn_string_t source_str;
  svn_string_t target_str;
  svn_stream_t *source_stream;
  svn_stream_t *target_stream;
  svn_txdelta_stream_t *txstream;

  *msg = "txdelta stream and windows test";
  if (msg_only)
    return SVN_NO_ERROR;

  memcpy(source, "a\nb\nc\nd\ne", 9);
  for (i = 100; i--; )
    *p++ = '\n';
  for (i = 999; i--; p += 109)
    memcpy(p, source, 109);
  source[109000] = '\0';

  memcpy(target, source, 109001);
  for (i = 1000; i--; )
    target[i*109 + 4] = 'X';

  SVN_ERR(svn_checksum(&expected, svn_checksum_md5, target, 109000, pool));
  /* f6fd44565e14c6e44b35292719deb77e */
  printf("expected: %s\n", svn_checksum_to_cstring(expected, pool));

  source_str.data = source;
  source_str.len = 109000;
  source_stream = svn_stream_from_string(&source_str, pool);

  target_str.data = target;
  target_str.len = 109000;
  target_stream = svn_stream_from_string(&target_str, pool);

  svn_txdelta(&txstream, source_stream, target_stream, pool);

  while (1)
    {
      svn_txdelta_window_t *window;

      SVN_ERR(svn_txdelta_next_window(&window, txstream, pool));
      if (window == NULL)
        break;

      /* ### examine the window */
    }

  actual = svn_checksum__from_digest(svn_txdelta_md5_digest(txstream),
                                     svn_checksum_md5, pool);;
  printf("  actual: %s\n", svn_checksum_to_cstring(actual, pool));

  if (!svn_checksum_match(expected, actual))
    {
      return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
                              "Checksums did not match.");
    }

  return SVN_NO_ERROR;
}
Exemplo n.º 10
0
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. */
          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_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);
}