205
207
if (rb->ra_session == NULL)
206
SVN_ERR(svn_client_open_ra_session(&rb->ra_session,
208
rb->ctx, rb->ra_session_pool));
208
SVN_ERR(svn_client_open_ra_session2(&rb->ra_session,
209
rb->ra_session_url, NULL,
210
rb->ctx, rb->ra_session_pool,
210
213
SVN_ERR(svn_ra_rev_prop(rb->ra_session, log_entry->revision,
211
214
name, &value, pool));
212
215
if (log_entry->revprops == NULL)
213
216
log_entry->revprops = apr_hash_make(pool);
214
apr_hash_set(log_entry->revprops, (const void *)name,
215
APR_HASH_KEY_STRING, (const void *)value);
217
svn_hash_sets(log_entry->revprops, name, value);
217
219
if (log_entry->revprops)
219
221
/* Pre-1.5 servers send the standard revprops unconditionally;
220
222
clear those the caller doesn't want. */
221
223
if (!want_author)
222
apr_hash_set(log_entry->revprops, SVN_PROP_REVISION_AUTHOR,
223
APR_HASH_KEY_STRING, NULL);
224
svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_AUTHOR, NULL);
225
apr_hash_set(log_entry->revprops, SVN_PROP_REVISION_DATE,
226
APR_HASH_KEY_STRING, NULL);
226
svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_DATE, NULL);
228
apr_hash_set(log_entry->revprops, SVN_PROP_REVISION_LOG,
229
APR_HASH_KEY_STRING, NULL);
228
svn_hash_sets(log_entry->revprops, SVN_PROP_REVISION_LOG, NULL);
234
233
if (rb->ra_session == NULL)
235
SVN_ERR(svn_client_open_ra_session(&rb->ra_session,
237
rb->ctx, rb->ra_session_pool));
234
SVN_ERR(svn_client_open_ra_session2(&rb->ra_session,
235
rb->ra_session_url, NULL,
236
rb->ctx, rb->ra_session_pool,
239
239
SVN_ERR(svn_ra_rev_proplist(rb->ra_session, log_entry->revision,
240
240
&log_entry->revprops, pool));
261
261
return rb->receiver(rb->baton, log_entry, pool);
265
/*** Public Interface. ***/
269
svn_client_log5(const apr_array_header_t *targets,
270
const svn_opt_revision_t *peg_revision,
271
const apr_array_header_t *revision_ranges,
273
svn_boolean_t discover_changed_paths,
274
svn_boolean_t strict_node_history,
275
svn_boolean_t include_merged_revisions,
276
const apr_array_header_t *revprops,
277
svn_log_entry_receiver_t real_receiver,
278
void *real_receiver_baton,
279
svn_client_ctx_t *ctx,
282
svn_ra_session_t *ra_session;
283
const char *url_or_path;
284
svn_boolean_t has_log_revprops;
285
const char *actual_url;
286
apr_array_header_t *condensed_targets;
287
svn_revnum_t ignored_revnum;
288
svn_opt_revision_t session_opt_rev;
289
const char *ra_target;
290
pre_15_receiver_baton_t rb = {0};
291
apr_pool_t *iterpool;
293
svn_opt_revision_t peg_rev;
295
if (revision_ranges->nelts == 0)
297
return svn_error_create
298
(SVN_ERR_CLIENT_BAD_REVISION, NULL,
299
_("Missing required revision specification"));
302
/* Make a copy of PEG_REVISION, we may need to change it to a
304
peg_rev.kind = peg_revision->kind;
305
peg_rev.value = peg_revision->value;
307
/* Use the passed URL, if there is one. */
308
url_or_path = APR_ARRAY_IDX(targets, 0, const char *);
309
session_opt_rev.kind = svn_opt_revision_unspecified;
311
for (i = 0; i < revision_ranges->nelts; i++)
264
/* Resolve the URLs or WC path in TARGETS as per the svn_client_log5 API.
266
The limitations on TARGETS specified by svn_client_log5 are enforced here.
267
So TARGETS can only contain a single WC path or a URL and zero or more
268
relative paths -- anything else will raise an error.
270
PEG_REVISION, TARGETS, and CTX are as per svn_client_log5.
272
If TARGETS contains a single WC path then set *RA_TARGET to the absolute
273
path of that single path if PEG_REVISION is dependent on the working copy
274
(e.g. PREV). Otherwise set *RA_TARGET to the corresponding URL for the
275
single WC path. Set *RELATIVE_TARGETS to an array with a single
278
If TARGETS contains only a single URL, then set *RA_TARGET to a copy of
279
that URL and *RELATIVE_TARGETS to an array with a single element "".
281
If TARGETS contains a single URL and one or more relative paths, then
282
set *RA_TARGET to a copy of that URL and *RELATIVE_TARGETS to a copy of
283
each relative path after the URL.
285
If *PEG_REVISION is svn_opt_revision_unspecified, then *PEG_REVISION is
286
set to svn_opt_revision_head for URLs or svn_opt_revision_working for a
289
*RA_TARGET and *RELATIVE_TARGETS are allocated in RESULT_POOL. */
291
resolve_log_targets(apr_array_header_t **relative_targets,
292
const char **ra_target,
293
svn_opt_revision_t *peg_revision,
294
const apr_array_header_t *targets,
295
svn_client_ctx_t *ctx,
296
apr_pool_t *result_pool,
297
apr_pool_t *scratch_pool)
300
svn_boolean_t url_targets;
302
/* Per svn_client_log5, TARGETS contains either a URL followed by zero or
303
more relative paths, or one working copy path. */
304
const char *url_or_path = APR_ARRAY_IDX(targets, 0, const char *);
306
/* svn_client_log5 requires at least one target. */
307
if (targets->nelts == 0)
308
return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
309
_("No valid target found"));
311
/* Initialize the output array. At a minimum, we need room for one
312
(possibly empty) relpath. Otherwise, we have to hold a relpath
313
for every item in TARGETS except the first. */
314
*relative_targets = apr_array_make(result_pool,
315
MAX(1, targets->nelts - 1),
316
sizeof(const char *));
318
if (svn_path_is_url(url_or_path))
320
/* An unspecified PEG_REVISION for a URL path defaults
321
to svn_opt_revision_head. */
322
if (peg_revision->kind == svn_opt_revision_unspecified)
323
peg_revision->kind = svn_opt_revision_head;
325
/* The logic here is this: If we get passed one argument, we assume
326
it is the full URL to a file/dir we want log info for. If we get
327
a URL plus some paths, then we assume that the URL is the base,
328
and that the paths passed are relative to it. */
329
if (targets->nelts > 1)
331
/* We have some paths, let's use them. Start after the URL. */
332
for (i = 1; i < targets->nelts; i++)
336
target = APR_ARRAY_IDX(targets, i, const char *);
338
if (svn_path_is_url(target) || svn_dirent_is_absolute(target))
339
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
340
_("'%s' is not a relative path"),
343
APR_ARRAY_PUSH(*relative_targets, const char *) =
344
apr_pstrdup(result_pool, target);
349
/* If we have a single URL, then the session will be rooted at
350
it, so just send an empty string for the paths we are
352
APR_ARRAY_PUSH(*relative_targets, const char *) = "";
355
/* Remember that our targets are URLs. */
358
else /* WC path target. */
361
const char *target_abspath;
364
if (targets->nelts > 1)
365
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
366
_("When specifying working copy paths, only "
367
"one target may be given"));
369
/* An unspecified PEG_REVISION for a working copy path defaults
370
to svn_opt_revision_working. */
371
if (peg_revision->kind == svn_opt_revision_unspecified)
372
peg_revision->kind = svn_opt_revision_working;
374
/* Get URLs for each target */
375
target = APR_ARRAY_IDX(targets, 0, const char *);
377
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, scratch_pool));
378
SVN_ERR(svn_wc__node_get_url(&url_or_path, ctx->wc_ctx, target_abspath,
379
scratch_pool, scratch_pool));
380
APR_ARRAY_PUSH(*relative_targets, const char *) = "";
383
/* If this is a revision type that requires access to the working copy,
384
* we use our initial target path to figure out where to root the RA
385
* session, otherwise we use our URL. */
386
if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
389
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
390
_("PREV, BASE, or COMMITTED revision "
391
"keywords are invalid for URL"));
394
SVN_ERR(svn_dirent_get_absolute(
395
ra_target, APR_ARRAY_IDX(targets, 0, const char *), result_pool));
399
*ra_target = apr_pstrdup(result_pool, url_or_path);
405
/* Keep track of oldest and youngest opt revs found.
407
If REV is younger than *YOUNGEST_REV, or *YOUNGEST_REV is
408
svn_opt_revision_unspecified, then set *YOUNGEST_REV equal to REV.
410
If REV is older than *OLDEST_REV, or *OLDEST_REV is
411
svn_opt_revision_unspecified, then set *OLDEST_REV equal to REV. */
413
find_youngest_and_oldest_revs(svn_revnum_t *youngest_rev,
414
svn_revnum_t *oldest_rev,
417
/* Is REV younger than YOUNGEST_REV? */
418
if (! SVN_IS_VALID_REVNUM(*youngest_rev)
419
|| rev > *youngest_rev)
422
if (! SVN_IS_VALID_REVNUM(*oldest_rev)
423
|| rev < *oldest_rev)
427
typedef struct rev_range_t
429
svn_revnum_t range_start;
430
svn_revnum_t range_end;
433
/* Convert array of svn_opt_revision_t ranges to an array of svn_revnum_t
436
Given a log target URL_OR_ABSPATH@PEG_REV and an array of
437
svn_opt_revision_range_t's OPT_REV_RANGES, resolve the opt revs in
438
OPT_REV_RANGES to svn_revnum_t's and return these in *REVISION_RANGES, an
439
array of rev_range_t *.
441
Set *YOUNGEST_REV and *OLDEST_REV to the youngest and oldest revisions
442
found in *REVISION_RANGES.
444
If the repository needs to be contacted to resolve svn_opt_revision_date or
445
svn_opt_revision_head revisions, then the session used to do this is
446
RA_SESSION; it must be an open session to any URL in the right repository.
449
convert_opt_rev_array_to_rev_range_array(
450
apr_array_header_t **revision_ranges,
451
svn_revnum_t *youngest_rev,
452
svn_revnum_t *oldest_rev,
453
svn_ra_session_t *ra_session,
454
const char *url_or_abspath,
455
const apr_array_header_t *opt_rev_ranges,
456
const svn_opt_revision_t *peg_rev,
457
svn_client_ctx_t *ctx,
458
apr_pool_t *result_pool,
459
apr_pool_t *scratch_pool)
462
svn_revnum_t head_rev = SVN_INVALID_REVNUM;
464
/* Initialize the input/output parameters. */
465
*youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
467
/* Convert OPT_REV_RANGES to an array of rev_range_t and find the youngest
468
and oldest revision range that spans all of OPT_REV_RANGES. */
469
*revision_ranges = apr_array_make(result_pool, opt_rev_ranges->nelts,
470
sizeof(rev_range_t *));
472
for (i = 0; i < opt_rev_ranges->nelts; i++)
313
474
svn_opt_revision_range_t *range;
315
range = APR_ARRAY_IDX(revision_ranges, i, svn_opt_revision_range_t *);
475
rev_range_t *rev_range;
476
svn_boolean_t start_same_as_end = FALSE;
478
range = APR_ARRAY_IDX(opt_rev_ranges, i, svn_opt_revision_range_t *);
480
/* Right now RANGE can be any valid pair of svn_opt_revision_t's. We
481
will now convert all RANGEs in place to the corresponding
482
svn_opt_revision_number kind. */
317
483
if ((range->start.kind != svn_opt_revision_unspecified)
318
484
&& (range->end.kind == svn_opt_revision_unspecified))
357
523
_("Missing required revision specification"));
360
/* Determine the revision to open the RA session to. */
361
if (session_opt_rev.kind == svn_opt_revision_unspecified)
363
if (range->start.kind == svn_opt_revision_number &&
364
range->end.kind == svn_opt_revision_number)
367
(range->start.value.number > range->end.value.number ?
368
range->start : range->end);
370
else if (range->start.kind == svn_opt_revision_head ||
371
range->end.kind == svn_opt_revision_head)
373
session_opt_rev.kind = svn_opt_revision_head;
375
else if (range->start.kind == svn_opt_revision_date &&
376
range->end.kind == svn_opt_revision_date)
379
(range->start.value.date > range->end.value.date ?
380
range->start : range->end);
385
/* Use the passed URL, if there is one. */
386
if (svn_path_is_url(url_or_path))
388
/* Initialize this array, since we'll be building it below */
389
condensed_targets = apr_array_make(pool, 1, sizeof(const char *));
391
/* The logic here is this: If we get passed one argument, we assume
392
it is the full URL to a file/dir we want log info for. If we get
393
a URL plus some paths, then we assume that the URL is the base,
394
and that the paths passed are relative to it. */
395
if (targets->nelts > 1)
397
/* We have some paths, let's use them. Start after the URL. */
398
for (i = 1; i < targets->nelts; i++)
402
target = APR_ARRAY_IDX(targets, i, const char *);
404
if (svn_path_is_url(target) || svn_dirent_is_absolute(target))
405
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
406
_("'%s' is not a relative path"),
409
APR_ARRAY_PUSH(condensed_targets, const char *) = target;
526
/* Does RANGE describe a single svn_opt_revision_t? */
527
if (range->start.kind == range->end.kind)
529
if (range->start.kind == svn_opt_revision_number)
531
if (range->start.value.number == range->end.value.number)
532
start_same_as_end = TRUE;
534
else if (range->start.kind == svn_opt_revision_date)
536
if (range->start.value.date == range->end.value.date)
537
start_same_as_end = TRUE;
541
start_same_as_end = TRUE;
545
rev_range = apr_palloc(result_pool, sizeof(*rev_range));
546
SVN_ERR(svn_client__get_revision_number(
547
&rev_range->range_start, &head_rev,
548
ctx->wc_ctx, url_or_abspath, ra_session,
549
&range->start, scratch_pool));
550
if (start_same_as_end)
551
rev_range->range_end = rev_range->range_start;
414
/* If we have a single URL, then the session will be rooted at
415
it, so just send an empty string for the paths we are
417
APR_ARRAY_PUSH(condensed_targets, const char *) = "";
553
SVN_ERR(svn_client__get_revision_number(
554
&rev_range->range_end, &head_rev,
555
ctx->wc_ctx, url_or_abspath, ra_session,
556
&range->end, scratch_pool));
558
/* Possibly update the oldest and youngest revisions requested. */
559
find_youngest_and_oldest_revs(youngest_rev,
561
rev_range->range_start);
562
find_youngest_and_oldest_revs(youngest_rev,
564
rev_range->range_end);
565
APR_ARRAY_PUSH(*revision_ranges, rev_range_t *) = rev_range;
572
compare_rev_to_segment(const void *key_p,
573
const void *element_p)
576
* (svn_revnum_t *)key_p;
577
const svn_location_segment_t *segment =
578
*((const svn_location_segment_t * const *) element_p);
580
if (rev < segment->range_start)
582
else if (rev > segment->range_end)
588
/* Run svn_ra_get_log2 for PATHS, one or more paths relative to RA_SESSION's
589
common parent, for each revision in REVISION_RANGES, an array of
592
RA_SESSION is an open session pointing to ACTUAL_LOC.
594
LOG_SEGMENTS is an array of svn_location_segment_t * items representing the
595
history of PATHS from the oldest to youngest revisions found in
598
The TARGETS, LIMIT, DISCOVER_CHANGED_PATHS, STRICT_NODE_HISTORY,
599
INCLUDE_MERGED_REVISIONS, REVPROPS, REAL_RECEIVER, and REAL_RECEIVER_BATON
600
parameters are all as per the svn_client_log5 API. */
602
run_ra_get_log(apr_array_header_t *revision_ranges,
603
apr_array_header_t *paths,
604
apr_array_header_t *log_segments,
605
svn_client__pathrev_t *actual_loc,
606
svn_ra_session_t *ra_session,
607
/* The following are as per svn_client_log5. */
608
const apr_array_header_t *targets,
610
svn_boolean_t discover_changed_paths,
611
svn_boolean_t strict_node_history,
612
svn_boolean_t include_merged_revisions,
613
const apr_array_header_t *revprops,
614
svn_log_entry_receiver_t real_receiver,
615
void *real_receiver_baton,
616
svn_client_ctx_t *ctx,
617
apr_pool_t *scratch_pool)
620
pre_15_receiver_baton_t rb = {0};
621
apr_pool_t *iterpool;
622
svn_boolean_t has_log_revprops;
624
SVN_ERR(svn_ra_has_capability(ra_session, &has_log_revprops,
625
SVN_RA_CAPABILITY_LOG_REVPROPS,
628
if (!has_log_revprops)
422
apr_array_header_t *target_urls;
423
apr_array_header_t *real_targets;
425
/* See FIXME about multiple wc targets, below. */
426
if (targets->nelts > 1)
427
return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
428
_("When specifying working copy paths, only "
429
"one target may be given"));
431
/* An unspecified PEG_REVISION for a working copy path defautls
432
to svn_opt_revision_working. */
433
if (peg_rev.kind == svn_opt_revision_unspecified)
434
peg_rev.kind = svn_opt_revision_working;
436
/* Get URLs for each target */
437
target_urls = apr_array_make(pool, 1, sizeof(const char *));
438
real_targets = apr_array_make(pool, 1, sizeof(const char *));
439
iterpool = svn_pool_create(pool);
440
for (i = 0; i < targets->nelts; i++)
443
const char *target = APR_ARRAY_IDX(targets, i, const char *);
444
const char *target_abspath;
446
svn_pool_clear(iterpool);
447
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, iterpool));
448
SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, target_abspath,
452
return svn_error_createf
453
(SVN_ERR_ENTRY_MISSING_URL, NULL,
454
_("Entry '%s' has no URL"),
455
svn_dirent_local_style(target, pool));
457
APR_ARRAY_PUSH(target_urls, const char *) = url;
458
APR_ARRAY_PUSH(real_targets, const char *) = target;
461
/* if we have no valid target_urls, just exit. */
462
if (target_urls->nelts == 0)
465
/* Find the base URL and condensed targets relative to it. */
466
SVN_ERR(svn_uri_condense_targets(&url_or_path, &condensed_targets,
467
target_urls, TRUE, pool, iterpool));
469
if (condensed_targets->nelts == 0)
470
APR_ARRAY_PUSH(condensed_targets, const char *) = "";
472
/* 'targets' now becomes 'real_targets', which has bogus,
473
unversioned things removed from it. */
474
targets = real_targets;
475
svn_pool_destroy(iterpool);
480
/* If this is a revision type that requires access to the working copy,
481
* we use our initial target path to figure out where to root the RA
482
* session, otherwise we use our URL. */
483
if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_rev.kind))
484
SVN_ERR(svn_dirent_condense_targets(&ra_target, NULL, targets,
487
ra_target = url_or_path;
489
SVN_ERR(svn_client__ra_session_from_path(&ra_session, &ignored_revnum,
490
&actual_url, ra_target, NULL,
491
&peg_rev, &session_opt_rev,
494
SVN_ERR(svn_ra_has_capability(ra_session, &has_log_revprops,
495
SVN_RA_CAPABILITY_LOG_REVPROPS, pool));
497
if (!has_log_revprops) {
498
630
/* See above pre-1.5 notes. */
501
633
/* Create ra session on first use */
502
rb.ra_session_pool = pool;
503
rb.ra_session_url = actual_url;
634
rb.ra_session_pool = scratch_pool;
635
rb.ra_session_url = actual_loc->url;
507
638
/* It's a bit complex to correctly handle the special revision words
508
639
* such as "BASE", "COMMITTED", and "PREV". For example, if the
551
682
* epg wonders if the repository could send a unified stream of log
552
683
* entries if the paths and revisions were passed down.
554
iterpool = svn_pool_create(pool);
685
iterpool = svn_pool_create(scratch_pool);
555
686
for (i = 0; i < revision_ranges->nelts; i++)
557
svn_revnum_t start_revnum, end_revnum, youngest_rev = SVN_INVALID_REVNUM;
688
const char *old_session_url;
558
689
const char *path = APR_ARRAY_IDX(targets, 0, const char *);
559
690
const char *local_abspath_or_url;
560
svn_opt_revision_range_t *range;
561
692
limit_receiver_baton_t lb;
562
693
svn_log_entry_receiver_t passed_receiver;
563
694
void *passed_receiver_baton;
564
695
const apr_array_header_t *passed_receiver_revprops;
696
svn_location_segment_t **matching_segment;
697
svn_revnum_t younger_rev;
566
699
svn_pool_clear(iterpool);
568
701
if (!svn_path_is_url(path))
569
SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, iterpool));
702
SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path,
571
705
local_abspath_or_url = path;
573
range = APR_ARRAY_IDX(revision_ranges, i, svn_opt_revision_range_t *);
707
range = APR_ARRAY_IDX(revision_ranges, i, rev_range_t *);
575
SVN_ERR(svn_client__get_revision_number(&start_revnum, &youngest_rev,
576
ctx->wc_ctx, local_abspath_or_url,
577
ra_session, &range->start,
579
SVN_ERR(svn_client__get_revision_number(&end_revnum, &youngest_rev,
580
ctx->wc_ctx, local_abspath_or_url,
581
ra_session, &range->end,
709
/* Issue #4355: Account for renames spanning requested
711
younger_rev = MAX(range->range_start, range->range_end);
712
matching_segment = bsearch(&younger_rev, log_segments->elts,
713
log_segments->nelts, log_segments->elt_size,
714
compare_rev_to_segment);
715
/* LOG_SEGMENTS is supposed to represent the history of PATHS from
716
the oldest to youngest revs in REVISION_RANGES. This function's
717
current sole caller svn_client_log5 *should* be providing
718
LOG_SEGMENTS that span the oldest to youngest revs in
719
REVISION_RANGES, even if one or more of the svn_location_segment_t's
720
returned have NULL path members indicating a gap in the history. So
721
MATCHING_SEGMENT should never be NULL, but clearly sometimes it is,
722
see http://svn.haxx.se/dev/archive-2013-06/0522.shtml
723
So to be safe we handle that case. */
724
if (matching_segment == NULL)
727
/* A segment with a NULL path means there is gap in the history.
728
We'll just proceed and let svn_ra_get_log2 fail with a useful
730
if ((*matching_segment)->path != NULL)
732
/* ...but if there is history, then we must account for issue
733
#4355 and make sure our RA session is pointing at the correct
735
const char *segment_url = svn_path_url_add_component2(
736
actual_loc->repos_root_url, (*matching_segment)->path,
738
SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url,
584
744
if (has_log_revprops)
635
795
return SVN_NO_ERROR;
798
/*** Public Interface. ***/
801
svn_client_log5(const apr_array_header_t *targets,
802
const svn_opt_revision_t *peg_revision,
803
const apr_array_header_t *opt_rev_ranges,
805
svn_boolean_t discover_changed_paths,
806
svn_boolean_t strict_node_history,
807
svn_boolean_t include_merged_revisions,
808
const apr_array_header_t *revprops,
809
svn_log_entry_receiver_t real_receiver,
810
void *real_receiver_baton,
811
svn_client_ctx_t *ctx,
814
svn_ra_session_t *ra_session;
815
const char *old_session_url;
816
const char *ra_target;
817
svn_opt_revision_t youngest_opt_rev;
818
svn_revnum_t youngest_rev;
819
svn_revnum_t oldest_rev;
820
svn_opt_revision_t peg_rev;
821
svn_client__pathrev_t *actual_loc;
822
apr_array_header_t *log_segments;
823
apr_array_header_t *revision_ranges;
824
apr_array_header_t *relative_targets;
826
if (opt_rev_ranges->nelts == 0)
828
return svn_error_create
829
(SVN_ERR_CLIENT_BAD_REVISION, NULL,
830
_("Missing required revision specification"));
833
/* Make a copy of PEG_REVISION, we may need to change it to a
835
peg_rev = *peg_revision;
837
SVN_ERR(resolve_log_targets(&relative_targets, &ra_target, &peg_rev,
838
targets, ctx, pool, pool));
840
SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &actual_loc,
841
ra_target, NULL, &peg_rev, &peg_rev,
844
/* Convert OPT_REV_RANGES to an array of rev_range_t and find the youngest
845
and oldest revision range that spans all of OPT_REV_RANGES. */
846
SVN_ERR(convert_opt_rev_array_to_rev_range_array(&revision_ranges,
851
opt_rev_ranges, &peg_rev,
854
/* Make ACTUAL_LOC and RA_SESSION point to the youngest operative rev. */
855
youngest_opt_rev.kind = svn_opt_revision_number;
856
youngest_opt_rev.value.number = youngest_rev;
857
SVN_ERR(svn_client__resolve_rev_and_url(&actual_loc, ra_session,
859
&youngest_opt_rev, ctx, pool));
860
SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
861
actual_loc->url, pool));
863
/* Save us an RA layer round trip if we are on the repository root and
864
know the result in advance. All the revision data has already been
867
if (strcmp(actual_loc->url, actual_loc->repos_root_url) == 0)
869
svn_location_segment_t *segment = apr_pcalloc(pool, sizeof(*segment));
870
log_segments = apr_array_make(pool, 1, sizeof(segment));
872
segment->range_start = oldest_rev;
873
segment->range_end = actual_loc->rev;
875
APR_ARRAY_PUSH(log_segments, svn_location_segment_t *) = segment;
879
/* Get the svn_location_segment_t's representing the requested log
881
SVN_ERR(svn_client__repos_location_segments(&log_segments, ra_session,
883
actual_loc->rev, /* peg */
884
actual_loc->rev, /* start */
885
oldest_rev, /* end */
890
SVN_ERR(run_ra_get_log(revision_ranges, relative_targets, log_segments,
891
actual_loc, ra_session, targets, limit,
892
discover_changed_paths, strict_node_history,
893
include_merged_revisions, revprops, real_receiver,
894
real_receiver_baton, ctx, pool));